From f78a7fed25f221ea3d2a070fdee5ed4e8c7ed899 Mon Sep 17 00:00:00 2001 From: Luckycat133 Date: Sat, 18 Jul 2026 17:08:47 +0800 Subject: [PATCH 01/21] docs: mark iOS as the primary Chat Buddy app --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index ea239c2..1c6abd6 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,9 @@ # Chat Buddy iOS +> **Status: Active Development — primary development version of Chat Buddy.** +> The [web application](https://github.com/Luckycat133/Chat_Buddy) is in maintenance mode and serves as the migration source for existing user data. + ![iOS 26+](https://img.shields.io/badge/iOS-26.0+-black?style=flat&logo=apple) ![SwiftUI](https://img.shields.io/badge/SwiftUI-100%25-blue?style=flat&logo=swift) ![License MIT](https://img.shields.io/badge/License-MIT-green.svg) @@ -14,6 +17,15 @@ - ⏱️ **Moments Background Orchestration**: Deep integration with the iOS `BackgroundTasks` framework (`BGAppRefreshTask` & `BGProcessingTask`). Your AI companions will naturally and autonomously generate updates, celebrate birthdays, and post holiday greetings to their "Moments" feed even while the app is completely backgrounded. - 🎨 **Premium Native UI/UX**: Built with a rigorous design system featuring glassmorphism elements, dynamic accent colors, smooth SwiftUI transitions, and robust bilingual (English/Chinese) localization support. +## 🗺 Roadmap + +- **MVP stabilization (current)**: Keep the Web → iOS data schema compatible, expand core chat/storage tests, and validate background tasks on physical devices. +- **Internal beta**: Complete migration testing and collect crash/performance feedback from a small device matrix. +- **TestFlight**: Ship a signed beta with release notes, privacy disclosures, and a repeatable archive process. +- **App Store**: Complete review assets, support documentation, and production monitoring before public release. + +Background refresh is opportunistic: iOS decides when `BackgroundTasks` run, so the app does not promise execution at an exact time. API keys are stored in Keychain rather than plain-text `UserDefaults`. + ## 🛠 Tech Stack - **UI Framework**: SwiftUI @@ -30,6 +42,8 @@ 3. The project uses standard iOS frameworks and requires no complex third-party setup. 4. Set your run destination and hit `Cmd + R`. +Pull requests are also built and unit-tested on GitHub Actions using the macOS 26 runner and Xcode 26.5. + ### Terminal Build & Test ```bash From 63daa734293f9f176a27ac162968e36c6399cbcb Mon Sep 17 00:00:00 2001 From: Luckycat133 Date: Sat, 18 Jul 2026 17:08:51 +0800 Subject: [PATCH 02/21] docs: add primary app status and roadmap in Chinese --- README_zh.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README_zh.md b/README_zh.md index ea840e6..b9448ff 100644 --- a/README_zh.md +++ b/README_zh.md @@ -2,6 +2,9 @@ # Chat Buddy iOS +> **状态:积极开发中 —— Chat Buddy 的主要开发版本。** +> [Web 版本](https://github.com/Luckycat133/Chat_Buddy)现处于维护模式,并作为现有用户数据迁移到 iOS 的来源。 + **Chat Buddy iOS** 是一款基于 SwiftUI 纯原生打造的高性能智能 AI 伴侣应用。该项目将广受欢迎的 Web 端“Chat Buddy”的核心功能深度迁移至 iOS 环境,提供了无缝衔接且纯粹的原生体验。 ![iOS 26+](https://img.shields.io/badge/iOS-26.0+-black?style=flat&logo=apple) @@ -16,6 +19,15 @@ - ⏱️ **Moments 后台编排 (朋友圈)**: 深度集成 iOS `BackgroundTasks` 机制 (`BGAppRefreshTask` & `BGProcessingTask`)。你的 AI 伴侣们能够自主、自然地在后台发文字/图片状态、记住你的生日、甚至在特定的节假日为你送出祝福,即便你并未打开应用。 - 🎨 **顶级原生 UI/UX**: 配备严谨的 Glassmorphism 毛玻璃设计系统。支持自定义提取主题强调色、流畅的 SwiftUI 过渡动画,并在整个应用内完美支持英文和简体中文的动态双语切换。 +## 🗺 版本路线 + +- **MVP 稳定化(当前)**:保持 Web → iOS 数据 schema 兼容,扩充核心聊天和存储测试,并在真机上验证后台任务。 +- **内部测试**:完成迁移测试,在小型设备矩阵上收集崩溃和性能反馈。 +- **TestFlight**:发布已签名的测试版,补齐发布说明、隐私声明和可重复的归档流程。 +- **App Store**:上线前完成审核素材、支持文档和生产监控。 + +后台刷新属于机会性调度:实际执行时间由 iOS 决定,应用不承诺在固定时刻执行。API Key 存储在 Keychain,不会以明文写入 `UserDefaults`。 + ## 🛠 技术栈 - **界面框架**: SwiftUI @@ -32,6 +44,8 @@ 3. 本项目完全使用标准的 iOS 原生框架,不需要繁杂的第三方依赖 (No CocoaPods / No SPM)。 4. 选择 iOS 模拟器或真实设备,按下 `Cmd + R` 即可直接编译运行! +Pull Request 会在 GitHub Actions 中使用 macOS 26 和 Xcode 26.5 自动构建并运行单元测试。 + ### 终端构建与测试 ```bash From 42b7fa3c42c76247e3d5bc15064f968440c86fac Mon Sep 17 00:00:00 2001 From: Luckycat133 Date: Sat, 18 Jul 2026 17:08:56 +0800 Subject: [PATCH 03/21] ci: build and test the iOS app --- .github/workflows/ios-ci.yml | 39 ++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/workflows/ios-ci.yml diff --git a/.github/workflows/ios-ci.yml b/.github/workflows/ios-ci.yml new file mode 100644 index 0000000..b3a5b38 --- /dev/null +++ b/.github/workflows/ios-ci.yml @@ -0,0 +1,39 @@ +name: iOS CI + +on: + pull_request: + push: + branches: + - main + +permissions: + contents: read + +concurrency: + group: ios-ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build-and-unit-test: + runs-on: macos-26 + timeout-minutes: 45 + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Select Xcode 26.5 + run: sudo xcode-select --switch /Applications/Xcode_26.5.app/Contents/Developer + + - name: Show toolchain + run: xcodebuild -version + + - name: Build and run unit tests + run: | + set -o pipefail + xcodebuild test \ + -project Chat_Buddy_iOS.xcodeproj \ + -scheme Chat_Buddy_iOS \ + -destination 'platform=iOS Simulator,OS=26.5,name=iPhone 17 Pro' \ + -only-testing:Chat_Buddy_iOSTests \ + CODE_SIGNING_ALLOWED=NO From d273cddb6036ce25d2455e36c0c6d17ebe70c329 Mon Sep 17 00:00:00 2001 From: Luckycat133 Date: Sat, 18 Jul 2026 17:11:35 +0800 Subject: [PATCH 04/21] fix: restore iOS application compilation --- Chat_Buddy_iOS/Features/Chats/ChatViewModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Chat_Buddy_iOS/Features/Chats/ChatViewModel.swift b/Chat_Buddy_iOS/Features/Chats/ChatViewModel.swift index 78edcc1..b09e82d 100644 --- a/Chat_Buddy_iOS/Features/Chats/ChatViewModel.swift +++ b/Chat_Buddy_iOS/Features/Chats/ChatViewModel.swift @@ -173,7 +173,7 @@ enum ChatViewModelError: LocalizedError { isTyping = false } - private func saveNewMemories(_ memories: [ExtractedMemory], for personaId: String, service: MemoryService?) { + private func saveNewMemories(_ memories: [AIPipeline.ExtractedMemory], for personaId: String, service: MemoryService?) { for extracted in memories { service?.addMemory( personaId: personaId, From 9e35f683d2d5783b87c3522765363f5feec866d1 Mon Sep 17 00:00:00 2001 From: Luckycat133 Date: Sat, 18 Jul 2026 17:11:39 +0800 Subject: [PATCH 05/21] fix: restore iOS application compilation --- Chat_Buddy_iOS/Services/API/KeychainService.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Chat_Buddy_iOS/Services/API/KeychainService.swift b/Chat_Buddy_iOS/Services/API/KeychainService.swift index 81f790a..327001a 100644 --- a/Chat_Buddy_iOS/Services/API/KeychainService.swift +++ b/Chat_Buddy_iOS/Services/API/KeychainService.swift @@ -1,6 +1,7 @@ import Foundation import Security import LocalAuthentication +import os enum KeychainError: LocalizedError { case encodingFailed From 19263ef8321479a8779514d0e19c9d22e4f7cc7b Mon Sep 17 00:00:00 2001 From: Luckycat133 Date: Sat, 18 Jul 2026 17:11:48 +0800 Subject: [PATCH 06/21] fix: restore iOS application compilation --- Chat_Buddy_iOS/Services/API/AIClient.swift | 25 +++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/Chat_Buddy_iOS/Services/API/AIClient.swift b/Chat_Buddy_iOS/Services/API/AIClient.swift index 9a4c082..c11fff1 100644 --- a/Chat_Buddy_iOS/Services/API/AIClient.swift +++ b/Chat_Buddy_iOS/Services/API/AIClient.swift @@ -1,7 +1,30 @@ import Foundation import os.log -actor AIClient { +protocol AIClientProtocol: Sendable { + func sendChatCompletion( + messages: [ChatMessage], + model: String?, + temperature: Double?, + config: APIConfig + ) async throws -> ChatCompletionResponse +} + +extension AIClientProtocol { + func sendChatCompletion( + messages: [ChatMessage], + config: APIConfig + ) async throws -> ChatCompletionResponse { + try await sendChatCompletion( + messages: messages, + model: nil, + temperature: nil, + config: config + ) + } +} + +actor AIClient: AIClientProtocol { static let shared = AIClient() private var apiClient: APIClient From 58e9c2154f36411ecd7542afe16fd889b7c782b9 Mon Sep 17 00:00:00 2001 From: Luckycat133 Date: Sat, 18 Jul 2026 17:14:11 +0800 Subject: [PATCH 07/21] ci: provision the simulator deterministically --- .github/workflows/ios-ci.yml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ios-ci.yml b/.github/workflows/ios-ci.yml index b3a5b38..fdbaf92 100644 --- a/.github/workflows/ios-ci.yml +++ b/.github/workflows/ios-ci.yml @@ -28,12 +28,26 @@ jobs: - name: Show toolchain run: xcodebuild -version + - name: Prepare iOS simulator + run: | + set -euo pipefail + SIMULATOR_UDID=$(xcrun simctl list devices available | awk -F '[()]' '/iPhone 17 Pro/ {print $2; exit}') + if [ -z "$SIMULATOR_UDID" ]; then + SIMULATOR_UDID=$(xcrun simctl create \ + 'CI iPhone 17 Pro' \ + 'com.apple.CoreSimulator.SimDeviceType.iPhone-17-Pro' \ + 'com.apple.CoreSimulator.SimRuntime.iOS-26-5') + fi + echo "SIMULATOR_UDID=$SIMULATOR_UDID" >> "$GITHUB_ENV" + xcrun simctl boot "$SIMULATOR_UDID" || true + xcrun simctl bootstatus "$SIMULATOR_UDID" -b + - name: Build and run unit tests run: | set -o pipefail xcodebuild test \ -project Chat_Buddy_iOS.xcodeproj \ -scheme Chat_Buddy_iOS \ - -destination 'platform=iOS Simulator,OS=26.5,name=iPhone 17 Pro' \ + -destination "platform=iOS Simulator,id=$SIMULATOR_UDID" \ -only-testing:Chat_Buddy_iOSTests \ CODE_SIGNING_ALLOWED=NO From 01e1121e75d020707c9b365d5678630275689e05 Mon Sep 17 00:00:00 2001 From: Luckycat133 Date: Sat, 18 Jul 2026 17:18:37 +0800 Subject: [PATCH 08/21] fix: resolve Xcode 26.5 compile errors --- Chat_Buddy_iOS/Services/API/APIClient.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Chat_Buddy_iOS/Services/API/APIClient.swift b/Chat_Buddy_iOS/Services/API/APIClient.swift index 1e40862..2ffa59a 100644 --- a/Chat_Buddy_iOS/Services/API/APIClient.swift +++ b/Chat_Buddy_iOS/Services/API/APIClient.swift @@ -77,14 +77,14 @@ actor APIClient { Double(maxRetries - retriesLeft + 1) + Double.random(in: 0...0.5), Self.maxRetryDelay ) - Self.logger.info("Rate limited (429), retrying in \(waitTime, format: .seconds(style: .floatingPoint))s...") + Self.logger.info("Rate limited (429), retrying in \(waitTime, privacy: .public)s...") try await Task.sleep(for: .seconds(waitTime)) return try await fetchWithRetry(request, retriesLeft: retriesLeft - 1, attempt: attempt + 1) } if httpResponse.statusCode >= 500 && retriesLeft > 0 { let waitTime = min(1.0 * Double(maxRetries - retriesLeft + 1), Self.maxRetryDelay) - Self.logger.info("Server error (\(httpResponse.statusCode)), retrying in \(waitTime, format: .seconds(style: .floatingPoint))s...") + Self.logger.info("Server error (\(httpResponse.statusCode)), retrying in \(waitTime, privacy: .public)s...") try await Task.sleep(for: .seconds(waitTime)) return try await fetchWithRetry(request, retriesLeft: retriesLeft - 1, attempt: attempt + 1) } @@ -101,7 +101,7 @@ actor APIClient { } catch { if retriesLeft > 0 && !(error is CancellationError) { let waitTime = Double(attempt + 1) - Self.logger.info("Network error, retrying in \(waitTime, format: .seconds(style: .floatingPoint))s...") + Self.logger.info("Network error, retrying in \(waitTime, privacy: .public)s...") try await Task.sleep(for: .seconds(waitTime)) return try await fetchWithRetry(request, retriesLeft: retriesLeft - 1, attempt: attempt + 1) } From 6209bfc8c6c48670de5ea4a006fa5c35479a6426 Mon Sep 17 00:00:00 2001 From: Luckycat133 Date: Sat, 18 Jul 2026 17:18:43 +0800 Subject: [PATCH 09/21] fix: resolve Xcode 26.5 compile errors --- Chat_Buddy_iOS/Services/API/KeychainService.swift | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Chat_Buddy_iOS/Services/API/KeychainService.swift b/Chat_Buddy_iOS/Services/API/KeychainService.swift index 327001a..bb1b8fc 100644 --- a/Chat_Buddy_iOS/Services/API/KeychainService.swift +++ b/Chat_Buddy_iOS/Services/API/KeychainService.swift @@ -127,14 +127,10 @@ enum KeychainService { logger.warning("Authentication failed for key: \(key)") throw KeychainError.authenticationFailed - case errSecBiometryNotAvailable, errSecBiometryNotEnrolled: - logger.warning("Biometric not available for key: \(key)") + case errSecInteractionNotAllowed: + logger.warning("Keychain interaction is not available for key: \(key)") throw KeychainError.biometricNotAvailable - case errSecBiometryLockout: - logger.warning("Biometric locked out for key: \(key)") - throw KeychainError.biometricFailed - default: logger.error("Keychain error for key \(key): \(status)") return nil From f3eeea595a0b812abb994a859331fa1a135a0f2a Mon Sep 17 00:00:00 2001 From: Luckycat133 Date: Sat, 18 Jul 2026 17:19:19 +0800 Subject: [PATCH 10/21] fix: resolve Xcode 26.5 compile errors --- Chat_Buddy_iOS/Services/Chat/AIPipeline.swift | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Chat_Buddy_iOS/Services/Chat/AIPipeline.swift b/Chat_Buddy_iOS/Services/Chat/AIPipeline.swift index 43a8619..b43717a 100644 --- a/Chat_Buddy_iOS/Services/Chat/AIPipeline.swift +++ b/Chat_Buddy_iOS/Services/Chat/AIPipeline.swift @@ -93,10 +93,11 @@ enum AIPipeline { // RAG context injection var ragBlock = "" if ragEnabled, let query = userQuery ?? session.displayMessages.last(where: { $0.role == .user })?.content { - let ragResults = RAGService.searchDocuments(query: query, topK: 3) - if !ragResults.isEmpty { - ragBlock = RAGService.buildRAGContext(ragResults) - } + ragBlock = RAGService.buildRAGContext( + query: query, + indexedChunks: RAGService.loadIndex(), + topK: 3 + ) ?? "" } let systemMsg = ChatMessage( From a59591c5f9d877e5a6e000397520546728f9643f Mon Sep 17 00:00:00 2001 From: Luckycat133 Date: Sat, 18 Jul 2026 17:26:00 +0800 Subject: [PATCH 11/21] fix: share Keychain key derivation with data import --- Chat_Buddy_iOS/Services/API/APIConfigStore.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Chat_Buddy_iOS/Services/API/APIConfigStore.swift b/Chat_Buddy_iOS/Services/API/APIConfigStore.swift index 1143c04..6808e26 100644 --- a/Chat_Buddy_iOS/Services/API/APIConfigStore.swift +++ b/Chat_Buddy_iOS/Services/API/APIConfigStore.swift @@ -15,8 +15,8 @@ final class APIConfigStore { private let storage = StorageService.shared - private static let activeKeyKey = "api-key-active" - private static func profileKeyKey(_ id: UUID) -> String { "api-key-profile-\(id.uuidString)" } + static let activeKeyKey = "api-key-active" + static func profileKeyKey(_ id: UUID) -> String { "api-key-profile-\(id.uuidString)" } init() { let (config, savedProfiles) = Self.loadFromStorage(storage: StorageService.shared) From f4ad1d3882a490a366e6fdf4a2db1dbdc0f3af5e Mon Sep 17 00:00:00 2001 From: Luckycat133 Date: Sat, 18 Jul 2026 17:32:20 +0800 Subject: [PATCH 12/21] fix: propagate Keychain import failures --- Chat_Buddy_iOS/Services/Storage/DataImporter.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Chat_Buddy_iOS/Services/Storage/DataImporter.swift b/Chat_Buddy_iOS/Services/Storage/DataImporter.swift index 1da11fe..05160c1 100644 --- a/Chat_Buddy_iOS/Services/Storage/DataImporter.swift +++ b/Chat_Buddy_iOS/Services/Storage/DataImporter.swift @@ -60,7 +60,7 @@ struct DataImporter { configStore.activeConfig = config // 如果备份中包含 API 密钥,也保存到 Keychain if !config.apiKey.isEmpty { - KeychainService.set(APIConfigStore.activeKeyKey, value: config.apiKey) + try KeychainService.set(APIConfigStore.activeKeyKey, value: config.apiKey) } count += 1 } @@ -70,7 +70,7 @@ struct DataImporter { // 如果备份中包含 API 密钥,也保存到 Keychain for profile in profiles { if !profile.config.apiKey.isEmpty { - KeychainService.set(APIConfigStore.profileKeyKey(profile.id), value: profile.config.apiKey) + try KeychainService.set(APIConfigStore.profileKeyKey(profile.id), value: profile.config.apiKey) } } count += profiles.count From d6ab31d91ce3806cf18bc6b1000ea333348fde8f Mon Sep 17 00:00:00 2001 From: Luckycat133 Date: Sat, 18 Jul 2026 17:37:06 +0800 Subject: [PATCH 13/21] fix: use the Xcode 26 AttributedString index API --- .../Features/Chats/Components/MarkdownContentView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Chat_Buddy_iOS/Features/Chats/Components/MarkdownContentView.swift b/Chat_Buddy_iOS/Features/Chats/Components/MarkdownContentView.swift index 9afeb31..d31c737 100644 --- a/Chat_Buddy_iOS/Features/Chats/Components/MarkdownContentView.swift +++ b/Chat_Buddy_iOS/Features/Chats/Components/MarkdownContentView.swift @@ -203,7 +203,7 @@ struct MarkdownContentView: View { // Only highlight if it's a word boundary let before = range.lowerBound == result.startIndex let after = range.upperBound == result.endIndex - if (before || !String(result.characters[result.index(before: range.lowerBound)]).first!.isLetter) + if (before || !String(result.characters[result.index(beforeCharacter: range.lowerBound)]).first!.isLetter) && (after || !String(result.characters[range.upperBound]).first!.isLetter) { result[range].foregroundColor = .purple result[range].font = .system(size: 12, weight: .semibold, design: .monospaced) From df2a264dc3d41f68001876f6fd0a96611642852b Mon Sep 17 00:00:00 2001 From: Luckycat133 Date: Sat, 18 Jul 2026 17:42:04 +0800 Subject: [PATCH 14/21] fix: resolve chat view compilation --- Chat_Buddy_iOS/Features/Chats/ChatView.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Chat_Buddy_iOS/Features/Chats/ChatView.swift b/Chat_Buddy_iOS/Features/Chats/ChatView.swift index f467664..1f19786 100644 --- a/Chat_Buddy_iOS/Features/Chats/ChatView.swift +++ b/Chat_Buddy_iOS/Features/Chats/ChatView.swift @@ -354,7 +354,7 @@ struct ChatView: View { scrollToMessageId = initialFocusMessageId } // Proactive greeting - let lastDate = viewModel.messages.last?.timestamp + let lastDate = allMessages.last?.timestamp let isZhUI = localization.uiLanguage.resolved == .zh if let greeting = GreetingService.checkWindowOpenGreeting( lastMessageDate: lastDate, @@ -362,7 +362,7 @@ struct ChatView: View { isZh: isZhUI ) { let greetMsg = ChatMessage(role: .assistant, content: greeting) - viewModel.messages.append(greetMsg) + chatStore.appendMessage(greetMsg, to: sessionId) } } .task(id: viewModel.inputText) { From 8aa1869e4b5ebbff586729eceada511f450203ce Mon Sep 17 00:00:00 2001 From: Luckycat133 Date: Sat, 18 Jul 2026 17:42:09 +0800 Subject: [PATCH 15/21] fix: resolve chat view compilation --- Chat_Buddy_iOS/Features/Chats/GroupDetailsView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Chat_Buddy_iOS/Features/Chats/GroupDetailsView.swift b/Chat_Buddy_iOS/Features/Chats/GroupDetailsView.swift index 5e5e9d6..d34139c 100644 --- a/Chat_Buddy_iOS/Features/Chats/GroupDetailsView.swift +++ b/Chat_Buddy_iOS/Features/Chats/GroupDetailsView.swift @@ -117,7 +117,7 @@ struct GroupDetailsView: View { .font(DSTypography.caption1) .foregroundStyle(.secondary) } else { - ForEach(session.polls) { poll in + ForEach(session.polls, id: \.id) { (poll: ChatPoll) in VStack(alignment: .leading, spacing: 4) { Text(poll.question) .font(DSTypography.footnote.weight(.semibold)) From 8c864ab09575220ffe3d0802bf4cf46dd083a045 Mon Sep 17 00:00:00 2001 From: Luckycat133 Date: Sat, 18 Jul 2026 17:48:45 +0800 Subject: [PATCH 16/21] fix: disambiguate poll status colors --- Chat_Buddy_iOS/Features/Chats/GroupDetailsView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Chat_Buddy_iOS/Features/Chats/GroupDetailsView.swift b/Chat_Buddy_iOS/Features/Chats/GroupDetailsView.swift index d34139c..7c4910c 100644 --- a/Chat_Buddy_iOS/Features/Chats/GroupDetailsView.swift +++ b/Chat_Buddy_iOS/Features/Chats/GroupDetailsView.swift @@ -125,7 +125,7 @@ struct GroupDetailsView: View { ? (isZh ? "已结束" : "Closed") : (isZh ? "进行中" : "Active")) .font(DSTypography.caption2) - .foregroundStyle(poll.isExpired ? .secondary : .green) + .foregroundStyle(poll.isExpired ? Color.secondary : Color.green) } } } From eb49f44d428b4deaa2c4a96ef88e63a88594fa82 Mon Sep 17 00:00:00 2001 From: Luckycat133 Date: Sat, 18 Jul 2026 17:56:39 +0800 Subject: [PATCH 17/21] Disambiguate model preset iteration --- .../Features/Settings/Advanced/ModelSwitcherView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Chat_Buddy_iOS/Features/Settings/Advanced/ModelSwitcherView.swift b/Chat_Buddy_iOS/Features/Settings/Advanced/ModelSwitcherView.swift index f34340f..2d3805d 100644 --- a/Chat_Buddy_iOS/Features/Settings/Advanced/ModelSwitcherView.swift +++ b/Chat_Buddy_iOS/Features/Settings/Advanced/ModelSwitcherView.swift @@ -41,7 +41,7 @@ struct ModelSwitcherView: View { var body: some View { List { Section(isZh ? "模型选择" : "Model") { - ForEach(ModelPreset.all) { preset in + ForEach(ModelPreset.all, id: \.id) { (preset: ModelPreset) in Button { selectedModel = preset.id } label: { From c997c410e132d5b9e50927a7be406a5a0673f9f6 Mon Sep 17 00:00:00 2001 From: Luckycat133 Date: Sat, 18 Jul 2026 17:56:44 +0800 Subject: [PATCH 18/21] Simplify learning report generation --- .../Advanced/LearningReportView.swift | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/Chat_Buddy_iOS/Features/Settings/Advanced/LearningReportView.swift b/Chat_Buddy_iOS/Features/Settings/Advanced/LearningReportView.swift index f6dc8d6..e823d86 100644 --- a/Chat_Buddy_iOS/Features/Settings/Advanced/LearningReportView.swift +++ b/Chat_Buddy_iOS/Features/Settings/Advanced/LearningReportView.swift @@ -29,20 +29,18 @@ struct LearningReportView: View { } private var reportText: String { - [ - isZh ? "学习报告" : "Learning Report", - DateFormatter.localizedString(from: Date(), dateStyle: .medium, timeStyle: .short), - "", - (isZh ? "会话数" : "Sessions") + ": \(totalSessions)", - (isZh ? "消息数" : "Messages") + ": \(totalMessages)", - (isZh ? "积分" : "Points") + ": \(social.points)", - (isZh ? "签到连续" : "Streak") + ": \(social.streakDays)", - (isZh ? "今日任务完成率" : "Daily Task Completion") + ": \(Int(completionRate * 100))%", - "", - isZh ? "高频互动角色:" : "Most interacted personas:", - topPersonas.prefix(5).map { "- \($0.name): \($0.count)" }.joined(separator: "\n") - ] - .joined(separator: "\n") + let title = isZh ? "学习报告" : "Learning Report" + let date = DateFormatter.localizedString(from: Date(), dateStyle: .medium, timeStyle: .short) + let sessions = "\(isZh ? "会话数" : "Sessions"): \(totalSessions)" + let messages = "\(isZh ? "消息数" : "Messages"): \(totalMessages)" + let points = "\(isZh ? "积分" : "Points"): \(social.points)" + let streak = "\(isZh ? "签到连续" : "Streak"): \(social.streakDays)" + let completion = "\(isZh ? "今日任务完成率" : "Daily Task Completion"): \(Int(completionRate * 100))%" + let personasTitle = isZh ? "高频互动角色:" : "Most interacted personas:" + let personas = topPersonas.prefix(5).map { "- \($0.name): \($0.count)" }.joined(separator: "\n") + + return [title, date, "", sessions, messages, points, streak, completion, "", personasTitle, personas] + .joined(separator: "\n") } var body: some View { From da942992ede58367f4e3225de45d3b03076d12c4 Mon Sep 17 00:00:00 2001 From: Luckycat133 Date: Sat, 18 Jul 2026 17:56:49 +0800 Subject: [PATCH 19/21] Fix RAG service argument labels --- .../Features/Settings/Advanced/KnowledgeBaseView.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Chat_Buddy_iOS/Features/Settings/Advanced/KnowledgeBaseView.swift b/Chat_Buddy_iOS/Features/Settings/Advanced/KnowledgeBaseView.swift index a883226..d064498 100644 --- a/Chat_Buddy_iOS/Features/Settings/Advanced/KnowledgeBaseView.swift +++ b/Chat_Buddy_iOS/Features/Settings/Advanced/KnowledgeBaseView.swift @@ -56,7 +56,7 @@ struct KnowledgeBaseView: View { if !data.docs.isEmpty { Button(role: .destructive) { - for doc in data.docs { RAGService.removeDocumentFromIndex(id: doc.id) } + for doc in data.docs { RAGService.removeDocumentFromIndex(documentId: doc.id) } data.docs.removeAll() save() } label: { @@ -90,7 +90,7 @@ struct KnowledgeBaseView: View { .padding(.vertical, 2) .swipeActions { Button(role: .destructive) { - RAGService.removeDocumentFromIndex(id: doc.id) + RAGService.removeDocumentFromIndex(documentId: doc.id) data.docs.removeAll { $0.id == doc.id } save() } label: { @@ -142,7 +142,7 @@ struct KnowledgeBaseView: View { data.docs.insert(doc, at: 0) save() // Index document for RAG search - RAGService.addDocumentToIndex(id: doc.id, title: doc.name, content: doc.content) + RAGService.addDocumentToIndex(id: doc.id, name: doc.name, content: doc.content) errorText = nil } catch { errorText = error.localizedDescription From 4a854fbbfabee7d45bffeb65e998a720c74650e0 Mon Sep 17 00:00:00 2001 From: Luckycat133 Date: Sat, 18 Jul 2026 17:56:54 +0800 Subject: [PATCH 20/21] Fix focused chat navigation initializer --- Chat_Buddy_iOS/Features/Chats/ChatView.swift | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Chat_Buddy_iOS/Features/Chats/ChatView.swift b/Chat_Buddy_iOS/Features/Chats/ChatView.swift index 1f19786..6c752b5 100644 --- a/Chat_Buddy_iOS/Features/Chats/ChatView.swift +++ b/Chat_Buddy_iOS/Features/Chats/ChatView.swift @@ -17,7 +17,13 @@ struct ChatView: View { let sessionId: String let persona: Persona - let initialFocusMessageId: String? = nil + let initialFocusMessageId: String? + + init(sessionId: String, persona: Persona, initialFocusMessageId: String? = nil) { + self.sessionId = sessionId + self.persona = persona + self.initialFocusMessageId = initialFocusMessageId + } @State private var viewModel = ChatViewModel() @State private var showClearAlert = false From 24f847adfdcd7dba201c0fcfd391b063e530e2ee Mon Sep 17 00:00:00 2001 From: Luckycat133 Date: Sat, 18 Jul 2026 18:05:01 +0800 Subject: [PATCH 21/21] Fix Xcode 26 accent style compatibility --- .../Features/Settings/Advanced/ModelSwitcherView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Chat_Buddy_iOS/Features/Settings/Advanced/ModelSwitcherView.swift b/Chat_Buddy_iOS/Features/Settings/Advanced/ModelSwitcherView.swift index 2d3805d..5d6b667 100644 --- a/Chat_Buddy_iOS/Features/Settings/Advanced/ModelSwitcherView.swift +++ b/Chat_Buddy_iOS/Features/Settings/Advanced/ModelSwitcherView.swift @@ -57,7 +57,7 @@ struct ModelSwitcherView: View { Spacer() if selectedModel == preset.id { Image(systemName: "checkmark.circle.fill") - .foregroundStyle(.accent) + .foregroundStyle(Color.accentColor) } } }