15 Commits
Author SHA1 Message Date
rune 9d2ce6f667 Catch up localization for this cycle's new UI strings (nb/sv/da/de/fr)
Ran the standard xcodebuild -exportLocalizations audit pass, which
caught 22 new Text() literals from this week's work (Hugging Face
provider UI, External MCP npx/Node.js install flow, Mail integration).
That tool only extracts Text() though — a follow-up manual sweep for
Button()/.help()/Label() literals specifically in the files touched
this session found 6 more genuinely new strings it missed (Custom
Model ID sheet's Add button, three HF pricing/model-picker tooltips,
the "Generate a token" link, the "Varies" pricing label).

All 28 translated into Norwegian Bokmål, Swedish, Danish, German, and
French, following this project's established conventions (informal
address, "token" kept as an English loanword, brand/product names
left untranslated). Verified no existing translations were lost in
the process (28 net new keys, 0 removed, 0 lost) and the app still
builds and the full test suite still passes.

A much larger, pre-existing gap was also found while auditing this —
showSystemMessage(...) (83 unique literal calls) and several other
Button()/.help() strings from earlier work have apparently never been
swept by any i18n pass in this project's history, since this project's
SWIFT_EMIT_LOC_STRINGS=NO setting means only regex-based Text()
extraction runs, not full compiler-based String Catalog extraction.
Deliberately out of scope for this release per discussion — logged
as a follow-up item, not fixed here.
2026-08-28 13:49:58 +02:00
rune 4d65ab7703 Surface the Google API key field regardless of search provider
It was only shown when Search Provider was set to Google, but the
same key is also needed for Google embeddings in Semantic Search
(Advanced tab) — a completely separate feature from web search. A
user who only wanted Google for embeddings had no way to find where
to enter the key. Now always visible under General → Web Search, with
a caption clarifying both uses; Search Engine ID stays conditional
since it's web-search-specific. Also pointed the Semantic Search tab's
"no providers available" hint at the same location.
2026-08-16 15:15:49 +02:00
rune 25028e3405 Add env-var support and native HTTP transport for External MCP Servers
External MCP Servers previously only spoke stdio (spawn a local
command + args). Adds:
- env vars for stdio servers (merged into the subprocess environment,
  not embedded in the args string), with a masked key-value editor
- a native Streamable HTTP transport (URL + Bearer token + custom
  headers), so HTTP-based MCP servers like Obsidian's Local REST API
  plugin connect directly without needing npx/Node.js as a bridge

Introduces an MCPTransport abstraction (stdio/HTTP) so ExternalMCPClient
stays transport-agnostic — mirrors how Provider.swift already abstracts
AI backends in this codebase.

Also fixes a real crash found via live testing against Obsidian:
convertInputSchema force-unwrapped a tool parameter's `type`, which
isn't required by JSON Schema — Obsidian's plugin was the first real
server to send a parameter without one. Live-verified end to end
(vault search/read/write/edit) before this commit, per the project's
standing rule to hold external-service-dependent changes until they're
actually confirmed working, not just compiling and passing tests.
2026-08-13 16:05:42 +02:00
rune 30e18f92a5 Track usage independent of conversation save state; fix Analytics toolbar button
Conversation text is still only persisted when explicitly saved (⌘S),
but tokens/cost/model/provider are now logged to a new usage_events
table for every completed AI response regardless — the Analytics
view now reads from this table instead of messages, so it reflects
real usage even for conversations that were never saved. Adds a
By Provider chart mode alongside Over Time/By Model.

Also fixes the Analytics entry point: ToolbarItem(placement: .navigation)
silently doesn't render in a plain .sheet-presented NavigationStack on
macOS. Moved the button inline next to the segmented picker, matching
the codebase's existing convention (e.g. the model-favorites star filter).
2026-08-12 13:49:12 +02:00
rune d460230158 Add Usage Analytics view: tokens/questions/cost over time and by model
New large modal off the Stats sheet with a 6-way timeframe picker
(Today/7 Days/Week/Month/Year/Total), three tappable summary tiles,
and native Swift Charts (bar chart over time, pie chart by model).
Backed by date-range-filtered DatabaseService queries plus a new
daily-bucketed query — no schema changes needed since messages
already carry timestamp/tokens/cost/role.
2026-08-12 13:05:41 +02:00
rune 30efc58d16 Translate Jarvis Run Details; fix String vs LocalizedStringKey bug
detailRow's label and outputBlock's title were typed String instead
of LocalizedStringKey, so Text(label)/Text(title) inside them could
never localize regardless of catalog content — the exact anti-pattern
CLAUDE.md's i18n rules warn against for helper functions. Fixed the
types, then translated the 6 genuinely-new strings (Run Details, No
output for this run., Started, Duration, Trigger, Tokens) into
nb/sv/da/de/fr. A few others (Cost, Error, Output, Copied!) were
already covered from being reused elsewhere in the catalog.
2026-08-07 13:44:04 +02:00
rune 79840bfb98 Translate the CLI Access settings section into nb/sv/da/de/fr
Six strings from the new CLI Access section (897bfdc) had never
been added to the localization catalog: the section title and
description, the enable toggle, and the shell-function instructions.
Verified live in a Norwegian build.
2026-08-06 07:21:44 +02:00
rune c21fe49f4f Add per-language thinking verbs; close major i18n translation gap
- ThinkingVerbs now picks from a hand-written verb list per active
  display language (en/nb/sv/da/de/fr) instead of always English,
  fixed to be nonisolated at the type level (this project defaults
  to MainActor isolation, which was breaking the static verb arrays).
- SyncStatusIndicator.tooltipText was typed String instead of
  LocalizedStringKey, silently bypassing localization for .help() —
  fixed.
- Translated ~150 strings (750 individual translations) into
  nb/sv/da/de/fr that had never been localized: the entire slash-
  command dropdown, sync status labels, reasoning-effort
  descriptions, model sort options, and settings rows added across
  recent features (Git Sync conflict recovery, crash recovery,
  notes, backup, external MCP servers). Most were invisible to
  xcodebuild -exportLocalizations because they're LocalizedStringKey-
  typed properties/helpers rather than literal Text() calls, per the
  same gap documented from the original Phase 7 audit.
- AI-translated per the README's existing disclosure; verified live
  in nb and fr builds, and diffed the catalog to confirm zero
  pre-existing translations were altered, only additions.
2026-08-05 12:57:49 +02:00
rune 6480a50eee Sync folder structure via Git Sync (folders.json), plus bugs found testing it
Folders and conversation→folder assignments now sync across machines:
- Folder gains updatedAt (v11 migration) to resolve renames/reparents
  last-write-wins across machines.
- New folders.json manifest at the sync repo root: folder tree +
  conversationId→folderId assignments, imported before conversation
  files so new conversations land in the right folder immediately.
- Local folders missing from the manifest are pruned (reparent-safe),
  guarded the same way conversation-orphan cleanup already is against
  an empty/stale manifest wiping everything.

Three real bugs found and fixed during live multi-machine testing:
- Sidebar never refreshed after Git Sync imported conversations/folders
  directly into the database — only reloaded on launch or when the
  advanced conversation list closed, with no equivalent hook for the
  Settings sheet.
- "Sync Now" exported before pulling, so it could write folders.json
  as an untracked file that then collided with the remote's tracked
  copy on the next pull ("untracked working tree files would be
  overwritten by merge"). Reordered to pull → import → export → push.
- Folder assignment only applied to brand-new conversations during
  import, so any conversation already synced to a machine before this
  feature existed never got filed — which in practice is every
  conversation on a second machine, not an edge case. Now backfills
  a folder assignment for existing conversations that aren't filed
  anywhere locally yet, without clobbering an already-set folderId.

Also renamed the "Initialize Repository" button to "Clone Repository"
(it's always been a git clone, not new-repo creation) across the UI,
localization catalog, and Help Book.
2026-08-03 11:48:23 +02:00
rune 76b58e6fdc 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.
2026-08-02 14:58:14 +02:00
runeandClaude Sonnet 5 cf3f4ebfe4 Relicense oAI from AGPL-3.0-or-later to PolyForm Noncommercial 1.0.0
Switches the project from AGPL to a source-available license that
restricts commercial use — selling oAI or any part of it, standalone
or bundled into another product/service, now requires a separate
commercial license from the copyright holder. Noncommercial use,
study, modification, and sharing remain fully permitted.

Updates: LICENSE (canonical PolyForm Noncommercial 1.0.0 text +
commercial licensing contact note), SPDX headers and file-header
boilerplate across all Swift source files, the in-app About dialog's
license link (+ its localization catalog entry), README.md and
DEVELOPMENT.md license sections.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 11:16:09 +02:00
runeandClaude Sonnet 4.6 40c5f25517 Add personal data tools, 2nd Brain trust toggle, and research agents
Personal Data Tools: native Calendar, Reminders, Contacts (hidden pending
Apple TCC fix in beta), and Location & Maps access via EventKit, Contacts
framework, and MapKit. Write actions (create event/reminder, complete
reminder) gate through an approval sheet. Four hardened-runtime entitlements
added to oAI.entitlements; Info.plist usage strings added for all services.
Personal Data section shows a β badge while Contacts is hidden.

2nd Brain always-trust: inline toggle on the Agent Skills row for the skill
named "2nd Brain" skips the bash approval dialog when the command contains
.brain_helper.py, gated by three runtime checks in MCPService.

Research agents: spawn_research_agents tool runs up to 5 concurrent read-only
sub-agents (read_file, list_directory, search_files, web_search — no write,
no bash, no nesting). Bounded by maxConcurrentAgents setting (default 3) and
a hard ceiling of 8 tasks. Added items field to Tool.Function.Parameters.Property
for JSON Schema array support; wired into AnthropicProvider.convertParametersToDict.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 14:04:47 +02:00
runeandClaude Sonnet 4.6 20121981a0 Add French localization and catch up nb/sv/da/de translations
French (fr) added as a 5th supported language; full catalog translated.
Also caught up nb/sv/da/de for ~300 strings added since the last
localization pass (Jarvis, Anytype, model categories, reasoning effort,
Combine Conversations) plus Button/Toggle/Menu/CommandMenu titles and
custom sectionHeader/row helpers in Settings that were never extracted
by prior tooling, leaving Settings and the View menu English-only
regardless of locale.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 11:17:36 +02:00
runeandClaude Sonnet 4.6 8451db1142 UI redesign Phase 1: NavigationSplitView with collapsible sidebar
- Replace root VStack with NavigationSplitView (2-column, collapsible sidebar)
- Add SidebarView: new chat button, conversation search, list with swipe actions
- Slim HeaderView to text-only (provider + model + star); remove all icon rows
- Move status pills (Online, MCP, Synced) to footer right side
- Remove version number and shortcut hints from footer
- Add resizable InputBar with drag handle (persisted height) and globe/network.slash online toggle
- Fix Norwegian menu appearing on English systems (CFBundleLocalizations in Info.plist)
- Add View menu (Model Info, History, Stats, Credits, Online Mode toggle ⌘⇧O)
- Add ⌘L as alias for Search Conversations (muscle memory for /load users)
- Add Check for Updates to Help menu with download URL from Gitea API
- Add one-time Intel/Rosetta deprecation warning on first launch
- Swift 6: fix self.Self.isoString() call sites in DatabaseService

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 11:18:48 +02:00
rune 49f842f119 New version v2.3.6 2026-03-04 10:19:16 +01:00