mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-08-20 14:15:12 +00:00
2 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
55652d4912
|
fix(web-shell): keep split-view session list fresh and preserve panes across view switches (#6418)
* fix(web-shell): keep split-view session list fresh and preserve panes across view switches The in-window split view's "add pane" picker read a stale session snapshot — `useSessions` only fetches on mount — so sessions created after entering the split never appeared. And switching away from the split and back cleared the panes, because the live pane set lived in local state that died on unmount while the seed it re-mounted from was never updated (and the no-arg "Open Split View" button reset it to empty). - Reload the picker list when it opens and when the parent's session-list reload token changes, so it never offers a removed session or misses a new one. - Mirror the live pane set up to the app via onPanesChange so it survives SplitView unmounting; restore it (instead of reseeding empty) when the split is reopened without an explicit selection. * test(web-shell): cover split-view refresh/restore per review; coalesce token reloads Addresses review feedback on #6418: - SplitView: skip a token-driven reload while one is already in flight, so a burst of session-list changes (bulk create/delete) doesn't fire a redundant concurrent round-trip per bump (matches the sidebar's poll guard). - SplitView test: the freshness test now proves the picker re-renders with the refreshed list — a session appearing only after reload shows up — not just that reload() was called. - App test: cover the openSplitView preserve/restore path end-to-end — a reported pane set survives leaving the split and is restored on reopen. * fix(web-shell): reload split picker on every token bump (drop in-flight guard) The in-flight guard added in the previous commit could drop a session-list reload token that arrives while a reload is still running: the effect has already run for that token value, and clearing the in-flight flag in `finally` doesn't re-run it, so the picker could stay stale after burst create/delete/ rename activity — and the split has no polling fallback to recover. Reload on every distinct token bump instead. `useDaemonResource` serializes responses via its sequence counter (last write wins), so overlapping reloads are correct, and the token is bumped only on discrete session-change events — an occasional redundant fetch is far cheaper than a lost refresh. * test(web-shell): cover openSplitView explicit-selection branch (dedupe + cap) Per review: the restore branch of openSplitView was covered but the explicit-selection branch (dedupe + MAX_SPLIT_PANES cap, replacing any prior set) was only exercised, not asserted. Add a `?split=` URL test with duplicate and over-cap ids that asserts the split seeds exactly the deduped, capped selection. |
||
|
|
f41e95ac18
|
feat(web-shell): add Session Overview panel and in-window split view (#6400)
* feat(web-shell): add Session Overview panel and in-window split view
Add a large-screen "Session Overview" mission-control panel and an
in-window split view so users can monitor and drive multiple daemon
sessions at once.
- SessionOverviewPanel: ranked live cards (needs-approval -> running ->
idle) merging the workspace session list with the detail=full status
report. Multi-select opens the selected sessions as a split view in
the current tab ("Open in split") or in a new browser tab ("Open in
new tab", via a ?split=a,b URL).
- SplitView + ChatPane: one DaemonWorkspaceProvider hosting N
DaemonSessionProvider panes, each a self-contained interactive chat
(transcript, composer, streaming, tool/ask approvals). Browser focus
scopes the keyboard per pane, so panes never contend over approvals.
- Sidebar entry points gated to large screens; the split view's Back
returns to the Session Overview.
* refactor(web-shell): address review feedback on the session overview / split view
- SessionOverviewPanel: prune the selection Set when a session leaves the list
(so a reappearing session isn't silently reselected) and make select-all use
the intersection rather than prev.size.
- Extract isAskUserPermission into a shared util so App.tsx and ChatPane.tsx no
longer keep verbatim copies that can drift.
- SplitView: dismiss the "add session" picker on Escape or a click outside it.
- Tests: MAX_PANES cap, popup-blocked path, checkbox-selects-without-navigating,
stale-selection pruning, and a direct test for the extracted util.
* fix(web-shell): address /review findings on the split view
- ToolApproval: add a `keyboardActive` prop; split panes pass false so global
Enter/Escape/digit shortcuts can't confirm the wrong session's approval, and
the outer session's approval overlay is no longer rendered behind the split
(where it would keep its global shortcuts while hidden).
- ChatPane: defer the composer commit until sendPrompt resolves, so a rejected
prompt (transcript loading / disconnected / turn active) preserves the draft
instead of silently dropping it.
- SplitView: include a per-mount nonce in each pane's clientId so two tabs
opening the same split don't share a client id — which suppressOwnUserEcho
would treat as a self-echo and drop from the transcript.
- SessionOverviewPanel: cap the split selection to MAX_SPLIT_PANES before
building the ?split= URL or opening the in-window split, with a hint when more
are selected; also dismiss the split picker on Escape / click-outside.
- Tests covering each.
* fix(web-shell): address second /review round on the split view
- SplitView: wrap each pane in its own ErrorBoundary, so a render crash in one
pane (malformed block, unexpected tool shape) shows an inline fallback with a
close action instead of white-screening the whole split.
- splitUrl / overview: carry the daemon token into the new-tab split URL's
fragment. The current tab has already stripped the token from its URL, so a
token-auth (`serve --open`) deployment would otherwise open the split tab
unauthenticated. The token rides the hash (never sent to the server / logs).
- Tests: per-pane error isolation, token-in-fragment (and none without a token),
and the overview polling effects (interval fires, document.hidden skips, and
the in-flight guard prevents overlapping polls).
* fix(web-shell): hide the outer chat under the split and share app-level contexts
- App: hide (display:none) + aria-hide the outer chat subtree whenever
mainView !== 'chat', not only when a panel is open. Previously the outer
chat/composer/toolbar stayed reachable by keyboard/AT behind the full-page
split (it was only covered visually). State is preserved (node stays mounted).
- App: wrap SplitView in the app-level WebShellCustomizationProvider and
CompactModeContext so split panes render markdown / tool-headers / thinking
the same way the single-session chat does. Todo contexts stay chat-only —
they belong to the outer session, not the panes.
* refactor(web-shell): address review suggestions — coverage, dedup, split UX
- ToolApproval: add a dedicated test on the real component that the global
keyboard shortcut is armed by default and NOT armed when keyboardActive=false
(the cross-pane approval safety mechanism).
- SplitView: auto-exit to the Session Overview when the last pane is closed
(guarded so an initial empty seed doesn't bounce straight back out).
- ChatPane: add tests for the cancel action, the empty/whitespace submit guard,
and error routing to the onError prop.
- Extract the shared session-list page size + organization feature flag into
constants/sessions.ts, used by the overview, split view, and sidebar, so the
values can't drift between the three.
* fix(web-shell): surface outer approval + failed refresh in overview/split
- Split view: when the outer (main) session is waiting on an approval
that's hidden behind the split, show a non-blocking notice banner with
a "Go to it" button that returns to the chat where the approval lives.
- Auto-close the split (like the overview panel) when the viewport shrinks
below the large-screen breakpoint, so users aren't stranded.
- Session Overview: surface a failed refresh inline (keeping the last-good
cards) instead of silently swallowing it once cards are on screen.
- Tests: status-report poll cadence, picker dismiss (Escape / outside /
inside click), inline refresh-failure banner.
* fix(web-shell): sever window.opener on split tab; tighten hidden-chat test
- openSelectedInNewTab now clears win.opener (the split tab carries a
daemon token in its URL fragment) to prevent reverse tabnabbing, matching
the existing bug-report window.open path.
- Strengthen the split-view App test so a missing outer-chat subtree fails
instead of passing vacuously through an optional chain.
* fix(web-shell): split-view focus/stability/robustness follow-ups
- Refocus the composer after a shrink-driven split close so keyboard users
aren't dropped onto <body> (skips when an approval or panel takes over).
- Stabilize SplitView onExit via useCallback so its last-pane-close effect
doesn't re-fire on every App re-render.
- ChatPane: surface a per-pane connection-loss banner instead of silently
showing stale messages when a pane's daemon connection drops.
- ChatPane: anchor the streaming timer to the active turn's start (last user
message timestamp) so a pane opened mid-turn shows real elapsed time.
- Tests: split auto-close on shrink, outer-approval split notice + return-to-
chat, connection banner, and streaming-timer anchoring.
|