Commit Graph
2604 Commits
Author SHA1 Message Date
jaygupta17 e19b6ad3be Merge remote-tracking branch 'origin/main' into feat/nested-git-repos
# Conflicts:
#	packages/ui/src/lib/addSelectionToChat.test.ts
2026-08-26 09:22:57 +05:30
jaygupta17 e7a3d0e38d fix(git): stop auto-select re-picking a repository that failed its probe
A corrupt nested repository can be discovered (its .git entry exists)
but fail its status probe. The stale-selection recovery cleared the
pick and forced a re-scan, and auto-select immediately re-picked the
same path, looping discovery walk + probe for as long as the surface
stayed visible.

clearNestedRepoSelection now remembers the dropped repository per root
(session-only set, cleared on runtime switch), and auto-select skips
remembered paths. When every candidate has failed, no selection is made
and surfaces settle into their unresolved state instead of churning
requests. A manual picker pick remains possible and is probed like any
other.
2026-08-26 09:04:50 +05:30
Bohdan Triapitsyn 428854fd78 refactor(sync): replace the session-status setState monkeypatch with an owned API
The global session-status store patched its own setState to derive
active membership for callers replacing statusById — a boundary that
silently trusted any caller passing both fields to keep them consistent.
replaceGlobalSessionStatusById is now the one sanctioned way to swap the
map from outside the event reducers; the runtime-switch reset and the
tests that replaced the map directly go through it, and the patch is
gone. Follow-up to #3126 review.
2026-08-26 03:13:21 +03:00
Bohdan Triapitsyn 8d8d389294 docs(vscode): changelog for the resilient webview connection status (#2997) 2026-08-26 02:43:35 +03:00
Bohdan Triapitsyn cc0abad9af refactor(vscode): share the cached-state retry schedule across providers
The staggered re-send lived as three near-identical private methods in
the chat view, agent manager, and session editor providers. One helper
now owns the delays, the connected gate, and the replaced-target guard;
the providers keep thin wrappers over their own panel/view identity.
Also restores the package.json trailing newline the branch dropped.
2026-08-26 02:42:47 +03:00
Bohdan Triapitsyn 8638b8abcd Merge branch 'main' into pr2997-integration 2026-08-26 02:40:48 +03:00
Bohdan Triapitsyn f0b1c889f0 Merge main into pr1-large-file-preview
The floating editor toolbar this branch was based on was removed on main
(the toolbar is always docked now); the file-view region resolves to
main's structure with this branch's virtualizer-bound ScrollableOverlay.
2026-08-26 02:26:20 +03:00
Bohdan Triapitsyn 79b726451d Merge main and keep the read timeout armed without AbortSignal.any
When the caller passes its own AbortSignal and AbortSignal.any is
unavailable, the timeout was silently dropped — disabling the fix on
exactly the bootstrap reads it targets, since those carry a cancellation
signal. Compose the two signals manually through an AbortController in
that case; listeners detach when the request settles.
2026-08-26 02:23:21 +03:00
Bohdan Triapitsyn 90b6776e5b Merge branch 'main' into pr2969-integration 2026-08-26 02:22:29 +03:00
Bohdan Triapitsyn 448ecc12b8 fix(ui): close review findings from the switch-and-scroll integration
- Markdown DOM cache: key includes a content-length fingerprint so an
  edited or reverted part re-materializing under the same id cannot
  restore stale DOM, memoization uses scalar identities instead of the
  part object (store reducers recreate part objects on unrelated updates,
  which re-ran the async render pipeline for identical content), and a
  probe with a mismatched locale/directory no longer destroys the entry
  it failed to claim.
- Sidebar bootstrap: the layout-level sync owner only knows known
  directories, so expanded projects bootstrapped serialized at background
  priority. The visible collection now publishes a second, expansion-
  aware demand owner, restoring concurrent hydration for expanded
  projects and worktree groups.
- Settings: local changes still sitting in the debounce buffer are not
  yet tracked as mutations, so a settings GET racing the debounce window
  briefly reverted them; reconciled results now reapply the pending
  buffer.
2026-08-26 01:03:47 +03:00
Bohdan Triapitsyn ac880e8e62 Merge main (anchored-turn chat scrolling) into perf/switch-and-scroll
Main replaced the chat timeline scroll engine while this branch was in
flight, which obsoletes two of its subareas and reshapes a third:

- Chat timeline: main's LegendList-based MessageList/ChatContainer win;
  the activation-overscan staircase targeted the removed tanstack path
  (LegendList provides adaptive rendering natively) and is dropped along
  with its test.
- Scroll shadows: main's hook-based masks stay (the virtualized list owns
  its scroll element — there is no wrapper to hand the styling to); the
  viewport-wrapper ScrollShadow rewrite, its index.css replacement, its
  test, and the call-site viewportClassName adaptations are reverted to
  main. The chat OverlayScrollbar keeps this branch's disableHorizontal.
- OverlayScrollbar: the direct-DOM rewrite lands, but binding now follows
  the live container node instead of binding once per ref object — the
  chat scroller remounts on every session switch, and a bind-once
  contract left the scrollbar attached to a dead element.
- Markdown renderer: the detached-DOM cache and warm-block fast path
  merge with main's block-commit reveal (enter cascade), streaming code
  highlighting, and gutter reservation; the per-block reconcile keeps
  both the decoration-refresh path and the reveal cascade.
2026-08-26 00:59:45 +03:00
Bohdan Triapitsyn 88c76557a5 docs: changelog for the anchored-turn chat scrolling rework
Also point a stale mobile.css comment at useChatTimelineScroll — the
engine it referenced no longer exists.
2026-08-26 00:40:13 +03:00
Bohdan Triapitsyn 5f0f8e64ed refactor(chat): split the composer status bar from the assistant status chip
The composer rendered a second StatusRow instance carrying the
pending-changes bar and the todos dropdown, so every restyle of the
floating assistant-status chip (glass, placement, sizing) silently
restyled the composer bar and its dropdown too — for the fourth time.

StatusRow is now only the floating chip above the composer; the
composer's own bar is a new ComposerStatusBar with the pre-glass layout,
its own container name, and its own container-query classes (the
anti-overlap rules that hide the long active todo under 38rem and the
changed-files label under 30rem moved with it — losing them during the
split let the two dropdown triggers overlap on mobile). The two
components share no markup and no CSS hooks anymore.
2026-08-26 00:35:25 +03:00
Bohdan Triapitsyn 043e71d725 fix(ui): give the scroll shadow size variable a real default
The base rule declared --scroll-shadow-size as var(--scroll-shadow-size,
48px) — a self-referential cycle, which computes to invalid and silently
resolves every mask below to none. The old ScrollShadow component masked
the bug by setting the variable inline; the hook-based path (the chat
scroller) sets only the data attributes, so the chat lost its top and
bottom scroll fades. A concrete 48px default restores them; inline
overrides from the component still win.
2026-08-26 00:09:34 +03:00
Bohdan Triapitsyn 2d8571c6f2 fix(chat): animate end following only during a live stream
The animated maintainScrollAtEnd also ran outside streaming, so opening
a historical session glided visibly through the whole conversation as
late row measurements corrected the end position, and an in-flight glide
could supersede explicit navigation. Corrections are instant unless the
session is actively working.

Returning to the bottom during a stream also lands on the end as of that
moment, and the list's own follow may not have re-armed after the user's
earlier gestures — the queued-send jump then fell behind the growing
reply. goToBottom now re-asserts the edge a few times (150/400/800ms)
until it holds; a new user gesture cancels the window.
2026-08-25 23:54:00 +03:00
Bohdan Triapitsyn ab370c018a refactor(chat): drop the blur from the block reveal
During a long stream the follow scroll masks the entrance entirely, so
the blur was invisible where it streamed and only a cost risk on weak
GPUs. Keep the compositor-only fade and rise (and the same-tick cascade)
— those are effectively free and still show on short replies where no
scroll motion is involved.
2026-08-25 23:16:15 +03:00
Bohdan Triapitsyn 5e37a436c1 feat(chat): soften the committed-block reveal into a materialize cascade
The 140ms fade read as a pop next to the scroll glide. The reveal now
runs 320ms on an ease-out-quint curve with a 10px rise and a touch of
blur resolving — paint-only properties throughout, so row measurement is
untouched — and blocks committed in the same tick cascade with a 55ms
stagger instead of appearing together. Reduced motion still disables the
whole thing.
2026-08-25 23:08:36 +03:00
c_w_xiaohei ceee7f350e fix(ui): preserve upstream sidebar and selection behavior 2026-08-26 02:36:07 +08:00
c_w_xiaohei f329a22cf3 test(ui): align selection with context shell 2026-08-26 02:31:35 +08:00
c_w_xiaohei 1d694932aa fix(ui): complete session rebase integration 2026-08-26 02:31:19 +08:00
c_w_xiaohei 45693e84bd perf(ui): widen markdown session cache
Retain detached Markdown DOM for eight recent sessions so medium-frequency session switching can restore more first-screen content without reparsing it.

Limit each session to four cached parts, reducing the worst-case retained fragment count from 36 to 32 while preventing long sessions from dominating the cache.
2026-08-26 00:44:14 +08:00
c_w_xiaohei fa1503a038 perf(ui): narrow session switch fanout
Keep sidebar rows independent from the active sync directory by moving export history loading behind an explicit-directory command.

Make active-project selection preserve project topology and remove activeProjectId from mounted group render props. Reuse a per-session-array ID index so subscription snapshots do not repeatedly scan session lists.
2026-08-26 00:44:14 +08:00
c_w_xiaohei 701173e399 perf(ui): index and batch session updates
Batch global session, status, ordering, and activity timing mutations at the existing directory event boundary so large subagent bursts publish each owner once.

Maintain active session roots, children, and directory buckets in the global store, reuse them in Sidebar projections, and avoid rebuilding live aggregates and structural data for unrelated renders while preserving authoritative ordering reconciliation.
2026-08-26 00:42:37 +08:00
c_w_xiaohei df924aa532 fix(ui): bound settings mutation history
Skip mutation history when no settings operation is in flight and merge updates that share the same operation visibility window. This keeps stale-response reconciliation unchanged while preventing history from growing with repeated debounced updates.

Add a repeated-update regression covering an older pending load and latest-value reconciliation.

Tests: bun test src/lib/persistence.test.ts
2026-08-26 00:42:37 +08:00
c_w_xiaohei 4454bf7e14 refactor(ui): encapsulate settings mutation tracking
Keep settings mutation revisions and in-flight operations behind a private tracker so stale GET and PUT responses cannot overwrite newer local settings.

Remove redundant runtime identity and numeric operation IDs. Use operation object identity while preserving runtime reset and mutation retention behavior.

Tests: bun test src/lib/persistence.test.ts
2026-08-26 00:42:36 +08:00
c_w_xiaohei 532bdab5e8 fix(ui): preserve upstream behavior after performance rebase 2026-08-26 00:42:36 +08:00
c_w_xiaohei 9b9d7069c7 perf(ui): isolate scroll shadow effects 2026-08-26 00:42:36 +08:00
c_w_xiaohei dd6a632bc5 perf(ui): streamline overlay scrollbar updates 2026-08-26 00:42:36 +08:00
c_w_xiaohei cd179bd118 perf(ui): avoid parsing markdown control icons 2026-08-26 00:42:36 +08:00
c_w_xiaohei ed37d8e249 perf(ui): isolate sidebar session selection 2026-08-26 00:42:36 +08:00
c_w_xiaohei 77da97953f perf(ui): reuse detached markdown DOM 2026-08-26 00:42:36 +08:00
c_w_xiaohei 1dfa3aee2f perf(ui): stage history overscan restoration 2026-08-26 00:42:36 +08:00
c_w_xiaohei 1c3c26f2bf perf(ui): batch Mermaid viewer initialization 2026-08-26 00:42:36 +08:00
c_w_xiaohei 25be29731f perf(ui): reuse warm markdown blocks 2026-08-26 00:42:36 +08:00
c_w_xiaohei 26dbc2f309 perf(ui): streamline session sidebar state 2026-08-26 00:42:36 +08:00
jaygupta17 d00a48b8b1 test(ui): drop stale chat surface assertions
Carries openchamber/openchamber#3124 until it lands: upstream commit
c82f188fc removed setActiveSurface from production code but left the
addSelectionToChat test asserting it, so the ui suite fails on any
branch including main. Byte-identical to that PR's fix; drop this
commit when rebasing onto main after #3124 merges.
2026-08-25 21:30:45 +05:30
Bohdan Triapitsyn d71bcb5025 feat(chat): colored streaming code and a reveal for committed blocks
With block-level commit the open code fence grows by whole lines at the
throttle cadence, so the old reason to skip highlighting it (re-tokenizing
a growing block ~40x/sec) no longer applies: a partial fence now
highlights too, and streamed code arrives colored instead of colorizing
only when the fence closes. Fences beyond 300 lines fall back to plain
text until closed, keeping the repeated worker re-tokenization bounded.

A freshly committed block also enters with a short fade-and-rise. The
class goes on the block's children (the wrapper is display:contents and
cannot animate) and the transform is paint-only, so virtualized row
measurement is unaffected; reduced motion disables it.

Verified over CDP on a production build: streamed code inside a still-
open fence renders highlighted, and the code text's x-position is
identical during and after the stream.
2026-08-25 18:40:42 +03:00
jaygupta17 72c1d28fc6 fix(ui): drop unused nestedRepoSelection binding and stale review-flow dependency 2026-08-25 21:09:39 +05:30
jaygupta17 11a48958e8 fix(git): resolve blank nested-repo surfaces; add repository pickers
The resolution gate on the pull-request, walkthrough, and mobile changes
surfaces stayed on forever (rootIsGitRepo stays false on a non-repo
root) while NestedRepoResolutionStates exits once the selected
repository probes as a repository, so those surfaces rendered nothing.
The gate now shows resolution states only while the operating directory
has not proven to be a repository, matching GitView.

Extract GitHeader's repository switcher into git/NestedRepoPicker and
mount it in the diff toolbar, a new slim header in the pull-request
view, the walkthrough header, and the mobile changes header. The pick
is shared per root, so every surface follows.

The walkthrough tab mounts keep-alive and hidden; it now receives a
visible prop so discovery waits until the tab is actually opened. Add
component tests for the shared resolution states.
2026-08-25 20:47:02 +05:30
Bohdan Triapitsyn 3b8ed8fc36 feat(chat): block-level streaming reveal with a gliding follow
Token-by-token streaming mutates the trailing paragraph in place on
every tick: words rewrap, the last line jitters, and the whole reply
reads as flicker. Streamed text now commits only up to the last complete
line — prose arrives a paragraph at a time (a markdown paragraph is one
logical line), code fences reveal line by line, tables row by row — and
a shown block never changes again. A paragraph that runs long without a
newline releases at the last sentence (then word) boundary so the stream
never stalls. Applies to assistant text and reasoning; tool output keeps
its raw tail.

With growth arriving in block steps, the end follow switches to the
list's animated mode so each step is a glide — reveal and scroll read as
one continuous motion. The gesture opt-out now measures at-end from the
live list state instead of the cached flag, which the animated glide
deliberately leaves stale while trailing the edge; without that, a drag
during a glide could leave the scroll-to-bottom pill unshown.

Measured: end-following holds at distance 0 for the whole stream, and
the mobile drag opt-out shows the pill in three of three runs. The
continuous glide costs ~15% more main-thread time per streamed character
than the instant follow — the price of the motion.
2026-08-25 18:16:49 +03:00
Bohdan Triapitsyn b946fc7083 fix(chat): stable code-block geometry, hydration reveal, deduped file stats
Three first-render polish issues:

Code blocks jumped at end-of-stream: streaming defers the per-line
line-number markup, so the finished decorate pass inserted the gutter
column and shifted every code line right. The gutter's horizontal
footprint is now reserved with CSS while the markup is deferred, so the
final pass only fills in numbers and colors.

File references were verified twice per render and re-verified with the
wrong directory: the annotation pass's own DOM writes re-triggered its
MutationObserver, and the pass also ran before the effective directory
resolved — issuing stat probes under an empty directory and a second
time under the real one. The observer now ignores the pass's own
mutations and annotation waits for a resolved directory.

Content replacing the hydration skeleton popped in: it now plays a
one-shot 180ms fade (reduced-motion aware); cached session switches
never carry the class and stay instant. Also removed the dead
disableStaging prop and its unused pendingRevealWork threading.

Verified on a production build over CDP: a streamed code block's text
keeps its exact x-position across end-of-stream, and stat probes for a
message with file mentions are unique per path with the directory header
always present. The hydration fade path could not be exercised in the
harness (the live event stream pre-populates parts in a single-project
environment) and needs an eyeball check on a cold multi-project open.
2026-08-25 17:51:43 +03:00
Bohdan Triapitsyn 8c102cddd0 fix(chat): teleport the send anchor when sent away from the live edge
Sending from mid-history positioned the new turn with an animated
scrollToIndex; over a long distance the smooth scroll gets cancelled by
rows mounting and measuring along the way — a weak scroll attempt that
dies partway, leaving the reader where they were. Park instantly when
the viewport was not at the end at send time; the short smooth park
remains for sends from the live edge.
2026-08-25 17:25:06 +03:00
jaygupta17 e26b55e067 fix(git): exit nested-repo states once resolution succeeds
NestedRepoResolutionStates had no success exit: on a non-repo root
rootIsGitRepo stays false forever, so once repositories were found the
pull-request and walkthrough tabs kept showing 'Checking repository'
even after the selected repository probed as a repository. GitView never
hit this because its call site sits inside its own isGitRepo === false
branch. The component now takes the operating directory's probe and
returns null when it resolves true.

DiffView also still keyed its not-a-repository gate and every diff
fetch off the raw project root, so opening a change from a nested
repository showed 'This directory is not a Git repository'. It now
resolves the nested repository for git data and diff operations while
session-scoped lookups (session messages, review-flow directory) stay
on the root.
2026-08-25 19:55:00 +05:30
Bohdan Triapitsyn f85e70b359 fix(chat): return to the live edge on queued sends and teleport rail jumps
Sending while the agent is working queues the message without creating a
user row, so the anchor-arming send path had nothing to claim and the
viewport stayed parked mid-history. Queued sends now jump straight to
the live edge (instant, so end following re-latches immediately).

Rail selection also switches from smooth to instant scrolling: a long
smooth scroll through the virtualized timeline gets cancelled by row
remounts and lands mid-way or on the wrong message, while a teleport
always arrives.
2026-08-25 17:09:55 +03:00
Bohdan Triapitsyn 04f338cc49 feat(chat): upgrade @legendapp/list to 3.3.8 and let it own end following
3.3.x makes maintainScrollAtEnd follow content growth on its own — a
tail row growing in place included — which is exactly what the manual
totalSize correction existed for. Delete that correction (the totalSize
listener now only drives the anchored-turn glide) and pick up 3.3.x's
measurement batching, prepend-flash fixes, and web programmatic-scroll
fixes. Opt the explicit maintainScrollAtEnd config into footerLayout per
the 3.1.1 guidance.

The library's own released-on-user-scroll heuristic proved unreliable
one run in three against synthetic touch, so the gesture state machine
stays authoritative: while a real gesture owns the scroll, the list's
end pinning is switched off through a threaded endPinningReleased prop
and re-engages when the user returns to the end.

Validated with the CDP battery on a production build: stream follow
stays at distance 0, mobile drag releases with the pill shown in three
of three runs, resize oscillation stays at the reduced level, the rail
reaches the last turn, and profiled streaming cost per rendered
character matches the tuned 3.2.0 numbers.
2026-08-25 16:49:22 +03:00
jaygupta17 cda273d69f feat(git): resolve nested repositories in the other git surfaces
Extract GitView's resolution flow into hooks/useNestedGitDirectory (root
probe, discovery, auto-select, stale-selection recovery) so the flow no
longer depends on SessionSidebar probing the root first, and reuse it in
the pull-request view, walkthrough view, and mobile changes surface —
all three now operate on the selected nested repository instead of
dead-ending on a non-repo root. Shared pending/failed/unsupported/empty
states live in git/NestedRepoResolutionStates; desktop changes inherits
the behavior through GitView. Selection stays shared per root, so the
picker's pick carries across surfaces.
2026-08-25 19:15:53 +05:30
jaygupta17 5e3f9d1ba2 fix(git): harden nested repository discovery
- discard an in-flight discovery when the runtime switches, mirroring the
  isRequestCurrent pattern, so a late completion cannot repopulate the
  cleared map and suppress a fresh scan
- treat a 501 from /api/fs/git-dirs as an explicit 'unsupported' marker
  instead of a generic failure; the VS Code webview now answers the route
  with unsupportedWebRouteResponse so non-repo roots show the honest
  not-a-repository state without a futile Retry
2026-08-25 19:15:35 +05:30
jaygupta17 f56b934dc9 Merge remote-tracking branch 'origin/main' into feat/nested-git-repos
# Conflicts:
#	packages/web/server/lib/fs/routes.test.js
2026-08-25 18:39:25 +05:30
Bohdan Triapitsyn e801afec43 fix(chat): do not snap back to the end after a width resize
A slow edge drag settles the resize detector repeatedly, and every settle
replayed the instant return to the live edge — reading as exactly the
jumping the resize suspension removes. Stay where the reader is; geometry
changed, and the next explicit action or stream correction re-engages the
edge as usual.
2026-08-25 16:01:22 +03:00
Bohdan Triapitsyn 5c39db0e36 fix(chat): release end pinning while the list width resizes
A pinned viewport shook during window resizes while a scrolled-up one
stayed calm: both pinning mechanisms — the growth corrections and the
list's maintain-scroll-at-end — re-assert the end against rows that are
still re-measuring, fighting the size compensation that keeps the free
case stable. While the width is actively changing, stand both down and
hold the reading position the same way the free path does, then re-assert
the live edge once with a single instant write after the resize settles
(only when the viewport was still following).
2026-08-25 15:54:39 +03:00