* feat(chat): add message history navigation in ChatInput
* feat: Adds settng to persist draft of messages to local storage to save work if page refreshes or crashes
## 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
* feat(mobile): split controls into separate Agent and Model buttons
- Add MobileAgentButton with tap-to-cycle and long-press-to-open behavior
- Add MobileModelButton for direct model panel access
- Remove agent selection from UnifiedControlsDrawer for cleaner mobile UX
- Agent cycling accessible via Tab key shortcut
* fix(mobile): optimize controls layout and touch handling
---------
Co-authored-by: Jovines <jovines@qq.com>
* feat(mobile): add quick actions button and improve mobile UX
Add mobile-optimized quick action buttons and improve autocomplete UX.
- Add quick command button (/) in mobile chat footer
- Add tabbed autocomplete interface for Commands/Agents/Files
- Fix agent selector conflict with model selector
- Improve TerminalViewport Android IME support
- Add reusable Slider component for settings
- Update StatusChip styling for mobile layout
Files changed:
- ChatInput.tsx: Mobile quick actions, command menu, autocomplete tabs
- AgentMentionAutocomplete.tsx: Tab support for mobile
- CommandAutocomplete.tsx: Tab support and touch handling
- FileMentionAutocomplete.tsx: Tab support for mobile
- ModelControls.tsx: Agent selector fix
- StatusChip.tsx: Mobile layout improvements
- TerminalViewport.tsx: Android IME improvements
- ui/slider.tsx: New reusable slider component
* feat(chat): remove unused BrowserVoiceButton component from ChatInput
* fix: Resolve linter errors
Add TextSelectionMenu component that appears when selecting text in messages.
Provides quick actions: Add to chat, New session, Copy.
- Add TextSelectionMenu.tsx component with desktop and mobile layouts
- Integrate menu into MessageBody with message-content-text ref
- Add mobile CSS for text selection behavior
* feat(chat): support inline comment drafts in chat input
Add per-session inline comment draft tracking
Append inline drafts to outgoing messages when queuing
Consume drafts after sending to clear state
* feat: add actions menu to PierreDiffViewer
Add a dropdown actions menu in the diff viewer with More and Delete options
Enable inline comment drafting support and selection synchronization to prevent loops
Replace previous action icon with contextual actions in the header
* feat(plan): enable inline comment drafting in PlanView
Add inline comment draft support with addDraft, removeDraft and drafts state
Compute session key for drafts based on currentSessionId and draft state
Prepare dropdown and toast utilities for draft actions in PlanView
* feat: add inline comment formatting utilities
Introduce formatInlineCommentDraft to render inline comments for diff and plan views
Add appendInlineComments to attach comments to existing messages with proper separation
Provide hasInlineComments to detect inline comment blocks in text
* feat: add inline comment draft store
Persist drafts per session key for inline comments
Add, remove, and clear drafts with unique IDs per session
Consume drafts to move them out of store after submission
- stop button replaces send button in footer instead of overlay
- floating queue button above stop when input has content
- buttons same size, stop icon larger for emphasis
* 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
* chore: upgrade @opencode-ai/sdk to 1.1.40
* chore(ui): bump @opencode-ai/sdk to 1.1.40
* feat(chat): add mobile controls drawer and panel switching
Add mobile-only controls drawer with open/close and panel switching
Introduce state and handlers for mobile controls and panels
Reset mobile UI state when switching to non-mobile or returning to unified controls
* feat: add mobile chat controls utilities
Add utilities to compute and display the selected agent and model names in mobile chat controls.
Add effort variant formatting, serialization, parsing, and ranking helpers for quick options.
Expose helpers to build quick effort option lists for the UI
* feat(ModelControls): allow external mobile panel control
Initialize mobile panel state from external props when provided
Fall back to internal mobile panel state when external control is absent
Notify parent on panel changes via onMobilePanelChange callback
* feat(chat): add StatusChip component
Add StatusChip button that displays agent, model, and effort
Show marquee animation when the label is truncated
Bind to config and session stores to reflect current context
* feat: add UnifiedControlsDrawer chat controls
Add a side panel to switch agent, model, and effort quickly
Show recent agents and models for faster reselect
Persist agent/model/variant selections in session
* fix(openchamber): correct base64 to Uint8Array typing
* feat: add reduced-motion support for marquee animations
Add marquee-text--auto to enable continuous scrolling
Introduce prefers-reduced-motion media query to disable animations
Apply reduced-motion rules to active marquee and hover states
* feat[worktrees]: enhance sdk worktree removal with fallbacks
Add dynamic resolution of remove/delete/archive methods for worktrees
Fallback to delete or archive when remove is not available
Throw clear error when SDK version does not support worktree removal
* feat(ui): track recent agents and efforts in UI store
Add recentAgents array to UI state for quick access
Track up to 5 variants per provider/model in recentEfforts
Expose addRecentAgent and addRecentEffort actions to update history
* fix(deps): upgrade @opencode-ai/sdk to 1.1.42
Upgrade the OpenCode AI SDK to 1.1.42 across packages
Refresh lockfile entries to reflect the new SDK version and integrity hash
Ensure downstream packages consume the latest SDK and remain compatible
* refactor: simplify agent overflow logic in UnifiedControlsDrawer
* feat: re-organizaed input footer UI in chat
Add StopIcon component and use it for aborting generation in chat controls
Introduce ProviderLogoOrFallback to show provider logo or fallback icon in model controls
Remove mobile-only abort button in StatusRow and simplify abort handling
* feat: improved render of changelog in UpdateDialog
Add a centralized useEffectiveDirectory hook to resolve the active directory
Use the hook across DiffView, FilesView, GitView, and TerminalView
Fix list styling by rendering ul/ol bullets outside
* refactor: normalize permission rules and improve command draft handling
Add normalized rule set construction for permissions in Agents pages
Track initial command draft state to detect unsaved changes
Prefill draft fields when duplicating or selecting existing commands
* feat: add global session-level edit mode for agent permissions
* feat: display provider logos for favorite/recent models
Show provider logo next to model name in favorites and recents
Render provider logos in ModelControls, ModelMultiSelect, and ModelSelector lists
Maintain zero-logo state for other sections to avoid clutter
* feat: render user message as markdown instead of plain text
Render agent mentions as markdown links in user text
Apply inside list style for chat content to fix list rendering
Rely on SimpleMarkdownRenderer for consistent rendering
* fix(openchamber): adjust layout and overscroll behavior
Enable overscroll-auto on overlay containers for smoother scrolling
Move page content to full-width wrapper and preserve section borders
Show AboutSettings inside its own bordered block when visible
* feat: integrate GitHub auth status store and UI
Introduce GitHubAuthStore to track connection status and polling
Show GitHub avatar in header when connected
Guard issue/pr dialogs behind GitHub auth status and show notices
* feat: add GitHub multi-account support
Add API and UI flow to activate a GitHub account
Show and switch between multiple GitHub accounts in header
Persist and normalize accounts list with current selection
Introduce a hook to detect text truncation for dynamic UI updates
Replace static marquee spans with a reusable marquee component in files and labels
Enable copying of terminal selection on mouse up and touch end
* 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
* fix(server): improve external server and error handling
- Skip OpenCode shutdown when using external server
(OPENCODE_SKIP_START=true)
- Return 404 status for non-existent directories instead of
empty array
- Move error logging before response handling
* feat(ui): add configurable text justification activity setting
- Replace hardcoded ENABLE_TEXT_JUSTIFICATION_ACTIVITY constant with
dynamic showTextJustificationActivity setting in useUIStore
- Add toggle UI in Chat settings section (Settings > Chat)
- Add setting to DesktopSettings type for cross-runtime persistence
- Default value is false (matching original constant behavior)
---------
Co-authored-by: iiyangdianfeng <goodydfwow@gmail.com>
* feat: add plan/build mode switching and Plan view tab
Add PlanView view and integrate plan tab into main layout
Introduce plan_enter and plan_exit tools with icons and status strings
Persist per-session agent selections in context to reduce UI flicker during mode switches
* feat: enchanced plan discovery checks in header and plan view
* fix(ui): align PlanView and Header with sessionDirectory logic
Remove dependency on context store in header and compute sessionDirectory from session or currentDirectory
Compute display and repo paths using sessionDirectory for PlanView and update path resolution
Poll plan content every 3 seconds to reflect changes without heavy retries
* feat(ui): improve header tab switching and add copy buttons for plan
Switch header tab navigation to central UI store and auto-switch when plan is unavailable
Show checkmark icon after copying file contents or path with auto-hide timeout
Extend plan view path resolution to prefer session directory when present
* feat: add Apply Patch tool with patch input and diff view
Introduce Apply Patch tool with a patch input field and diff preview
Display target file path or file count for patch operations in tool UI
Update status label to show applying patch during execution
* fix: include apply_patch in editing tools detection
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
- Removed unused activeTurnAnchorId and activeTurnSpacerHeight from MemoryDebugPanel.
- Simplified useChatScrollManager by eliminating unnecessary state and functions related to active turn anchoring.
- Updated loadMessages function signatures to accept an optional limit parameter across message and session stores.
- Enhanced scroll behavior in useScrollEngine to support dynamic bottom tracking during animations.
- Improved event stream handling to allow for resyncing messages with a specified limit.
- Cleaned up session memory state to remove active turn properties, focusing on viewport anchoring.
* refactor(chat): enhance scroll management
Add bottom detection with epsilon tolerance for precise positioning
Introduce pending anchor restoration for seamless session switching
Include active turn anchor and spacer height in message store
- Add bottom detection with epsilon tolerance for precise positioning
- Introduce pending anchor restoration for seamless session switching
Include active turn anchor and spacer height in message store
* Stability and performance improvements. (#1)
## Changelog
### Performance Improvements
- **perf: make terminal creation cwd check async (#9)**
Replaced synchronous `fs.existsSync` with `fs.promises.access` in the terminal creation handler to prevent blocking the event loop.
Improves throughput under high load.
- **perf: optimize fuzzyMatchScore by avoiding redundant lowercasing (#8)**
Renamed `fuzzyMatchScore` to `fuzzyMatchScoreNormalized` and updated it to accept a pre-lowercased query, avoiding repeated string allocations.
Updated the call site in `searchFilesystemFiles`.
~25% search performance improvement on large datasets.
- **perf: use async file read in update-install handler (#7)**
Replaced `fs.readFileSync` with `await fs.promises.readFile` to avoid blocking the event loop.
**Benchmark (10k ops):**
- Sync: ~95ms (blocking)
- Async: ~1124ms (non-blocking)
Higher per-call overhead, but better server responsiveness.
- **perf(server): optimize mkdir endpoint with async fs (#6)**
Replaced `fs.mkdirSync` with `await fsPromises.mkdir` in `/api/fs/mkdir`.
Prevents event-loop blocking and improves concurrent performance.
**Result:** ~2× throughput improvement (100 concurrent requests).
- **perf: parallelize fs checks in validateProjectEntries (#4)**
Replaced serial `for...of` with `Promise.all + map`.
Reduced validation time for 500 projects from ~110ms to ~20ms (~5× speedup).
- **perf: cache getLoginShellPath result to avoid blocking event loop (#5)**
Cached `getLoginShellPath` result to avoid repeated `spawnSync` calls (~400ms each).
Subsequent calls reduced to <1ms.
---
### Server Fixes
- **fix(server): use async check for terminal restart endpoint (#3)**
Replaced `fs.existsSync` with `fs.promises.stat` in `/api/terminal/:sessionId/restart`.
Added directory validation for better robustness.
---
### UI & Accessibility
- **feat(ui): add aria-labels to git identities sidebar buttons (#1)**
- Added `aria-label="Create new profile"` to the create button
- Added `aria-label="Profile actions"` to the dropdown trigger
- Added `.Jules/palette.md` for UX/a11y learnings
---
### UI Performance (Bolt)
- **⚡ Bolt: Optimize MessageList re-renders by preserving referential equality (#2)**
- **⚡ Bolt: Optimize MessageList re-renders by preserving referential equality (#11)**
- **⚡ Bolt: Optimize MessageList re-renders by preserving referential equality (#12)**
* stability and improvements (#17)
* feat: add corner radius setting and update snackbar actions
- Add `cornerRadius` to UI store and settings.
- Add Corner Radius slider to Visual Settings section.
- Apply corner radius to ChatInput component.
- Remove default close button from Snackbar (Sonner).
- Add "OK" action button to session deletion toasts.
- Ensure `cornerRadius` setting is visible in OpenChamberPage.
* fix(ui): add missing aria-label to radius slider and verify functionality
- Added `aria-label="Corner radius in pixels"` to the desktop version of the corner radius slider for accessibility.
- Verified functionality and accessibility compliance via script.
* fix(ui): restore input bar offset setting on desktop
- Restored the Input Bar Offset setting to be visible on desktop, not just mobile.
- Verified both Corner Radius and Input Bar Offset sliders are accessible.
* Fix mobile layout for chat input controls (#16)
* Fix mobile layout for chat input controls
- Reduced horizontal gaps in mobile model controls.
- Added max-width constraints to model, variant, and agent labels on mobile to prevent overflow and cramping.
- Optimized spacing for mobile view.
* feat(git): auto-select gitmoji for generated commit messages
When the "Generate commit message" feature is used and gitmoji is enabled, automatically prepend the appropriate gitmoji based on the commit subject keywords.
- Added `KEYWORD_MAP` to map commit types to gitmojis.
- Added `matchGitmojiFromSubject` helper.
- Updated `handleGenerateCommitMessage` to apply the gitmoji.
* feat(git): auto-select gitmoji for generated commit messages
- Added `KEYWORD_MAP` to map commit types to gitmojis.
- Added `matchGitmojiFromSubject` helper.
- Updated `handleGenerateCommitMessage` to apply the gitmoji.
- Fixed mobile layout for model controls.
On iOS devices, when the keyboard is open, there's a home indicator bar
(approximately 34px tall) at the bottom of the keyboard that needs additional
safe area padding.
Changes:
- Add --oc-keyboard-home-indicator CSS variable
- Add ios-keyboard-safe-area class for keyboard open state
- Detect iOS userAgent and set keyboard home indicator height
- Apply ios-keyboard-safe-area class to ChatInput when keyboard is open
Co-authored-by: Jovines <jovines@qq.com>
* fix(upload): increase attachment size limit to 50mb
Raises body limits in web proxy and desktop app. Adds client-side size checks and better 413 error handling.
* fix(client): auto-compress large images to avoid server body limits
Adds client-side compression for images >1MB. Resizes to max 2048px and converts large PNGs to JPEG. Adds detailed logging for debugging.
* chore: remove debug logs from client and proxy