Commit Graph
98 Commits
Author SHA1 Message Date
Leonidandbashrusakh 0242765bc8 fix(session): keep pinned sessions on refresh (#2057)
* fix(session): keep pinned sessions on refresh

* fix(session): type sidebar persistence test

* fix(session): remove invalid sidebar persistence harness

---------

Co-authored-by: bashrusakh <bashrusakh@users.noreply.github.com>
2026-07-11 14:50:39 +03:00
Leonidandbashrusakh bfaf62e222 fix(sidebar): add project sort modes (#2067)
* fix(sidebar): add project sort modes

* fix(sidebar): preserve manual sort order

* fix(sidebar): move sort state before render

---------

Co-authored-by: bashrusakh <bashrusakh@users.noreply.github.com>
2026-07-11 14:43:57 +03:00
a1aae30e66 Share project edit form; add per-project default model (#2015)
* feat(chat): migrate history list to @tanstack/react-virtual with deterministic mobile history loading

- Replace virtua with @tanstack/react-virtual for chat history on all
  surfaces: bottom anchoring (anchorTo: end), key-stable prepend
  preservation, and native iOS touch/momentum deferral live in the core
- Patch virtual-core to clamp the render range to real scroll bounds
  during transient adjustments (OpenCode upstream parity)
- Rows render in normal flow inside a translated wrapper so sticky user
  headers keep working; measurement snapshots cached per session
- Pre-write container height in scrollToFn so the browser cannot clamp
  anchor corrections to the stale height; hold the prepend anchor for up
  to 180 frames on mobile while fresh rows settle (cancelled by user
  input; desktop relies on core anchoring alone)
- Adaptive row-size estimate from per-session measured averages; disable
  reveal fade-in for virtualized history rows
- Mobile loads older history only through an explicit localized top
  button: no scroll-position trigger and no post-mount background
  prepend, so every insert happens from a resting state; a quiet-window
  hold defers any stray prepend commit while a touch gesture is active
- Desktop/VS Code keep the seamless scroll-up trigger and progressive
  background prepend

* Share project edit form between settings and sidebar dialog

Extract ProjectIdentityFields and useProjectIdentityForm so the projects
settings page and sidebar Edit dialog share the same layout and behavior.
Rename the project menu action from Rename to Edit, and add per-project
default model selection for new chats with persistence and draft-session
resolution ahead of global defaults.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* Unify project edit UI with shared ProjectIdentityEditor shell

Wrap header, fields, and inline Save changes button in one editor
component used identically by settings projects page and sidebar
dialog. Remove dialog-specific footer, title, and padding so both
surfaces render the same layout.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* Include Actions and Worktree sections in project Edit dialog

Extract ProjectSettingsPanel with the full settings=projects content
(identity, actions, worktree) and render it from both the settings page
and sidebar Edit dialog. Keep the dialog open after identity save so
users can configure actions and worktrees without reopening.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* Narrow project Edit dialog to modal-appropriate width

Use max-w-2xl instead of max-w-4xl so the popup does not inherit the
full settings page width.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* Unify project settings subsections and auto-save all fields

- Add shared ProjectSettingsSubsection with consistent titles and dividers
- Auto-save identity, actions, and worktree setup commands (debounced)
- Remove Save changes and Save Actions buttons
- Split worktree into Worktree and Existing worktrees subsections
- Align controls to shared max width across all subsections

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* Harden project settings auto-save error handling

- Only update worktree setup snapshot after successful save; toast on failure
- Toast when actions auto-save is blocked by validation for >1s

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* Show toast when project identity auto-save fails

Wrap onSave in try/catch and surface settings.projects.page.toast.saveFailed
so rejected parent callbacks are not silently swallowed.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* Fix clearing project default model from settings

Send null instead of undefined when no default model is selected so
updateProjectMeta enters the defaultModel branch and deletes the field.
Apply consistently in prepareSaveData, ProjectsPage, and SessionSidebar.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

---------

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
2026-07-09 13:54:05 +03:00
Bohdan Triapitsyn 086982c1b3 feat: add temporary sidebar share opinion prompt
Adds a dialog with call booking and survey actions for user feedback
Shows a one-time sidebar toast prompting users to share what’s useful or missing
Adds localized copy for the new feedback entry points
2026-07-06 00:37:24 +03:00
Bohdan Triapitsyn 3f5151d424 feat(ui): unify list virtualization on @tanstack/react-virtual and polish scroll behavior
- Migrate sidebar session groups, git changes panel, virtualized code
  blocks, and JSON tree viewer from virtua to @tanstack/react-virtual;
  virtua remains only inside the Pierre diff viewer integration
- Sidebar: preserve scroll position when virtualization enables
  mid-session (enable only once the ancestor scroll element is resolved,
  seed initial offset from its live scrollTop, render plain rows for the
  single pre-paint frame); disable native scroll anchoring on the
  sessions scroller; keep row spacing identical between plain and
  virtualized modes; absolute row positioning so variable-height rows
  cannot drift past the container
- Chat: expand tool/thinking blocks downward by only adjusting scroll
  for rows growing above the viewport; raise the desktop history-load
  lead to 1.5 viewports so prepends land above the visible area
- Git changes: compute the prefetch window from the first visible row,
  skipping overscan rows above the viewport
- Sidebar rows: make the whole highlighted row area clickable, guarded
  against double-firing from interactive children
2026-07-03 18:44:10 +03:00
c184ddf185 fix(worktree): subagent sessions kept when deleting worktree group from sidebar (#1806)
* fix(worktree): include sessions when deleting worktree group from sidebar

allGroupSessions was guarded by group.isArchivedBucket, returning [] for
active worktree groups. This caused the 'delete worktree' button in the
sidebar to send an empty session list — SessionDialogs only removed the
git worktree directory and skipped archiving any sessions, leaving them
orphaned.

Remove the guard so all sessions (including recursive children /
subagent sessions) are collected regardless of archived state.

* fix(sessions): delete all descendants on hard-delete instead of relying on server cascade

The previous code sent only the root session ID and assumed the server
would cascade-delete all children. If the cascade failed, children were
left orphaned. Delete root + descendants individually; 404 responses
from already-cascade-deleted children are treated as success.

* fix(sessions): clear worktree metadata when deleting a session

Deleted sessions kept their worktree attachment in both
session-worktree-store and session-ui-store. Clean it up on successful
deletion and on 404 (already deleted).

* fix(worktree): search subagent sessions across all directories before delete

WorktreeSectionContent and BranchPickerDialog used useSessions(), which
is scoped to the current sync directory. Subagent sessions created in
other worktrees/project roots were missed and left orphaned. Search
across active + archived global sessions when collecting descendants.

---------

Co-authored-by: Leonid Skorobogatyy <bash@opencode.itc.local>
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-06-29 00:42:13 +03:00
bashrusakhandLeonid Skorobogatyy 7ee893e1e5 fix(sidebar): preserve explicit session selection on stale worktree data (#1813)
When clicking a session inside a worktree group, the layout effect in
useProjectSessionSelection could override the user's selection with the
project's first root session. This happened because projectSections
(and thus projectSessionMeta) might not yet include the worktree group
on the first render after the click.

The fix adds a guard: if currentSessionId is set but not found in the
current projectMap (stale data), the effect returns early instead of
falling through to auto-selection logic.

Fixes #1804

Co-authored-by: Leonid Skorobogatyy <bash@opencode.itc.local>
2026-06-29 00:21:54 +03:00
Bohdan Triapitsyn 4d4b3aa9d9 fix: improve context panel session visibility
Marks active embedded chat sessions as seen only while focused
Shows real session titles for context panel chat tabs
Names review sessions after the implementation session
2026-06-28 01:39:15 +03:00
Serhii DziupinandSerhii Dziupin 3e3cd82a47 feat: inherit model-picker reorder/accordion persistence and shift-delete from otto-ui (#1833)
* feat(model-picker): drag-to-reorder providers and persist accordion state

Ports two model/provider picker QoL features from otto-ui:
- Persist collapsed state of picker sections (favorites, recent, each
  provider) via a new persisted zustand store so collapse survives
  remounts and reloads, shared across every picker surface.
- Desktop drag-to-reorder of provider sections (whole header as the
  mouse activator, 8px threshold so a plain click still toggles
  collapse), with the order persisted in useUIStore.providerOrder and
  applied across ModelControls, ModelMultiSelect and ModelSelector.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* feat(sessions): shift-click quick action hard-deletes thread without prompt

Ports the shift-quick-delete feature from otto-ui. The sidebar quick
action (normally archive) becomes a no-prompt hard delete while Shift is
held: the icon switches to a trash bin, the affordance turns destructive,
and the click bypasses the confirmation dialog via a new skipConfirm
source flag. A shared useShiftKeyHeld hook (single window listener set,
useSyncExternalStore) keeps only the small action button re-rendering on
Shift state changes, and resets on window blur so the affordance can't
get stuck after alt-tab.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

---------

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
2026-06-26 19:36:47 +03:00
00821700de chore: remove dead code (59 unused files + ~125 unused exports) (#1835)
* chore: remove dead/unreferenced files across ui, vscode

Remove 59 unused source files (components, hooks, lib utils, stores,
barrels, and orphaned vscode github modules) that are not imported by
any entry-reachable code. Also drop a stale test mock for the removed
execCommands module.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* refactor: remove unused exported symbols (types, functions, consts, hooks)

Remove exported symbols whose identifier is referenced nowhere in the
repository (verified via repo-wide search), across ui types/contracts,
lib utilities, sync layer, stores, and components. Also drop the few
imports/private helpers orphaned by these removals.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* refactor: remove more unused exports (desktop, shortcuts, worktree, vscode)

Continue removing repo-wide unreferenced exported functions, consts and
types across lib/desktop, shortcuts, worktreeSessionCreator, sync, and
vscode gitService, with cascading orphaned helpers/imports cleaned up.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* chore: add dead-code cleanup tooling

* refactor: checkpoint dead-code cleanup

* refactor: remove dead-code suppressions

---------

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-06-26 19:27:53 +03:00
Bohdan Triapitsyn b0e476ab7c fix: prevent sidebar worktree reorder snap-back (#1827)
Invalidates cached group ordering when reorder state changes
Keeps dragged worktree groups in the new position immediately after drop
Validated with UI package type-check
2026-06-25 17:11:37 +03:00
Bohdan Triapitsyn c97d5c79c1 fix: prioritize session sidebar status indicators
Prevents pinned icons from crowding live indicators
Shows chevrons on hover when status or pin takes priority
2026-06-24 18:45:26 +03:00
bashrusakhandLeonid Skorobogatyy 57cef1b278 fix(sidebar): correct expansion-key format for virtualizer buffer (#1711)
* fix(sidebar): increase virtualizer buffer for expanded parents

Fix #1530: archive sub-session layout broken because the virtualizer
used a fixed 28px height estimate per row. Expanded parents with inline
children are much taller. Now dynamically increases bufferSize when
expanded parents are present.

* fix(sidebar): correct expansion-key format for virtualizer buffer

The expansion key was using raw sessionId instead of the scoped format
'project:{archived|active}:{sessionId}'. This made hasExpandedParent
always false, so bufferSize never increased. Also removed dead
hasSessionSearchQuery branch.

---------

Co-authored-by: Leonid Skorobogatyy <bash@opencode.itc.local>
2026-06-23 22:24:11 +03:00
bashrusakhandLeonid Skorobogatyy 83256ba924 fix(sidebar): preserve pinned sessions and folder refs on empty session list (#1706)
Added sessions.length === 0 guard to useSidebarPersistence.ts and
sessions.length === 0 && archivedSessions.length === 0 guard to
useSessionFolderCleanup.ts. Prevents data loss when server returns
empty list during transient failures.

Co-authored-by: Leonid Skorobogatyy <bash@opencode.itc.local>
2026-06-23 22:01:24 +03:00
weixiang1862 56ca5bcf74 fix(mobile): subagent chevron overlaps session title on mobile (#1582)
`mobile.css` applies `min-width: 36px; min-height: 36px` to all `[role="button]` elements on mobile devices for touch targets, enlarging the subagent chevron from `14×14px to 36×36px`. This extends the chevron box 20px past the content edge, visually overlapping the session title. Add inline `minWidth/minHeight: 14` to pin the chevron size.
2026-06-23 11:30:02 +03:00
bashrusakh 59ecd86b4b perf: isolate chat streaming renders and reduce sidebar render cost (#1672)
Reworks the chat and session-sidebar render paths to cut render cascades, memory
  churn, and UI jank on large sessions and big session trees. Behavior is preserved;
  the changes are about *when* and *how much* the UI re-renders.

  ## Chat streaming
  - Freeze the streaming message's parts in the bulk turn projection during streaming,
    and re-inject live parts only in an isolated tail leaf, so a ~60/sec delta stream
    no longer re-runs the whole-session projection or re-renders unrelated rows.
    session with referential reuse of unchanged turns.
  - Memoize message rows with field-aware comparators instead of reference equality.
  - Replace the manual child-session polling in the task tool with the live SSE
    stream + a one-shot load, removing a fetch/settle state machine.

  ## History loading & scroll
  - Load an initial page fast, then prepend one older page in the background so the
    scroll container has headroom and "load older on scroll-up" fires before the user
    hits the absolute top.
  - Compensate scroll synchronously (in a layout effect, before paint) for prepends —
    including background prepends that don't originate from a user scroll — so the
    viewport stays stable instead of judder-correcting on the next frame.

  ## Markdown rendering
  - Render markdown synchronously *styled* on first paint (paragraphs, lists, code
    cards, tables, inline code) instead of raw escaped text; the async pass then only
    upgrades syntax-highlight colors. Eliminates the flash of full-width raw text.
  - Load KaTeX CSS eagerly with the main bundle instead of inside the lazy markdown
    chunk, avoiding a late stylesheet injection on first render.

  ## Sidebar
  - Hoist per-row recursive tree walks out of row comparators into per-group
    precomputed sets/keys; batch live-session lookups into a single map; add a
    group-level memo boundary.
  - Isolate rename drafts so per-keystroke typing doesn't repaint the row tree.

  ## Sync layer
  - Add a staleness guard so a slow message fetch can't repopulate a session the user
    navigated away from.
  - Throw on fetch failure for authoritative loaders so a transient blip can't read as
    an empty server response.

  ## Cleanup
  - Remove dead code (unused hooks, params, duplicated inline types) surfaced while
    reworking the above.

  ## Known issue
  - A rare, purely cosmetic first-paint width flash can still appear on large sessions;
    it has no behavioral or data impact and is tracked for a follow-up runtime trace.
2026-06-18 00:43:16 +03:00
bashrusakhandLeonid Skorobogatyy d909c43979 refactor(sidebar): remove dead directoryStatus probe artifact (#1660)
Commit 7205d7a3 replaced runtime directory probing with buildKnownSessionDirectories

filtering, but left the hook, prop, state, and UI checks as a transitional artifact.

- Delete useDirectoryStatusProbe.ts

- Remove directoryStatus state/prop from SessionSidebar, SessionGroupSection, SessionNodeItem

- Remove isMissingDirectory checks, opacity-75 styling, and disabled states

- Simplify handleSessionSelect signature

- Update sidebar DOCUMENTATION.md

Co-authored-by: Leonid Skorobogatyy <bash@opencode.itc.local>
2026-06-15 14:45:50 +03:00
Bohdan Triapitsyn c73ab9cbd5 feat(vscode): startup parity + workspace-grouped session list (#1658)
* perf(vscode): gate API readiness and coalesce duplicate startup reads

Bring the VS Code bridge runtime to parity with the web startup
optimizations (PR #1650), which were web/desktop-only.

waitForApiUrl now hands out the OpenCode API URL only once the manager
reports 'connected', instead of as soon as getApiUrl() exposes
server.url. The URL is available the moment the process is spawned —
before waitForReady confirms it can serve and during a workspace-switch
restart (stale port) — so URL-presence alone let the bridge forward to a
not-yet-ready OpenCode and surface 502s. Gating on connected status
mirrors the web proxy's isOpenCodeReady hold. Also fail fast on 'error'
status so a missing CLI doesn't burn the full 30s timeout.

Coalesce concurrent identical GET reads (config/path/agents/agent/
project/command) at the bridge proxy so the single OpenCode process
serves them once. On cold start the webview's sync bootstrap and config
store fire these reads in parallel with no shared dedup; this is the
extension-host analog of the runtimeFetch coalescer. Shared reads carry
no AbortController so one caller's abort can't strand the others, and the
entry clears as soon as it settles (never serves stale).

* perf(vscode): fade the startup splash once mounted + connected, not on live fetch

The webview's initial-loading overlay held until a successful live
/api/config/providers AND /api/agent fetch completed. After the cache
hydration work those live reads are the slowest cold-start tail — the UI
underneath already paints pickers and the sidebar from cache and refreshes
in the background — so gating the splash on them kept it spinning long
after the app was usable.

Fade the overlay as soon as the UI is mounted and OpenCode is connected.
Per-widget loaders convey any remaining background refresh, matching how
web/desktop (which have no such splash) already behave. Removes the now
-obsolete bootstrapProvidersReady/AgentsReady/Failed tracking and
recordBootstrapFetch. Connection error/disconnected splash messages are
unchanged.

* fix(vscode): include captured OpenCode output in spawn-timeout error

When the managed OpenCode server fails to emit its 'listening' line within
the start timeout, the error discarded everything the process printed to
stdout/stderr — so the status report showed a bare 'Timeout waiting for
server to start' with no clue whether the process hung, crashed silently,
or printed a config/auth error. The exit path already includes the output;
the timeout path now does too (or notes that nothing was printed).

* feat(vscode): workspace-grouped session list with working folders, pinning, and archived toggle

Replace the flat multi-workspace session list with the grouped project view,
using each open VS Code workspace folder as a header (no per-worktree
subgroups). This restores native folder and pin support, which the flat list
silently dropped, and fixes the clipped left padding on session rows.

- Group sessions strictly by open workspace; funnel all non-archived sessions
  into the workspace's group so they no longer fall into the archived bucket.
- Keep the project/group/folder + buttons but make them open a draft in the
  correct workspace and navigate to chat; hide the project actions (...) menu,
  which isn't relevant in VS Code.
- Force the minimal single-line row layout (the second metadata row is
  redundant under workspace headers) and drop the per-row tooltip.
- Add a show/hide archived toggle next to the archive-all control, since the
  VS Code header has no display-mode menu.
- Size the hover action reveal so the timestamp clears the row buttons.
2026-06-15 13:02:26 +03:00
Bohdan Triapitsyn a45376d585 perf: migrate chat rendering to virtua (#1651)
* refactor: migrate chat history virtualization to virtua

* refactor: render loaded chat history directly

* refactor: finish virtua migration

* perf: defer tool body rendering

* perf: queue deferred tool body mounts

* perf: quiet and defer markdown file probes

* perf: defer markdown code highlighting

* perf: stabilize markdown plugin lists

* perf: defer mermaid markdown rendering

* perf: delay markdown file reference annotation

* perf: attach markdown table listeners on demand

* perf: trim markdown render overhead
2026-06-15 03:29:40 +03:00
Bohdan Triapitsyn e372c8d8cb perf: instant startup via cache hydration + decoupled readiness (#1650)
* perf(startup): hydrate providers/agents from cache (stale-while-revalidate)

Persist last-known provider/agent snapshots instead of stripping them, so the
model/agent pickers paint instantly on cold start. Freshness is preserved by the
background refresh in initializeApp() and activateDirectory() (which overwrite on
success) and by the existing provider/agent config-change subscriptions, so the
prior stale-provider regression stays fixed without blanking the UI during fetch.

* perf(startup): cache directory session list for instant sidebar

Persist a capped slice of each directory's session list and seed the child store
from it on creation, so the sidebar paints chats immediately on cold start.
Bootstrap phase-3 loadSessions overwrites with the fresh list; its empty-list
race guard preserves the seeded sessions during OpenCode warmup.

* perf(startup): hold API requests through OpenCode warmup instead of 503

The readiness gate returned 503 the instant OpenCode wasn't ready, pushing the
client into an exponential-backoff retry loop (500ms -> 1s -> ...) that wasted
seconds of cold-start time and could fail bootstrap outright. Now hold the
request and poll readiness up to a bounded window so the first call succeeds as
soon as OpenCode is up (typically sub-second); still 503 fast past the window so
a genuinely-down server doesn't hang. Adds coverage for both paths.

* perf(startup): surface cached providers/agents in pickers (optimistic readiness)

The model/agent pickers gated purely on isInitialized, so they showed
"Loading…" for the entire init round-trip even when provider/agent data was
already hydrated from cache — making the persisted-cache work invisible. Treat
the pickers as ready as soon as cached providers are present (stale-while-
revalidate), so they paint last-known models/agents instantly and refresh in the
background. First-ever launch (no cache) still shows Loading until init.

* perf(startup): don't abort directory bootstrap on transient phase-1 failure

A failed initial path.get OR session.status aborted the whole directory
bootstrap, stranding it in loading and skipping phase 2/3 (session load).
session.status is live data the event pipeline keeps current, and path.get is
tolerable once a project is resolved from global state. Now only a total
failure (or path.get failing with no resolved project) aborts, so the sidebar
and chat keep advancing and loading sessions through warmup hiccups.

* perf(startup): don't bootstrap directories from archived sidebar rows

Each sidebar session row called useDirectoryStore(dir), which defaulted to
bootstrap:true and triggered a full directory bootstrap. Archived sessions point
at dozens of (often deleted) worktrees, so on startup this fired a session-list
fetch + 6x2s empty-retry storm per dead directory (the logs the user saw). The
store ref there is only read on-demand via getState() in export handlers, never
subscribed, so archived rows don't need it bootstrapped. Add a { bootstrap }
option to useDirectoryStore and skip bootstrap for archived rows; active rows
still bootstrap so live cross-directory session/status keeps aggregating.

* perf(startup): stop empty-session bootstrap retry storm on web/desktop

The post-bootstrap retry re-ran the full directory bootstrap 6x2s whenever the
session list came back empty, on the theory that empty meant OpenCode wasn't
ready. But loadSessions already retries transient failures twice over
(listGlobalSessionPages throws on 5xx and retries internally), so on web/desktop
an empty result is authoritative — the directory genuinely has no sessions (e.g.
deleted worktrees referenced only by archived sessions). That produced the
dozens of '[bootstrap] sessions empty ... 6 attempts; giving up' log storms.
Gate the retry to VS Code, where the bridge can return an empty 200 during
warmup that the inner retries can't catch.

* perf(startup): scope provider/agent config to project (worktrees inherit)

Providers/agents/defaults are project-level, but were keyed per directory, so a
worktree fetched and cached its own snapshot — duplicating the parent project's
load (the trace showed initializeApp loading the worktree and activateDirectory
loading the project concurrently, ~8s of redundant background work).

- resolveConfigDirectory() maps a worktree to its owning project; loadProviders
  /loadAgents/activateDirectory now key by it, so a worktree reuses one shared
  project snapshot. activateDirectory resolves up-front so activeDirectoryKey and
  the snapshot key always match (picker stays consistent); the OpenCode working
  directory is unaffected.
- Add a 30s runtime freshness guard so the stale-while-revalidate background
  refresh skips re-fetching config that was just loaded (initializeApp then
  activateDirectory for the same project), and to avoid churn on rapid project
  switches. Config-change invalidation clears the snapshot, which bypasses the
  guard, so freshness never masks a needed refresh.

* fix(sidebar): default archived sessions to hidden to avoid startup flash

useSessionDisplayStore defaulted showArchivedSessions to true, so on startup
archived sessions rendered by default and then vanished once the persisted
preference rehydrated to hidden — a visible flash. Default to hidden so the
pre-hydration state is the quiet one; users who opted into showing archived keep
their persisted true (default change doesn't override persisted state).

* perf(startup): persist worktree->project mapping to kill cold double-load

The worktree->project map (availableWorktreesByProject) is populated by async git
discovery, so it isn't ready when initializeApp runs — a worktree's first config
load couldn't resolve to its project and duplicated the project's provider/agent
load, saturating OpenCode during cold start (the source of the slow first
createSession/send the user observed). Cache resolved worktree->project mappings
to localStorage so resolveConfigDirectory resolves synchronously at init on
subsequent launches; the project is loaded once and activateDirectory hits the
freshness guard. worktree->project is immutable so a cached entry is safe; live
resolution still populates/corrects the cache.

* perf(startup): persist worktree map for instant sidebar + first-launch keying

Worktree discovery is async (git), so availableWorktreesByProject was empty at
startup: the sidebar worktree list appeared late, and useConfigStore couldn't
resolve a worktree to its project on the first launch (causing the cold
worktree+project double-load). Persist the discovered worktree map to
localStorage and seed it synchronously on store init (stale-while-revalidate:
discovery refreshes in the background via the existing setState, which now
write-through persists). The sidebar paints worktrees instantly and
resolveConfigDirectory resolves the project from the very first launch.

* perf(startup): coalesce concurrent duplicate OpenCode reads in runtimeFetch

On cold start the sync bootstrap and the config store independently fire the same
idempotent reads (providers, config, path, agents, project) concurrently with no
shared dedup, saturating the single OpenCode process and delaying work queued
behind it (e.g. createSession). Coalesce genuinely-concurrent identical GETs to
those read endpoints at the transport layer so OpenCode does the work once; each
caller receives an independent response clone. Tightly scoped: GET only,
allowlisted read paths, never event streams, never a signal-bearing request (so
one caller's abort can't cancel the shared fetch). Entries clear on settle, so it
only shares overlapping in-flight requests — never a stale response.

* perf(startup): cache git branches so the draft branch selector paints instantly

The branch selector above the composer was the slowest-loading element: it's
gated behind a cold 'git branch' fetch (useGitStore, not persisted). Cache the
per-directory branch list to localStorage and seed the store on init (with
isGitRepo:true so the selector's gate passes), and write the cache on every
successful fetchBranches. The ChatInput draft-branch effect now refreshes on
staleness (>30s) rather than mere absence, so seeded branches show immediately
and still refresh in the background without a spinner — no stale-forever
regression. Only the branch list is cached; status/log/diff are untouched.
2026-06-15 03:16:34 +03:00
Tom Rochette 560e24cbc2 fix: prevent worktree modal from disappearing after opening (#1585)
The worktree dialog state was local React state in SessionSidebar,
which gets destroyed when the component unmounts (mobile drawer close,
VSCode view switch). This caused the modal to briefly appear then
disappear.

Three changes:
- Move newWorktreeDialogOpen from local state to useUIStore so it
  survives component unmount
- Guard useProjectSessionSelection layout effect to skip when the
  worktree dialog is open (prevents auto-session-selection from
  closing the sidebar)
- Remove setSessionSwitcherOpen(false) from worktree button handler
  on mobile (prevents drawer close that unmounts the sidebar)

Fixes #1414
2026-06-13 08:55:17 +03:00
Paolo InsognaandBohdan Triapitsyn b1810013e5 feat: Add delete session in the sessions menu. (#1557)
Signed-off-by: Paolo Insogna <paolo@cowtech.it>
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-06-12 23:13:41 +03:00
Tom a0597b1065 fix: prevent cascade rollback from restoring deleted session descendants (#1555)
The OpenCode server cascade-deletes all child sessions when a parent
is removed. The client was sending individual DELETE requests for each
descendant, which returned 404 after the parent's cascade removed them.
The 404 triggered rollback in deleteSessionAction, restoring already-
deleted sessions back into the global store.

Changes:
- executeDeleteSession: only send the root session delete; the server
  cascade handles descendants.
- deleteSession / deleteSessionInDirectory: treat 404 in catch as
  success, acting as a safety net for remaining paths (e.g. sidebar
  bulk action bar when parent and child are both selected).
2026-06-12 22:54:39 +03:00
Bohdan Triapitsyn 5272b95be7 fix: extend session row selection highlight to cover gutter area
Selection highlight now visually wraps status indicators and chevrons
Active session gets a subtle primary background tint
Multi-select rows use the interactive-selection token
2026-06-12 19:32:40 +03:00
Bohdan Triapitsyn c281937406 refactor(recent): replace active-now tracking with recent session window
Replace persisted 'active now' tracking with 48-hour recency window
Remove Zustand ActiveNowStore and localStorage persistence
Simplify session sidebar data flow
2026-06-12 14:34:33 +03:00
Bohdan Triapitsyn 28aeb4950b refactor: simplify session list display
Makes minimal session rows the default
Removes session diff stat badges from navigation surfaces
Keeps expanded session rows in VS Code
2026-06-12 12:38:31 +03:00
Bohdan Triapitsyn a01b7a982f fix: align VS Code subsession chevrons in metadata row
Moves VS Code subsession chevrons into the metadata row
Centers the chevron with session metadata
Keeps web and desktop sidebar layout unchanged
2026-06-11 13:19:14 +03:00
Maksym Mospanenko 7b33805ea0 feat(vscode): support multi-root workspaces (#1493)
Add proper VS Code multi-root workspace support. New sessions now start in the workspace folder the user chooses instead of always using the first folder.

The VS Code sidebar now shows one shared flat session list for the currently opened workspace folders, keeps that list synced when folders are added or removed, and excludes sessions from worktrees unless that worktree is opened as a workspace folder.

Also keep the OpenCode server process independent from a specific workspace folder so changing the selected folder does not restart or interrupt existing sessions.
2026-06-10 23:58:40 +03:00
Pascal AndréandBohdan Triapitsyn 49a1424e5f feat: add complete French localization (#1482)
* feat: add French locale runtime

Add French to OpenChamber's shared i18n runtime, dictionaries, and parity tests so the existing language picker can load a complete fr locale across shared UI surfaces.

* fix: localize shared UI formatting

Remove remaining shared UI locale hardcodings so dates, numbers, and first-party helper copy follow the active app locale instead of leaking English on French surfaces.

* feat: localize VS Code French surfaces

Localize VS Code bootstrap, native runtime messages, panel titles, and manifest contribution strings so French users get consistent first-party copy across the extension experience.

* fix: TASK-2026-05-30-008 correct French review findings

Fix broken French relative-time and weekday strings reported on PR #1482 and restore proper import order in quota utils without broadening scope.

* fix: TASK-2026-05-30-008 address final PR review comments

Capture the localized More Info label once in the VS Code CLI-missing flow and replace the remaining inline French-only utility strings with dictionary-driven copy plus required locale keys.

* fix: TASK-2026-05-30-008 normalize French glossary

Correct glossary-level French terminology on the live PR branch, keeping canonical technical terms like PR, worktree, stash, HEAD, Mermaid, Markdown, remote, and session while replacing misleading literal translations.

* fix: TASK-2026-05-30-008 refine French terminology pass

Clean up remaining glossary mistakes on the French PR branch, especially around Mermaid, Markdown, PR, worktree, stash, branch, remote, and commit terminology, while keeping behavior unchanged.

* fix: TASK-2026-05-30-008 clean remaining French false friends

Correct the SOCKS5 mistranslation and a final small set of obvious false-friend technical nouns on the French branch without changing behavior.

* fix: TASK-2026-05-30-008 correct French glossary terms

Replace remaining false-friend translations in the French UI dictionaries and normalize technical labels for the French PR branch.

* fix: TASK-2026-05-30-008 remove remaining French Mermaid false friend

Replace the last confirmed Sirène translation with Mermaid and re-run the requested blacklist and build verification on the PR branch.

* fix: TASK-2026-05-30-008 enforce French glossary policy

Keep skill/PR/worktree/remote terminology developer-credible in French and remove remaining machine-translated Git and settings copy.

* fix: TASK-2026-05-30-008 keep prompt terminology in French

Replace remaining technical invite translations with prompt wording across scheduled tasks, multi-run, prompt templates, and Magic Prompts.

* fix: TASK-2026-05-30-008 finalize French terminology cleanup

Polish remaining worktree/remote wording, remove visible metadata leakage, and correct final Git and settings labels on the French PR branch.

* fix: TASK-2026-05-30-008 polish final French strings

Correct the last aria-like artifacts and awkward worktree/remote/GitHub URL phrasing in the French dictionaries.

* fix: TASK-2026-05-30-008 normalize final French glossary framing

Tighten the last worktree/remote/checkout wording and fix remaining French grammar around canonical technical terms.

* fix: TASK-2026-05-30-008 align final developer glossary wording

Normalize the last French framing around canonical developer terms like worktree, remote, prompt, and checkout.

* fix: TASK-2026-05-30-008 harmonize final French sentence framing

Replace the last raw franglais around checkout, remote, worktree, and prompt-facing labels with more natural French framing while keeping the chosen technical terms.

* fix: TASK-2026-05-30-008 add compact relative date keys

Replace French-specific prefix stripping in compact session date labels with dedicated i18n keys across locale dictionaries, preserving existing compact label output while making French wording robust.

* docs: add French documentation

* docs: mention French locale folder

---------

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-06-10 20:19:30 +03:00
Bohdan Triapitsyn 12c8cbf544 feat: add right-click menus across sidebar rows
Open row actions at the pointer position
Keep three-dot menus working separately
Reuse shared menu styling for consistent visuals
2026-06-10 15:12:10 +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 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 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 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
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
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 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
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 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
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
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