From 76b58e6fdc8b26c2a2a18081c5c33f36c51b5605 Mon Sep 17 00:00:00 2001 From: Rune Olsen Date: Sun, 2 Aug 2026 14:58:14 +0200 Subject: [PATCH] Rename app from oAI to Confab "oAI" reads as easily confused with OpenAI, both visually and in casual conversation. Renamed to "Confab" throughout: Xcode target/scheme/bundle ID (com.oai.Confab), Info.plist and Help Book identity, all user-facing UI text, internal Log subsystem and color identifiers, localization catalogs (6 languages, including a proper reworded/retranslated Intel-deprecation notice), Help Book HTML content, and docs (README/DEVELOPMENT/PRIVACY/SECURITY). Deliberately cosmetic-only: the on-disk data folder (~/Library/Application Support/oAI/), database/backup filenames, Keychain service identifiers, and EncryptionService's key-derivation inputs are all left untouched so existing conversations, settings, and stored API keys survive the update with zero migration and no re-entering credentials. Verified live: a real signed build successfully decrypted a stored API key and loaded an existing conversation database after the bundle ID change. Also includes a small already-completed, previously uncommitted model-release-date feature (ModelInfo/OpenRouterModels/ OpenRouterProvider/ModelInfoView) that happened to share several files with this rename. Gitignored on this branch and updated on disk but not part of this commit: CLAUDE.md, RELEASE_NOTES.md, and the build*.sh scripts. --- DEVELOPMENT.md | 14 +- PRIVACY.md | 30 +-- README.md | 28 +-- SECURITY.md | 10 +- oAI.xcodeproj/project.pbxproj | 80 ++++---- oAI/Confab.entitlements | 20 ++ oAI/Info.plist | 36 ++-- oAI/Localizable.xcstrings | 90 ++++----- oAI/Models/AgentSkill.swift | 8 +- oAI/Models/Conversation.swift | 8 +- oAI/Models/EmailLog.swift | 8 +- oAI/Models/Folder.swift | 8 +- oAI/Models/HistoryEntry.swift | 8 +- oAI/Models/JarvisModels.swift | 2 +- oAI/Models/Message.swift | 8 +- oAI/Models/MockData.swift | 8 +- oAI/Models/ModelCategory.swift | 8 +- oAI/Models/ModelInfo.swift | 9 +- oAI/Models/SessionStats.swift | 8 +- oAI/Models/Settings.swift | 8 +- oAI/Models/Skill.swift | 8 +- oAI/Models/SyncModels.swift | 6 +- oAI/Models/UsageStats.swift | 8 +- oAI/Providers/AIProvider.swift | 8 +- oAI/Providers/AnthropicProvider.swift | 8 +- oAI/Providers/AppleFoundationProvider.swift | 8 +- oAI/Providers/OllamaProvider.swift | 8 +- oAI/Providers/OpenAIProvider.swift | 8 +- oAI/Providers/OpenRouterModels.swift | 10 +- oAI/Providers/OpenRouterProvider.swift | 25 +-- oAI/Providers/ProviderRegistry.swift | 8 +- .../Contents/Info.plist | 6 +- .../Resources/en.lproj/images/icon.png | Bin .../Contents/Resources/en.lproj/index.html | 78 ++++---- .../Contents/Resources/en.lproj/styles.css | 2 +- .../{oAI.help => Confab.help}/README.md | 20 +- oAI/Services/AgentSkillFilesService.swift | 8 +- oAI/Services/AnthropicOAuthService.swift | 8 +- oAI/Services/AnytypeMCPService.swift | 10 +- oAI/Services/BackupService.swift | 12 +- oAI/Services/ContactsService.swift | 8 +- oAI/Services/ContextSelectionService.swift | 8 +- oAI/Services/ConversationExportService.swift | 8 +- oAI/Services/ConversationMergeService.swift | 8 +- oAI/Services/DatabaseService.swift | 8 +- oAI/Services/DraftRecoveryService.swift | 8 +- oAI/Services/EmailHandlerService.swift | 12 +- oAI/Services/EmailLogService.swift | 10 +- oAI/Services/EmailService.swift | 10 +- oAI/Services/EmbeddingService.swift | 10 +- oAI/Services/EncryptionService.swift | 13 +- oAI/Services/EventKitService.swift | 8 +- oAI/Services/ExternalMCPClient.swift | 2 +- oAI/Services/GitSyncService.swift | 38 ++-- oAI/Services/IMAPClient.swift | 10 +- oAI/Services/JarvisService.swift | 4 +- oAI/Services/LocationMapsService.swift | 8 +- oAI/Services/MCPService.swift | 10 +- oAI/Services/PaperlessService.swift | 10 +- oAI/Services/SMTPClient.swift | 10 +- oAI/Services/SettingsService.swift | 8 +- oAI/Services/ThinkingVerbs.swift | 8 +- oAI/Services/UpdateCheckService.swift | 8 +- oAI/Services/WebSearchService.swift | 8 +- .../Extensions/Color+Extensions.swift | 36 ++-- .../Extensions/String+Extensions.swift | 8 +- .../Extensions/View+Extensions.swift | 26 +-- oAI/Utilities/Logging.swift | 12 +- oAI/Utilities/SyntaxHighlighter.swift | 8 +- oAI/ViewModels/ChatViewModel.swift | 12 +- oAI/Views/Main/ChatView.swift | 18 +- oAI/Views/Main/ContentView.swift | 10 +- oAI/Views/Main/FooterView.swift | 20 +- oAI/Views/Main/HeaderView.swift | 36 ++-- oAI/Views/Main/InputBar.swift | 38 ++-- oAI/Views/Main/MarkdownContentView.swift | 22 +-- oAI/Views/Main/MessageRow.swift | 30 +-- oAI/Views/Main/SidebarView.swift | 16 +- oAI/Views/Main/SyncStatusIndicator.swift | 8 +- oAI/Views/Screens/AboutView.swift | 10 +- oAI/Views/Screens/AgentSkillEditorSheet.swift | 8 +- oAI/Views/Screens/AgentSkillsView.swift | 8 +- oAI/Views/Screens/BashApprovalSheet.swift | 8 +- .../Screens/CombineConversationsSheet.swift | 8 +- oAI/Views/Screens/ConversationListView.swift | 12 +- oAI/Views/Screens/CreditsView.swift | 8 +- oAI/Views/Screens/EmailLogView.swift | 8 +- oAI/Views/Screens/HelpView.swift | 8 +- oAI/Views/Screens/HistoryView.swift | 12 +- oAI/Views/Screens/JarvisView.swift | 2 +- oAI/Views/Screens/ModelInfoView.swift | 11 +- oAI/Views/Screens/ModelSelectorView.swift | 8 +- .../Screens/PersonalDataApprovalSheet.swift | 8 +- oAI/Views/Screens/SettingsView.swift | 20 +- oAI/Views/Screens/SkillEditorSheet.swift | 8 +- oAI/Views/Screens/SkillsView.swift | 8 +- oAI/Views/Screens/StatsView.swift | 8 +- oAI/oAI-InfoPlist.xcstrings | 18 +- oAI/oAIApp.swift | 14 +- oAITests/AIProviderTests.swift | 2 +- oAITests/AnthropicProviderTests.swift | 2 +- oAITests/BackupServiceTests.swift | 2 +- oAITests/ChatViewModelPureLogicTests.swift | 2 +- oAITests/ContextSelectionServiceDBTests.swift | 2 +- oAITests/ContextSelectionServiceTests.swift | 2 +- oAITests/ConversationExportServiceTests.swift | 2 +- .../ConversationListViewPureLogicTests.swift | 54 ++++++ oAITests/ConversationMergeServiceTests.swift | 2 +- oAITests/DatabaseServiceTests.swift | 2 +- oAITests/DraftRecoveryServiceTests.swift | 2 +- oAITests/EmbeddingServiceTests.swift | 2 +- oAITests/GitSyncServiceTests.swift | 2 +- oAITests/GitignoreParserTests.swift | 2 +- oAITests/MCPServicePDFTests.swift | 2 +- oAITests/MessageCodableTests.swift | 2 +- oAITests/NativeTextEditorPureLogicTests.swift | 174 ++++++++++++++++++ oAITests/OllamaProviderTests.swift | 2 +- oAITests/OpenAIProviderTests.swift | 2 +- oAITests/OpenRouterModelsTests.swift | 35 +++- oAITests/OpenRouterProviderTests.swift | 2 +- oAITests/SettingsProviderTests.swift | 2 +- 121 files changed, 987 insertions(+), 692 deletions(-) create mode 100644 oAI/Confab.entitlements rename oAI/Resources/{oAI.help => Confab.help}/Contents/Info.plist (88%) rename oAI/Resources/{oAI.help => Confab.help}/Contents/Resources/en.lproj/images/icon.png (100%) rename oAI/Resources/{oAI.help => Confab.help}/Contents/Resources/en.lproj/index.html (94%) rename oAI/Resources/{oAI.help => Confab.help}/Contents/Resources/en.lproj/styles.css (99%) rename oAI/Resources/{oAI.help => Confab.help}/README.md (78%) create mode 100644 oAITests/ConversationListViewPureLogicTests.swift create mode 100644 oAITests/NativeTextEditorPureLogicTests.swift diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index d8e40f3..6c53e5b 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -1,4 +1,4 @@ -# oAI — Development Guide +# Confab — Development Guide ## Project Structure @@ -47,7 +47,7 @@ oAI/ │ └── EmailHandlerService.swift # Email AI responder │ └── Resources/ - └── oAI.help/ # macOS Help Book + └── Confab.help/ # macOS Help Book ``` ## Key Technologies @@ -76,16 +76,16 @@ oAI/ ```bash # Clean build -xcodebuild clean -scheme oAI +xcodebuild clean -scheme Confab # Debug build -xcodebuild -scheme oAI -configuration Debug +xcodebuild -scheme Confab -configuration Debug # Run tests -xcodebuild test -scheme oAI +xcodebuild test -scheme Confab ``` -**Output:** `~/Library/Developer/Xcode/DerivedData/oAI-*/Build/Products/Debug/oAI.app` +**Output:** `~/Library/Developer/Xcode/DerivedData/oAI-*/Build/Products/Debug/Confab.app` In Xcode: `⌘B` build, `⌘R` run, `⌘⇧K` clean, `⌘.` stop. @@ -96,7 +96,7 @@ XProtect 5331 flags Debug builds (bash + IMAP + file access = RAT signature matc ## Logs ``` -~/Library/Logs/oAI.log +~/Library/Logs/Confab.log ``` ## Performance Notes diff --git a/PRIVACY.md b/PRIVACY.md index 4174f33..4a4659a 100644 --- a/PRIVACY.md +++ b/PRIVACY.md @@ -2,38 +2,38 @@ **Last updated:** 2026-07-27 -oAI is a native macOS app. This document describes what data it handles, where it goes, and what control you have over it. +Confab is a native macOS app. This document describes what data it handles, where it goes, and what control you have over it. ## Summary -- oAI does not collect analytics, telemetry, crash reports, or usage data of any kind. There is no tracking SDK in the app, and the developer has no visibility into how you use it. -- Everything oAI stores — conversations, settings, command history — stays in a local SQLite database on your Mac, unless you explicitly enable a sync or backup feature. +- Confab does not collect analytics, telemetry, crash reports, or usage data of any kind. There is no tracking SDK in the app, and the developer has no visibility into how you use it. +- Everything Confab stores — conversations, settings, command history — stays in a local SQLite database on your Mac, unless you explicitly enable a sync or backup feature. - The only data that leaves your Mac is data you choose to send: messages sent to the AI provider/model you've selected, and, for optional integrations you turn on yourself (email, Anytype, Paperless-NGX, external MCP servers), whatever those specific features are configured to talk to. -- oAI is free and open source. You can read exactly what it does at **https://gitlab.pm/rune/oai-swift**. +- Confab is free and open source. You can read exactly what it does at **https://gitlab.pm/rune/oai-swift**. ## Data stored locally -oAI keeps its data in a SQLite database at `~/Library/Application Support/oAI/oai_conversations.db`: +Confab keeps its data in a SQLite database at `~/Library/Application Support/oAI/oai_conversations.db`: - Saved conversations and messages - App settings and feature toggles - Command history (last 5,000 entries, auto-pruned) - Email processing logs, if the email assistant feature is used -Log files (no message content, no credentials) are written to `~/Library/Logs/oAI.log` for troubleshooting. +Log files (no message content, no credentials) are written to `~/Library/Logs/Confab.log` for troubleshooting. -None of this is sent anywhere by oAI itself. Deleting the database file (or uninstalling the app) removes it. +None of this is sent anywhere by Confab itself. Deleting the database file (or uninstalling the app) removes it. ## API keys and credentials - Provider API keys (OpenRouter, Anthropic, OpenAI, Google) are stored in the macOS **Keychain**, not the database, and not in plaintext anywhere on disk. - A small number of other credentials that can't use Keychain directly (e.g. email account password, if you set up the email assistant) are stored **encrypted at rest** in the local database, using a key derived from your Mac's hardware identifier — this key never leaves your device and isn't transmitted anywhere. -- oAI never transmits your API keys or credentials to anyone other than the service they belong to (e.g. your OpenRouter key is only ever sent to OpenRouter's API). +- Confab never transmits your API keys or credentials to anyone other than the service they belong to (e.g. your OpenRouter key is only ever sent to OpenRouter's API). ## AI providers — where your messages actually go -oAI is a client for AI providers you choose and configure yourself: OpenRouter, Anthropic, OpenAI, Google, Ollama (self-hosted, stays local), and Apple's on-device Foundation Models (macOS 26+, never leaves your Mac). When you send a message, its content — plus whatever conversation history and system prompt context oAI includes — is sent to whichever provider and model you have selected for that conversation. +Confab is a client for AI providers you choose and configure yourself: OpenRouter, Anthropic, OpenAI, Google, Ollama (self-hosted, stays local), and Apple's on-device Foundation Models (macOS 26+, never leaves your Mac). When you send a message, its content — plus whatever conversation history and system prompt context Confab includes — is sent to whichever provider and model you have selected for that conversation. -Each provider handles that data under its own privacy policy, over which oAI has no control: +Each provider handles that data under its own privacy policy, over which Confab has no control: - OpenRouter: https://openrouter.ai/privacy - Anthropic: https://www.anthropic.com/privacy - OpenAI: https://openai.com/privacy @@ -50,14 +50,14 @@ The following are **off by default** and require you to explicitly enable them i - **Bash command execution** — lets the AI run shell commands on your Mac. Off by default; when on, can optionally require your approval before each command runs. - **MCP file access** — lets the AI read/write files in folders you explicitly allow. Includes PDF text extraction. Folder access is scoped to what you approve, nothing outside it. - **Email assistant (IMAP/SMTP)** — polls a mailbox you configure and can send AI-generated replies. Your mail server credentials and the email content it processes are stored locally as described above; email content is sent to your selected AI provider to generate a response. -- **Anytype / Paperless-NGX integrations** — connect to instances you run yourself (typically on your own network or self-hosted server). Data flows directly between oAI and your own instance. -- **External MCP servers** — you can connect any third-party MCP server of your choosing; oAI has no visibility into or control over what that server does with data passed to it. +- **Anytype / Paperless-NGX integrations** — connect to instances you run yourself (typically on your own network or self-hosted server). Data flows directly between Confab and your own instance. +- **External MCP servers** — you can connect any third-party MCP server of your choosing; Confab has no visibility into or control over what that server does with data passed to it. - **Semantic search / embeddings** — if enabled, message text is sent to your selected embedding provider (OpenAI, OpenRouter, or Google) to generate vector embeddings, which are then stored locally. - **Web search** — when online mode is enabled, your query may be sent to DuckDuckGo, Google Search, or included as `:online` context to OpenRouter, depending on configuration. ## Crash reports -oAI does not include any crash reporting or analytics SDK, and does not automatically collect or transmit crash data. If oAI crashes, macOS writes a local crash log to your own Mac (viewable in Console.app), but it is not sent to the developer automatically — Apple's automatic crash-sharing pipeline for Developer ID–distributed apps like oAI (i.e. not sold through the Mac App Store) does not route reports back to the developer. If you'd like to help fix a crash, you're welcome to attach that log when reporting an issue — see `SECURITY.md` / the contact link below for how. +Confab does not include any crash reporting or analytics SDK, and does not automatically collect or transmit crash data. If Confab crashes, macOS writes a local crash log to your own Mac (viewable in Console.app), but it is not sent to the developer automatically — Apple's automatic crash-sharing pipeline for Developer ID–distributed apps like Confab (i.e. not sold through the Mac App Store) does not route reports back to the developer. If you'd like to help fix a crash, you're welcome to attach that log when reporting an issue — see `SECURITY.md` / the contact link below for how. ## iCloud backup @@ -73,11 +73,11 @@ Since everything is local, you're always in full control: ## Children's privacy -oAI is not directed at children and does not knowingly collect data from children. +Confab is not directed at children and does not knowingly collect data from children. ## Changes to this policy -If oAI's data handling changes in a meaningful way, this document will be updated and the date at the top revised. Given the app's local-first design, we don't expect that to happen often. +If Confab's data handling changes in a meaningful way, this document will be updated and the date at the top revised. Given the app's local-first design, we don't expect that to happen often. ## Contact diff --git a/README.md b/README.md index f44bf9b..ec41ede 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# oAI +# Confab A powerful native macOS AI chat application with support for multiple providers (including on-device Apple Intelligence), MCP-powered tool access, advanced memory management, and seamless Git synchronization. -![oAI Chat Interface](Screenshots/1.png) +![Confab Chat Interface](Screenshots/1.png) ## Features @@ -16,7 +16,7 @@ A powerful native macOS AI chat application with support for multiple providers ### 💬 Core Chat Capabilities - **Streaming Responses** - Real-time token streaming for faster interactions - **Conversation Management** - Save, load, export, and search conversations, organized into folders -- **Unsaved Changes & Crash Recovery** - Standard Mac-style save prompt on New Chat/Clear/Load/Quit with unsaved messages; the in-progress conversation (and selected model) is also mirrored to disk periodically and offered back on next launch if oAI crashes or is force-quit +- **Unsaved Changes & Crash Recovery** - Standard Mac-style save prompt on New Chat/Clear/Load/Quit with unsaved messages; the in-progress conversation (and selected model) is also mirrored to disk periodically and offered back on next launch if Confab crashes or is force-quit - **Combine Conversations** - Merge 2+ saved conversations, either by chronological concatenation or AI-assisted synthesis - **File Attachments** - Support for text files, images, and PDFs - **Image Generation** - Create images with supported models (DALL-E, Flux, etc.) - renders inline in chat @@ -75,7 +75,7 @@ Seamless conversation backup and sync across devices: ![Editing an Agent Skill](Screenshots/5.png) ### 📚 Anytype Integration -Connect oAI to your local [Anytype](https://anytype.io) knowledge base: +Connect Confab to your local [Anytype](https://anytype.io) knowledge base: - **Search** - find objects by keyword across all spaces or within a specific one - **Read** - open any object and read its full markdown content - **Append** - add content to the end of an existing object without touching existing text or internal links (preferred over full update) @@ -84,7 +84,7 @@ Connect oAI to your local [Anytype](https://anytype.io) knowledge base: - All data stays on your machine (local API, no cloud) ### 🛰️ Jarvis Integration -Connect oAI to a self-hosted [Jarvis](https://jarvis.pm) agent-automation server: +Connect Confab to a self-hosted [Jarvis](https://jarvis.pm) agent-automation server: - **Agent Management** - List, create, edit, enable/disable, run, and stop agents - **Run History** - Expandable per-run output with status and timing - **Usage & Credits** - Per-agent usage stats and credits balance @@ -129,17 +129,17 @@ Automated email responses powered by AI: Download the latest release from the [Releases page](https://gitlab.pm/rune/oai-swift/releases). Two builds are available: -- **oAI-x.x.x-AppleSilicon.dmg** - for Macs with an Apple Silicon chip (M1 and later) -- **oAI-x.x.x-Universal.dmg** - runs natively on both Apple Silicon and Intel Macs +- **Confab-x.x.x-AppleSilicon.dmg** - for Macs with an Apple Silicon chip (M1 and later) +- **Confab-x.x.x-Universal.dmg** - runs natively on both Apple Silicon and Intel Macs ### Installing from DMG 1. Open the downloaded `.dmg` file -2. Drag **oAI.app** into the **Applications** folder +2. Drag **Confab.app** into the **Applications** folder 3. Eject the DMG -4. Launch oAI from Applications or Spotlight +4. Launch Confab from Applications or Spotlight -Release DMGs are signed and notarized by Apple, so oAI opens normally on first launch with no Gatekeeper warning. +Release DMGs are signed and notarized by Apple, so Confab opens normally on first launch with no Gatekeeper warning. ### Requirements - macOS 26.2 or later @@ -329,16 +329,16 @@ AI-powered email auto-responder: ## License -oAI is source-available under the **PolyForm Noncommercial License 1.0.0**. +Confab is source-available under the **PolyForm Noncommercial License 1.0.0**. -This means you are free to use, study, modify, and share oAI for any noncommercial purpose. Commercial use — including selling oAI or any part of it, standalone or bundled into another product or service — requires a separate commercial license. +This means you are free to use, study, modify, and share Confab for any noncommercial purpose. Commercial use — including selling Confab or any part of it, standalone or bundled into another product or service — requires a separate commercial license. See [LICENSE](LICENSE) for the full license text, or visit [polyformproject.org/licenses/noncommercial/1.0.0](https://polyformproject.org/licenses/noncommercial/1.0.0). For commercial licensing inquiries, contact Rune Olsen via [oai.pm](https://oai.pm). ## Privacy & Security -- [Privacy Policy](PRIVACY.md) - what oAI stores, what it sends to AI providers, and what stays local +- [Privacy Policy](PRIVACY.md) - what Confab stores, what it sends to AI providers, and what stays local - [Security Policy](SECURITY.md) - supported versions and how to report a vulnerability @@ -355,7 +355,7 @@ See [LICENSE](LICENSE) for the full license text, or visit [polyformproject.org/ ## Disclaimer -oAI can take real actions on your behalf when you enable optional features - it can run shell commands, read/write files, send emails, and create calendar events or reminders. Write actions are gated behind explicit opt-in settings and, for bash/calendar/reminders, an on-screen approval prompt before they run. Review your permission settings carefully before use. Content you send is processed by whichever AI provider and model you have selected - see [PRIVACY.md](PRIVACY.md) for details on what goes where. oAI is provided "as is" without warranty of any kind - the author accepts no responsibility for actions taken by the agent or any consequences thereof. See [LICENSE](LICENSE) for full terms. +Confab can take real actions on your behalf when you enable optional features - it can run shell commands, read/write files, send emails, and create calendar events or reminders. Write actions are gated behind explicit opt-in settings and, for bash/calendar/reminders, an on-screen approval prompt before they run. Review your permission settings carefully before use. Content you send is processed by whichever AI provider and model you have selected - see [PRIVACY.md](PRIVACY.md) for details on what goes where. Confab is provided "as is" without warranty of any kind - the author accepts no responsibility for actions taken by the agent or any consequences thereof. See [LICENSE](LICENSE) for full terms. --- diff --git a/SECURITY.md b/SECURITY.md index 4b469d4..401e4e8 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,23 +2,23 @@ ## Supported Versions -Only the latest publicly released version of oAI is supported with security fixes. Please update to the latest version before reporting an issue, and confirm it still reproduces there. +Only the latest publicly released version of Confab is supported with security fixes. Please update to the latest version before reporting an issue, and confirm it still reproduces there. ## Reporting a Vulnerability -If you discover a security vulnerability in oAI, please report it privately rather than opening a public GitHub issue. +If you discover a security vulnerability in Confab, please report it privately rather than opening a public GitHub issue. To report a security concern, use the contact form at **[https://oai.pm/#contact](https://oai.pm/#contact)**. Please include as much detail as possible: - A description of the vulnerability and its potential impact - Steps to reproduce the issue -- The oAI version and macOS version you're using -- Any relevant logs (`~/Library/Logs/oAI.log`), with sensitive data redacted +- The Confab version and macOS version you're using +- Any relevant logs (`~/Library/Logs/Confab.log`), with sensitive data redacted ## Scope -oAI is a native macOS app that stores conversations, settings, and API keys locally (SQLite database and Keychain). Areas of particular interest for security reports include: +Confab is a native macOS app that stores conversations, settings, and API keys locally (SQLite database and Keychain). Areas of particular interest for security reports include: - API key handling and Keychain storage - MCP file access permission checks - Bash execution approval flow diff --git a/oAI.xcodeproj/project.pbxproj b/oAI.xcodeproj/project.pbxproj index e5fe0a3..1e2c139 100644 --- a/oAI.xcodeproj/project.pbxproj +++ b/oAI.xcodeproj/project.pbxproj @@ -22,17 +22,17 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - A550A6622F3B72EA00136F2B /* oAI.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = oAI.app; sourceTree = BUILT_PRODUCTS_DIR; }; - A586FF5130122589002CFF95 /* oAITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = oAITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + A550A6622F3B72EA00136F2B /* Confab.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Confab.app; sourceTree = BUILT_PRODUCTS_DIR; }; + A586FF5130122589002CFF95 /* ConfabTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ConfabTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ - 911C4D0E69E11B84C61453DC /* Exceptions for "oAI" folder in "oAI" target */ = { + 911C4D0E69E11B84C61453DC /* Exceptions for "oAI" folder in "Confab" target */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( Info.plist, ); - target = A550A6612F3B72EA00136F2B /* oAI */; + target = A550A6612F3B72EA00136F2B /* Confab */; }; /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ @@ -40,7 +40,7 @@ A550A6642F3B72EA00136F2B /* oAI */ = { isa = PBXFileSystemSynchronizedRootGroup; exceptions = ( - 911C4D0E69E11B84C61453DC /* Exceptions for "oAI" folder in "oAI" target */, + 911C4D0E69E11B84C61453DC /* Exceptions for "oAI" folder in "Confab" target */, ); path = oAI; sourceTree = ""; @@ -84,8 +84,8 @@ A550A6632F3B72EA00136F2B /* Products */ = { isa = PBXGroup; children = ( - A550A6622F3B72EA00136F2B /* oAI.app */, - A586FF5130122589002CFF95 /* oAITests.xctest */, + A550A6622F3B72EA00136F2B /* Confab.app */, + A586FF5130122589002CFF95 /* ConfabTests.xctest */, ); name = Products; sourceTree = ""; @@ -93,9 +93,9 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - A550A6612F3B72EA00136F2B /* oAI */ = { + A550A6612F3B72EA00136F2B /* Confab */ = { isa = PBXNativeTarget; - buildConfigurationList = A550A66D2F3B72EC00136F2B /* Build configuration list for PBXNativeTarget "oAI" */; + buildConfigurationList = A550A66D2F3B72EC00136F2B /* Build configuration list for PBXNativeTarget "Confab" */; buildPhases = ( A550A65E2F3B72EA00136F2B /* Sources */, A550A65F2F3B72EA00136F2B /* Frameworks */, @@ -108,18 +108,18 @@ fileSystemSynchronizedGroups = ( A550A6642F3B72EA00136F2B /* oAI */, ); - name = oAI; + name = Confab; packageProductDependencies = ( A550A6812F3B730000136F2B /* GRDB */, A52B47512F3E45BA004200E2 /* MarkdownUI */, ); - productName = oAI; - productReference = A550A6622F3B72EA00136F2B /* oAI.app */; + productName = Confab; + productReference = A550A6622F3B72EA00136F2B /* Confab.app */; productType = "com.apple.product-type.application"; }; - A586FF5030122589002CFF95 /* oAITests */ = { + A586FF5030122589002CFF95 /* ConfabTests */ = { isa = PBXNativeTarget; - buildConfigurationList = A586FF5930122589002CFF95 /* Build configuration list for PBXNativeTarget "oAITests" */; + buildConfigurationList = A586FF5930122589002CFF95 /* Build configuration list for PBXNativeTarget "ConfabTests" */; buildPhases = ( A586FF4D30122589002CFF95 /* Sources */, A586FF4E30122589002CFF95 /* Frameworks */, @@ -133,11 +133,11 @@ fileSystemSynchronizedGroups = ( A586FF5230122589002CFF95 /* oAITests */, ); - name = oAITests; + name = ConfabTests; packageProductDependencies = ( ); - productName = oAITests; - productReference = A586FF5130122589002CFF95 /* oAITests.xctest */; + productName = ConfabTests; + productReference = A586FF5130122589002CFF95 /* ConfabTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; /* End PBXNativeTarget section */ @@ -182,8 +182,8 @@ projectDirPath = ""; projectRoot = ""; targets = ( - A550A6612F3B72EA00136F2B /* oAI */, - A586FF5030122589002CFF95 /* oAITests */, + A550A6612F3B72EA00136F2B /* Confab */, + A586FF5030122589002CFF95 /* ConfabTests */, ); }; /* End PBXProject section */ @@ -225,7 +225,7 @@ /* Begin PBXTargetDependency section */ A586FF5630122589002CFF95 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = A550A6612F3B72EA00136F2B /* oAI */; + target = A550A6612F3B72EA00136F2B /* Confab */; targetProxy = A586FF5530122589002CFF95 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ @@ -358,7 +358,8 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_ENTITLEMENTS = oAI/oAI.entitlements; + CODE_SIGN_ENTITLEMENTS = oAI/Confab.entitlements; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = YES; @@ -367,11 +368,12 @@ ENABLE_USER_SELECTED_FILES = readonly; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = oAI/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = Confab; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; - INFOPLIST_KEY_NSCalendarsFullAccessUsageDescription = "oAI can read and create calendar events when you ask it to, if you enable Calendar access in Settings."; - INFOPLIST_KEY_NSContactsUsageDescription = "oAI can search your contacts when you ask it to, if you enable Contacts access in Settings."; - INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "oAI can use your current location to answer questions, if you enable Location & Maps access in Settings."; - INFOPLIST_KEY_NSRemindersFullAccessUsageDescription = "oAI can read and create reminders when you ask it to, if you enable Reminders access in Settings."; + INFOPLIST_KEY_NSCalendarsFullAccessUsageDescription = "Confab can read and create calendar events when you ask it to, if you enable Calendar access in Settings."; + INFOPLIST_KEY_NSContactsUsageDescription = "Confab can search your contacts when you ask it to, if you enable Contacts access in Settings."; + INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Confab can use your current location to answer questions, if you enable Location & Maps access in Settings."; + INFOPLIST_KEY_NSRemindersFullAccessUsageDescription = "Confab can read and create reminders when you ask it to, if you enable Reminders access in Settings."; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; @@ -387,7 +389,7 @@ "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 26.2; MARKETING_VERSION = 2.4.4; - PRODUCT_BUNDLE_IDENTIFIER = com.oai.oAI; + PRODUCT_BUNDLE_IDENTIFIER = com.oai.Confab; PRODUCT_NAME = "$(TARGET_NAME)"; REGISTER_APP_GROUPS = YES; SDKROOT = auto; @@ -408,7 +410,8 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_ENTITLEMENTS = oAI/oAI.entitlements; + CODE_SIGN_ENTITLEMENTS = oAI/Confab.entitlements; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = YES; @@ -417,11 +420,12 @@ ENABLE_USER_SELECTED_FILES = readonly; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = oAI/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = Confab; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; - INFOPLIST_KEY_NSCalendarsFullAccessUsageDescription = "oAI can read and create calendar events when you ask it to, if you enable Calendar access in Settings."; - INFOPLIST_KEY_NSContactsUsageDescription = "oAI can search your contacts when you ask it to, if you enable Contacts access in Settings."; - INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "oAI can use your current location to answer questions, if you enable Location & Maps access in Settings."; - INFOPLIST_KEY_NSRemindersFullAccessUsageDescription = "oAI can read and create reminders when you ask it to, if you enable Reminders access in Settings."; + INFOPLIST_KEY_NSCalendarsFullAccessUsageDescription = "Confab can read and create calendar events when you ask it to, if you enable Calendar access in Settings."; + INFOPLIST_KEY_NSContactsUsageDescription = "Confab can search your contacts when you ask it to, if you enable Contacts access in Settings."; + INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Confab can use your current location to answer questions, if you enable Location & Maps access in Settings."; + INFOPLIST_KEY_NSRemindersFullAccessUsageDescription = "Confab can read and create reminders when you ask it to, if you enable Reminders access in Settings."; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; @@ -437,7 +441,7 @@ "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 26.2; MARKETING_VERSION = 2.4.4; - PRODUCT_BUNDLE_IDENTIFIER = com.oai.oAI; + PRODUCT_BUNDLE_IDENTIFIER = com.oai.Confab; PRODUCT_NAME = "$(TARGET_NAME)"; REGISTER_APP_GROUPS = YES; SDKROOT = auto; @@ -463,7 +467,7 @@ GENERATE_INFOPLIST_FILE = YES; MACOSX_DEPLOYMENT_TARGET = 27.0; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.oai.oAITests; + PRODUCT_BUNDLE_IDENTIFIER = com.oai.ConfabTests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; STRING_CATALOG_GENERATE_SYMBOLS = NO; @@ -471,7 +475,7 @@ SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/oAI.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/oAI"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Confab.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Confab"; }; name = Debug; }; @@ -485,7 +489,7 @@ GENERATE_INFOPLIST_FILE = YES; MACOSX_DEPLOYMENT_TARGET = 27.0; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.oai.oAITests; + PRODUCT_BUNDLE_IDENTIFIER = com.oai.ConfabTests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; STRING_CATALOG_GENERATE_SYMBOLS = NO; @@ -493,7 +497,7 @@ SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/oAI.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/oAI"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Confab.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Confab"; }; name = Release; }; @@ -509,7 +513,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - A550A66D2F3B72EC00136F2B /* Build configuration list for PBXNativeTarget "oAI" */ = { + A550A66D2F3B72EC00136F2B /* Build configuration list for PBXNativeTarget "Confab" */ = { isa = XCConfigurationList; buildConfigurations = ( A550A66E2F3B72EC00136F2B /* Debug */, @@ -518,7 +522,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - A586FF5930122589002CFF95 /* Build configuration list for PBXNativeTarget "oAITests" */ = { + A586FF5930122589002CFF95 /* Build configuration list for PBXNativeTarget "ConfabTests" */ = { isa = XCConfigurationList; buildConfigurations = ( A586FF5730122589002CFF95 /* Debug */, diff --git a/oAI/Confab.entitlements b/oAI/Confab.entitlements new file mode 100644 index 0000000..a86f180 --- /dev/null +++ b/oAI/Confab.entitlements @@ -0,0 +1,20 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.network.client + + com.apple.security.network.server + + com.apple.security.personal-information.calendars + + com.apple.security.personal-information.reminders + + com.apple.security.personal-information.addressbook + + com.apple.security.personal-information.location + + + diff --git a/oAI/Info.plist b/oAI/Info.plist index 5d5967f..4de5a36 100644 --- a/oAI/Info.plist +++ b/oAI/Info.plist @@ -1,18 +1,18 @@ - - - - - CFBundleHelpBookFolder - oAI.help - CFBundleHelpBookName - oAI Help - CFBundleLocalizations - - en - nb - da - de - sv - - - + + + + + CFBundleHelpBookFolder + Confab.help + CFBundleHelpBookName + Confab Help + CFBundleLocalizations + + en + nb + da + de + sv + + + diff --git a/oAI/Localizable.xcstrings b/oAI/Localizable.xcstrings index 6dabd62..eaccd3e 100644 --- a/oAI/Localizable.xcstrings +++ b/oAI/Localizable.xcstrings @@ -523,36 +523,36 @@ } } }, - "• No credentials needed in oAI" : { + "• No credentials needed in Confab" : { "localizations" : { "da" : { "stringUnit" : { "state" : "translated", - "value" : "• Ingen legitimationsoplysninger nødvendige i oAI" + "value" : "• Ingen legitimationsoplysninger nødvendige i Confab" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "• Keine Zugangsdaten in oAI erforderlich" + "value" : "• Keine Zugangsdaten in Confab erforderlich" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "• Aucun identifiant requis dans oAI" + "value" : "• Aucun identifiant requis dans Confab" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "• Ingen legitimasjon nødvendig i oAI" + "value" : "• Ingen legitimasjon nødvendig i Confab" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "• Inga inloggningsuppgifter behövs i oAI" + "value" : "• Inga inloggningsuppgifter behövs i Confab" } } } @@ -1878,36 +1878,36 @@ } } }, - "About oAI" : { + "About Confab" : { "localizations" : { "da" : { "stringUnit" : { "state" : "translated", - "value" : "Om oAI" + "value" : "Om Confab" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Über oAI" + "value" : "Über Confab" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "À propos d'oAI" + "value" : "À propos de Confab" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Om oAI" + "value" : "Om Confab" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Om oAI" + "value" : "Om Confab" } } } @@ -4666,36 +4666,36 @@ } } }, - "Controls which messages are written to ~/Library/Logs/oAI.log" : { + "Controls which messages are written to ~/Library/Logs/Confab.log" : { "localizations" : { "da" : { "stringUnit" : { "state" : "translated", - "value" : "Styrer hvilke beskeder der skrives til ~/Library/Logs/oAI.log" + "value" : "Styrer hvilke beskeder der skrives til ~/Library/Logs/Confab.log" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Steuert, welche Nachrichten in ~/Library/Logs/oAI.log geschrieben werden" + "value" : "Steuert, welche Nachrichten in ~/Library/Logs/Confab.log geschrieben werden" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Contrôle quels messages sont écrits dans ~/Library/Logs/oAI.log" + "value" : "Contrôle quels messages sont écrits dans ~/Library/Logs/Confab.log" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Styrer hvilke meldinger som skrives til ~/Library/Logs/oAI.log" + "value" : "Styrer hvilke meldinger som skrives til ~/Library/Logs/Confab.log" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Styr vilka meddelanden som skrivs till ~/Library/Logs/oAI.log" + "value" : "Styr vilka meddelanden som skrivs till ~/Library/Logs/Confab.log" } } } @@ -6795,36 +6795,36 @@ } } }, - "Example: oai-bot-x7k2m9p3@gmail.com" : { + "Example: confab-bot-x7k2m9p3@gmail.com" : { "localizations" : { "da" : { "stringUnit" : { "state" : "translated", - "value" : "Example: oai-bot-x7k2m9p3@gmail.com" + "value" : "Example: confab-bot-x7k2m9p3@gmail.com" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Example: oai-bot-x7k2m9p3@gmail.com" + "value" : "Example: confab-bot-x7k2m9p3@gmail.com" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Exemple : oai-bot-x7k2m9p3@gmail.com" + "value" : "Exemple : confab-bot-x7k2m9p3@gmail.com" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Example: oai-bot-x7k2m9p3@gmail.com" + "value" : "Example: confab-bot-x7k2m9p3@gmail.com" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Example: oai-bot-x7k2m9p3@gmail.com" + "value" : "Example: confab-bot-x7k2m9p3@gmail.com" } } } @@ -9805,7 +9805,7 @@ } }, "Multi-provider AI chat client" : { - "comment" : "A description of oAI.", + "comment" : "A description of Confab.", "isCommentAutoGenerated" : true, "localizations" : { "da" : { @@ -10576,108 +10576,108 @@ } } }, - "oAI" : { + "Confab" : { "comment" : "The name of the app.", "isCommentAutoGenerated" : true, "localizations" : { "da" : { "stringUnit" : { "state" : "translated", - "value" : "oAI" + "value" : "Confab" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "oAI" + "value" : "Confab" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "oAI" + "value" : "Confab" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "oAI" + "value" : "Confab" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "oAI" + "value" : "Confab" } } } }, - "oAI Help" : { + "Confab Help" : { "localizations" : { "da" : { "stringUnit" : { "state" : "translated", - "value" : "oAI-hjælp" + "value" : "Confab-hjælp" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "oAI-Hilfe" + "value" : "Confab-Hilfe" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Aide oAI" + "value" : "Aide Confab" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "oAI-hjelp" + "value" : "Confab-hjelp" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "oAI-hjälp" + "value" : "Confab-hjälp" } } } }, - "oAI v2.4 is the last version to support Intel Macs and Rosetta. Starting with macOS 28, oAI will require Apple Silicon. Consider upgrading your Mac to continue receiving updates." : { + "Confab (formerly oAI) v2.4 was the last version to support Intel Macs and Rosetta. Starting with macOS 28, Confab will require Apple Silicon. Consider upgrading your Mac to continue receiving updates." : { "comment" : "A warning that Intel Macs are no longer supported.", "isCommentAutoGenerated" : true, "localizations" : { "da" : { "stringUnit" : { "state" : "translated", - "value" : "oAI v2.4 er den sidste version, der understøtter Intel-Mac og Rosetta. Fra macOS 28 vil oAI kræve Apple Silicon. Overvej at opgradere din Mac for at fortsætte med at få opdateringer." + "value" : "Confab (tidligere oAI) v2.4 var den sidste version, der understøttede Intel-Mac og Rosetta. Fra macOS 28 vil Confab kræve Apple Silicon. Overvej at opgradere din Mac for at fortsætte med at få opdateringer." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "oAI v2.4 ist die letzte Version, die Intel-Macs und Rosetta unterstützt. Ab macOS 28 benötigt oAI Apple Silicon. Erwäge ein Upgrade deines Mac, um weiterhin Updates zu erhalten." + "value" : "Confab (früher oAI) v2.4 war die letzte Version, die Intel-Macs und Rosetta unterstützte. Ab macOS 28 benötigt Confab Apple Silicon. Erwäge ein Upgrade deines Mac, um weiterhin Updates zu erhalten." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "oAI v2.4 est la dernière version à prendre en charge les Mac Intel et Rosetta. À partir de macOS 28, oAI nécessitera Apple Silicon. Envisage de mettre à niveau ton Mac pour continuer à recevoir des mises à jour." + "value" : "Confab (anciennement oAI) v2.4 était la dernière version à prendre en charge les Mac Intel et Rosetta. À partir de macOS 28, Confab nécessitera Apple Silicon. Envisage de mettre à niveau ton Mac pour continuer à recevoir des mises à jour." } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "oAI v2.4 er den siste versjonen som støtter Intel-Mac og Rosetta. Fra macOS 28 vil oAI kreve Apple Silicon. Vurder å oppgradere Mac-en din for å fortsette å få oppdateringer." + "value" : "Confab (tidligere oAI) v2.4 var den siste versjonen som støttet Intel-Mac og Rosetta. Fra macOS 28 vil Confab kreve Apple Silicon. Vurder å oppgradere Mac-en din for å fortsette å få oppdateringer." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "oAI v2.4 är den sista versionen som stöder Intel-Mac och Rosetta. Från macOS 28 kommer oAI att kräva Apple Silicon. Överväg att uppgradera din Mac för att fortsätta få uppdateringar." + "value" : "Confab (tidigare oAI) v2.4 var den sista versionen som stödde Intel-Mac och Rosetta. Från macOS 28 kommer Confab att kräva Apple Silicon. Överväg att uppgradera din Mac för att fortsätta få uppdateringar." } } } @@ -15616,7 +15616,7 @@ } }, "Update Available%@" : { - "comment" : "A button that opens a website with information about a new version of oAI. The argument is the version number of the new version.", + "comment" : "A button that opens a website with information about a new version of Confab. The argument is the version number of the new version.", "isCommentAutoGenerated" : true, "localizations" : { "da" : { @@ -15998,7 +15998,7 @@ } }, "v%@" : { - "comment" : "A label showing the current version of oAI.", + "comment" : "A label showing the current version of Confab.", "extractionState" : "stale", "isCommentAutoGenerated" : true, "localizations" : { diff --git a/oAI/Models/AgentSkill.swift b/oAI/Models/AgentSkill.swift index 3143c65..8ee8f08 100644 --- a/oAI/Models/AgentSkill.swift +++ b/oAI/Models/AgentSkill.swift @@ -1,17 +1,17 @@ // // AgentSkill.swift -// oAI +// Confab // // SKILL.md-style behavioral skills — markdown instruction files injected into the system prompt // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Models/Conversation.swift b/oAI/Models/Conversation.swift index cddee0f..3b56669 100644 --- a/oAI/Models/Conversation.swift +++ b/oAI/Models/Conversation.swift @@ -1,17 +1,17 @@ // // Conversation.swift -// oAI +// Confab // // Model for saved conversations // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Models/EmailLog.swift b/oAI/Models/EmailLog.swift index 84f8e26..c94d87f 100644 --- a/oAI/Models/EmailLog.swift +++ b/oAI/Models/EmailLog.swift @@ -1,17 +1,17 @@ // // EmailLog.swift -// oAI +// Confab // // Email processing log entry model // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Models/Folder.swift b/oAI/Models/Folder.swift index b572d36..d3bd80c 100644 --- a/oAI/Models/Folder.swift +++ b/oAI/Models/Folder.swift @@ -1,17 +1,17 @@ // // Folder.swift -// oAI +// Confab // // Model for grouping saved conversations // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Models/HistoryEntry.swift b/oAI/Models/HistoryEntry.swift index 5034a66..83a7222 100644 --- a/oAI/Models/HistoryEntry.swift +++ b/oAI/Models/HistoryEntry.swift @@ -1,17 +1,17 @@ // // HistoryEntry.swift -// oAI +// Confab // // Command history entry model // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Models/JarvisModels.swift b/oAI/Models/JarvisModels.swift index a88b255..33fe407 100644 --- a/oAI/Models/JarvisModels.swift +++ b/oAI/Models/JarvisModels.swift @@ -1,6 +1,6 @@ // // JarvisModels.swift -// oAI +// Confab // // Data models for the Jarvis (oAI-Web) API integration. // diff --git a/oAI/Models/Message.swift b/oAI/Models/Message.swift index c7df759..6a82f5a 100644 --- a/oAI/Models/Message.swift +++ b/oAI/Models/Message.swift @@ -1,17 +1,17 @@ // // Message.swift -// oAI +// Confab // // Core message model for chat conversations // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Models/MockData.swift b/oAI/Models/MockData.swift index 34305c1..8cb6982 100644 --- a/oAI/Models/MockData.swift +++ b/oAI/Models/MockData.swift @@ -1,17 +1,17 @@ // // MockData.swift -// oAI +// Confab // // Mock data for Phase 1 testing // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Models/ModelCategory.swift b/oAI/Models/ModelCategory.swift index 307cf53..130d1b0 100644 --- a/oAI/Models/ModelCategory.swift +++ b/oAI/Models/ModelCategory.swift @@ -1,17 +1,17 @@ // // ModelCategory.swift -// oAI +// Confab // // Category tags for AI models, inferred from model name/id/description. // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Models/ModelInfo.swift b/oAI/Models/ModelInfo.swift index 1285794..ed68853 100644 --- a/oAI/Models/ModelInfo.swift +++ b/oAI/Models/ModelInfo.swift @@ -1,17 +1,17 @@ // // ModelInfo.swift -// oAI +// Confab // // Model information and capabilities // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // @@ -33,6 +33,7 @@ struct ModelInfo: Identifiable, Codable, Hashable { var architecture: Architecture? = nil var topProvider: String? = nil var categories: [ModelCategory] = [] + var releaseDate: Date? = nil struct Pricing: Codable, Hashable { let prompt: Double // per 1M tokens diff --git a/oAI/Models/SessionStats.swift b/oAI/Models/SessionStats.swift index 9bc9e7b..9781b31 100644 --- a/oAI/Models/SessionStats.swift +++ b/oAI/Models/SessionStats.swift @@ -1,17 +1,17 @@ // // SessionStats.swift -// oAI +// Confab // // Session statistics tracking // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Models/Settings.swift b/oAI/Models/Settings.swift index effe640..f0f263b 100644 --- a/oAI/Models/Settings.swift +++ b/oAI/Models/Settings.swift @@ -1,17 +1,17 @@ // // Settings.swift -// oAI +// Confab // // Application settings and configuration // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Models/Skill.swift b/oAI/Models/Skill.swift index c87a5bb..c1a85ac 100644 --- a/oAI/Models/Skill.swift +++ b/oAI/Models/Skill.swift @@ -1,17 +1,17 @@ // // Shortcut.swift -// oAI +// Confab // // User-defined slash command templates (prompt shortcuts/macros) // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Models/SyncModels.swift b/oAI/Models/SyncModels.swift index 75eaaa6..7f7ae07 100644 --- a/oAI/Models/SyncModels.swift +++ b/oAI/Models/SyncModels.swift @@ -3,11 +3,11 @@ import Foundation // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Models/UsageStats.swift b/oAI/Models/UsageStats.swift index 8a7f520..426db62 100644 --- a/oAI/Models/UsageStats.swift +++ b/oAI/Models/UsageStats.swift @@ -1,17 +1,17 @@ // // UsageStats.swift -// oAI +// Confab // // All-time usage statistics (aggregated from the messages table) // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Providers/AIProvider.swift b/oAI/Providers/AIProvider.swift index 32e0e2d..9b6ad99 100644 --- a/oAI/Providers/AIProvider.swift +++ b/oAI/Providers/AIProvider.swift @@ -1,17 +1,17 @@ // // AIProvider.swift -// oAI +// Confab // // Protocol for AI provider implementations // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Providers/AnthropicProvider.swift b/oAI/Providers/AnthropicProvider.swift index 34cb0fd..824514c 100644 --- a/oAI/Providers/AnthropicProvider.swift +++ b/oAI/Providers/AnthropicProvider.swift @@ -1,17 +1,17 @@ // // AnthropicProvider.swift -// oAI +// Confab // // Anthropic Messages API provider with SSE streaming and tool support // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Providers/AppleFoundationProvider.swift b/oAI/Providers/AppleFoundationProvider.swift index 9cdb041..07cff0c 100644 --- a/oAI/Providers/AppleFoundationProvider.swift +++ b/oAI/Providers/AppleFoundationProvider.swift @@ -1,17 +1,17 @@ // // AppleFoundationProvider.swift -// oAI +// Confab // // Apple Foundation Models provider (on-device Apple Intelligence) // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Providers/OllamaProvider.swift b/oAI/Providers/OllamaProvider.swift index 8ebcab3..8a95b0a 100644 --- a/oAI/Providers/OllamaProvider.swift +++ b/oAI/Providers/OllamaProvider.swift @@ -1,17 +1,17 @@ // // OllamaProvider.swift -// oAI +// Confab // // Ollama local AI provider with JSON-lines streaming // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Providers/OpenAIProvider.swift b/oAI/Providers/OpenAIProvider.swift index c99d647..7e5727d 100644 --- a/oAI/Providers/OpenAIProvider.swift +++ b/oAI/Providers/OpenAIProvider.swift @@ -1,17 +1,17 @@ // // OpenAIProvider.swift -// oAI +// Confab // // OpenAI API provider with SSE streaming and tool support // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Providers/OpenRouterModels.swift b/oAI/Providers/OpenRouterModels.swift index bb5f712..6d5366f 100644 --- a/oAI/Providers/OpenRouterModels.swift +++ b/oAI/Providers/OpenRouterModels.swift @@ -1,17 +1,17 @@ // // OpenRouterModels.swift -// oAI +// Confab // // OpenRouter API request and response models // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // @@ -330,6 +330,7 @@ struct OpenRouterModelsResponse: Codable { let architecture: Architecture? let supportedParameters: [String]? let outputModalities: [String]? + let created: Int? struct PricingData: Codable { let prompt: String @@ -357,6 +358,7 @@ struct OpenRouterModelsResponse: Codable { case architecture case supportedParameters = "supported_parameters" case outputModalities = "output_modalities" + case created } } } diff --git a/oAI/Providers/OpenRouterProvider.swift b/oAI/Providers/OpenRouterProvider.swift index 658937d..1d6845e 100644 --- a/oAI/Providers/OpenRouterProvider.swift +++ b/oAI/Providers/OpenRouterProvider.swift @@ -1,17 +1,17 @@ // // OpenRouterProvider.swift -// oAI +// Confab // // OpenRouter AI provider implementation with SSE streaming // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // @@ -106,6 +106,7 @@ class OpenRouterProvider: AIProvider { }, topProvider: modelData.id.components(separatedBy: "/").first ) + info.releaseDate = modelData.created.map { Date(timeIntervalSince1970: TimeInterval($0)) } info.categories = ModelCategory.infer( name: modelData.name, id: modelData.id, @@ -171,8 +172,8 @@ class OpenRouterProvider: AIProvider { urlRequest.httpMethod = "POST" urlRequest.addValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization") urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type") - urlRequest.addValue("https://github.com/yourusername/oAI", forHTTPHeaderField: "HTTP-Referer") - urlRequest.addValue("oAI-Swift", forHTTPHeaderField: "X-Title") + urlRequest.addValue("https://github.com/yourusername/Confab", forHTTPHeaderField: "HTTP-Referer") + urlRequest.addValue("Confab-Swift", forHTTPHeaderField: "X-Title") urlRequest.httpBody = try JSONSerialization.data(withJSONObject: ["model": model, "prompt": prompt]) let (data, response) = try await session.data(for: urlRequest) @@ -233,8 +234,8 @@ class OpenRouterProvider: AIProvider { urlRequest.httpMethod = "POST" urlRequest.addValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization") urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type") - urlRequest.addValue("https://github.com/yourusername/oAI", forHTTPHeaderField: "HTTP-Referer") - urlRequest.addValue("oAI-Swift", forHTTPHeaderField: "X-Title") + urlRequest.addValue("https://github.com/yourusername/Confab", forHTTPHeaderField: "HTTP-Referer") + urlRequest.addValue("Confab-Swift", forHTTPHeaderField: "X-Title") urlRequest.httpBody = try JSONEncoder().encode(apiRequest) let (data, response) = try await session.data(for: urlRequest) @@ -294,8 +295,8 @@ class OpenRouterProvider: AIProvider { urlRequest.httpMethod = "POST" urlRequest.addValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization") urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type") - urlRequest.addValue("https://github.com/yourusername/oAI", forHTTPHeaderField: "HTTP-Referer") - urlRequest.addValue("oAI-Swift", forHTTPHeaderField: "X-Title") + urlRequest.addValue("https://github.com/yourusername/Confab", forHTTPHeaderField: "HTTP-Referer") + urlRequest.addValue("Confab-Swift", forHTTPHeaderField: "X-Title") urlRequest.httpBody = try JSONSerialization.data(withJSONObject: body) let (data, response) = try await session.data(for: urlRequest) @@ -333,8 +334,8 @@ class OpenRouterProvider: AIProvider { urlRequest.addValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization") urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type") urlRequest.addValue("text/event-stream", forHTTPHeaderField: "Accept") - urlRequest.addValue("https://github.com/yourusername/oAI", forHTTPHeaderField: "HTTP-Referer") - urlRequest.addValue("oAI-Swift", forHTTPHeaderField: "X-Title") + urlRequest.addValue("https://github.com/yourusername/Confab", forHTTPHeaderField: "HTTP-Referer") + urlRequest.addValue("Confab-Swift", forHTTPHeaderField: "X-Title") urlRequest.httpBody = try JSONEncoder().encode(apiRequest) let (bytes, response) = try await session.bytes(for: urlRequest) diff --git a/oAI/Providers/ProviderRegistry.swift b/oAI/Providers/ProviderRegistry.swift index e5d9a2a..a14adf3 100644 --- a/oAI/Providers/ProviderRegistry.swift +++ b/oAI/Providers/ProviderRegistry.swift @@ -1,17 +1,17 @@ // // ProviderRegistry.swift -// oAI +// Confab // // Registry for managing multiple AI providers // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Resources/oAI.help/Contents/Info.plist b/oAI/Resources/Confab.help/Contents/Info.plist similarity index 88% rename from oAI/Resources/oAI.help/Contents/Info.plist rename to oAI/Resources/Confab.help/Contents/Info.plist index c68a78d..d3088fb 100644 --- a/oAI/Resources/oAI.help/Contents/Info.plist +++ b/oAI/Resources/Confab.help/Contents/Info.plist @@ -5,11 +5,11 @@ CFBundleDevelopmentRegion en CFBundleIdentifier - com.rune.oAI.help + com.rune.Confab.help CFBundleInfoDictionaryVersion 6.0 CFBundleName - oAI Help + Confab Help CFBundlePackageType BNDL CFBundleShortVersionString @@ -23,7 +23,7 @@ HPDBookIconPath images/icon.png HPDBookTitle - oAI Help + Confab Help HPDBookType 3 diff --git a/oAI/Resources/oAI.help/Contents/Resources/en.lproj/images/icon.png b/oAI/Resources/Confab.help/Contents/Resources/en.lproj/images/icon.png similarity index 100% rename from oAI/Resources/oAI.help/Contents/Resources/en.lproj/images/icon.png rename to oAI/Resources/Confab.help/Contents/Resources/en.lproj/images/icon.png diff --git a/oAI/Resources/oAI.help/Contents/Resources/en.lproj/index.html b/oAI/Resources/Confab.help/Contents/Resources/en.lproj/index.html similarity index 94% rename from oAI/Resources/oAI.help/Contents/Resources/en.lproj/index.html rename to oAI/Resources/Confab.help/Contents/Resources/en.lproj/index.html index 8c82dd4..6195e5c 100644 --- a/oAI/Resources/oAI.help/Contents/Resources/en.lproj/index.html +++ b/oAI/Resources/Confab.help/Contents/Resources/en.lproj/index.html @@ -3,18 +3,18 @@ - + - - - oAI Help + + + Confab Help
- oAI Icon -

oAI Help

+ Confab Icon +

Confab Help

AI Chat Assistant for macOS

@@ -56,7 +56,7 @@

Getting Started

-

oAI is a powerful AI chat assistant that connects to multiple AI providers including OpenAI, Anthropic, OpenRouter, and local models via Ollama. The app is available in English, Norwegian Bokmål, Swedish, Danish, German, and French — it follows your macOS language preference automatically.

+

Confab is a powerful AI chat assistant that connects to multiple AI providers including OpenAI, Anthropic, OpenRouter, and local models via Ollama. The app is available in English, Norwegian Bokmål, Swedish, Danish, German, and French — it follows your macOS language preference automatically.

Quick Start

@@ -75,7 +75,7 @@

AI Providers & API Keys

-

oAI supports multiple AI providers. You'll need an API key from at least one provider to use the app.

+

Confab supports multiple AI providers. You'll need an API key from at least one provider to use the app.

Supported Providers

    @@ -153,7 +153,7 @@

    Use / to move through the list, Return to select the highlighted model.

    Default Model

    -

    Set a model that oAI always opens with in Settings → General → Model Settings → Default Model. Click Choose… to pick from the full model list, or Clear to remove the default. Switching models during a chat session does not change your saved default — it only changes the current session.

    +

    Set a model that Confab always opens with in Settings → General → Model Settings → Default Model. Click Choose… to pick from the full model list, or Clear to remove the default. Switching models during a chat session does not change your saved default — it only changes the current session.

@@ -310,7 +310,7 @@

Memory & Context

-

oAI features an enhanced memory and context system with intelligent message selection, semantic search, and automatic summarization.

+

Confab features an enhanced memory and context system with intelligent message selection, semantic search, and automatic summarization.

Basic Memory Control

Control whether the AI remembers previous messages:

@@ -322,7 +322,7 @@

Smart Context Selection

-

When enabled, oAI intelligently selects which messages to send instead of sending all history. This reduces token usage by 50-80% while maintaining context quality.

+

When enabled, Confab intelligently selects which messages to send instead of sending all history. This reduces token usage by 50-80% while maintaining context quality.

How It Works

    @@ -380,7 +380,7 @@

Progressive Summarization

-

For very long conversations, oAI automatically summarizes older messages to save tokens while preserving context.

+

For very long conversations, Confab automatically summarizes older messages to save tokens while preserving context.

How It Works

    @@ -517,7 +517,7 @@

    The Save dialog includes a folder picker — choose an existing folder or pick New Folder… to create one on the spot.

    Unsaved Changes & Crash Recovery

    -

    oAI tracks unsaved changes like a standard Mac document. Starting a New Chat, clearing the chat, loading a different conversation, or quitting the app while there are unsaved messages shows the standard save prompt:

    +

    Confab tracks unsaved changes like a standard Mac document. Starting a New Chat, clearing the chat, loading a different conversation, or quitting the app while there are unsaved messages shows the standard save prompt:

    • Save — saves the conversation (prompting for a name and folder if it hasn't been saved yet), then proceeds
    • Don't Save (⌘D in the prompt) — discards the changes and proceeds
    • @@ -525,7 +525,7 @@
    - 💡 Crash Recovery: While you're chatting, oAI periodically mirrors the in-progress conversation to disk (every 10 seconds by default — adjustable or turned off in Settings → General). If oAI crashes or is force-quit, the next launch offers to restore the conversation you were working on, including the model you had selected. This mirror is invisible and separate from your saved conversations — it's cleared automatically as soon as you save, discard, or restore it. + 💡 Crash Recovery: While you're chatting, Confab periodically mirrors the in-progress conversation to disk (every 10 seconds by default — adjustable or turned off in Settings → General). If Confab crashes or is force-quit, the next launch offers to restore the conversation you were working on, including the model you had selected. This mirror is invisible and separate from your saved conversations — it's cleared automatically as soon as you save, discard, or restore it.

    Renaming Conversations

    @@ -618,7 +618,7 @@
  • Click Clone Repository to initialize
  • -

    Saving to Git is explicit rather than automatic — see Unsaved Changes & Crash Recovery for how oAI tracks and prompts you to save. Every explicit save (⌘S, Save Chat As…, or the unsaved-changes prompt) triggers a background sync (export + commit + push) automatically when Git Sync is configured.

    +

    Saving to Git is explicit rather than automatic — see Unsaved Changes & Crash Recovery for how Confab tracks and prompts you to save. Every explicit save (⌘S, Save Chat As…, or the unsaved-changes prompt) triggers a background sync (export + commit + push) automatically when Git Sync is configured.

    ⚠️ Multi-Machine Warning: Syncing on multiple machines simultaneously can cause merge conflicts. Pull before you start working on a different machine. @@ -645,7 +645,7 @@

    Sync Status Indicators

    -

    oAI shows sync status in two places:

    +

    Confab shows sync status in two places:

    Header Indicator (Green/Orange/Red Pill)

      @@ -713,7 +713,7 @@ The weather is sunny today!

      Restoring on a New Machine

      To restore your conversations on a new Mac:

        -
      1. Install oAI on the new machine
      2. +
      3. Install Confab on the new machine
      4. Open Settings → Sync tab
      5. Enter your repository URL and credentials
      6. Click Clone Repository
      7. @@ -726,9 +726,9 @@ The weather is sunny today!

        SSH Key (Recommended)

        Most secure option. Generate an SSH key and add it to your Git service:

          -
        1. Generate key: ssh-keygen -t ed25519 -C "oai@yourmac"
        2. +
        3. Generate key: ssh-keygen -t ed25519 -C "confab@yourmac"
        4. Add to git service (GitHub Settings → SSH Keys)
        5. -
        6. Use SSH URL in oAI: git@github.com:username/repo.git
        7. +
        8. Use SSH URL in Confab: git@github.com:username/repo.git

        Access Token

        @@ -748,7 +748,7 @@ The weather is sunny today!
      8. Enable Auto-Sync on One Machine - Avoid conflicts by syncing automatically on your primary Mac only
      9. Manual Sync on Others - Use Pull/Push buttons on secondary machines
      10. Regular Backups - Git history preserves all versions of your conversations
      11. -
      12. Don't Edit Manually - The README warns against manual edits; always use oAI's sync features
      13. +
      14. Don't Edit Manually - The README warns against manual edits; always use Confab's sync features

    Troubleshooting

    @@ -776,7 +776,7 @@ The weather is sunny today!

    Email Handler (AI Assistant)

    -

    Turn oAI into an AI-powered email auto-responder. Monitor an inbox and automatically reply to emails with intelligent, context-aware responses.

    +

    Turn Confab into an AI-powered email auto-responder. Monitor an inbox and automatically reply to emails with intelligent, context-aware responses.

    💡 Use Cases: Customer support automation, personal assistant emails, automated FAQ responses, email-based task management. @@ -784,7 +784,7 @@ The weather is sunny today!

    How It Works

      -
    1. IMAP Monitoring - oAI polls your inbox every 30 seconds for new emails
    2. +
    3. IMAP Monitoring - Confab polls your inbox every 30 seconds for new emails
    4. Subject Filter - Only emails with your identifier (e.g., [Jarvis]) are processed
    5. AI Processing - Email content is sent to your configured AI model
    6. Auto-Reply - AI-generated response is sent via SMTP
    7. @@ -812,7 +812,7 @@ The weather is sunny today!
    8. Click Test Connection to verify settings
    9. Set Subject Identifier (e.g., [Jarvis])
    10. Select AI Provider and Model for responses
    11. -
    12. Save settings and restart oAI
    13. +
    14. Save settings and restart Confab
    @@ -927,8 +927,8 @@ The weather is sunny today!
  • Verify subject identifier matches exactly (case-sensitive)
  • Check email is in INBOX (not Spam/Junk)
  • Ensure email handler is enabled in Settings
  • -
  • Restart oAI to reinitialize monitoring
  • -
  • Check logs: ~/Library/Logs/oAI.log
  • +
  • Restart Confab to reinitialize monitoring
  • +
  • Check logs: ~/Library/Logs/Confab.log

Connection Errors

@@ -943,7 +943,7 @@ The weather is sunny today!

SMTP TLS Errors

  • Use port 465 (direct TLS) instead of 587 (STARTTLS)
  • -
  • Port 465 is more reliable with oAI's implementation
  • +
  • Port 465 is more reliable with Confab's implementation
  • If only 587 available, contact support
@@ -959,7 +959,7 @@ The weather is sunny today!
  • Check Email Log for duplicate entries
  • Emails should be marked as read after processing
  • -
  • Restart oAI if duplicates persist
  • +
  • Restart Confab if duplicates persist

Best Practices

@@ -1290,7 +1290,7 @@ Whenever the user asks you to translate something, translate it to Norwegian Bok

iCloud Backup

-

Back up and restore all your oAI settings with one click. Backups are saved to iCloud Drive so they're available on any Mac where you're signed in.

+

Back up and restore all your Confab settings with one click. Backups are saved to iCloud Drive so they're available on any Mac where you're signed in.

What is included: All settings and preferences — providers, model defaults, MCP configuration, appearance, advanced options, shortcuts, skills, and more.

@@ -1315,7 +1315,7 @@ Whenever the user asks you to translate something, translate it to Norwegian Bok
- 💡 New Mac Setup: Back up on your old Mac, sign in to iCloud on your new Mac, open oAI, restore from the backup file — and all your settings are restored in seconds. You only need to re-enter API keys. + 💡 New Mac Setup: Back up on your old Mac, sign in to iCloud on your new Mac, open Confab, restore from the backup file — and all your settings are restored in seconds. You only need to re-enter API keys.

Backup Format

@@ -1325,7 +1325,7 @@ Whenever the user asks you to translate something, translate it to Norwegian Bok

Reasoning / Thinking Tokens

-

Some AI models can "think out loud" before giving their final answer — reasoning through the problem step by step. oAI streams this thinking content live and displays it in a collapsible block above the response.

+

Some AI models can "think out loud" before giving their final answer — reasoning through the problem step by step. Confab streams this thinking content live and displays it in a collapsible block above the response.

Supported Models

Reasoning is available on models that support extended thinking, including:

@@ -1382,7 +1382,7 @@ Whenever the user asks you to translate something, translate it to Norwegian Bok

Anytype Integration

-

oAI can connect to your local Anytype desktop app, giving the AI read and write access to your personal knowledge base. All data stays on your machine — the API is local-only.

+

Confab can connect to your local Anytype desktop app, giving the AI read and write access to your personal knowledge base. All data stays on your machine — the API is local-only.

Requirements

    @@ -1392,7 +1392,7 @@ Whenever the user asks you to translate something, translate it to Norwegian Bok

    Setup

      -
    1. Open oAI Settings → Anytype tab
    2. +
    3. Open Confab Settings → Anytype tab
    4. Enable the toggle
    5. Enter your API key (leave the URL as the default unless your setup is unusual)
    6. Click Test Connection — a success message will show how many spaces were found
    7. @@ -1450,7 +1450,7 @@ Whenever the user asks you to translate something, translate it to Norwegian Bok

      Toggle a server off/on or delete it entirely with the trash icon. Crashed servers automatically restart up to 3 times with increasing delay (5s, 15s, 30s) before giving up.

      - Note: Tool names from every external server are prefixed with that server's slug (derived from its Name) so they never collide with oAI's built-in tools or each other. + Note: Tool names from every external server are prefixed with that server's slug (derived from its Name) so they never collide with Confab's built-in tools or each other.
@@ -1578,7 +1578,7 @@ Whenever the user asks you to translate something, translate it to Norwegian Bok

Settings

-

Customize oAI to your preferences. Press ⌘, to open Settings.

+

Customize Confab to your preferences. Press ⌘, to open Settings.

General Tab

    @@ -1669,7 +1669,7 @@ Whenever the user asks you to translate something, translate it to Norwegian Bok

Paperless Tab Beta

-

Connect oAI to a self-hosted Paperless-NGX instance so the AI can search and read your document archive.

+

Connect Confab to a self-hosted Paperless-NGX instance so the AI can search and read your document archive.

  • URL — Base URL of your Paperless instance (e.g. https://paperless.yourdomain.com)
  • API Token — Found in Paperless → Settings → API Tokens
  • @@ -1710,7 +1710,7 @@ Whenever the user asks you to translate something, translate it to Norwegian Bok

Anytype Tab

-

Connect oAI to your local Anytype desktop app so the AI can search, read, and add content to your knowledge base.

+

Connect Confab to your local Anytype desktop app so the AI can search, read, and add content to your knowledge base.

  • Enable Anytype — toggle to activate the integration
  • API URL — local Anytype API endpoint (default: http://127.0.0.1:31009)
  • @@ -1751,7 +1751,7 @@ Whenever the user asks you to translate something, translate it to Norwegian Bok

Default System Prompt

-

oAI includes a carefully crafted default system prompt that emphasizes:

+

Confab includes a carefully crafted default system prompt that emphasizes:

  • Accuracy First - Never invent information or make assumptions
  • Ask for Clarification - Request details when requests are ambiguous
  • @@ -1773,7 +1773,7 @@ Whenever the user asks you to translate something, translate it to Norwegian Bok

How Prompts Are Combined

-

When you send a message, oAI constructs the complete system prompt like this:

+

When you send a message, Confab constructs the complete system prompt like this:

Complete System Prompt =

@@ -1812,7 +1812,7 @@ Whenever the user asks you to translate something, translate it to Norwegian Bok
diff --git a/oAI/Resources/oAI.help/Contents/Resources/en.lproj/styles.css b/oAI/Resources/Confab.help/Contents/Resources/en.lproj/styles.css similarity index 99% rename from oAI/Resources/oAI.help/Contents/Resources/en.lproj/styles.css rename to oAI/Resources/Confab.help/Contents/Resources/en.lproj/styles.css index 7518e9c..c8402dc 100644 --- a/oAI/Resources/oAI.help/Contents/Resources/en.lproj/styles.css +++ b/oAI/Resources/Confab.help/Contents/Resources/en.lproj/styles.css @@ -1,4 +1,4 @@ -/* oAI Help Stylesheet - Apple Human Interface Guidelines */ +/* Confab Help Stylesheet - Apple Human Interface Guidelines */ :root { --primary-color: #007AFF; diff --git a/oAI/Resources/oAI.help/README.md b/oAI/Resources/Confab.help/README.md similarity index 78% rename from oAI/Resources/oAI.help/README.md rename to oAI/Resources/Confab.help/README.md index a599629..95608bb 100644 --- a/oAI/Resources/oAI.help/README.md +++ b/oAI/Resources/Confab.help/README.md @@ -1,31 +1,31 @@ -# oAI Help Book +# Confab Help Book -This folder contains the Apple Help Book for oAI. +This folder contains the Apple Help Book for Confab. ## Adding to Xcode Project 1. **Add the help folder to your project:** - In Xcode, right-click on the `Resources` folder in the Project Navigator - - Select "Add Files to 'oAI'..." - - Select the `oAI.help` folder + - Select "Add Files to 'Confab'..." + - Select the `Confab.help` folder - **Important:** Check "Create folder references" (NOT "Create groups") - Click "Add" 2. **Configure the help book in build settings:** - - Select your oAI target in Xcode + - Select your Confab target in Xcode - Go to the "Info" tab - - Add a new key: `CFBundleHelpBookName` with value: `oAI Help` - - Add another key: `CFBundleHelpBookFolder` with value: `oAI.help` + - Add a new key: `CFBundleHelpBookName` with value: `Confab Help` + - Add another key: `CFBundleHelpBookFolder` with value: `Confab.help` 3. **Build and test:** - Build the app (⌘B) - Run the app (⌘R) - - Press ⌘? or select Help → oAI Help to open the help + - Press ⌘? or select Help → Confab Help to open the help ## Structure ``` -oAI.help/ +Confab.help/ ├── Contents/ │ ├── Info.plist # Help book metadata │ └── Resources/ @@ -37,7 +37,7 @@ oAI.help/ ## Features -- ✅ Comprehensive coverage of all oAI features +- ✅ Comprehensive coverage of all Confab features - ✅ Searchable via macOS Help menu search - ✅ Dark mode support - ✅ Organized by topic with table of contents diff --git a/oAI/Services/AgentSkillFilesService.swift b/oAI/Services/AgentSkillFilesService.swift index 4b74fb8..4167af4 100644 --- a/oAI/Services/AgentSkillFilesService.swift +++ b/oAI/Services/AgentSkillFilesService.swift @@ -1,17 +1,17 @@ // // AgentSkillFilesService.swift -// oAI +// Confab // // Manages per-skill file directories in Application Support/oAI/skills// // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Services/AnthropicOAuthService.swift b/oAI/Services/AnthropicOAuthService.swift index fe99144..25421a4 100644 --- a/oAI/Services/AnthropicOAuthService.swift +++ b/oAI/Services/AnthropicOAuthService.swift @@ -1,17 +1,17 @@ // // AnthropicOAuthService.swift -// oAI +// Confab // // OAuth 2.0 PKCE flow for Anthropic Pro/Max subscription login // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Services/AnytypeMCPService.swift b/oAI/Services/AnytypeMCPService.swift index 488fa5a..aa7a9b9 100644 --- a/oAI/Services/AnytypeMCPService.swift +++ b/oAI/Services/AnytypeMCPService.swift @@ -1,17 +1,17 @@ // // AnytypeMCPService.swift -// oAI +// Confab // // Anytype MCP integration via local HTTP API at http://127.0.0.1:31009 // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // @@ -29,7 +29,7 @@ class AnytypeMCPService { static let shared = AnytypeMCPService() private let settings = SettingsService.shared - private let log = Logger(subsystem: "com.oai.oAI", category: "mcp") + private let log = Logger(subsystem: Log.subsystem, category: "mcp") private let apiVersion = "2025-11-08" private let timeout: TimeInterval = 10 diff --git a/oAI/Services/BackupService.swift b/oAI/Services/BackupService.swift index 534f4b1..65d4dc4 100644 --- a/oAI/Services/BackupService.swift +++ b/oAI/Services/BackupService.swift @@ -1,17 +1,17 @@ // // BackupService.swift -// oAI +// Confab // // iCloud Drive backup of non-encrypted settings (Option C, v1) // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // @@ -49,7 +49,7 @@ struct FavoritesPayload: Codable { final class BackupService { static let shared = BackupService() - private let log = Logger(subsystem: "oAI", category: "backup") + private let log = Logger(subsystem: Log.subsystem, category: "backup") /// Whether iCloud Drive is available on this machine var iCloudAvailable: Bool = false @@ -332,7 +332,7 @@ enum BackupError: LocalizedError { case .invalidFormat(let detail): return "The backup file is not valid: \(detail)" case .unsupportedVersion(let v): - return "Backup version \(v) is not supported by this version of oAI." + return "Backup version \(v) is not supported by this version of Confab." } } } diff --git a/oAI/Services/ContactsService.swift b/oAI/Services/ContactsService.swift index 1863c4e..363bc05 100644 --- a/oAI/Services/ContactsService.swift +++ b/oAI/Services/ContactsService.swift @@ -1,17 +1,17 @@ // // ContactsService.swift -// oAI +// Confab // // Read-only Contacts integration: search and "my card" lookup // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Services/ContextSelectionService.swift b/oAI/Services/ContextSelectionService.swift index 1311b85..316cd80 100644 --- a/oAI/Services/ContextSelectionService.swift +++ b/oAI/Services/ContextSelectionService.swift @@ -1,6 +1,6 @@ // // ContextSelectionService.swift -// oAI +// Confab // // Smart context selection for AI conversations // Selects relevant messages instead of sending entire history @@ -8,11 +8,11 @@ // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Services/ConversationExportService.swift b/oAI/Services/ConversationExportService.swift index 04c7b6d..ab267a8 100644 --- a/oAI/Services/ConversationExportService.swift +++ b/oAI/Services/ConversationExportService.swift @@ -1,17 +1,17 @@ // // ConversationExportService.swift -// oAI +// Confab // // Shared conversation export: Markdown, HTML, and PDF // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Services/ConversationMergeService.swift b/oAI/Services/ConversationMergeService.swift index 7015ab7..210057f 100644 --- a/oAI/Services/ConversationMergeService.swift +++ b/oAI/Services/ConversationMergeService.swift @@ -1,17 +1,17 @@ // // ConversationMergeService.swift -// oAI +// Confab // // Combine multiple saved conversations into one (simple concatenation or AI-assisted merge) // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Services/DatabaseService.swift b/oAI/Services/DatabaseService.swift index c79d665..e291cd7 100644 --- a/oAI/Services/DatabaseService.swift +++ b/oAI/Services/DatabaseService.swift @@ -1,17 +1,17 @@ // // DatabaseService.swift -// oAI +// Confab // // SQLite persistence layer for conversations using GRDB // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Services/DraftRecoveryService.swift b/oAI/Services/DraftRecoveryService.swift index f71910a..40bbbd0 100644 --- a/oAI/Services/DraftRecoveryService.swift +++ b/oAI/Services/DraftRecoveryService.swift @@ -1,6 +1,6 @@ // // DraftRecoveryService.swift -// oAI +// Confab // // Crash-recovery draft for the in-progress conversation — a lightweight, // invisible mirror of the current chat, distinct from named saved conversations. @@ -8,11 +8,11 @@ // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Services/EmailHandlerService.swift b/oAI/Services/EmailHandlerService.swift index 123bcb7..709a123 100644 --- a/oAI/Services/EmailHandlerService.swift +++ b/oAI/Services/EmailHandlerService.swift @@ -1,17 +1,17 @@ // // EmailHandlerService.swift -// oAI +// Confab // // AI-powered email auto-responder service // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // @@ -32,7 +32,7 @@ final class EmailHandlerService { private let emailService = EmailService.shared private let emailLog = EmailLogService.shared private let mcp = MCPService.shared - private let log = Logger(subsystem: "com.oai.oAI", category: "email-handler") + private let log = Logger(subsystem: Log.subsystem, category: "email-handler") // Rate limiting private var emailsProcessedThisHour: Int = 0 @@ -403,7 +403,7 @@ final class EmailHandlerService { diff --git a/oAI/Services/EmailLogService.swift b/oAI/Services/EmailLogService.swift index a4ee419..bf06ed4 100644 --- a/oAI/Services/EmailLogService.swift +++ b/oAI/Services/EmailLogService.swift @@ -1,17 +1,17 @@ // // EmailLogService.swift -// oAI +// Confab // // Service for managing email handler activity logs // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // @@ -29,7 +29,7 @@ final class EmailLogService { static let shared = EmailLogService() private let db = DatabaseService.shared - private let log = Logger(subsystem: "com.oai.oAI", category: "email-log") + private let log = Logger(subsystem: Log.subsystem, category: "email-log") private init() {} diff --git a/oAI/Services/EmailService.swift b/oAI/Services/EmailService.swift index e0beda9..4f191b0 100644 --- a/oAI/Services/EmailService.swift +++ b/oAI/Services/EmailService.swift @@ -1,17 +1,17 @@ // // EmailService.swift -// oAI +// Confab // // IMAP IDLE email monitoring service for AI email handler // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // @@ -71,7 +71,7 @@ final class EmailService { static let shared = EmailService() private let settings = SettingsService.shared - private let log = Logger(subsystem: "com.oai.oAI", category: "email") + private let log = Logger(subsystem: Log.subsystem, category: "email") // IMAP IDLE state private var isConnected = false diff --git a/oAI/Services/EmbeddingService.swift b/oAI/Services/EmbeddingService.swift index f096bc5..078de26 100644 --- a/oAI/Services/EmbeddingService.swift +++ b/oAI/Services/EmbeddingService.swift @@ -1,6 +1,6 @@ // // EmbeddingService.swift -// oAI +// Confab // // Embedding generation and semantic search // Supports multiple providers: OpenAI, OpenRouter, Google @@ -8,11 +8,11 @@ // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // @@ -205,7 +205,7 @@ final class EmbeddingService { request.httpMethod = "POST" request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization") request.setValue("application/json", forHTTPHeaderField: "Content-Type") - request.setValue("https://github.com/yourusername/oAI", forHTTPHeaderField: "HTTP-Referer") + request.setValue("https://github.com/yourusername/Confab", forHTTPHeaderField: "HTTP-Referer") let body: [String: Any] = [ "input": text, diff --git a/oAI/Services/EncryptionService.swift b/oAI/Services/EncryptionService.swift index 1a05ee9..de62c1f 100644 --- a/oAI/Services/EncryptionService.swift +++ b/oAI/Services/EncryptionService.swift @@ -1,6 +1,6 @@ // // EncryptionService.swift -// oAI +// Confab // // Secure encryption for sensitive data (API keys) // Uses CryptoKit with machine-specific key derivation @@ -8,11 +8,11 @@ // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // @@ -72,7 +72,10 @@ class EncryptionService { /// Derive encryption key from machine-specific data private static func deriveEncryptionKey() -> SymmetricKey { let machineUUID = getMachineUUID() - let bundleID = Bundle.main.bundleIdentifier ?? "com.oai.oAI" + // Pinned, not read from Bundle.main.bundleIdentifier: the app's bundle ID changed + // (oAI -> Confab rename) but this key material must not, or every already-encrypted + // setting (provider API keys, sync/email credentials) becomes undecryptable. + let bundleID = "com.oai.oAI" let salt = "oAI-secure-storage-v1" let keyMaterial = "\(machineUUID)-\(bundleID)-\(salt)" let hash = SHA256.hash(data: Data(keyMaterial.utf8)) diff --git a/oAI/Services/EventKitService.swift b/oAI/Services/EventKitService.swift index 0507d3c..235475a 100644 --- a/oAI/Services/EventKitService.swift +++ b/oAI/Services/EventKitService.swift @@ -1,17 +1,17 @@ // // EventKitService.swift -// oAI +// Confab // // Calendar and Reminders integration via EventKit // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Services/ExternalMCPClient.swift b/oAI/Services/ExternalMCPClient.swift index 1f8188b..6b85464 100644 --- a/oAI/Services/ExternalMCPClient.swift +++ b/oAI/Services/ExternalMCPClient.swift @@ -75,7 +75,7 @@ final class ExternalMCPClient { let _: MCPInitializeResult = try await timedRequest(seconds: 15, method: "initialize", params: [ "protocolVersion": "2024-11-05", "capabilities": [:] as [String: Any], - "clientInfo": ["name": "oAI", "version": "1.0"] as [String: Any] + "clientInfo": ["name": "Confab", "version": "1.0"] as [String: Any] ]) try sendNotification(method: "notifications/initialized") diff --git a/oAI/Services/GitSyncService.swift b/oAI/Services/GitSyncService.swift index fb28cde..a21db81 100644 --- a/oAI/Services/GitSyncService.swift +++ b/oAI/Services/GitSyncService.swift @@ -3,11 +3,11 @@ import Foundation // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // @@ -24,7 +24,7 @@ class GitSyncService { private let settings = SettingsService.shared private let db = DatabaseService.shared - private let log = Logger(subsystem: "com.oai.oAI", category: "sync") + private let log = Logger(subsystem: Log.subsystem, category: "sync") private(set) var syncStatus = SyncStatus() private(set) var isSyncing = false @@ -90,7 +90,7 @@ class GitSyncService { } /// Push local changes to remote - func push(message: String = "Sync from oAI") async throws { + func push(message: String = "Sync from Confab") async throws { try ensureCloned() let localPath = expandPath(settings.syncLocalPath) @@ -314,20 +314,20 @@ class GitSyncService { } let readme = """ - # oAI Conversation Sync + # Confab Conversation Sync - This repository contains your oAI conversations in markdown format. + This repository contains your Confab conversations in markdown format. ## ⚠️ WARNING - DO NOT MANUALLY EDIT - **This repository is automatically managed by oAI.** + **This repository is automatically managed by Confab.** - ❌ **DO NOT manually edit** these files - ❌ **DO NOT add** files to this repository - ❌ **DO NOT delete** files from this repository - - ❌ **DO NOT merge conflicts** manually (let oAI handle it) + - ❌ **DO NOT merge conflicts** manually (let Confab handle it) - **Why?** oAI rebuilds its internal database from these files. Manual edits will be: + **Why?** Confab rebuilds its internal database from these files. Manual edits will be: - Overwritten on next sync - May cause data corruption - May prevent proper import/restore @@ -335,13 +335,13 @@ class GitSyncService { ## How It Works ### Export (Automatic) - - oAI saves conversations to its local database + - Confab saves conversations to its local database - Auto-sync exports conversations to `conversations/*.md` - Files are committed and pushed to this git repository ### Import (On New Machine) - Clone this repository on a new machine - - oAI imports markdown files into its database + - Confab imports markdown files into its database - Your conversation history is restored ### Sync Across Machines @@ -399,28 +399,28 @@ class GitSyncService { ## Troubleshooting **Problem:** Files not syncing? - - Check Settings → Sync in oAI + - Check Settings → Sync in Confab - Verify git credentials are correct - Check network connection **Problem:** Conflicts after editing? - Restore from git: `git reset --hard origin/main` - - Re-export from oAI: Manual Sync → Export All → Push + - Re-export from Confab: Manual Sync → Export All → Push **Problem:** Lost conversations? - - Conversations are in your local oAI database + - Conversations are in your local Confab database - Export manually: Settings → Sync → Export All - Check git history for deleted files ## Support - For help with oAI, see: - - Settings → Help in oAI app + For help with Confab, see: + - Settings → Help in Confab app - GitHub issues (if open source) --- - **Generated by oAI v1.0** + **Generated by Confab v1.0** **Last updated:** \(ISO8601DateFormatter().string(from: Date())) """ @@ -504,7 +504,7 @@ class GitSyncService { try await exportAllConversations() // Push to git - try await push(message: "Auto-sync from oAI") + try await push(message: "Auto-sync from Confab") // Success await MainActor.run { diff --git a/oAI/Services/IMAPClient.swift b/oAI/Services/IMAPClient.swift index 165ee6a..5da3fea 100644 --- a/oAI/Services/IMAPClient.swift +++ b/oAI/Services/IMAPClient.swift @@ -1,17 +1,17 @@ // // IMAPClient.swift -// oAI +// Confab // // Swift-native IMAP client for email monitoring // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // @@ -26,7 +26,7 @@ import Network import os class IMAPClient { - private let log = Logger(subsystem: "com.oai.oAI", category: "imap") + private let log = Logger(subsystem: Log.subsystem, category: "imap") private var connection: NWConnection? private var host: String diff --git a/oAI/Services/JarvisService.swift b/oAI/Services/JarvisService.swift index 32f33fb..73390bc 100644 --- a/oAI/Services/JarvisService.swift +++ b/oAI/Services/JarvisService.swift @@ -1,6 +1,6 @@ // // JarvisService.swift -// oAI +// Confab // // HTTP client for the Jarvis (oAI-Web) REST API. // Auth: Authorization: Bearer @@ -15,7 +15,7 @@ final class JarvisService: Sendable { static let shared = JarvisService() private init() {} - private let log = Logger(subsystem: "com.oai.oAI", category: "jarvis") + private let log = Logger(subsystem: Log.subsystem, category: "jarvis") private var baseURL: String { SettingsService.shared.jarvisURL } private var apiKey: String? { SettingsService.shared.jarvisAPIKey } diff --git a/oAI/Services/LocationMapsService.swift b/oAI/Services/LocationMapsService.swift index e2f15bb..4e5e008 100644 --- a/oAI/Services/LocationMapsService.swift +++ b/oAI/Services/LocationMapsService.swift @@ -1,17 +1,17 @@ // // LocationMapsService.swift -// oAI +// Confab // // Read-only Location and Maps integration via CoreLocation and MapKit // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Services/MCPService.swift b/oAI/Services/MCPService.swift index 4a7ce74..21e4b0e 100644 --- a/oAI/Services/MCPService.swift +++ b/oAI/Services/MCPService.swift @@ -1,17 +1,17 @@ // // MCPService.swift -// oAI +// Confab // // MCP (Model Context Protocol) service for filesystem tool execution // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // @@ -100,7 +100,7 @@ class MCPService { // Always allow the system temp directory — external MCP servers and tools write // intermediate data there (e.g. Safari MCP page-content files, generated images). // Check both NSTemporaryDirectory() (per-user Darwin temp dir) and /tmp (what this - // codebase's own temp files actually use, e.g. ChatViewModel's /tmp/oai_generated_* + // codebase's own temp files actually use, e.g. ChatViewModel's /tmp/confab_generated_* // and ExternalMCPClient's /tmp/oai_mcp_* — they resolve to different directories. let tmpCandidates = [ (NSTemporaryDirectory() as NSString).standardizingPath, diff --git a/oAI/Services/PaperlessService.swift b/oAI/Services/PaperlessService.swift index e7470cf..fd59864 100644 --- a/oAI/Services/PaperlessService.swift +++ b/oAI/Services/PaperlessService.swift @@ -1,17 +1,17 @@ // // PaperlessService.swift -// oAI +// Confab // // Paperless-NGX integration: search, read, and upload documents via REST API // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // @@ -29,7 +29,7 @@ class PaperlessService { static let shared = PaperlessService() private let settings = SettingsService.shared - private let log = Logger(subsystem: "com.oai.oAI", category: "mcp") + private let log = Logger(subsystem: Log.subsystem, category: "mcp") private let readTimeout: TimeInterval = 15 private let uploadTimeout: TimeInterval = 60 diff --git a/oAI/Services/SMTPClient.swift b/oAI/Services/SMTPClient.swift index e4f5648..e7e2644 100644 --- a/oAI/Services/SMTPClient.swift +++ b/oAI/Services/SMTPClient.swift @@ -1,17 +1,17 @@ // // SMTPClient.swift -// oAI +// Confab // // Swift-native SMTP client for sending emails // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // @@ -26,7 +26,7 @@ import Network import os class SMTPClient { - private let log = Logger(subsystem: "com.oai.oAI", category: "smtp") + private let log = Logger(subsystem: Log.subsystem, category: "smtp") private var connection: NWConnection? private var host: String diff --git a/oAI/Services/SettingsService.swift b/oAI/Services/SettingsService.swift index cfada45..18e8158 100644 --- a/oAI/Services/SettingsService.swift +++ b/oAI/Services/SettingsService.swift @@ -1,17 +1,17 @@ // // SettingsService.swift -// oAI +// Confab // // Settings persistence: SQLite for preferences, Keychain for API keys // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Services/ThinkingVerbs.swift b/oAI/Services/ThinkingVerbs.swift index c26eb2c..1d29793 100644 --- a/oAI/Services/ThinkingVerbs.swift +++ b/oAI/Services/ThinkingVerbs.swift @@ -1,17 +1,17 @@ // // ThinkingVerbs.swift -// oAI +// Confab // // Fun random verbs for AI thinking/processing states // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Services/UpdateCheckService.swift b/oAI/Services/UpdateCheckService.swift index 56cd3d3..b7ecaef 100644 --- a/oAI/Services/UpdateCheckService.swift +++ b/oAI/Services/UpdateCheckService.swift @@ -1,17 +1,17 @@ // // UpdateCheckService.swift -// oAI +// Confab // // Checks for new releases on GitLab and surfaces an update badge in the footer // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Services/WebSearchService.swift b/oAI/Services/WebSearchService.swift index b07aff0..c90daad 100644 --- a/oAI/Services/WebSearchService.swift +++ b/oAI/Services/WebSearchService.swift @@ -1,17 +1,17 @@ // // WebSearchService.swift -// oAI +// Confab // // DuckDuckGo web search for non-OpenRouter providers // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Utilities/Extensions/Color+Extensions.swift b/oAI/Utilities/Extensions/Color+Extensions.swift index e0579d7..da65545 100644 --- a/oAI/Utilities/Extensions/Color+Extensions.swift +++ b/oAI/Utilities/Extensions/Color+Extensions.swift @@ -1,17 +1,17 @@ // // Color+Extensions.swift -// oAI +// Confab // // Color scheme matching Python TUI dark theme // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // @@ -26,30 +26,30 @@ import SwiftUI extension Color { // MARK: - oAI Color Palette (Matching Python TUI) - static let oaiBackground = Color(hex: "#1e1e1e") // Main background - static let oaiSurface = Color(hex: "#2d2d2d") // Cards, surfaces - static let oaiPrimary = Color(hex: "#cccccc") // Primary text - static let oaiSecondary = Color(hex: "#888888") // Secondary text - static let oaiAccent = Color(hex: "#0a7aca") // Blue accent (assistant) - static let oaiSuccess = Color(hex: "#90ee90") // Green (user messages) - static let oaiError = Color(hex: "#ff6b6b") // Red (errors) - static let oaiWarning = Color(hex: "#ffaa00") // Orange (warnings) - static let oaiBorder = Color(hex: "#555555") // Borders, dividers + static let confabBackground = Color(hex: "#1e1e1e") // Main background + static let confabSurface = Color(hex: "#2d2d2d") // Cards, surfaces + static let confabPrimary = Color(hex: "#cccccc") // Primary text + static let confabSecondary = Color(hex: "#888888") // Secondary text + static let confabAccent = Color(hex: "#0a7aca") // Blue accent (assistant) + static let confabSuccess = Color(hex: "#90ee90") // Green (user messages) + static let confabError = Color(hex: "#ff6b6b") // Red (errors) + static let confabWarning = Color(hex: "#ffaa00") // Orange (warnings) + static let confabBorder = Color(hex: "#555555") // Borders, dividers // MARK: - Message Role Colors static func messageColor(for role: MessageRole) -> Color { switch role { - case .user: return .oaiSuccess - case .assistant: return .oaiAccent - case .system: return .oaiSecondary + case .user: return .confabSuccess + case .assistant: return .confabAccent + case .system: return .confabSecondary } } static func messageBackground(for role: MessageRole) -> Color { switch role { - case .user: return .oaiSurface - case .assistant: return .oaiBackground + case .user: return .confabSurface + case .assistant: return .confabBackground case .system: return Color(hex: "#2a2a2a") } } diff --git a/oAI/Utilities/Extensions/String+Extensions.swift b/oAI/Utilities/Extensions/String+Extensions.swift index 9ef5256..e8b1608 100644 --- a/oAI/Utilities/Extensions/String+Extensions.swift +++ b/oAI/Utilities/Extensions/String+Extensions.swift @@ -1,17 +1,17 @@ // // String+Extensions.swift -// oAI +// Confab // // String utility extensions // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Utilities/Extensions/View+Extensions.swift b/oAI/Utilities/Extensions/View+Extensions.swift index 5cdff9f..cb3e661 100644 --- a/oAI/Utilities/Extensions/View+Extensions.swift +++ b/oAI/Utilities/Extensions/View+Extensions.swift @@ -1,17 +1,17 @@ // // View+Extensions.swift -// oAI +// Confab // // SwiftUI view helpers and modifiers // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // @@ -57,33 +57,33 @@ extension View { // MARK: - Common Styling - func oaiCardStyle() -> some View { + func confabCardStyle() -> some View { self - .background(Color.oaiSurface) + .background(Color.confabSurface) .cornerRadius(8) .overlay( RoundedRectangle(cornerRadius: 8) - .stroke(Color.oaiBorder, lineWidth: 1) + .stroke(Color.confabBorder, lineWidth: 1) ) } - func oaiButton() -> some View { + func confabButton() -> some View { self .padding(.horizontal, 12) .padding(.vertical, 6) - .background(Color.oaiSurface) - .foregroundColor(.oaiPrimary) + .background(Color.confabSurface) + .foregroundColor(.confabPrimary) .cornerRadius(6) } - func oaiTextField() -> some View { + func confabTextField() -> some View { self .padding(8) - .background(Color.oaiBackground) + .background(Color.confabBackground) .cornerRadius(6) .overlay( RoundedRectangle(cornerRadius: 6) - .stroke(Color.oaiBorder, lineWidth: 1) + .stroke(Color.confabBorder, lineWidth: 1) ) } } diff --git a/oAI/Utilities/Logging.swift b/oAI/Utilities/Logging.swift index ef8e9d0..d335237 100644 --- a/oAI/Utilities/Logging.swift +++ b/oAI/Utilities/Logging.swift @@ -1,17 +1,17 @@ // // Logging.swift -// oAI +// Confab // // Dual logging: os.Logger (unified log) + file (~Library/Logs/oAI.log) // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // @@ -82,7 +82,7 @@ final class FileLogger: @unchecked Sendable { private init() { let logsDir = FileManager.default.homeDirectoryForCurrentUser .appendingPathComponent("Library/Logs") - let logFile = logsDir.appendingPathComponent("oAI.log") + let logFile = logsDir.appendingPathComponent("Confab.log") // Ensure file exists if !FileManager.default.fileExists(atPath: logFile.path) { @@ -142,7 +142,7 @@ struct AppLogger: Sendable { // MARK: - Log Namespace enum Log { - private nonisolated static let subsystem = "com.oai.oAI" + nonisolated static let subsystem = "com.oai.Confab" nonisolated static let api = AppLogger(subsystem: subsystem, category: "api") nonisolated static let db = AppLogger(subsystem: subsystem, category: "database") diff --git a/oAI/Utilities/SyntaxHighlighter.swift b/oAI/Utilities/SyntaxHighlighter.swift index fd5871a..b8ef78a 100644 --- a/oAI/Utilities/SyntaxHighlighter.swift +++ b/oAI/Utilities/SyntaxHighlighter.swift @@ -1,17 +1,17 @@ // // SyntaxHighlighter.swift -// oAI +// Confab // // Keyword-based syntax highlighting using AttributedString // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/ViewModels/ChatViewModel.swift b/oAI/ViewModels/ChatViewModel.swift index 37f64ea..47e6a27 100644 --- a/oAI/ViewModels/ChatViewModel.swift +++ b/oAI/ViewModels/ChatViewModel.swift @@ -1,17 +1,17 @@ // // ChatViewModel.swift -// oAI +// Confab // // Main chat view model // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // @@ -1563,7 +1563,7 @@ Don't narrate future actions ("Let me...") - just use the tools. didContinueAfterImages = true let timestamp = Int(Date().timeIntervalSince1970) let tempPaths: [String] = finalImages.enumerated().compactMap { i, imgData in - let path = "/tmp/oai_generated_\(timestamp)_\(i).png" + let path = "/tmp/confab_generated_\(timestamp)_\(i).png" let ok = FileManager.default.createFile(atPath: path, contents: imgData) Log.ui.debug("Saved generated image to temp: \(path) ok=\(ok)") return ok ? path : nil @@ -2138,7 +2138,7 @@ Don't narrate future actions ("Let me...") - just use the tools. let alert = NSAlert() alert.alertStyle = .informational alert.messageText = "Restore unsaved conversation?" - alert.informativeText = "oAI didn't close properly last time. Would you like to restore the conversation you were working on?" + alert.informativeText = "Confab didn't close properly last time. Would you like to restore the conversation you were working on?" alert.addButton(withTitle: "Restore") alert.addButton(withTitle: "Discard") diff --git a/oAI/Views/Main/ChatView.swift b/oAI/Views/Main/ChatView.swift index f06e76e..0fd37a4 100644 --- a/oAI/Views/Main/ChatView.swift +++ b/oAI/Views/Main/ChatView.swift @@ -1,17 +1,17 @@ // // ChatView.swift -// oAI +// Confab // // Main chat interface // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // @@ -64,7 +64,7 @@ struct ChatView: View { } .padding() } - .background(Color.oaiBackground) + .background(Color.confabBackground) .onChange(of: viewModel.messages.count) { withAnimation { proxy.scrollTo("bottom", anchor: .bottom) @@ -101,7 +101,7 @@ struct ChatView: View { mcpEnabled: viewModel.mcpEnabled ) } - .background(Color.oaiBackground) + .background(Color.confabBackground) .sheet(isPresented: $viewModel.showShortcuts) { ShortcutsView() } @@ -142,12 +142,12 @@ struct ProcessingIndicator: View { HStack(spacing: 8) { Text(thinkingText) .font(.system(size: 14, weight: .medium)) - .foregroundColor(.oaiSecondary) + .foregroundColor(.confabSecondary) HStack(spacing: 4) { ForEach(0..<3) { index in Circle() - .fill(Color.oaiSecondary) + .fill(Color.confabSecondary) .frame(width: 6, height: 6) .scaleEffect(animating ? 1.0 : 0.5) .animation( @@ -161,7 +161,7 @@ struct ProcessingIndicator: View { } .padding(.horizontal, 16) .padding(.vertical, 12) - .background(Color.oaiSecondary.opacity(0.05)) + .background(Color.confabSecondary.opacity(0.05)) .cornerRadius(8) .onAppear { animating = true diff --git a/oAI/Views/Main/ContentView.swift b/oAI/Views/Main/ContentView.swift index b3d88d2..ff9b101 100644 --- a/oAI/Views/Main/ContentView.swift +++ b/oAI/Views/Main/ContentView.swift @@ -1,17 +1,17 @@ // // ContentView.swift -// oAI +// Confab // // Root navigation container — NavigationSplitView with collapsible sidebar // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // @@ -134,7 +134,7 @@ struct ContentView: View { UserDefaults.standard.set(true, forKey: "hasShownIntelWarning") } } message: { - Text("oAI v2.4 is the last version to support Intel Macs and Rosetta. Starting with macOS 28, oAI will require Apple Silicon. Consider upgrading your Mac to continue receiving updates.") + Text("Confab (formerly oAI) v2.4 was the last version to support Intel Macs and Rosetta. Starting with macOS 28, Confab will require Apple Silicon. Consider upgrading your Mac to continue receiving updates.") } .alert("Software Update", isPresented: Binding( get: { updateService.manualCheckMessage != nil }, diff --git a/oAI/Views/Main/FooterView.swift b/oAI/Views/Main/FooterView.swift index 819f80e..3bfc05b 100644 --- a/oAI/Views/Main/FooterView.swift +++ b/oAI/Views/Main/FooterView.swift @@ -1,17 +1,17 @@ // // FooterView.swift -// oAI +// Confab // // Footer bar with session summary // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // @@ -102,7 +102,7 @@ struct FooterView: View { .background(.ultraThinMaterial) .overlay( Rectangle() - .fill(Color.oaiBorder.opacity(0.5)) + .fill(Color.confabBorder.opacity(0.5)) .frame(height: 1), alignment: .top ) @@ -153,7 +153,7 @@ struct SaveIndicator: View { .foregroundColor(color) Text(label) .font(.system(size: guiSize - 2)) - .foregroundColor(isUnsaved ? .secondary : .oaiPrimary) + .foregroundColor(isUnsaved ? .secondary : .confabPrimary) } } .buttonStyle(.plain) @@ -175,15 +175,15 @@ struct FooterItem: View { HStack(spacing: 6) { Image(systemName: icon) .font(.system(size: guiSize - 2)) - .foregroundColor(.oaiSecondary) + .foregroundColor(.confabSecondary) Text(label) .font(.system(size: guiSize - 2)) - .foregroundColor(.oaiSecondary) + .foregroundColor(.confabSecondary) Text(value) .font(.system(size: guiSize - 2, weight: .medium)) - .foregroundColor(.oaiPrimary) + .foregroundColor(.confabPrimary) } } } @@ -279,5 +279,5 @@ struct UpdateBadge: View { FooterView(stats: stats, conversationName: "My Project", hasUnsavedChanges: true) FooterView(stats: stats, conversationName: "My Project", hasUnsavedChanges: false) } - .background(Color.oaiBackground) + .background(Color.confabBackground) } diff --git a/oAI/Views/Main/HeaderView.swift b/oAI/Views/Main/HeaderView.swift index eec05a3..3167020 100644 --- a/oAI/Views/Main/HeaderView.swift +++ b/oAI/Views/Main/HeaderView.swift @@ -1,6 +1,6 @@ // // HeaderView.swift -// oAI +// Confab // // Slim header — provider, model name, star only. // Status pills and stats live in SidebarView and FooterView respectively. @@ -8,11 +8,11 @@ // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // @@ -53,7 +53,7 @@ struct HeaderView: View { .background(.ultraThinMaterial) .overlay( Rectangle() - .fill(Color.oaiBorder.opacity(0.5)) + .fill(Color.confabBorder.opacity(0.5)) .frame(height: 1), alignment: .bottom ) @@ -73,7 +73,7 @@ struct HeaderView: View { } Text(name) .font(.system(size: settings.guiTextSize - 1, weight: .medium)) - .foregroundColor(.oaiPrimary) + .foregroundColor(.confabPrimary) .lineLimit(1) .frame(maxWidth: 300) } @@ -128,29 +128,29 @@ struct HeaderView: View { HStack(spacing: 6) { Text(model.name) .font(.system(size: settings.guiTextSize, weight: .medium)) - .foregroundColor(.oaiPrimary) + .foregroundColor(.confabPrimary) HStack(spacing: 3) { if model.capabilities.vision { - Image(systemName: "eye").font(.system(size: 9)).foregroundColor(.oaiSecondary) + Image(systemName: "eye").font(.system(size: 9)).foregroundColor(.confabSecondary) } if model.capabilities.tools { - Image(systemName: "wrench").font(.system(size: 9)).foregroundColor(.oaiSecondary) + Image(systemName: "wrench").font(.system(size: 9)).foregroundColor(.confabSecondary) } if model.capabilities.online { - Image(systemName: "globe").font(.system(size: 9)).foregroundColor(.oaiSecondary) + Image(systemName: "globe").font(.system(size: 9)).foregroundColor(.confabSecondary) } if model.capabilities.imageGeneration { - Image(systemName: "paintbrush").font(.system(size: 9)).foregroundColor(.oaiSecondary) + Image(systemName: "paintbrush").font(.system(size: 9)).foregroundColor(.confabSecondary) } } - Image(systemName: "chevron.down").font(.caption2).foregroundColor(.oaiSecondary) + Image(systemName: "chevron.down").font(.caption2).foregroundColor(.confabSecondary) } } else { HStack(spacing: 4) { Text("No model selected") .font(.system(size: settings.guiTextSize)) - .foregroundColor(.oaiSecondary) - Image(systemName: "chevron.down").font(.caption2).foregroundColor(.oaiSecondary) + .foregroundColor(.confabSecondary) + Image(systemName: "chevron.down").font(.caption2).foregroundColor(.confabSecondary) } } } @@ -165,7 +165,7 @@ struct HeaderView: View { Button(action: { settings.toggleFavoriteModel(model.id) }) { Image(systemName: isFav ? "star.fill" : "star") .font(.system(size: settings.guiTextSize - 3)) - .foregroundColor(isFav ? .yellow : .oaiSecondary) + .foregroundColor(isFav ? .yellow : .confabSecondary) } .buttonStyle(.plain) .help(isFav ? "Remove from favorites" : "Add to favorites") @@ -187,7 +187,7 @@ struct StatusPill: View { .frame(width: 6, height: 6) Text(label) .font(.system(size: 10, weight: .medium)) - .foregroundColor(.oaiSecondary) + .foregroundColor(.confabSecondary) } .padding(.horizontal, 6) .padding(.vertical, 2) @@ -230,7 +230,7 @@ struct SyncStatusPill: View { .frame(width: 6, height: 6) Text(syncLabel) .font(.system(size: 10, weight: .medium)) - .foregroundColor(.oaiSecondary) + .foregroundColor(.confabSecondary) } .padding(.horizontal, 6) .padding(.vertical, 2) @@ -265,5 +265,5 @@ struct SyncStatusPill: View { ) Spacer() } - .background(Color.oaiBackground) + .background(Color.confabBackground) } diff --git a/oAI/Views/Main/InputBar.swift b/oAI/Views/Main/InputBar.swift index 71d71af..30978e6 100644 --- a/oAI/Views/Main/InputBar.swift +++ b/oAI/Views/Main/InputBar.swift @@ -1,17 +1,17 @@ // // InputBar.swift -// oAI +// Confab // // Message input bar with resizable height and online toggle // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // @@ -86,7 +86,7 @@ struct InputBar: View { if text.isEmpty { Text("Type a message or / for commands...") .font(.system(size: settings.inputTextSize)) - .foregroundColor(.oaiSecondary) + .foregroundColor(.confabSecondary) .padding(.horizontal, 12) .padding(.top, 10) .allowsHitTesting(false) @@ -96,7 +96,7 @@ struct InputBar: View { NativeTextEditor( text: $text, font: .systemFont(ofSize: settings.inputTextSize), - textColor: NSColor(Color.oaiPrimary), + textColor: NSColor(Color.confabPrimary), isFocused: isInputFocused, onReturn: { if showCommandDropdown { @@ -157,11 +157,11 @@ struct InputBar: View { } } .frame(height: inputHeight) - .background(Color.oaiSurface) + .background(Color.confabSurface) .cornerRadius(8) .overlay( RoundedRectangle(cornerRadius: 8) - .stroke(isInputFocused ? Color.oaiAccent : Color.oaiBorder, lineWidth: 1) + .stroke(isInputFocused ? Color.confabAccent : Color.confabBorder, lineWidth: 1) ) // Send / stop + attach buttons @@ -170,7 +170,7 @@ struct InputBar: View { Button(action: pickFile) { Image(systemName: "paperclip") .font(.title2) - .foregroundColor(.oaiPrimary.opacity(0.7)) + .foregroundColor(.confabPrimary.opacity(0.7)) } .buttonStyle(.plain) .help("Attach file") @@ -180,7 +180,7 @@ struct InputBar: View { Button(action: onCancel) { Image(systemName: "stop.circle.fill") .font(.title) - .foregroundColor(.oaiError.opacity(0.9)) + .foregroundColor(.confabError.opacity(0.9)) } .buttonStyle(.plain) .help("Stop generation") @@ -188,7 +188,7 @@ struct InputBar: View { Button(action: onSend) { Image(systemName: "arrow.up.circle.fill") .font(.title) - .foregroundColor(text.isEmpty ? .oaiPrimary.opacity(0.4) : .oaiAccent.opacity(0.9)) + .foregroundColor(text.isEmpty ? .confabPrimary.opacity(0.4) : .confabAccent.opacity(0.9)) } .buttonStyle(.plain) .disabled(text.isEmpty) @@ -198,7 +198,7 @@ struct InputBar: View { .frame(width: 40) } .padding() - .background(Color.oaiSurface) + .background(Color.confabSurface) } .onAppear { isInputFocused = true @@ -330,22 +330,22 @@ struct CommandSuggestionsView: View { VStack(alignment: .leading, spacing: 2) { Text(suggestion.command) .font(.body) - .foregroundColor(.oaiPrimary) + .foregroundColor(.confabPrimary) Text(suggestion.description) .font(.caption) - .foregroundColor(.oaiSecondary) + .foregroundColor(.confabSecondary) } Spacer() } .padding(.horizontal, 12) .padding(.vertical, 8) - .background(index == selectedIndex ? Color.oaiAccent.opacity(0.2) : Color.oaiSurface) + .background(index == selectedIndex ? Color.confabAccent.opacity(0.2) : Color.confabSurface) } .buttonStyle(.plain) .id(suggestion.command) if index < suggestions.count - 1 { - Divider().background(Color.oaiBorder) + Divider().background(Color.confabBorder) } } } @@ -356,9 +356,9 @@ struct CommandSuggestionsView: View { } } } - .background(Color.oaiSurface) + .background(Color.confabSurface) .cornerRadius(8) - .overlay(RoundedRectangle(cornerRadius: 8).stroke(Color.oaiBorder, lineWidth: 1)) + .overlay(RoundedRectangle(cornerRadius: 8).stroke(Color.confabBorder, lineWidth: 1)) } } @@ -374,5 +374,5 @@ struct CommandSuggestionsView: View { onToggleOnline: {} ) } - .background(Color.oaiBackground) + .background(Color.confabBackground) } diff --git a/oAI/Views/Main/MarkdownContentView.swift b/oAI/Views/Main/MarkdownContentView.swift index d7d4a1c..dcdd4e3 100644 --- a/oAI/Views/Main/MarkdownContentView.swift +++ b/oAI/Views/Main/MarkdownContentView.swift @@ -1,17 +1,17 @@ // // MarkdownContentView.swift -// oAI +// Confab // // Renders markdown content with syntax-highlighted code blocks // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // @@ -196,12 +196,12 @@ struct TableView: View { if index < data.headers.count { Text(data.headers[index].trimmingCharacters(in: .whitespaces)) .font(.system(size: fontSize, weight: .semibold)) - .foregroundColor(.oaiPrimary) + .foregroundColor(.confabPrimary) .frame(maxWidth: .infinity, alignment: alignmentFor( index < data.alignments.count ? data.alignments[index] : .leading )) .padding(8) - .background(Color.oaiSecondary.opacity(0.1)) + .background(Color.confabSecondary.opacity(0.1)) if index < data.headers.count - 1 { Divider() @@ -211,7 +211,7 @@ struct TableView: View { } .overlay( Rectangle() - .stroke(Color.oaiSecondary.opacity(0.3), lineWidth: 1) + .stroke(Color.confabSecondary.opacity(0.3), lineWidth: 1) ) // Rows @@ -224,7 +224,7 @@ struct TableView: View { Text(cellContent.trimmingCharacters(in: .whitespaces)) .font(.system(size: fontSize)) - .foregroundColor(.oaiPrimary) + .foregroundColor(.confabPrimary) .frame(maxWidth: .infinity, alignment: alignmentFor(alignment)) .padding(8) @@ -233,10 +233,10 @@ struct TableView: View { } } } - .background(rowIndex % 2 == 0 ? Color.clear : Color.oaiSecondary.opacity(0.05)) + .background(rowIndex % 2 == 0 ? Color.clear : Color.confabSecondary.opacity(0.05)) .overlay( Rectangle() - .stroke(Color.oaiSecondary.opacity(0.3), lineWidth: 1) + .stroke(Color.confabSecondary.opacity(0.3), lineWidth: 1) ) } } @@ -244,7 +244,7 @@ struct TableView: View { .cornerRadius(8) .overlay( RoundedRectangle(cornerRadius: 8) - .strokeBorder(Color.oaiSecondary.opacity(0.3), lineWidth: 1) + .strokeBorder(Color.confabSecondary.opacity(0.3), lineWidth: 1) ) ) } diff --git a/oAI/Views/Main/MessageRow.swift b/oAI/Views/Main/MessageRow.swift index 906929b..d76059a 100644 --- a/oAI/Views/Main/MessageRow.swift +++ b/oAI/Views/Main/MessageRow.swift @@ -1,17 +1,17 @@ // // MessageRow.swift -// oAI +// Confab // // Individual message display // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // @@ -76,7 +76,7 @@ struct MessageRow: View { Button(action: toggleStar) { Image(systemName: isStarred ? "star.fill" : "star") .font(.system(size: 11)) - .foregroundColor(isStarred ? .yellow : .oaiSecondary) + .foregroundColor(isStarred ? .yellow : .confabSecondary) } .buttonStyle(.plain) .transition(.opacity) @@ -94,7 +94,7 @@ struct MessageRow: View { .font(.system(size: 11)) } } - .foregroundColor(showCopied ? .green : .oaiSecondary) + .foregroundColor(showCopied ? .green : .confabSecondary) } .buttonStyle(.plain) .transition(.opacity) @@ -103,7 +103,7 @@ struct MessageRow: View { Text(message.timestamp, style: .time) .font(.caption2) - .foregroundColor(.oaiSecondary) + .foregroundColor(.confabSecondary) } // Thinking / reasoning block (collapsible) @@ -146,7 +146,7 @@ struct MessageRow: View { .font(.caption) Text(attachments[index].path) .font(.caption) - .foregroundColor(.oaiSecondary) + .foregroundColor(.confabSecondary) } } } @@ -180,7 +180,7 @@ struct MessageRow: View { } } .font(.caption2) - .foregroundColor(.oaiSecondary) + .foregroundColor(.confabSecondary) } } .frame(maxWidth: .infinity, alignment: .leading) @@ -400,18 +400,18 @@ struct MessageRow: View { if isErrorMessage { HStack(alignment: .top, spacing: 8) { Image(systemName: "exclamationmark.triangle.fill") - .foregroundColor(.oaiError) + .foregroundColor(.confabError) .font(.system(size: settings.dialogTextSize)) Text(message.content) .font(.system(size: settings.dialogTextSize)) - .foregroundColor(.oaiPrimary) + .foregroundColor(.confabPrimary) .frame(maxWidth: .infinity, alignment: .leading) .textSelection(.enabled) } } else { Text(message.content) .font(.system(size: settings.dialogTextSize)) - .foregroundColor(.oaiPrimary) + .foregroundColor(.confabPrimary) .frame(maxWidth: .infinity, alignment: .leading) .textSelection(.enabled) } @@ -419,7 +419,7 @@ struct MessageRow: View { // User messages: preserve line breaks as-is (plain text, not markdown) Text(message.content) .font(.system(size: settings.dialogTextSize)) - .foregroundColor(.oaiPrimary) + .foregroundColor(.confabPrimary) .lineSpacing(4) .frame(maxWidth: .infinity, alignment: .leading) .textSelection(.enabled) @@ -434,7 +434,7 @@ struct MessageRow: View { private var messageBorderColor: Color { if isErrorMessage { - return .oaiError.opacity(0.5) + return .confabError.opacity(0.5) } return Color.messageColor(for: message.role).opacity(0.3) } @@ -571,5 +571,5 @@ struct GeneratedImagesView: View { MessageRow(message: Message.mockSystem) } .padding() - .background(Color.oaiBackground) + .background(Color.confabBackground) } diff --git a/oAI/Views/Main/SidebarView.swift b/oAI/Views/Main/SidebarView.swift index 31985b8..1d83e21 100644 --- a/oAI/Views/Main/SidebarView.swift +++ b/oAI/Views/Main/SidebarView.swift @@ -1,17 +1,17 @@ // // SidebarView.swift -// oAI +// Confab // // Collapsible sidebar: new chat, conversation list, status pills // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // @@ -53,7 +53,7 @@ struct SidebarView: View { Text("New Chat") .font(.system(size: 14, weight: .medium)) } - .foregroundColor(.oaiPrimary) + .foregroundColor(.confabPrimary) .contentShape(Rectangle()) } .buttonStyle(.plain) @@ -63,7 +63,7 @@ struct SidebarView: View { Button(action: { createFolderPrompt() }) { Image(systemName: "folder.badge.plus") .font(.system(size: 14)) - .foregroundColor(.oaiPrimary) + .foregroundColor(.confabPrimary) .contentShape(Rectangle()) } .buttonStyle(.plain) @@ -236,7 +236,7 @@ struct SidebarView: View { } .listRowBackground( chatViewModel.currentConversationName == conversation.name - ? Color.oaiAccent.opacity(0.15) + ? Color.confabAccent.opacity(0.15) : Color.clear ) .draggable(conversation.id.uuidString) { @@ -245,7 +245,7 @@ struct SidebarView: View { .foregroundStyle(.white) .padding(.horizontal, 10) .padding(.vertical, 6) - .background(Color.oaiAccent, in: RoundedRectangle(cornerRadius: 6)) + .background(Color.confabAccent, in: RoundedRectangle(cornerRadius: 6)) } .swipeActions(edge: .trailing, allowsFullSwipe: false) { Button(role: .destructive) { diff --git a/oAI/Views/Main/SyncStatusIndicator.swift b/oAI/Views/Main/SyncStatusIndicator.swift index 433f560..06cc747 100644 --- a/oAI/Views/Main/SyncStatusIndicator.swift +++ b/oAI/Views/Main/SyncStatusIndicator.swift @@ -1,17 +1,17 @@ // // SyncStatusIndicator.swift -// oAI +// Confab // // Git sync status indicator (bottom-right corner) // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Views/Screens/AboutView.swift b/oAI/Views/Screens/AboutView.swift index 85b9267..a9a6d0f 100644 --- a/oAI/Views/Screens/AboutView.swift +++ b/oAI/Views/Screens/AboutView.swift @@ -1,17 +1,17 @@ // // AboutView.swift -// oAI +// Confab // // About modal with app icon and version info // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // @@ -45,7 +45,7 @@ struct AboutView: View { .clipShape(RoundedRectangle(cornerRadius: 24)) .shadow(color: .cyan.opacity(0.3), radius: 12) - Text("oAI") + Text("Confab") .font(.system(size: 28, weight: .bold)) Text("Version \(appVersion) (\(buildNumber))") diff --git a/oAI/Views/Screens/AgentSkillEditorSheet.swift b/oAI/Views/Screens/AgentSkillEditorSheet.swift index bc37a99..ea220fa 100644 --- a/oAI/Views/Screens/AgentSkillEditorSheet.swift +++ b/oAI/Views/Screens/AgentSkillEditorSheet.swift @@ -1,17 +1,17 @@ // // AgentSkillEditorSheet.swift -// oAI +// Confab // // Create or edit a SKILL.md-style agent skill, with optional support files // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Views/Screens/AgentSkillsView.swift b/oAI/Views/Screens/AgentSkillsView.swift index 293241e..dd05345 100644 --- a/oAI/Views/Screens/AgentSkillsView.swift +++ b/oAI/Views/Screens/AgentSkillsView.swift @@ -1,17 +1,17 @@ // // AgentSkillsView.swift -// oAI +// Confab // // Modal for managing SKILL.md-style agent skills (opened via /skills command) // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Views/Screens/BashApprovalSheet.swift b/oAI/Views/Screens/BashApprovalSheet.swift index e09a84b..ce7bcbc 100644 --- a/oAI/Views/Screens/BashApprovalSheet.swift +++ b/oAI/Views/Screens/BashApprovalSheet.swift @@ -1,17 +1,17 @@ // // BashApprovalSheet.swift -// oAI +// Confab // // Approval UI for AI-requested bash commands // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Views/Screens/CombineConversationsSheet.swift b/oAI/Views/Screens/CombineConversationsSheet.swift index 1fdd786..2c66e77 100644 --- a/oAI/Views/Screens/CombineConversationsSheet.swift +++ b/oAI/Views/Screens/CombineConversationsSheet.swift @@ -1,17 +1,17 @@ // // CombineConversationsSheet.swift -// oAI +// Confab // // Combine 2+ saved conversations into one, optionally using AI to merge content // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Views/Screens/ConversationListView.swift b/oAI/Views/Screens/ConversationListView.swift index d1ce2ee..708426d 100644 --- a/oAI/Views/Screens/ConversationListView.swift +++ b/oAI/Views/Screens/ConversationListView.swift @@ -1,17 +1,17 @@ // // ConversationListView.swift -// oAI +// Confab // // Saved conversations list // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // @@ -345,7 +345,7 @@ struct ConversationListView: View { } .listRowBackground( !isSelecting && index == selectedIndex - ? Color.oaiAccent.opacity(0.15) + ? Color.confabAccent.opacity(0.15) : Color.clear ) .id(conversation.id) @@ -355,7 +355,7 @@ struct ConversationListView: View { .foregroundStyle(.white) .padding(.horizontal, 10) .padding(.vertical, 6) - .background(Color.oaiAccent, in: RoundedRectangle(cornerRadius: 6)) + .background(Color.confabAccent, in: RoundedRectangle(cornerRadius: 6)) } .swipeActions(edge: .trailing, allowsFullSwipe: false) { Button(role: .destructive) { diff --git a/oAI/Views/Screens/CreditsView.swift b/oAI/Views/Screens/CreditsView.swift index 9d9e5ba..d7e45a1 100644 --- a/oAI/Views/Screens/CreditsView.swift +++ b/oAI/Views/Screens/CreditsView.swift @@ -1,17 +1,17 @@ // // CreditsView.swift -// oAI +// Confab // // Account credits and balance // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Views/Screens/EmailLogView.swift b/oAI/Views/Screens/EmailLogView.swift index 95e62fc..5ebbfd6 100644 --- a/oAI/Views/Screens/EmailLogView.swift +++ b/oAI/Views/Screens/EmailLogView.swift @@ -1,17 +1,17 @@ // // EmailLogView.swift -// oAI +// Confab // // Email handler activity log viewer // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Views/Screens/HelpView.swift b/oAI/Views/Screens/HelpView.swift index 27cf139..87d9f0b 100644 --- a/oAI/Views/Screens/HelpView.swift +++ b/oAI/Views/Screens/HelpView.swift @@ -1,17 +1,17 @@ // // HelpView.swift -// oAI +// Confab // // Help and commands reference with expandable detail and search // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Views/Screens/HistoryView.swift b/oAI/Views/Screens/HistoryView.swift index 3978a00..6aab0e1 100644 --- a/oAI/Views/Screens/HistoryView.swift +++ b/oAI/Views/Screens/HistoryView.swift @@ -1,17 +1,17 @@ // // HistoryView.swift -// oAI +// Confab // // Command history viewer with search // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // @@ -149,7 +149,7 @@ struct HistoryView: View { } } .frame(minWidth: 600, minHeight: 400) - .background(Color.oaiBackground) + .background(Color.confabBackground) .task { loadHistory() isListFocused = true @@ -191,7 +191,7 @@ struct HistoryRow: View { } .padding(.vertical, 8) .padding(.horizontal, 4) - .listRowBackground(isSelected ? Color.oaiAccent.opacity(0.2) : Color.clear) + .listRowBackground(isSelected ? Color.confabAccent.opacity(0.2) : Color.clear) } } diff --git a/oAI/Views/Screens/JarvisView.swift b/oAI/Views/Screens/JarvisView.swift index dbcf5a4..411cf2a 100644 --- a/oAI/Views/Screens/JarvisView.swift +++ b/oAI/Views/Screens/JarvisView.swift @@ -1,6 +1,6 @@ // // JarvisView.swift -// oAI +// Confab // // Main modal for managing Jarvis (oAI-Web) agents and usage. // diff --git a/oAI/Views/Screens/ModelInfoView.swift b/oAI/Views/Screens/ModelInfoView.swift index 5a18113..b819f0c 100644 --- a/oAI/Views/Screens/ModelInfoView.swift +++ b/oAI/Views/Screens/ModelInfoView.swift @@ -1,17 +1,17 @@ // // ModelInfoView.swift -// oAI +// Confab // // Rich model information modal // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // @@ -69,6 +69,9 @@ struct ModelInfoView: View { if let provider = model.topProvider { infoRow("Provider", provider) } + if let releaseDate = model.releaseDate { + infoRow("Released", releaseDate.formatted(date: .abbreviated, time: .omitted)) + } if let desc = model.description { VStack(alignment: .leading, spacing: 6) { Text("Description") diff --git a/oAI/Views/Screens/ModelSelectorView.swift b/oAI/Views/Screens/ModelSelectorView.swift index b717bd5..2a96903 100644 --- a/oAI/Views/Screens/ModelSelectorView.swift +++ b/oAI/Views/Screens/ModelSelectorView.swift @@ -1,17 +1,17 @@ // // ModelSelectorView.swift -// oAI +// Confab // // Model selection screen // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Views/Screens/PersonalDataApprovalSheet.swift b/oAI/Views/Screens/PersonalDataApprovalSheet.swift index ca31968..4db81bb 100644 --- a/oAI/Views/Screens/PersonalDataApprovalSheet.swift +++ b/oAI/Views/Screens/PersonalDataApprovalSheet.swift @@ -1,17 +1,17 @@ // // PersonalDataApprovalSheet.swift -// oAI +// Confab // // Approval UI for AI-requested Calendar/Reminders write actions // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Views/Screens/SettingsView.swift b/oAI/Views/Screens/SettingsView.swift index 43ed5f1..a420d9d 100644 --- a/oAI/Views/Screens/SettingsView.swift +++ b/oAI/Views/Screens/SettingsView.swift @@ -1,17 +1,17 @@ // // SettingsView.swift -// oAI +// Confab // // Settings and configuration screen // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // @@ -514,7 +514,7 @@ It's better to admit "I need more information" or "I cannot do that" than to fak } } } - Text("Controls which messages are written to ~/Library/Logs/oAI.log") + Text("Controls which messages are written to ~/Library/Logs/Confab.log") .font(.system(size: 13)) .foregroundStyle(.secondary) .fixedSize(horizontal: false, vertical: true) @@ -1605,7 +1605,7 @@ It's better to admit "I need more information" or "I cannot do that" than to fak .font(.system(size: 13)) Text("• Add public key to your git provider") .font(.system(size: 13)) - Text("• No credentials needed in oAI") + Text("• No credentials needed in Confab") .font(.system(size: 13)) } .foregroundStyle(.secondary) @@ -1822,7 +1822,7 @@ It's better to admit "I need more information" or "I cannot do that" than to fak .font(.system(size: settingsService.guiTextSize)) .foregroundColor(.secondary) .fixedSize(horizontal: false, vertical: true) - Text("Example: oai-bot-x7k2m9p3@gmail.com") + Text("Example: confab-bot-x7k2m9p3@gmail.com") .font(.system(size: settingsService.guiTextSize - 1, design: .monospaced)) .foregroundColor(.blue) .padding(.vertical, 4) @@ -2483,7 +2483,7 @@ It's better to admit "I need more information" or "I cannot do that" than to fak } VStack(alignment: .leading, spacing: 4) { - Text("Jarvis is oAI-Web, a self-hosted companion server that lets oAI sync and connect remotely.") + Text("Jarvis is oAI-Web, a self-hosted companion server that lets Confab sync and connect remotely.") .foregroundStyle(.secondary) Link("→ Jarvis / oAI-Web on Gitea", destination: URL(string: "https://gitlab.pm/rune/oai-web")!) } @@ -2585,7 +2585,7 @@ It's better to admit "I need more information" or "I cannot do that" than to fak .frame(width: 220) } } - Text("When enabled, oAI backs up automatically in the background (checked at launch and hourly while running) — no need to press \"Back Up Now\" yourself.") + Text("When enabled, Confab backs up automatically in the background (checked at launch and hourly while running) — no need to press \"Back Up Now\" yourself.") .font(.system(size: 13)) .foregroundStyle(.secondary) .fixedSize(horizontal: false, vertical: true) @@ -2706,7 +2706,7 @@ It's better to admit "I need more information" or "I cannot do that" than to fak defer { url.stopAccessingSecurityScopedResource() } try await backupService.importSettings(from: url) await MainActor.run { - backupMessage = "Settings restored. Re-enter your API keys to resume using oAI." + backupMessage = "Settings restored. Re-enter your API keys to resume using Confab." backupMessageIsError = false isImporting = false } diff --git a/oAI/Views/Screens/SkillEditorSheet.swift b/oAI/Views/Screens/SkillEditorSheet.swift index 962c828..c7f387c 100644 --- a/oAI/Views/Screens/SkillEditorSheet.swift +++ b/oAI/Views/Screens/SkillEditorSheet.swift @@ -1,17 +1,17 @@ // // ShortcutEditorSheet.swift -// oAI +// Confab // // Create or edit a user-defined shortcut (prompt template) // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Views/Screens/SkillsView.swift b/oAI/Views/Screens/SkillsView.swift index 38d325f..64f9ebc 100644 --- a/oAI/Views/Screens/SkillsView.swift +++ b/oAI/Views/Screens/SkillsView.swift @@ -1,17 +1,17 @@ // // ShortcutsView.swift -// oAI +// Confab // // Modal for managing user-defined shortcuts (opened via /shortcuts command) // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/Views/Screens/StatsView.swift b/oAI/Views/Screens/StatsView.swift index dbd9834..6e005a1 100644 --- a/oAI/Views/Screens/StatsView.swift +++ b/oAI/Views/Screens/StatsView.swift @@ -1,17 +1,17 @@ // // StatsView.swift -// oAI +// Confab // // Session statistics screen // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // diff --git a/oAI/oAI-InfoPlist.xcstrings b/oAI/oAI-InfoPlist.xcstrings index fb5f7cd..11e8759 100644 --- a/oAI/oAI-InfoPlist.xcstrings +++ b/oAI/oAI-InfoPlist.xcstrings @@ -8,31 +8,31 @@ "da" : { "stringUnit" : { "state" : "translated", - "value" : "oAI" + "value" : "Confab" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "oAI" + "value" : "Confab" } }, "en" : { "stringUnit" : { "state" : "new", - "value" : "oAI" + "value" : "Confab" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "oAI" + "value" : "Confab" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "oAI" + "value" : "Confab" } } } @@ -44,7 +44,7 @@ "en" : { "stringUnit" : { "state" : "new", - "value" : "oAI can read and create calendar events when you ask it to, if you enable Calendar access in Settings." + "value" : "Confab can read and create calendar events when you ask it to, if you enable Calendar access in Settings." } } } @@ -56,7 +56,7 @@ "en" : { "stringUnit" : { "state" : "new", - "value" : "oAI can search your contacts when you ask it to, if you enable Contacts access in Settings." + "value" : "Confab can search your contacts when you ask it to, if you enable Contacts access in Settings." } } } @@ -68,7 +68,7 @@ "en" : { "stringUnit" : { "state" : "new", - "value" : "oAI can use your current location to answer questions, if you enable Location & Maps access in Settings." + "value" : "Confab can use your current location to answer questions, if you enable Location & Maps access in Settings." } } } @@ -80,7 +80,7 @@ "en" : { "stringUnit" : { "state" : "new", - "value" : "oAI can read and create reminders when you ask it to, if you enable Reminders access in Settings." + "value" : "Confab can read and create reminders when you ask it to, if you enable Reminders access in Settings." } } } diff --git a/oAI/oAIApp.swift b/oAI/oAIApp.swift index 6e2fd0e..2a7c6a0 100644 --- a/oAI/oAIApp.swift +++ b/oAI/oAIApp.swift @@ -1,17 +1,17 @@ // // oAIApp.swift -// oAI +// Confab // // Main app entry point // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // -// This file is part of oAI. +// This file is part of Confab. // -// oAI is licensed under the PolyForm Noncommercial License 1.0.0. +// Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial -// purpose. Commercial use — including selling oAI or any part of +// purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // @@ -117,7 +117,7 @@ struct oAIApp: App { .commands { // ── Apple menu ──────────────────────────────────────────────── CommandGroup(replacing: .appInfo) { - Button("About oAI") { showAbout = true } + Button("About Confab") { showAbout = true } } CommandGroup(replacing: .appSettings) { @@ -206,7 +206,7 @@ struct oAIApp: App { // ── Help menu ───────────────────────────────────────────────── CommandGroup(replacing: .help) { - Button("oAI Help") { openHelp() } + Button("Confab Help") { openHelp() } .keyboardShortcut("?", modifiers: .command) Divider() Button(UpdateCheckService.shared.isCheckingManually ? "Checking…" : "Check for Updates…") { @@ -224,7 +224,7 @@ struct oAIApp: App { // through NSHelpManager/Help Viewer — see CLAUDE.md's macOS 27 beta note for why // (Apple's Tips.app replacement for Help Viewer can't resolve anchors on this beta). // Revisit once macOS 27 reaches RC. - if let helpBookURL = Bundle.main.url(forResource: "oAI.help", withExtension: nil) { + if let helpBookURL = Bundle.main.url(forResource: "Confab.help", withExtension: nil) { NSWorkspace.shared.open(helpBookURL.appendingPathComponent("Contents/Resources/en.lproj/index.html")) } } diff --git a/oAITests/AIProviderTests.swift b/oAITests/AIProviderTests.swift index 65e391e..56976a6 100644 --- a/oAITests/AIProviderTests.swift +++ b/oAITests/AIProviderTests.swift @@ -7,7 +7,7 @@ import Testing import Foundation -@testable import oAI +@testable import Confab @Suite("ChatResponse / Usage decoding") struct AIProviderTests { diff --git a/oAITests/AnthropicProviderTests.swift b/oAITests/AnthropicProviderTests.swift index 3a859ec..8ed5591 100644 --- a/oAITests/AnthropicProviderTests.swift +++ b/oAITests/AnthropicProviderTests.swift @@ -7,7 +7,7 @@ import Testing import Foundation -@testable import oAI +@testable import Confab @Suite("AnthropicProvider request/response conversion") struct AnthropicProviderTests { diff --git a/oAITests/BackupServiceTests.swift b/oAITests/BackupServiceTests.swift index c3fa605..04cc947 100644 --- a/oAITests/BackupServiceTests.swift +++ b/oAITests/BackupServiceTests.swift @@ -7,7 +7,7 @@ import Testing import Foundation -@testable import oAI +@testable import Confab @Suite("BackupService.decideFavoritesSync") struct BackupServiceFavoritesSyncTests { diff --git a/oAITests/ChatViewModelPureLogicTests.swift b/oAITests/ChatViewModelPureLogicTests.swift index 4ea0d46..2acee45 100644 --- a/oAITests/ChatViewModelPureLogicTests.swift +++ b/oAITests/ChatViewModelPureLogicTests.swift @@ -6,7 +6,7 @@ // Copyright (C) 2026 Rune Olsen import Testing -@testable import oAI +@testable import Confab @Suite("ChatViewModel pure static helpers") struct ChatViewModelPureLogicTests { diff --git a/oAITests/ContextSelectionServiceDBTests.swift b/oAITests/ContextSelectionServiceDBTests.swift index f63f4fc..1855b23 100644 --- a/oAITests/ContextSelectionServiceDBTests.swift +++ b/oAITests/ContextSelectionServiceDBTests.swift @@ -7,7 +7,7 @@ import Testing import Foundation -@testable import oAI +@testable import Confab @Suite("ContextSelectionService DB-coupled paths, against a throwaway in-memory queue") struct ContextSelectionServiceDBTests { diff --git a/oAITests/ContextSelectionServiceTests.swift b/oAITests/ContextSelectionServiceTests.swift index de96c0e..34a1c53 100644 --- a/oAITests/ContextSelectionServiceTests.swift +++ b/oAITests/ContextSelectionServiceTests.swift @@ -6,7 +6,7 @@ // Copyright (C) 2026 Rune Olsen import Testing -@testable import oAI +@testable import Confab @Suite("ContextSelectionService pure scoring helpers") struct ContextSelectionServiceTests { diff --git a/oAITests/ConversationExportServiceTests.swift b/oAITests/ConversationExportServiceTests.swift index 4a9bb5a..d34b929 100644 --- a/oAITests/ConversationExportServiceTests.swift +++ b/oAITests/ConversationExportServiceTests.swift @@ -7,7 +7,7 @@ import Testing import Foundation -@testable import oAI +@testable import Confab @Suite("ConversationExportService") struct ConversationExportServiceTests { diff --git a/oAITests/ConversationListViewPureLogicTests.swift b/oAITests/ConversationListViewPureLogicTests.swift new file mode 100644 index 0000000..d73cb73 --- /dev/null +++ b/oAITests/ConversationListViewPureLogicTests.swift @@ -0,0 +1,54 @@ +// +// ConversationListViewPureLogicTests.swift +// oAITests +// +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 +// Copyright (C) 2026 Rune Olsen + +import Testing +import Foundation +@testable import Confab + +@Suite("ConversationListView.idsInRange") +struct ConversationListViewPureLogicTests { + + private let ids = (0..<6).map { _ in UUID() } + + @Test("Forward range includes anchor and target inclusive") + func forwardRange() { + let result = ConversationListView.idsInRange(orderedIds: ids, anchorId: ids[1], targetId: ids[4]) + #expect(result == Set(ids[1...4])) + } + + @Test("Backward range (target above anchor) still selects the span between them") + func backwardRange() { + let result = ConversationListView.idsInRange(orderedIds: ids, anchorId: ids[4], targetId: ids[1]) + #expect(result == Set(ids[1...4])) + } + + @Test("Anchor equal to target selects just that one id") + func sameAnchorAndTarget() { + let result = ConversationListView.idsInRange(orderedIds: ids, anchorId: ids[2], targetId: ids[2]) + #expect(result == [ids[2]]) + } + + @Test("Nil anchor (first Shift-click) falls back to just the target") + func nilAnchorFallsBackToTarget() { + let result = ConversationListView.idsInRange(orderedIds: ids, anchorId: nil, targetId: ids[3]) + #expect(result == [ids[3]]) + } + + @Test("Anchor no longer present in the ordered list falls back to just the target") + func missingAnchorFallsBackToTarget() { + let staleAnchor = UUID() + let result = ConversationListView.idsInRange(orderedIds: ids, anchorId: staleAnchor, targetId: ids[3]) + #expect(result == [ids[3]]) + } + + @Test("Single-element list selects that element") + func singleElementList() { + let solo = [ids[0]] + let result = ConversationListView.idsInRange(orderedIds: solo, anchorId: ids[0], targetId: ids[0]) + #expect(result == [ids[0]]) + } +} diff --git a/oAITests/ConversationMergeServiceTests.swift b/oAITests/ConversationMergeServiceTests.swift index aeba23f..3b100db 100644 --- a/oAITests/ConversationMergeServiceTests.swift +++ b/oAITests/ConversationMergeServiceTests.swift @@ -6,7 +6,7 @@ // Copyright (C) 2026 Rune Olsen import Testing -@testable import oAI +@testable import Confab @Suite("ConversationMergeService AI response parsing") struct ConversationMergeServiceParsingTests { diff --git a/oAITests/DatabaseServiceTests.swift b/oAITests/DatabaseServiceTests.swift index 540af09..e95bf6e 100644 --- a/oAITests/DatabaseServiceTests.swift +++ b/oAITests/DatabaseServiceTests.swift @@ -7,7 +7,7 @@ import Testing import Foundation -@testable import oAI +@testable import Confab @Suite("DatabaseService migrations, against a throwaway in-memory queue") struct DatabaseServiceMigrationTests { diff --git a/oAITests/DraftRecoveryServiceTests.swift b/oAITests/DraftRecoveryServiceTests.swift index 5f02cb0..ce29185 100644 --- a/oAITests/DraftRecoveryServiceTests.swift +++ b/oAITests/DraftRecoveryServiceTests.swift @@ -7,7 +7,7 @@ import Testing import Foundation -@testable import oAI +@testable import Confab @Suite("DraftRecoveryService") struct DraftRecoveryServiceTests { diff --git a/oAITests/EmbeddingServiceTests.swift b/oAITests/EmbeddingServiceTests.swift index f135ffd..1ce9c2e 100644 --- a/oAITests/EmbeddingServiceTests.swift +++ b/oAITests/EmbeddingServiceTests.swift @@ -6,7 +6,7 @@ // Copyright (C) 2026 Rune Olsen import Testing -@testable import oAI +@testable import Confab @Suite("EmbeddingService serialization") struct EmbeddingServiceTests { diff --git a/oAITests/GitSyncServiceTests.swift b/oAITests/GitSyncServiceTests.swift index 725cfe4..5bc12da 100644 --- a/oAITests/GitSyncServiceTests.swift +++ b/oAITests/GitSyncServiceTests.swift @@ -7,7 +7,7 @@ import Testing import Foundation -@testable import oAI +@testable import Confab @Suite("GitSyncService pure helpers") struct GitSyncServiceTests { diff --git a/oAITests/GitignoreParserTests.swift b/oAITests/GitignoreParserTests.swift index ac77942..dc95c0a 100644 --- a/oAITests/GitignoreParserTests.swift +++ b/oAITests/GitignoreParserTests.swift @@ -6,7 +6,7 @@ // Copyright (C) 2026 Rune Olsen import Testing -@testable import oAI +@testable import Confab @Suite("GitignoreParser") struct GitignoreParserTests { diff --git a/oAITests/MCPServicePDFTests.swift b/oAITests/MCPServicePDFTests.swift index 794f22b..7f829b4 100644 --- a/oAITests/MCPServicePDFTests.swift +++ b/oAITests/MCPServicePDFTests.swift @@ -9,7 +9,7 @@ import Testing import Foundation import CoreGraphics import AppKit -@testable import oAI +@testable import Confab @Suite("MCPService PDF text extraction") struct MCPServicePDFTests { diff --git a/oAITests/MessageCodableTests.swift b/oAITests/MessageCodableTests.swift index 5bfb14b..7be0bb1 100644 --- a/oAITests/MessageCodableTests.swift +++ b/oAITests/MessageCodableTests.swift @@ -7,7 +7,7 @@ import Testing import Foundation -@testable import oAI +@testable import Confab @Suite("Message Codable + Equatable") struct MessageCodableTests { diff --git a/oAITests/NativeTextEditorPureLogicTests.swift b/oAITests/NativeTextEditorPureLogicTests.swift new file mode 100644 index 0000000..212d121 --- /dev/null +++ b/oAITests/NativeTextEditorPureLogicTests.swift @@ -0,0 +1,174 @@ +// +// NativeTextEditorPureLogicTests.swift +// oAITests +// +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 +// Copyright (C) 2026 Rune Olsen + +import Testing +import Foundation +import AppKit +import SwiftUI +@testable import Confab + +@Suite("NativeTextEditor.inlineCodeRanges") +struct NativeTextEditorPureLogicTests { + + @Test("No backticks yields no ranges") + func noBackticks() { + #expect(NativeTextEditor.inlineCodeRanges(in: "hello world").isEmpty) + } + + @Test("A single closed backtick pair is matched, backticks included") + func singleClosedPair() { + let text = "Hello `code` world" + let ranges = NativeTextEditor.inlineCodeRanges(in: text) + #expect(ranges.count == 1) + let matched = (text as NSString).substring(with: ranges[0]) + #expect(matched == "`code`") + } + + @Test("An unterminated single backtick is not matched") + func unterminatedBacktick() { + let text = "Hello `code world" + #expect(NativeTextEditor.inlineCodeRanges(in: text).isEmpty) + } + + @Test("Multiple closed pairs are all matched") + func multiplePairs() { + let text = "`one` and `two` and `three`" + let ranges = NativeTextEditor.inlineCodeRanges(in: text) + #expect(ranges.count == 3) + let nsText = text as NSString + #expect(ranges.map { nsText.substring(with: $0) } == ["`one`", "`two`", "`three`"]) + } + + @Test("Backtick pair cannot span a newline") + func doesNotSpanNewline() { + let text = "`code\nblock`" + #expect(NativeTextEditor.inlineCodeRanges(in: text).isEmpty) + } + + @Test("Empty string yields no ranges") + func emptyString() { + #expect(NativeTextEditor.inlineCodeRanges(in: "").isEmpty) + } + + @Test("Adjacent backtick pairs on the same line are each matched separately") + func adjacentPairs() { + let text = "`a``b`" + let ranges = NativeTextEditor.inlineCodeRanges(in: text) + let nsText = text as NSString + #expect(ranges.map { nsText.substring(with: $0) } == ["`a`", "`b`"]) + } +} + +@Suite("NativeTextEditor.fencedCodeBlockRanges") +struct NativeTextEditorFencedBlockTests { + + @Test("No fences yields no ranges") + func noFences() { + #expect(NativeTextEditor.fencedCodeBlockRanges(in: "hello world").isEmpty) + } + + @Test("A closed multi-line fence is matched in full, including the language tag") + func closedMultilineFence() { + let text = "```python\nprint(\"hi\")\n```" + let ranges = NativeTextEditor.fencedCodeBlockRanges(in: text) + #expect(ranges.count == 1) + #expect((text as NSString).substring(with: ranges[0]) == text) + } + + @Test("An unterminated fence (no closing triple) is not matched") + func unterminatedFence() { + let text = "```python\nprint(\"hi\")" + #expect(NativeTextEditor.fencedCodeBlockRanges(in: text).isEmpty) + } + + @Test("Text before and after a fence is excluded from the match") + func surroundingTextExcluded() { + let text = "before\n```\ncode\n```\nafter" + let ranges = NativeTextEditor.fencedCodeBlockRanges(in: text) + #expect(ranges.count == 1) + #expect((text as NSString).substring(with: ranges[0]) == "```\ncode\n```") + } +} + +@Suite("NativeTextEditor.codeStyledRanges") +struct NativeTextEditorCodeStyledRangesTests { + + @Test("Combines a fenced block and a separate inline span") + func combinesFencedAndInline() { + let text = "See `foo` then:\n```\nbar\n```" + let ranges = NativeTextEditor.codeStyledRanges(in: text) + let nsText = text as NSString + #expect(ranges.map { nsText.substring(with: $0) } == ["`foo`", "```\nbar\n```"]) + } + + @Test("A backtick inside a fenced block's own content is not separately re-matched as inline") + func backtickInsideFenceNotDoubleMatched() { + let text = "```\nuse `code` here\n```" + let ranges = NativeTextEditor.codeStyledRanges(in: text) + #expect(ranges.count == 1) + #expect((text as NSString).substring(with: ranges[0]) == text) + } +} + +@Suite("NativeTextEditor.fencedCodeBlocks (language + code-content extraction)") +struct NativeTextEditorFencedCodeBlocksTests { + + @Test("Extracts the language tag and the code content, excluding fences and the tag line") + func extractsLanguageAndCodeContent() { + let text = "```python\nprint(\"hi\")\n```" + let blocks = NativeTextEditor.fencedCodeBlocks(in: text) + #expect(blocks.count == 1) + #expect(blocks[0].language == "python") + let nsText = text as NSString + #expect(nsText.substring(with: blocks[0].codeRange) == "print(\"hi\")\n") + } + + @Test("No language tag yields a nil language") + func noLanguageTagYieldsNil() { + let text = "```\ncode\n```" + let blocks = NativeTextEditor.fencedCodeBlocks(in: text) + #expect(blocks.count == 1) + #expect(blocks[0].language == nil) + } + + @Test("Language alias is passed through as typed, unresolved (SyntaxHighlighter resolves aliases itself)") + func languageAliasPassthrough() { + let text = "```py\nprint(1)\n```" + let blocks = NativeTextEditor.fencedCodeBlocks(in: text) + #expect(blocks[0].language == "py") + } + + @Test("Unterminated fence yields no blocks") + func unterminatedFenceYieldsNoBlocks() { + #expect(NativeTextEditor.fencedCodeBlocks(in: "```python\nprint(1)").isEmpty) + } +} + +@Suite("SyntaxHighlighter runs → NSColor") +struct SyntaxHighlighterBridgingTests { + + // NB: bridging via `NSAttributedString(highlighted)` does NOT work — that path stores + // SwiftUI's `.foregroundColor` under a private `SwiftUI.ForegroundColor` key, not the + // standard Cocoa `.foregroundColor` key (confirmed empirically before landing on this + // `.runs`-based approach, which is what `NativeTextEditor` actually uses in production). + + @Test("A Python keyword's foreground color is readable via AttributedString.runs and converts to NSColor") + func keywordColorReadableViaRuns() { + let highlighted = SyntaxHighlighter.highlight(code: "def foo():", language: "python") + #expect(!highlighted.runs.isEmpty) + + var foundKeywordColor = false + let expected = NSColor(SyntaxHighlighter.keywordColor) + for run in highlighted.runs { + guard let color = run.foregroundColor else { continue } + if NSColor(color).usingColorSpace(.deviceRGB) == expected.usingColorSpace(.deviceRGB) { + foundKeywordColor = true + } + } + #expect(foundKeywordColor) + } +} diff --git a/oAITests/OllamaProviderTests.swift b/oAITests/OllamaProviderTests.swift index b3a758f..f793c84 100644 --- a/oAITests/OllamaProviderTests.swift +++ b/oAITests/OllamaProviderTests.swift @@ -7,7 +7,7 @@ import Testing import Foundation -@testable import oAI +@testable import Confab @Suite("OllamaProvider request/response conversion") struct OllamaProviderTests { diff --git a/oAITests/OpenAIProviderTests.swift b/oAITests/OpenAIProviderTests.swift index 46c1f85..7b99533 100644 --- a/oAITests/OpenAIProviderTests.swift +++ b/oAITests/OpenAIProviderTests.swift @@ -7,7 +7,7 @@ import Testing import Foundation -@testable import oAI +@testable import Confab @Suite("OpenAIProvider request/response conversion") struct OpenAIProviderTests { diff --git a/oAITests/OpenRouterModelsTests.swift b/oAITests/OpenRouterModelsTests.swift index 460d7df..99ed70e 100644 --- a/oAITests/OpenRouterModelsTests.swift +++ b/oAITests/OpenRouterModelsTests.swift @@ -7,7 +7,7 @@ import Testing import Foundation -@testable import oAI +@testable import Confab @Suite("OpenRouterModels decoding") struct OpenRouterModelsTests { @@ -129,4 +129,37 @@ struct OpenRouterModelsTests { #expect(delta.contentBlockImages.count == 1) #expect(delta.contentBlockImages.first?.imageUrl.url == "https://example.com/stream.png") } + + // MARK: - Model list: ModelData.created (release date) + + @Test("ModelData decodes a created timestamp when present") + func modelDataDecodesCreated() throws { + let json = """ + { + "id": "anthropic/claude-sonnet-4-5", + "name": "Claude Sonnet 4.5", + "description": null, + "context_length": 200000, + "pricing": {"prompt": "0.000003", "completion": "0.000015"}, + "created": 1735689600 + } + """.data(using: .utf8)! + let model = try JSONDecoder().decode(OpenRouterModelsResponse.ModelData.self, from: json) + #expect(model.created == 1735689600) + } + + @Test("ModelData decodes to nil created when the field is absent") + func modelDataMissingCreated() throws { + let json = """ + { + "id": "some/model", + "name": "Some Model", + "description": null, + "context_length": 4096, + "pricing": {"prompt": "0", "completion": "0"} + } + """.data(using: .utf8)! + let model = try JSONDecoder().decode(OpenRouterModelsResponse.ModelData.self, from: json) + #expect(model.created == nil) + } } diff --git a/oAITests/OpenRouterProviderTests.swift b/oAITests/OpenRouterProviderTests.swift index ff24c4b..8ef8708 100644 --- a/oAITests/OpenRouterProviderTests.swift +++ b/oAITests/OpenRouterProviderTests.swift @@ -7,7 +7,7 @@ import Testing import Foundation -@testable import oAI +@testable import Confab @Suite("OpenRouterProvider request/response conversion") struct OpenRouterProviderTests { diff --git a/oAITests/SettingsProviderTests.swift b/oAITests/SettingsProviderTests.swift index ca2255a..b429aad 100644 --- a/oAITests/SettingsProviderTests.swift +++ b/oAITests/SettingsProviderTests.swift @@ -6,7 +6,7 @@ // Copyright (C) 2026 Rune Olsen import Testing -@testable import oAI +@testable import Confab @Suite("Settings.Provider display metadata") struct SettingsProviderTests {