From 39002336c0f0ea68e0e2ba40fcd6151cf5e3e754 Mon Sep 17 00:00:00 2001 From: Rocky <72559939+hkgood@users.noreply.github.com> Date: Wed, 26 Aug 2026 14:40:06 +0800 Subject: [PATCH] feat(app): add flow diagnostics and refine interface Improve failure recovery visibility, strengthen account-session handling, and make the cross-platform interface more consistent. --- CHANGELOG.md | 8 +- OSGKeyboard/OSGKeyboardApp.swift | 15 +- OSGKeyboard/Services/FlowDiagnostics.swift | 21 + .../Services/FlowFailureLogStore.swift | 271 +++++++++++ .../FlowPictureInPictureController.swift | 52 +- OSGKeyboard/Services/FlowSessionManager.swift | 107 ++++- OSGKeyboard/Views/AIAgentSkillsView.swift | 80 +++- .../AIClipboardSkillLayoutDemoView.swift | 10 +- OSGKeyboard/Views/AIKeyboardDemoView.swift | 2 +- .../AccountAppleAuthorizationButton.swift | 3 +- .../Account/AccountCenterUITestHarness.swift | 14 +- .../Views/Account/AccountCenterView.swift | 103 ++-- OSGKeyboard/Views/Account/AccountModels.swift | 5 +- .../Account/AccountPurchaseHistoryView.swift | 76 ++- .../Views/Account/AccountRewardsCard.swift | 23 +- .../Account/AccountSessionCoordinator.swift | 96 +++- .../Views/Account/LiveAccountServices.swift | 49 +- .../Views/ClipboardHistoryDemoView.swift | 2 +- .../OSGAnimatedMeshBackground.swift | 39 +- .../Components/PrivacyDisclosureViews.swift | 1 + .../Components/WideLayoutComponents.swift | 1 + OSGKeyboard/Views/EditDemoView.swift | 2 +- OSGKeyboard/Views/EnginePickerSection.swift | 2 +- .../Views/FlowDiagnosticsSettingsView.swift | 139 ++++++ OSGKeyboard/Views/HistoryView.swift | 78 ++- OSGKeyboard/Views/HomeView.swift | 39 +- OSGKeyboard/Views/KeyboardPreviewSheet.swift | 3 +- OSGKeyboard/Views/KeyboardPreviewStub.swift | 2 +- OSGKeyboard/Views/MainAppRoot.swift | 34 +- .../Views/OnboardingExperienceView.swift | 446 +++++++++++++++--- .../Views/PersonalDictionaryEntrySheet.swift | 12 +- .../Views/PersonalDictionaryView.swift | 48 +- OSGKeyboard/Views/PolishStylesView.swift | 81 +++- .../Views/ReleaseNotesScreenshotHarness.swift | 86 ++++ OSGKeyboard/Views/ReleaseNotesSheet.swift | 3 +- .../Views/SettingsProviderControls.swift | 9 +- OSGKeyboard/Views/SettingsView.swift | 9 + .../Views/TypingInputSettingsView.swift | 231 +++++---- OSGKeyboard/en.lproj/Localizable.strings | 43 +- OSGKeyboard/zh-Hans.lproj/Localizable.strings | 43 +- OSGKeyboardExt/Typing/TypingRootView.swift | 18 +- OSGKeyboardExt/Views/AIKeyboardView.swift | 16 +- .../Views/ClipboardKeyboardViews.swift | 6 +- .../Views/KeyboardTopControls.swift | 14 +- OSGKeyboardExt/Views/LastInputEditView.swift | 10 +- .../Views/NativeKeyboardKeyStyle.swift | 19 +- .../Views/ToolbarActionButtons.swift | 6 +- .../DesignSystem/MonthlyUsageCalendar.swift | 12 +- .../SupportDeveloperSection.swift | 2 +- .../DesignSystem/UsageStatsCluster.swift | 13 +- .../Features/Account/AccountAPIClient.swift | 25 +- OSGKeyboardMac/MacComponents.swift | 10 +- OSGKeyboardMac/MacDictationOverlayView.swift | 2 +- OSGKeyboardMac/MacDictionaryView.swift | 10 +- .../MacLocalASRModelSettingsView.swift | 2 +- OSGKeyboardMac/MacOnboardingView.swift | 4 +- OSGKeyboardMac/MacPolishStylesView.swift | 2 +- OSGKeyboardMac/MacSettingsComponents.swift | 6 +- .../MacSupportDeveloperTipRows.swift | 2 + OSGKeyboardMac/MacTheme.swift | 31 +- .../DesignSystem/CardPageLayout.swift | 110 ++++- .../DesignSystem/ColorLibrary.swift | 171 +++++++ .../DesignSystem/ProgressTrack.swift | 53 +++ .../DesignSystem/RecordButton.swift | 13 +- OSGKeyboardShared/DesignSystem/Theme.swift | 78 +-- .../DesignSystem/UsageSurfaceCard.swift | 5 +- OSGKeyboardTests/AccountAPIClientTests.swift | 31 ++ .../AccountCenterViewModelTests.swift | 186 +++++++- .../FlowFailureLogStoreTests.swift | 126 +++++ OSGKeyboardTests/ReferralProfileTests.swift | 3 +- .../TestSupport/AccountTestDoubles.swift | 9 +- docs/assets/whats-new/account-sign-in-en.png | Bin 0 -> 57121 bytes docs/assets/whats-new/account-sign-in-zh.png | Bin 0 -> 67059 bytes .../whats-new/personal-style-card-en.png | Bin 0 -> 68463 bytes .../whats-new/personal-style-card-zh.png | Bin 0 -> 63050 bytes .../whats-new/personal-terms-card-en.png | Bin 0 -> 36022 bytes .../whats-new/personal-terms-card-zh.png | Bin 0 -> 32533 bytes docs/osgkeyboardversion.html | 189 +++++--- 78 files changed, 2737 insertions(+), 735 deletions(-) create mode 100644 OSGKeyboard/Services/FlowFailureLogStore.swift create mode 100644 OSGKeyboard/Views/FlowDiagnosticsSettingsView.swift create mode 100644 OSGKeyboard/Views/ReleaseNotesScreenshotHarness.swift create mode 100644 OSGKeyboardShared/DesignSystem/ColorLibrary.swift create mode 100644 OSGKeyboardShared/DesignSystem/ProgressTrack.swift create mode 100644 OSGKeyboardTests/FlowFailureLogStoreTests.swift create mode 100644 docs/assets/whats-new/account-sign-in-en.png create mode 100644 docs/assets/whats-new/account-sign-in-zh.png create mode 100644 docs/assets/whats-new/personal-style-card-en.png create mode 100644 docs/assets/whats-new/personal-style-card-zh.png create mode 100644 docs/assets/whats-new/personal-terms-card-en.png create mode 100644 docs/assets/whats-new/personal-terms-card-zh.png diff --git a/CHANGELOG.md b/CHANGELOG.md index 64aa894..935a096 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,12 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- **Flow startup diagnostics**: save a privacy-safe ten-second state trace whenever session startup reaches a terminal failure, with local export and deletion controls in Settings. / **Flow 启动诊断**:会话启动最终失败时保存隐私安全的最近十秒状态轨迹,并在设置中提供本机导出与清除入口。 + ### Changed +- **Card depth**: add a faint directional long shadow to card surfaces throughout the iOS app in both light and dark appearances. / **卡片层次**:为 iOS App 内的卡片界面统一加入淡雅的定向长投影,并适配浅色与深色外观。 +- **Neutral dialog actions**: use adaptive black or white styling for system alerts, confirmation dialogs, and modal actions instead of inheriting the green brand accent. / **中性弹窗操作色**:系统警告、确认弹窗及模态操作改用自适应黑色或白色,不再继承绿色品牌强调色。 - **Monthly usage calendar**: replace the seven-day Home and Mac Dashboard chart with a full current-month calendar whose date-circle opacity reflects each day's dictation characters, placing the Home calendar between its four metrics and Personal Dictionary. / **月度用量日历**:以完整的当月日历替换首页与 Mac 总览的近七天图表,并通过日期圆形背景透明度表示每天的听写字数;首页日历位于四项统计指标与个性词库之间。 - **Home credits and navigation**: move the credit and invitation card from Settings to Home above the usage metrics, explain free signup and referral rewards before sign-in, and open History or Personal Dictionary directly from their metric cards. / **首页积分与导航**:将积分与邀请卡片从设置迁移到首页统计指标上方,登录前明确说明免费注册与邀请奖励,并支持从听写字数和词库指标卡片直接进入历史记录或个性词库。 - **Personal style threshold**: allow generating a learned speaking style after 2,500 effective dictation characters instead of 5,000. / **专属风格门槛**:生成学习型说话风格所需的有效听写字符由 5,000 降至 2,500。 ### Fixed +- **Durable Apple account sessions**: persist refresh operation identifiers before token rotation, reliably store and validate Apple user identifiers, require one-time reauthentication for unverifiable legacy sessions, and prevent failed Keychain deletion or concurrent account work from restoring a revoked login. / **可靠 Apple 账号会话**:在令牌轮换前持久化刷新操作标识,可靠存储并验证 Apple 用户标识,为无法验证的旧会话执行一次重新认证,并防止 Keychain 删除失败或并发账号操作恢复已撤销的登录态。 +- **Account sign-in presentation**: use the concise reward message consistently and keep a uniform app surface beneath translucent Apple authorization sheets. / **账号登录呈现**:统一使用精简的积分奖励文案,并在半透明 Apple 授权弹窗下保持一致的 App 背景。 - **Recommended hotword details**: label Home recommendations by their actual source, render suggestion chips in adaptive monochrome, remove internal usage counts from dictionary rows, and anchor the iPhone confirmation popover to the selected hotword. / **推荐热词详情**:按真实来源标记首页推荐词,以自适应黑白配色显示推荐标签,移除词库列表中的内部使用次数,并让 iPhone 确认弹窗指向所选热词。 ## [2.0.3] - 2026-08-24 @@ -44,7 +51,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **Conversational skill replies**: make non-business replies sound like an ordinary person talking with friends or colleagues, allow one fitting emoji when emotion clearly calls for it, keep business replies naturally professional, and apply the active user-owned learned style only as a bounded wording-and-rhythm layer. / **口语化技能回复**:非商务回复采用普通人与朋友、好友或同事交谈的自然口吻,情绪明确时可合理使用一个表情;商务回复保持自然专业,当前用户自有的学习风格仅作为受限的用词与节奏层应用。 ### Fixed -- **Durable account sessions**: persist refresh operation identifiers before network rotation, recover safely after crashes or Keychain write failures, and sign out when Apple reports revoked credentials. / **可靠账号会话**:在网络轮换前持久化刷新操作标识,在崩溃或 Keychain 写入失败后安全恢复,并在 Apple 凭据被撤销时退出登录。 - **Personal dictionary aliases**: generate common speech-recognition mistakes for Home term suggestions through the same concurrency-safe save path used by manual entries. / **个性词库易错词**:首页推荐词现在与手动词条共用并发安全的保存流程,并自动生成常见语音误识别写法。 - **Home service setup guidance**: explain whether user-owned API keys are missing, link directly to the relevant provider settings, and offer OSG credits as the no-key alternative. / **首页服务配置引导**:明确说明缺少哪类自备 API Key,直接跳转到对应服务商设置,并提供无需填写 Key 的 OSG 积分方案。 - **Live keyboard configuration guidance**: apply speech-language changes to an already open keyboard and explain when clipboard capture is paused because Full Access is off. / **键盘实时配置引导**:识别语言变更会同步到已打开的键盘,并在“允许完全访问”关闭导致剪贴板采集暂停时给出说明。 diff --git a/OSGKeyboard/OSGKeyboardApp.swift b/OSGKeyboard/OSGKeyboardApp.swift index a67dbdc..d75cac7 100644 --- a/OSGKeyboard/OSGKeyboardApp.swift +++ b/OSGKeyboard/OSGKeyboardApp.swift @@ -4,6 +4,7 @@ import OSGKeyboardHostSupport import OSGKeyboardShared import SwiftUI +import UIKit @main struct OSGKeyboardApp: App { @@ -19,6 +20,11 @@ struct OSGKeyboardApp: App { init() { MaterialIconsFont.registerIfNeeded() + // Keep system alert and confirmation actions neutral instead of + // inheriting the app's green AccentColor. + UIView.appearance( + whenContainedInInstancesOf: [UIAlertController.self] + ).tintColor = .label // Deliberately do NOT prepare Custom LM here. App launch already sits // near ~150 MB RSS in Debug; compiling CLM during onboarding races the // keyboard extension and gets the host jetsammed (signal 9). Warmup @@ -29,7 +35,10 @@ struct OSGKeyboardApp: App { var body: some Scene { WindowGroup { #if DEBUG - if ProcessInfo.processInfo.arguments.contains("--whats-new-host") { + if OnboardingExperienceView.isPreviewEnabled { + OOBEPreviewHarness() + .preferredColorScheme(appearance.colorScheme) + } else if ProcessInfo.processInfo.arguments.contains("--whats-new-host") { // Approach A: Notes-like host only; real keyboard extension overlays it. // Also used by `--keyboard-appear-stress=` (pass both flags). Self.makeWhatsNewHostView() @@ -43,6 +52,10 @@ struct OSGKeyboardApp: App { AssistantKeyboardUITestHarness() } else if ProcessInfo.processInfo.arguments.contains("--account-ui-test") { AccountCenterUITestHarness() + } else if ProcessInfo.processInfo.arguments.contains("--polish-styles-screenshot") { + PolishStylesScreenshotHarness() + } else if ProcessInfo.processInfo.arguments.contains("--home-dictionary-screenshot") { + HomeDictionaryScreenshotHarness() } else if ProcessInfo.processInfo.arguments.contains("--managed-consent-ui-test") { ManagedCloudConsentUITestHarness() } else if ProcessInfo.processInfo.arguments.contains("--pip-device-ui-test") { diff --git a/OSGKeyboard/Services/FlowDiagnostics.swift b/OSGKeyboard/Services/FlowDiagnostics.swift index 264623f..9aa7977 100644 --- a/OSGKeyboard/Services/FlowDiagnostics.swift +++ b/OSGKeyboard/Services/FlowDiagnostics.swift @@ -10,10 +10,31 @@ import OSGKeyboardShared enum FlowDiagnostics { static func log(_ message: String) { + FlowFailureLogStore.shared.record(message) OSGDiag.log(message, category: "flow") } static func logDrain(_ report: FlowCaptureDrainReport) { FlowPipelineDiagnostics.logDrain(report) } + + @discardableResult + static func persistStartupFailure( + reason: String, + context: [String: String] + ) -> URL? { + let url = FlowFailureLogStore.shared.persistStartupFailure( + reason: reason, + context: context + ) + if let url { + OSGDiag.log( + "Flow startup failure report saved file=\(url.lastPathComponent)", + category: "flow" + ) + } else { + OSGDiag.log("Flow startup failure report could not be saved", category: "flow") + } + return url + } } diff --git a/OSGKeyboard/Services/FlowFailureLogStore.swift b/OSGKeyboard/Services/FlowFailureLogStore.swift new file mode 100644 index 0000000..acba6e0 --- /dev/null +++ b/OSGKeyboard/Services/FlowFailureLogStore.swift @@ -0,0 +1,271 @@ +// FlowFailureLogStore.swift +// OSGKeyboard · Main App +// +// Keeps a short in-memory Flow breadcrumb window and writes it only when a +// session start reaches a terminal failure. Reports contain operational state, +// never audio, transcripts, clipboard contents, prompts, or credentials. + +import Foundation + +struct FlowFailureLogEvent: Codable, Equatable, Sendable { + let timestamp: Date + let message: String +} + +struct FlowStartupFailureReport: Codable, Equatable, Sendable { + let schemaVersion: Int + let reportID: UUID + let capturedAt: Date + let windowStartedAt: Date + let reason: String + let context: [String: String] + let appVersion: String + let buildNumber: String + let operatingSystem: String + let events: [FlowFailureLogEvent] +} + +private struct FlowStartupFailureExport: Codable, Sendable { + let schemaVersion: Int + let generatedAt: Date + let reports: [FlowStartupFailureReport] +} + +final class FlowFailureLogStore: @unchecked Sendable { + static let shared = FlowFailureLogStore() + + static let retentionWindow: TimeInterval = 10 + + private let directoryURL: URL + private let maxReportCount: Int + private let maxReportAge: TimeInterval + private let maxTotalBytes: Int + private let now: () -> Date + private let fileManager: FileManager + private let lock = NSLock() + private var events: [FlowFailureLogEvent] = [] + + init( + directoryURL: URL = FlowFailureLogStore.defaultDirectoryURL(), + maxReportCount: Int = 20, + maxReportAge: TimeInterval = 14 * 24 * 60 * 60, + maxTotalBytes: Int = 2 * 1_024 * 1_024, + now: @escaping () -> Date = Date.init, + fileManager: FileManager = .default + ) { + self.directoryURL = directoryURL + self.maxReportCount = maxReportCount + self.maxReportAge = maxReportAge + self.maxTotalBytes = maxTotalBytes + self.now = now + self.fileManager = fileManager + } + + func record(_ message: String) { + let timestamp = now() + let event = FlowFailureLogEvent( + timestamp: timestamp, + message: Self.redact(message) + ) + lock.withLock { + events.append(event) + pruneEvents(referenceDate: timestamp) + } + } + + @discardableResult + func persistStartupFailure( + reason: String, + context: [String: String] + ) -> URL? { + let capturedAt = now() + let capturedEvents = lock.withLock { + pruneEvents(referenceDate: capturedAt) + return events + } + let report = FlowStartupFailureReport( + schemaVersion: 1, + reportID: UUID(), + capturedAt: capturedAt, + windowStartedAt: capturedEvents.first?.timestamp ?? capturedAt, + reason: Self.redact(reason), + context: context.mapValues(Self.redact), + appVersion: Self.bundleValue("CFBundleShortVersionString"), + buildNumber: Self.bundleValue("CFBundleVersion"), + operatingSystem: ProcessInfo.processInfo.operatingSystemVersionString, + events: capturedEvents + ) + + do { + try prepareDirectory() + let fileURL = directoryURL.appendingPathComponent( + Self.reportFilename(date: capturedAt, id: report.reportID), + isDirectory: false + ) + let encoder = JSONEncoder() + encoder.dateEncodingStrategy = .iso8601 + encoder.outputFormatting = [.prettyPrinted, .sortedKeys, .withoutEscapingSlashes] + try encoder.encode(report).write(to: fileURL, options: .atomic) + try? fileManager.setAttributes( + [ + .protectionKey: FileProtectionType.completeUntilFirstUserAuthentication, + .modificationDate: capturedAt + ], + ofItemAtPath: fileURL.path + ) + pruneReports(referenceDate: capturedAt) + return fileURL + } catch { + return nil + } + } + + func reportURLs() -> [URL] { + reportFileURLs().sorted { lhs, rhs in + modificationDate(for: lhs) > modificationDate(for: rhs) + } + } + + func makeExportURL() -> URL? { + let decoder = JSONDecoder() + decoder.dateDecodingStrategy = .iso8601 + let reports = reportURLs().compactMap { url -> FlowStartupFailureReport? in + guard let data = try? Data(contentsOf: url) else { return nil } + return try? decoder.decode(FlowStartupFailureReport.self, from: data) + } + guard !reports.isEmpty else { return nil } + + let export = FlowStartupFailureExport( + schemaVersion: 1, + generatedAt: now(), + reports: reports + ) + do { + try prepareDirectory() + let exportURL = directoryURL.appendingPathComponent( + "flow-startup-diagnostics-export.json", + isDirectory: false + ) + let encoder = JSONEncoder() + encoder.dateEncodingStrategy = .iso8601 + encoder.outputFormatting = [.prettyPrinted, .sortedKeys, .withoutEscapingSlashes] + try encoder.encode(export).write(to: exportURL, options: .atomic) + try? fileManager.setAttributes( + [.protectionKey: FileProtectionType.completeUntilFirstUserAuthentication], + ofItemAtPath: exportURL.path + ) + return exportURL + } catch { + return nil + } + } + + func deleteAllReports() { + for url in reportFileURLs() { + try? fileManager.removeItem(at: url) + } + try? fileManager.removeItem( + at: directoryURL.appendingPathComponent( + "flow-startup-diagnostics-export.json", + isDirectory: false + ) + ) + } + + private func pruneEvents(referenceDate: Date) { + let cutoff = referenceDate.addingTimeInterval(-Self.retentionWindow) + events.removeAll { $0.timestamp < cutoff } + } + + private func prepareDirectory() throws { + try fileManager.createDirectory( + at: directoryURL, + withIntermediateDirectories: true + ) + try? fileManager.setAttributes( + [.protectionKey: FileProtectionType.completeUntilFirstUserAuthentication], + ofItemAtPath: directoryURL.path + ) + } + + private func pruneReports(referenceDate: Date) { + let sorted = reportURLs() + var retainedBytes = 0 + + for (index, url) in sorted.enumerated() { + let values = try? url.resourceValues( + forKeys: [.contentModificationDateKey, .fileSizeKey] + ) + let modifiedAt = values?.contentModificationDate ?? .distantPast + let fileSize = values?.fileSize ?? 0 + let isExpired = referenceDate.timeIntervalSince(modifiedAt) > maxReportAge + let exceedsCount = index >= maxReportCount + let exceedsSize = retainedBytes + fileSize > maxTotalBytes + + if isExpired || exceedsCount || exceedsSize { + try? fileManager.removeItem(at: url) + } else { + retainedBytes += fileSize + } + } + } + + private func reportFileURLs() -> [URL] { + guard let urls = try? fileManager.contentsOfDirectory( + at: directoryURL, + includingPropertiesForKeys: [.contentModificationDateKey, .fileSizeKey], + options: [.skipsHiddenFiles] + ) else { + return [] + } + return urls.filter { + $0.pathExtension == "json" && $0.lastPathComponent.hasPrefix("flow-start-failure-") + } + } + + private func modificationDate(for url: URL) -> Date { + (try? url.resourceValues(forKeys: [.contentModificationDateKey])) + .flatMap(\.contentModificationDate) ?? .distantPast + } + + private static func defaultDirectoryURL() -> URL { + let baseURL = (try? FileManager.default.url( + for: .applicationSupportDirectory, + in: .userDomainMask, + appropriateFor: nil, + create: true + )) ?? FileManager.default.temporaryDirectory + return baseURL.appendingPathComponent("FlowDiagnostics", isDirectory: true) + } + + private static func bundleValue(_ key: String) -> String { + Bundle.main.object(forInfoDictionaryKey: key) as? String ?? "unknown" + } + + private static func reportFilename(date: Date, id: UUID) -> String { + let formatter = ISO8601DateFormatter() + formatter.formatOptions = [.withInternetDateTime] + let timestamp = formatter.string(from: date) + .replacingOccurrences(of: ":", with: "-") + return "flow-start-failure-\(timestamp)-\(id.uuidString.lowercased()).json" + } + + private static func redact(_ value: String) -> String { + var redacted = value.replacingOccurrences( + of: #"[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[1-5][0-9A-Fa-f]{3}-[89ABab][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}"#, + with: "", + options: .regularExpression + ) + redacted = redacted.replacingOccurrences( + of: #"/private/var/mobile/Containers/\S+"#, + with: "", + options: .regularExpression + ) + redacted = redacted.replacingOccurrences( + of: #"(?i)(api[_-]?key|authorization|bearer|access[_-]?token|refresh[_-]?token)=\S+"#, + with: "$1=", + options: .regularExpression + ) + return redacted + } +} diff --git a/OSGKeyboard/Services/FlowPictureInPictureController.swift b/OSGKeyboard/Services/FlowPictureInPictureController.swift index f837187..9353886 100644 --- a/OSGKeyboard/Services/FlowPictureInPictureController.swift +++ b/OSGKeyboard/Services/FlowPictureInPictureController.swift @@ -10,6 +10,7 @@ import AVFoundation import AVKit import CoreMedia import OSGKeyboardHostSupport +import OSGKeyboardShared import UIKit /// Why `startAndWait` could not prove an active PiP window. @@ -174,11 +175,19 @@ final class FlowPictureInPictureController: NSObject, FlowPictureInPictureContro ) async -> FlowPiPStartOutcome { if isPictureInPictureActive { return .started } + FlowDiagnostics.log( + "PiP startAndWait begin generation=\(generation) " + + "hostTimeout=\(String(format: "%.2f", hostTimeout)) " + + "activeTimeout=\(String(format: "%.2f", activeTimeout)) " + + "supported=\(AVPictureInPictureController.isPictureInPictureSupported()) " + + hostDiagnosticTag + ) let deadline = Date().addingTimeInterval(activeTimeout) let supportsPiP = await waitForPictureInPictureSupport( timeout: min(max(0, deadline.timeIntervalSinceNow), 0.8) ) guard supportsPiP else { + FlowDiagnostics.log("PiP unavailable: unsupported \(hostDiagnosticTag)") return .failed(.unsupported) } @@ -186,6 +195,7 @@ final class FlowPictureInPictureController: NSObject, FlowPictureInPictureContro timeout: min(hostTimeout, max(0, deadline.timeIntervalSinceNow)) ) guard hostReady else { + FlowDiagnostics.log("PiP unavailable: hostNotReady \(hostDiagnosticTag)") return .failed(.hostNotReady) } @@ -193,8 +203,12 @@ final class FlowPictureInPictureController: NSObject, FlowPictureInPictureContro guard await prepareControllerForStart() else { stopFramePump() if lastSystemStartFailure != nil { + FlowDiagnostics.log( + "PiP unavailable: systemRejected \(systemFailureDiagnosticTag)" + ) return .failed(.systemRejected) } + FlowDiagnostics.log("PiP unavailable: notPossible \(hostDiagnosticTag)") return .failed(hasHostView ? .notPossible : .hostNotReady) } @@ -212,7 +226,7 @@ final class FlowPictureInPictureController: NSObject, FlowPictureInPictureContro guard pipController?.isPictureInPicturePossible == true else { FlowDiagnostics.log( - "PiP generation \(generation) never became possible" + "PiP generation \(generation) never became possible \(hostDiagnosticTag)" ) return .failed(.notPossible) } @@ -262,7 +276,8 @@ final class FlowPictureInPictureController: NSObject, FlowPictureInPictureContro } FlowDiagnostics.log( "PiP startAndWait failed: \(failure) possible=\(pipController?.isPictureInPicturePossible == true) " - + "requests=\(startRequestCount)" + + "active=\(pipController?.isPictureInPictureActive == true) " + + "requests=\(startRequestCount) \(systemFailureDiagnosticTag)" ) return .failed(failure) } @@ -403,6 +418,26 @@ final class FlowPictureInPictureController: NSObject, FlowPictureInPictureContro return isHostInWindowHierarchy } + private var hostDiagnosticTag: String { + guard let hostView else { + return "hostAttached=0 hostWindow=0 hostBounds=nil hostAlpha=nil" + } + return String( + format: "hostAttached=1 hostWindow=%d hostBounds=%.1fx%.1f hostAlpha=%.3f", + hostView.window == nil ? 0 : 1, + hostView.bounds.width, + hostView.bounds.height, + hostView.alpha + ) + } + + private var systemFailureDiagnosticTag: String { + guard let error = lastSystemStartFailure as NSError? else { + return "systemError=nil" + } + return "systemErrorDomain=\(error.domain) systemErrorCode=\(error.code)" + } + private func configureControllerIfNeeded() { guard pipController == nil else { return } guard AVPictureInPictureController.isPictureInPictureSupported() else { return } @@ -604,7 +639,7 @@ final class FlowPictureInPictureController: NSObject, FlowPictureInPictureContro let phoneInset = CGFloat(22) let phoneRect = canvas.insetBy(dx: phoneInset, dy: phoneInset) let phonePath = UIBezierPath(roundedRect: phoneRect, cornerRadius: 28) - context.setStrokeColor(UIColor(red: 0.898, green: 0.906, blue: 0.922, alpha: 1).cgColor) + context.setStrokeColor(UIColor(OSGColor.pictureInPictureOutline).cgColor) context.setLineWidth(2.5) context.addPath(phonePath.cgPath) context.strokePath() @@ -639,11 +674,11 @@ final class FlowPictureInPictureController: NSObject, FlowPictureInPictureContro private func drawLogoCard(in context: CGContext, rect: CGRect, tuckProgress: CGFloat) { let cardPath = UIBezierPath(roundedRect: rect, cornerRadius: 16) - context.setFillColor(UIColor(red: 0.20, green: 0.78, blue: 0.55, alpha: 1).cgColor) + context.setFillColor(UIColor(OSGColor.pictureInPictureAccent).cgColor) context.addPath(cardPath.cgPath) context.fillPath() - context.setStrokeColor(UIColor(red: 0.20, green: 0.78, blue: 0.55, alpha: 1).cgColor) + context.setStrokeColor(UIColor(OSGColor.pictureInPictureAccent).cgColor) context.setLineWidth(1.5) context.addPath(cardPath.cgPath) context.strokePath() @@ -751,6 +786,7 @@ extension FlowPictureInPictureController: @preconcurrency AVPictureInPictureCont guard pictureInPictureController === pipController else { return } isPictureInPictureActive = true lastSystemStartFailure = nil + FlowDiagnostics.log("PiP delegate didStart generation=\(generation)") releaseAudioSessionForLowPowerPiP() } @@ -772,7 +808,11 @@ extension FlowPictureInPictureController: @preconcurrency AVPictureInPictureCont // Sample-buffer fallback may need warm-up retries. VideoCall PiP uses // the automatic-inline path plus the bounded startAndWait fallback. lastSystemStartFailure = error - FlowDiagnostics.log("PiP start attempt failed (will retry): \(error.localizedDescription)") + let error = error as NSError + FlowDiagnostics.log( + "PiP start attempt failed (will retry): " + + "domain=\(error.domain) code=\(error.code)" + ) } func pictureInPictureController( diff --git a/OSGKeyboard/Services/FlowSessionManager.swift b/OSGKeyboard/Services/FlowSessionManager.swift index f0a2757..7293f7f 100644 --- a/OSGKeyboard/Services/FlowSessionManager.swift +++ b/OSGKeyboard/Services/FlowSessionManager.swift @@ -302,6 +302,14 @@ final class FlowSessionManager: ObservableObject { ) guard AppGroup.isAvailable else { debug("cannot start flow session: App Group unavailable") + FlowDiagnostics.persistStartupFailure( + reason: "appGroupUnavailable", + context: startupFailureContext( + trigger: reason, + attemptCount: 0, + elapsed: 0 + ) + ) return } @@ -396,20 +404,35 @@ final class FlowSessionManager: ObservableObject { reason: String = "unspecified", startCapture: Bool = false ) { - OSGDiag.log( + FlowDiagnostics.log( "activateOnForeground reason=\(reason) startCapture=\(startCapture) " + "onboardingDone=\(ProviderConfig.shared.hasCompletedOnboarding) " - + "\(OSGDiag.memoryTag())", - category: "flow" + + "\(OSGDiag.memoryTag())" ) guard AppGroup.isAvailable else { - OSGDiag.log("activateOnForeground aborted reason=appGroupUnavailable", category: "flow") + FlowDiagnostics.log("activateOnForeground aborted reason=appGroupUnavailable") + FlowDiagnostics.persistStartupFailure( + reason: "appGroupUnavailable", + context: startupFailureContext( + trigger: "activateOnForeground:\(reason)", + attemptCount: 0, + elapsed: 0 + ) + ) return } guard AppPermissions.flowRequirementsMet else { - OSGDiag.log("activateOnForeground aborted reason=permissions", category: "flow") + FlowDiagnostics.log("activateOnForeground aborted reason=permissions") sessionWarning = permissionWarningMessage() FlowSessionBridge.setHostReady(false) + FlowDiagnostics.persistStartupFailure( + reason: "permissionsMissing", + context: startupFailureContext( + trigger: "activateOnForeground:\(reason)", + attemptCount: 0, + elapsed: 0 + ) + ) return } @@ -422,10 +445,30 @@ final class FlowSessionManager: ObservableObject { /// User-requested recovery always discards the rejected AVKit generation. func retryPiPRecovery() { - guard AppGroup.isAvailable else { return } + guard AppGroup.isAvailable else { + FlowDiagnostics.log("manual PiP recovery aborted reason=appGroupUnavailable") + FlowDiagnostics.persistStartupFailure( + reason: "appGroupUnavailable", + context: startupFailureContext( + trigger: FlowPiPActivationTrigger.manualRetry.rawValue, + attemptCount: 0, + elapsed: 0 + ) + ) + return + } guard AppPermissions.flowRequirementsMet else { sessionWarning = permissionWarningMessage() FlowSessionBridge.setHostReady(false) + FlowDiagnostics.log("manual PiP recovery aborted reason=permissions") + FlowDiagnostics.persistStartupFailure( + reason: "permissionsMissing", + context: startupFailureContext( + trigger: FlowPiPActivationTrigger.manualRetry.rawValue, + attemptCount: 0, + elapsed: 0 + ) + ) return } wantsActiveSession = true @@ -933,6 +976,7 @@ final class FlowSessionManager: ObservableObject { guard !Task.isCancelled else { return } traceState("startSessionAsync.begin", extra: "trigger=\(trigger.rawValue)") sessionWarning = nil + let recoveryStartedAt = Date() guard AppPermissions.flowRequirementsMet else { sessionWarning = permissionWarningMessage() @@ -940,11 +984,20 @@ final class FlowSessionManager: ObservableObject { traceState("startSessionAsync.blocked", extra: "reason=permissions") FlowSessionBridge.setHostReady(false) isColdStartHandoff = false + FlowDiagnostics.persistStartupFailure( + reason: "permissionsMissing", + context: startupFailureContext( + trigger: trigger.rawValue, + attemptCount: 0, + elapsed: Date().timeIntervalSince(recoveryStartedAt) + ) + ) return } let deadline = Date().addingTimeInterval(FlowPiPRecoveryPolicy.totalBudget) var lastFailure: FlowPiPStartFailure = .timedOut + var attemptsPerformed = 0 for attempt in 1...FlowPiPRecoveryPolicy.maxAttempts { guard FlowPiPRecoveryPolicy.canContinue( @@ -984,6 +1037,7 @@ final class FlowSessionManager: ObservableObject { let remaining = deadline.timeIntervalSinceNow guard remaining > 0 else { break } + attemptsPerformed = attempt pipLifecycleState = isActive ? .recovering(attempt: attempt, total: FlowPiPRecoveryPolicy.maxAttempts) : .preparing(attempt: attempt, total: FlowPiPRecoveryPolicy.maxAttempts) @@ -1001,6 +1055,8 @@ final class FlowSessionManager: ObservableObject { remainingBudget: deadline.timeIntervalSinceNow ) ) + let recoveryElapsed = FlowPiPRecoveryPolicy.totalBudget + - max(0, deadline.timeIntervalSinceNow) guard FlowPiPRecoveryPolicy.canContinue( operation: operation, currentOperation: pipRecoveryOperation, @@ -1029,7 +1085,9 @@ final class FlowSessionManager: ObservableObject { lastFailure = failure traceState( "startSessionAsync.failed", - extra: "reason=pipUnavailable trigger=\(trigger.rawValue) attempt=\(attempt) failure=\(failure)" + extra: "reason=pipUnavailable trigger=\(trigger.rawValue) " + + "attempt=\(attempt) failure=\(failure) " + + "elapsed=\(String(format: "%.2f", recoveryElapsed))" ) } } @@ -1040,6 +1098,14 @@ final class FlowSessionManager: ObservableObject { FlowSessionBridge.setHostReady(false) refreshHostReady() debug("PiP keep-alive failed after bounded recovery: \(lastFailure)") + FlowDiagnostics.persistStartupFailure( + reason: String(describing: lastFailure), + context: startupFailureContext( + trigger: trigger.rawValue, + attemptCount: attemptsPerformed, + elapsed: Date().timeIntervalSince(recoveryStartedAt) + ) + ) } private func activateFlowSessionAfterPiPProof(duration: TimeInterval?) { @@ -3539,6 +3605,33 @@ final class FlowSessionManager: ObservableObject { FlowDiagnostics.log(message) } + private func startupFailureContext( + trigger: String, + attemptCount: Int, + elapsed: TimeInterval + ) -> [String: String] { + let memory = OSGDiag.memorySnapshot() + return [ + "trigger": trigger, + "attemptCount": "\(attemptCount)", + "elapsedSeconds": String(format: "%.3f", elapsed), + "appForeground": isAppForeground ? "true" : "false", + "sessionActive": isActive ? "true" : "false", + "sessionIntent": wantsActiveSession ? "true" : "false", + "pipLifecycleState": String(describing: pipLifecycleState), + "pipGeneration": "\(pipController.generation)", + "pipActive": pipController.isPictureInPictureActive ? "true" : "false", + "hostReady": FlowSessionBridge.isHostReady() ? "true" : "false", + "bridgeSessionActive": FlowSessionBridge.isSessionActive() ? "true" : "false", + "heartbeatStalenessSeconds": FlowSessionBridge.heartbeatStaleness() + .map { String(format: "%.3f", $0) } ?? "nil", + "microphonePermission": String(describing: AppPermissions.micStatus), + "speechPermission": String(describing: AppPermissions.speechStatus), + "rssMB": String(format: "%.1f", memory.rssMB), + "physicalFootprintMB": String(format: "%.1f", memory.physFootprintMB) + ] + } + private static func safeErrorLogMetadata(_ error: Error) -> String { "errorCategory=\(String(reflecting: type(of: error))) " + "errorBytes=\(error.localizedDescription.utf8.count)" diff --git a/OSGKeyboard/Views/AIAgentSkillsView.swift b/OSGKeyboard/Views/AIAgentSkillsView.swift index c0e9e9c..18fdb8e 100644 --- a/OSGKeyboard/Views/AIAgentSkillsView.swift +++ b/OSGKeyboard/Views/AIAgentSkillsView.swift @@ -67,6 +67,7 @@ struct AIAgentSkillsView: View { } label: { Image(systemName: "plus") } + .tint(palette.textPrimary) .accessibilityLabel(Text("skills.add")) } } @@ -141,6 +142,7 @@ struct AIAgentSkillsView: View { .frame(maxWidth: .infinity, alignment: .leading) .padding(Spacing.md) .background(palette.surface, in: skillCardShape) + .cardElevation() .accessibilityIdentifier("skills.clipboard.guide") } @@ -367,6 +369,7 @@ struct AIAgentSkillsView: View { .padding(.vertical, Spacing.xs) .background(palette.surface, in: skillCardShape) .clipShape(skillCardShape) + .cardElevation() .contentShape(Rectangle()) } @@ -600,7 +603,10 @@ private struct SkillDetailView: View { .textSelection(.enabled) .frame(maxWidth: .infinity, alignment: .leading) .padding(Spacing.md) - .surfaceCard() + .background( + palette.surface, + in: RoundedRectangle(cornerRadius: Radius.xl, style: .continuous) + ) } } @@ -731,11 +737,15 @@ private struct SkillEditorSheet: View { Form { Section("skills.editor.name") { TextField("skills.editor.namePlaceholder", text: $name) + .settingsListRow() + .cardListRow(elevated: false) } Section("skills.editor.summary") { TextField("skills.editor.summaryPlaceholder", text: $summary, axis: .vertical) .lineLimit(6...12) .frame(minHeight: 108, alignment: .top) + .settingsListRow(alignment: .topLeading) + .cardListRow(elevated: false) } Section("skills.editor.icon") { Button { @@ -743,11 +753,12 @@ private struct SkillEditorSheet: View { } label: { HStack(spacing: Spacing.md) { Image(systemName: systemImage) - .font(.system(size: 22, weight: .semibold)) - .foregroundStyle(palette.accent) - .frame(width: 44, height: 44) + .font(.system(size: 16, weight: .semibold)) + .foregroundStyle(palette.textPrimary) + .symbolRenderingMode(.monochrome) + .frame(width: 38, height: 38) .background( - palette.accentMuted, + palette.textPrimary.opacity(0.08), in: RoundedRectangle(cornerRadius: Radius.medium, style: .continuous) ) VStack(alignment: .leading, spacing: 2) { @@ -764,11 +775,16 @@ private struct SkillEditorSheet: View { .foregroundStyle(palette.textTertiary) } } + .buttonStyle(.plain) + .settingsListRow() + .cardListRow(elevated: false) } Section { TextEditor(text: $prompt) .font(.body.monospaced()) .frame(minHeight: 180) + .padding(Spacing.md) + .cardListRow(elevated: false) } header: { HStack { Text("skills.editor.prompt") @@ -782,20 +798,33 @@ private struct SkillEditorSheet: View { } } Section { - TextField("skills.editor.linkPlaceholder", text: $shortcutLink) - .textInputAutocapitalization(.never) - .keyboardType(.URL) - .autocorrectionDisabled() - TextField("skills.editor.shortcutNamePlaceholder", text: $shortcutName) - if isLookingUp { - Text("skills.editor.lookingUp") - .font(TypeStyle.caption) - .foregroundStyle(palette.textTertiary) - } else if let lookupMessage { - Text(lookupMessage) - .font(TypeStyle.caption) - .foregroundStyle(palette.textTertiary) + VStack(spacing: 0) { + TextField("skills.editor.linkPlaceholder", text: $shortcutLink) + .textInputAutocapitalization(.never) + .keyboardType(.URL) + .autocorrectionDisabled() + .settingsListRow() + + Divider().background(palette.divider) + + TextField("skills.editor.shortcutNamePlaceholder", text: $shortcutName) + .settingsListRow() + + if isLookingUp { + Divider().background(palette.divider) + Text("skills.editor.lookingUp") + .font(TypeStyle.caption) + .foregroundStyle(palette.textTertiary) + .settingsListRow() + } else if let lookupMessage { + Divider().background(palette.divider) + Text(lookupMessage) + .font(TypeStyle.caption) + .foregroundStyle(palette.textTertiary) + .settingsListRow() + } } + .cardListRow(elevated: false) } header: { Text("skills.editor.shortcut") } footer: { @@ -803,6 +832,8 @@ private struct SkillEditorSheet: View { } Section { Toggle("skills.editor.thinking", isOn: $thinkingEnabled) + .settingsListRow() + .cardListRow(elevated: false) } footer: { Text("skills.editor.thinkingHint") } @@ -811,6 +842,8 @@ private struct SkillEditorSheet: View { Button("common.delete", role: .destructive) { confirmDelete = true } + .settingsListRow() + .cardListRow(elevated: false) } } } @@ -819,6 +852,7 @@ private struct SkillEditorSheet: View { .toolbar { ToolbarItem(placement: .cancellationAction) { Button("common.cancel") { dismiss() } + .tint(palette.textPrimary) } ToolbarItem(placement: .confirmationAction) { Button("common.save") { @@ -830,6 +864,7 @@ private struct SkillEditorSheet: View { } } .disabled(!canSave) + .tint(palette.textPrimary) } } .sheet(isPresented: $showSymbolPicker) { @@ -970,12 +1005,12 @@ private struct SkillSymbolPicker: View { } label: { Image(systemName: symbol) .font(.system(size: 18, weight: .medium)) - .foregroundStyle( - symbol == selection ? palette.textOnAccent : palette.textPrimary - ) + .foregroundStyle(palette.textPrimary) .frame(width: 44, height: 44) .background( - symbol == selection ? palette.accent : palette.surfaceElevated, + symbol == selection + ? palette.textPrimary.opacity(0.12) + : palette.surfaceElevated, in: RoundedRectangle(cornerRadius: 8, style: .continuous) ) } @@ -993,6 +1028,7 @@ private struct SkillSymbolPicker: View { .toolbar { ToolbarItem(placement: .confirmationAction) { Button("common.done") { dismiss() } + .tint(palette.textPrimary) } } } diff --git a/OSGKeyboard/Views/AIClipboardSkillLayoutDemoView.swift b/OSGKeyboard/Views/AIClipboardSkillLayoutDemoView.swift index d5220b6..fd1a5d1 100644 --- a/OSGKeyboard/Views/AIClipboardSkillLayoutDemoView.swift +++ b/OSGKeyboard/Views/AIClipboardSkillLayoutDemoView.swift @@ -32,7 +32,7 @@ struct AIClipboardSkillLayoutDemoView: View { ) .background(Palette.light.background) } - .background(Color(red: 0.06, green: 0.06, blue: 0.07).ignoresSafeArea()) + .background(OSGColor.demoBackground.ignoresSafeArea()) .environment(\.locale, language == .en ? Locale(identifier: "en") : Locale(identifier: "zh-Hans")) .preferredColorScheme(.light) .task { await runTimeline() } @@ -48,7 +48,7 @@ struct AIClipboardSkillLayoutDemoView: View { VStack(spacing: 10) { Text("技能布局预览") .font(.headline) - .foregroundStyle(.white) + .foregroundStyle(OSGColor.fixedLightContent) HStack(spacing: 16) { Button { count = max(1, count - 1) @@ -58,7 +58,7 @@ struct AIClipboardSkillLayoutDemoView: View { } Text("\(count) 个") .font(.title2.monospacedDigit().weight(.semibold)) - .foregroundStyle(.white) + .foregroundStyle(OSGColor.fixedLightContent) .frame(minWidth: 72) Button { count = min(AIClipboardSkillCatalog.catalog.count, count + 1) @@ -67,14 +67,14 @@ struct AIClipboardSkillLayoutDemoView: View { .font(.system(size: 28)) } } - .foregroundStyle(.green) + .foregroundStyle(OSGColor.brandAccent) HStack(spacing: 8) { ForEach([3, 8, AIClipboardSkillCatalog.catalog.count], id: \.self) { n in Button("\(n)") { count = n } .buttonStyle(.borderedProminent) - .tint(count == n ? .green : .gray) + .tint(count == n ? OSGColor.brandAccent : OSGColor.demoSelectionInactive) } } } diff --git a/OSGKeyboard/Views/AIKeyboardDemoView.swift b/OSGKeyboard/Views/AIKeyboardDemoView.swift index fcde759..20d333a 100644 --- a/OSGKeyboard/Views/AIKeyboardDemoView.swift +++ b/OSGKeyboard/Views/AIKeyboardDemoView.swift @@ -29,7 +29,7 @@ struct AIKeyboardDemoView: View { var body: some View { ZStack { - Color(red: 0.06, green: 0.06, blue: 0.07).ignoresSafeArea() + OSGColor.demoBackground.ignoresSafeArea() // Both scenes sit on the bottom band so what's-new crop matches Ext chrome. VStack(spacing: 0) { Spacer(minLength: 0) diff --git a/OSGKeyboard/Views/Account/AccountAppleAuthorizationButton.swift b/OSGKeyboard/Views/Account/AccountAppleAuthorizationButton.swift index 8e41804..0ae1187 100644 --- a/OSGKeyboard/Views/Account/AccountAppleAuthorizationButton.swift +++ b/OSGKeyboard/Views/Account/AccountAppleAuthorizationButton.swift @@ -152,7 +152,8 @@ private enum AccountAppleAuthorizationPayload { let identityToken = String(data: identityTokenData, encoding: .utf8), let authorizationCode = String(data: authorizationCodeData, encoding: .utf8), !identityToken.isEmpty, - !authorizationCode.isEmpty + !authorizationCode.isEmpty, + !credential.user.isEmpty else { return nil } diff --git a/OSGKeyboard/Views/Account/AccountCenterUITestHarness.swift b/OSGKeyboard/Views/Account/AccountCenterUITestHarness.swift index d9b7f4b..9e1a0de 100644 --- a/OSGKeyboard/Views/Account/AccountCenterUITestHarness.swift +++ b/OSGKeyboard/Views/Account/AccountCenterUITestHarness.swift @@ -13,16 +13,22 @@ import SwiftUI struct AccountCenterUITestHarness: View { @StateObject private var coordinator: AccountSessionCoordinator private let startsSignIn: Bool + private let screenshotLanguage: AppUILanguage? init() { + let arguments = ProcessInfo.processInfo.arguments let startsSignIn = ProcessInfo.processInfo.arguments.contains( "--account-signin-loading" ) + let staysSignedOut = arguments.contains("--account-signed-out") + screenshotLanguage = arguments.contains("--screenshot-lang=en") + ? .english + : arguments.contains("--screenshot-lang=zh") ? .chinese : nil let service = AccountCenterUITestService( - shouldFailSnapshot: ProcessInfo.processInfo.arguments.contains( + shouldFailSnapshot: arguments.contains( "--account-snapshot-failure" ), - startsSignedOut: startsSignIn, + startsSignedOut: startsSignIn || staysSignedOut, signInDelay: startsSignIn ? .seconds(5) : .zero ) self.startsSignIn = startsSignIn @@ -46,6 +52,7 @@ struct AccountCenterUITestHarness: View { .environmentObject(coordinator) } } + .environment(\.locale, screenshotLanguage?.swiftUILocale ?? Locale.current) .task { await coordinator.restoreIfNeeded() if startsSignIn { @@ -53,7 +60,8 @@ struct AccountCenterUITestHarness: View { with: AppleAuthorizationPayload( identityToken: "ui-test-identity", authorizationCode: "ui-test-authorization", - nonce: "ui-test-nonce" + nonce: "ui-test-nonce", + userIdentifier: "ui-test-apple-user" ) ) } diff --git a/OSGKeyboard/Views/Account/AccountCenterView.swift b/OSGKeyboard/Views/Account/AccountCenterView.swift index 50c215c..0a26d8d 100644 --- a/OSGKeyboard/Views/Account/AccountCenterView.swift +++ b/OSGKeyboard/Views/Account/AccountCenterView.swift @@ -21,11 +21,17 @@ struct AccountCenterView: View { var body: some View { ZStack { + // AuthenticationServices uses translucent system chrome on + // iOS 26. Keep its underlying surface uniform without covering + // the account content that remains visible below the system sheet. palette.background.ignoresSafeArea() sessionContent } + .background(palette.background) .navigationTitle("account.title") .navigationBarTitleDisplayMode(.inline) + .toolbarBackground(palette.background, for: .navigationBar) + .toolbarBackground(.visible, for: .navigationBar) .task(id: coordinator.accountID) { guard let accountID = coordinator.accountID else { coordinator.creditPurchases.reset() @@ -42,18 +48,6 @@ struct AccountCenterView: View { .onDisappear { coordinator.creditPurchases.dismissSuccessMessage() } - .alert( - "account.error.title", - isPresented: operationErrorBinding - ) { - Button("common.done") { - coordinator.dismissOperationError() - } - } message: { - if let key = coordinator.operationErrorKey { - Text(LocalizedStringKey(key)) - } - } .alert( "account.profile.editTitle", isPresented: $showProfileEditor @@ -62,12 +56,14 @@ struct AccountCenterView: View { .textInputAutocapitalization(.words) .autocorrectionDisabled() Button("common.cancel", role: .cancel) {} + .tint(palette.textPrimary) Button("account.profile.save") { if case let .signedIn(session) = coordinator.sessionPhase { saveDisplayName(session: session) } } .disabled(!canSaveCurrentDisplayName) + .tint(palette.textPrimary) } message: { Text("account.profile.editMessage") } @@ -132,6 +128,8 @@ struct AccountCenterView: View { .surfaceCard() } } + .scrollContentBackground(.hidden) + .background(palette.background) } private func signedInContent(session: AccountSession) -> some View { @@ -143,6 +141,8 @@ struct AccountCenterView: View { } .padding(.bottom, Spacing.lg) } + .scrollContentBackground(.hidden) + .background(palette.background) .refreshable { async let accountRefresh: Void = coordinator.refreshAccountData(force: true) async let catalogRefresh: Void = @@ -173,6 +173,16 @@ struct AccountCenterView: View { EmptyView() } + if let errorKey = coordinator.accountRefreshErrorKey { + AccountStateCard( + systemImage: "exclamationmark.triangle", + titleKey: errorKey, + actionKey: "account.retry" + ) { + Task { await coordinator.refreshAccountData(force: true) } + } + } + CardSection("account.storekit.section") { VStack(spacing: Spacing.sm) { AccountCreditPurchaseSection( @@ -257,7 +267,7 @@ struct AccountCenterView: View { .font(.system(size: 16, weight: .semibold)) } } - .foregroundStyle(palette.accent) + .foregroundStyle(palette.textPrimary) .frame(width: 40, height: 40) .background(palette.surfaceElevated, in: Circle()) } @@ -267,6 +277,7 @@ struct AccountCenterView: View { } .padding(Spacing.lg) .background(palette.surface, in: shape) + .cardElevation() .accessibilityIdentifier("account.summary") } @@ -378,7 +389,7 @@ struct AccountCenterView: View { if coordinator.operation == .deletingAccount { ProgressView("account.delete.loading") - .tint(palette.accent) + .tint(palette.textPrimary) .frame(maxWidth: .infinity) } @@ -393,22 +404,16 @@ struct AccountCenterView: View { Button("common.cancel") { showDeleteReauthentication = false } + .tint(palette.textPrimary) } } } + .toolbarBackground(palette.background, for: .navigationBar) + .toolbarBackground(.visible, for: .navigationBar) .presentationDetents([.medium]) + .presentationBackground(palette.background) } - private var operationErrorBinding: Binding { - Binding( - get: { coordinator.operationErrorKey != nil }, - set: { isPresented in - if !isPresented { - coordinator.dismissOperationError() - } - } - ) - } } struct AccountAvatarView: View { @@ -416,28 +421,20 @@ struct AccountAvatarView: View { let displayName: String? let size: CGFloat - private static let gradients: [[Color]] = [ - [.indigo, .blue], - [.purple, .pink], - [.teal, .cyan], - [.orange, .red], - [.mint, .green] - ] - var body: some View { Text(initials) .font(.system(size: size * 0.34, weight: .bold, design: .rounded)) - .foregroundStyle(.white) + .foregroundStyle(OSGColor.fixedLightContent) .frame(width: size, height: size) .background( LinearGradient( - colors: Self.gradients[gradientIndex], + colors: OSGColor.accountAvatarGradients[gradientIndex], startPoint: .topLeading, endPoint: .bottomTrailing ), in: Circle() ) - .overlay(Circle().stroke(.white.opacity(0.2), lineWidth: 0.5)) + .overlay(Circle().stroke(OSGColor.accountAvatarOutline, lineWidth: 0.5)) .accessibilityHidden(true) } @@ -453,7 +450,7 @@ struct AccountAvatarView: View { let hash = accountID.uuidString.unicodeScalars.reduce(UInt32(2_166_136_261)) { ($0 ^ $1.value) &* 16_777_619 } - return Int(hash % UInt32(Self.gradients.count)) + return Int(hash % UInt32(OSGColor.accountAvatarGradients.count)) } } @@ -578,13 +575,13 @@ private struct AccountCreditPurchaseSection: View { if purchasingProductID == option.productID { ProgressView() .controlSize(.small) - .tint(.white) + .tint(OSGColor.fixedLightContent) } else { Text("account.storekit.purchase") .font(TypeStyle.caption.weight(.semibold)) } } - .foregroundStyle(.white) + .foregroundStyle(OSGColor.fixedLightContent) .padding(.horizontal, Spacing.sm) .frame(minWidth: 54, minHeight: 34) .background( @@ -717,22 +714,18 @@ struct AccountCreditProgress: View { var body: some View { VStack(spacing: Spacing.xs) { - GeometryReader { proxy in - ZStack(alignment: .leading) { - Capsule() - .fill(palette.surfaceElevated) - HStack(spacing: 0) { - Rectangle() - .fill(palette.success) - .frame(width: proxy.size.width * remainingFraction) - Rectangle() - .fill(palette.textTertiary.opacity(0.55)) - .frame(width: proxy.size.width * usedFraction) - } - .clipShape(Capsule()) - } - } - .frame(height: 8) + ProgressTrack( + segments: [ + ProgressTrackSegment( + fraction: remainingFraction, + color: palette.success + ), + ProgressTrackSegment( + fraction: usedFraction, + color: palette.textTertiary.opacity(0.32) + ) + ] + ) if showsLabels { HStack(spacing: Spacing.sm) { @@ -787,7 +780,7 @@ struct AccountInvitationButton: View { } label: { Label("account.referral.inviteTitle", systemImage: "person.badge.plus") .font(TypeStyle.bodyEmph) - .foregroundStyle(.white) + .foregroundStyle(OSGColor.fixedLightContent) .padding(.horizontal, Spacing.sm) .frame(minHeight: 38) .background( diff --git a/OSGKeyboard/Views/Account/AccountModels.swift b/OSGKeyboard/Views/Account/AccountModels.swift index 93a6041..62d7717 100644 --- a/OSGKeyboard/Views/Account/AccountModels.swift +++ b/OSGKeyboard/Views/Account/AccountModels.swift @@ -245,14 +245,14 @@ struct AppleAuthorizationPayload: Equatable, Sendable { let authorizationCode: String let nonce: String let displayName: String? - let userIdentifier: String? + let userIdentifier: String init( identityToken: String, authorizationCode: String, nonce: String, displayName: String? = nil, - userIdentifier: String? = nil + userIdentifier: String ) { self.identityToken = identityToken self.authorizationCode = authorizationCode @@ -265,6 +265,7 @@ struct AppleAuthorizationPayload: Equatable, Sendable { enum AccountAppleCredentialState: Equatable, Sendable { case authorized case revoked + case reauthenticationRequired case unknown } diff --git a/OSGKeyboard/Views/Account/AccountPurchaseHistoryView.swift b/OSGKeyboard/Views/Account/AccountPurchaseHistoryView.swift index 611fc05..8305962 100644 --- a/OSGKeyboard/Views/Account/AccountPurchaseHistoryView.swift +++ b/OSGKeyboard/Views/Account/AccountPurchaseHistoryView.swift @@ -68,50 +68,48 @@ struct AccountPurchaseHistoryView: View { private func purchaseList( _ records: [AccountCreditPurchaseRecord] ) -> some View { - List { - ForEach(records) { record in - purchaseRow(record) - .listRowBackground(palette.surface) - .listRowInsets( - EdgeInsets( - top: 0, - leading: Spacing.md, - bottom: 0, - trailing: Spacing.md - ) - ) - .listRowSeparatorTint(palette.divider) - .task { - guard record.id == records.last?.id else { return } - await manager.loadNextPurchaseHistoryPage(accountID: accountID) - } - } + ScrollView { + LazyVStack(spacing: CardLayoutMetrics.compactItemSpacing) { + ForEach(records) { record in + purchaseRow(record) + .padding(.horizontal, Spacing.md) + .surfaceCard() + .task { + guard record.id == records.last?.id else { return } + await manager.loadNextPurchaseHistoryPage(accountID: accountID) + } + } - if manager.isLoadingMoreHistory { - HStack { - Spacer() - ProgressView() - .tint(palette.accent) - Spacer() - } - .listRowBackground(palette.surface) - } else if let errorKey = manager.historyLoadMoreErrorKey { - Button { - Task { - await manager.loadNextPurchaseHistoryPage(accountID: accountID) + if manager.isLoadingMoreHistory { + HStack { + Spacer() + ProgressView() + .tint(palette.accent) + Spacer() } - } label: { - Text(LocalizedStringKey(errorKey)) - .font(TypeStyle.caption) - .foregroundStyle(palette.accent) - .frame(maxWidth: .infinity) + .settingsListRow() + .surfaceCard() + } else if let errorKey = manager.historyLoadMoreErrorKey { + Button { + Task { + await manager.loadNextPurchaseHistoryPage(accountID: accountID) + } + } label: { + Text(LocalizedStringKey(errorKey)) + .font(TypeStyle.caption) + .foregroundStyle(palette.accent) + .frame(maxWidth: .infinity) + } + .buttonStyle(.plain) + .settingsListRow() + .surfaceCard() } - .buttonStyle(.plain) - .listRowBackground(palette.surface) } + .padding(.horizontal, Spacing.lg) + .padding(.top, Spacing.md) + .padding(.bottom, Spacing.xxl) } - .listStyle(.insetGrouped) - .scrollContentBackground(.hidden) + .scrollClipDisabled() .background(palette.background) .accessibilityIdentifier("account.purchaseHistory.list") .refreshable { diff --git a/OSGKeyboard/Views/Account/AccountRewardsCard.swift b/OSGKeyboard/Views/Account/AccountRewardsCard.swift index 3ad6ca7..d6bf760 100644 --- a/OSGKeyboard/Views/Account/AccountRewardsCard.swift +++ b/OSGKeyboard/Views/Account/AccountRewardsCard.swift @@ -31,15 +31,6 @@ struct AccountRewardsCard: View { guard coordinator.isSignedIn else { return } await coordinator.refreshAccountData() } - .alert("account.error.title", isPresented: operationErrorBinding) { - Button("common.done") { - coordinator.dismissOperationError() - } - } message: { - if let key = coordinator.operationErrorKey { - Text(LocalizedStringKey(key)) - } - } } private var signedOutContent: some View { @@ -59,10 +50,10 @@ struct AccountRewardsCard: View { ) VStack(alignment: .leading, spacing: Spacing.xxs) { - Text("home.accountRewards.signedOut.title") + Text("account.signedOut.title") .font(TypeStyle.headline) .foregroundStyle(palette.textPrimary) - Text("home.accountRewards.signedOut.summary") + Text("account.signedOut.body") .font(TypeStyle.footnote) .foregroundStyle(palette.textSecondary) .fixedSize(horizontal: false, vertical: true) @@ -189,16 +180,6 @@ struct AccountRewardsCard: View { ) } - private var operationErrorBinding: Binding { - Binding( - get: { coordinator.operationErrorKey != nil }, - set: { isPresented in - if !isPresented { - coordinator.dismissOperationError() - } - } - ) - } } private struct AccountReferralLinkView: View { diff --git a/OSGKeyboard/Views/Account/AccountSessionCoordinator.swift b/OSGKeyboard/Views/Account/AccountSessionCoordinator.swift index f33b12d..b78989c 100644 --- a/OSGKeyboard/Views/Account/AccountSessionCoordinator.swift +++ b/OSGKeyboard/Views/Account/AccountSessionCoordinator.swift @@ -58,8 +58,10 @@ final class AccountSessionCoordinator: ObservableObject { private var didAttemptRestore = false private var accountRefreshTask: Task? private var accountRefreshRequestID: UUID? + private var pendingForcedAccountRefresh = false private var sessionEventsTask: Task? private var appleCredentialRevocationCancellable: AnyCancellable? + private var sessionRevision: UInt64 = 0 init( dependencies: AccountDependencies, @@ -70,6 +72,7 @@ final class AccountSessionCoordinator: ObservableObject { UserDefaultsReferralProfileStore(), accountRefreshInterval: TimeInterval = 10 * 60, now: @escaping () -> Date = Date.init, + notificationCenter: NotificationCenter = .default, analyticsClient: any AnalyticsClient = NoopAnalyticsClient(), onAccountAuthenticated: @escaping (UUID) async -> Void = { _ in }, onAccountSignedOut: @escaping () async -> Void = {}, @@ -102,11 +105,13 @@ final class AccountSessionCoordinator: ObservableObject { await self?.handleSessionEvent(event) } } - appleCredentialRevocationCancellable = NotificationCenter.default + appleCredentialRevocationCancellable = notificationCenter .publisher(for: ASAuthorizationAppleIDProvider.credentialRevokedNotification) .sink { [weak self] _ in Task { @MainActor [weak self] in - await self?.handleAppleCredentialRevocation() + await self?.handleAppleCredentialRevocation( + errorKey: "account.error.sessionExpired" + ) } } } @@ -142,6 +147,7 @@ final class AccountSessionCoordinator: ObservableObject { return } await onAccountAuthenticated(session.accountID) + advanceSessionRevision() sessionPhase = .signedIn(session) creditPurchases.startSession(accountID: session.accountID) referralProfile.startSession(accountID: session.accountID) @@ -167,11 +173,19 @@ final class AccountSessionCoordinator: ObservableObject { } func validateAppleCredentialState() async { - guard isSignedIn, - await sessionService.appleCredentialState() == .revoked else { + guard isSignedIn else { return } + switch await sessionService.appleCredentialState() { + case .authorized, .unknown: return + case .revoked: + await handleAppleCredentialRevocation( + errorKey: "account.error.sessionExpired" + ) + case .reauthenticationRequired: + await handleAppleCredentialRevocation( + errorKey: "account.error.appleReauthenticationRequired" + ) } - await handleAppleCredentialRevocation() } @discardableResult @@ -208,6 +222,7 @@ final class AccountSessionCoordinator: ObservableObject { do { let session = try await sessionService.signIn(with: payload) await onAccountAuthenticated(session.accountID) + advanceSessionRevision() sessionPhase = .signedIn(session) creditPurchases.startSession(accountID: session.accountID) referralProfile.startSession(accountID: session.accountID) @@ -229,6 +244,9 @@ final class AccountSessionCoordinator: ObservableObject { return } if let accountRefreshTask { + if force { + pendingForcedAccountRefresh = true + } await accountRefreshTask.value return } @@ -257,16 +275,27 @@ final class AccountSessionCoordinator: ObservableObject { accountRefreshTask = task await task.value finishAccountRefresh(requestID: requestID) + let shouldRunForcedRefresh = pendingForcedAccountRefresh + pendingForcedAccountRefresh = false + if shouldRunForcedRefresh, self.accountID != nil { + await refreshAccountData(force: true) + } } func updateDisplayName(_ displayName: String) async -> Bool { - guard operation == nil, isSignedIn else { return false } + guard operation == nil, let expectedAccountID = accountID else { return false } + let expectedRevision = sessionRevision operation = .updatingProfile operationErrorKey = nil defer { operation = nil } do { let account = try await centerService.updateDisplayName(displayName) + guard sessionRevision == expectedRevision, + accountID == expectedAccountID, + account.accountID == expectedAccountID else { + return false + } sessionPhase = .signedIn(account) if case let .loaded(snapshot) = snapshotPhase { snapshotPhase = .loaded( @@ -279,6 +308,10 @@ final class AccountSessionCoordinator: ObservableObject { } return true } catch { + guard sessionRevision == expectedRevision, + accountID == expectedAccountID else { + return false + } operationErrorKey = errorMessageKey( for: error, fallback: "account.error.updateProfile" @@ -288,14 +321,24 @@ final class AccountSessionCoordinator: ObservableObject { } func prepareManagedGateway() async -> Bool { - guard operation == nil, isSignedIn else { return false } + guard operation == nil, let expectedAccountID = accountID else { return false } + let expectedRevision = sessionRevision operation = .preparingManagedGateway operationErrorKey = nil defer { operation = nil } do { try await sessionService.prepareManagedGateway() + guard sessionRevision == expectedRevision, + accountID == expectedAccountID else { + await sessionService.clearManagedGateway() + return false + } return true } catch { + guard sessionRevision == expectedRevision, + accountID == expectedAccountID else { + return false + } operationErrorKey = errorMessageKey( for: error, fallback: "account.error.managedGateway" @@ -318,6 +361,7 @@ final class AccountSessionCoordinator: ObservableObject { do { try await sessionService.signOut() await onAccountSignedOut() + advanceSessionRevision() creditPurchases.endSession() clearAccountRefreshState() referralProfile.endSession() @@ -342,6 +386,7 @@ final class AccountSessionCoordinator: ObservableObject { try await sessionService.deleteAccount(with: payload) await onAccountDeleted() await onAccountSignedOut() + advanceSessionRevision() creditPurchases.endSession() clearAccountRefreshState() referralProfile.endSession(removeCache: true) @@ -412,6 +457,7 @@ final class AccountSessionCoordinator: ObservableObject { accountRefreshTask?.cancel() accountRefreshTask = nil accountRefreshRequestID = nil + pendingForcedAccountRefresh = false lastAccountRefreshAt = nil isRefreshingAccountData = false accountRefreshErrorKey = nil @@ -427,26 +473,48 @@ final class AccountSessionCoordinator: ObservableObject { private func handleSessionEvent(_ event: AccountSessionEvent) async { switch event { case .expired: - await transitionToExpiredSession() + await expireSession( + errorKey: "account.error.sessionExpired", + signsOutService: false + ) } } - private func handleAppleCredentialRevocation() async { - guard isSignedIn else { return } - try? await sessionService.signOut() - await transitionToExpiredSession() + private func handleAppleCredentialRevocation(errorKey: String) async { + guard operation != .signingOut, + operation != .deletingAccount else { + return + } + await expireSession(errorKey: errorKey, signsOutService: true) } - private func transitionToExpiredSession() async { + private func expireSession( + errorKey: String, + signsOutService: Bool + ) async { guard isSignedIn else { return } + advanceSessionRevision() + if signsOutService { + do { + try await sessionService.signOut() + } catch { + operationErrorKey = "account.error.signOut" + return + } + } await sessionService.clearManagedGateway() await onAccountSignedOut() creditPurchases.endSession() clearAccountRefreshState() referralProfile.endSession() + operation = nil sessionPhase = .signedOut snapshotPhase = .idle - operationErrorKey = "account.error.sessionExpired" + operationErrorKey = errorKey + } + + private func advanceSessionRevision() { + sessionRevision &+= 1 } private func redeemPendingReferralIfNeeded() async { diff --git a/OSGKeyboard/Views/Account/LiveAccountServices.swift b/OSGKeyboard/Views/Account/LiveAccountServices.swift index 295a04b..37a88b2 100644 --- a/OSGKeyboard/Views/Account/LiveAccountServices.swift +++ b/OSGKeyboard/Views/Account/LiveAccountServices.swift @@ -186,7 +186,7 @@ private struct SystemAppleCredentialStateChecker: AppleCredentialStateChecking { case .revoked, .notFound: continuation.resume(returning: .revoked) case .transferred: - continuation.resume(returning: .unknown) + continuation.resume(returning: .reauthenticationRequired) @unknown default: continuation.resume(returning: .unknown) } @@ -324,6 +324,15 @@ private actor LiveAccountService: + "appAttest=\(evidence.appAttest == nil ? 0 : 1)", category: "account" ) + do { + try await appleUserIdentifiers.saveAppleUserIdentifier(payload.userIdentifier) + } catch { + OSGDiag.log( + "signIn failed stage=apple-user-identifier", + category: "account" + ) + throw AccountAPIError.secureStorage + } let session: OSGKeyboardHostSupport.AccountSession do { session = try await apiClient.signInWithApple( @@ -337,33 +346,43 @@ private actor LiveAccountService: ) ) } catch { + try? await appleUserIdentifiers.clearAppleUserIdentifier() OSGDiag.log( "signIn failed stage=session error=\(AccountDiagnostic.code(for: error))", category: "account" ) throw error } - if let userIdentifier = payload.userIdentifier, - !userIdentifier.isEmpty { - try? await appleUserIdentifiers.saveAppleUserIdentifier(userIdentifier) - } - let account: OSGAccount + let account: OSGAccount? do { account = try await apiClient.account() + } catch let error as AccountAPIError { + switch error { + case .sessionUnavailable, .unauthorized, .refreshTokenReuse: + try? await appleUserIdentifiers.clearAppleUserIdentifier() + await invalidateManagedGatewaySession() + throw error + default: + OSGDiag.log( + "signIn account fallback error=\(AccountDiagnostic.code(for: error))", + category: "account" + ) + account = nil + } } catch { OSGDiag.log( - "signIn failed stage=account error=\(AccountDiagnostic.code(for: error))", + "signIn account fallback error=\(AccountDiagnostic.code(for: error))", category: "account" ) - throw error + account = nil } configuration.setManagedGatewayAccountSessionAvailable(true) await synchronizeManagedGrant() OSGDiag.log("signIn completed", category: "account") return uiSession( session, - createdAtEpochSeconds: account.createdAtEpochSeconds, - displayName: account.displayName + createdAtEpochSeconds: account?.createdAtEpochSeconds ?? 0, + displayName: account?.displayName ?? payload.displayName ) } @@ -375,7 +394,6 @@ private actor LiveAccountService: try await apiClient.logout() } catch { try? await grants.clearGrant() - try? await appleUserIdentifiers.clearAppleUserIdentifier() throw error } try? await grants.clearGrant() @@ -396,10 +414,15 @@ private actor LiveAccountService: } func appleCredentialState() async -> AccountAppleCredentialState { - guard let userIdentifier = try? await appleUserIdentifiers.loadAppleUserIdentifier(), - !userIdentifier.isEmpty else { + let userIdentifier: String? + do { + userIdentifier = try await appleUserIdentifiers.loadAppleUserIdentifier() + } catch { return .unknown } + guard let userIdentifier, !userIdentifier.isEmpty else { + return .reauthenticationRequired + } return await appleCredentialChecker.state(for: userIdentifier) } diff --git a/OSGKeyboard/Views/ClipboardHistoryDemoView.swift b/OSGKeyboard/Views/ClipboardHistoryDemoView.swift index c542970..4b6e567 100644 --- a/OSGKeyboard/Views/ClipboardHistoryDemoView.swift +++ b/OSGKeyboard/Views/ClipboardHistoryDemoView.swift @@ -34,7 +34,7 @@ struct ClipboardHistoryDemoView: View { var body: some View { ZStack { - Color(red: 0.06, green: 0.06, blue: 0.07).ignoresSafeArea() + OSGColor.demoBackground.ignoresSafeArea() VStack(spacing: 0) { Spacer(minLength: 0) keyboardChrome diff --git a/OSGKeyboard/Views/Components/OSGAnimatedMeshBackground.swift b/OSGKeyboard/Views/Components/OSGAnimatedMeshBackground.swift index 9b3cfd3..c2aac93 100644 --- a/OSGKeyboard/Views/Components/OSGAnimatedMeshBackground.swift +++ b/OSGKeyboard/Views/Components/OSGAnimatedMeshBackground.swift @@ -12,9 +12,10 @@ // (backgrounds do not need 120 Hz) — roughly halves GPU vs. uncapped TimelineView. // - `accessibilityReduceMotion` → static frame at phase = 0 (Apple HIG). // - Palette is the same near-black base used across the rest of the app -// (`Color(red: 0.06, green: 0.06, blue: 0.07)`) with cool-warm accents that +// (`OSGColor.demoBackground`) with cool-warm accents that // sit in the "premium, calm, late-night" register — fits the Typeless vibe. +import OSGKeyboardShared import SwiftUI @available(iOS 18.0, *) @@ -108,43 +109,13 @@ struct AnimatedMeshPalette: Sendable { var colors: [Color] /// Deep indigo / amber / teal. Default — works for the dark home screen. - static let aurora = AnimatedMeshPalette(colors: [ - Color(red: 0.06, green: 0.07, blue: 0.13), // top-left - Color(red: 0.10, green: 0.08, blue: 0.20), // top-mid - Color(red: 0.05, green: 0.10, blue: 0.18), // top-right - Color(red: 0.12, green: 0.09, blue: 0.16), // left-mid - Color(red: 0.18, green: 0.11, blue: 0.22), // center ← deepest violet - Color(red: 0.08, green: 0.12, blue: 0.20), // right-mid - Color(red: 0.16, green: 0.10, blue: 0.10), // bottom-left (amber hint) - Color(red: 0.10, green: 0.08, blue: 0.18), // bottom-mid - Color(red: 0.05, green: 0.09, blue: 0.14) // bottom-right - ]) + static let aurora = AnimatedMeshPalette(colors: OSGColor.meshAurora) /// Cooler / more "Apple" feel — slate blue, steel, ice. - static let polar = AnimatedMeshPalette(colors: [ - Color(red: 0.05, green: 0.07, blue: 0.10), - Color(red: 0.09, green: 0.12, blue: 0.18), - Color(red: 0.07, green: 0.09, blue: 0.14), - Color(red: 0.10, green: 0.13, blue: 0.18), - Color(red: 0.13, green: 0.16, blue: 0.22), - Color(red: 0.08, green: 0.10, blue: 0.16), - Color(red: 0.06, green: 0.10, blue: 0.16), - Color(red: 0.09, green: 0.11, blue: 0.18), - Color(red: 0.05, green: 0.07, blue: 0.12) - ]) + static let polar = AnimatedMeshPalette(colors: OSGColor.meshPolar) /// Warm "candlelight" — fits evening writing session vibes. - static let ember = AnimatedMeshPalette(colors: [ - Color(red: 0.08, green: 0.06, blue: 0.05), - Color(red: 0.14, green: 0.09, blue: 0.06), - Color(red: 0.10, green: 0.07, blue: 0.06), - Color(red: 0.16, green: 0.10, blue: 0.07), - Color(red: 0.22, green: 0.13, blue: 0.08), - Color(red: 0.12, green: 0.08, blue: 0.07), - Color(red: 0.10, green: 0.06, blue: 0.05), - Color(red: 0.15, green: 0.09, blue: 0.06), - Color(red: 0.08, green: 0.06, blue: 0.05) - ]) + static let ember = AnimatedMeshPalette(colors: OSGColor.meshEmber) } // MARK: - Helpers diff --git a/OSGKeyboard/Views/Components/PrivacyDisclosureViews.swift b/OSGKeyboard/Views/Components/PrivacyDisclosureViews.swift index 5e952e9..44cc56b 100644 --- a/OSGKeyboard/Views/Components/PrivacyDisclosureViews.swift +++ b/OSGKeyboard/Views/Components/PrivacyDisclosureViews.swift @@ -26,5 +26,6 @@ struct PrivacyInfoCard: View { .padding(Spacing.md) .frame(maxWidth: .infinity, alignment: .leading) .background(palette.surface, in: RoundedRectangle(cornerRadius: Radius.large, style: .continuous)) + .cardElevation() } } diff --git a/OSGKeyboard/Views/Components/WideLayoutComponents.swift b/OSGKeyboard/Views/Components/WideLayoutComponents.swift index 04c22f2..363aaf4 100644 --- a/OSGKeyboard/Views/Components/WideLayoutComponents.swift +++ b/OSGKeyboard/Views/Components/WideLayoutComponents.swift @@ -37,5 +37,6 @@ struct WideCard: View { content() .padding(padding) .background(palette.surface, in: shape) + .cardElevation() } } diff --git a/OSGKeyboard/Views/EditDemoView.swift b/OSGKeyboard/Views/EditDemoView.swift index 812ac95..ea43e17 100644 --- a/OSGKeyboard/Views/EditDemoView.swift +++ b/OSGKeyboard/Views/EditDemoView.swift @@ -36,7 +36,7 @@ struct EditDemoView: View { var body: some View { ZStack { - Color(red: 0.06, green: 0.06, blue: 0.07).ignoresSafeArea() + OSGColor.demoBackground.ignoresSafeArea() VStack(spacing: 0) { Spacer(minLength: 0) Group { diff --git a/OSGKeyboard/Views/EnginePickerSection.swift b/OSGKeyboard/Views/EnginePickerSection.swift index e665e36..e5a126e 100644 --- a/OSGKeyboard/Views/EnginePickerSection.swift +++ b/OSGKeyboard/Views/EnginePickerSection.swift @@ -75,7 +75,7 @@ struct EnginePickerSection: View { HStack(spacing: Spacing.sm) { VStack(alignment: .leading, spacing: 2) { Text(title) - .font(TypeStyle.body) + .font(.system(size: 15, weight: isSelected ? .semibold : .regular)) .foregroundStyle(isSelected ? palette.accent : palette.textPrimary) Text(subtitle) .font(TypeStyle.caption2) diff --git a/OSGKeyboard/Views/FlowDiagnosticsSettingsView.swift b/OSGKeyboard/Views/FlowDiagnosticsSettingsView.swift new file mode 100644 index 0000000..eb80444 --- /dev/null +++ b/OSGKeyboard/Views/FlowDiagnosticsSettingsView.swift @@ -0,0 +1,139 @@ +// FlowDiagnosticsSettingsView.swift +// OSGKeyboard · Main App +// +// Local-only export and deletion controls for Flow startup failure reports. + +import OSGKeyboardShared +import SwiftUI + +struct FlowDiagnosticsSettingsView: View { + @Environment(\.themePalette) private var palette: ThemePalette + @ObservedObject private var config = ProviderConfig.shared + + @State private var reportURLs: [URL] = [] + @State private var exportURL: URL? + @State private var showsClearConfirmation = false + + var body: some View { + ScrollView { + CardPageContent { + CardSection("settings.diagnostics.section") { + VStack(spacing: 0) { + VStack(alignment: .leading, spacing: Spacing.xxs) { + Text(reportCountText) + .font(TypeStyle.bodyEmph) + .foregroundStyle(palette.textPrimary) + Text(reportStatusText) + .font(TypeStyle.caption2) + .foregroundStyle(palette.textTertiary) + } + .frame(maxWidth: .infinity, alignment: .leading) + .settingsListRow() + + if let exportURL { + Divider().background(palette.divider) + + ShareLink(item: exportURL) { + actionRow( + title: "settings.diagnostics.export", + systemImage: "square.and.arrow.up" + ) + } + .buttonStyle(.plain) + + Divider().background(palette.divider) + + Button(role: .destructive) { + showsClearConfirmation = true + } label: { + actionRow( + title: "settings.diagnostics.clear", + systemImage: "trash" + ) + } + .buttonStyle(.plain) + } + } + .surfaceCard() + } + + CardSection("settings.diagnostics.privacy.section") { + Text("settings.diagnostics.privacy.body") + .font(TypeStyle.caption2) + .foregroundStyle(palette.textSecondary) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(Spacing.md) + .surfaceCard() + } + } + } + .background(palette.background.ignoresSafeArea()) + .navigationTitle("settings.diagnostics.title") + .navigationBarTitleDisplayMode(.inline) + .onAppear(perform: refreshReports) + .alert("settings.diagnostics.clear.title", isPresented: $showsClearConfirmation) { + Button("common.cancel", role: .cancel) {} + Button("settings.diagnostics.clear.confirm", role: .destructive) { + FlowFailureLogStore.shared.deleteAllReports() + refreshReports() + } + } message: { + Text("settings.diagnostics.clear.message") + } + } + + private var reportCountText: String { + String( + format: AppL10n.string( + "settings.diagnostics.count", + language: config.uiLanguage + ), + reportURLs.count + ) + } + + private var reportStatusText: String { + guard let latest = reportURLs.first, + let modifiedAt = try? latest.resourceValues( + forKeys: [.contentModificationDateKey] + ).contentModificationDate else { + return AppL10n.string( + "settings.diagnostics.empty", + language: config.uiLanguage + ) + } + let format = AppL10n.string( + "settings.diagnostics.latest", + language: config.uiLanguage + ) + return String( + format: format, + modifiedAt.formatted(date: .abbreviated, time: .shortened) + ) + } + + private func actionRow( + title: LocalizedStringKey, + systemImage: String + ) -> some View { + HStack(spacing: Spacing.sm) { + Image(systemName: systemImage) + .foregroundStyle(palette.accent) + .frame(width: 22) + Text(title) + .font(TypeStyle.body) + .foregroundStyle(palette.textPrimary) + Spacer(minLength: Spacing.sm) + Image(systemName: "chevron.right") + .font(.system(size: 12, weight: .semibold)) + .foregroundStyle(palette.textTertiary) + } + .settingsListRow() + .contentShape(Rectangle()) + } + + private func refreshReports() { + reportURLs = FlowFailureLogStore.shared.reportURLs() + exportURL = FlowFailureLogStore.shared.makeExportURL() + } +} diff --git a/OSGKeyboard/Views/HistoryView.swift b/OSGKeyboard/Views/HistoryView.swift index 08a2dd1..32215eb 100644 --- a/OSGKeyboard/Views/HistoryView.swift +++ b/OSGKeyboard/Views/HistoryView.swift @@ -47,22 +47,23 @@ struct HistoryView: View { } label: { Image(systemName: "trash") } + .tint(palette.textPrimary) .accessibilityLabel("history.clear.button") + .confirmationDialog( + "history.clear.title", + isPresented: $showClearConfirmation, + titleVisibility: .visible + ) { + Button("history.clear.confirm", role: .destructive) { + store.clearAll() + } + Button("common.cancel", role: .cancel) {} + } message: { + Text("history.clear.message") + } } } } - .confirmationDialog( - "history.clear.title", - isPresented: $showClearConfirmation, - titleVisibility: .visible - ) { - Button("history.clear.confirm", role: .destructive) { - store.clearAll() - } - Button("common.cancel", role: .cancel) {} - } message: { - Text("history.clear.message") - } .confirmationDialog( "history.clearDay.title", isPresented: $showDeleteDayConfirmation, @@ -82,37 +83,37 @@ struct HistoryView: View { } } - // MARK: - List + // MARK: - Card list private var list: some View { - List { - ForEach(store.groupedByDay, id: \.day) { group in - Section { - ForEach(group.items) { entry in - historyRow(entry) - .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) - .listRowBackground(palette.surface) - .listRowSeparatorTint(palette.divider) + ScrollView { + CardPageContent { + ForEach(store.groupedByDay, id: \.day) { group in + VStack(alignment: .leading, spacing: SettingsListMetrics.sectionLabelSpacing) { + daySectionHeader(day: group.day) + + LazyVStack(spacing: CardLayoutMetrics.compactItemSpacing) { + ForEach(group.items) { entry in + historyRow(entry) + .surfaceCard() + .contextMenu { + Button("common.delete", role: .destructive) { + delete(entry) + } + } + } + } } - .onDelete { offsets in - delete(items: group.items, at: offsets) - } - } header: { - daySectionHeader(day: group.day) } - .listSectionMargins(.horizontal, Spacing.lg) } + .tabBarScrollBottomPadding() } - .listStyle(.insetGrouped) - .listSectionSpacing(CardLayoutMetrics.sectionSpacing) - .scrollContentBackground(.hidden) + .scrollClipDisabled() .background(palette.background) - .contentMargins(.top, Spacing.md, for: .scrollContent) - .tabBarListScrollBottomMargin() } /// Date label + per-day delete, flush with the section card's left/right edges - /// (Settings section labels share the same edge; system List headers inset further). + /// (CardPageContent gives labels and cards the same horizontal guide). private func daySectionHeader(day: Date) -> some View { HStack(alignment: .center, spacing: Spacing.sm) { Text(Self.dayFormatter.string(from: day)) @@ -134,9 +135,8 @@ struct HistoryView: View { .accessibilityLabel("history.clearDay.button") } .frame(maxWidth: .infinity, alignment: .leading) - // Cancel the default List section-header content inset so the label - // lines up with the card's left edge (rows use leading: 0). - .padding(.horizontal, -SettingsListMetrics.rowHorizontalPadding) + // CardPageContent owns horizontal padding, so no row-specific + // compensation is needed to keep the label flush with the cards. .textCase(nil) } @@ -181,9 +181,7 @@ struct HistoryView: View { // MARK: - Mutations - private func delete(items: [SpeechHistoryEntry], at offsets: IndexSet) { - for index in offsets { - store.delete(id: items[index].id) - } + private func delete(_ entry: SpeechHistoryEntry) { + store.delete(id: entry.id) } } diff --git a/OSGKeyboard/Views/HomeView.swift b/OSGKeyboard/Views/HomeView.swift index 605f04f..5318c99 100644 --- a/OSGKeyboard/Views/HomeView.swift +++ b/OSGKeyboard/Views/HomeView.swift @@ -352,6 +352,23 @@ struct HomeView: View { compact: Bool = false ) -> some View { VStack(spacing: CardLayoutMetrics.sectionSpacing) { + #if DEBUG + if ProcessInfo.processInfo.arguments.contains("--home-dictionary-screenshot") { + dictionaryCard + } else { + standardHomeContentSections(layout: layout, compact: compact) + } + #else + standardHomeContentSections(layout: layout, compact: compact) + #endif + } + } + + @ViewBuilder + private func standardHomeContentSections( + layout: UsageStatsClusterLayout, + compact: Bool + ) -> some View { AccountRewardsCard { path.append(HomeRoute.account) } @@ -375,7 +392,6 @@ struct HomeView: View { ) dictionaryCard - } } private var dictionaryCard: some View { @@ -496,14 +512,18 @@ struct HomeView: View { pendingDictionarySuggestion = nil } .buttonStyle(.bordered) + .tint(palette.textPrimary) Spacer(minLength: 0) - Button("home.card.dictionary.confirm.add") { + Button { addSuggestedTerm(suggestion) + } label: { + Text("home.card.dictionary.confirm.add") + .foregroundStyle(palette.background) } .buttonStyle(.borderedProminent) - .tint(palette.accent) + .tint(palette.textPrimary) } } .padding(Spacing.lg) @@ -576,9 +596,11 @@ struct HomeView: View { private var headerGradientColors: [Color] { // 云端引擎未配置(缺 API Key)时不算就绪,保持中性灰渐变。 if sessionIsLive, !needsAPIKeySetup { + let leadingOpacity = colorScheme == .dark ? 0.18 : 0.28 + let trailingOpacity = colorScheme == .dark ? 0.06 : 0.10 return [ - palette.accent.opacity(0.28), - palette.accent.opacity(0.10), + palette.accent.opacity(leadingOpacity), + palette.accent.opacity(trailingOpacity), palette.background.opacity(0) ] } @@ -601,7 +623,11 @@ struct HomeView: View { .renderingMode(.template) .scaledToFit() .frame(width: logoWidth, height: logoHeight) - .foregroundStyle(colorScheme == .dark ? Color.white : Color.black) + .foregroundStyle( + colorScheme == .dark + ? OSGColor.fixedLightContent + : OSGColor.fixedDarkContent + ) .accessibilityHidden(true) } .frame(maxWidth: .infinity) @@ -769,6 +795,7 @@ struct HomeView: View { .frame(maxWidth: .infinity, alignment: .leading) .padding(Spacing.md) .background(palette.surface, in: RoundedRectangle(cornerRadius: Radius.xl, style: .continuous)) + .cardElevation() } private var flowStatusColor: Color { diff --git a/OSGKeyboard/Views/KeyboardPreviewSheet.swift b/OSGKeyboard/Views/KeyboardPreviewSheet.swift index 848d865..1b3a528 100644 --- a/OSGKeyboard/Views/KeyboardPreviewSheet.swift +++ b/OSGKeyboard/Views/KeyboardPreviewSheet.swift @@ -96,7 +96,8 @@ struct KeyboardPreviewSheet: View { .padding(.horizontal, 2) } .padding(Spacing.md) - .background(palette.surface, in: RoundedRectangle(cornerRadius: Radius.medium)) + .background(palette.formSurface, in: RoundedRectangle(cornerRadius: Radius.medium)) + .cardElevation() } private var controls: some View { diff --git a/OSGKeyboard/Views/KeyboardPreviewStub.swift b/OSGKeyboard/Views/KeyboardPreviewStub.swift index 498a85b..34c146a 100644 --- a/OSGKeyboard/Views/KeyboardPreviewStub.swift +++ b/OSGKeyboard/Views/KeyboardPreviewStub.swift @@ -217,7 +217,7 @@ struct KeyboardPreviewStub: View { case .idle: Image(systemName: "mic.fill") .font(.system(size: 32, weight: .medium)) - .foregroundStyle(.white) + .foregroundStyle(OSGColor.fixedLightContent) case .recording: HStack(spacing: 3) { ForEach(0..<12, id: \.self) { i in diff --git a/OSGKeyboard/Views/MainAppRoot.swift b/OSGKeyboard/Views/MainAppRoot.swift index 316dd31..b5bdc3b 100644 --- a/OSGKeyboard/Views/MainAppRoot.swift +++ b/OSGKeyboard/Views/MainAppRoot.swift @@ -19,6 +19,7 @@ enum AnalyticsFirstOpenAttribution { struct MainAppRoot: View { @Environment(\.scenePhase) private var scenePhase + @Environment(\.themePalette) private var palette: ThemePalette // Singleton is owned by `ProviderConfig.shared`, not by this view — // `@ObservedObject` keeps subscriptions correct across Settings replay. @@ -64,13 +65,27 @@ struct MainAppRoot: View { } var body: some View { - Group { + ZStack { + palette.background + .ignoresSafeArea() mainContent } .environment(\.locale, config.uiLanguage.swiftUILocale) .environmentObject(flowManager) .environmentObject(accountSession) .environmentObject(analytics) + .alert( + "account.error.title", + isPresented: accountOperationErrorBinding + ) { + Button("common.done") { + accountSession.dismissOperationError() + } + } message: { + if let key = accountSession.operationErrorKey { + Text(LocalizedStringKey(key)) + } + } .background { FlowPiPHostView { view in flowManager.attachPiPHostView(view) @@ -209,6 +224,7 @@ struct MainAppRoot: View { // launch. The coordinator no-ops after a terminal restore, so // this only retries a previously interrupted attempt. await accountSession.restoreIfNeeded() + await accountSession.validateAppleCredentialState() } if config.hasCompletedOnboarding { activateForegroundServices(reason: "scenePhase.active") @@ -217,13 +233,21 @@ struct MainAppRoot: View { releaseNotes.presentIfNeeded(onboardingCompleted: true) } } - Task { - await accountSession.validateAppleCredentialState() - await AppCloudSync.shared.pullAllIfEnabled() - } + Task { await AppCloudSync.shared.pullAllIfEnabled() } } } + private var accountOperationErrorBinding: Binding { + Binding( + get: { accountSession.operationErrorKey != nil }, + set: { isPresented in + if !isPresented { + accountSession.dismissOperationError() + } + } + ) + } + private func refreshOfficialSkillCatalog(reason: String) { Task { let outcome = await OfficialSkillCatalogRefreshService.shared.refreshIfNeeded( diff --git a/OSGKeyboard/Views/OnboardingExperienceView.swift b/OSGKeyboard/Views/OnboardingExperienceView.swift index 16bb0e2..6d02c8a 100644 --- a/OSGKeyboard/Views/OnboardingExperienceView.swift +++ b/OSGKeyboard/Views/OnboardingExperienceView.swift @@ -67,6 +67,19 @@ private extension ManagedGatewayOOBEFeature { } } + var editorSystemImage: String { + switch self { + case .voiceInput: + return "message" + case .clipboardTranslate: + return "translate" + case .clipboardReply: + return "arrowshape.turn.up.left" + case .askAI: + return "sparkles.2" + } + } + var next: ManagedGatewayOOBEFeature? { switch self { case .voiceInput: @@ -81,9 +94,18 @@ private extension ManagedGatewayOOBEFeature { } } +#if DEBUG +private enum OOBEPreviewDestination { + case practice(ManagedGatewayOOBEFeature) + case loginReward + case complete +} +#endif + struct OnboardingExperienceView: View { @Environment(\.scenePhase) private var scenePhase @Environment(\.themePalette) private var palette + @Environment(\.accessibilityReduceMotion) private var reduceMotion @EnvironmentObject private var flowManager: FlowSessionManager @EnvironmentObject private var accountSession: AccountSessionCoordinator @@ -104,11 +126,15 @@ struct OnboardingExperienceView: View { @State private var isPreparingManagedPractice = false @State private var managedPracticeReady = false @State private var managedPracticeFailed = false - @State private var practiceFeature: ManagedGatewayOOBEFeature = .voiceInput + @State private var practiceFeature = Self.initialPracticeFeature @State private var practiceSessionID: UUID? @State private var completedPracticeFeatures: Set = [] @State private var didCopyPracticeSample = false @State private var didRefreshLoginReward = false + @State private var showsKeyboardSwitchIcon = false + @State private var showsLoginRewardIcon = false + @State private var showsCompleteIcon = false + @State private var previewPageIndex = 0 @FocusState private var keyboardSwitchFieldFocused: Bool @FocusState private var practiceFieldFocused: Bool @@ -117,9 +143,122 @@ struct OnboardingExperienceView: View { "onboarding.experience.oobe.completedFeatures.v1" private var currentStep: OnboardingExperienceStep { - OnboardingExperienceStep(rawValue: config.onboardingPage) ?? .introduction + #if DEBUG + switch activePreviewDestination { + case .practice: + return .practice + case .loginReward: + return .loginReward + case .complete: + return .complete + case nil: + break + } + #endif + return OnboardingExperienceStep(rawValue: config.onboardingPage) ?? .introduction } + #if DEBUG + private var activePreviewDestination: OOBEPreviewDestination? { + guard Self.previewsAllScreensFromArguments else { + return Self.previewDestination + } + return Self.allPreviewDestinations[ + min(previewPageIndex, Self.allPreviewDestinations.count - 1) + ] + } + #endif + + private var isPreviewMode: Bool { + #if DEBUG + Self.isPreviewEnabled + #else + false + #endif + } + + private var previewLanguageOverride: AppUILanguage? { + #if DEBUG + isPreviewMode ? Self.previewLanguage : nil + #else + nil + #endif + } + + private var previewsAllScreens: Bool { + #if DEBUG + Self.previewsAllScreensFromArguments + #else + false + #endif + } + + private static var initialPracticeFeature: ManagedGatewayOOBEFeature { + #if DEBUG + if case let .practice(feature) = previewDestination { + return feature + } + #endif + return .voiceInput + } + + #if DEBUG + static var isPreviewEnabled: Bool { + previewDestination != nil + } + + static var previewLanguage: AppUILanguage { + let arguments = ProcessInfo.processInfo.arguments + if arguments.contains("--screenshot-lang=en") { + return .english + } + if arguments.contains("--screenshot-lang=zh") { + return .chinese + } + return ProviderConfig.shared.uiLanguage + } + + private static var previewsAllScreensFromArguments: Bool { + ProcessInfo.processInfo.arguments.contains("--oobe-preview=all") + } + + private static let allPreviewDestinations: [OOBEPreviewDestination] = [ + .practice(.voiceInput), + .practice(.clipboardTranslate), + .practice(.clipboardReply), + .practice(.askAI), + .loginReward, + .complete + ] + + private static var previewDestination: OOBEPreviewDestination? { + let prefix = "--oobe-preview=" + guard let argument = ProcessInfo.processInfo.arguments.first( + where: { $0.hasPrefix(prefix) } + ) else { + return nil + } + switch String(argument.dropFirst(prefix.count)) { + case "all": + return .practice(.voiceInput) + case "voice": + return .practice(.voiceInput) + case "translate": + return .practice(.clipboardTranslate) + case "reply": + return .practice(.clipboardReply) + case "ask-ai": + return .practice(.askAI) + case "login": + return .loginReward + case "complete": + return .complete + default: + return nil + } + } + #endif + var body: some View { ZStack { palette.background.ignoresSafeArea() @@ -137,6 +276,7 @@ struct OnboardingExperienceView: View { } } .onAppear { + guard !isPreviewMode else { return } migrateLegacyProgressIfNeeded() applyPrivacySafeDefaultsIfNeeded() refreshState() @@ -147,9 +287,11 @@ struct OnboardingExperienceView: View { } } .onDisappear { + guard !isPreviewMode else { return } endPractice() } .onChange(of: scenePhase) { _, phase in + guard !isPreviewMode else { return } guard phase == .active else { return } refreshState() if currentStep == .keyboard, hasOpenedKeyboardSettings { @@ -164,6 +306,7 @@ struct OnboardingExperienceView: View { } } .onChange(of: currentStep) { previous, current in + guard !isPreviewMode else { return } if previous == .keyboardSwitch { keyboardSwitchFieldFocused = false } @@ -182,15 +325,18 @@ struct OnboardingExperienceView: View { } } .onChange(of: accountSession.isSignedIn) { _, isSignedIn in + guard !isPreviewMode else { return } guard isSignedIn, currentStep == .loginReward else { return } didRefreshLoginReward = false refreshLoginRewardIfNeeded() } .onChange(of: config.hasAcknowledgedCloudSharing) { _, acknowledged in + guard !isPreviewMode else { return } guard acknowledged, currentStep == .practice else { return } prepareManagedPractice() } .task(id: currentStep) { + guard !isPreviewMode else { return } switch currentStep { case .keyboardSwitch: await monitorKeyboardVerification() @@ -248,17 +394,23 @@ struct OnboardingExperienceView: View { Image("osglogo") .resizable() + .renderingMode(.template) .scaledToFit() - .frame(maxWidth: 174, maxHeight: 50) + .frame(maxWidth: 122, maxHeight: 35) + .foregroundStyle(palette.textPrimary) .accessibilityHidden(true) Text("onboarding.experience.intro.eyebrow") - .font(TypeStyle.caption) + .font(.system(size: 10, weight: .medium)) .tracking(1.2) - .foregroundStyle(palette.accent) + .foregroundStyle(palette.textPrimary) .padding(.horizontal, Spacing.sm) .frame(height: 28) - .background(palette.accentMuted, in: Capsule()) + .background(palette.surfaceElevated, in: Capsule()) + .overlay { + Capsule() + .stroke(palette.dividerStrong, lineWidth: 0.5) + } .padding(.top, Spacing.hero) Text("onboarding.experience.intro.title") @@ -275,18 +427,18 @@ struct OnboardingExperienceView: View { VStack(spacing: Spacing.sm) { promiseRow( icon: "waveform", - title: "onboarding.experience.privacy.local.title", - detail: "onboarding.experience.privacy.local.body" + title: "onboarding.experience.promise.voice.title", + detail: "onboarding.experience.promise.voice.body" ) promiseRow( - icon: "network", - title: "onboarding.experience.privacy.cloud.title", - detail: "onboarding.experience.privacy.cloud.body" + icon: "sparkles", + title: "onboarding.experience.promise.agent.title", + detail: "onboarding.experience.promise.agent.body" ) promiseRow( - icon: "keyboard", - title: "onboarding.experience.privacy.anywhere.title", - detail: "onboarding.experience.privacy.anywhere.body" + icon: "key", + title: "onboarding.experience.promise.privacy.title", + detail: "onboarding.experience.promise.privacy.body" ) } .padding(.top, Spacing.xxl) @@ -316,9 +468,9 @@ struct OnboardingExperienceView: View { HStack(alignment: .top, spacing: Spacing.md) { Image(systemName: icon) .font(.system(size: 19, weight: .semibold)) - .foregroundStyle(palette.accent) + .foregroundStyle(palette.textPrimary) .frame(width: 42, height: 42) - .background(palette.accentMuted, in: RoundedRectangle(cornerRadius: Radius.medium)) + .background(palette.surfaceMuted, in: RoundedRectangle(cornerRadius: Radius.medium)) VStack(alignment: .leading, spacing: 3) { Text(title) @@ -334,6 +486,7 @@ struct OnboardingExperienceView: View { } .padding(Spacing.md) .background(palette.surface, in: RoundedRectangle(cornerRadius: Radius.large, style: .continuous)) + .cardElevation() } // MARK: - Permissions @@ -390,9 +543,9 @@ struct OnboardingExperienceView: View { HStack(spacing: Spacing.md) { Image(systemName: icon) .font(.system(size: 20, weight: .semibold)) - .foregroundStyle(palette.accent) + .foregroundStyle(palette.textPrimary) .frame(width: 42, height: 42) - .background(palette.accentMuted, in: RoundedRectangle(cornerRadius: Radius.medium)) + .background(palette.surfaceMuted, in: RoundedRectangle(cornerRadius: Radius.medium)) VStack(alignment: .leading, spacing: 2) { Text(title) @@ -412,6 +565,7 @@ struct OnboardingExperienceView: View { } .padding(Spacing.md) .background(palette.surface, in: RoundedRectangle(cornerRadius: Radius.large, style: .continuous)) + .cardElevation() } // MARK: - Keyboard setup @@ -465,8 +619,13 @@ struct OnboardingExperienceView: View { ) { VStack(spacing: Spacing.md) { Image(systemName: "globe") - .font(.system(size: 34, weight: .semibold)) - .foregroundStyle(palette.accent) + .font(.system(size: 40, weight: .ultraLight)) + .foregroundStyle(palette.textPrimary) + .symbolEffect( + .drawOn, + isActive: !reduceMotion && !showsKeyboardSwitchIcon + ) + .symbolEffectsRemoved(reduceMotion) .accessibilityHidden(true) Text("onboarding.experience.keyboardSwitch.instruction") @@ -481,6 +640,7 @@ struct OnboardingExperienceView: View { palette.surface, in: RoundedRectangle(cornerRadius: Radius.large, style: .continuous) ) + .cardElevation() .padding(.top, Spacing.xxl) TextField( @@ -497,16 +657,9 @@ struct OnboardingExperienceView: View { .padding(Spacing.md) .frame(maxWidth: .infinity, minHeight: 72, alignment: .topLeading) .background( - palette.surface, + palette.formSurface, in: RoundedRectangle(cornerRadius: Radius.large, style: .continuous) ) - .overlay( - RoundedRectangle(cornerRadius: Radius.large, style: .continuous) - .stroke( - keyboardReady ? palette.accent : palette.dividerStrong, - lineWidth: keyboardReady ? 1.5 : 0.5 - ) - ) .accessibilityIdentifier("onboarding.keyboardSwitch.textField") .padding(.top, Spacing.lg) @@ -537,6 +690,20 @@ struct OnboardingExperienceView: View { .padding(.top, Spacing.sm) } } + .task { + guard !reduceMotion else { return } + showsKeyboardSwitchIcon = false + do { + // 等待页面首帧完成,再绘制出现输入法切换图标。 + try await Task.sleep(for: .milliseconds(120)) + showsKeyboardSwitchIcon = true + } catch { + showsKeyboardSwitchIcon = false + } + } + .onDisappear { + showsKeyboardSwitchIcon = false + } } private var keyboardVerificationStatus: LocalizedStringKey { @@ -634,11 +801,13 @@ struct OnboardingExperienceView: View { @ViewBuilder private var practiceContent: some View { - if !config.hasAcknowledgedCloudSharing { + if isPreviewMode { + practiceFeatureContent + } else if !config.hasAcknowledgedCloudSharing { VStack(spacing: Spacing.md) { Image(systemName: "sparkles") .font(.system(size: 34, weight: .semibold)) - .foregroundStyle(palette.accent) + .foregroundStyle(palette.textPrimary) Text("onboarding.experience.practice.cloudBody") .font(TypeStyle.caption) @@ -687,31 +856,38 @@ struct OnboardingExperienceView: View { } .practiceSetupCard(palette: palette) } else { - VStack(spacing: Spacing.md) { - if practiceFeature == .voiceInput { - practiceVoiceSample - } else if practiceFeature == .clipboardTranslate - || practiceFeature == .clipboardReply { - practiceClipboardSample - } - practiceEditor - HStack(spacing: Spacing.xs) { - if managedPracticeReady { - ProgressView() - .controlSize(.small) - .tint(palette.accent) - } - Text("onboarding.experience.practice.waiting") - .font(TypeStyle.caption) - .foregroundStyle(palette.textSecondary) - } + practiceFeatureContent + } + } - Button("onboarding.experience.practice.skip") { - goToLoginReward() - } - .font(TypeStyle.caption) - .foregroundStyle(palette.textTertiary) + private var practiceFeatureContent: some View { + VStack(spacing: Spacing.md) { + if practiceFeature == .voiceInput { + practiceVoiceSample + } else if practiceFeature == .clipboardTranslate + || practiceFeature == .clipboardReply { + practiceClipboardSample + } else if practiceFeature == .askAI { + practiceAskAIPrompt } + practiceEditor + HStack(spacing: Spacing.xs) { + if managedPracticeReady || isPreviewMode { + ProgressView() + .controlSize(.small) + .tint(palette.accent) + } + Text("onboarding.experience.practice.waiting") + .font(TypeStyle.caption) + .foregroundStyle(palette.textSecondary) + } + + Button("onboarding.experience.practice.skip") { + guard !isPreviewMode else { return } + goToLoginReward() + } + .font(TypeStyle.caption) + .foregroundStyle(palette.textTertiary) } } @@ -728,8 +904,10 @@ struct OnboardingExperienceView: View { private var practiceEditor: some View { VStack(spacing: 0) { HStack(spacing: Spacing.xs) { - Image(systemName: practiceFeature == .askAI ? "sparkles" : "message.fill") - .foregroundStyle(palette.accent) + Image(systemName: practiceFeature.editorSystemImage) + .font(.system(size: 16, weight: .regular)) + .foregroundStyle(palette.textPrimary) + .accessibilityHidden(true) Text(practiceFeature.progressKey) .font(TypeStyle.caption) .foregroundStyle(palette.textSecondary) @@ -749,7 +927,8 @@ struct OnboardingExperienceView: View { .accessibilityIdentifier("onboarding.practice.textEditor") } .frame(maxWidth: .infinity, minHeight: 172, maxHeight: 240) - .background(palette.surface, in: RoundedRectangle(cornerRadius: Radius.large, style: .continuous)) + .background(palette.formSurface, in: RoundedRectangle(cornerRadius: Radius.large, style: .continuous)) + .cardElevation() } private var practiceVoiceSample: some View { @@ -771,7 +950,7 @@ struct OnboardingExperienceView: View { private var practiceClipboardSample: some View { VStack(alignment: .leading, spacing: Spacing.md) { - Text("onboarding.experience.practice.sample") + Text(LocalizedStringKey(practiceClipboardSampleLocalizationKey)) .font(TypeStyle.bodyEmph) .foregroundStyle(palette.textPrimary) .fixedSize(horizontal: false, vertical: true) @@ -806,9 +985,29 @@ struct OnboardingExperienceView: View { } .padding(Spacing.md) .background(palette.surface, in: RoundedRectangle(cornerRadius: Radius.large, style: .continuous)) + .cardElevation() + } + + private var practiceAskAIPrompt: some View { + Text("onboarding.experience.practice.askAIPrompt") + .font(TypeStyle.bodyEmph) + .foregroundStyle(palette.textPrimary) + .fixedSize(horizontal: false, vertical: true) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(Spacing.md) + .background(palette.accentMuted, in: RoundedRectangle(cornerRadius: Radius.large)) + } + + private var practiceClipboardSampleLocalizationKey: String { + practiceFeature == .clipboardTranslate + ? "onboarding.experience.practice.translateSample" + : "onboarding.experience.practice.replySample" } private var practiceTitle: LocalizedStringKey { + if isPreviewMode { + return practiceFeature.titleKey + } if !permissionsReady { return "onboarding.experience.practice.permissionsTitle" } @@ -828,6 +1027,9 @@ struct OnboardingExperienceView: View { } private var practiceSubtitle: LocalizedStringKey { + if isPreviewMode { + return practiceFeature.subtitleKey + } if !permissionsReady { return "onboarding.experience.practice.permissionsSubtitle" } @@ -855,9 +1057,14 @@ struct OnboardingExperienceView: View { onBack: goBack ) { VStack(spacing: Spacing.lg) { - Image(systemName: accountSession.isSignedIn ? "checkmark.seal.fill" : "gift.fill") - .font(.system(size: 42, weight: .semibold)) - .foregroundStyle(palette.accent) + Image(systemName: "gift.circle") + .font(.system(size: 50, weight: .ultraLight)) + .foregroundStyle(palette.textPrimary) + .symbolEffect( + .drawOn, + isActive: !reduceMotion && !showsLoginRewardIcon + ) + .symbolEffectsRemoved(reduceMotion) .accessibilityHidden(true) Text( @@ -907,9 +1114,24 @@ struct OnboardingExperienceView: View { palette.surface, in: RoundedRectangle(cornerRadius: Radius.xl, style: .continuous) ) + .cardElevation() .padding(.top, Spacing.xxl) .accessibilityIdentifier("onboarding.loginReward.card") } + .task { + guard !reduceMotion else { return } + showsLoginRewardIcon = false + do { + // 等待页面首帧完成后,仅播放一次礼物图标绘制动画。 + try await Task.sleep(for: .milliseconds(120)) + showsLoginRewardIcon = true + } catch { + showsLoginRewardIcon = false + } + } + .onDisappear { + showsLoginRewardIcon = false + } } private var loginRewardBalance: Int64? { @@ -926,14 +1148,15 @@ struct OnboardingExperienceView: View { VStack(spacing: 0) { Spacer(minLength: Spacing.xxl) - ZStack { - Circle() - .fill(palette.accentMuted) - .frame(width: 108, height: 108) - Image(systemName: completedAllPracticeFeatures ? "checkmark" : "sparkles") - .font(.system(size: 44, weight: .semibold)) - .foregroundStyle(palette.accent) - } + Image(systemName: "checkmark.circle") + .font(.system(size: 72, weight: .ultraLight)) + .foregroundStyle(palette.textPrimary) + .symbolEffect( + .drawOn, + isActive: !reduceMotion && !showsCompleteIcon + ) + .symbolEffectsRemoved(reduceMotion) + .frame(width: 108, height: 108) Text( completedAllPracticeFeatures @@ -970,20 +1193,35 @@ struct OnboardingExperienceView: View { .frame(maxWidth: 560) .frame(maxWidth: .infinity) } + .task { + guard !reduceMotion else { return } + showsCompleteIcon = false + do { + // 完成页每次进入时只绘制一次主图标。 + try await Task.sleep(for: .milliseconds(120)) + showsCompleteIcon = true + } catch { + showsCompleteIcon = false + } + } + .onDisappear { + showsCompleteIcon = false + } } private func capability(_ icon: String, _ title: LocalizedStringKey) -> some View { VStack(spacing: Spacing.xs) { Image(systemName: icon) - .font(.system(size: 20, weight: .semibold)) + .font(.system(size: 24, weight: .semibold)) .foregroundStyle(palette.accent) Text(title) .font(TypeStyle.caption) .foregroundStyle(palette.textPrimary) } - .frame(maxWidth: .infinity) - .padding(.vertical, Spacing.md) - .background(palette.surface, in: RoundedRectangle(cornerRadius: Radius.medium)) + .frame(maxWidth: .infinity, maxHeight: .infinity) + .aspectRatio(1.42, contentMode: .fit) + .background(palette.surface, in: RoundedRectangle(cornerRadius: Radius.large)) + .cardElevation() } // MARK: - Bottom action @@ -1005,6 +1243,9 @@ struct OnboardingExperienceView: View { } private var primaryActionDisabled: Bool { + if previewsAllScreens { + return false + } if isRequestingPermissions || (currentStep == .keyboardSwitch && !keyboardReady) { return true } @@ -1048,6 +1289,9 @@ struct OnboardingExperienceView: View { } private func performPrimaryAction() { + if advancePreviewIfNeeded() { + return + } switch currentStep { case .introduction: goForward() @@ -1072,6 +1316,26 @@ struct OnboardingExperienceView: View { } } + private func advancePreviewIfNeeded() -> Bool { + #if DEBUG + guard previewsAllScreens else { return false } + guard previewPageIndex < Self.allPreviewDestinations.count - 1 else { + return true + } + withAnimation(Motion.soft) { + previewPageIndex += 1 + if case let .practice(feature) = activePreviewDestination { + practiceFeature = feature + practiceText = "" + didCopyPracticeSample = false + } + } + return true + #else + return false + #endif + } + // MARK: - State and actions private var permissionsReady: Bool { @@ -1269,8 +1533,16 @@ struct OnboardingExperienceView: View { } private func copyPracticeSample() { + let sample = AppL10n.string( + practiceClipboardSampleLocalizationKey, + language: previewLanguageOverride + ) + if isPreviewMode { + UIPasteboard.general.string = sample + didCopyPracticeSample = true + return + } guard let sessionID = practiceSessionID else { return } - let sample = AppL10n.string("onboarding.experience.practice.sample") guard KeyboardSetupBridge.seedOOBEClipboardMaterial( sample, sessionID: sessionID @@ -1451,6 +1723,32 @@ struct OnboardingExperienceView: View { } } +#if DEBUG +@MainActor +struct OOBEPreviewHarness: View { + @ObservedObject private var config = ProviderConfig.shared + @StateObject private var flowManager = FlowSessionManager() + @StateObject private var accountSession: AccountSessionCoordinator + + init() { + _accountSession = StateObject( + wrappedValue: AccountSessionCoordinator( + dependencies: LiveAccountDependencyFactory.make() + ) + ) + } + + var body: some View { + ThemedRoot { + OnboardingExperienceView(config: config) + .environmentObject(flowManager) + .environmentObject(accountSession) + } + .environment(\.locale, OnboardingExperienceView.previewLanguage.swiftUILocale) + } +} +#endif + private extension View { func practiceSetupCard(palette: ThemePalette) -> some View { frame(maxWidth: .infinity, minHeight: 172) @@ -1459,6 +1757,7 @@ private extension View { palette.surface, in: RoundedRectangle(cornerRadius: Radius.large, style: .continuous) ) + .cardElevation() } } @@ -1556,5 +1855,6 @@ private struct KeyboardSettingsPreview: View { .padding(Spacing.md) } .background(palette.surface, in: RoundedRectangle(cornerRadius: Radius.xl, style: .continuous)) + .cardElevation() } } diff --git a/OSGKeyboard/Views/PersonalDictionaryEntrySheet.swift b/OSGKeyboard/Views/PersonalDictionaryEntrySheet.swift index b88edef..0a09c83 100644 --- a/OSGKeyboard/Views/PersonalDictionaryEntrySheet.swift +++ b/OSGKeyboard/Views/PersonalDictionaryEntrySheet.swift @@ -26,15 +26,14 @@ struct PersonalDictionaryEntrySheet: View { TextField("settings.personalDictionary.add.field", text: $term) .font(TypeStyle.body) .foregroundStyle(palette.textPrimary) - .tint(palette.accent) + .tint(palette.textPrimary) .focused($termFocused) .textInputAutocapitalization(.never) .autocorrectionDisabled() .settingsListRow() - .background(palette.surface, in: RoundedRectangle(cornerRadius: Radius.large, style: .continuous)) - .overlay( - RoundedRectangle(cornerRadius: Radius.large, style: .continuous) - .stroke(termFocused ? palette.accent : palette.divider, lineWidth: termFocused ? 1 : 0.5) + .background( + palette.formSurface, + in: RoundedRectangle(cornerRadius: Radius.large, style: .continuous) ) Text("settings.personalDictionary.add.footer") @@ -56,11 +55,12 @@ struct PersonalDictionaryEntrySheet: View { .toolbar { ToolbarItem(placement: .cancellationAction) { Button("common.cancel") { dismiss() } + .tint(palette.textPrimary) } ToolbarItem(placement: .confirmationAction) { Button("common.save") { save() } .disabled(trimmedTerm.isEmpty) - .tint(palette.accent) + .tint(palette.textPrimary) } } .onAppear { diff --git a/OSGKeyboard/Views/PersonalDictionaryView.swift b/OSGKeyboard/Views/PersonalDictionaryView.swift index 6484718..ffff9f9 100644 --- a/OSGKeyboard/Views/PersonalDictionaryView.swift +++ b/OSGKeyboard/Views/PersonalDictionaryView.swift @@ -45,6 +45,7 @@ struct PersonalDictionaryView: View { } label: { Image(systemName: "trash") } + .tint(palette.textPrimary) .accessibilityLabel(AppL10n.string("settings.personalDictionary.clearAll")) .confirmationDialog( AppL10n.string("settings.personalDictionary.clearAll.confirmTitle"), @@ -67,6 +68,7 @@ struct PersonalDictionaryView: View { } label: { Image(systemName: "plus") } + .tint(palette.textPrimary) .accessibilityLabel(AppL10n.string("settings.personalDictionary.add.title")) } } @@ -94,36 +96,40 @@ struct PersonalDictionaryView: View { } } - // MARK: - List + // MARK: - Card list private var list: some View { - List { - ForEach(filteredSections, id: \.0) { _, items in - Section { - ForEach(items) { entry in - entryRow(entry) - .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) - .listRowBackground(palette.surface) - .listRowSeparatorTint(palette.divider) - } - .onDelete { offsets in - delete(items: items, at: offsets) + ScrollView { + CardPageContent(topPadding: Spacing.lg) { + ForEach(filteredSections, id: \.0) { category, items in + CardSection( + title: SharedL10n.string(category.labelKey, language: config.uiLanguage) + ) { + LazyVStack(spacing: CardLayoutMetrics.compactItemSpacing) { + ForEach(items) { entry in + entryRow(entry) + .surfaceCard() + .contextMenu { + Button("common.delete", role: .destructive) { + delete(entry) + } + } + } + } } } } + .tabBarScrollBottomPadding() } - .listStyle(.insetGrouped) - .listSectionSpacing(CardLayoutMetrics.sectionSpacing) - .scrollContentBackground(.hidden) + .scrollClipDisabled() .background(palette.background) - // 让搜索栏与首个词条之间留出呼吸空间,视觉更透气。 - .contentMargins(.top, Spacing.lg, for: .scrollContent) + // CardPageContent keeps the search results and their section labels + // on the same horizontal guide while preserving top breathing room. .searchable( text: $searchText, placement: .navigationBarDrawer(displayMode: .always), prompt: "settings.personalDictionary.search.prompt" ) - .tabBarListScrollBottomMargin() } private func entryRow(_ entry: PersonalDictionary.Entry) -> some View { @@ -254,12 +260,6 @@ struct PersonalDictionaryView: View { persist() } - private func delete(items: [PersonalDictionary.Entry], at offsets: IndexSet) { - for index in offsets { - delete(items[index]) - } - } - private func clearAll() { dictionary.recordClearAll() generatingAliasEntryIDs = [] diff --git a/OSGKeyboard/Views/PolishStylesView.swift b/OSGKeyboard/Views/PolishStylesView.swift index 728a124..218b6fd 100644 --- a/OSGKeyboard/Views/PolishStylesView.swift +++ b/OSGKeyboard/Views/PolishStylesView.swift @@ -10,6 +10,7 @@ import SwiftUI @MainActor struct PolishStylesView: View { @Environment(\.themePalette) private var palette + @Environment(\.colorScheme) private var colorScheme @ObservedObject private var config = ProviderConfig.shared @ObservedObject private var history = SpeechHistoryStore.shared @@ -51,6 +52,7 @@ struct PolishStylesView: View { } .tabBarScrollBottomPadding() } + .scrollClipDisabled() .background(palette.background) .navigationTitle("polishStyles.title") .navigationBarTitleDisplayMode(.large) @@ -61,6 +63,7 @@ struct PolishStylesView: View { } label: { Image(systemName: "plus") } + .tint(palette.textPrimary) .disabled(catalog.entries.count >= PolishStyleLimits.maximumUserPacks) .accessibilityLabel(Text("polishStyles.add")) } @@ -111,6 +114,15 @@ struct PolishStylesView: View { let reachedLimit = catalog.entries.count >= PolishStyleLimits.maximumUserPacks let isActionAvailable = corpus.isReady && !reachedLimit let canGenerate = isActionAvailable && !isGeneratingLearnedStyle + let completedCharacterCount = min(corpus.effectiveCharacterCount, required) + let learnedFraction = required > 0 + ? Double(completedCharacterCount) / Double(required) + : 0 + let progressDescription = AppL10n.format( + "polishStyles.learn.progress", + Int64(corpus.effectiveCharacterCount), + Int64(required) + ) return VStack(alignment: .leading, spacing: Spacing.md) { HStack(alignment: .top, spacing: Spacing.md) { @@ -134,20 +146,19 @@ struct PolishStylesView: View { } } - ProgressView( - value: Double(min(corpus.effectiveCharacterCount, required)), - total: Double(required) + ProgressTrack( + segments: [ + ProgressTrackSegment( + fraction: learnedFraction, + color: palette.accent + ) + ] ) - .tint(palette.accent) + .accessibilityLabel(Text("polishStyles.learn.title")) + .accessibilityValue(Text(progressDescription)) HStack { - Text( - AppL10n.format( - "polishStyles.learn.progress", - Int64(corpus.effectiveCharacterCount), - Int64(required) - ) - ) + Text(progressDescription) .font(TypeStyle.caption2) .foregroundStyle(palette.textTertiary) @@ -224,6 +235,19 @@ struct PolishStylesView: View { private func packCard(_ pack: PolishStylePack) -> some View { let isSelected = pack.id == activeID + let shape = RoundedRectangle(cornerRadius: Radius.xl, style: .continuous) + let selectedFillColors = colorScheme == .dark + ? [ + OSGColor.selectedCardFillLeadingDark, + OSGColor.selectedCardFillTrailingDark + ] + : [ + OSGColor.selectedCardFillLeadingLight, + OSGColor.selectedCardFillTrailingLight + ] + let selectedStroke = colorScheme == .dark + ? OSGColor.selectedCardStrokeDark + : OSGColor.selectedCardStrokeLight return ZStack(alignment: .topTrailing) { Button { activate(pack) @@ -263,11 +287,25 @@ struct PolishStylesView: View { CatalogCardChrome.checkIcon(palette: palette) } } - .background( - isSelected ? palette.accentMuted : palette.surface, - in: RoundedRectangle(cornerRadius: Radius.xl, style: .continuous) - ) - .clipShape(RoundedRectangle(cornerRadius: Radius.xl, style: .continuous)) + .background { + shape + .fill(palette.surface) + .overlay { + if isSelected { + shape + .fill( + LinearGradient( + colors: selectedFillColors, + startPoint: .topLeading, + endPoint: .bottomTrailing + ) + ) + .overlay(shape.stroke(selectedStroke, lineWidth: 0.75)) + } + } + } + .clipShape(shape) + .cardElevation(accented: isSelected) .contextMenu { Button("polishStyles.duplicate") { duplicate(pack) @@ -423,7 +461,7 @@ private struct PolishStylePromptDetailSheet: View { .textSelection(.enabled) .frame(maxWidth: .infinity, alignment: .leading) .padding(Spacing.md) - .surfaceCard() + .surfaceCard(elevated: false) } } .background(palette.background) @@ -432,6 +470,7 @@ private struct PolishStylePromptDetailSheet: View { .toolbar { ToolbarItem(placement: .confirmationAction) { Button("common.done") { dismiss() } + .tint(palette.textPrimary) } } } @@ -467,9 +506,13 @@ private struct PolishStyleEditorSheet: View { Form { Section("polishStyles.editor.name") { TextField("polishStyles.editor.namePlaceholder", text: $name) + .settingsListRow() + .cardListRow(elevated: false) } Section { Toggle("polishStyles.editor.allowsAddedEmoji", isOn: $allowsAddedEmoji) + .settingsListRow() + .cardListRow(elevated: false) } footer: { Text("polishStyles.editor.allowsAddedEmoji.hint") } @@ -477,6 +520,8 @@ private struct PolishStyleEditorSheet: View { TextEditor(text: $prompt) .font(.body.monospaced()) .frame(minHeight: 320) + .padding(Spacing.md) + .cardListRow(elevated: false) .onChange(of: prompt) { _, newValue in // Paste-only custom prompts that declare emoji opt-in // should flip the toggle so post-processing keeps them. @@ -505,6 +550,7 @@ private struct PolishStyleEditorSheet: View { .toolbar { ToolbarItem(placement: .cancellationAction) { Button("common.cancel") { dismiss() } + .tint(palette.textPrimary) } ToolbarItem(placement: .confirmationAction) { Button("common.save") { @@ -525,6 +571,7 @@ private struct PolishStyleEditorSheet: View { || prompt.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty || prompt.count > PolishStyleLimits.maximumPromptCharacters ) + .tint(palette.textPrimary) } } } diff --git a/OSGKeyboard/Views/ReleaseNotesScreenshotHarness.swift b/OSGKeyboard/Views/ReleaseNotesScreenshotHarness.swift new file mode 100644 index 0000000..11bcf62 --- /dev/null +++ b/OSGKeyboard/Views/ReleaseNotesScreenshotHarness.swift @@ -0,0 +1,86 @@ +// ReleaseNotesScreenshotHarness.swift +// OSGKeyboard · Main App +// +// Deterministic DEBUG hosts for capturing real production cards used by the +// remote What's New page. These hosts seed only simulator-local fixture data. + +#if DEBUG +import OSGKeyboardShared +import SwiftUI + +@MainActor +struct PolishStylesScreenshotHarness: View { + private let language: AppUILanguage + + init() { + language = ReleaseNotesScreenshotFixture.language + ProviderConfig.shared.uiLanguage = language + ReleaseNotesScreenshotFixture.seedReadyStyleCorpus(language: language) + } + + var body: some View { + ThemedRoot { + PolishStylesView() + } + .environment(\.locale, language.swiftUILocale) + .preferredColorScheme(.light) + } +} + +@MainActor +struct HomeDictionaryScreenshotHarness: View { + @StateObject private var flowManager = FlowSessionManager() + private let language: AppUILanguage + + init() { + language = ReleaseNotesScreenshotFixture.language + ProviderConfig.shared.uiLanguage = language + ReleaseNotesScreenshotFixture.seedFrequentTerms(language: language) + } + + var body: some View { + ThemedRoot { + HomeView() + .environmentObject(flowManager) + } + .environment(\.locale, language.swiftUILocale) + .preferredColorScheme(.light) + } +} + +@MainActor +private enum ReleaseNotesScreenshotFixture { + static var language: AppUILanguage { + ProcessInfo.processInfo.arguments.contains("--screenshot-lang=en") + ? .english + : .chinese + } + + static func seedReadyStyleCorpus(language: AppUILanguage) { + let history = SpeechHistoryStore.shared + history.clearAll() + let token = language == .chinese ? "自然表达习惯" : "NaturalVoice" + let sample = String(String(repeating: token, count: 500).prefix(2_500)) + history.append( + text: sample, + prePolishText: sample, + engineMode: "local", + source: .dictation + ) + history.reloadFromDisk() + } + + static func seedFrequentTerms(language: AppUILanguage) { + let store = FrequentTermStore() + store.clear() + let terms = language == .chinese + ? ["少数派", "工作流", "语音键盘"] + : ["VoiceFlow", "PromptKit", "OSGAgent"] + for term in terms { + for _ in 0..<4 { + store.recordCommittedText(term) + } + } + } +} +#endif diff --git a/OSGKeyboard/Views/ReleaseNotesSheet.swift b/OSGKeyboard/Views/ReleaseNotesSheet.swift index c882c5c..6341486 100644 --- a/OSGKeyboard/Views/ReleaseNotesSheet.swift +++ b/OSGKeyboard/Views/ReleaseNotesSheet.swift @@ -48,7 +48,7 @@ struct ReleaseNotesSheet: View { if isLoading { ProgressView() - .tint(palette.accent) + .tint(palette.textPrimary) } } } else { @@ -70,6 +70,7 @@ struct ReleaseNotesSheet: View { Button(AppL10n.string("common.done", language: language)) { dismiss() } + .tint(palette.textPrimary) } } } diff --git a/OSGKeyboard/Views/SettingsProviderControls.swift b/OSGKeyboard/Views/SettingsProviderControls.swift index 38242c3..6d582ed 100644 --- a/OSGKeyboard/Views/SettingsProviderControls.swift +++ b/OSGKeyboard/Views/SettingsProviderControls.swift @@ -93,10 +93,6 @@ struct SettingsCredentialRow: View { .padding(.horizontal, Spacing.sm) .frame(minHeight: 38) .background(palette.surfaceElevated, in: RoundedRectangle(cornerRadius: Radius.medium, style: .continuous)) - .overlay( - RoundedRectangle(cornerRadius: Radius.medium, style: .continuous) - .stroke(palette.divider, lineWidth: 0.5) - ) } private func iconButton(systemName: String, label: LocalizedStringKey, action: @escaping () -> Void) -> some View { @@ -157,8 +153,8 @@ struct SettingsModelPickerRow: View { .onDisappear { invalidateRequest() } } - /// Editable model id + trailing menu chevron in one well (same chrome as - /// Mac `MacPickerFieldBox`). Chevron is overlaid so it stays inside the border. + /// Editable model id + trailing menu chevron in one well. Chevron is + /// overlaid so it stays inside the field surface. private var comboField: some View { let shape = RoundedRectangle(cornerRadius: Radius.medium, style: .continuous) let chevronWidth: CGFloat = 28 @@ -173,7 +169,6 @@ struct SettingsModelPickerRow: View { .padding(.trailing, chevronWidth + Spacing.sm) .frame(maxWidth: .infinity, minHeight: controlHeight, alignment: .leading) .background(palette.surfaceElevated, in: shape) - .overlay(shape.stroke(palette.divider, lineWidth: 0.5)) Menu { if models.isEmpty { diff --git a/OSGKeyboard/Views/SettingsView.swift b/OSGKeyboard/Views/SettingsView.swift index f464dfb..f87b2dc 100644 --- a/OSGKeyboard/Views/SettingsView.swift +++ b/OSGKeyboard/Views/SettingsView.swift @@ -24,6 +24,7 @@ private enum SettingsRoute: Hashable { case general case aiAgent case clipboard + case diagnostics case about } @@ -144,6 +145,8 @@ struct SettingsView: View { AIAgentSettingsView(config: config) case .clipboard: ClipboardSettingsView(config: config) + case .diagnostics: + FlowDiagnosticsSettingsView() case .about: AboutSettingsView(config: config) } @@ -314,6 +317,10 @@ struct SettingsView: View { Divider().background(palette.divider) + settingsRouteButton(.diagnostics, title: "settings.diagnostics.title") + + Divider().background(palette.divider) + // Opens the remote release-notes sheet (same as post-upgrade prompt). SettingsVersionRow() } @@ -356,9 +363,11 @@ struct SettingsView: View { private struct SettingsSheetDismissButton: View { @Environment(\.dismiss) private var dismiss + @Environment(\.themePalette) private var palette var body: some View { Button("common.done") { dismiss() } + .tint(palette.textPrimary) } } diff --git a/OSGKeyboard/Views/TypingInputSettingsView.swift b/OSGKeyboard/Views/TypingInputSettingsView.swift index 74f2865..83a8848 100644 --- a/OSGKeyboard/Views/TypingInputSettingsView.swift +++ b/OSGKeyboard/Views/TypingInputSettingsView.swift @@ -12,112 +12,163 @@ struct TypingInputSettingsView: View { @ObservedObject private var config = ProviderConfig.shared @ObservedObject private var configuration = TypingInputConfiguration.shared @ObservedObject private var deployment = RimeDeploymentController.shared - @State private var showClearHabitsConfirmation = false private var isDeploying: Bool { deployment.isDeploying } var body: some View { - List { - Section { - Picker( - selection: $configuration.schema + ScrollView { + LazyVStack(alignment: .leading, spacing: CardLayoutMetrics.sectionSpacing) { + settingsSection( + title: AppL10n.string( + "settings.typingInput.schema.section", + language: config.uiLanguage + ) ) { - ForEach(TypingInputSchema.allCases) { schema in - Text(AppL10n.string(schema.labelKey, language: config.uiLanguage)) - .font(TypeStyle.body) - .tag(schema) - } - } label: { - Text(AppL10n.string("settings.typingInput.schema.picker", language: config.uiLanguage)) - .font(TypeStyle.body) - } - .pickerStyle(.inline) - } header: { - Text(AppL10n.string("settings.typingInput.schema.section", language: config.uiLanguage)) - .font(TypeStyle.caption2) - } - - Section { - ForEach(PinyinFuzzyPair.allCases) { pair in - Toggle( - isOn: Binding( - get: { configuration.fuzzyPairs.contains(pair) }, - set: { enabled in - configuration.setFuzzyPair(pair, enabled: enabled) - deployUpdatedSchemas() - } - ) - ) { - Text(pair.displayName) - .font(TypeStyle.body) - } - } - } header: { - Text(AppL10n.string("settings.typingInput.fuzzy.section", language: config.uiLanguage)) - .font(TypeStyle.caption2) - } footer: { - Text(AppL10n.string("settings.typingInput.fuzzy.footer", language: config.uiLanguage)) - .font(TypeStyle.caption2) - } - - Section { - HStack { - Text(AppL10n.string("settings.typingInput.resources.status", language: config.uiLanguage)) - .font(TypeStyle.body) - Spacer() - if isDeploying { - ProgressView() - .controlSize(.small) - } else { - Text(statusText) - .font(TypeStyle.body) - .foregroundStyle( - hasDeploymentError ? palette.danger : palette.textSecondary - ) - } + schemaOptionsCard } - Button(AppL10n.string("settings.typingInput.resources.redeploy", language: config.uiLanguage)) { - deployUpdatedSchemas() + settingsSection( + title: AppL10n.string( + "settings.typingInput.fuzzy.section", + language: config.uiLanguage + ), + footer: AppL10n.string( + "settings.typingInput.fuzzy.footer", + language: config.uiLanguage + ) + ) { + fuzzyPairsCard } - .font(TypeStyle.body) - .disabled(isDeploying) - } header: { - Text(AppL10n.string("settings.typingInput.resources.section", language: config.uiLanguage)) - .font(TypeStyle.caption2) - } - Section { - Button(AppL10n.string("settings.typingInput.habits.clear", language: config.uiLanguage)) { - showClearHabitsConfirmation = true + settingsSection( + title: AppL10n.string( + "settings.typingInput.resources.section", + language: config.uiLanguage + ) + ) { + resourcesCard } - .font(TypeStyle.body) - .disabled(isDeploying) - .foregroundStyle(palette.danger) - } footer: { - Text(AppL10n.string("settings.typingInput.habits.footer", language: config.uiLanguage)) - .font(TypeStyle.caption2) } + .padding(.horizontal, Spacing.lg) + .padding(.top, Spacing.md) + .padding(.bottom, Spacing.lg) } - .listSectionSpacing(CardLayoutMetrics.sectionSpacing) - .scrollContentBackground(.hidden) + .scrollClipDisabled() .background(palette.background) .navigationTitle(AppL10n.string("settings.typingInput.title", language: config.uiLanguage)) .navigationBarTitleDisplayMode(.inline) - .confirmationDialog( - AppL10n.string("settings.typingInput.habits.clear.title", language: config.uiLanguage), - isPresented: $showClearHabitsConfirmation, - titleVisibility: .visible - ) { - Button( - AppL10n.string("settings.typingInput.habits.clear.confirm", language: config.uiLanguage), - role: .destructive - ) { - deployment.clearTypingHabits() + } + + private var schemaOptionsCard: some View { + VStack(spacing: 0) { + ForEach(TypingInputSchema.allCases) { schema in + schemaOptionRow(schema) + + if schema != TypingInputSchema.allCases.last { + Divider().background(palette.divider) + } + } + } + .surfaceCard() + } + + private func schemaOptionRow(_ schema: TypingInputSchema) -> some View { + let isSelected = configuration.schema == schema + return Button { + guard !isSelected else { return } + configuration.schema = schema + } label: { + HStack(spacing: Spacing.sm) { + Text(AppL10n.string(schema.labelKey, language: config.uiLanguage)) + .font(.system(size: 15, weight: isSelected ? .semibold : .regular)) + .foregroundStyle(isSelected ? palette.accent : palette.textPrimary) + Spacer() + if isSelected { + Image(systemName: "checkmark") + .font(.system(size: 13, weight: .semibold)) + .foregroundStyle(palette.accent) + } + } + .settingsListRow() + .contentShape(Rectangle()) + } + .buttonStyle(.plain) + .accessibilityValue(isSelected ? "selected" : "notSelected") + } + + private var fuzzyPairsCard: some View { + VStack(spacing: 0) { + ForEach(PinyinFuzzyPair.allCases) { pair in + Toggle( + isOn: Binding( + get: { configuration.fuzzyPairs.contains(pair) }, + set: { enabled in + configuration.setFuzzyPair(pair, enabled: enabled) + deployUpdatedSchemas() + } + ) + ) { + Text(pair.displayName) + .font(TypeStyle.body) + } + .settingsListRow() + + if pair != PinyinFuzzyPair.allCases.last { + Divider().background(palette.divider) + } + } + } + .surfaceCard() + } + + private var resourcesCard: some View { + VStack(spacing: 0) { + HStack { + Text(AppL10n.string("settings.typingInput.resources.status", language: config.uiLanguage)) + .font(TypeStyle.body) + Spacer() + if isDeploying { + ProgressView() + .controlSize(.small) + } else { + Text(statusText) + .font(TypeStyle.body) + .foregroundStyle( + hasDeploymentError ? palette.danger : palette.textSecondary + ) + } + } + .settingsListRow() + + Divider().background(palette.divider) + + Button(AppL10n.string("settings.typingInput.resources.redeploy", language: config.uiLanguage)) { + deployUpdatedSchemas() + } + .font(TypeStyle.body) + .settingsListRow() + .disabled(isDeploying) + } + .surfaceCard() + } + + private func settingsSection( + title: String? = nil, + footer: String? = nil, + @ViewBuilder content: () -> Content + ) -> some View { + VStack(alignment: .leading, spacing: SettingsListMetrics.sectionLabelSpacing) { + if let title { + Text(title) + .cardSectionLabel() + } + content() + if let footer { + Text(footer) + .font(TypeStyle.caption2) + .foregroundStyle(palette.textSecondary) + .fixedSize(horizontal: false, vertical: true) } - Button(AppL10n.string("common.cancel", language: config.uiLanguage), role: .cancel) {} - } message: { - Text(AppL10n.string("settings.typingInput.habits.clear.message", language: config.uiLanguage)) } } diff --git a/OSGKeyboard/en.lproj/Localizable.strings b/OSGKeyboard/en.lproj/Localizable.strings index 59799b1..3a266c5 100644 --- a/OSGKeyboard/en.lproj/Localizable.strings +++ b/OSGKeyboard/en.lproj/Localizable.strings @@ -38,14 +38,14 @@ "onboarding.polish.localHint" = "Fill in an API key to enable AI polish. Without a key, raw ASR text is inserted."; "onboarding.experience.intro.eyebrow" = "AI AGENT VOICE KEYBOARD"; "onboarding.experience.intro.title" = "Speak. It’s written."; -"onboarding.experience.intro.subtitle" = "More than dictation. OSGKeyboard understands, writes, translates, and edits—then its AI Agent helps with what comes next."; +"onboarding.experience.intro.subtitle" = "The best voice keyboard, with your best personal AI Agent."; "onboarding.experience.intro.action" = "Get Started"; -"onboarding.experience.privacy.local.title" = "On-device transcription available"; -"onboarding.experience.privacy.local.body" = "With the local engine, speech recognition stays on your device."; -"onboarding.experience.privacy.cloud.title" = "Clear cloud boundaries"; -"onboarding.experience.privacy.cloud.body" = "Audio or text is sent only when you choose cloud recognition or an AI feature, and only to the configured service."; -"onboarding.experience.privacy.anywhere.title" = "Your AI Agent, in any app"; -"onboarding.experience.privacy.anywhere.body" = "Dictate, translate, edit, and act from chat, email, notes, or code."; +"onboarding.experience.promise.voice.title" = "Smart voice keyboard"; +"onboarding.experience.promise.voice.body" = "Type 6× faster with multiple polishing styles."; +"onboarding.experience.promise.agent.title" = "Personal AI Agent"; +"onboarding.experience.promise.agent.body" = "Translate, summarize, and learn your tone to write smart replies."; +"onboarding.experience.promise.privacy.title" = "Privacy protected"; +"onboarding.experience.promise.privacy.body" = "Bring your own AI API key and stay in control of your privacy."; "onboarding.experience.permissions.title" = "Let us hear you"; "onboarding.experience.permissions.subtitle" = "Voice input needs microphone and speech recognition access. Two system prompts will appear next."; "onboarding.experience.permissions.systemHint" = "These permissions are used only when you start voice input."; @@ -68,7 +68,8 @@ "onboarding.experience.keyboardSwitch.checkSettings" = "Check Full Access in Settings"; "onboarding.experience.keyboardSwitch.waitingAction" = "Waiting for Verification"; "onboarding.experience.practice.skip" = "Skip for Now"; -"onboarding.experience.practice.sample" = "I’ll arrive tomorrow afternoon and message you when I’m there."; +"onboarding.experience.practice.translateSample" = "我明天下午到,到了以后给你消息。"; +"onboarding.experience.practice.replySample" = "I’ll arrive tomorrow afternoon and message you when I’m there."; "onboarding.experience.practice.editorTitle" = "New Message"; "onboarding.experience.practice.permissionsTitle" = "Enable voice access"; "onboarding.experience.practice.permissionsSubtitle" = "Go back to finish permission setup, then try your first voice input."; @@ -95,17 +96,18 @@ "onboarding.experience.practice.readAloud" = "Read aloud"; "onboarding.experience.practice.voiceSample" = "Today is Thursday. Where are you planning to go for fun?"; "onboarding.experience.practice.translateTitle" = "Try Translate"; -"onboarding.experience.practice.translateSubtitle" = "Copy the sample below, tap Translate in OSGKeyboard, then insert the result into the field."; +"onboarding.experience.practice.translateSubtitle" = "Copy the Chinese sample, translate it into English in OSGKeyboard, then insert the result."; "onboarding.experience.practice.replyTitle" = "Try Reply"; -"onboarding.experience.practice.replySubtitle" = "Use the same sample, tap Reply in the keyboard, then insert the generated response."; +"onboarding.experience.practice.replySubtitle" = "Copy the sample below, tap Reply in the keyboard, then insert the generated response."; "onboarding.experience.practice.askAITitle" = "Ask AI"; "onboarding.experience.practice.askAISubtitle" = "Hold the keyboard microphone and ask a question. Tap it again to stop recording and wait for the answer, then insert the result."; +"onboarding.experience.practice.askAIPrompt" = "Try asking: “What’s in the news today?”"; "onboarding.experience.practice.copyAction" = "Copy Sample"; "onboarding.experience.practice.copied" = "Copied. Switch to OSGKeyboard."; "onboarding.experience.practice.waiting" = "Waiting for you to complete this step in the keyboard…"; "onboarding.experience.login.title" = "Sign in for 1,000 credits"; -"onboarding.experience.login.subtitle" = "You’ve completed all four lessons. Sign in to keep using OSG managed AI without entering an API key."; -"onboarding.experience.login.body" = "If this device is eligible for the first-time signup reward, 1,000 credits are added automatically. Device integrity checks prevent duplicate claims."; +"onboarding.experience.login.subtitle" = "Sign in for free credits and start using AI without an API key."; +"onboarding.experience.login.body" = "Register for the first time to get free credits, then invite friends to earn more."; "onboarding.experience.login.skip" = "Not Now"; "onboarding.experience.login.signedIn" = "Signed in. Your credits are up to date."; "onboarding.experience.login.balance %lld" = "Current balance: %lld credits"; @@ -279,6 +281,18 @@ "settings.systemPrompt.hint" = "Used by the cloud LLM when polishing your dictation. Local engine skips this step."; "settings.about.title" = "About"; "settings.version.title" = "Version"; +"settings.diagnostics.title" = "Startup Diagnostics"; +"settings.diagnostics.section" = "Saved Reports"; +"settings.diagnostics.count" = "%d startup failure reports"; +"settings.diagnostics.empty" = "No startup failures have been saved."; +"settings.diagnostics.latest" = "Latest: %@"; +"settings.diagnostics.export" = "Export Diagnostic Logs"; +"settings.diagnostics.clear" = "Clear Diagnostic Logs"; +"settings.diagnostics.clear.title" = "Clear diagnostic logs?"; +"settings.diagnostics.clear.message" = "This permanently removes all saved Flow startup failure reports from this device."; +"settings.diagnostics.clear.confirm" = "Clear Logs"; +"settings.diagnostics.privacy.section" = "Privacy"; +"settings.diagnostics.privacy.body" = "Reports stay on this device until you export or clear them. They include startup state, timings, error codes, and memory usage, but never audio, transcripts, clipboard content, prompts, API keys, or account identifiers."; "releaseNotes.title" = "What's New"; "releaseNotes.openHint" = "View release notes for this version"; "releaseNotes.unavailable.title" = "Cannot load page"; @@ -833,11 +847,12 @@ "account.signedOut.body" = "Register and invite friends to earn credits."; "account.signedOut.localUnaffected" = "On-device features and your own API keys work without an account."; "home.accountRewards.signedOut.title" = "Sign in for free credits"; -"home.accountRewards.signedOut.summary" = "Eligible new users receive 1,000 credits; after an invited friend completes qualifying usage, you both receive 1,000 credits."; +"home.accountRewards.signedOut.summary" = "Register and invite friends to earn credits."; "account.signIn.apple" = "Sign in with Apple"; "account.signIn.hint" = "Creates or opens your optional OSG account."; "account.signIn.loading" = "Signing in…"; "account.error.sessionExpired" = "Your session expired. Sign in again to continue."; +"account.error.appleReauthenticationRequired" = "Sign in with Apple again to keep your account protected."; "account.summary.section" = "Account"; "account.summary.signedIn" = "Signed in"; "account.summary.protected" = "Protected account"; @@ -905,7 +920,7 @@ "account.referral.friendRewardQualification" = "Your friend also receives %@ credits after qualifying usage"; "account.referral.shareShort" = "Share"; "account.referral.inviteTitle" = "Invite"; -"account.referral.equalRewardDescription" = "For each successful invitation, you and your friend each receive 1,000 credits."; +"account.referral.equalRewardDescription" = "Invite friends—both receive 1,000 credits"; "account.referral.equalRewardGeneric" = "For each successful invitation, both of you receive credits."; "account.referral.summaryCompact" = "Rewarded %d · Pending %d"; "account.referral.preparingLink" = "Loading your invitation link…"; diff --git a/OSGKeyboard/zh-Hans.lproj/Localizable.strings b/OSGKeyboard/zh-Hans.lproj/Localizable.strings index e64ca2d..4c7fb3d 100644 --- a/OSGKeyboard/zh-Hans.lproj/Localizable.strings +++ b/OSGKeyboard/zh-Hans.lproj/Localizable.strings @@ -38,14 +38,14 @@ "onboarding.polish.localHint" = "填写 API Key 才能开启 AI 润色;未填写时将直接插入原始识别结果。"; "onboarding.experience.intro.eyebrow" = "AI AGENT 语音输入法"; "onboarding.experience.intro.title" = "开口,即成文"; -"onboarding.experience.intro.subtitle" = "不只是语音输入。OSGKeyboard 能听懂、成文、翻译和编辑,也能调用 AI Agent 帮你处理下一步。"; +"onboarding.experience.intro.subtitle" = "最好的语音输入法,最好的贴身 AI Agent"; "onboarding.experience.intro.action" = "开始设置"; -"onboarding.experience.privacy.local.title" = "支持设备端转写"; -"onboarding.experience.privacy.local.body" = "使用本地引擎时,语音识别在你的设备上完成。"; -"onboarding.experience.privacy.cloud.title" = "明确的云端边界"; -"onboarding.experience.privacy.cloud.body" = "只有选择云端识别或 AI 功能时,相应的语音或文字才会发送给已配置的服务。"; -"onboarding.experience.privacy.anywhere.title" = "AI Agent,随处可用"; -"onboarding.experience.privacy.anywhere.body" = "在聊天、邮件、笔记或代码编辑中,随时口述、翻译、编辑和处理任务。"; +"onboarding.experience.promise.voice.title" = "智能语音输入法"; +"onboarding.experience.promise.voice.body" = "6 倍输入速度,各种润色方式"; +"onboarding.experience.promise.agent.title" = "随身 AI Agent"; +"onboarding.experience.promise.agent.body" = "翻译、总结,还能学习你的语气,帮你智能回复"; +"onboarding.experience.promise.privacy.title" = "隐私有保障"; +"onboarding.experience.promise.privacy.body" = "支持自带 AI API Key,隐私尽在掌握"; "onboarding.experience.permissions.title" = "让我们听见你"; "onboarding.experience.permissions.subtitle" = "语音输入需要麦克风和语音识别。接下来会依次出现两个系统提示。"; "onboarding.experience.permissions.systemHint" = "权限只在你主动使用语音输入时生效。"; @@ -68,7 +68,8 @@ "onboarding.experience.keyboardSwitch.checkSettings" = "返回设置检查完整访问"; "onboarding.experience.keyboardSwitch.waitingAction" = "等待验证"; "onboarding.experience.practice.skip" = "暂时跳过体验"; -"onboarding.experience.practice.sample" = "我明天下午到,到了以后给你消息。"; +"onboarding.experience.practice.translateSample" = "I’ll arrive tomorrow afternoon and message you when I’m there."; +"onboarding.experience.practice.replySample" = "我明天下午到,到了以后给你消息。"; "onboarding.experience.practice.editorTitle" = "新消息"; "onboarding.experience.practice.permissionsTitle" = "开启语音权限"; "onboarding.experience.practice.permissionsSubtitle" = "返回上一步完成权限设置,然后开始第一次语音输入。"; @@ -95,17 +96,18 @@ "onboarding.experience.practice.readAloud" = "请照着念"; "onboarding.experience.practice.voiceSample" = "今天是礼拜四,你打算去哪里玩?"; "onboarding.experience.practice.translateTitle" = "试试翻译"; -"onboarding.experience.practice.translateSubtitle" = "先复制下面的示例,再在 OSGKeyboard 中点击「翻译」。确认结果后插入文本框。"; +"onboarding.experience.practice.translateSubtitle" = "复制下方英文,在 OSGKeyboard 中将它翻译成中文,再把结果插入文本框。"; "onboarding.experience.practice.replyTitle" = "试试回复"; -"onboarding.experience.practice.replySubtitle" = "使用同一段示例文字,点击键盘中的「回复」,然后插入生成的回复。"; +"onboarding.experience.practice.replySubtitle" = "复制下方示例文字,点击键盘中的「回复」,再插入生成的回复。"; "onboarding.experience.practice.askAITitle" = "问问 AI"; "onboarding.experience.practice.askAISubtitle" = "长按键盘麦克风,说出一个问题;再次点击结束录音并等待回答,然后将结果插入文本框。"; +"onboarding.experience.practice.askAIPrompt" = "试着问问:“今天有什么新闻?”"; "onboarding.experience.practice.copyAction" = "复制示例文字"; "onboarding.experience.practice.copied" = "已复制,请切换到 OSGKeyboard"; "onboarding.experience.practice.waiting" = "等待你在键盘中完成这一步…"; "onboarding.experience.login.title" = "登录领 1000 积分"; -"onboarding.experience.login.subtitle" = "四项体验已经完成。登录后可继续使用 OSG 托管 AI,无需填写 API Key。"; -"onboarding.experience.login.body" = "符合首次注册资格时,1000 积分会自动到账。设备完整性检查用于防止重复领取。"; +"onboarding.experience.login.subtitle" = "登录获赠积分,无需 API Key 即可开始免费使用"; +"onboarding.experience.login.body" = "首次注册,将免费获取积分,邀请好友,还可以获赠更多"; "onboarding.experience.login.skip" = "暂不登录"; "onboarding.experience.login.signedIn" = "登录成功,积分已刷新"; "onboarding.experience.login.balance %lld" = "当前余额:%lld 积分"; @@ -279,6 +281,18 @@ "settings.systemPrompt.hint" = "云端润色时使用。本地识别模式不会用到此提示词。"; "settings.about.title" = "关于"; "settings.version.title" = "版本"; +"settings.diagnostics.title" = "启动诊断"; +"settings.diagnostics.section" = "已保存报告"; +"settings.diagnostics.count" = "%d 份启动失败报告"; +"settings.diagnostics.empty" = "尚未保存启动失败报告。"; +"settings.diagnostics.latest" = "最近一次:%@"; +"settings.diagnostics.export" = "导出诊断日志"; +"settings.diagnostics.clear" = "清除诊断日志"; +"settings.diagnostics.clear.title" = "清除诊断日志?"; +"settings.diagnostics.clear.message" = "这将从本机永久删除所有 Flow 会话启动失败报告。"; +"settings.diagnostics.clear.confirm" = "清除日志"; +"settings.diagnostics.privacy.section" = "隐私"; +"settings.diagnostics.privacy.body" = "报告会保留在本机,直到你主动导出或清除。报告仅包含启动状态、耗时、错误码和内存占用,不会保存录音、转写、剪贴板内容、提示词、API Key 或账号标识。"; "releaseNotes.title" = "更新说明"; "releaseNotes.openHint" = "查看本版本更新说明"; "releaseNotes.unavailable.title" = "无法加载页面"; @@ -832,11 +846,12 @@ "account.signedOut.body" = "注册和邀请好友,都可获赠积分"; "account.signedOut.localUnaffected" = "本地功能与自有 API Key 无需账号也可继续使用。"; "home.accountRewards.signedOut.title" = "登录领免费积分"; -"home.accountRewards.signedOut.summary" = "符合资格的新用户可领 1,000 积分;邀请好友完成首次有效使用,双方各得 1,000 积分。"; +"home.accountRewards.signedOut.summary" = "注册和邀请好友,都可获赠积分"; "account.signIn.apple" = "通过 Apple 登录"; "account.signIn.hint" = "创建或打开可选的 OSG 账号。"; "account.signIn.loading" = "正在登录…"; "account.error.sessionExpired" = "登录状态已过期,请重新登录后继续。"; +"account.error.appleReauthenticationRequired" = "为继续保护账号,请重新通过 Apple 登录。"; "account.summary.section" = "账号"; "account.summary.signedIn" = "已登录"; "account.summary.protected" = "账号已安全保护"; @@ -904,7 +919,7 @@ "account.referral.friendRewardQualification" = "好友也可获得 %@ 积分,完成首次有效使用后发放"; "account.referral.shareShort" = "分享"; "account.referral.inviteTitle" = "邀请"; -"account.referral.equalRewardDescription" = "每成功邀请一位好友,你和好友各获得 1000 积分。"; +"account.referral.equalRewardDescription" = "邀请好友,共同获得1000积分"; "account.referral.equalRewardGeneric" = "每成功邀请一位好友,双方均可获得积分。"; "account.referral.summaryCompact" = "已奖励 %d · 待确认 %d"; "account.referral.preparingLink" = "正在加载邀请链接…"; diff --git a/OSGKeyboardExt/Typing/TypingRootView.swift b/OSGKeyboardExt/Typing/TypingRootView.swift index 29627b6..122725c 100644 --- a/OSGKeyboardExt/Typing/TypingRootView.swift +++ b/OSGKeyboardExt/Typing/TypingRootView.swift @@ -371,7 +371,9 @@ struct TypingRootView: View { } private var expandChevronFill: Color { - colorScheme == .dark ? Color(white: 0.30) : .white + colorScheme == .dark + ? OSGColor.keyboardDarkInputFill + : OSGColor.fixedLightContent } // MARK: - Editing toolbar (iPad) @@ -423,7 +425,9 @@ struct TypingRootView: View { } private var editingToolbarButtonFill: Color { - colorScheme == .dark ? Color(white: 0.30) : .white + colorScheme == .dark + ? OSGColor.keyboardDarkInputFill + : OSGColor.fixedLightContent } /// UIKit-recycled labels — no SwiftUI Button per candidate. @@ -773,7 +777,9 @@ struct TypingRootView: View { } private var selectedCandidateFill: Color { - colorScheme == .dark ? Color(white: 0.36) : .white + colorScheme == .dark + ? OSGColor.keyboardDarkInputFillRaised + : OSGColor.fixedLightContent } private var returnKeyFill: Color { @@ -786,7 +792,9 @@ struct TypingRootView: View { private var returnKeyBorder: Color { if state.returnKeyRole.usesActionFill { - return Color.black.opacity(colorScheme == .dark ? 0.10 : 0.08) + return colorScheme == .dark + ? OSGColor.keyboardActionBorderDark + : OSGColor.keyboardActionBorderLight } return palette.divider } @@ -799,7 +807,7 @@ struct TypingRootView: View { } private var returnKeyTextColor: Color { - state.returnKeyRole.usesActionFill ? .white : keyTextColor + state.returnKeyRole.usesActionFill ? OSGColor.fixedLightContent : keyTextColor } /// The send key stays recognizable in both appearances without becoming neon. diff --git a/OSGKeyboardExt/Views/AIKeyboardView.swift b/OSGKeyboardExt/Views/AIKeyboardView.swift index 8686196..df9fc6e 100644 --- a/OSGKeyboardExt/Views/AIKeyboardView.swift +++ b/OSGKeyboardExt/Views/AIKeyboardView.swift @@ -188,7 +188,7 @@ struct AIKeyboardView: View { Button(action: state.confirmPendingAIAnswer) { Image(systemName: "plus") .font(.system(size: 20, weight: .semibold)) - .foregroundStyle(.white) + .foregroundStyle(OSGColor.fixedLightContent) .frame(width: Layout.fieldActionWidth, height: 44) .background(palette.accent, in: Capsule()) } @@ -709,13 +709,13 @@ struct AIKeyboardView: View { if state.assistantInsertionSucceeded { Image(systemName: "checkmark") .font(.system(size: 27, weight: .bold)) - .foregroundStyle(.white) + .foregroundStyle(OSGColor.fixedLightContent) } else { switch state.aiSession.phase { case .listening: - waveform(color: .white) + waveform(color: OSGColor.fixedLightContent) case .preparing, .recognizing, .generating: - ProgressView().tint(.white) + ProgressView().tint(OSGColor.fixedLightContent) case .inactive, .idle, .ready, .awaitingSend, .inserted, .sent, .failed: ordinaryMicrophoneContent } @@ -726,17 +726,17 @@ struct AIKeyboardView: View { private var ordinaryMicrophoneContent: some View { switch state.phase { case .recording: - waveform(color: Color(red: 1, green: 0.78, blue: 0.78)) + waveform(color: OSGColor.recordingWaveform) case .requestingPermissions, .processing: - ProgressView().tint(.white) + ProgressView().tint(OSGColor.fixedLightContent) case .error: Image(systemName: "exclamationmark.triangle.fill") .font(.system(size: 25, weight: .semibold)) - .foregroundStyle(.white) + .foregroundStyle(OSGColor.fixedLightContent) case .idle, .denied: Image(systemName: "mic.fill") .font(.system(size: 28, weight: .semibold)) - .foregroundStyle(.white) + .foregroundStyle(OSGColor.fixedLightContent) .symbolEffect(.breathe, isActive: micIsHoldingForAI) } } diff --git a/OSGKeyboardExt/Views/ClipboardKeyboardViews.swift b/OSGKeyboardExt/Views/ClipboardKeyboardViews.swift index d94256f..cf4bd81 100644 --- a/OSGKeyboardExt/Views/ClipboardKeyboardViews.swift +++ b/OSGKeyboardExt/Views/ClipboardKeyboardViews.swift @@ -104,7 +104,7 @@ struct ClipboardEnableGuideView: View { Button(action: onOpenSettings) { ExtL10n.text("keyboard.clipboard.guide.cta") .font(.system(size: 15, weight: .semibold)) - .foregroundStyle(.white) + .foregroundStyle(OSGColor.fixedLightContent) .padding(.horizontal, 20) .padding(.vertical, 10) .background(palette.accent, in: Capsule()) @@ -222,6 +222,7 @@ struct ClipboardHistoryPanelView: View { } .buttonStyle(.glass) .buttonBorderShape(.capsule) + .tint(palette.textPrimary) Button { // Dismiss the popup before publishing an empty history @@ -231,12 +232,13 @@ struct ClipboardHistoryPanelView: View { } label: { ExtL10n.text("keyboard.clipboard.clear.confirm") .font(.system(size: 13, weight: .semibold)) + .foregroundStyle(palette.background) .frame(maxWidth: .infinity) .frame(height: 36) } .buttonStyle(.glassProminent) .buttonBorderShape(.capsule) - .tint(palette.accent) + .tint(palette.textPrimary) } } .padding(16) diff --git a/OSGKeyboardExt/Views/KeyboardTopControls.swift b/OSGKeyboardExt/Views/KeyboardTopControls.swift index b12e008..6142966 100644 --- a/OSGKeyboardExt/Views/KeyboardTopControls.swift +++ b/OSGKeyboardExt/Views/KeyboardTopControls.swift @@ -43,7 +43,11 @@ struct KeyboardBrandLogo: View { .resizable() .renderingMode(.template) .scaledToFit() - .foregroundStyle(colorScheme == .dark ? Color.white : Color.black) + .foregroundStyle( + colorScheme == .dark + ? OSGColor.fixedLightContent + : OSGColor.fixedDarkContent + ) .frame( width: KeyboardTopBarMetrics.logoWidth, height: KeyboardTopBarMetrics.logoHeight @@ -222,13 +226,17 @@ struct KeyboardTopControls: View { } private var pressedFill: Color { - colorScheme == .dark ? Color(white: 0.22) : Color(white: 0.84) + colorScheme == .dark + ? OSGColor.keyboardDarkControlFill + : OSGColor.keyboardLightKeyPressed } /// 分段轨道底色与选中键面(NativeKeyboardKeyColors.fill)拉开明度; /// 浅色模式叠加半透明黑色,在不同宿主键盘底色上维持可见对比。 private var tabTrackFill: Color { - colorScheme == .dark ? Color(white: 0.12) : Color.black.opacity(0.12) + colorScheme == .dark + ? OSGColor.keyboardDarkControlBorder + : OSGColor.keyboardLightControlBorder } private func isSelected(_ tab: KeyboardInputTab) -> Bool { diff --git a/OSGKeyboardExt/Views/LastInputEditView.swift b/OSGKeyboardExt/Views/LastInputEditView.swift index f8a1a23..26137c2 100644 --- a/OSGKeyboardExt/Views/LastInputEditView.swift +++ b/OSGKeyboardExt/Views/LastInputEditView.swift @@ -123,7 +123,7 @@ struct LastInputEditView: View { Color.clear if case .listening = state.editSession { Capsule() - .stroke(Color.white.opacity(0.28), lineWidth: 1.5) + .stroke(OSGColor.recordListeningRing, lineWidth: 1.5) .scaleEffect(1 + min(max(state.level, 0), 1) * 0.08) .animation(Motion.soft, value: state.level) } @@ -161,16 +161,16 @@ struct LastInputEditView: View { private var primaryIcon: some View { switch state.editSession { case .preparing, .processing, .applying, .appending: - ProgressView().tint(.white) + ProgressView().tint(OSGColor.fixedLightContent) case .review: Image(systemName: "checkmark") .font(.system(size: 21, weight: .bold)) - .foregroundStyle(.white) + .foregroundStyle(OSGColor.fixedLightContent) case .listening: WaveformView( level: state.level, barCount: 7, - color: .white, + color: OSGColor.fixedLightContent, active: true ) .frame(width: 35, height: 22) @@ -178,7 +178,7 @@ struct LastInputEditView: View { default: Image(systemName: "mic.fill") .font(.system(size: 21, weight: .semibold)) - .foregroundStyle(.white) + .foregroundStyle(OSGColor.fixedLightContent) } } diff --git a/OSGKeyboardExt/Views/NativeKeyboardKeyStyle.swift b/OSGKeyboardExt/Views/NativeKeyboardKeyStyle.swift index b54c533..fb336f7 100644 --- a/OSGKeyboardExt/Views/NativeKeyboardKeyStyle.swift +++ b/OSGKeyboardExt/Views/NativeKeyboardKeyStyle.swift @@ -3,32 +3,37 @@ // // Shared native-like key surface used by voice and typing action rows. +import OSGKeyboardShared import SwiftUI enum NativeKeyboardKeyColors { static func fill(for colorScheme: ColorScheme) -> Color { - colorScheme == .dark ? Color(white: 0.32) : .white + colorScheme == .dark + ? OSGColor.keyboardDarkKeyFill + : OSGColor.fixedLightContent } static func pressedFill(for colorScheme: ColorScheme) -> Color { - colorScheme == .dark ? Color(white: 0.23) : Color(white: 0.84) + colorScheme == .dark + ? OSGColor.keyboardDarkKeyPressed + : OSGColor.keyboardLightKeyPressed } static func text(for colorScheme: ColorScheme) -> Color { - colorScheme == .dark ? .white : Color(red: 0.06, green: 0.06, blue: 0.08) + colorScheme == .dark ? OSGColor.fixedLightContent : OSGColor.keyboardLightText } /// Adaptive brand green: brighter in dark mode and deeper in light mode. static func sendFill(for colorScheme: ColorScheme) -> Color { colorScheme == .dark - ? Color(red: 0.286, green: 0.725, blue: 0.416) - : Color(red: 0.196, green: 0.549, blue: 0.298) + ? OSGColor.keyboardDarkSend + : OSGColor.keyboardLightSend } static func sendPressedFill(for colorScheme: ColorScheme) -> Color { colorScheme == .dark - ? Color(red: 0.227, green: 0.627, blue: 0.353) - : Color(red: 0.157, green: 0.447, blue: 0.247) + ? OSGColor.keyboardDarkSendPressed + : OSGColor.keyboardLightSendPressed } } diff --git a/OSGKeyboardExt/Views/ToolbarActionButtons.swift b/OSGKeyboardExt/Views/ToolbarActionButtons.swift index 7a44381..d164734 100644 --- a/OSGKeyboardExt/Views/ToolbarActionButtons.swift +++ b/OSGKeyboardExt/Views/ToolbarActionButtons.swift @@ -67,7 +67,9 @@ private struct ToolbarKeySurface: View { case .standard: return palette.divider case .send: - return Color.black.opacity(colorScheme == .dark ? 0.10 : 0.08) + return colorScheme == .dark + ? OSGColor.keyboardActionBorderDark + : OSGColor.keyboardActionBorderLight } } } @@ -340,7 +342,7 @@ struct RectangularToolbarButton: View { } private var buttonForeground: Color { - isSend ? .white : NativeKeyboardKeyColors.text(for: colorScheme) + isSend ? OSGColor.fixedLightContent : NativeKeyboardKeyColors.text(for: colorScheme) } // 按下即响、即震、即执行,与系统键盘 / 打字面保持一致(Button 默认松手才触发)。 diff --git a/OSGKeyboardHostSupport/DesignSystem/MonthlyUsageCalendar.swift b/OSGKeyboardHostSupport/DesignSystem/MonthlyUsageCalendar.swift index 9d8cc58..8723b26 100644 --- a/OSGKeyboardHostSupport/DesignSystem/MonthlyUsageCalendar.swift +++ b/OSGKeyboardHostSupport/DesignSystem/MonthlyUsageCalendar.swift @@ -1,7 +1,7 @@ // MonthlyUsageCalendar.swift // OSGKeyboard · HostSupport // -// Current-month dictation heat map. Every date remains visible; the accent +// Current-month dictation heat map. Every date remains visible; the monochrome // circle's opacity communicates that day's character count relative to the // busiest day in the same month. @@ -158,21 +158,21 @@ public struct MonthlyUsageCalendar: View { let isToday = calendar.isDateInToday(point.date) let isFuture = calendar.startOfDay(for: point.date) > calendar.startOfDay(for: Date()) let opacity = fillOpacity(for: point.value, isFuture: isFuture) - let usesLightText = opacity >= 0.48 + let usesContrastingText = opacity >= 0.48 return Text(day.formatted()) .font(.system(size: compact ? 12 : 13, weight: isToday ? .semibold : .medium, design: .rounded)) .foregroundStyle( isFuture ? palette.textTertiary.opacity(0.45) - : (usesLightText ? palette.textOnAccent : palette.textPrimary) + : (usesContrastingText ? palette.background : palette.textPrimary) ) .frame(width: cellDiameter, height: cellDiameter) - .background(palette.accent.opacity(opacity), in: Circle()) + .background(palette.textPrimary.opacity(opacity), in: Circle()) .overlay { if isToday { Circle() - .stroke(palette.accent, lineWidth: 1.5) + .stroke(palette.textPrimary, lineWidth: 1.5) } } .frame(maxWidth: .infinity) @@ -188,7 +188,7 @@ public struct MonthlyUsageCalendar: View { 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) - return 0.14 + sqrt(ratio) * 0.62 + return 0.05 + pow(ratio, 1.15) * 0.73 } } diff --git a/OSGKeyboardHostSupport/DesignSystem/SupportDeveloperSection.swift b/OSGKeyboardHostSupport/DesignSystem/SupportDeveloperSection.swift index 800cf91..0d58e1a 100644 --- a/OSGKeyboardHostSupport/DesignSystem/SupportDeveloperSection.swift +++ b/OSGKeyboardHostSupport/DesignSystem/SupportDeveloperSection.swift @@ -112,7 +112,7 @@ public struct SupportDeveloperSection: View { } .frame(maxWidth: .infinity) .padding(.vertical, Spacing.sm) - .foregroundStyle(.white) + .foregroundStyle(OSGColor.fixedLightContent) .background(palette.accent, in: RoundedRectangle(cornerRadius: Radius.large, style: .continuous)) } .buttonStyle(.plain) diff --git a/OSGKeyboardHostSupport/DesignSystem/UsageStatsCluster.swift b/OSGKeyboardHostSupport/DesignSystem/UsageStatsCluster.swift index e9a8f67..f148427 100644 --- a/OSGKeyboardHostSupport/DesignSystem/UsageStatsCluster.swift +++ b/OSGKeyboardHostSupport/DesignSystem/UsageStatsCluster.swift @@ -223,12 +223,12 @@ public struct UsageStatsCluster: View { VStack(spacing: CardLayoutMetrics.compactItemSpacing) { HStack(spacing: CardLayoutMetrics.compactItemSpacing) { compactCell( - systemImage: "waveform.badge.microphone", + systemImage: "timer", value: UsageStatisticsStore.formatDuration(dictationDurationSeconds, language: language), label: SharedL10n.string("stat.dictationTime", language: language) ) compactCell( - systemImage: "text.quote", + systemImage: "quote.bubble", value: UsageStatisticsStore.formatCount(dictationCharacterCount, language: language), label: SharedL10n.string("stat.words", language: language), action: onOpenHistory @@ -242,7 +242,7 @@ public struct UsageStatsCluster: View { label: SharedL10n.string("stat.translation", language: language) ) compactCell( - systemImage: "books.vertical", + systemImage: "book.pages", value: UsageStatisticsStore.formatCount(dictionaryTermCount, language: language), label: SharedL10n.string("stat.dictionary", language: language), action: onOpenDictionary @@ -292,9 +292,9 @@ public struct UsageStatsCluster: View { return ZStack(alignment: .bottomTrailing) { // Subtle watermark stays fully visible within the card edges. Image(systemName: systemImage) - .font(.system(size: 26, weight: .semibold)) - .foregroundStyle(palette.textPrimary.opacity(0.06)) - .frame(width: 32, height: 32, alignment: .center) + .font(.system(size: 38, weight: .ultraLight)) + .foregroundStyle(palette.textPrimary.opacity(0.10)) + .frame(width: 44, height: 44, alignment: .center) .offset(x: Spacing.xs, y: Spacing.xs) VStack(alignment: .leading, spacing: Spacing.xxs) { @@ -323,6 +323,7 @@ public struct UsageStatsCluster: View { } } .clipShape(shape) + .cardElevation() } private var disclosureIndicator: some View { diff --git a/OSGKeyboardHostSupport/Features/Account/AccountAPIClient.swift b/OSGKeyboardHostSupport/Features/Account/AccountAPIClient.swift index 333f8db..9f564a8 100644 --- a/OSGKeyboardHostSupport/Features/Account/AccountAPIClient.swift +++ b/OSGKeyboardHostSupport/Features/Account/AccountAPIClient.swift @@ -539,22 +539,18 @@ public actor AccountAPIClient { } private func clearSession() async throws { - cachedSession = nil - didLoadSession = true - var storageFailed = false do { try await sessionVault.clearSession() } catch { - storageFailed = true - } - do { - try await sessionVault.clearRefreshTransaction() - } catch { - storageFailed = true - } - if storageFailed { + // Keep the cached session aligned with Keychain so a transient + // deletion failure can be retried instead of resurrecting on launch. throw AccountAPIError.secureStorage } + cachedSession = nil + didLoadSession = true + // A stale operation identifier is harmless once its refresh token is + // gone; do not turn a successful credential purge into a failed logout. + try? await sessionVault.clearRefreshTransaction() } private func invalidateSession(ifAccessTokenMatches expectedAccessToken: String) async throws { @@ -562,12 +558,7 @@ public actor AccountAPIClient { current.accessToken == expectedAccessToken else { return } - do { - try await clearSession() - } catch { - publishSessionInvalidation() - throw error - } + try await clearSession() publishSessionInvalidation() } diff --git a/OSGKeyboardMac/MacComponents.swift b/OSGKeyboardMac/MacComponents.swift index 187a4fc..33dd9b7 100644 --- a/OSGKeyboardMac/MacComponents.swift +++ b/OSGKeyboardMac/MacComponents.swift @@ -104,9 +104,9 @@ struct MacToggleStyle: ToggleStyle { Capsule() .fill(configuration.isOn ? palette.accent : offTrackColor) Circle() - .fill(Color.white) + .fill(OSGColor.macToggleKnob) .frame(width: 16, height: 16) - .shadow(color: .black.opacity(0.25), radius: 1, y: 1) + .shadow(color: OSGColor.macToggleKnobShadow, radius: 1, y: 1) .padding(2) } .frame(width: 36, height: 20) @@ -117,8 +117,8 @@ struct MacToggleStyle: ToggleStyle { private var offTrackColor: Color { colorScheme == .dark - ? Color.white.opacity(0.18) - : Color.black.opacity(0.15) + ? OSGColor.macToggleOffTrackDark + : OSGColor.macToggleOffTrackLight } } @@ -411,7 +411,7 @@ struct MacHeaderActionButtonStyle: ButtonStyle { configuration.label .padding(.horizontal, Spacing.md) .frame(height: MacMetrics.pageHeaderControlHeight) - .foregroundStyle(.white) + .foregroundStyle(OSGColor.fixedLightContent) .background( palette.accent.opacity(configuration.isPressed ? 0.82 : 1), in: Capsule() diff --git a/OSGKeyboardMac/MacDictationOverlayView.swift b/OSGKeyboardMac/MacDictationOverlayView.swift index 1486139..f7016ef 100644 --- a/OSGKeyboardMac/MacDictationOverlayView.swift +++ b/OSGKeyboardMac/MacDictationOverlayView.swift @@ -71,7 +71,7 @@ struct MacDictationOverlayView: View { Capsule(style: .continuous) .stroke(palette.dividerStrong, lineWidth: 0.5) ) - .shadow(color: Color.black.opacity(0.22), radius: 14, y: 5) + .shadow(color: OSGColor.macOverlayShadow, radius: 14, y: 5) .padding(Self.shadowMargin) .contentShape(Capsule(style: .continuous)) // Manual drag: `isMovableByWindowBackground` doesn't work on a diff --git a/OSGKeyboardMac/MacDictionaryView.swift b/OSGKeyboardMac/MacDictionaryView.swift index 3e6598f..efaae54 100644 --- a/OSGKeyboardMac/MacDictionaryView.swift +++ b/OSGKeyboardMac/MacDictionaryView.swift @@ -173,7 +173,7 @@ struct MacDictionaryView: View { } .padding(.horizontal, Spacing.sm) .frame(width: 220, height: MacMetrics.pageHeaderControlHeight) - .background(palette.surface, in: Capsule()) + .background(palette.formSurface, in: Capsule()) .overlay(Capsule().stroke(palette.divider, lineWidth: 0.5)) } @@ -289,9 +289,13 @@ private struct MacDictionaryEntryEditor: View { Button(MacL10n.string("mac.cancel", language: language)) { dismiss() } - Button(MacL10n.string("mac.save", language: language), action: save) + .tint(palette.textPrimary) + Button(action: save) { + Text(MacL10n.string("mac.save", language: language)) + .foregroundStyle(palette.background) + } .buttonStyle(.borderedProminent) - .tint(palette.accent) + .tint(palette.textPrimary) .disabled(trimmedTerm.isEmpty) } } diff --git a/OSGKeyboardMac/MacLocalASRModelSettingsView.swift b/OSGKeyboardMac/MacLocalASRModelSettingsView.swift index 4e7646d..9330bd9 100644 --- a/OSGKeyboardMac/MacLocalASRModelSettingsView.swift +++ b/OSGKeyboardMac/MacLocalASRModelSettingsView.swift @@ -385,7 +385,7 @@ struct MacLocalASRModelSettingsView: View { MacSettingsToolButton( title: MacL10n.string("mac.localASR.download", language: lang), fill: palette.accent, - foreground: .white + foreground: OSGColor.fixedLightContent ) { modelVM.installModel(model) } diff --git a/OSGKeyboardMac/MacOnboardingView.swift b/OSGKeyboardMac/MacOnboardingView.swift index 20e4507..903b9ae 100644 --- a/OSGKeyboardMac/MacOnboardingView.swift +++ b/OSGKeyboardMac/MacOnboardingView.swift @@ -272,7 +272,9 @@ struct MacOnboardingView: View { .resizable() .scaledToFit() .frame(width: 128, height: 128) - .foregroundStyle(colorScheme == .dark ? Color.white : palette.accent) + .foregroundStyle( + colorScheme == .dark ? OSGColor.fixedLightContent : palette.accent + ) .accessibilityLabel("OSGKeyboard") .allowsHitTesting(false) } diff --git a/OSGKeyboardMac/MacPolishStylesView.swift b/OSGKeyboardMac/MacPolishStylesView.swift index 5544a64..8c6ef3e 100644 --- a/OSGKeyboardMac/MacPolishStylesView.swift +++ b/OSGKeyboardMac/MacPolishStylesView.swift @@ -514,7 +514,7 @@ private struct MacPolishStyleEditor: View { .font(.body.monospaced()) .frame(minHeight: 320) .padding(4) - .background(palette.surface, in: RoundedRectangle(cornerRadius: Radius.medium)) + .background(palette.formSurface, in: RoundedRectangle(cornerRadius: Radius.medium)) .overlay( RoundedRectangle(cornerRadius: Radius.medium) .stroke(palette.divider, lineWidth: 1) diff --git a/OSGKeyboardMac/MacSettingsComponents.swift b/OSGKeyboardMac/MacSettingsComponents.swift index b2d39da..8b978ed 100644 --- a/OSGKeyboardMac/MacSettingsComponents.swift +++ b/OSGKeyboardMac/MacSettingsComponents.swift @@ -421,14 +421,14 @@ struct MacProviderModelRow: View { HStack(spacing: Spacing.sm) { Text(modelId) .font(TypeStyle.mono) - .foregroundStyle(isSelected ? palette.accent : palette.textPrimary) + .foregroundStyle(palette.textPrimary) .lineLimit(1) .truncationMode(.middle) Spacer(minLength: 0) if isSelected { Image(systemName: "checkmark") .font(.system(size: 11, weight: .semibold)) - .foregroundStyle(palette.accent) + .foregroundStyle(palette.textPrimary) } } .padding(.horizontal, Spacing.sm) @@ -436,7 +436,7 @@ struct MacProviderModelRow: View { .frame(maxWidth: .infinity, alignment: .leading) .background( RoundedRectangle(cornerRadius: Radius.small, style: .continuous) - .fill(isSelected ? palette.accentMuted : Color.clear) + .fill(isSelected ? palette.textPrimary.opacity(0.10) : Color.clear) ) .contentShape(Rectangle()) } diff --git a/OSGKeyboardMac/MacSupportDeveloperTipRows.swift b/OSGKeyboardMac/MacSupportDeveloperTipRows.swift index deddd52..79b46c7 100644 --- a/OSGKeyboardMac/MacSupportDeveloperTipRows.swift +++ b/OSGKeyboardMac/MacSupportDeveloperTipRows.swift @@ -56,6 +56,7 @@ struct MacSupportDeveloperTipRows: View { Button(SharedL10n.string("tip.alert.dismiss", language: language)) { tipManager.acknowledgePurchaseState() } + .tint(palette.textPrimary) } message: { Text(SharedL10n.string("tip.thankYou.message", language: language)) } @@ -66,6 +67,7 @@ struct MacSupportDeveloperTipRows: View { Button(SharedL10n.string("tip.alert.dismiss", language: language)) { tipManager.acknowledgePurchaseState() } + .tint(palette.textPrimary) } message: { Text(errorMessage) } diff --git a/OSGKeyboardMac/MacTheme.swift b/OSGKeyboardMac/MacTheme.swift index 22e0625..9f927c6 100644 --- a/OSGKeyboardMac/MacTheme.swift +++ b/OSGKeyboardMac/MacTheme.swift @@ -30,6 +30,7 @@ enum MacSystemPalette { ThemePalette( background: resolved(dark ? darkBackground : warmBackground, dark: dark), surface: resolved(dark ? darkSurface : warmSurface, dark: dark), + formSurface: resolved(dark ? darkFormSurface : warmFormSurface, dark: dark), surfaceElevated: resolved(dark ? darkElevated : warmElevated, dark: dark), surfaceMuted: resolved(dark ? darkMuted : warmMuted, dark: dark), @@ -45,14 +46,14 @@ enum MacSystemPalette { textPrimary: resolved(.labelColor, dark: dark), textSecondary: resolved(.secondaryLabelColor, dark: dark), textTertiary: resolved(.tertiaryLabelColor, dark: dark), - textOnAccent: Color.white, + textOnAccent: OSGColor.fixedLightContent, divider: dark - ? Color.white.opacity(0.08) - : Color.black.opacity(0.06), + ? OSGColor.macDarkDivider + : OSGColor.lightDivider, dividerStrong: dark - ? Color.white.opacity(0.12) - : Color.black.opacity(0.10), + ? OSGColor.macDarkDividerStrong + : OSGColor.lightDividerStrong, recordRed: resolved(.systemRed, dark: dark), recordBlue: resolved(.systemBlue, dark: dark) @@ -62,13 +63,15 @@ enum MacSystemPalette { // MARK: - Warm Light-mode surfaces (matched to iOS `Palette.light`) /// #F2F1EE — warm gray page background. - private static let warmBackground = NSColor(srgbRed: 0.949, green: 0.945, blue: 0.933, alpha: 1) + private static let warmBackground = NSColor(OSGColor.lightBackground) /// #FCFBF9 — warm off-white card/control surface. - private static let warmSurface = NSColor(srgbRed: 0.988, green: 0.984, blue: 0.976, alpha: 1) + private static let warmSurface = NSColor(OSGColor.lightSurface) + /// #FCFBF9 — forms retain the ordinary card fill in light mode. + private static let warmFormSurface = warmSurface /// #EBEAE7 — slightly recessed elevated surface. - private static let warmElevated = NSColor(srgbRed: 0.922, green: 0.918, blue: 0.906, alpha: 1) + private static let warmElevated = NSColor(OSGColor.lightSurfaceElevated) /// #EEEDE9 — muted fill between background and surface. - private static let warmMuted = NSColor(srgbRed: 0.933, green: 0.929, blue: 0.918, alpha: 1) + private static let warmMuted = NSColor(OSGColor.lightSurfaceMuted) // MARK: - Dark-mode surfaces (Apple standard elevated grays) // @@ -78,13 +81,15 @@ enum MacSystemPalette { // clearly elevated above the background — mirroring the iOS dark palette. /// #1C1C1E — page background. - private static let darkBackground = NSColor(srgbRed: 0.110, green: 0.110, blue: 0.118, alpha: 1) + private static let darkBackground = NSColor(OSGColor.macDarkBackground) /// #2C2C2E — card / control surface, clearly lighter than the background. - private static let darkSurface = NSColor(srgbRed: 0.173, green: 0.173, blue: 0.180, alpha: 1) + private static let darkSurface = NSColor(OSGColor.macDarkSurface) + /// #343438 — interactive form fill between cards and elevated chrome. + private static let darkFormSurface = NSColor(OSGColor.macDarkFormSurface) /// #3A3A3C — elevated fill for selected / raised chrome. - private static let darkElevated = NSColor(srgbRed: 0.227, green: 0.227, blue: 0.235, alpha: 1) + private static let darkElevated = NSColor(OSGColor.macDarkSurfaceElevated) /// #242426 — muted fill between background and surface. - private static let darkMuted = NSColor(srgbRed: 0.141, green: 0.141, blue: 0.149, alpha: 1) + private static let darkMuted = NSColor(OSGColor.macDarkSurfaceMuted) /// Resolves a (possibly dynamic) AppKit colour to its concrete value under /// the requested appearance, so the two static palettes differ by value. diff --git a/OSGKeyboardShared/DesignSystem/CardPageLayout.swift b/OSGKeyboardShared/DesignSystem/CardPageLayout.swift index f6f8d70..b852edd 100644 --- a/OSGKeyboardShared/DesignSystem/CardPageLayout.swift +++ b/OSGKeyboardShared/DesignSystem/CardPageLayout.swift @@ -81,34 +81,134 @@ public struct SurfaceCardModifier: ViewModifier { @Environment(\.themePalette) private var palette private let enabled: Bool + private let elevated: Bool - public init(enabled: Bool = true) { + public init(enabled: Bool = true, elevated: Bool = true) { self.enabled = enabled + self.elevated = elevated } + @ViewBuilder public func body(content: Content) -> some View { if enabled { let shape = RoundedRectangle(cornerRadius: Radius.xl, style: .continuous) - content + let card = content .background( - palette.surface, + elevated ? palette.surface : palette.formSurface, in: shape ) // Clip child backgrounds as well as the card surface. Without // this, a full-width child can visually square off a corner. .clipShape(shape) + if elevated { + card.cardElevation() + } else { + card + } } else { content } } } +private struct CardElevationModifier: ViewModifier { + @Environment(\.colorScheme) private var colorScheme + + let accented: Bool + + @ViewBuilder + func body(content: Content) -> some View { + #if os(iOS) + // Layered low-opacity shadows create direction and length without + // turning the warm background gray. + content + .shadow( + color: nearShadowColor, + radius: 1, + x: 2, + y: 3 + ) + .shadow( + color: midShadowColor, + radius: 3, + x: 6, + y: 8 + ) + .shadow( + color: farShadowColor, + radius: 9, + x: 12, + y: 18 + ) + #else + content + #endif + } + + private var nearShadowColor: Color { + guard accented else { return OSGColor.cardShadowNear } + return colorScheme == .dark + ? OSGColor.selectedCardShadowNearDark + : OSGColor.selectedCardShadowNearLight + } + + private var midShadowColor: Color { + guard accented else { return OSGColor.cardShadowMid } + return colorScheme == .dark + ? OSGColor.selectedCardShadowMidDark + : OSGColor.selectedCardShadowMidLight + } + + private var farShadowColor: Color { + guard accented else { return OSGColor.cardShadowFar } + return colorScheme == .dark + ? OSGColor.selectedCardShadowFarDark + : OSGColor.selectedCardShadowFarLight + } +} + +private struct CardListRowModifier: ViewModifier { + let bottomSpacing: CGFloat + let elevated: Bool + + func body(content: Content) -> some View { + content + .surfaceCard(elevated: elevated) + .listRowInsets( + EdgeInsets( + top: 0, + leading: 0, + bottom: bottomSpacing, + trailing: 0 + ) + ) + .listRowBackground(Color.clear) + .listRowSeparator(.hidden) + } +} + public extension View { func cardSectionLabel() -> some View { modifier(CardSectionLabelModifier()) } - func surfaceCard(enabled: Bool = true) -> some View { - modifier(SurfaceCardModifier(enabled: enabled)) + func surfaceCard( + enabled: Bool = true, + elevated: Bool = true + ) -> some View { + modifier(SurfaceCardModifier(enabled: enabled, elevated: elevated)) + } + + func cardElevation(accented: Bool = false) -> some View { + modifier(CardElevationModifier(accented: accented)) + } + + /// Makes a native List/Form row render as an elevated app card while + /// retaining system scrolling, swipe actions, refresh, and keyboard logic. + func cardListRow( + bottomSpacing: CGFloat = 0, + elevated: Bool = true + ) -> some View { + modifier(CardListRowModifier(bottomSpacing: bottomSpacing, elevated: elevated)) } } diff --git a/OSGKeyboardShared/DesignSystem/ColorLibrary.swift b/OSGKeyboardShared/DesignSystem/ColorLibrary.swift new file mode 100644 index 0000000..58ebdc4 --- /dev/null +++ b/OSGKeyboardShared/DesignSystem/ColorLibrary.swift @@ -0,0 +1,171 @@ +// ColorLibrary.swift +// OSGKeyboard · Shared Design System +// +// Canonical custom colour values used across the app, keyboard extension, +// previews, and platform chrome. Views should prefer semantic ThemePalette +// roles; use these fixed tokens only when a surface intentionally does not +// follow the active app appearance. + +import SwiftUI + +public enum OSGColor { + // MARK: - Brand + + public static let brandAccent = color(0x3AA05A) + public static let aiTeal = color(0x2BAFA4) + /// Content that must remain white on colored or generated artwork. + public static let fixedLightContent = Color.white + public static let fixedDarkContent = Color.black + + // MARK: - iOS app theme + + public static let darkBackground = color(0x0A0A0B) + public static let darkSurface = color(0x18181B) + /// Interactive form fill: lighter than cards without looking elevated. + public static let darkFormSurface = color(0x202024) + public static let darkSurfaceElevated = color(0x27272A) + public static let darkSurfaceMuted = color(0x121215) + public static let darkDanger = color(0xFF453A) + public static let darkWarning = color(0xFFBF18) + public static let darkTextPrimary = color(0xFFFFFF) + public static let darkTextSecondary = color(0xB3B3B3) + public static let darkTextTertiary = color(0x808080) + public static let darkDivider = Color.white.opacity(0.06) + public static let darkDividerStrong = Color.white.opacity(0.10) + + public static let lightBackground = color(0xF2F1EE) + public static let lightSurface = color(0xFCFBF9) + /// Light mode already has enough contrast, so forms retain the card fill. + public static let lightFormSurface = lightSurface + public static let lightSurfaceElevated = color(0xEBEAE7) + public static let lightSurfaceMuted = color(0xEEEDE9) + public static let lightDanger = color(0xFF3B30) + public static let lightWarning = color(0xFF9E18) + public static let lightTextPrimary = color(0x111118) + public static let lightTextSecondary = color(0x64646F) + public static let lightTextTertiary = color(0x8E8E9A) + public static let lightDivider = Color.black.opacity(0.06) + public static let lightDividerStrong = Color.black.opacity(0.10) + + // MARK: - macOS app theme + + public static let macDarkBackground = color(0x1C1C1E) + public static let macDarkSurface = color(0x2C2C2E) + public static let macDarkFormSurface = color(0x343438) + public static let macDarkSurfaceElevated = color(0x3A3A3C) + public static let macDarkSurfaceMuted = color(0x242426) + public static let macDarkDivider = Color.white.opacity(0.08) + public static let macDarkDividerStrong = Color.white.opacity(0.12) + public static let macToggleKnob = Color.white + public static let macToggleKnobShadow = Color.black.opacity(0.25) + public static let macToggleOffTrackDark = Color.white.opacity(0.18) + public static let macToggleOffTrackLight = Color.black.opacity(0.15) + public static let macOverlayShadow = Color.black.opacity(0.22) + + // MARK: - Recording and generated media + + public static let recordRed = color(0xFF3B30) + public static let recordBlue = color(0x007AFF) + public static let recordingWaveform = color(0xFFC7C7) + public static let recordDiscHairlineIdle = Color.white.opacity(0.08) + public static let recordDiscHairlineActive = Color.white.opacity(0.12) + public static let recordDiscOverlayStroke = Color.white.opacity(0.16) + public static let recordListeningRing = Color.white.opacity(0.28) + public static let pictureInPictureOutline = color(0xE5E7EB) + public static let pictureInPictureAccent = color(0x33C78C) + + // MARK: - Keyboard surfaces + + public static let keyboardDarkKeyFill = color(0x525252) + public static let keyboardDarkKeyPressed = color(0x3B3B3B) + public static let keyboardDarkInputFill = color(0x4D4D4D) + public static let keyboardDarkInputFillRaised = color(0x5C5C5C) + public static let keyboardDarkControlFill = color(0x383838) + public static let keyboardDarkControlBorder = color(0x1F1F1F) + public static let keyboardLightKeyPressed = color(0xD6D6D6) + public static let keyboardLightControlBorder = Color.black.opacity(0.12) + public static let keyboardActionBorderDark = Color.black.opacity(0.10) + public static let keyboardActionBorderLight = Color.black.opacity(0.08) + public static let keyboardLightText = color(0x0F0F14) + public static let keyboardDarkSend = color(0x49B96A) + public static let keyboardLightSend = color(0x328C4C) + public static let keyboardDarkSendPressed = brandAccent + public static let keyboardLightSendPressed = color(0x28723F) + + // MARK: - Fixed dark presentation surfaces + + public static let demoBackground = color(0x0F0F12) + public static let demoSelectionInactive = Color.gray + public static let accountAvatarOutline = Color.white.opacity(0.20) + public static let accountAvatarGradients: [[Color]] = [ + [.indigo, .blue], + [.purple, .pink], + [.teal, .cyan], + [.orange, .red], + [.mint, .green] + ] + + // MARK: - Shared effects + + public static let cardShadowNear = Color.black.opacity(0.018) + public static let cardShadowMid = Color.black.opacity(0.018) + public static let cardShadowFar = Color.black.opacity(0.025) + public static let selectedCardFillLeadingDark = brandAccent.opacity(0.24) + public static let selectedCardFillTrailingDark = brandAccent.opacity(0.15) + public static let selectedCardFillLeadingLight = brandAccent.opacity(0.18) + public static let selectedCardFillTrailingLight = brandAccent.opacity(0.11) + public static let selectedCardStrokeDark = brandAccent.opacity(0.26) + public static let selectedCardStrokeLight = brandAccent.opacity(0.18) + public static let selectedCardShadowNearDark = brandAccent.opacity(0.12) + public static let selectedCardShadowMidDark = brandAccent.opacity(0.075) + public static let selectedCardShadowFarDark = brandAccent.opacity(0.035) + public static let selectedCardShadowNearLight = brandAccent.opacity(0.075) + public static let selectedCardShadowMidLight = brandAccent.opacity(0.05) + public static let selectedCardShadowFarLight = brandAccent.opacity(0.03) + + // MARK: - Animated mesh palettes + + public static let meshAurora: [Color] = [ + color(0x0F1221), // top-left + color(0x1A1433), // top-mid + color(0x0D1A2E), // top-right + color(0x1F1729), // left-mid + color(0x2E1C38), // center — deepest violet + color(0x141F33), // right-mid + color(0x291A1A), // bottom-left — amber hint + color(0x1A142E), // bottom-mid + color(0x0D1724) // bottom-right + ] + + public static let meshPolar: [Color] = [ + color(0x0D121A), + color(0x171F2E), + color(0x121724), + color(0x1A212E), + color(0x212938), + color(0x141A29), + color(0x0F1A29), + color(0x171C2E), + color(0x0D121F) + ] + + public static let meshEmber: [Color] = [ + color(0x140F0D), + color(0x24170F), + color(0x1A120F), + color(0x291A12), + color(0x382114), + color(0x1F1412), + color(0x1A0F0D), + color(0x26170F), + color(0x140F0D) + ] + + private static func color(_ hex: UInt32) -> Color { + Color( + red: Double((hex >> 16) & 0xFF) / 255, + green: Double((hex >> 8) & 0xFF) / 255, + blue: Double(hex & 0xFF) / 255 + ) + } +} diff --git a/OSGKeyboardShared/DesignSystem/ProgressTrack.swift b/OSGKeyboardShared/DesignSystem/ProgressTrack.swift new file mode 100644 index 0000000..11d9a58 --- /dev/null +++ b/OSGKeyboardShared/DesignSystem/ProgressTrack.swift @@ -0,0 +1,53 @@ +// ProgressTrack.swift +// OSGKeyboard · Shared Design System +// +// Shared linear progress track for consistent height, shape, and background. + +import SwiftUI + +public struct ProgressTrackSegment { + let fraction: Double + let color: Color + + public init(fraction: Double, color: Color) { + self.fraction = fraction + self.color = color + } +} + +public struct ProgressTrack: View { + @Environment(\.themePalette) private var palette + + private let segments: [ProgressTrackSegment] + + public init(segments: [ProgressTrackSegment]) { + self.segments = segments + } + + public var body: some View { + GeometryReader { proxy in + ZStack(alignment: .leading) { + Capsule() + .fill(palette.surfaceElevated.opacity(0.55)) + + HStack(spacing: 0) { + ForEach(segments.indices, id: \.self) { index in + Rectangle() + .fill(segments[index].color) + .frame( + width: proxy.size.width * clampedFraction( + segments[index].fraction + ) + ) + } + } + .clipShape(Capsule()) + } + } + .frame(height: 8) + } + + private func clampedFraction(_ fraction: Double) -> Double { + min(max(fraction, 0), 1) + } +} diff --git a/OSGKeyboardShared/DesignSystem/RecordButton.swift b/OSGKeyboardShared/DesignSystem/RecordButton.swift index 882e58c..973f3f4 100644 --- a/OSGKeyboardShared/DesignSystem/RecordButton.swift +++ b/OSGKeyboardShared/DesignSystem/RecordButton.swift @@ -68,7 +68,7 @@ public struct RecordButton: View { } private var waveformColor: Color { - Color(red: 1.0, green: 0.78, blue: 0.78) + OSGColor.recordingWaveform } private enum Layout { @@ -103,7 +103,10 @@ public struct RecordButton: View { .animation(Motion.soft, value: level) Circle() - .stroke(Color.white.opacity(isIdle ? 0.08 : 0.12), lineWidth: 0.5) + .stroke( + isIdle ? OSGColor.recordDiscHairlineIdle : OSGColor.recordDiscHairlineActive, + lineWidth: 0.5 + ) .frame(width: Layout.outerRing, height: Layout.outerRing) ZStack { @@ -120,7 +123,7 @@ public struct RecordButton: View { Circle() .fill(discGradient) Circle() - .stroke(Color.white.opacity(0.16), lineWidth: 1) + .stroke(OSGColor.recordDiscOverlayStroke, lineWidth: 1) .blendMode(.overlay) } @@ -129,13 +132,13 @@ public struct RecordButton: View { case .idleReady, .idleUnavailable: Image(systemName: "mic.fill") .font(.system(size: 36, weight: .medium)) - .foregroundStyle(.white) + .foregroundStyle(OSGColor.fixedLightContent) case .recording: VStack(spacing: 3) { if let remainingSeconds { Text(formatRemaining(remainingSeconds)) .font(.system(size: 22, weight: .semibold, design: .rounded)) - .foregroundStyle(.white) + .foregroundStyle(OSGColor.fixedLightContent) .monospacedDigit() .contentTransition(.numericText()) .offset(y: 3) diff --git a/OSGKeyboardShared/DesignSystem/Theme.swift b/OSGKeyboardShared/DesignSystem/Theme.swift index 757b679..be7a66f 100644 --- a/OSGKeyboardShared/DesignSystem/Theme.swift +++ b/OSGKeyboardShared/DesignSystem/Theme.swift @@ -1,7 +1,8 @@ // Theme.swift // OSGKeyboard · Design System // -// Single source of truth for colour, spacing, corner radius, typography. +// Semantic theme roles, spacing, corner radius, and typography. Canonical +// custom colour values live in `ColorLibrary.swift`. // Inspired by Dieter Rams ("less but better") and Apple Human Interface: // every surface has a single purpose, every token earns its place, and // the visual hierarchy is carried by *whitespace + one accent*, never by @@ -19,6 +20,7 @@ import SwiftUI public struct ThemePalette: Sendable, Equatable { public let background: Color public let surface: Color + public let formSurface: Color public let surfaceElevated: Color public let surfaceMuted: Color @@ -47,35 +49,36 @@ public struct ThemePalette: Sendable, Equatable { public enum Palette { // Backgrounds - public static let background = Color(red: 0.039, green: 0.039, blue: 0.043) // #0A0A0B - public static let surface = Color(red: 0.094, green: 0.094, blue: 0.106) // #18181B - public static let surfaceElevated = Color(red: 0.153, green: 0.153, blue: 0.169) // #27272A - public static let surfaceMuted = Color(red: 0.071, green: 0.071, blue: 0.082) // #121215 + public static let background = OSGColor.darkBackground + public static let surface = OSGColor.darkSurface + public static let formSurface = OSGColor.darkFormSurface + public static let surfaceElevated = OSGColor.darkSurfaceElevated + public static let surfaceMuted = OSGColor.darkSurfaceMuted // Accents - public static let accent = Color(red: 0.227, green: 0.627, blue: 0.353) // #3AA05A + public static let accent = OSGColor.brandAccent public static let accentMuted = accent.opacity(0.18) public static let accentGlow = accent.opacity(0.42) - public static let aiTeal = Color(red: 0.169, green: 0.686, blue: 0.643) // #2BAFA4 + public static let aiTeal = OSGColor.aiTeal // Semantic - public static let danger = Color(red: 1.000, green: 0.271, blue: 0.227) // #FF453A + public static let danger = OSGColor.darkDanger public static let success = accent // unified brand green in UI - public static let warning = Color(red: 1.000, green: 0.749, blue: 0.094) // #FFBF18 + public static let warning = OSGColor.darkWarning // Text - public static let textPrimary = Color.white - public static let textSecondary = Color(white: 0.7) - public static let textTertiary = Color(white: 0.50) - public static let textOnAccent = Color.black + public static let textPrimary = OSGColor.darkTextPrimary + public static let textSecondary = OSGColor.darkTextSecondary + public static let textTertiary = OSGColor.darkTextTertiary + public static let textOnAccent = OSGColor.fixedDarkContent // Lines - public static let divider = Color.white.opacity(0.06) - public static let dividerStrong = Color.white.opacity(0.10) + public static let divider = OSGColor.darkDivider + public static let dividerStrong = OSGColor.darkDividerStrong // Recording state - public static let recordRed = Color(red: 1.000, green: 0.231, blue: 0.188) // #FF3B30 - public static let recordBlue = Color(red: 0.000, green: 0.478, blue: 1.000) // #007AFF + public static let recordRed = OSGColor.recordRed + public static let recordBlue = OSGColor.recordBlue /// Canonical dark palette — preserves every legacy literal above so /// existing call sites that read `Palette.background` directly keep @@ -84,6 +87,7 @@ public enum Palette { public static let dark = ThemePalette( background: background, surface: surface, + formSurface: formSurface, surfaceElevated: surfaceElevated, surfaceMuted: surfaceMuted, accent: accent, @@ -105,25 +109,26 @@ public enum Palette { /// Light palette — warm gray backgrounds for daytime use. public static let light = ThemePalette( - background: Color(red: 0.949, green: 0.945, blue: 0.933), // #F2F1EE warm gray - surface: Color(red: 0.988, green: 0.984, blue: 0.976), // #FCFBF9 - surfaceElevated: Color(red: 0.922, green: 0.918, blue: 0.906), // #EBEAE7 - surfaceMuted: Color(red: 0.933, green: 0.929, blue: 0.918), // #EEEDE9 - accent: Color(red: 0.227, green: 0.627, blue: 0.353), // #3AA05A - accentMuted: Color(red: 0.227, green: 0.627, blue: 0.353).opacity(0.14), - accentGlow: Color(red: 0.227, green: 0.627, blue: 0.353).opacity(0.32), - aiTeal: Color(red: 0.169, green: 0.686, blue: 0.643), // #2BAFA4 - danger: Color(red: 1.000, green: 0.231, blue: 0.188), // #FF3B30 - success: Color(red: 0.227, green: 0.627, blue: 0.353), // same as accent - warning: Color(red: 1.000, green: 0.620, blue: 0.094), // #FF9E18 - textPrimary: Color(red: 0.067, green: 0.067, blue: 0.094), // #111118 - textSecondary: Color(red: 0.392, green: 0.392, blue: 0.435), // #64646F - textTertiary: Color(red: 0.557, green: 0.557, blue: 0.604), // #8E8E9A - textOnAccent: Color.white, - divider: Color.black.opacity(0.06), - dividerStrong: Color.black.opacity(0.10), - recordRed: Color(red: 1.000, green: 0.231, blue: 0.188), // #FF3B30 - recordBlue: Color(red: 0.000, green: 0.478, blue: 1.000) // #007AFF + background: OSGColor.lightBackground, + surface: OSGColor.lightSurface, + formSurface: OSGColor.lightFormSurface, + surfaceElevated: OSGColor.lightSurfaceElevated, + surfaceMuted: OSGColor.lightSurfaceMuted, + accent: OSGColor.brandAccent, + accentMuted: OSGColor.brandAccent.opacity(0.14), + accentGlow: OSGColor.brandAccent.opacity(0.32), + aiTeal: OSGColor.aiTeal, + danger: OSGColor.lightDanger, + success: OSGColor.brandAccent, + warning: OSGColor.lightWarning, + textPrimary: OSGColor.lightTextPrimary, + textSecondary: OSGColor.lightTextSecondary, + textTertiary: OSGColor.lightTextTertiary, + textOnAccent: OSGColor.fixedLightContent, + divider: OSGColor.lightDivider, + dividerStrong: OSGColor.lightDividerStrong, + recordRed: OSGColor.recordRed, + recordBlue: OSGColor.recordBlue ) } @@ -235,6 +240,7 @@ private struct CardSurfaceModifier: ViewModifier { content .padding(padding) .background(palette.surface, in: RoundedRectangle(cornerRadius: Radius.large, style: .continuous)) + .cardElevation() } } diff --git a/OSGKeyboardShared/DesignSystem/UsageSurfaceCard.swift b/OSGKeyboardShared/DesignSystem/UsageSurfaceCard.swift index 2c7de33..b4aad76 100644 --- a/OSGKeyboardShared/DesignSystem/UsageSurfaceCard.swift +++ b/OSGKeyboardShared/DesignSystem/UsageSurfaceCard.swift @@ -1,8 +1,8 @@ // UsageSurfaceCard.swift // OSGKeyboard · Shared // -// Flat semantic surface used by home / dashboard stats on every platform. -// Deliberately shadowless — hierarchy comes from the semantic surface fill. +// Semantic surface used by home / dashboard stats on every platform. +// Shared card elevation keeps its depth consistent with the rest of the app. import SwiftUI @@ -30,5 +30,6 @@ public struct UsageSurfaceCard: View { .padding(padding) .background(palette.surface, in: shape) .clipShape(shape) + .cardElevation() } } diff --git a/OSGKeyboardTests/AccountAPIClientTests.swift b/OSGKeyboardTests/AccountAPIClientTests.swift index cb80ef1..362c32c 100644 --- a/OSGKeyboardTests/AccountAPIClientTests.swift +++ b/OSGKeyboardTests/AccountAPIClientTests.swift @@ -437,6 +437,37 @@ final class AccountAPIClientTests: XCTestCase { XCTAssertEqual(requests.single?.url?.path, "/v1/auth/logout") } + func testLogoutClearFailureRetainsSessionForRetry() async throws { + let retained = makeAccountSession() + let store = InMemoryAccountSecurityStore( + session: retained, + sessionClearFailures: 1 + ) + let transport = QueueAccountTransport([]) + let client = AccountAPIClient( + baseURL: URL(string: "https://account.test")!, + transport: transport, + sessionVault: store + ) + + do { + try await client.logout() + XCTFail("Expected the first Keychain deletion to fail") + } catch let error as AccountAPIError { + XCTAssertEqual(error, .secureStorage) + } + + let sessionAfterFailure = await store.session + let cachedSessionAfterFailure = try await client.currentSession() + XCTAssertEqual(sessionAfterFailure, retained) + XCTAssertEqual(cachedSessionAfterFailure, retained) + + try await client.logout() + + let sessionAfterRetry = await store.session + XCTAssertNil(sessionAfterRetry) + } + func testDeleteAccountUsesAuthenticatedDeleteAndClearsPrivateSession() async throws { let store = InMemoryAccountSecurityStore(session: makeAccountSession()) let transport = QueueAccountTransport([ diff --git a/OSGKeyboardTests/AccountCenterViewModelTests.swift b/OSGKeyboardTests/AccountCenterViewModelTests.swift index 6e60406..4336c5a 100644 --- a/OSGKeyboardTests/AccountCenterViewModelTests.swift +++ b/OSGKeyboardTests/AccountCenterViewModelTests.swift @@ -3,6 +3,7 @@ // // Pure invitation parsing and account coordinator state-machine coverage. +import AuthenticationServices @testable import OSGKeyboard import XCTest @@ -184,7 +185,8 @@ final class AccountCenterViewModelTests: XCTestCase { with: AppleAuthorizationPayload( identityToken: "identity", authorizationCode: "authorization", - nonce: "nonce" + nonce: "nonce", + userIdentifier: "apple-user" ) ) } @@ -341,6 +343,40 @@ final class AccountCenterViewModelTests: XCTestCase { XCTAssertEqual(loadCount, 2) } + @MainActor + func testForcedRefreshQueuedDuringInFlightRefreshRunsAfterward() async { + let account = AccountSession( + accountID: UUID(), + createdAtEpochSeconds: 1_700_000_000 + ) + let clock = MutableAccountClock(now: Date(timeIntervalSince1970: 1_000)) + let service = AccountServiceSpy( + restoredSession: account, + snapshot: makeSnapshot(account: account), + accountLoadDelayNanoseconds: 20_000_000 + ) + let coordinator = AccountSessionCoordinator( + dependencies: AccountDependencies( + sessionService: service, + centerService: service + ), + pendingReferralStore: InMemoryPendingReferralStore(), + now: { clock.now } + ) + await coordinator.restoreIfNeeded() + clock.now = clock.now.addingTimeInterval(601) + + let staleRefresh = Task { await coordinator.refreshAccountData() } + while !coordinator.isRefreshingAccountData { + await Task.yield() + } + await coordinator.refreshAccountData(force: true) + await staleRefresh.value + + let loadCount = await service.loadCount() + XCTAssertEqual(loadCount, 3) + } + @MainActor func testFailedAccountRefreshKeepsLastSuccessfulSnapshotAndTime() async { let account = AccountSession( @@ -488,7 +524,8 @@ final class AccountCenterViewModelTests: XCTestCase { with: AppleAuthorizationPayload( identityToken: "identity", authorizationCode: "authorization", - nonce: "nonce" + nonce: "nonce", + userIdentifier: "apple-user" ) ) @@ -570,6 +607,128 @@ final class AccountCenterViewModelTests: XCTestCase { XCTAssertEqual(signOutCount, 1) } + @MainActor + func testMissingAppleIdentifierRequiresOneTimeReauthentication() async { + let account = AccountSession( + accountID: UUID(), + createdAtEpochSeconds: 1_700_000_000 + ) + let service = AccountServiceSpy( + restoredSession: account, + snapshot: makeSnapshot(account: account), + appleCredentialState: .reauthenticationRequired + ) + let coordinator = AccountSessionCoordinator( + dependencies: AccountDependencies( + sessionService: service, + centerService: service + ), + pendingReferralStore: InMemoryPendingReferralStore() + ) + await coordinator.restoreIfNeeded() + + await coordinator.validateAppleCredentialState() + + XCTAssertEqual(coordinator.sessionPhase, .signedOut) + XCTAssertEqual( + coordinator.operationErrorKey, + "account.error.appleReauthenticationRequired" + ) + } + + @MainActor + func testRevocationSignOutFailureKeepsSessionForRetry() async { + let account = AccountSession( + accountID: UUID(), + createdAtEpochSeconds: 1_700_000_000 + ) + let service = AccountServiceSpy( + restoredSession: account, + snapshot: makeSnapshot(account: account), + shouldFailSignOut: true, + appleCredentialState: .revoked + ) + let coordinator = AccountSessionCoordinator( + dependencies: AccountDependencies( + sessionService: service, + centerService: service + ), + pendingReferralStore: InMemoryPendingReferralStore() + ) + await coordinator.restoreIfNeeded() + + await coordinator.validateAppleCredentialState() + + XCTAssertEqual(coordinator.sessionPhase, .signedIn(account)) + XCTAssertEqual(coordinator.operationErrorKey, "account.error.signOut") + } + + @MainActor + func testCredentialRevocationNotificationClearsSignedInState() async { + let account = AccountSession( + accountID: UUID(), + createdAtEpochSeconds: 1_700_000_000 + ) + let center = NotificationCenter() + let service = AccountServiceSpy( + restoredSession: account, + snapshot: makeSnapshot(account: account) + ) + let coordinator = AccountSessionCoordinator( + dependencies: AccountDependencies( + sessionService: service, + centerService: service + ), + pendingReferralStore: InMemoryPendingReferralStore(), + notificationCenter: center + ) + await coordinator.restoreIfNeeded() + + center.post( + name: ASAuthorizationAppleIDProvider.credentialRevokedNotification, + object: nil + ) + for _ in 0..<100 where coordinator.isSignedIn { + await Task.yield() + } + + XCTAssertEqual(coordinator.sessionPhase, .signedOut) + XCTAssertEqual(coordinator.operationErrorKey, "account.error.sessionExpired") + } + + @MainActor + func testRevocationPreventsInFlightProfileUpdateFromRestoringSession() async { + let account = AccountSession( + accountID: UUID(), + createdAtEpochSeconds: 1_700_000_000, + displayName: "Before" + ) + let service = AccountServiceSpy( + restoredSession: account, + snapshot: makeSnapshot(account: account), + profileUpdateDelayNanoseconds: 20_000_000, + appleCredentialState: .revoked + ) + let coordinator = AccountSessionCoordinator( + dependencies: AccountDependencies( + sessionService: service, + centerService: service + ), + pendingReferralStore: InMemoryPendingReferralStore() + ) + await coordinator.restoreIfNeeded() + + let update = Task { await coordinator.updateDisplayName("After") } + while coordinator.operation != .updatingProfile { + await Task.yield() + } + await coordinator.validateAppleCredentialState() + let didUpdate = await update.value + + XCTAssertFalse(didUpdate) + XCTAssertEqual(coordinator.sessionPhase, .signedOut) + } + private func makeReferral(status: AccountReferralStatus) -> AccountReferral { AccountReferral( id: UUID(), @@ -642,9 +801,11 @@ private actor AccountServiceSpy: AccountSessionServicing, AccountCenterServicing private let refreshedSnapshot: AccountCenterSnapshot? private let shouldFailRedemption: Bool private let shouldFailAccountRefresh: Bool + private let shouldFailSignOut: Bool private let signOutDelayNanoseconds: UInt64 private let signInDelayNanoseconds: UInt64 private let accountLoadDelayNanoseconds: UInt64 + private let profileUpdateDelayNanoseconds: UInt64 private var remainingRestoreFailures: Int private let storedAppleCredentialState: AccountAppleCredentialState private var redeemed: [String] = [] @@ -662,10 +823,12 @@ private actor AccountServiceSpy: AccountSessionServicing, AccountCenterServicing refreshedSnapshot: AccountCenterSnapshot? = nil, shouldFailRedemption: Bool = false, shouldFailAccountRefresh: Bool = false, + shouldFailSignOut: Bool = false, signOutDelayNanoseconds: UInt64 = 0, signInDelayNanoseconds: UInt64 = 0, restoreFailureCount: Int = 0, accountLoadDelayNanoseconds: UInt64 = 0, + profileUpdateDelayNanoseconds: UInt64 = 0, appleCredentialState: AccountAppleCredentialState = .unknown ) { restored = restoredSession @@ -674,10 +837,12 @@ private actor AccountServiceSpy: AccountSessionServicing, AccountCenterServicing self.refreshedSnapshot = refreshedSnapshot self.shouldFailRedemption = shouldFailRedemption self.shouldFailAccountRefresh = shouldFailAccountRefresh + self.shouldFailSignOut = shouldFailSignOut self.signOutDelayNanoseconds = signOutDelayNanoseconds self.signInDelayNanoseconds = signInDelayNanoseconds remainingRestoreFailures = restoreFailureCount self.accountLoadDelayNanoseconds = accountLoadDelayNanoseconds + self.profileUpdateDelayNanoseconds = profileUpdateDelayNanoseconds self.storedAppleCredentialState = appleCredentialState } @@ -703,6 +868,9 @@ private actor AccountServiceSpy: AccountSessionServicing, AccountCenterServicing try await Task.sleep(nanoseconds: signOutDelayNanoseconds) } logoutCount += 1 + if shouldFailSignOut { + throw AccountServiceSpyError.failed + } } func deleteAccount(with payload: AppleAuthorizationPayload) async throws { @@ -739,6 +907,20 @@ private actor AccountServiceSpy: AccountSessionServicing, AccountCenterServicing return centerSnapshot } + func updateDisplayName(_ displayName: String) async throws -> AccountSession { + if profileUpdateDelayNanoseconds > 0 { + try await Task.sleep(nanoseconds: profileUpdateDelayNanoseconds) + } + guard let signedInAccount else { + throw AccountIntegrationError.unavailable + } + return AccountSession( + accountID: signedInAccount.accountID, + createdAtEpochSeconds: signedInAccount.createdAtEpochSeconds, + displayName: displayName + ) + } + func redeemReferral(code: String) async throws { if shouldFailRedemption { throw AccountServiceSpyError.failed diff --git a/OSGKeyboardTests/FlowFailureLogStoreTests.swift b/OSGKeyboardTests/FlowFailureLogStoreTests.swift new file mode 100644 index 0000000..1c52c0a --- /dev/null +++ b/OSGKeyboardTests/FlowFailureLogStoreTests.swift @@ -0,0 +1,126 @@ +// FlowFailureLogStoreTests.swift +// OSGKeyboardTests + +import Foundation +@testable import OSGKeyboard +import XCTest + +final class FlowFailureLogStoreTests: XCTestCase { + func testFailureReportKeepsOnlyTenSecondsAndRedactsSensitiveMetadata() throws { + let directory = temporaryDirectory() + defer { try? FileManager.default.removeItem(at: directory) } + + let clock = MutableFailureLogClock( + Date(timeIntervalSince1970: 1_800_000_000) + ) + let store = FlowFailureLogStore( + directoryURL: directory, + now: { clock.current } + ) + + store.record("event=tooOld") + clock.advance(by: 11) + store.record( + "event=current sessionId=550e8400-e29b-41d4-a716-446655440000 " + + "access_token=secret container=/private/var/mobile/Containers/example" + ) + + let url = try XCTUnwrap( + store.persistStartupFailure( + reason: "timedOut", + context: ["authorization": "bearer=secret"] + ) + ) + let decoder = JSONDecoder() + decoder.dateDecodingStrategy = .iso8601 + let report = try decoder.decode( + FlowStartupFailureReport.self, + from: Data(contentsOf: url) + ) + + XCTAssertEqual(report.reason, "timedOut") + XCTAssertEqual(report.events.count, 1) + XCTAssertFalse(report.events[0].message.contains("tooOld")) + XCTAssertFalse(report.events[0].message.contains("550e8400")) + XCTAssertFalse(report.events[0].message.contains("secret")) + XCTAssertFalse(report.events[0].message.contains("/private/var/mobile")) + XCTAssertEqual(report.events[0].message.components(separatedBy: "").count, 2) + } + + func testReportRetentionKeepsNewestFilesWithinCountLimit() throws { + let directory = temporaryDirectory() + defer { try? FileManager.default.removeItem(at: directory) } + + let clock = MutableFailureLogClock( + Date(timeIntervalSince1970: 1_800_000_000) + ) + let store = FlowFailureLogStore( + directoryURL: directory, + maxReportCount: 2, + now: { clock.current } + ) + + for index in 0..<3 { + store.record("event=failure\(index)") + XCTAssertNotNil( + store.persistStartupFailure( + reason: "timedOut", + context: ["index": "\(index)"] + ) + ) + clock.advance(by: 1) + } + + XCTAssertEqual(store.reportURLs().count, 2) + } + + func testExportCombinesReportsAndClearRemovesAllFiles() throws { + let directory = temporaryDirectory() + defer { try? FileManager.default.removeItem(at: directory) } + + let store = FlowFailureLogStore(directoryURL: directory) + store.record("event=failure") + XCTAssertNotNil( + store.persistStartupFailure( + reason: "notPossible", + context: [:] + ) + ) + + let exportURL = try XCTUnwrap(store.makeExportURL()) + let exportText = try String(contentsOf: exportURL, encoding: .utf8) + XCTAssertTrue(exportText.contains("\"reports\"")) + XCTAssertTrue(exportText.contains("\"notPossible\"")) + + store.deleteAllReports() + XCTAssertTrue(store.reportURLs().isEmpty) + XCTAssertFalse(FileManager.default.fileExists(atPath: exportURL.path)) + } + + private func temporaryDirectory() -> URL { + FileManager.default.temporaryDirectory.appendingPathComponent( + "FlowFailureLogStoreTests-\(UUID().uuidString)", + isDirectory: true + ) + } +} + +private final class MutableFailureLogClock: @unchecked Sendable { + private let lock: NSLock + private var value: Date + + init(_ date: Date) { + value = date + lock = NSLock() + } + + var current: Date { + lock.withLock { value } + } + + func advance(by interval: TimeInterval) { + lock.withLock { + value = value.addingTimeInterval(interval) + } + } +} diff --git a/OSGKeyboardTests/ReferralProfileTests.swift b/OSGKeyboardTests/ReferralProfileTests.swift index dc61376..b3a7a0a 100644 --- a/OSGKeyboardTests/ReferralProfileTests.swift +++ b/OSGKeyboardTests/ReferralProfileTests.swift @@ -314,7 +314,8 @@ final class ReferralProfileTests: XCTestCase { with: AppleAuthorizationPayload( identityToken: "identity", authorizationCode: "authorization", - nonce: "nonce" + nonce: "nonce", + userIdentifier: "apple-user" ) ) diff --git a/OSGKeyboardTests/TestSupport/AccountTestDoubles.swift b/OSGKeyboardTests/TestSupport/AccountTestDoubles.swift index 31665d7..7916458 100644 --- a/OSGKeyboardTests/TestSupport/AccountTestDoubles.swift +++ b/OSGKeyboardTests/TestSupport/AccountTestDoubles.swift @@ -13,17 +13,20 @@ actor InMemoryAccountSecurityStore: AccountSessionVault, AppAttestKeyStateStorin private(set) var clearSessionCount = 0 private(set) var clearRefreshTransactionCount = 0 private var remainingSessionSaveFailures: Int + private var remainingSessionClearFailures: Int init( session: AccountSession? = nil, refreshTransaction: AccountRefreshTransaction? = nil, keyState: AppAttestKeyState? = nil, - sessionSaveFailures: Int = 0 + sessionSaveFailures: Int = 0, + sessionClearFailures: Int = 0 ) { self.session = session self.refreshTransaction = refreshTransaction self.keyState = keyState self.remainingSessionSaveFailures = sessionSaveFailures + self.remainingSessionClearFailures = sessionClearFailures } func loadSession() async throws -> AccountSession? { @@ -39,6 +42,10 @@ actor InMemoryAccountSecurityStore: AccountSessionVault, AppAttestKeyStateStorin } func clearSession() async throws { + if remainingSessionClearFailures > 0 { + remainingSessionClearFailures -= 1 + throw AccountAPIError.secureStorage + } session = nil clearSessionCount += 1 } diff --git a/docs/assets/whats-new/account-sign-in-en.png b/docs/assets/whats-new/account-sign-in-en.png new file mode 100644 index 0000000000000000000000000000000000000000..ee0582952c491d700013d967398407bba3344176 GIT binary patch literal 57121 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

!~~wk~@+W8My0&z9uB_<&3gy?o^$ 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⪻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 R|HA(*Bc=E_U-H@O{|EK4qL2Up literal 0 HcmV?d00001 diff --git a/docs/assets/whats-new/account-sign-in-zh.png b/docs/assets/whats-new/account-sign-in-zh.png new file mode 100644 index 0000000000000000000000000000000000000000..fc658ad40c7e683ccd369faac851886e831ac2c8 GIT binary patch literal 67059 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�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 literal 0 HcmV?d00001 diff --git a/docs/osgkeyboardversion.html b/docs/osgkeyboardversion.html index c2498ba..b636604 100644 --- a/docs/osgkeyboardversion.html +++ b/docs/osgkeyboardversion.html @@ -178,98 +178,155 @@

暂无与当前版本匹配的更新说明。

No release notes match this app version.

- -
-
2.0.1
+ +
+
2.0.2
-

更安全、更顺畅的 AI 体验

+

UI 与交互全面升级

    -
  • 未登录、退出账号或会话过期时,所有 AI 功能会自动切回用户自备 API Key,并阻止缓存的托管 Grant 继续消耗积分
  • -
  • 首次使用流程更加精简,可依次体验听写、翻译、智能回复和语音提问,再决定是否登录
  • -
  • 官方 AI 技能目录可独立更新,无需等待新的 App 版本
  • -
  • 修复后台挂起时的埋点数据库锁、重复显示购买成功提示,以及账号和设置刷新不稳定的问题
  • +
  • iPhone 使用 iOS 26 原生 Liquid Glass 标签栏与页面过渡,切换标签时会保留各页面的导航状态
  • +
  • 技能页改为更清晰的已安装与未安装列表;点按技能进入完整详情页,集中管理安装、快捷指令配置和自定义技能编辑
  • +
  • 重新整理首页、账号、AI 服务和个人词库中的信息层级;缺少必要配置时,会直接说明原因并提供下一步入口
  • +
+
+ +
+
+

新增 Apple 账号登录

+
+ OSGKeyboard 通过 Apple 登录账号的真实界面卡片 +
+
    +
  • 支持通过 Apple 登录创建或打开 OSG 账号,集中查看积分、邀请奖励和购买记录
  • +
  • 登录后可直接通过 OSG 积分使用托管语音与 AI 服务,也可通过 App Store 购买积分包
  • +
  • 本地听写和用户自备 API Key 仍可独立使用,无需登录
  • +
+
+ +
+

邀请好友,双方得积分

+
+ OSGKeyboard 账号中的积分余额与邀请奖励 +
+
    +
  • 登录可领取注册奖励;每成功邀请一位好友,你和好友都可以获得积分
  • +
  • 邀请链接与积分余额会跟随账号保持同步
  • +
+
+
+ +
+

新增个人专属风格

+
+ 润色风格页面中的生成专属风格真实界面卡片 +
+
    +
  • 可从自己的听写表达与明确修改中学习说话风格,生成专属于你的措辞和表达习惯
  • +
  • 个人风格可应用于剪贴板回复,在不改变原意的前提下调整用词与节奏,让回复更像你自己
  • +
  • 用于学习的文字语料仅在你主动点击生成时发送给所选 AI 服务,生成结果需要确认后才会保存和启用
  • +
+
+ +
+

新增智能剪贴板技能

+
    +
  • 端侧模型与本地规则会对剪贴板内容进行基础语义识别,并从完整技能目录中智能推荐最多五个相关操作
  • +
  • 可识别任务、邀约、问句、情绪和语言,推荐回复、翻译、总结、澄清、共情、商务回复或整理清单等技能
  • +
  • 复制单个电话号码时可直接拨号或新建联系人;复制网页链接时可直接打开,或提取公开网页内容进行总结
  • +
  • 识别和推荐过程在设备端完成;只有主动运行需要 AI 的技能时,相关内容才会发送给所选服务
  • +
+
+ +
+

新增自动推荐个人热词

+
+ 首页个性词库中的个人热词推荐真实界面卡片 +
+
    +
  • 根据键盘中反复输入的新词自动生成个人热词推荐,让常用名称、术语和表达更容易再次输入
  • +
  • 推荐词必须由你明确确认后才会加入个人词库;本地学习不会记录完整击键内容
-

Safer, Smoother AI

+

UI and Interaction, Rebuilt

    -
  • When signed out or after a session expires, every AI feature now returns to your own API key and cached managed grants can no longer consume credits
  • -
  • The shorter first-run guide demonstrates dictation, translation, smart reply, and voice questions before the optional sign-in step
  • -
  • The official AI skill catalog can refresh without waiting for a new App Store release
  • -
  • Fixes analytics database locks during suspension, repeated purchase confirmations, and unreliable account or settings refreshes
  • +
  • iPhone now uses the native iOS 26 Liquid Glass tab bar and transitions while preserving each tab's navigation state
  • +
  • Skills are organized into clearer Installed and Not Installed lists; tap one to manage installation, Shortcut setup, or custom-skill editing on a dedicated detail page
  • +
  • Home, Account, AI Service, and Personal Dictionary now have a clearer hierarchy, with direct explanations and next steps when setup is incomplete
-
-
- -
-
2.0.0
- -
-

全新积分体验

-
-

邀请好友,双方得积分

+

New Sign in with Apple

- OSGKeyboard 设置中的邀请奖励卡片 + Real OSGKeyboard account card with Sign in with Apple
    -
  • 每成功邀请一位好友,你和好友各获得1000 积分
  • -
  • 可在设置的账号区域分享邀请链接
  • -
-
- -
-

OSG 积分模式

-
- OSGKeyboard AI 服务中已选择使用积分 -
-
    -
  • 使用 Apple 登录后,无需配置自己的 API Key,即可使用托管语音与 AI 服务
  • -
  • 支持通过 App Store 购买积分包,积分余额自动同步
  • -
  • 本地听写和自备 API Key 仍可独立使用,无需登录
  • -
-
-
-
- -
-
-

A New Credits Experience

- -
-

Invite Friends, Earn Credits Together

-
- Referral rewards card in OSGKeyboard Settings -
-
    -
  • For every successful referral, you and your friend each receive 1,000 credits
  • -
  • Share your invitation link from the Account section in Settings
  • -
-
- -
-

OSG Credits Mode

-
- Use Credits selected in OSGKeyboard AI Service -
-
    -
  • Sign in with Apple to use managed speech and AI services without configuring your own API key
  • -
  • Purchase credit packs through the App Store and keep your balance synchronized automatically
  • +
  • Use Sign in with Apple to create or open an OSG account and manage credits, referral rewards, and purchase history in one place
  • +
  • After signing in, use managed speech and AI services with OSG credits, with additional credit packs available through the App Store
  • Local dictation and user-owned API keys remain available independently, with no sign-in required
+ +
+

Invite Friends, Earn Credits Together

+
+ Credit balance and referral rewards in an OSGKeyboard account +
+
    +
  • Sign in to receive the signup reward; for every successful referral, both you and your friend can earn credits
  • +
  • Your invitation link and credit balance stay synchronized with your account
  • +
+
+
+ +
+

New Personal Style

+
+ Real Generate Your Style card on the Polish Styles page +
+
    +
  • Learn a personal speaking style from your dictation and explicit edits, capturing the wording and expression patterns that make your voice distinct
  • +
  • Apply that style to clipboard replies so wording and rhythm sound more like you without changing the original meaning
  • +
  • Learning text is sent to the selected AI service only when you explicitly tap Generate, and the result must be reviewed before it is saved and enabled
  • +
+
+ +
+

New Smart Clipboard Skills

+
    +
  • On-device models and local rules perform lightweight semantic recognition, then recommend up to five relevant actions from the complete skill catalog
  • +
  • Tasks, invitations, questions, sentiment, and language can surface skills such as Reply, Translate, Summarize, Clarify, Empathy, Business Reply, or Organize List
  • +
  • A single copied phone number can open calling or a prefilled new-contact form; a copied web link can open directly or be summarized from bounded public webpage content
  • +
  • Recognition and recommendation stay on device. Content is sent to the selected service only after you run a skill that requires AI
  • +
+
+ +
+

New Automatic Personal-Term Suggestions

+
+ Real personal-term suggestion card on Home +
+
    +
  • Repeated new terms from keyboard input become suggestions, making frequently used names, terminology, and expressions easier to type again
  • +
  • A suggestion joins Personal Dictionary only after your explicit confirmation; local learning does not record complete keystrokes
  • +
+ + + + + +
1.8.0