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
* fix: restore scroll position when switching chat sessions
When switching between chat sessions, scroll position now restores to
where the user left off instead of always jumping to the bottom.
- Save pixel-level scrollPosition (scrollTop/scrollHeight/clientHeight)
in viewport store on every scroll event
- Add restoreSavedScrollPosition to timeline controller for ratio-based
restoration (handles content size changes between visits)
- Suppress intermediate scroll events during session transition with an
explicit flag, cleared deterministically after restore completes
- Cancel in-flight animations/follow-loops on session switch
- Preserve scrollPosition when session-ui-store rebuilds SessionMemoryState
* fix: keep streaming sessions pinned on restore
---------
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.
* fix: unify startup logo and loading theme behavior
Show desktop window immediately with animated splash logo
Align splash/logo colors with selected app theme and default themes
Keep auth loading state on full-screen logo without size jump
Make project SVG icons follow active app theme
Use the active theme foreground color for project icons discovered from favicons
Apply server-side SVG color overrides for currentColor icons via icon request params
Keep non-SVG project icons unchanged while preserving existing fallback behavior
* perf: speed up desktop startup and unify loading logo visuals
Desktop startup now shows UI sooner while backend boot continues in background
Startup host probing uses a faster local path with safer remote fallback retries
OpenChamber logo cube highlights now match splash screens consistently
* fix: keep macOS traffic-light buttons in the correct position on load
Stop native window title updates on macOS during app initialization
Prevent title bar relayout that reset custom traffic-light positioning
* fix: recover missing providers and agents after fast startup
Retries provider/agent loading when connection is up but core config is still empty
Prevents cold-start state where models/agents appear only after manual project switch
Keeps startup responsive with throttled background recovery in app bootstrap
* 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>
## 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
- Implement rate limiting for login attempts (10 attempts per 5min window, 15min lockout)
- Add Retry-After header and 429 response for rate-limited requests
- UI shows rate-limited state with countdown message
- Separate limits for identified IPs vs unknown clients (3 attempts without IP)
- Add cleanup mechanism for stale rate limit records
Co-authored-by: Jovines <jovines@qq.com>
* feat: add QR code and password URL parameter for Cloudflare tunnel
Add --tunnel-qr flag to generate scannable QR code for tunnel URL in terminal.
Add --tunnel-password-url flag to include password as URL parameter for auto-login.
Frontend now automatically detects and submits password token from URL.
* docs: update README with tunnel QR and password URL features
Add dev script for concurrent server, web, and UI watch mode.