chore: snapshot uncommitted local work before rebasing onto cloud-agent
Safety commit so the 94 uncommitted files in the working tree (in-flight Account/Mac/Tip work plus the feature-preview recording tooling) survive the move onto cursor/cloud-agent-1787824905651-vxuwc. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -75,7 +75,30 @@ public struct MonthlyUsageCalendar: View {
|
||||
}
|
||||
|
||||
private var cellDiameter: CGFloat {
|
||||
#if os(macOS)
|
||||
// Mac 端收紧圆点直径,让左侧日历与右侧 2×2 网格高度接近。
|
||||
compact ? 26 : 28
|
||||
#else
|
||||
compact ? 28 : 32
|
||||
#endif
|
||||
}
|
||||
|
||||
/// 日期网格的行间距(Mac 端更紧凑)。
|
||||
private var dayRowSpacing: CGFloat {
|
||||
#if os(macOS)
|
||||
compact ? 2 : 3
|
||||
#else
|
||||
compact ? 3 : 5
|
||||
#endif
|
||||
}
|
||||
|
||||
/// 卡片内各区块(标题 / 星期 / 日期网格)的垂直间距。
|
||||
private var sectionSpacing: CGFloat {
|
||||
#if os(macOS)
|
||||
Spacing.xs
|
||||
#else
|
||||
Spacing.sm
|
||||
#endif
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
@@ -91,7 +114,7 @@ public struct MonthlyUsageCalendar: View {
|
||||
}
|
||||
|
||||
private var calendarContent: some View {
|
||||
VStack(alignment: .leading, spacing: Spacing.sm) {
|
||||
VStack(alignment: .leading, spacing: sectionSpacing) {
|
||||
header
|
||||
weekdayHeader
|
||||
dayGrid
|
||||
@@ -133,7 +156,7 @@ public struct MonthlyUsageCalendar: View {
|
||||
}
|
||||
|
||||
private var dayGrid: some View {
|
||||
LazyVGrid(columns: columns, spacing: compact ? 3 : 5) {
|
||||
LazyVGrid(columns: columns, spacing: dayRowSpacing) {
|
||||
ForEach(Array(calendarCells.enumerated()), id: \.offset) { _, point in
|
||||
if let point {
|
||||
dayCell(point)
|
||||
@@ -161,7 +184,7 @@ public struct MonthlyUsageCalendar: View {
|
||||
let usesContrastingText = opacity >= 0.48
|
||||
|
||||
return Text(day.formatted())
|
||||
.font(.system(size: compact ? 12 : 13, weight: isToday ? .semibold : .medium, design: .rounded))
|
||||
.font(.system(size: dayFontSize, weight: isToday ? .semibold : .medium, design: .rounded))
|
||||
.foregroundStyle(
|
||||
isFuture
|
||||
? palette.textTertiary.opacity(0.45)
|
||||
@@ -185,6 +208,14 @@ public struct MonthlyUsageCalendar: View {
|
||||
)
|
||||
}
|
||||
|
||||
private var dayFontSize: CGFloat {
|
||||
#if os(macOS)
|
||||
compact ? 11 : 12
|
||||
#else
|
||||
compact ? 12 : 13
|
||||
#endif
|
||||
}
|
||||
|
||||
private func fillOpacity(for value: Int, isFuture: Bool) -> Double {
|
||||
guard value > 0, !isFuture else { return 0 }
|
||||
let ratio = min(max(Double(value) / Double(maximumValue), 0), 1)
|
||||
|
||||
@@ -1,146 +0,0 @@
|
||||
// SupportDeveloperSection.swift
|
||||
// OSGKeyboard · HostSupport
|
||||
//
|
||||
// Optional voluntary tip block for Settings. Does not gate features.
|
||||
|
||||
import SwiftUI
|
||||
#if canImport(OSGKeyboardShared)
|
||||
import OSGKeyboardShared
|
||||
#endif
|
||||
|
||||
/// iOS Settings card for the consumable support tip.
|
||||
public struct SupportDeveloperSection: View {
|
||||
@ObservedObject private var tipManager: TipPurchaseManager
|
||||
@Environment(\.themePalette) private var palette
|
||||
|
||||
private let language: AppUILanguage
|
||||
|
||||
@State private var showThankYouAlert = false
|
||||
@State private var showErrorAlert = false
|
||||
@State private var errorMessage = ""
|
||||
|
||||
public init(
|
||||
language: AppUILanguage,
|
||||
tipManager: TipPurchaseManager = .shared
|
||||
) {
|
||||
self.language = language
|
||||
self.tipManager = tipManager
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
CardSection(title: SharedL10n.string("tip.title", language: language)) {
|
||||
VStack(alignment: .leading, spacing: Spacing.sm) {
|
||||
SupportDeveloperTipBody(language: language)
|
||||
|
||||
if tipManager.supportCount > 0 {
|
||||
Text(
|
||||
SharedL10n.format(
|
||||
"tip.thankYou.past",
|
||||
language: language,
|
||||
tipManager.supportCount
|
||||
)
|
||||
)
|
||||
.font(TypeStyle.caption)
|
||||
.foregroundStyle(palette.accent)
|
||||
}
|
||||
|
||||
tipButton
|
||||
.disabled(isPurchaseInFlight)
|
||||
|
||||
Text(SharedL10n.string("tip.consumableNotice", language: language))
|
||||
.font(TypeStyle.caption2)
|
||||
.foregroundStyle(palette.textTertiary)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
.padding(Spacing.md)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.surfaceCard()
|
||||
}
|
||||
.onChange(of: tipManager.purchaseState) { _, newValue in
|
||||
switch newValue {
|
||||
case .succeeded:
|
||||
showThankYouAlert = true
|
||||
case .failed(let message):
|
||||
errorMessage = message
|
||||
showErrorAlert = true
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
.alert(
|
||||
SharedL10n.string("tip.thankYou.title", language: language),
|
||||
isPresented: $showThankYouAlert
|
||||
) {
|
||||
Button(SharedL10n.string("tip.alert.dismiss", language: language)) {
|
||||
tipManager.acknowledgePurchaseState()
|
||||
}
|
||||
} message: {
|
||||
Text(SharedL10n.string("tip.thankYou.message", language: language))
|
||||
}
|
||||
.alert(
|
||||
SharedL10n.string("tip.error.title", language: language),
|
||||
isPresented: $showErrorAlert
|
||||
) {
|
||||
Button(SharedL10n.string("tip.alert.dismiss", language: language)) {
|
||||
tipManager.acknowledgePurchaseState()
|
||||
}
|
||||
} message: {
|
||||
Text(errorMessage)
|
||||
}
|
||||
}
|
||||
|
||||
private var isPurchaseInFlight: Bool {
|
||||
switch tipManager.purchaseState {
|
||||
case .loading, .purchasing:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private var tipButton: some View {
|
||||
Button {
|
||||
Task { await tipManager.purchase() }
|
||||
} label: {
|
||||
HStack(spacing: Spacing.sm) {
|
||||
if isPurchaseInFlight {
|
||||
ProgressView()
|
||||
.controlSize(.small)
|
||||
}
|
||||
Text(tipButtonTitle)
|
||||
.font(TypeStyle.body.weight(.semibold))
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.vertical, Spacing.sm)
|
||||
.foregroundStyle(OSGColor.fixedLightContent)
|
||||
.background(palette.accent, in: RoundedRectangle(cornerRadius: Radius.large, style: .continuous))
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.accessibilityLabel(tipButtonTitle)
|
||||
}
|
||||
|
||||
private var tipButtonTitle: String {
|
||||
if let product = tipManager.product {
|
||||
return SharedL10n.format("tip.button", language: language, product.displayPrice)
|
||||
}
|
||||
return SharedL10n.string("tip.buttonFallback", language: language)
|
||||
}
|
||||
}
|
||||
|
||||
/// Shared copy + tip button for macOS Settings (Mac chrome wraps this).
|
||||
public struct SupportDeveloperTipBody: View {
|
||||
@Environment(\.themePalette) private var palette
|
||||
|
||||
private let language: AppUILanguage
|
||||
|
||||
public init(language: AppUILanguage) {
|
||||
self.language = language
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
Text(SharedL10n.string("tip.body", language: language))
|
||||
.font(TypeStyle.body)
|
||||
.foregroundStyle(palette.textSecondary)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
}
|
||||
@@ -98,7 +98,7 @@ public struct UsageStatsCluster<Header: View>: View {
|
||||
chartCard
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
splitStatGrid
|
||||
.frame(maxWidth: .infinity)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
}
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
@@ -121,6 +121,7 @@ public struct UsageStatsCluster<Header: View>: View {
|
||||
systemImage: "waveform"
|
||||
)
|
||||
}
|
||||
.frame(maxHeight: splitRowMaxHeight)
|
||||
HStack(spacing: splitItemSpacing) {
|
||||
splitCell(
|
||||
title: SharedL10n.string("stat.translation", language: language),
|
||||
@@ -136,6 +137,7 @@ public struct UsageStatsCluster<Header: View>: View {
|
||||
action: onOpenDictionary
|
||||
)
|
||||
}
|
||||
.frame(maxHeight: splitRowMaxHeight)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,7 +166,8 @@ public struct UsageStatsCluster<Header: View>: View {
|
||||
title: title,
|
||||
value: value,
|
||||
caption: caption,
|
||||
accent: accent
|
||||
accent: accent,
|
||||
expands: splitCellsFillHeight
|
||||
)
|
||||
.overlay(alignment: .topTrailing) {
|
||||
disclosureIndicator
|
||||
@@ -173,15 +176,17 @@ public struct UsageStatsCluster<Header: View>: View {
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.frame(maxWidth: .infinity)
|
||||
.frame(maxWidth: .infinity, maxHeight: splitRowMaxHeight)
|
||||
} else {
|
||||
UsageStatCard(
|
||||
title: title,
|
||||
value: value,
|
||||
caption: caption,
|
||||
systemImage: systemImage,
|
||||
accent: accent
|
||||
accent: accent,
|
||||
expands: splitCellsFillHeight
|
||||
)
|
||||
.frame(maxHeight: splitRowMaxHeight)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,7 +340,7 @@ public struct UsageStatsCluster<Header: View>: View {
|
||||
|
||||
private var splitSectionSpacing: CGFloat {
|
||||
#if os(macOS)
|
||||
Spacing.md
|
||||
Spacing.sm
|
||||
#else
|
||||
CardLayoutMetrics.sectionSpacing
|
||||
#endif
|
||||
@@ -343,11 +348,24 @@ public struct UsageStatsCluster<Header: View>: View {
|
||||
|
||||
private var splitItemSpacing: CGFloat {
|
||||
#if os(macOS)
|
||||
Spacing.md
|
||||
Spacing.sm
|
||||
#else
|
||||
CardLayoutMetrics.compactItemSpacing
|
||||
#endif
|
||||
}
|
||||
|
||||
private var splitRowMaxHeight: CGFloat? {
|
||||
splitCellsFillHeight ? .infinity : nil
|
||||
}
|
||||
|
||||
/// Mac 侧的 2×2 网格拉伸到与左侧日历同高,保证左右卡片底边对齐。
|
||||
private var splitCellsFillHeight: Bool {
|
||||
#if os(macOS)
|
||||
layout == .split
|
||||
#else
|
||||
false
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
extension UsageStatsCluster where Header == EmptyView {
|
||||
|
||||
Reference in New Issue
Block a user