Commit Graph
200 Commits
Author SHA1 Message Date
Tom RochetteandBohdan Triapitsyn 7b1b3167a4 feat: server-side GitHub search for issue/PR pickers (#1352)
Replace local-only filtering in GitHub issue/PR picker dialogs with
server-side GitHub Search API queries. Search text is sent as a query
parameter to the server, which uses the GitHub Search API
(issuesAndPullRequests endpoint) with repo: qualifiers including fork
network support. Results are debounced at 350ms to respect API rate
limits.

- Add query parameter to GitHubAPI issuesList/prsList interface
- Server routes use Search API when query is present, standard list
  endpoint when absent
- Fork networks handled via repo:owner/repo OR repo:owner/upstream
- PR search fetches full PR details after Search API for head/base/draft
  fields
- Remove local filter memos from all three picker dialogs
- Add debounced search effect with abort controller cleanup
- Update VS Code backend and webview API for parity
- Update search placeholders in all locales

Closes #1350

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-06-08 15:37:42 +03:00
JakeandBohdan Triapitsyn 5589ca991a feat(vscode): add archive all sessions action (#1262)
* feat(vscode): add archive all sessions action

* Update packages/ui/src/components/layout/VSCodeLayout.tsx

Signed-off-by: Jake <101855602+jjdubski@users.noreply.github.com>

* fix: Fail-open default archives all sessions when workspace can't be resolved

* fix: Already-archived descendants are included in the archive call

* fix: add i18n for the newly added toasts

* Restore icon name from 'add-line' to 'add'

Signed-off-by: Jake <101855602+jjdubski@users.noreply.github.com>

* update: fix merge conflict and update code structure into components

---------

Signed-off-by: Jake <101855602+jjdubski@users.noreply.github.com>
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-06-08 15:11:05 +03:00
Bohdan Triapitsyn e0113c637d feat: support fast worktree-backed session flows
Add a directory-created fast path for worktree creation so session and send flows can continue once the target directory exists while Git attachment and bootstrap finish in the background.

Track bootstrap status explicitly in shared UI contracts, including pending, ready, and failed states. Background watchers now surface failures and timeouts, update stored worktree metadata, and keep web and VS Code runtime behavior in parity.

Move GitHub issue/PR worktree sessions and assistant-answer fork sessions onto the unified send path so provider, model, agent, and variant selections are preserved. The assistant-answer fork dialog can optionally create a worktree outside VS Code.

Make worktree deletion dialogs close after linked-session cleanup while removing the worktree in the background, and clean up failed fast-create artifacts safely without recursively deleting user or agent-written files.

Validation: bun test packages/ui/src/lib/worktrees/worktreeBootstrap.test.ts packages/ui/src/lib/worktrees/worktreeManager.test.ts; bun run type-check; bun run lint.
2026-06-06 23:25:39 +03:00
Bohdan Triapitsyn 48b0a4f292 fix: preserve chat folder assignments on reload
Prevents folder cleanup before sessions finish loading
Keeps saved chat folder memberships after desktop reload
2026-06-05 20:58:54 +03:00
Bohdan Triapitsyn e192359da9 fix: improve startup readiness performance 2026-06-05 15:09:24 +03:00
Bohdan Triapitsyn f40be80c4b fix: default project directory selection to not show hidden files
Show hidden starts disabled when opening Add project directory
Keeps the setting local to the dialog
2026-06-04 18:49:37 +03:00
Bohdan Triapitsyn 837a47b6a3 fix: keep session titles and lists in sync
Update sidebar titles from session events
Load all session pages instead of partial lists
Refresh sessions only for newly added project directories
2026-06-03 18:43:13 +03:00
Bohdan Triapitsyn 874dc15263 fix: respect time format preference in UI
Add shared time formatting helpers that apply the Appearance time format preference consistently. Update visible time labels across chat, quota usage, scheduled tasks, tunnels, context details, git views, PR metadata, and passkey settings to use the selected 12-hour, 24-hour, or automatic format. Leave date-only and non-UI formatting untouched so unrelated behavior does not change.
2026-06-03 18:20:35 +03:00
Bohdan Triapitsyn 570ae9dbc8 fix: keep worktree sessions in the right group
Prevents stale worktree lists from overwriting newly created worktrees
Uses the created worktree path when selecting linked worktree sessions
2026-06-03 15:16:36 +03:00
Bohdan Triapitsyn a0b22c9960 fix: show recent sessions in project groups
Keeps Recent as an extra shortcut list
Restores sessions under their project and worktree groups
2026-06-03 14:51:17 +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 2f3d5107e8 feat: improve session sidebar archive and pagination controls
Add a toggle for displaying archived sessions in the sidebar menu
Load more sessions in smaller increments
Reset expanded session counts when collapsing groups or projects
2026-06-02 13:25:28 +03:00
Bohdan Triapitsyn 6a88cd09cc feat: add dialog for "Start new session from this answer" (#1501)
* feat: add dialog for "Start new session from this answer"

Replace the one-click fork action on assistant messages with a dialog
(ForkSessionDialog) that lets the user pick model, thinking level, and
agent, plus edit the instructions sent to the new session.

The instructions field is prefilled with the previous fixed fork prompt
and is mandatory. The composed message is now fully visible (no synthetic
preface): the user's instructions sit above a short fixed connective that
opens the assistant content. createSessionFromAssistantMessage takes the
chosen execution params instead of reading from config.

Also fix TodoSendDialog visuals: narrower vertical layout, model trigger
no longer stretches with centered text, and the agent/thinking dropdowns
portal to body so opening them no longer nudges the dialog height. Extract
the shared ThinkingPill into its own component.

* fix: address review feedback on fork session dialog

- Fix "bellow" -> "below" typo in the fork content preface (now user-visible
  since the message is no longer synthetic)
- Reset ForkSessionDialog state only on open transition, reading the config
  store snapshot via getState() so background store refreshes can't discard
  in-progress instruction edits
2026-06-02 12:53:30 +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 32ab46c567 feat: refine VS Code session UI 2026-05-28 01:13:03 +03:00
Robert HamiltonandBohdan Triapitsyn cc3d1bd63c fix: improve todo send dialog model picker (#1426)
* fix: improve todo send dialog model picker

* fix: truncate long model selector labels

---------

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-05-26 20:46:30 +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
youfch cc7969ac00 fix: session rename exits immediately due to focus race (#1429)
* fix: session rename exits immediately due to focus race

Replace fragile onBlur handler with document mousedown listener for click-outside detection. Defer dropdown-initiated rename to onOpenChangeComplete (fires after CSS transition + cleanup). Use finalFocus instead of dead onCloseAutoFocus (never forwarded to Base UI).

* chore: remove dead editCancelledRef after onBlur removal

onBlur was replaced by document mousedown listener. editCancelledRef was only read in the old onBlur handler — its write in the Escape key handler was the sole remaining use.
2026-05-26 11:50:16 +03:00
kostazolandKonstantin Zolin 4c34031f8d Keep completed todos at the end (#1430)
Co-authored-by: Konstantin Zolin <zolin_ka@vk.com>
2026-05-26 11:49:30 +03:00
Bohdan Triapitsyn c444415cb2 feat: brought back mobile new session sidebar action
Adds a mobile-only new session button to the sidebar header
Opens a draft session from the sidebar
Keeps desktop sidebar actions unchanged
2026-05-25 15:28:02 +03:00
Bohdan Triapitsyn f8beffce32 perf: reduce startup request fanout
Avoid repeated sidebar session and worktree refreshes
Limit concurrent GitHub PR status checks
Reuse cached worktree root resolution
2026-05-25 11:58:59 +03:00
jeremysamuel13andBohdan Triapitsyn c5862cc6ee fix: resolve symlinks in project directory paths (#1316)
* fix: resolve symlinks in project directory paths

OpenCode stores sessions using the canonical (realpath) directory, but
OpenChamber passed the unresolved symlink path in several places. The
string-match directory filter would fail when a project was accessed via
a symlink, making sessions invisible.

Changes:

- Add safeRealpathSync to settings normalization — project paths and
  lastDirectory are canonicalized at persistence time
- Add Express middleware before the API proxy to resolve symlinks in
  ?directory= query params on in-flight requests
- Resolve symlinks in /api/fs/list so the directory browser returns
  canonical paths, allowing the "already added" check to work correctly
- Reconcile the in-memory projects store when the server responds with
  normalized paths, preventing temporary duplicates

Fixes #1315

* fix: avoid sync realpath in opencode proxy

---------

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-05-24 15:46:11 +03:00
Bohdan Triapitsyn f94d87b5fd perf(git): cache project-root resolution to stop N² polling cascade (#1398)
* perf(git): cache project-root resolution to stop N² polling cascade

Opening a workspace with many projects/worktrees fired hundreds of
`POST /api/fs/exec` requests (e.g. ~700 for 19 projects) within seconds,
dominated by repeated `git rev-parse --absolute-git-dir` /
`--git-common-dir` for the same directories.

Root cause: in `useProjectRepoStatus`, each project's `ensureStatus`
settles independently and mutates the git store, which re-derives
`projectGitBranchesKey` and re-runs `getRootBranch` for *all* projects on
every change. `getRootBranch` had no caching, so this produced an N×N
burst of uncached git plumbing calls.

Changes:
- worktreeStatus: extract `resolveProjectRoot` to module scope with a
  60s TTL cache + in-flight dedupe (root resolution is static within a
  session). Combine the two `rev-parse` queries into one subprocess.
  Add `getRootBranch(dir, { knownBranch })` fast-path that skips a
  redundant git status when the directory is its own root, while still
  resolving the primary-root branch correctly for linked worktrees.
  Export `invalidateResolvedProjectRootCache`.
- useProjectRepoStatus: replace the cascade effect with a debounced,
  diff-based pass that only resolves projects that are new or whose
  branch actually changed, passing the known branch through.
- worktreeManager: invalidate the root cache on worktree create/remove.
- Add unit tests for caching, dedupe, invalidation, rev-parse
  precedence, non-git fallback, linked-worktree resolution and the
  knownBranch fast-path.

Reduces startup from hundreds of requests to roughly one root
resolution per project.

* fix(git): clear in-flight resolves and guard write-back on cache invalidation

`invalidateResolvedProjectRootCache` cleared `resolvedRootCache` but left
`inFlightRootResolves` intact, so during a worktree topology change a
resolution already in flight could (1) be handed to callers arriving after
invalidation and (2) re-seed the cache with the pre-invalidation root when it
settled, defeating invalidation for up to the full TTL.

Drop the in-flight entry on invalidation and add an epoch guard so a resolve
that was invalidated mid-flight does not write its now-stale result back.
Add a regression test for the concurrent-invalidation scenario.

* fix(git): bound root cache and avoid early sidebar resolves
2026-05-24 13:20:55 +03:00
9af0de0056 Add mobile context notes tab (#1355)
* Add mobile context notes tab

* Fix bot comments

---------

Co-authored-by: Konstantin Zolin <zolin_ka@vk.com>
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-05-23 21:48:56 +03:00
Tom Rochette 0244d7ffcd feat: add quick-add button to directory explorer rows (#1351) 2026-05-23 21:37:51 +03:00
Roberto BertóandBohdan Triapitsyn 0c60fd6222 feat: rename sessions inline via double-click (#1320)
* feat: rename sessions inline via double-click

Double-clicking a session name in the sidebar or the mobile session
status bar now switches it into an inline editable input. Enter saves,
Esc cancels, and clicking elsewhere blurs the input to save. This
mirrors the VSCode/Finder rename pattern and removes the need to open
the session menu for what is a very common action.

* fix(rename): close sidebar input on empty title; drop duplicate mobile editor

Two issues from PR review:

1. handleSaveEdit (sidebar) only closed the input when editTitle.trim()
   was non-empty. Clearing the title and pressing Enter or blurring left
   the input open with no exit path other than Escape. The save handler
   now always closes the editor; an empty title is treated as a silent
   cancel (no update call).

2. ExpandedView (mobile) renders the current session twice — once in the
   sticky header, once in the session list — so a single editingSessionId
   produced two simultaneous inputs for the current session. The list row
   for the current session now suppresses its rename input; the header
   remains the single editor in that case.

* fix: refine inline session rename editing

---------

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-05-23 21:15:15 +03:00
Bohdan Triapitsyn 6fd3afd25a feat: replace prompt templates with snippets
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.
2026-05-21 20:00:35 +03:00
Bohdan Triapitsyn 51c8d52ab5 perf(ui): improve VS Code chat session switching
Improve chat session switching and history pagination, with most of the aggressive limits scoped to the VS Code webview where the freezes were observed.

Session history loading and pagination:

- Reduce the VS Code message page size to 30 records so switching sessions does not immediately hydrate large histories into the webview.

- Keep manual Load older messages in VS Code fixed at 30 records per request instead of growing the request size over time.

- Add a bounded VS Code initial-tail expansion path from 30 to 50, 80, and 120 records only when the initial page has no user-message turn boundary, preventing large final turns from rendering as an empty chat.

- Lower the normal web message page size from 200 to 150 for a mild shared optimization without adopting the aggressive VS Code limits.

- Make session pagination metadata reactive per session so ChatContainer receives cursor updates from materialization and reconnect paths without requiring a switch away and back.

- Write pagination metadata before publishing newly materialized messages so the first render sees the correct has-more state.

- Store cursor information from direct materialization and reconnect message fetches in the shared session prefetch metadata cache.

VS Code cache and memory pressure reductions:

- Use a shared per-directory session recency map so cache eviction is based on app-level recency instead of whichever useSync instance happened to run.

- Limit VS Code warm session cache retention to 4 sessions and evict heavy inactive message caches after switching away from a large session.

- Disable sidebar session prefetch in VS Code because warming extra sessions was increasing webview memory and GC pressure during navigation.

- Remove dropdown background message prefetch so opening the switcher does not start additional session materialization work.

- Drop cached session-message-record snapshots when evicting session data so stale derived records do not remain after the raw session cache is cleared.

- Add bounded LRU caching for session message record snapshots, with much smaller VS Code limits and a VS Code cap that avoids caching snapshots above 30 messages.

- Bound the turn-window model cache in VS Code and avoid caching turn models for sessions above the VS Code message-page size.

Chat render-path reductions:

- Reuse ChatContainer's already-materialized message records in plan detection instead of adding a second active-session message subscription.

- Add a no-op guard when marking session plan availability so repeated detections do not create new Map references and fan out renders.

- Add no-op guards for session switcher and dropdown open state updates to avoid unnecessary store updates and renders.

- Convert several session-specific hooks to useSyncExternalStore with empty-session no-subscribe behavior so empty IDs do not subscribe to broad store updates.

- Remount the chat viewport when the current session changes, isolating per-session viewport and list state.

- Change the virtualized message-list fallback to render only a tail window when the virtualizer has not produced rows yet, instead of rendering an entire large history.

VS Code layout and header improvements:

- Remove the broad useSessions subscription from the VS Code layout header path and subscribe only to the active session title and initial-session existence.

- Unmount the compact VS Code session sidebar when the user is in chat view instead of keeping the hidden session list mounted and subscribed.

- Compute the latest assistant model and latest context-token usage in a single reverse scan of current-session messages instead of scanning the same list twice.

- Remove switcher git-status warmup work so the switcher reads already-loaded branch labels without starting extra background git status requests.

Markdown and file-reference safeguards:

- Skip expensive syntax highlighting for very large code blocks, with a 200-line cap in VS Code and a softer 1200-line cap in web.

- Add an LRU cap to file-reference stat lookups so the cache cannot grow without bound across many rendered messages.

- Limit the number of file references annotated per render to 40 in VS Code and 200 in web to prevent large assistant outputs from spawning too many stat checks.

- Clear file-link annotations when file-reference mode is disabled so stale attributes and handlers do not remain on previously annotated nodes.

Assistant-message action and preview reductions:

- Skip preview URL scanning on VS Code, mobile, and mini-chat surfaces so assistant text and tool output are not scanned where the preview action is unavailable.

- Skip Save-as-Plan project lookup on VS Code, mini-chat, and mobile surfaces.

- Hide Save-as-Plan and Start MultiRun assistant-message actions on VS Code, mini-chat, and mobile surfaces.

- Resolve the current session directory on demand for assistant actions instead of subscribing each assistant message to the full session list.

Tool and task rendering optimizations:

- Prefer finalized task metadata summaries without fetching child-session messages when the summary is already present.

- Avoid polling or final-fetching task child sessions once a final metadata summary is available.

- Use VS Code-specific task child fetch limits of 30 records for initial, active, and idle fetches.

- Parse diff stats by scanning patch text line-by-line instead of splitting large patches into arrays.

- Count write-tool lines by scanning content instead of allocating a split array for large files.

- Avoid trimming large patch strings just to test whether they contain content.

- Memoize diff and write statistics so unchanged tool parts do not recalculate them on every render.

VS Code bridge improvements:

- Return JSON and text proxy responses through the VS Code bridge as bodyText instead of base64 so the webview avoids synchronous base64 decoding for common API responses.

- Keep binary responses on the base64 path while making bodyBase64 optional in the bridge contract.

- Strip content-length, content-encoding, and transfer-encoding headers from proxied responses because the bridge reconstructs the Response body.

Validation:

- bun run type-check

- bun run lint

- bun run vscode:build
2026-05-21 15:45:44 +03:00
Bohdan Triapitsyn 471ae69b1a refactor(ui): full-width header with framed chat shell
Header now spans the full window width above the [sidebar | chat | right-sidebar] row instead of nesting inside the central column. The chat area becomes a self-contained framed window with its own border and rounded corners on all four sides, and sidebars sit flush against the header sharing its bg-sidebar so the seam is invisible.

Removed the duplicated shell controls the old layout needed to fake header-height inside sidebars: portal host on RightSidebar, paddingTop reservation, top drag overlay, duplicated layout-left / chat-new buttons in SidebarHeader, the showDesktopSidebarChrome block in SessionSidebar, and the conditional traffic-lights inset on the desktop header. Mac WCO inset now lives only on the header.

Moved the new-session action into the SessionSwitcher dropdown as its first item, removed the standalone chat-new button from the header, relocated scheduled-tasks into the left action group of the sidebar header, and bumped ContextPanel tab strip to h-10 to balance the more prominent header.
2026-05-21 15:45:44 +03:00
Isaac Sanchez-HawkinsandIsaac Sanchez 2bf0c17b05 fix(ui): associate save plan title label (#1331)
* fix(ui): associate save plan title label

* test(ui): make save plan label test locale-independent

---------

Co-authored-by: Isaac Sanchez <isanchez-hawkins@arize.com>
2026-05-19 17:42:14 +03:00
Bohdan Triapitsyn c81441ecfc feat: improve VS Code editor chat actions
Targets active editor chat before falling back to sidebar
Adds session open-in-editor action in the VS Code sidebar
Uses attachment badges for VS Code file and selection context
2026-05-19 15:53:54 +03:00
vhqtvnandvhqtvn cbe6335ed9 fix(sessions): align archive-cascade count with executed list (#1298)
* fix(sessions): align archive-cascade count with executed list

When archiving a parent session, the dialog showed the total descendant
count but the actual archive request skipped already-archived
descendants — so the toast/result didn't match what the user was told.

Collect descendants once, then filter per-action: hard-delete cascades
to every descendant; archive skips already-archived ones. Dialog count
and the ID list passed to archiveSessions/deleteSessions now agree.

* fix(sessions): snapshot descendant IDs at dialog open

Address Greptile review: handleDeleteSession populated the dialog
count from a fresh collectDescendants() call, then confirmDeleteSession
re-collected at confirm time — so a background sync that mutated
childrenMap while the dialog was open could re-introduce the same
count-vs-executed mismatch this PR fixed.

Store the computed descendant IDs in deleteSessionConfirm and pass
them through to executeDeleteSession, so the executed list is always
the snapshot the user was shown. The no-dialog direct-execute path
also routes through the snapshot for consistency.

---------

Co-authored-by: vhqtvn <8930337+vhqtvn@users.noreply.github.com>
2026-05-18 18:13:43 +03:00
vhqtvnandvhqtvn 871d04e607 perf(sidebar): virtualize archived bucket above 50 rows (#1301)
The archived bucket is the only sidebar group that routinely grows
into the hundreds or thousands of rows (projects accumulate archived
sessions over time). On main, expanding the bucket mounts every row
at once — for a project with ~1400 archived sessions that's
thousands of subscriptions, layouts, and event handlers committed
on a single render.

Wrap the archived bucket's row map in `@tanstack/react-virtual`
(already in deps) when the visible count crosses 50. DOM stays
bounded to the visible window + overscan regardless of total size.

Implementation notes:
- Threshold + estimate are local constants; only the archived
  bucket is gated, every other group keeps its eager render.
- The virtualizer needs a scroll element. The sidebar's scroll
  lives on an ancestor `ScrollableOverlay`; rather than threading
  a ref through every intermediate component for this one use
  case, we walk up the DOM from the row container.
- `measureElement` handles variable row heights (subagent
  expansion, status indicators, etc.).
- ResizeObserver re-measures on sidebar resize, mirroring the
  pattern in ChangesSection.

Co-authored-by: vhqtvn <8930337+vhqtvn@users.noreply.github.com>
2026-05-18 18:07:12 +03:00
vhqtvnandvhqtvn 9ab90d4f1a fix(sidebar): key parent-expansion per render context (#1302)
The same session can appear in two render contexts at once — most
visibly under "Recent" and under its project's root group. Expansion
state was keyed by bare session id, so toggling the chevron in one
context flipped it in the other; the user clicked one row and a
duplicate elsewhere expanded/collapsed in sync.

Switch the expansion set to composite keys of the form
`<renderContext>:<active|archived>:<sessionId>`. SessionNodeItem
already computes this exact shape for its menu instance key
(menuInstanceKey); reuse it as expansionKey so the two render-time
instances of one session keep independent expand state. Updated:

- isExpanded reads `expandedParents.has(expansionKey)`.
- Chevron click / Enter / Space pass `expansionKey` to toggleParent
  instead of session.id.
- The memo-equality check compares the same composite key on both
  sides so it correctly re-renders when only one context's state
  changes.
- toggleParent's parameter is renamed `expansionKey` to reflect the
  new shape; the implementation is unchanged.
- The auto-expand-parent-on-subagent-navigation effect doesn't know
  which context the user will look at the parent in, so it fans the
  parentID out to all four (project|recent) × (active|archived)
  combinations.

Storage is bumped to `oc.sessions.expandedParents.v2`. Existing v1
data (bare session ids) is one-shot migrated by fanning each id
across all four contexts and rewritten under the v2 key; the v1 key
is then removed so a downgrade-then-reupgrade doesn't re-migrate.

Co-authored-by: vhqtvn <8930337+vhqtvn@users.noreply.github.com>
2026-05-18 17:57:05 +03:00
Bohdan Triapitsyn 5c1333a139 fix: restore typing in branch dropdown search fields
Prevent menu typeahead from intercepting search input keystrokes
Fix branch search in worktree and Git sidebar dropdowns
Cover related directory tree and integrate branch inputs
2026-05-17 23:48:05 +03:00
Bohdan Triapitsyn 2290cf4b59 Reduce React Doctor diagnostics in FilesView (#1271)
* Reduce React Doctor diagnostics in FilesView

* fix: address Greptile review feedback — use Base UI initialFocus and restore useMemo for displayedContent

- Replace useEffect-based dialog focus with Base UI Popup's initialFocus
  prop to avoid race with built-in focus management
- Drop unused no-op onOpenAutoFocus/onCloseAutoFocus from dialog wrapper
- Restore useMemo for displayedContent (200K char slice) to avoid
  per-render string allocation for large files
- Update DirectoryExplorerDialog to use initialFocus={false}
2026-05-17 23:31:28 +03:00
Erman HAVUÇandBohdan Triapitsyn 6369cf76a7 feat(ui): context panel enhancements — resizable panels, drag-and-drop todo ordering, and persistent sizes (#1269)
* fix: remove max-h-80 cap on quick notes textarea so resized height is respected

* feat: add drag & drop reordering to project todo items

* feat: make todo panel resizable with density-aware sizing

* feat: open plan import file picker at project root

* feat: persist quick notes and todo panel sizes across sessions

* refactor(ui): scale content height with padding in projectnotestodopanel

* Update packages/ui/src/components/session/ProjectNotesTodoPanel.tsx

Signed-off-by: Erman HAVUÇ <ermanhavuc@gmail.com>

* fix(ui): harden context panel resizing and import

---------

Signed-off-by: Erman HAVUÇ <ermanhavuc@gmail.com>
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-05-17 23:26:26 +03:00
Isaac Sanchez-HawkinsandIsaac Sanchez 00c112077d fix: match root project sessions in switcher (#1274)
* fix: match root project sessions in switcher

* test: cover project path matching edge cases

---------

Co-authored-by: Isaac Sanchez <isanchez-hawkins@arize.com>
2026-05-16 16:46:05 +03:00
Bohdan Triapitsyn 9828ddb4b1 feat: open subagent sessions read-only in context panel
Adds read-only embedded chat mode without hiding permission prompts
Opens subagent sessions in the context panel instead of replacing the main chat
Fixes context panel message loading for embedded sessions
2026-05-15 22:16:04 +03:00
Bohdan Triapitsyn 60f380479a feat: add session switcher to VS Code layout
Open recent sessions from the VS Code header
Add compact session switcher mode
Limit session prefetch to the active directory
2026-05-15 20:22:01 +03:00
Bohdan Triapitsyn b6149614bd feat: add session switcher dropdown in header
Open recent sessions from chat headers
Support session switching in mini chat
Share pinned and active session state
2026-05-15 18:16:09 +03:00
Bohdan Triapitsyn 3e53d7e071 feat: add fusion for multi-run sessions
Adds Run fusion for multi-run-like sessions
Combines sibling outputs into a new fusion session
Adds configurable Fusion prompts in Magic Prompts settings
2026-05-15 13:26:46 +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
Jake b4cbd7f0d6 Fix: archived session bulk delete for VS Code extension (#1256)
* fix/archived sessiongroup bulk delete

* Remove in line comment

Signed-off-by: Jake <101855602+jjdubski@users.noreply.github.com>

* small fix for lint

* indentation fix, restore to original

---------

Signed-off-by: Jake <101855602+jjdubski@users.noreply.github.com>
2026-05-13 11:12:30 +03:00
Isaac Sanchez-HawkinsandIsaac Sanchez ebf87f64dc fix(ui): label session rename buttons (#1238)
Co-authored-by: Isaac Sanchez <isanchez-hawkins@arize.com>
2026-05-12 11:02:53 +03:00
Isaac Sanchez-HawkinsandIsaac Sanchez 75ad1d31b5 fix(tasks): avoid weekday checkbox double toggle (#1225)
* fix(tasks): avoid weekday checkbox double toggle

* fix(tasks): preserve weekday hover affordance

* fix(tasks): remove redundant weekday hover class

---------

Co-authored-by: Isaac Sanchez <isanchez-hawkins@arize.com>
2026-05-12 11:00:21 +03:00
Bohdan Triapitsyn 1a0f3bea8e fix: allow PR worktrees from existing local branches
Use existing local PR branches instead of blocking worktree creation
Only block PR branches already checked out in another worktree
Clarify local branch validation messages across locales
2026-05-08 16:59:31 +03:00
Bohdan Triapitsyn 0ea573f766 refactor: redesign chat scroll system
The previous system layered three hooks (useScrollEngine, useChatScrollManager,
useChatTimelineController) with overlapping responsibilities, four parallel
ResizeObservers/MutationObservers, and six entry points to "scroll to bottom"
(force-flag combinations, persistent follow loops, materialization recovery).
This produced bugs where users could not break free of auto-follow during
streaming: scrollbar drag, keyboard scrolling and find-in-page were not
detected as user intent, and observers kept restarting the follow loop on
every DOM mutation.

The new architecture replaces the two low-level hooks with a single
useChatAutoFollow that owns scroll behaviour end to end:

- One state: 'following' or 'released'. No follow modes, no pin flags,
  no marker pixels.
- One scroll writer: a lerp loop that runs only while the session is
  streaming and state is 'following'. Idle sessions never write scrollTop
  programmatically.
- One user-intent detector: wheel up, touch drag down, keyboard
  (PageUp / Home / ArrowUp), pointerdown on the OverlayScrollbar thumb,
  and explicit releaseAutoFollow() calls all flip the state to 'released'.
- A 1.2s grace period after explicit release: re-pin will not auto-engage
  inside this window, so a small wheel up cannot snap the user back even
  while they remain near the bottom spacer.
- Re-pin and the scroll-to-bottom button share the same threshold: the
  height of the empty bottom spacer (10vh on desktop, 40px on mobile).
  Released users see the button only after they have scrolled past the
  spacer that already exists at the end of the chat.
- Save/restore of scroll position uses ratio mapping, debounced at 150ms
  on user-driven scroll events; programmatic writes are masked via a short
  window so they never persist as user positions.
- Container reattachment is detected via a useLayoutEffect probe over
  scrollRef.current. Listeners and observers re-bind when ChatViewport
  mounts after hydration or after the first message promotes a draft
  session into a real chat.
- A pending-restore queue replays restoreSnapshot once the scroll
  container appears, fixing the case where a hydrating session landed at
  the top instead of the bottom.

Removed: useScrollEngine.ts, useChatScrollManager.ts, the persistent
follow loop with its own ResizeObserver+MutationObserver pair, the
materialization-recovery .finally resume that yanked idle users to the
bottom on transient sync gaps, and the openchamber:session-reselected
event (re-select still works through the existing onSessionSelected
callback). The openchamber:chat-force-scroll-bottom event remains for
synthetic-message paths like git-message generation.

Net change: ~1300 lines removed, two hooks replaced with one, one
observer pair instead of four.
2026-05-08 14:20:16 +03:00
Bohdan Triapitsyn e1ff21bc0a feat: add Electron Mini Chat windows (#1161)
Add dedicated Electron Mini Chat windows for focused chat sessions without the full desktop shell. Mini Chat can open existing sessions or draft sessions, supports pinning above other windows, transfers sessions or drafts back to the main window, and deduplicates existing-session windows.

Expose Mini Chat entry points from the main header, session sidebar, command palette, and `mod+alt+n`. Add a dedicated Vite entry and React runtime so the compact surface can stay isolated from full-app chrome while still sharing chat, sync, theme, locale, model, agent, and worktree behavior.

Keep Mini Chat behavior scoped to the compact surface:
- limit assistant/user message actions to the appropriate Mini Chat set
- hide workspace changed-files UI in Mini Chat
- keep draft worktree selection and streaming directory state in sync
- mark sessions viewed while they are open in Mini Chat
- support Mini Chat-specific keyboard shortcuts for input focus, model selection, thinking variant cycling, favorite model cycling, and opening new Mini Chat drafts

Harden Electron integration by gating Mini Chat controls on desktop IPC availability, restricting pin/unpin IPC to Mini Chat windows, and only closing Mini Chat after the main window handoff succeeds.
2026-05-08 12:22:59 +03:00
Bohdan Triapitsyn e892346c6b refactor: stabilize live chat sync materialization (#1132)
Canonicalize session message/part materialization across load, prefetch, reconnect, and recovery paths so OpenChamber restores session snapshots through one consistent merge flow.

Preserve live assistant streaming text when stale or delayed snapshots arrive, while still replacing optimistic user parts with confirmed server snapshots to avoid duplicated user messages.

Narrow recovery triggers to explicit incomplete snapshot signals instead of broad session-event fallbacks, reducing unnecessary session refetches during active streaming.

Keep turn windowing aligned with parented assistant replies and add regression coverage for materialization gaps, stale snapshot protection, optimistic user replacement, reconnect recovery, and turn grouping.
2026-05-07 18:57:44 +03:00