Adds item-level search inside Settings so users can find concrete settings like provider auth, agent mode, terminal font size, tunnel options, notification events, and similar controls instead of only filtering top-level pages.
Groups search results by Settings page and shows localized labels plus optional descriptions where useful.
Supports keyboard navigation with Arrow Up/Down, Enter, and Escape, matching the existing autocomplete interaction style.
Opens the correct Settings page or split-page draft state before scrolling to the matching control.
Highlights the matched setting with a subtle token-based background so users can see where they landed without an aggressive outline.
Adds explicit data-settings-item anchors across Settings pages and a centralized search registry with runtime/mobile availability guards.
Updates Settings UI skill guidance so future Settings changes keep search registry entries, anchors, localization, and availability guards in sync.
Added a mobile MCP overlay so MCP tools can be opened and managed from the mobile UI without relying on desktop-only dropdown behavior.
Improved mobile session panel touch handling so tapping the status/session area opens the right panel reliably on phones and tablets.
Cleaned up mobile usage provider metadata by removing duplicate rows, hiding unset providers, and showing provider logos consistently.
Added eager loading for provider logos used in mobile usage views to avoid delayed or missing icons when the panel opens.
Refined the mobile update and about flows in OpenChamber settings so release/update information is easier to read on small screens.
Adjusted related layout, header, VS Code layout, command palette, and settings text/localization details needed for the mobile polish.
Agent settings now correctly save an emptied system prompt instead of silently keeping the old text.
After saving agent, command, or skill settings, OpenChamber refreshes the updated configuration instead of showing stale values from a short-lived cache.
Reload actions now surface refresh failures consistently while avoiding noisy background errors.
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.
* feat(settings): add opencode plugins page
Manage opencode `plugin` array entries (npm, scoped npm, versioned,
local paths) and auto-loaded plugin files in `~/.config/opencode/plugins/`
and `<project>/.opencode/plugins/`. Mirrors MCP CRUD pattern.
- Server: `plugins.js` data layer + `plugin-routes.js` REST routes
- UI: PluginsSidebar / PluginsPage / AddPluginDialog
- Store: usePluginsStore (cache TTL, in-flight dedup, narrow selectors)
- i18n: 41 keys across 7 locales
Whitelist /api/config/plugins in JSON body-parser so POST/PATCH bodies
parse; opencode plugin specs runtime-resolve OPENCODE_CONFIG dir so
parallel test files do not cross-pollute module-frozen consts.
* feat(settings/plugins): hook npm registry for update + invalid-version detection
Plugins page now consults registry.npmjs.org with a 1h server cache. Sidebar
rows show an update badge with the latest version, group headers show how
many updates are available, the kebab adds an "Update to latest" action
that reuses the existing PATCH+restart flow, and the editor surfaces a
banner for update-available / missing-version / missing-package / malformed
/ missing-path / unreadable-path / offline-registry states. A refresh
button in the sidebar header forces a cache bypass.
- Server: `npm-registry.js` (cache + in-flight dedup + 5s timeout, 404
cached, network failures NOT cached) + `plugin-spec.js` (parser + exact
semver detection) + `GET /api/config/plugins/registry?specs=...&refresh=`
- Routes accept up to 100 specs/request, dedup by npm package name before
fetching, classify each result by kind, never propagate network failure
as 500.
- Client: `registryInfo` slice + `loadRegistryInfo` (fire-and-forget after
loadPlugins, refreshes on mutations) + `updateToLatest(id)`.
- UI: `RegistryBadge` per-row + `RegistryBanner` per-entry editor, both
use theme tokens (text-only color, no new bg/border tokens) and the
shared Icon sprite. Per-spec subscriptions only.
- i18n: 24 new keys (incl. split singular/plural for "N update(s)
available" because the runtime does not parse ICU plural format).
* fix(settings/plugins): keep registry badge visible for long specs
Sidebar entry row used `inline-flex` with `truncate` only on the spec
text. With long npm specs the badge could be pushed past the row edge
and clipped by the parent overflow. Switch to `flex` with spec
`flex-1 min-w-0 truncate` and add `shrink-0` to the badge wrapper so
the update indicator stays anchored to the right of the row.
* fix(settings/plugins): use code-box icon to distinguish from MCP
Plugins nav entry used 'plug' which is visually too close to MCP's
'plug-2' icon. Swap to 'code-box' for clearer differentiation in the
Settings nav list.
* Update packages/ui/src/components/sections/plugins/PluginsPage.tsx
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Signed-off-by: Quat3rnion <81202811+Quat3rnion@users.noreply.github.com>
* Update packages/ui/src/stores/usePluginsStore.ts
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Signed-off-by: Quat3rnion <81202811+Quat3rnion@users.noreply.github.com>
* fix(settings/plugins): validate registry directory + surface save errors
- registry endpoint: return 400 on invalid directory query (was silently falling back to homedir, breaking relative path specs)
- save failure toast: prefer result.message over generic 'Reload failed'
* fix(settings/plugins): address review follow-ups
---------
Signed-off-by: Quat3rnion <81202811+Quat3rnion@users.noreply.github.com>
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
Replace the prompt-template workflow with snippet support that is compatible with opencode snippet conventions. Snippets are now stored and loaded from global and project snippet directories, including legacy pluralized paths, with frontmatter metadata for aliases and descriptions. Snippet expansion supports recursive references plus prepend and append sections, while inject sections are treated as unsupported no-ops so OpenChamber remains compatible without requiring an external plugin.
Add the snippets settings experience and remove the old prompt-template settings surface. The new settings page and sidebar support creating, editing, deleting, selecting, and describing snippets, with localized copy across every supported locale. The settings navigation now exposes Snippets with a dedicated icon and metadata.
Wire snippets into all prompt-entry surfaces that need them. Chat, multi-run groups, and scheduled task prompts now offer hash-trigger snippet autocomplete and expand snippets before sending work to OpenCode. Chat also uses an adaptive compact placeholder on mobile or narrow composer widths so helper trigger guidance stays readable in constrained layouts.
Keep multi-run aligned with grouped prompts. Multi-run sessions now use a shared title builder that handles both legacy titles and the newer g1, g2 prompt-group title format. Fusion parsing now recognizes grouped multi-run titles, scopes fusion sources to the same prompt group, and creates fusion sessions under the matching group so outputs from different prompts are not mixed accidentally.
Harden the icon sprite pipeline. The sprite generator now discovers icon names used through typed icon maps, JSX icon props, IconName returns, and generated-value flows without scanning unrelated string literals or the generated sprite itself. The generated sprite is strictly typed so invalid icon names are caught by type checking, and existing invalid or unsafe icon references were cleaned up across settings, provider, Git identity, scheduled task, voice, header, and sidebar surfaces.
Update backend configuration routes and documentation for snippets. The OpenCode config route layer now exposes snippet CRUD and expansion endpoints, accepts JSON bodies for snippet writes, and removes the old prompt-template provider. Scheduled task runtime expansion now uses snippets before dispatching messages.
Add regression coverage for snippet storage and expansion, config-route JSON handling, and multi-run title parsing. Validated with full type checking, full linting, targeted multi-run title tests, and targeted OpenCode snippet/config route tests.
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
Use base-ui's transition-status pattern (data-starting-style /
data-ending-style) for Dialog, DropdownMenu, Select, Tooltip and Popover
so open/close animate consistently at 150ms ease-out without flicker.
Wrap dialog popups in a centered flex container so the scale animation
no longer fights with translate-based positioning, and switch
PendingChangesBar to a real Popover so it actually animates closed.
Convert ScheduledTaskEditorDialog from raw Radix to the shared Dialog
wrapper, give ScheduledTasksDialog a stable min-height to prevent layout
shift mid-animation, and reset NewWorktreeDialog form state on open
instead of close so fields don't empty during the close transition.
Drop tooltip delay from 700ms to 300ms globally and remove all
per-component overrides except the model/variant/agent selectors in the
chat input (600ms).
* fix(ui): center skills empty state and fix terminal toolbar overlap
Fix two UI layout issues in the settings and terminal views.
Skills empty state:
- Add h-full to the max-w-3xl content wrapper so flex centering works
- Make book icon responsive (h-10 w-10 sm:h-12 sm:w-12)
- Add px-4 padding for narrow viewports
Terminal toolbar:
- Reserve right padding (pr-20) for dock fullscreen/close buttons
- Remove pb-1 from quick keys to fix vertical misalignment
- Change shrink-0 to min-w-0 for graceful shrinking on narrow widths
* ref(ui): gate pr-20 padding on desktop runtime only
* fix(ui): keep settings sidebar open, center content, and fix worktree refresh
- Prevent settings nav from collapsing on split pages (Magic Prompts, Agents, etc.)
- Center settings content with max-w-3xl for better layout on wide screens
- Widen settings window from 960px to 1200px max-width
- Fix source branch dropdown overflow with max-height and scrolling
- Add worktreeRefreshNonce to trigger immediate sidebar refresh after worktree creation
* ref(ui): address Greptile review — remove dead isNavCollapsed code and split worktree effect
- Remove isNavCollapsed variable and all dead branches (resize handle, renderSettingsNav param, SETTINGS_NAV_RAIL_WIDTH)
- Split SessionSidebar effect so worktreeRefreshNonce only triggers discoverWorktrees, not refreshGlobalSessions
- Both type-check and lint pass
* fix(ui): refine settings and worktree dropdown behavior
---------
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
Remove body position:fixed lock, visualViewport listener in MainLayout
(keyboard-inset heuristics, scroll-to-zero lock, focusin/focusout RAF),
[data-keyboard-avoid-active] translateY rule, and related CSS vars
(--oc-keyboard-inset, --oc-keyboard-avoid-offset, --oc-keyboard-home-indicator,
--oc-visual-viewport-offset-top). Strip data-keyboard-avoid* attrs and
keyboardAvoid props from Dialog/ScrollableOverlay and consumers. Drop
isKeyboardOpen from useUIStore.
With body unlocked and the layout plain flex-col (h-dvh), the browser
shrinks the viewport on keyboard open naturally — composer sits above
the keyboard, header stops lagging, input no longer jitters.
FilesView.nudgeEditorSelectionAboveKeyboard now derives the occluded
bottom locally from visualViewport + documentElement.clientHeight.
* 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.
- Add configurable visible/instructions prompt families for commit/PR generation, PR checks/comments flows, and git conflict resolution helpers.
- Refactor prompt sending to explicit visible + synthetic parts instead of newline-based splitting, with legacy override migration for old keys.
- Polish Magic Prompts settings UX with grouped sidebar entries, tooltip-based descriptions, AI icon, and validation that visible prompts cannot be empty across web and VS Code runtimes.
Show Update button in sidebar only when an update is available
Support update button on mobile sidebar and remove duplicate mobile Settings entry
Force desktop Tauri recheck and preflight before download to avoid missing pending updates
* feat: add Cloudflare Tunnel settings for desktop app
Add a 'Remote Tunnel' section in Settings (desktop-only) that lets users
start/stop a Cloudflare quick tunnel on demand, with auto-generated
password protection and a QR code for easy mobile access.
- Server: 4 new API endpoints (check/status/start/stop) reusing the
existing cloudflare-tunnel module
- UI: TunnelSettings component with full state machine
(checking → idle/not-available → starting → active → stopping)
- QR code rendered via the qrcode package for in-app display
- Hidden from VS Code extension (desktop/web only)
* fix: use ?token= instead of ?p= in tunnel password URLs
REST API endpoints were building passwordUrl with ?p=<token> but
SessionAuthGate reads the ?token= query param, causing QR code
auto-login to fail — the password was never extracted from the URL.
Standardize all three tunnel URL construction sites to use ?token=
so scanning the QR code correctly pre-fills and submits the password.
* feat: secure remote tunnel access with one-time connect links
* feat: redesign remote tunnel settings and access flow
* fix: cleaned up unused desktop close code path
* feat: overhaul named tunnel setup and persistence flow
* chore: align codemirror language dependency resolution
---------
Co-authored-by: Brian-Hwang <brian.hwang@cornelisnetworks.com>
* feat(mobile): refactor drawer system with swipe gestures and improved status bar
- Add DrawerContext for centralized drawer state management
- Implement swipe gesture support for left/right drawers
- Refactor MobileSessionStatusBar with token usage indicator
- Update Header component with drawer toggle props
- Improve RightSidebar touch handling
- Add mobile-specific styling improvements
- Update UI store for drawer state management
* feat(mobile): update empty session hint text to clarify swipe gesture area
* fix(mobile): restore MobileAgentButton tap-to-cycle and long-press behavior
- Revert removal of tap-to-cycle agent switching functionality
- Re-add onCycleAgent prop and long-press detection (500ms)
- Click now cycles through primary agents, long-press opens selector panel
- Fixes regression from commit 569cc411
* feat(mobile): add project switcher bar and fix button interactions
- Add ProjectBar component to MobileSessionStatusBar for quick project switching
- Add useProjectStatus hook to track project-level session indicators
- Integrate project store with directory store for project management
- Add project status indicators (running/unread) to session status bar
- Fix MobileAgentButton pointer event handling with preventDefault
- Support horizontal scroll in project bar with touch gesture isolation
* feat(mobile): add long-press to remove projects and filter sessions by project
* fix: improve mobile agent switching UX
* fix: move drawer swipe hook to dedicated file
* refactor(mobile): simplify session status bar, remove More/Less toggle button
* fix: git diff navigation on mobile in sidebar mode
* feat(mobile): add configurable status bar and refine mobile chat controls
---------
Co-authored-by: Jovines <jovines@qq.com>
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
* refactor(settings): new IA shell + projects section + skills catalog discoverability
* chore(settings): split providers list by scope; show user before project
* fix: navigation flow in mobile Settings
* feat: redesign settings pages to use modern elevated surface patterns
* feat: replace helper text with tooltips in settings
* ui: redesign update dialog and fix external link routing
- Restructures UpdateDialog to focus on changelog readability with a wider max-w-4xl canvas
- Highlights @username contributor mentions with theme primary color
- Strips excessive vertical padding and right-aligns compact action buttons
- Disables streamdown's internal link safety dialog in favor of direct Tauri shell routing
* feat: refactor Git identities into dedicated Git settings page
* feat: unify sidebar background styling across VS Code and web/mobile
* fix: adjust button styling and layout for mobile settings pages
* feat: add MCP settings page and sidebar
* feat: hide models in provider view (thanks to @nguyenngothuong)
* feat: add "Add new provider" option to model selector dropdown
* fix: local evroc logo + provider dropdown icons
* fix: increase width of provider menu
* fix: dark theme background color for better contrast
* feat: update @opencode-ai/sdk dependency to v1.2.10
* fix: restore session sorting to only use updated time
* fix: added settings for sessions deletion dialog
* fix: adjust padding on settings pages for better layout
* fix: standardize select dropdown height across UI
* fix: agent selector UI and notification settings
* fix: remove redundant helper text from settings pages
* fix: update UI layout for description fields
* fix: remove border-none and shadow-none from textarea classes
* fix: enable context menu on sidebar items
* feat: refactor UI controls and layout patterns across settings pages
* fix: use headerless blocks when page title already provides context
* fix: remove subtask option from command settings
* fix: refactor mcp page settings
* fix: reduce spacing in skills configuration pages
* feat: refactor voice settings
* feat: refactor settings sidebar sections
* feat(session-folders): drag-to-folder DnD, sort by activity, and UX improvements
- Add DraggableSessionRow wrapping each session row so the whole row is
draggable; stopPropagation prevents outer group-reorder DnD from firing
- Add DroppableFolderWrapper + SessionFolderDndScope (inner DndContext
scoped per group) with closestCenter collision detection
- DragOverlay matches exact width/height of dragged row so cursor stays
aligned
- Folder header highlights (ring + primary colour) when a session hovers
over it during drag
- + button on folder header opens a dropdown: 'New session' / 'New folder'
- + button on each folder row creates a session scoped to that folder
- Empty folders are no longer auto-deleted (removed .filter(sessionIds.length>0)
from addSessionToFolder / removeSessionFromFolder / cleanupSessions)
- Sessions inside a folder are sorted by most-recent activity (same
compareSessionsByPinnedAndTime logic used everywhere else)
- Sort comparator now takes sessionAttentionStates so lastUserMessageAt /
lastStatusChangeAt is used when newer than session.time.updated; all
sort call-sites and their useMemo/useCallback deps updated accordingly
- Remove foldersMap from cleanup effect deps to prevent cascade re-renders
when folders change; read current value via getState() instead
* fix(session-folders): new session is placed into the correct folder
sendMessage() was calling useSessionManagementStore.createSession()
directly, bypassing the targetFolderId logic in useSessionStore.createSession.
Fix: read targetFolderId from draft at the top of the draft branch in
sendMessage, then call addSessionToFolder immediately after the session
is created and before the draft is closed. Also propagate targetFolderId
through openNewSessionDraft options and NewSessionDraftState type.
* feat(session-folders): add sub-folder support (one level deep)
- SessionFolder gains optional parentId field for hierarchy
- createFolder accepts parentId to create sub-folders
- deleteFolder cascades to remove all child sub-folders
- SessionFolderItem renders sub-folders before sessions in body;
new sub-folder button (RiFolderAddLine) visible at depth 0 only
- renderOneFolderItem in SessionSidebar builds the tree recursively;
sub-folders are indented via depth prop (ml-3 on root's children)
- Persist/hydrate parentId correctly from localStorage
* feat(session): add delete confirm dialogs and improve subtitle UX
- Add confirmation dialogs before deleting sessions or folders
- Show relative time (e.g., '2h ago', '35min ago') for recent sessions
- Replace +/- diff numbers with file change count (e.g., '3 files changed')
- New folders use default name without forcing rename
- Cleaner, less cluttered session list UI
* fix(session-folders): skip folder cleanup while sessions are loading
Prevents race condition on reload where cleanupSessions() runs before
the server returns the full session list, causing folder-session
assignments to be incorrectly wiped from localStorage.
* feat(mcp): add MCP Config Manager UI
- Backend: CRUD lib (mcp.js) + 5 REST routes (GET/POST/PATCH/DELETE /api/config/mcp/:name)
- Frontend: Zustand store (useMcpConfigStore), McpSidebar with status dots, McpPage with redesigned UX
- Textarea command editor: paste full shell commands, auto-split into args, one-arg-per-line view
- Compact env editor: wide value column, show/hide toggle, paste .env format support
- Header card: name, type badge, enabled toggle, connect/disconnect button
- Navigation: 'mcp' added to sidebar sections in SettingsView
- TypeScript: all packages pass type-check clean
* fix(mcp): remove constant truthiness lint error in McpPage
Replace '(isNewServer || true) &&' with unconditional render — type
selector should always be visible so the user can switch between
stdio and remote without recreating the server.
* fix: add MCP server management to VS Code backend
- Implement CRUD operations for MCP servers via bridge API
- Support local and remote MCP server configurations with validation
- Add VS Code webview endpoints for MCP server management
* feat: add project-level MCP server configuration
---------
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
## What / Why
This PR finishes the desktop refactor: the Tauri app is now a thin shell that launches the web server as a sidecar and loads the UI from `http://127.0.0.1:<port>`. All real backend logic lives in `packages/web/server/index.js`; desktop Rust keeps only native integrations (menu/dialog/notifications/updater/deep-link + window chrome).
This unblocks:
- consistent behavior across web/desktop/vscode (single backend)
- simpler desktop maintenance (no duplicated Rust backend)
- host switching between Local + remote instances in desktop
- reliable cold-start behavior on slow machines (VSCode + desktop)
## Key changes
- Desktop sidecar runtime
- build pipeline to bundle web dist + `openchamber-server` sidecar (`packages/desktop/scripts/build-sidecar.mjs`)
- robust local port selection (prefer saved/default, fallback to random; persisted in `~/.config/openchamber/settings.json`)
- improved PATH handling so the sidecar can locate `opencode` CLI (incl `~/.opencode/bin`, overrides, common bins)
- disable native right-click context menu in production builds (dev keeps it)
- Desktop instance switcher (Tauri-only)
- header button + modal to add/edit/delete remote hosts, set default, probe status/ping, switch back to Local escape hatch
- auth gate includes host switcher so you can recover when a remote host is broken/auth-required
- host list stored desktop-locally (not tied to the currently selected remote server)
- Notifications
- decision logic moved server-side; desktop notifications emitted via sidecar stdout and shown natively by Tauri
- prevent double-notifications on desktop Local origin (UI ignores SSE notification when native path is active)
- restore macOS notification sound
- Updates
- Tauri updater used only when viewing Local instance in desktop shell (avoid “remote web update” triggering desktop restart)
- Settings persistence & UX polish
- persist model favorites/recents via `/api/config/settings` (works for web + desktop; not origin-dependent)
- persist per-project sidebar collapse state in `projects[].sidebarCollapsed` via `/api/config/settings` (with debounce on toggles)
- macOS header sizing/traffic-lights offsets fixed (marketing macOS major injected from desktop; MultiRun header aligned)
- VSCode cold-start: keep retrying provider/agent loads after connection to avoid empty UI on slow machines
- misc lint/type fixes + bun.lock sync
- Desktop bootstrap / resiliency
- show onboarding screen when OpenCode CLI is missing (desktop Local origin), with retry hook to restart OpenCode after install
## Testing notes
- Desktop (macOS): switch Local <-> remote, set default host, verify auth gate recovery, native notifications (with sound), updater gated to Local
- Web: favorites/recents + per-project collapsed state persist across reload/restart
- VSCode: slow startup no longer results in missing providers/agents/models
- Open Settings as a centered windowed dialog on desktop with a blurred backdrop
- Update SettingsView to support windowed mode and adjust Mac titlebar padding for windowed use
- Export SettingsWindow and switch MainLayout to use it instead of full-screen overlay
* feat(openchamber): persist usage auto-refresh settings
Enable a switch to toggle automatic usage refresh
Provide input to configure refresh interval in milliseconds
Persist changes to desktop settings and server API when changed
* feat: add UsageCard component
Add a new UsageCard component to display a usage window with title, optional subtitle, and a progress bar
Show current usage percentage and a reset time label for the window
Render a formatted window label and a compact subtitle for concise UI
* feat(usage): add UsagePage UI for quota usage
Add a dedicated UsagePage with provider-based usage details
Show last updated time and auto-refresh status
Handle empty, not configured, and error states with informative banners
* feat(usage): add UsageProgressBar component
Introduce a new UsageProgressBar component to visualize quota usage
Display a gradient fill that changes with critical, warn, or normal tones
Expose accessible progress attributes for screen readers
* feat(usage): add UsageSidebar component
Display quotas for all providers in a scrollable sidebar
Refresh quotas with a button and loading indicator
Colorize provider rows based on usage status and runtime context
* feat: add usage section to Settings
Add a new Usage item to the Settings sidebar for desktop and mobile
Render UsagePage when the Usage tab is selected in Settings
Wire up new UsageSidebar and UsagePage components under usage
* feat: add Usage section to sidebar
Add new Usage section in the sidebar for API quota monitoring.
Display a bar chart icon and description for the Usage item.
Monitor and display API quota usage across providers.
* feat(desktop): add usage auto-refresh settings
Enable automatic refresh for usage data with new settings
Store refresh interval in milliseconds for usage updates
* feat(persistence): support usageAutoRefresh and usageRefreshIntervalMs
Persist usageAutoRefresh in desktop settings
Persist usageRefreshIntervalMs in desktop settings
Validate types for new fields during sanitizeWebSettings
* feat(quota): export providers and utilities
Expose QUOTA_PROVIDERS and QUOTA_PROVIDER_MAP for consumers
Export QuotaProviderMeta type for user code
Make formatting and usage resolution utilities available from quota module
* feat(quota): define base quota provider interface
Define QuotaProvider interface with id, name, isConfigured, and fetchQuota
Expose ProviderResult type in fetchQuota contract
* feat: add quota providers index and map
Expose QUOTA_PROVIDERS with OpenAI, Google and z.ai
Provide QUOTA_PROVIDER_MAP for quick provider lookup by id
* feat: add quota utils for percent formatting and tone
Add clampPercent to sanitize and clamp numbers to 0-100
Add formatPercent to render '-' for null and 'x%' for values
Add resolveUsageTone to categorize percent as safe, warn, or critical
* feat: add useQuotaStore for quota data
Load usage settings from desktop, VSCode, or API at startup
Fetch quotas for all providers in parallel and update loading state
Expose lastUpdated timestamp and error state for UI feedback
* feat: export quota types from quota module
Expose quota-related types in UI type definitions
Allow downstream code to import QuotaProviderId and related types
Aggregate quota exports under the quota module in index
* feat: add quota types for usage providers
Add QuotaProviderId and UsageWindow shapes to model quota data
Add ProviderUsage, ProviderResult, and related usage mapping for providers
* feat: add quota provider endpoints API
List available quota providers via GET /api/quota/providers
Retrieve quota details for a specific provider with GET /api/quota/:providerId
Log errors and return 500 with error message on quota fetch failures
* feat: add quota providers discovery and formatting
Detect configured quota providers from auth and account files
Normalize auth entries to tokens or objects for API usage
Expose formatted reset times and remaining window metrics
* feat: persist usage settings in UsageSidebar and remove from defaults
Load usage settings on mount for the sidebar
Persist changes to auto-refresh and refresh interval to server
Remove usage settings state and effects from DefaultsSettings
* fix(usage): guard auto-select in UsagePage when results empty
Guard auto-select in UsagePage when results are empty
Prevent unexpected provider selection on initial render
* fix(quota): set error state during quota updates
Reset error to null when a new quota result is added
Set error to the error message on fetch failure or fallback
Keep error state alongside results in all update paths
* feat: added themes system
* feat: smart sidebar auto-hide for files/diff tabs + lower files sidebar threshold
* feat: added Checkbox component and update theming
- Add reusable Checkbox component for toggles across UI
- Replace several inputs with Checkbox in settings and commands panels
- Add DiffIcon and apply surface/border theming to key UI areas
* feat: Add convert-vscode-theme.cjs to convert VS Code themes to OpenChamber format
* refactor: remove unused permission logic from ChatInput
- Remove unused permission rules parsing logic from ChatInput
- Memoize renderTheme in DiffWorkerProvider to avoid unnecessary recalculations
- Remove forceOpaque helper in vscode theme adapter
* fix: guard VSCode theme loading in MarkdownRenderer
* feat: add custom user themes loading and reload
- Load user themes from ~/.config/openchamber/themes at runtime
- Expose /api/config/themes to fetch custom themes
- Allow theme reloading from Settings → Theme → Reload themes in the UI
* feat: add chat virtualization and turn grouping infrastructure
Implement VirtualMessageList with virtualization and overscan for smooth scrolling
Refactor MessageList to render only visible messages and pass scroll refs
Introduce TurnGroupingContext and provider to stabilize per-turn rendering and reduce re-renders
* feat: add web server session activity endpoint for visibility restore
Add /api/session-activity endpoint to expose tracked session activity
Use web server activity first when restoring visibility, with fallback to global status
Update server SSE to set session phase on activity events
* feat(chat): split TurnGroupingContext into UI/Streaming contexts
Add separate UI state and streaming contexts to reduce re-renders.
Skip animations for items visible in collapsed view when expanding.
Track shown parts in collapsed mode to fade in progressively
* feat(ui): cache turn groups with expand state and guard web activity
Add isExpanded to the turn cache key to trigger updates correctly
Expose isGroupExpanded from UI state so groups reflect expand/collapse
Limit web server session activity fetch to web runtime only
Add file management API and UI components
Implement directory listing, search and CRUD operations in desktop backend
Expose new Files API on frontend to list, search, and modify files
* feat: add Files tab for browsing workspace files
- Add Files tab between Diff and Terminal in header
- Implement hierarchical file tree with expand/collapse
- Add fuzzy search with debouncing and relevance ranking
- Support gitignore filtering via `git check-ignore` (web + desktop)
- Add syntax highlighting for 150+ file types
- Add image preview (SVG, PNG, JPG, etc.)
- Add line numbers, wrap toggle, and copy button
- Desktop: split-pane layout matching DiffView
- Mobile: drill-in navigation with full-width sidebar
- Update help dialog with Cmd+3 shortcut
- Increase header breakpoint to 940px for new tab
* feat: enhance context and session stores to track agent/model/variant choices for historical sessions
* feat: implement line selection and commenting functionality in FilesView
* feat: Implement project management store with project path validation and synchronization
- Added `useProjectsStore` for managing projects, including adding, removing, renaming, and validating project paths.
- Implemented persistence for projects and active project ID using safe storage.
- Introduced synchronization from desktop settings to keep project data consistent.
- Enhanced session store to manage sessions by directory and added new methods for session management.
- Updated todo store to fetch session todos based on the directory context.
- Refactored server code to validate and resolve project directories for various API endpoints.
- Added project entry validation and sanitization to ensure data integrity.
* feat(settings): migrate legacy project settings and update settings loading logic
* feat: enhance project management with directory-aware settings and improved agent/command source handling
* feat: enhance session and project management with directory-aware settings and improved configuration refresh logic
* feat: enhance project management with worktree manager integration and project directory resolution
* feat: enhance agent groups store with project directory resolution and loading logic
* feat: add heartbeat management and global wrapping for SSE blocks in agent and chat providers
* feat: refactor command and project handling in useCommandsStore
- Replaced useDirectoryStore with useProjectsStore to manage project paths.
- Introduced getRequestDirectory function to determine the active project directory.
- Updated command fetching to respect project-level scoping.
- Enhanced error handling and logging for command configuration fetching.
- Improved command configuration saving and updating to utilize project directory context.
feat: enhance project path normalization in useProjectsStore
- Added resolveTildePath function to expand paths starting with ~.
- Updated normalizeProjectPath to utilize home directory for path expansion.
fix: update permission handling in useSessionStore
- Changed Permission type to PermissionRequest for clarity.
- Updated respondToPermission method to use requestId instead of permissionId.
refactor: improve permission utilities
- Introduced types for PermissionAction and PermissionRule.
- Enhanced getAgentDefinition and resolveConfigStore functions for better type safety.
- Added resolvePermissionAction to streamline permission resolution logic.
feat: add agent configuration retrieval endpoint
- Implemented new API endpoint to fetch agent configuration based on project directory.
- Enhanced getAgentPermissionSource to prioritize project-level permissions.
chore: update SDK version in package.json files
- Bumped @opencode-ai/sdk version to ^1.1.1 across all relevant package.json files.
refactor: streamline bridge message handling
- Updated handleBridgeMessage to accept directory parameter for agent and command requests.
- Improved local API request handling to extract directory from query parameters and headers.
feat: enhance project configuration management
- Added functions to retrieve and merge project configuration paths.
- Improved handling of existing project configuration files for agents and commands.
* feat: enhance VSCode integration and session management
- Added support for a sticky sidebar header background in light and dark themes.
- Introduced functions to read VSCode workspace directory and check if running in VSCode.
- Implemented detailed logging for session loading and creation processes.
- Enhanced session filtering based on directory structure and canonical paths.
- Added a new method to reorder projects and prevent modifications in VSCode workspace.
- Improved error handling and logging for app initialization and markdown file parsing.
- Updated API checks and health checks to ensure readiness before proceeding.
- Refactored code for better readability and maintainability across various modules.
* feat: improve agent and branch selection logic, enhance session management, and update multi-run creation response
* feat: add worktree management actions in agent group detail and sidebar, including delete and keep only options
* fix(ui): share IME guard and cover multi-run
* fix(session): reduce maximum visible sessions in group from 7 to 5
* feat(ui): update keyboard shortcuts to use platform-appropriate modifier keys
* fix(macos): update keyboard shortcuts to use Cmd modifier
Change Command Palette shortcut from Ctrl+X to Cmd+K
Update all menu shortcuts to use Cmd instead of Ctrl on macOS
* Fix linter issue
- Added skill scope helpers and CRUD operations for skills in opencodeConfig.ts.
- Introduced API endpoints for skill management in main.tsx and index.js.
- Enhanced server-side logic to support skill discovery, creation, updating, and deletion.
- Implemented supporting file operations for skills, including reading, writing, and deleting files.
- Updated package.json to use the latest version of @opencode-ai/sdk.