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:
2026-09-01 11:28:07 -07:00
co-authored by Claude Opus 5
parent 39002336c0
commit 5d51c5bf77
101 changed files with 3629 additions and 1354 deletions
@@ -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 {