feat(account): add managed credits and cloud gateway

Introduce optional Apple account-backed credits with scoped gateway access while preserving local and BYOK paths. Refresh assistant behavior, tests, privacy disclosures, docs, and the website for the 2.0 experience.
This commit is contained in:
Rocky
2026-08-20 11:43:21 +08:00
parent 0f9280bd00
commit 498f407585
301 changed files with 19221 additions and 10891 deletions
+1 -1
View File
@@ -50,7 +50,7 @@ enum MacAppContextService {
"com.microsoft.Word": .document,
"com.google.GoogleDocs": .document,
"com.evernote.Evernote": .document,
"com.microsoft.onenote.mac": .document,
"com.microsoft.onenote.mac": .document
]
/// Chat-style apps from the shared host registry (iOS bundle IDs often
+6 -6
View File
@@ -202,7 +202,7 @@ struct MacSettingsSection<Content: View>: View {
@Environment(\.themePalette) private var palette
let title: String
var footer: String? = nil
var footer: String?
@ViewBuilder var content: () -> Content
var body: some View {
@@ -238,7 +238,7 @@ struct MacInlineRow<Control: View>: View {
@Environment(\.themePalette) private var palette
let title: String
var subtitle: String? = nil
var subtitle: String?
@ViewBuilder var control: () -> Control
var body: some View {
@@ -306,7 +306,7 @@ struct MacProviderSettingRow<Content: View>: View {
@Environment(\.themePalette) private var palette
let title: String
var subtitle: String? = nil
var subtitle: String?
/// Cross-axis alignment between the label column and the control. Provider
/// rows keep `.top` (model row grows a status line below its field); single
/// control rows can pass `.center` to vertically center label and control.
@@ -350,7 +350,7 @@ struct MacSettingsIconButton: View {
@Environment(\.themePalette) private var palette
let systemName: String
var help: String? = nil
var help: String?
var disabled: Bool = false
let action: () -> Void
@@ -378,9 +378,9 @@ struct MacSettingsToolButton: View {
let title: String
/// Background fill. Defaults to the neutral elevated surface.
var fill: Color? = nil
var fill: Color?
/// Text color. Defaults to primary text (tertiary when disabled).
var foreground: Color? = nil
var foreground: Color?
/// Hairline divider border drawn for the neutral variant, hidden for
/// colored fills (delete / download) so the fill reads as the button.
var showsBorder: Bool = true
+5 -1
View File
@@ -70,7 +70,11 @@ struct MacSettingsICloudSyncRow: View {
get: { isEnabled },
set: { newValue in
guard newValue != isEnabled else { return }
newValue ? enableSync() : disableSync()
if newValue {
enableSync()
} else {
disableSync()
}
}
)
}
@@ -289,7 +289,7 @@ struct MacLocalASRModelSettingsView: View {
MacInlinePickerOption(
value: LocalASRDownloadSourcePreference.huggingface,
label: MacL10n.string("mac.localASR.downloadSource.huggingface", language: lang)
),
)
],
fillsWidth: true
)
+1 -1
View File
@@ -32,7 +32,7 @@ struct MacPolishStylesView: View {
.adaptive(minimum: MacMetrics.polishStyleCardMinWidth),
spacing: Spacing.md,
alignment: .top
),
)
]
}
+1 -1
View File
@@ -235,7 +235,7 @@ struct MacProviderThinkingRow: View {
@Environment(\.themePalette) private var palette
let title: String
var subtitle: String? = nil
var subtitle: String?
@Binding var isOn: Bool
var body: some View {
+18 -18
View File
@@ -28,34 +28,34 @@ enum MacSystemPalette {
private static func makePalette(dark: Bool) -> ThemePalette {
ThemePalette(
background: resolved(dark ? darkBackground : warmBackground, dark: dark),
surface: resolved(dark ? darkSurface : warmSurface, dark: dark),
background: resolved(dark ? darkBackground : warmBackground, dark: dark),
surface: resolved(dark ? darkSurface : warmSurface, dark: dark),
surfaceElevated: resolved(dark ? darkElevated : warmElevated, dark: dark),
surfaceMuted: resolved(dark ? darkMuted : warmMuted, dark: dark),
surfaceMuted: resolved(dark ? darkMuted : warmMuted, dark: dark),
accent: Palette.accent,
accentMuted: Palette.accent.opacity(dark ? 0.22 : 0.14),
accentGlow: Palette.accent.opacity(dark ? 0.40 : 0.32),
aiTeal: Palette.aiTeal,
accent: Palette.accent,
accentMuted: Palette.accent.opacity(dark ? 0.22 : 0.14),
accentGlow: Palette.accent.opacity(dark ? 0.40 : 0.32),
aiTeal: Palette.aiTeal,
danger: resolved(.systemRed, dark: dark),
success: Palette.accent,
warning: resolved(.systemOrange, dark: dark),
danger: resolved(.systemRed, dark: dark),
success: Palette.accent,
warning: resolved(.systemOrange, dark: dark),
textPrimary: resolved(.labelColor, dark: dark),
textSecondary: resolved(.secondaryLabelColor, dark: dark),
textTertiary: resolved(.tertiaryLabelColor, dark: dark),
textOnAccent: Color.white,
textPrimary: resolved(.labelColor, dark: dark),
textSecondary: resolved(.secondaryLabelColor, dark: dark),
textTertiary: resolved(.tertiaryLabelColor, dark: dark),
textOnAccent: Color.white,
divider: dark
divider: dark
? Color.white.opacity(0.08)
: Color.black.opacity(0.06),
dividerStrong: dark
dividerStrong: dark
? Color.white.opacity(0.12)
: Color.black.opacity(0.10),
recordRed: resolved(.systemRed, dark: dark),
recordBlue: resolved(.systemBlue, dark: dark)
recordRed: resolved(.systemRed, dark: dark),
recordBlue: resolved(.systemBlue, dark: dark)
)
}