Commit Graph
182 Commits
Author SHA1 Message Date
Bohdan Triapitsyn f321562abd chore: bump @opencode-ai/sdk to ^1.17.7 and update changelogs
Upgraded @opencode-ai/sdk dependency from ^1.17.0 to ^1.17.7 across all packages
Added unreleased changelog entries for VSCode startup parity, mobile tool card fix, and files workspace directory fix
Refined VSCode changelog to remove inaccurate project-level actions note
2026-06-15 13:24:39 +03:00
Bohdan Triapitsyn 75014e0317 release v1.12.4 2026-06-11 01:42:41 +03:00
Bohdan Triapitsyn a486e76233 chore: update runtime requirements
Require Node 22 or newer
Update project package manager to Bun 1.3.14
2026-06-10 17:37:19 +03:00
Bohdan Triapitsyn e3f3da4cb4 fix: restore dependency update compatibility
Fix VS Code webview type-check with TypeScript 5.9
Align ghostty-web to 0.4.0 across the workspace
Refresh ghostty-web patch for the updated package
2026-06-10 17:05:10 +03:00
renovate[bot] 908adc1634 chore(deps): update development dependencies (#1597)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-10 16:42:49 +03:00
Bohdan Triapitsyn 0c25c4aecb docs: update unreleased changelog entries 2026-06-10 15:39:14 +03:00
nerdosaurusandBohdan Triapitsyn d9b9b56599 Diagram editor pr (#1432)
* feat: add draw.io diagram editor integration

Embed draw.io editor via react-drawio (MIT, zero deps) for inline
editing of .drawio files. Changes auto-save to disk. Includes
inline editor in FilesView with Visual/Source toggle, dark mode
support, template picker for new files, and chat file attachment
integration.

* fix: debounce diagram autosave to prevent reload loop

* fix: ignore watcher-triggered xml prop changes to prevent reload loop

* fix: remove auto-save-to-disk, add manual save button for diagrams

Autosave writes triggered file watcher cascade that reloaded the
draw.io iframe and reset zoom. Replaced with explicit Save button
in the toolbar (floppy disk icon). Editor XML is stable on mount
and ignores watcher-triggered prop changes.

* fix: remove auto-save write from DiagramView, add save button

* fix: hide draw.io save/exit buttons in editor

* fix: also hide save-and-exit button

* fix: brighten save button styling, add saved confirmation

* fix: remove autoSaveStatus toggle on diagram save to prevent toolbar collapse

* fix: add local save confirmation state for diagram button

* fix: remount drawio iframe on theme change, persisting XML across mounts

* fix: clear persisted xml on mount to prevent leaking between files

* fix: initialize dark mode synchronously, preserve edits across theme remount

* fix: auto-focus drawio iframe on mount/theme-change for keyboard shortcuts

* fix: add diagram i18n keys to Traditional Chinese locale

* fix: restore upstream HMR host and LAN address support

* fix: load sub-agent sessions on bootstrap for sidebar visibility

Two-phase session load: first fetch root sessions (for accurate
sessionTotal), then fetch all sessions and include child sessions
(sub-agent delegations). This ensures sub-agent sessions appear
in the sidebar immediately instead of relying on the async global
session store.

* remove opencode-drawio from PR branch

* fix: atomic file writes to prevent concurrent read/write truncation

Three-layer defense against the O_TRUNC race:

1. Write side (server): replace direct writeFile with write-to-temp-
   then-rename. fs.rename is atomic on POSIX.

2. Read side (server): retry up to 3 times with 50ms backoff when
   readFile returns empty but stat reported non-zero size.

3. FilesView client: refuse to save empty draftContent when the
   original fileContent was non-empty.

* fix(dev): clean up orphaned OpenCode processes on Ctrl+C

* fix: allow empty file saves, log warning instead of blocking

Replaces the hard block on saving empty content with a console.warn.
The atomic write + read retry on the server side handle the O_TRUNC
race properly. The previous guard caused a UX regression by silently
preventing users from clearing a file and saving.

* fix: remove time window from sub-agent fallback for live tasks

While a task tool is active, the fallback now matches any session
with the correct parentID regardless of creation time. This allows
late-appearing child sessions to be found when the OpenCode server
is slow or the SSE event pipeline is delayed. The time window is
still applied once the task tool has completed, as a final sanity
check.

* fix: three diagram editor bugs from Greptile review

1. stableXmlRef now resets when xml prop changes — switching
   between .drawio files renders the correct content.

2. Focus effect only runs on mount, not on isDark changes —
   theme toggle no longer steals keyboard focus 600ms later.

3. saveDiagram updates xml state after writing — dirty-check
   guard works correctly for subsequent saves.

* fix: route session.created SSE events to correct directory

Three-layer fix for sub-agent sessions not appearing in sidebar and
inline chat:

1. protocol.js: parseSseEventEnvelope now extracts directory from
   properties.info.directory (where session.created/updated events
   carry it) in addition to properties.directory. WS frames relayed
   to the browser now carry the real directory instead of 'global',
   so child sessions routed to the correct directory store.

2. event-pipeline.ts: same fallback in resolveEventDirectory for
   defense-in-depth when SSE events bypass the WS relay.

3. resolveFallbackTaskSessionId.ts: time window lower bound now
   allows 2s grace before taskStartTime to accommodate server timing
   jitter (child session creation timestamps consistently precede the
   tool's recorded start by ~6-9ms), fixing the 'Open subtask'
   button not rendering in OpenChamber's inline chat.

* fix: sub-agent sidebar visibility, file zeroing guard, inline badge fallback

- Sync watchdog: periodic child session discovery poll (every 15s) detects
  sessions created by other OpenCode instances, triggers parent materialization
- protocol.js: parseSseEventEnvelope extracts directory from
  properties.info.directory for session.created/updated events
- event-pipeline.ts: same fallback in resolveEventDirectory for defense-in-depth
- resolveFallbackTaskSessionId: don't require taskStartTime (cross-OpenCode);
  pick most recent child when multiple idle candidates exist
- readTaskSessionIdFromOutput: parse <task id="ses_xxx"> format from output
- FilesView: reinstate empty-draft guard (block save when draftContent='' but
  fileContent had content) to prevent file zeroing on tab switch

* Fix diagram autosave reload loop

* Highlight drawio files as XML

* Use diff-compatible highlighting for drawio files

* Restore drawio file icon mapping

* Stabilize drawio source preview toggle

---------

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-06-08 18:50:56 +03:00
Bohdan Triapitsyn 3bfb7ba6aa release v1.12.3 2026-06-05 20:42:29 +03:00
Bohdan Triapitsyn a5628b20df release v1.12.2 2026-06-05 15:32:13 +03:00
Bohdan Triapitsyn 6466708078 release v1.12.1 2026-06-03 23:30:11 +03:00
Bohdan Triapitsyn 8e25bc4cce fix: keep new sessions grouped by project
Preserves session directory metadata across live updates
Keeps desktop and mobile session lists in project groups
Adds regression coverage for session grouping
2026-06-03 14:51:17 +03:00
Bohdan Triapitsyn c7bc026b4b refactor: remove legacy Tauri desktop support
Electron updater now uses Electron release metadata only
Removed legacy Tauri package and migration workflow
Replaced Tauri shim usage with the desktop bridge
2026-06-03 02:42:00 +03:00
Bohdan Triapitsyn e80bd15dd9 release v1.12.0 2026-06-03 01:41:19 +03:00
Bohdan Triapitsyn 2031e3b4a8 Decouple bundled UI from runtime API and add remote instance tooling (#1228)
Add a packaged-client runtime boundary so the shared UI can talk to local,
desktop, remote, and VS Code runtimes through the right transport instead of
assuming one same-origin web server.

Centralize OpenChamber-owned API access behind RuntimeAPIs, runtimeFetch, and
runtime URL helpers, while keeping official OpenCode traffic on the SDK path.
Support runtime switching, remote host selection, desktop client credentials,
and headless connection links for pairing packaged clients with remote
OpenChamber servers.

Harden the new auth model by moving long-lived client tokens out of browser
URLs, introducing short-lived scoped URL tokens for browser-owned transports,
restricting URL-token access to explicit readable/realtime routes, and making
client-token management session-scoped or self-scoped as appropriate.

Update browser-owned assets and preview proxy flows to work with the split
runtime model, including authenticated project icons, preview token propagation,
CSP-safe preview bridge injection, and preview proxy auth that survives
short-lived URL-token expiry.

Tighten Electron security boundaries for packaged clients by gating privileged
preload state to trusted origins and requiring explicit confirmation before
connect deep-links import or switch remote runtimes.

Also refresh agent guidance and project skills so future runtime/API, auth,
preview, UI, CLI, settings, locale, and drag-to-reorder work follows the new
architecture.
2026-06-02 00:43:05 +03:00
Bohdan Triapitsyn 27255c642d release v1.11.7 2026-05-27 01:16:43 +03:00
Dave OteroandBohdan Triapitsyn becd240168 Add Windows Electron desktop support (#1093)
* fix: make upstream sync actions target the selected remote

Ensure fetch and pull actually honor upstream selection so fork maintenance works from the Git sidebar, and surface upstream branch status alongside the primary origin-tracking indicators.

* feat: add Windows Electron desktop foundation

* fix(electron): stabilize Windows desktop packaging

* fix(electron): stabilize Windows desktop chrome

Use native Windows titlebar behavior with an Alt-accessible hidden menu, and harden Windows dev command launching so the desktop app follows platform conventions.

* fix(electron): stabilize Windows dev startup

* fix(electron): clarify desktop artifact names

* fix(electron): harden Windows desktop release and launch

* fix(electron): address Windows release review

* fix(electron): point updater and release links to org repo

* Fix Windows settings persistence fallback

* Fix Windows Electron dev startup

* Add Windows Electron window controls

* Fix Windows Electron install and opencode launch

* fix: resolve git status for repositories without upstream

Fixes repository detection stuck on Checking repository
Handles git status when no upstream is configured
Adds regression coverage for git status loading

* Add Windows app menu button

* fix: preserve file editor line endings

* ci: add desktop release smoke workflow

---------

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-05-26 18:13:59 +03:00
Bohdan Triapitsyn 6e16504dac release v1.11.6 2026-05-25 19:32:26 +03:00
Bohdan Triapitsyn 68f00e4b7b release v1.11.5 2026-05-25 02:23:56 +03:00
Bohdan Triapitsyn 6b4fe392b0 chore: update opencode sdk to 1.15.10 2026-05-25 00:56:09 +03:00
Bohdan Triapitsyn 07eb9d50d3 release v1.11.4 2026-05-22 02:07:11 +03:00
Bohdan Triapitsyn f354762e22 release v1.11.3 2026-05-19 19:26:38 +03:00
Bohdan Triapitsyn d5cdf464fa release v1.11.2 2026-05-18 01:01:31 +03:00
Bohdan Triapitsyn 77b4f27053 chore: fix bun dev script 2026-05-17 18:30:13 +03:00
Bohdan Triapitsyn 0779656d80 chore: add script for generating changelog preview 2026-05-17 14:51:25 +03:00
Bohdan Triapitsyn 947f725976 release v1.11.1 2026-05-15 23:18:55 +03:00
Bohdan Triapitsyn 0eb72eb867 chore: add React Doctor maintenance workflow
Adds a React Doctor batching script for targeted diagnostics cleanup
Adds OpenCode commands for cleanup and review follow-up workflows
Ignores generated local React Doctor reports
2026-05-14 18:31:47 +03:00
Bohdan Triapitsyn a4decfd3dd release v1.11.0 2026-05-14 15:36:07 +03:00
Bohdan Triapitsyn 92f1fa7dfe fix: avoid opencode structured output session breakage
Replace server-enforced structured output with local JSON parsing for Git generation
Render generated commit and PR JSON responses as compact chat cards
Tighten generation prompts while preserving active session context
2026-05-14 02:16:52 +03:00
Bohdan Triapitsyn ef85c63336 fix: voice input in Electron - local Whisper STT + network error handling
- Add local Whisper STT via Transformers.js with Web Worker (no UI freeze)
- Default sttProvider to 'local' in Electron (browser STT unavailable)
- Fix infinite toast loop: stop auto-restart on network errors
- Add retry limit with exponential backoff for transient STT errors
- Append voice transcript to input field (append-inline), not replace
- Add model catalog with download/load button in Voice Settings
2026-05-14 01:19:52 +03:00
Bohdan Triapitsyn ef2dc8751d chore: updated opencode sdk version to 1.4.48 2026-05-13 15:59:49 +03:00
Bohdan Triapitsyn 14357257ae perf(ui): migrate icons to SVG sprite system
Replace @remixicon/react with a shared Icon component that renders
via <use href> references to a single hidden SVG sprite. This reduces
DOM node count by replacing inline SVGs with lightweight references.

- Create Icon component with sprite injection (packages/ui/src/components/icon/)
- Migrate all 164 files from @remixicon/react to Icon component
- Auto-generate sprite data from remixicon bundle (scripts/generate-icon-sprite.mjs)
- Add bun run icons:generate to package.json
- Move @remixicon/react to devDependencies
- Add icon usage instructions to theme-system skill
2026-05-13 13:26:35 +03:00
Bohdan Triapitsyn c38727efc9 release v1.10.4 2026-05-09 01:18:33 +03:00
Bohdan Triapitsyn a591f63ed2 release v1.10.3 2026-05-08 16:26:07 +03:00
Bohdan Triapitsyn c827d6b8df fix: restore messages after redo
Refetches session messages before applying redo
Aligns undo/redo navigation with OpenCode behavior
Keeps restored messages visible after unrevert
2026-05-08 14:53:48 +03:00
Bohdan Triapitsyn 686fec7419 release v1.10.2 2026-05-07 23:54:35 +03:00
Bohdan Triapitsyn 633edb27c3 release v1.10.1 2026-05-06 20:03:29 +03:00
Bohdan Triapitsyn 966eb5a1e2 chore: update opencode sdk version 2026-05-06 19:46:01 +03:00
Bohdan Triapitsyn 495b9d56d2 release v1.10.0 2026-05-05 01:39:33 +03:00
bd9a91335c feat(preview): embedded dev-server preview pane + dev shutdown controls (#1062)
* feat: embedded preview proxy for local dev servers

Add a same-origin server proxy under /api/preview/proxy/:id and
matching UI surfaces so local dev servers (Vite, Next, etc.) can be
embedded inside OpenChamber.

Server (packages/web/server):
- New lib/preview/proxy-runtime.js: cookie-gated HTTP+WebSocket proxy
  to loopback hosts only, with TTL'd targets and SSRF allowlist.
- index.js wires the runtime alongside terminal/event-stream.

UI (packages/ui):
- ContextPanel preview tab with iframe, reload, and open-in-browser.
- Inline html code-block preview in MarkdownRenderer.
- Terminal auto-detects loopback URLs and offers to open them.
- i18n keys across en, es, pt-BR, uk, zh-CN.

* perf(preview): cache proxy targets across PreviewPane remounts

Module-scoped Map keyed by upstream URL so tab switches and component
remounts within the same page session reuse the existing proxy
registration instead of POSTing a fresh target each time.

In-memory only by design: the server holds the target map in memory
and the auth cookie is HttpOnly + scoped to the proxy id, so a stale
persisted entry would 404 after a server restart. Entries are evicted
on registration error and on a 30s safety margin before TTL expiry.

* feat(preview): surface dev-server-down state with retry overlay

Iframes don't expose HTTP status to the parent, so when the proxy
returns a 502 (upstream dev server is offline) the iframe just renders
the raw JSON error body. Probe the proxy URL out-of-band with HEAD
(falling back to GET on 404/405) and replace the iframe with a
friendly 'Dev server is not responding' overlay + retry button when
the upstream is unreachable.

Re-probes on reload, on URL change, and on proxy re-registration.

* feat(preview): strip frame-busting response headers

Many dev servers (Next.js, others) send X-Frame-Options: SAMEORIGIN
and/or a CSP with frame-ancestors that block embedding inside the
OpenChamber iframe. The proxy is same-origin and already
authenticated per-target, so embedding is otherwise safe.

- Drop X-Frame-Options outright on proxied responses.
- Surgically remove only the frame-ancestors directive from
  Content-Security-Policy and Content-Security-Policy-Report-Only,
  preserving every other directive. Drops the header entirely if no
  directives remain.
- Verified end-to-end: upstream sending both headers comes through
  with X-Frame-Options removed, CSP retaining default-src/script-src
  but no frame-ancestors, and unrelated headers untouched.

* docs(preview): design for remote-host relay agent

Design-only doc for the next phase of the embedded preview feature:
when OpenChamber runs remotely (cloud/shared/tunnel) and the user's
dev server runs on their local machine. Covers architecture (local
agent + outbound control WebSocket + server dispatch), pairing flow,
wire protocol, security model, failure modes, open questions, and
implementation milestones. No code changes.

* feat(preview): auto-open preview pane for loopback URLs in chat

Detect http(s) loopback URLs in incoming assistant messages and open the
preview pane automatically, deduped per (session, url) pair so re-renders
or repeated mentions do not steal focus. Add an inline Preview button
next to loopback links in chat markdown as a manual fallback when the
auto-open was dismissed or the URL appeared in an older message.

- url.ts: isLoopbackHttpUrl / extractLoopbackUrls helpers
- ChatContainer: module-level dedupe Set + effect on active session tail
- MarkdownRendererImpl: optional onPreviewLoopback in main renderer only
  (SimpleMarkdownRenderer for tool diffs is intentionally untouched)
- Reuses existing terminalView.preview.open i18n keys

* feat: preview enhancements, dev shutdown, and reliability fixes

Add preview start/stop UI in ContextPanel/Header, improve URL detection (Python HTTP server logs, trailing punctuation, IPv6 loopback), fix proxy path filtering to avoid disrupting non-preview WebSockets. Add dev-only /api/system/dev-shutdown endpoint and Header button to terminate local dev processes and orphaned preview servers. Improve terminal cleanup with process group killing, event pipeline reconnect backoff. Update file read APIs with optional flag and cache control. Add /api/system/free-port endpoint, detectDevServer.ts utility, and preview/shutdown i18n strings for 5 languages.

* fix: harden preview support

* fix: keep terminal toolbar interactive

* fix: keep expanded terminal below header

* fix: keep preview iframe under proxy path

* fix: respect project action preview urls

* fix: rewrite preview asset urls

* feat: capture preview console logs

* feat: annotate preview elements

* feat: attach preview annotation screenshots

* fix: improve proxied preview hmr

* feat: refine preview action UX

* fix: address preview review feedback

* fix: show auto-discover preview wait state

---------

Co-authored-by: William Biggers <will@Williams-MacBook-Pro.local>
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-04-30 00:03:38 +03:00
Bohdan Triapitsyn 17650becc0 release v1.9.10 2026-04-28 17:45:58 +03:00
Bohdan Triapitsyn 67f082988c release v1.9.9 2026-04-26 20:49:28 +03:00
Bohdan Triapitsyn a2dc5021bc fix: stabilize model and task UI hints
Keep thinking shortcut hint visible without layout shift
Restore JSON file icons across shared file lists
Use task status icons in the input status row
2026-04-26 14:27:06 +03:00
Bohdan Triapitsyn c94da0c1a0 release v1.9.8 2026-04-22 22:15:23 +03:00
Bohdan Triapitsyn 2a0aef89d5 release v1.9.7 2026-04-22 00:36:25 +03:00
Dave Otero d73edc672e Improve MCP settings auth flow, remote config support, and diagnostics UX (#953)
* feat: improve MCP settings auth workflow

* fix: complete MCP settings auth flow

* fix: harden MCP settings auth flow

* fix: add MCP settings refresh control

* fix: stabilize MCP authorization and status handling

* fix: clarify MCP advanced remote options toggle

* fix: improve MCP import and diagnostics

* feat: improve MCP settings panel visual hierarchy and UX

* fix: expose MCP auth actions in connected state

* fix: remove MCP import snippet helper text

* fix: address MCP review feedback

* fix: correct MCP page transport layout after rebase
2026-04-21 20:46:51 +03:00
Bohdan Triapitsyn 75c6277fa5 chore: update opencode sdk version 2026-04-20 16:02:54 +03:00
Bohdan Triapitsyn 285c3bcaae Migrate desktop shell from Tauri to Electron (#964)
* feat(electron): scaffold Electron desktop package

Main + preload + ssh manager, packaging scripts, icons, root build/lint/type-check wiring.

* feat(ui): add Electron runtime detection and desktopNative facade

isElectronShell via window.__OPENCHAMBER_ELECTRON__, isDesktopShell now covers both. desktopNative wraps window/title/theme calls so UI avoids direct Tauri imports. revealDesktopPath added.

* refactor(ui): route window/title/theme/export through desktopNative

SessionSidebar, MultiRunLauncher, useWindowTitle, ThemeSystemContext, exportSession drop direct @tauri-apps imports.

* refactor(ui): treat all desktop shells uniformly

device.ts switches Tauri-only checks to isDesktopShell. Header OpenInApp button uses actionDirectory so it falls back to the active project path.

* fix(ui): menu Copy clipboard fallback and softer sidebar tint

useMenuActions falls back to Clipboard API for the native Copy action when the page doesn't intercept. cssGenerator lowers sidebar strong/soft alpha so the tinted surface reads gentler.

* chore(electron): mirror Tauri build/type-check script shape

build script becomes no-op so root 'bun run build' skips packaging. Syntax validation (node --check) moves into type-check. electron:build root script still runs full sidecar+bundle+electron-builder.

* fix(electron): sync app identity, preload path, boot outcome, dev entry

Read version from packages/electron/package.json so 'electron ./main.mjs' dev entry reports the app version instead of Electron's. Bump electron package to 1.9.6 for workspace parity.
Resolve preload via app.getAppPath() in prod (bundle lives in dist-bundle while preload.mjs ships at app root).
Compute and inject __OPENCHAMBER_DESKTOP_BOOT_OUTCOME__ in main + preload so the loading gate dismisses (mirrors Tauri Rust injection).
Dev entry uses ./main.mjs to bypass the stale dist-bundle so source edits apply.

* refactor(open-in-app): split directory and file flows

Header button now opens the project/worktree directory only — drop activeFilePath prop and its Header prop passthrough. FilesView editor dropdown opens the active file only via new openDesktopFileInApp.

Electron main.mjs mirrors Tauri's open-chain logic: buildOpenProjectSpecs (finder/terminal direct, vscode-like via CLI -n, JetBrains via open -na --args) and buildOpenFileSpecs (finder -R reveal, terminal opens parent dir, editors via CLI or open -a). runSpecChain falls through specs until one exits 0.

* fix(files-view): keep floating toolbar mounted while its dropdowns are open

Portalled Base UI menu popups render outside floatingToolbarRef. The document mousedown listener and onMouseLeave collapsed the toolbar as soon as the popup appeared, unmounting the DropdownMenu root and swallowing clicks on its items. Track open dropdowns via onOpenChange and skip the collapse while count > 0; also ignore mousedowns that land inside a dropdown-menu-content/item.

* feat(electron): add quit confirmation with risk poller

Mirrors Tauri's macOS-only behavior: poll /api/openchamber/scheduled-tasks/status and /api/openchamber/tunnel/status every 5s. If active tunnel or running/enabled scheduled tasks are detected, Cmd+Q / dock Quit / menu Quit shows a native warning dialog listing reasons; otherwise quit proceeds silently.

performConfirmedQuit persists window state, kills sidecar, shuts down SSH, and fires a 1500ms unref'd safety timeout that calls app.exit(0) if the normal quit sequence stalls.

* feat(notifications): fix payload parsing, restore-on-click, session deep-link

Normalize input so both sidecar stdout path (flat) and UI IPC path ({ payload: {...} }) work; previous destructuring missed requireHidden (camelCase) and the payload wrapper so notifications showed with empty body.

Click handler restores the window if minimized, shows it if hidden, and focuses. When the notification payload carries sessionId, emit openchamber:open-session which the App listener routes to setCurrentSession — matches the PWA service-worker deep-link behavior. macOS notifications now also use sound 'Glass' for parity with Tauri.

* chore(electron): bump to Electron 41 + latest updater/context-menu

electron ^38.2.0 -> ^41.2.1
electron-updater ^6.6.2 -> ^6.8.3
electron-context-menu ^4.0.4 -> ^4.1.2

Dev boot verified: main process starts, preload exposes globals, API server + quit risk poller + autoUpdater all initialize without errors.

* fix: keep todo row alignment stable when expanding text

Keep checkbox and action buttons vertically centered in collapsed todo rows
Prevent first todo line from shifting when expanding to multiple lines

* fix: make commit highlights visible and input behavior reliable

Switch commit message field to native textarea for predictable auto-resize
Fix AI highlights append flow so inserted text is applied consistently
Make chat scroll-to-bottom control fully circular

* style: increase chat bubble corner radius consistency

Use larger radius for user chat message bubbles
Match chat input container radius to user message styling

* feat(electron): adopt OpenCode playbook improvements

mac: hardenedRuntime + entitlements.mac.plist + notarize + dmg.sign for Apple notarization parity.
single-instance lock + openchamber:// protocol with session/project/host routing (host switch done fully in main via activateMainWindow).
setAppUserModelId for Win toast identity; proxy-bypass-list switch; chdir(homedir) for Finder-launch cwd safety.
shell env probe (\$SHELL -il -> -l) merged into sidecar spawn; PATH deduped.
electron-log with 5MB rotation + 7-day cleanup; autoUpdater.logger wired; startup info log.
webContents zoom locked to 1 (zoom-changed + did-finish-load).
UI: openchamber:open-project -> useDirectoryStore.setDirectory.

* fix(electron): make bootOutcome mutable across re-navigation + project deep-link

host deep-link used to land on chooser because contextBridge exposed bootOutcome as read-only; initScript re-assignment became a silent no-op. drop preload's contextBridge for bootOutcome, inject it via main-world initScript, and move injection from did-finish-load to dom-ready so it lands before React mounts.

project deep-link updated currentDirectory only; activeProjectId stayed stale so the sidebar didn't highlight the new project. switch to projectsStore.setActiveProject (or addProject for new paths) which updates both.

add log.info around deep-link dispatch + host switch for diagnostics.

* fix(electron): desktop_hosts_set IPC args + persist initialHostChoiceCompleted + re-eval bootOutcome

UI calls invoke('desktop_hosts_set', { input: {...} }) but main was reading args.config — every onboarding 'i've completed installation' / host-dialog save wrote nothing, so desktopDefaultHostId stayed null and the chooser screen looped forever.

also:
- writeDesktopHostsConfig now persists desktopInitialHostChoiceCompleted so the tauri-compat flag survives writes.
- readDesktopHostsConfig returns initialHostChoiceCompleted so the UI-side config mirror is complete.
- after writing hosts, recompute state.bootOutcome + state.initScript; a subsequent window.location.reload() picks up target=local/status=ok via dom-ready injection without needing a full app restart.
- app.setName('OpenChamber') early (pre log.initialize) so electron-log logs land in ~/Library/Logs/OpenChamber/ instead of the package-derived '@openchamber/electron' path.

* chore(electron): rename appId to dev.openchamber.desktop

ai.opencode.* is the OpenCode team's reverse-DNS namespace; OpenChamber should not squat there. now that we're on Electron, drop the tauri-era inherited identifier and claim our own under openchamber.dev.

user-facing productName stays "OpenChamber". tauri identifier left as-is — legacy shell on the way out.

* feat(ci): add electron build+notarize+publish jobs to release workflow

three new jobs in release.yml, running in parallel with tauri:

- build-desktop-electron-macos: matrix(arm64, x86_64) on macos-26; installs Developer ID via keychain, runs build:sidecar + bundle:main + electron-builder --mac --arch <> --publish=never (with APPLE_ID / APPLE_APP_SPECIFIC_PASSWORD / APPLE_TEAM_ID env mapped from existing secrets). verifies hardened runtime, stapled notary ticket, required entitlements. uploads DMG/ZIP/blockmaps to the release and emits per-arch latest-mac.yml as a GH artifact.

- combine-electron-manifests: downloads latest-yml-*-apple-darwin artifacts, runs the existing finalize-latest-yml.mjs to merge per-arch files entries into a single latest-mac.yml, uploads combined yml to the release.

- finalize-release: now also waits on the two new jobs before flipping the draft release to published.

also: explicit artifactName in electron-builder config so arm64 and x64 dmg/zip never collide.

electron-updater in main.mjs (setFeedURL btriapitsyn/openchamber) fetches this latest-mac.yml on desktop_check_for_updates; downloadUpdate / quitAndInstall wire through our existing IPC handlers unchanged.

* docs: future-agent brief for tauri -> electron auto-update cutover

self-contained plan for the one-shot migration release that carries existing tauri installs into the electron shell via tauri's updater. written so a fresh agent with no branch context can execute it.

covers: the trick (repackage signed electron .app as a tauri tarball, minisign with existing TAURI_SIGNING_PRIVATE_KEY), workflow surgery on release.yml, rollback plan, validation steps against a real tauri install, and edge cases (CFBundleIdentifier change, notification perms re-prompt, deep-link re-registration).

* docs: soften framing of cutover playbook (no user-shaming)

* chore: mark electron as primary desktop shell; tune dmg installer window

AGENTS.md: explicit note that new desktop work lands in packages/electron/, packages/desktop/ (tauri) is maintenance-only until the cutover described in docs/TAURI_TO_ELECTRON_CUTOVER.md. updated runtime/entry-points/build-commands sections accordingly.

electron/package.json build.dmg: cleaner title ("OpenChamber 1.9.6" without -arch suffix), 660x400 window matching the tauri layout users are used to, icon size 128, explicit app/Applications positions.

* refactor(web): drop bun-specific runtime deps from server

- 11 test files migrated bun:test -> vitest; API (describe/it/expect) is drop-in; all 73 tests pass under vitest run.
- bun:sqlite -> better-sqlite3 in git/service.js::syncSandboxesToOpenCodeDb. api shift is db.query().get()/run() -> db.prepare().get()/run().
- add "test": "vitest run" script in packages/web.

no production code used Bun.* APIs; server is Express-on-Node already. this commit removes the remaining bun-runtime shape so the server module can be imported and booted inside an electron main process.

* feat(electron): boot web server in-process, drop sidecar subprocess

the electron main process now imports @openchamber/web/server/index.js as a workspace dependency and calls startWebUiServer({...}) directly. the returned handle exposes getPort() / stop() and the notification emitter takes an onDesktopNotification callback, so we no longer spawn a bun-compiled sidecar binary and no longer parse stdout for the one-line notify protocol.

- packages/electron/package.json: +@openchamber/web (workspace:*); extraResources drops 'sidecar'; build:sidecar script renamed to build:web-assets (kept the vite build step, dropped the bun compile step).
- packages/electron/main.mjs: remove spawn/kill-stale-sidecar/sidecar path resolver/stdout-prefix parser; rewrite spawnLocalServer to probe a free port (stored | DEFAULT_DESKTOP_PORT | OS-assigned) then import server and await startWebUiServer; killSidecar calls handle.stop({ exitProcess: false }); hoist user shell env (PATH, etc.) onto process.env once so opencode / git / rg children still inherit the expected runtime environment.
- packages/web/server/lib/notifications/emitter-runtime.js: accept an onDesktopNotification callback (late-bindable via setOnDesktopNotification). when set, notifications are dispatched through the callback instead of process.stdout; tauri path still uses stdout when no callback is bound.
- packages/web/server/index.js: main() wires options.onDesktopNotification to notificationEmitterRuntime.setOnDesktopNotification.
- release.yml + AGENTS.md updated for the new script name + runtime shape.

payoff: -300ms cold start on mac, single process in activity monitor, no stdio IPC, no bun binary in the packaged app. tauri sidecar path is untouched.

* build(electron): rebuild native deps explicitly, bump electron-builder

the previous build failed because electron-builder 24.13.3 tried to run \`bun rebuild\` on native deps (better-sqlite3, node-pty) and bun has no rebuild subcommand; it also couldn't find prebuild-install because bun hoists under node_modules/.bun/<pkg>@<ver>/ and never populates node_modules/.bin for transitive deps.

fix:
- bump electron-builder devDep to ^26, whose packageManager detection understands bun workspace layouts.
- add @electron/rebuild devDep + scripts/rebuild-native.mjs. the script rebuilds better-sqlite3 / node-pty / bun-pty against the installed electron version before electron-builder is invoked.
- set build.npmRebuild=false so electron-builder no longer attempts its own broken PM-based rebuild.
- package script: build:web-assets -> bundle:main -> rebuild:native -> electron-builder.

verified: CSC_IDENTITY_AUTO_DISCOVERY=false bun run electron:build produces signed-ad-hoc dmg/zip/blockmap/latest-mac.yml; artifacts land under packages/electron/dist as expected. cold-start from Applications should work (native bindings now match electron 41 node ABI).

* fix(electron): externalize web server + native deps from main bundle

the ESM bundle was statically inlining @openchamber/web transitively, which pulled in bun-pty/src/terminal.ts with its top-level \`import { dlopen } from "bun:ffi"\`. node's ESM loader parses every static import when the bundle loads, so the bun:ffi scheme crashed the packaged app at startup with ERR_UNSUPPORTED_ESM_URL_SCHEME — the runtime guard (if (globalThis.Bun) { await import('bun-pty') }) never got a chance to skip it.

fix: bundle-main.mjs marks @openchamber/web (+ its bun-pty / node-pty / better-sqlite3 transitives) as external. the dynamic \`await import('@openchamber/web/server/index.js')\` in main.mjs stays a runtime resolution; the conditional bun-pty import stays dynamic; native modules load from node_modules via the standard resolver.

* perf(web): classify UI-only deps as devDependencies, shrink packaged app

packages/web is a hybrid package: server code in server/, react UI source in src/, compiled UI output in dist/. the server serves dist/ as static files — it never imports react/radix/codemirror/etc. at runtime. but electron-builder, npm install, and similar tools treat everything under "dependencies" as shipping surface, so all of react + @radix-ui/* + @codemirror/* + @fontsource/* + @simplewebauthn/browser + cmdk + ghostty-web + ... were landing in app.asar even though the same code is already baked into dist/ chunks.

move ~24 UI-only packages to devDependencies. vite + its plugins still install them in dev (bun install fetches devDependencies in workspaces), so \`bun run build\` is unchanged. consumers doing \`npm install @openchamber/web\` no longer pull ~150MB of unused browser-side modules.

measured on aarch64 darwin build:
- app.asar: 281MB -> 44MB (-237MB, -84%)
- .dmg: 320MB -> 132MB (-59%)
- .zip: 305MB -> 129MB (-58%)

verified type-check, ui build, 73 vitest tests, packaged launch.

* chore(electron): center dmg installer icons, use cream brand background

dmg-builder 26 ignored our previous dmg.contents positions against its template background (they stayed at template coords, producing misalignment with the drawn arrow). switch to a solid backgroundColor (#FFFCF0, the splash light tone) so the template image is dropped entirely and our coordinates are authoritative. window tuned to 540x340, iconSize 100, iconTextSize 13.

dmgbuild treats contents coordinates as icon *centers* (not top-left), so with iconSize=100 in a 540 window, x=180 and x=360 place left and right clusters with equal 130px gaps on both sides of the window. y=140 vertically centres the icon+label pair.

* fix(electron): eliminate main-thread freezes in in-process server

Three blocking paths were running sync work on the Electron main event
loop, causing multi-second UI freezes under the new in-process server:

- package-manager.detectPackageManagerDetails fired spawnSync(pnpm/npm/
  yarn/bun bin -g) with 10s timeouts. In desktop runtime PM detection is
  pointless (app is .app bundle, updates via electron-updater) — short-
  circuit when OPENCHAMBER_RUNTIME=desktop. This was the ~5s freeze.
- buildInstalledApps iterated 22 OPEN_IN_APPS × spawnSync(mdfind, sips).
  Converted to execFile promises so child waits yield to the loop.
- orphan-project-file recovery re-scanned disk on every settings read
  (3+/s from fs/list/etc). Cache the outcome per process lifetime.

Also: resolveProjectDirectory prefers settings.lastDirectory over
activeProjectId so file-open from sidebar/chat doesn't 400 with
"Path is outside of active workspace" after the user navigates.

Plus dropdown typeahead fixes in DesktopHostSwitcher/BranchSelector:
stopPropagation on input keys so cmdk doesn't swallow typing.

* feat(electron): restore desktop LAN access for in-process server

spawnLocalServer now reads settings.desktopLanAccessEnabled and binds
on 0.0.0.0 when enabled, so phones/tablets on the same Wi-Fi can open
the app via http://<lan-ip>:<port>. Adds desktop_get_lan_address IPC
(UDP-connect route lookup with networkInterfaces fallback) for the
settings UI to show the reachable URL.

UI and settings plumbing already existed from the sidecar build; only
the Electron main-process wiring was missing.

* chore: added electron package to version bump script

* fix(electron): address PR review — harden IPC surface + polish

P1 security:
- Gate openchamber:invoke and openchamber:dialog:open by webContents
  origin. Only local (loopback / dev file://) senders can call desktop_*.
  Blocks remote hosts loaded via DesktopHostSwitcher from reading local
  files, opening apps, relaunching, etc.
- desktop_read_file now refuses paths outside $HOME / tmpdir and denies
  .ssh/.aws/.gnupg/.config/gh/credentials + .env/.pem/.key by name
  (defense-in-depth behind the origin gate).

P2:
- webPreferences.sandbox:false: add comment explaining preload needs Node
  (contextBridge+ipcRenderer) and why flipping to true would break IPC.
- desktop_set_vibrancy: comment the intentional no-op (no Electron
  equivalent for the Tauri NSVisualEffectView path), drop requiresRestart.
- desktopNative.ts: replace isTauriShell() guards with isDesktopShell()
  so the semantics match (previous check worked only because Electron
  preload exposes a __TAURI__ shim).
- AGENTS.md: correct entry description — server runs in-process, not as
  a sidecar subprocess.

* fix(electron): stop leaking desktop shell APIs to remote renderer pages

Preload was exposing __TAURI__ and __OPENCHAMBER_ELECTRON__ unconditionally,
so after DesktopHostSwitcher navigated the window to a remote OpenChamber
instance the remote UI saw isDesktopShell() === true and tried to invoke
desktop_* IPC. The main-process origin gate then threw "IPC not available
for this origin", surfacing as a user-visible error on the onboarding
screen of the remote.

Preload re-runs on cross-origin navigation; compute current origin up
front and only expose the shell globals + the openchamber:emit listener
when the document is loopback / state.localOrigin / file://. Remote
pages now look like a plain web runtime — no IPC path to reject.

* fix(electron): restore remote UI shell integration via per-command gate

Previous commit stripped __TAURI__ / __OPENCHAMBER_ELECTRON__ from remote
pages wholesale, which broke DesktopHostSwitcher for anyone switched to
a remote instance: no hosts list, "Unknown" probe status, open-in-new-
window dead. Also lost window chrome affordances that the remote UI
needs to render correctly inside the Electron shell.

Switch from an origin-level gate to a per-command allowlist:

- preload.mjs exposes __TAURI__ and __OPENCHAMBER_ELECTRON__ on every
  page (shell identity + IPC channel). __OPENCHAMBER_LOCAL_ORIGIN__ and
  __OPENCHAMBER_MACOS_MAJOR__ also go everywhere since HostSwitcher and
  window chrome depend on them and neither grants capability.
  __OPENCHAMBER_HOME__ stays local-only (leaks the OS username and is
  misleading if consumed as a workspace hint on a remote page).

- main.mjs ipcMain.handle accepts a curated COMMANDS_SAFE_FOR_REMOTE set
  (hosts_get, host_probe, new_window, new_window_at_url, set_window_*,
  is_window_fullscreen, start_window_drag, get_app_version,
  get_lan_address). Filesystem, shell.openPath, installed-apps scans,
  app relaunch, auto-update, hosts_set, dialog:open, read_file stay
  local-only — remote UI doesn't need them and can't weaponize them.

* ci(release): rebuild native modules against Electron ABI before packaging

Electron job skipped rebuild:native so bun install's Node-ABI builds of
better-sqlite3/node-pty/bun-pty shipped into the asar — packaged app
would crash on require. Local bun run package runs the step via
scripts/rebuild-native.mjs (npmRebuild is disabled in package.json);
mirror it in CI and pass ELECTRON_BUILDER_ARCH so the x64 matrix
cross-builds from the arm64 runner.

Tauri job untouched — both builds continue to produce side-by-side
release artifacts (latest.json for Tauri, latest-mac.yml for Electron)
so each shell's updater finds its own manifest.

* ci(release): split Electron arm64/x64 onto native macOS runners

Both Electron matrix entries were running on macos-26 (arm64) and
cross-building x64 from there. Works for Rust/Tauri; brittle for
native Node modules — better-sqlite3, node-pty, bun-pty (with its
rust-pty crate) each have their own cross-target quirks.

Pin arm64 → macos-14 and x64 → macos-13 so node-gyp and
@electron/rebuild build against the host arch. ELECTRON_BUILDER_ARCH
now just mirrors the runner for clarity.

* Revert "ci(release): split Electron arm64/x64 onto native macOS runners"

This reverts commit f217880e49609cf1418818af0f837b333dbb6f42.

* ci(test-build): add Electron DMG job to arm64 dispatch workflow

Parallel job to the existing Tauri DMG builder, same runner + Apple
cert path. Mirrors the release workflow steps (build:web-assets,
bundle:main, rebuild:native, electron-builder) so maintainers can
smoke-test a signed+notarized Electron DMG before merging.

* ci: use electron-builder v26 boolean arch flags

v26 dropped --arch <name> in favour of per-arch booleans (--arm64,
--x64, etc.). Test build was failing at dispatch time; release job
had the same bug latent. Switch both to the supported form.

* fix(electron): route external links to the system browser

<a href> clicks and window.open calls with non-local URLs were loading
inside the Electron BrowserWindow (or spawning a second Electron window
as a makeshift browser). Add an origin-aware navigation guard to each
window: loopback / state.localOrigin / configured desktop hosts keep
their existing in-window behaviour (HostSwitcher, in-window probes);
everything else hands off to shell.openExternal so http/https links
open in the user's default browser.
2026-04-20 15:41:15 +03:00
Bohdan Triapitsyn 4506c18f53 UI refresh: Base UI migration + flat tinted button language + mobile polish (#960)
* deps: bump @opencode-ai/sdk to 1.4.6 and add @base-ui/react

* build: align vite configs with Base UI and packages update

* feat(ui): migrate primitives to Base UI

Replace Radix-based wrappers (button, checkbox, collapsible, command,
dialog, dropdown-menu, input, radio, scroll-area, select, separator,
sonner, switch, textarea, toggle, tooltip) with @base-ui/react
equivalents and a refreshed visual language: flexoki theme tokens,
typography utilities, and a redesigned CommandPalette. Add new
fancy-button variant set and a local slot helper for asChild composition.

* refactor(ui consumers): adopt Base UI primitives across the app

Update chat, layout, git views, settings, multirun, session and agent
management screens to use the new Base UI-backed primitives (anchor
width token rename, dropdown/select/dialog usage adjustments, new
component APIs).

* fix(ScrollableOverlay): forward `disabled` to inner element

Add `disabled` to the type so it propagates via rest spread to the
underlying element (e.g. textarea in simple mode). Resolves a type
error reported after the main merge.

* refactor(dropdown consumers): migrate preventDefault sites to closeOnClick

Base UI's Menu.Item does not check `event.defaultPrevented` before
closing the menu, so the Radix-era `onSelect={(e) => e.preventDefault()}`
pattern no longer keeps the menu open. Replace those sites with the
Base UI `closeOnClick={false}` prop (VSCodeLayout rate-limit info
rows; AgentGroupDetail worktree actions).

* docs(AGENTS): note Base UI as primary source for UI primitives

* fix(ContextPanel): center expand/close buttons in tab header

Header (h-8) had the action row pinned with items-end + pb-1.5, which
placed the h-7 buttons ~2px above the header top and made them look
cropped. Switch to items-center so they sit on the same baseline as
the tabs.

* style(tabs): drop transitions on default variant, soften active pill

Default (underline) variant now switches instantly — no transform/width
transition on the underline indicator, no color transition on tab
buttons. Active pill bg is color-mixed down to 55% of
--interactive-selection so light-theme selection doesn't scream.

* style(button): pill-style elevation and introduce chip variant

Solid variants (default/destructive/neutral/primary/basic) now use the
active-pill shadow stack — hairline navy outer ring + stacked tight/soft
drops — instead of the pure-black inset/border approach. Outline
variant (and FancyButton basic) gets the same elevation without the
white top highlight (no fill to catch light).

Add a new chip variant: flat `border border-border/60` button without
elevation, intended for one-of-N toggle groups where a raised
button would read as the wrong affordance.

* style(header): apply pill-style shadow to OpenIn and Project Actions

Both split-button wrappers previously used a 1px CSS border for
definition. Replace that with the pill elevation stack so the header
buttons match the new Button variants and feel less hard-edged.

* refactor(toggles): migrate one-of-N segmented groups to chip variant

Color Mode, When-sessions-expire, voice/STT providers, auth method,
agent mode, MCP transport, tunnel type and terminal Ctrl/Cmd modifier
toggles all read as flat chip selectors rather than raised buttons.
Switch them from variant="outline" to the new chip variant so the
raised pill-style elevation doesn't fight the selected-state tint.

* fix(elevation): theme-adaptive pill-style shadow for dark mode

The navy (rgba(14,18,27,*)) outer ring was invisible on dark surfaces,
erasing the button/pill edge. Add dark: variants that use a subtle
white outer ring (rgba(255,255,255,0.10)) and darker drop shadows
(rgba(0,0,0,0.30/0.20)) so the elevation reads on both themes.

Applies to Button solid/outline variants, FancyButton equivalents,
the OpenIn and Project Actions header split-buttons, and the active
pill indicator in SortableTabsStrip.

* style(button): flat tinted variants with squircle corner-shape

Replace elevated pill-shadow design with a flat tinted language: pale tinted
fill + hairline tinted border + saturated tinted text for default/destructive.
Add CSS corner-shape: squircle with @supports gate so Chrome 136+ gets the
softened superellipse corners at 50px, Safari falls back to a slightly larger
round radius.

* style(tabs): replace active pill shadow with tinted border

* style(header): flat border and squircle corners for OpenIn / Actions

Drop pill-style shadow stacks in favor of a plain hairline border and apply
the same squircle corner-shape treatment as Button so header controls match
the new visual language.

* style(checkbox): drop filled background for tinted checkmark

Checked / indeterminate states now keep the transparent box, show a
primary-base check/minus glyph, and use a softened primary-tinted inset
border (50% mix) so the control reads as selected without a solid swatch.

* style(layout): widen sidebar-to-chat corner radius to 10px

* style(button): retune tinted variants and unify outline fill

- Bump primary fill toward noticeable but still soft (light 10/16/22,
  dark 16/22/30) and weaken both primary and destructive borders
  (12% light / 20% dark) so the tone reads without shouting.
- Further calm destructive across fill and border so revert-style
  actions match the muted reference.
- Align the outline variant fill with --surface-elevated so git-view
  generate/commit buttons share the header action button surface.

* style(tabs): align pill variant with new button language

Active pill now uses --surface-elevated fill and border-border/60 to
match the outline button. Track and tab/pill geometry adopt the same
squircle treatment as Button: 9-10px fallback with 50px radius under
@supports(corner-shape:squircle). Also tighten track padding (inline
2px, block 2px) and grow active tab height so the indicator fills
the track instead of floating inside it.

* refactor(theme): hardcode radius scale, drop per-theme override

Radius values are now fixed in the design system (flexoki-dark's scale
becomes the app default). Themes can no longer override --radius-* — the
config.radius block is removed from every theme JSON, dropped from the
Theme type, and no longer emitted by the CSS generator.

* chore(ui): lint/type cleanup across Base UI wrappers

* style(tabs): dial pill track background down to near-invisible

* refactor(chip): aria-pressed drives the selected tinted state

The chip variant now carries the tinted primary palette (same as the
default filled button) under aria-pressed, so consumers toggle selection
via a single aria attribute instead of repeating the border/text/bg
override classes. All existing chip consumers migrate to aria-pressed
and drop their hand-rolled selected className.

* chore(defaults): humanize variant label in thinking selector

* fix(diff): render all lines in single-file mode

Single-file mode wrapped PierreDiffViewer in its own layout=fill which
creates a nested virtual-root; combined with the outer CSS containment
on the wrapper, Pierre's virtualizer measured a clamped viewport and
stopped rendering lines past ~150. Mirror the stacked-mode approach:
hoist a ScrollableOverlay as the sole virtual root/content and render
PierreDiffViewer inline, so the shared virtualizer sees the real scroll
viewport and paints every line.

Also drops an unused cn import in SessionRetentionSettings.

* feat(files): collapse floating editor controls behind icon button

The floating file editor toolbar now starts as a single pill icon button
and expands into the full control row on hover or click. Clicking outside
or moving the pointer away collapses it back, keeping the editor surface
unobstructed while the file is being read/edited. Fullscreen overlay
keeps its always-visible toolbar.

* fix(chat): align mobile session status bar radius with chat input

* fix(mobile): keep radio/checkbox/switch at their native size

Mobile CSS forced min 36x36px on every <button>, which swallowed our
Radio and Checkbox primitives (also buttons with role=radio/checkbox)
and ballooned them on touch devices. Exclude role=radio/checkbox/switch
from the touch-target rule and pin the Radio/Checkbox box size with
arbitrary px so padding-scale overrides cannot stretch them either.

* chore(mobile): drop unused cornerRadius prop plumbing

The status bar now hardcodes var(--radius-lg) on both views, so the
cornerRadius prop and the useUIStore selector that sourced it in
ChatInput are dead weight.
2026-04-19 15:52:54 +03:00
Bohdan Triapitsyn 5710198864 release v1.9.6 2026-04-17 19:06:27 +03:00
Bohdan Triapitsyn f96ccc58c3 chore(deps): bump @opencode-ai/sdk to v1.4.6 2026-04-16 19:16:32 +03:00