* fix(files): match Windows prefixes case-insensitively
* fix(files): remove Windows paths case-insensitively
---------
Co-authored-by: Isaac Sanchez <isanchez-hawkins@arize.com>
Removed duplicate Mini Chat launch action from the closed-sidebar header
Enabled opening Mini Chat from draft sessions via the header PiP action
Added read-only context usage info to existing Mini Chat sessions
* fix(terminal): clean up idle websocket
* fix(terminal): simplify active reconnect state
---------
Co-authored-by: Isaac Sanchez <isanchez-hawkins@arize.com>
* fix(files): ignore stale text loads
* fix(files): clear loading on directory read error
---------
Co-authored-by: Isaac Sanchez <isanchez-hawkins@arize.com>
* fix(git): dedupe status fetches by mode
* test(git): assert reused status result
---------
Co-authored-by: Isaac Sanchez <isanchez-hawkins@arize.com>
* fix(ui): expose sidebar group collapse state
* fix(ui): keep sidebar group panel mounted
---------
Co-authored-by: Isaac Sanchez <isanchez-hawkins@arize.com>
Bump @container/model-controls thresholds so labels disappear before
they would ellipsis-truncate. Hide order: agent (< 22rem), variant
(< 14rem), model (< 10rem). VS Code keeps its tighter thresholds.
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
C.UTF-8 does not exist on macOS, causing the terminal to fall back
to ASCII encoding when LANG is unset. Use en_US.UTF-8 on macOS and
C.UTF-8 on Linux. Also set LC_CTYPE with the platform-appropriate
value (UTF-8 on macOS, C.UTF-8 on Linux) for additional safety.
Bottom-zone threshold now scales with the scroll container's clientHeight
rather than window.innerHeight. The threshold is meant to mirror the empty
spacer at the end of the chat, but what users perceive as "the empty tail"
is relative to the visible chat area, not the whole viewport — on desktop
the chat is shorter than the window because of the input row below it.
This keeps the scroll-to-bottom button hidden until the user has actually
scrolled past the spacer they see, regardless of viewport size.
Wheel and touch release detection no longer treats every nested
[data-scrollable] as a blocker. The old check returned true for any
nested scrollable ancestor, so wheel-up over a code block already at its
own scrollTop=0 produced no effect — the inner element could not consume
the gesture and the outer chat refused to release auto-follow. The new
check returns true only when the nested element can actually scroll up
(scrollTop > 0); otherwise the gesture bubbles to the chat and releases
auto-follow as expected.
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.
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.