Repository navigation
Side pane review fixes: animation, topic scope, dormancy, side chat safety, terminal links, CI gate - #4
Merged
Conversation
Code viewer tab ids carry the file path. On Windows the backslashes are read as CSS escapes, so the selector never matched and keyboard focus fell to <body> after cycling to, closing, or leaving the menu of such a tab; a path containing a quote made the selector throw and the mount fail. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016hW799u5Ji5k9khDAwu688
… literal colour check:ui-system was red on the side pane branch. tool-presentation.css had 101 selectors outside .vcp-ui-scope, an !important on the reduced-motion rule and two fixed 11px sizes; components.css (from upstream lioensky#234) fell back to a hard-coded indigo because --vcp-ui-primary is not a token. Every tool presentation selector now sits under .vcp-ui-scope (the chat messages container already carries it, and the file is alone in its layer, so the uniform specificity bump changes no cascade). The reduced-motion rule reuses the shimmer selector instead of !important, the 11px sizes use --vcp-ui-text-xs, and the pinned window button uses the accent token. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UCGGgmHsCToooXTgSXpmq9
Before, a provider that threw during mount (a corrupt restored payload, a failed lazy import, a bug) left its tab selected over a blank pane, with the error only in the console. The view now says the tab could not open, shows the error, and offers a retry; showing the tab again still retries as before, and closing the tab removes the error view. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016hW799u5Ji5k9khDAwu688
…wn with it initWorkspaceSidePane ran inside the main startup try block ahead of the filter manager and setupEventListeners, so any exception while building the side pane skipped the rest of startup and left the send button and the other main window bindings dead. It now runs in its own try/catch and reports the failure as a toast. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016hW799u5Ji5k9khDAwu688
A file:// page in the side browser runs with Electron's default file privileges, so its script could fetch other local files (SSH keys, VCPChat settings) and post them out. Like DeepSeek Harness (browser-guests.ts onBeforeRequest) and ZCode (isAllowedBrowserUrl), the side browser now only opens http, https and about pages, and its session cancels file: and other non-web subresource requests. Any page could also start downloads on its own, and each one opened the system browser. A download now reaches the system browser only right after a real input on that page, one per input, the same rule popups use. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016hW799u5Ji5k9khDAwu688
…sible focus ring - Arrow keys re-rendered the strip and then focused the removed button, while activateTab moved focus into the panel, so each press threw a keyboard user out of the tablist. Keyboard switches now activate without moving focus and refocus the new button by id, as Radix Tabs (used by ZCode) does. - While the new-tab page or hidden notifications was active, no tab had tabindex=0, so the strip had no Tab stop. The first tab now gets it. - Delete closes the focused closable tab. - Tabs and header buttons removed the outline with no replacement; they now get the same focus ring as the tab menu items. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016hW799u5Ji5k9khDAwu688
render() rebuilds the whole page, and it only put focus back on filter inputs and page tabs. Toggling a section or a batch, opening filters, picking a contributor, or any background refresh dropped focus to <body>. Interactive buttons now carry a data-focus-key that render() records and restores, the imperative equivalent of React keeping keyed nodes in ZCode and DSH. Clearing the filters lands on the search box. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016hW799u5Ji5k9khDAwu688
Shift+F10 on a change row opened the menu at (0,0) with focus left on the row, no arrow navigation, and Escape dropped focus. Like the Radix ContextMenu in ZCode's GitPaneChangeCard, the menu now opens beside the row when there is no pointer, takes focus, moves with the arrow keys, Home and End, and Escape, Tab or choosing an item returns focus to the row. Icon ligature text is hidden from screen readers. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016hW799u5Ji5k9khDAwu688
The button called openPythonAttachmentInTextEditor, which only accepts .py file URLs on Windows, so it failed silently for every file. Sending the path to open-external-link instead would hand model-supplied paths (.bat, .lnk) to shell.openExternal. The button now reveals workspace files through the workspace-checked git:reveal-path and shows the path for anything else. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016hW799u5Ji5k9khDAwu688
…keys stop Each arrow, Home or End press wrote settings over IPC, about 30 writes a second while an arrow key was held. The width still applies on every press; the save now waits until the keys stop for 400 ms (the debounced onLayoutChange ZCode's resizable.tsx uses), and runs at once when focus leaves the handle or the pane is torn down. Pointer drags save on release as before. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016hW799u5Ji5k9khDAwu688
…focus hold under real use - Reopening while the close animation runs no longer leaves the pane collapsed with its expand button hidden; a repeated open no longer jumps back to zero width. - With no current conversation (group selected, early startup) topic tabs of other conversations stay off the strip, so "close all" can no longer delete their side chats. - Keep views (side chats) no longer take places under the live-view limit; browser tabs sleep only for the limit, not for being hidden; collapsing the pane no longer puts the current tab to sleep. - Arrow keys on the tab strip keep focus on the tab; tab ids with Windows paths or quotes are looked up without building selectors. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019DFRovoZHMzeheXKkLNWKG
The 430-odd side pane, side chat, git and browser tests were not in any workflow; test:side-pane gathers them and the Chat Kernel and UI job runs it. The PTY-backed terminal handler tests need a real PowerShell and stay out until a Windows job exists. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019DFRovoZHMzeheXKkLNWKG
…en and re-show in constant time Measured in the real Electron app (headless, 6000-line JS file): opening went from 1215ms to 330ms and switching back to the tab from 752ms to ~110ms with no long task. Before, focusing the re-shown view forced a full style and layout pass over ~60k highlight nodes; now only the chunks in the viewport take part. Gutter and code are split into 200-line blocks with content-visibility:auto and an intrinsic height from the fixed 18px line height, so numbers stay aligned. highlight.js still highlights the whole text once; its output is split per line with spans that cross lines closed and reopened, so multi-line comments and template strings keep their colours. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017bCbRB3E9yajQ3HbdMrjiJ
File paths shown as changed files come from the model's tool-call arguments, and the viewer read any of them through get-text-content, so a prompt-injected path such as ~/.ssh/id_rsa showed its content, one click from being inserted back into the chat. Like the workspace-bound previews in ZCode and DeepSeek Harness, a path outside every registered workspace now shows where it is and is only read after the user clicks to read it. Workspace files are read as before; windows without the workspace service keep the old behaviour. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016hW799u5Ji5k9khDAwu688
git status runs whatever command a repository's .git/config names in core.fsmonitor. A project unzipped with a prepared .git folder could therefore run code as soon as the Git page read its status. Every git call now passes -c core.fsmonitor=false, as DeepSeek Harness does in scripts/change-scope.ts. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016hW799u5Ji5k9khDAwu688
… literal colour check:ui-system was red on the side pane branch. tool-presentation.css had 101 selectors outside .vcp-ui-scope, an !important on the reduced-motion rule and two fixed 11px sizes; components.css (from upstream lioensky#234) fell back to a hard-coded indigo because --vcp-ui-primary is not a token. Every tool presentation selector now sits under .vcp-ui-scope (the chat messages container already carries it, and the file is alone in its layer, so the uniform specificity bump changes no cascade). The reduced-motion rule reuses the shimmer selector instead of !important, the 11px sizes use --vcp-ui-text-xs, and the pinned window button uses the accent token. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UCGGgmHsCToooXTgSXpmq9
…pect the busy gate, the filter accepts input-method typing - Closing a side chat that has messages deletes its child topic, so it now asks first (also for close others / close all); declining keeps the tab. - A second send while a reply streams is refused, so the running reply keeps its stop button. A message button no longer overwrites or sends a typed draft, references or attachments. - The plan history filter no longer rebuilds the page while an input method composes, so Chinese input works; the search runs once composition ends. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019DFRovoZHMzeheXKkLNWKG
…ection copies - xterm's default OSC 8 handler showed a confirm and called window.open from the main window, which opened a bare window on the default session. The tab now passes a linkHandler that only opens http(s) through the side browser, as ZCode does. - Ctrl/Cmd+C with a selection copies it instead of sending ^C to the PTY shared with the AI tools; without a selection Ctrl+C still interrupts. - Ctrl/Cmd+Alt+B and Ctrl+PageUp/PageDown drive the side pane only and are no longer written to the shell as escape codes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019DFRovoZHMzeheXKkLNWKG
…fix branch claude/project-thread-20fk2r (PR #3) already finds tabs by id without selectors and keeps arrow-key focus on the strip, with its own tests. Drop the duplicate here so the two branches do not conflict. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019DFRovoZHMzeheXKkLNWKG
Keeps the change clear of the focus-restore lines the parallel fix branch edits. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019DFRovoZHMzeheXKkLNWKG
PR #4 adds the same tab lookup as findByTabId(container, selector, tabId). Use the same signature, text and keyboard comments so the two branches merge without a semantic clash, and rename this branch's keyboard test so the files don't collide. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016hW799u5Ji5k9khDAwu688
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UCGGgmHsCToooXTgSXpmq9
Merged
Owner
Author
|
I merged this into integration branch #2 at 7b4d765 (merge 8c303f6), after #3. Tab lookup and strip focus now come from #3, which uses your signature. Your ArrowLeft-from-new-tab fix and the Generated by Claude Code |
Choosing an item hid the focused menu button and dropped focus to body; closing a background tab left nothing to return to. Tab now closes the menu like Radix Menu (ZCode uses Radix ContextMenu), and after an action focus goes back to the tab, or to the active tab if that one was closed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016hW799u5Ji5k9khDAwu688
…start The 20ms hide timer runs from the second openTab, so on a loaded machine the probe slept before h.busy.add ran. Under 12-way parallel runs it failed 23 of 48; with the busy mark first, 0 of 48. CI runs this file through test:side-pane since 8d2d429. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KnTGLLVUgUjQG3f4cWBHJw
30f09c5 typed `cd '…'` without Enter so a foreground vim or password prompt would not receive it. But AI commands are written into the same PTY without clearing the input line (PowerShellExecutor writes `${wrappedCommand}\r`), so a jump left unsubmitted is joined to the next AI command. Under a real PTY on Linux, terminal-handlers.test.js "several views share one session" fails with `cd '/tmp/…'echo vcp-shared-…` -> `bash: cd: too many arguments`; on Windows the end marker never prints and the AI call waits for its timeout. Restoring `\r` makes that test pass again (only the Windows-only command-run case still fails here). Curly-quote doubling and the line-break refusal stay. The duplicate quoting test in terminal-command-run-watch.test.js is folded into terminal-cd-command.test.js. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KnTGLLVUgUjQG3f4cWBHJw
…scing a1bc4c3 added two whole-worktree `git diff --numstat` runs to every status read. On a VCPChat clone with 600 changed files that took getStatus from 45ms to 220ms (status 40ms, numstat 181ms), and the cost was paid by all nine internal callers: push and branch-switch prechecks, branch listing and the status panel summary, which runs its own `diff --numstat HEAD` on top. Counts are now read for getStatus (the Git page) and for the statuses handed back to the renderer after a write. ZCode reads numstat with status too (gitCliRepo.ts), so it stays there. 9f9a449's per-repository coalescing is removed. A caller that arrives while a read is in flight waits for the next read, so the common pair (status panel plus Git page) still ran two reads, just serially (434ms vs about 220ms for one). Out-of-order writes back to the Git page are already handled by 7d962eb's sequence number. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KnTGLLVUgUjQG3f4cWBHJw
17c29af skips re-extraction for a message id whose content length is unchanged, so a message edited to the same length that now contains a file write never got its file list. The cache now holds the content string itself (the same string the history already holds) and compares by identity, which is as cheap. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KnTGLLVUgUjQG3f4cWBHJw
5a8e8fe resets the address bar to the page URL on blur. Chromium also blurs the focused element when the whole window loses focus, so Alt-Tab to copy a URL from another app cleared what was typed. The reset now skips blurs where the document itself has no focus. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KnTGLLVUgUjQG3f4cWBHJw
Docs and contracts - Provider contract in SIDE_PANE_ARCHITECTURE.md now matches the code: mount context (scope, occurrence, restoredState), every handle method the controller calls, and a lifecycle section with the invariants (one mount per id, zero view resources while dormant, what never sleeps, suspend/resume follows visibility, failure paths). - Type-level requestClose, dormancy and load documented; 'detach' and isBusy examples describe what the code actually does. - Stale comments fixed (tab strip, launcher, tab utils, controller maps, lazy provider list, nonexistent gitSideProvider.js, eager example). Duplication - Menu arrow/Home/End handling shared via moveMenuFocus (4 copies; the browser copy had drifted: ArrowUp with nothing focused skipped the last item). - Controller visibility delivery and "focus unchanged" rule each in one helper. - Tool output and trajectory search hints live only in their tab types. Behaviour - Terminal restart uses the app confirm dialog (native confirm blocks the window); a second click waits on the same dialog. - Plan detail focus() moves focus into the tab instead of only reloading. Dead code - Unused toolOutputTabId / modelTrajectoryTabId exports, side chat handle setVisible, an unreachable branch and an unused local in side-pane-state. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019DFRovoZHMzeheXKkLNWKG
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019DFRovoZHMzeheXKkLNWKG
…egistered The chat and utility preloads are loaded by many windows (voice, assistant, notes, forum...), and each side pane handler module checked the caller on its own. ProjectForge's file revert and project delete checked nothing, and workspaces only after the last fix. A single policy table now lists, per preload domain, the app pages allowed to call (and the read-only channels open to other windows). main.js applies it at registration: the domain activator rejects a wrong window before loading the domain, and the directly registered workspaces, ProjectForge and source modules get a guarded ipcMain. Module-level checks stay as a second layer, as DSH asserts the sender per privileged handler. A contract test calls every channel of every policy domain from another window, a webview and a subframe, and checks main.js routes every side pane domain through the policy. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016hW799u5Ji5k9khDAwu688
ae9a99b coalesces terminal:data into one IPC per 16ms window, but the window opened on the first chunk, so every key echo reached the screen about 16ms late. The first chunk after a quiet period now goes out immediately and opens the window; chunks inside it are batched as before, and a window that had output opens the next one. ZCode forwards every chunk (terminalService.ts); flood throughput keeps the batching. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KnTGLLVUgUjQG3f4cWBHJw
…harp CI installs with --omit=optional, and loading the real fileManager pulls in sharp, which failed the Chat Kernel and UI contracts job. The test only needs the reference resolver registration. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016hW799u5Ji5k9khDAwu688
collectAgentDraftConfig falls back to currentSelectedItemRef for an empty name, and while switching Agents that ref already points at the next Agent. Clearing A's name, switching to B and back, then saving renamed A to B. The draft kept on switch now drops an empty name, so the restored form uses the name on disk. settingsManager.js is a guarded boundary file; its hash and rationale are updated. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KnTGLLVUgUjQG3f4cWBHJw
# Conflicts: # docs/contracts/generated/chat-event-graph.json
…side pane integration branch
…n fixed Conflicts follow #14: side chats keep dormancy 'keep'. Batch W's close-confirm and pending-mount rules stay for tabs that still sleep. # Conflicts: # docs/contracts/generated/chat-event-graph.json # modules/renderer/sideChatSurfaceOwner.js
…nd duplicates - Content asserts (exact Chinese copy, class names, markup, icon ligatures) are replaced by state: data-status / data-state / data-empty-reason / data-reason / data-action hooks, aria state, status type/code, and round-tripped user values. - Fixed-duration sleeps that raced debounces or timers now use node:test mock.timers (negative checks tick past the debounce) or the shared tests/helpers/wait-for.mjs; per-file waitFor copies are gone. - Tests coupled to internals (resource labels, private options, storage keys, internal call order, monkeypatched queue internals) assert observable effects. - Duplicates are folded into the surviving test; the vacuous dual-stream test now registers a real main-chat route. - New test for the restore guard: side chats restored after the user moved to another tab stay in the background (previously untested). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012AP71nAxrCFEpH7st6asf9
…er owner Every VCPLog toast registered its transitionend listener on the main window's DOM listener owner, which lives for the whole session and only released registrations on dispose. When the 500ms fallback removed the toast before transitionend fired (or the toast was dismissed by the notification page), the owner kept the listener and with it the detached toast forever: about 27 DOM nodes and 3 listeners per notification in a real Electron run. The owner now drops registrations that have finished (fired timeouts, fired once listeners), its timer cancel closure no longer shares scope with the callback, the toast's transitionend listener lives on the toast itself, and toast timers are left to fire and no-op instead of being cleared from outside. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SADzqzusVz3S3z7qBUSy8M
…to claude/project-thread-k7mpdc
If the reply finished on its own while a slow interrupt request was pending, the failed interrupt deleted it as if it were an empty placeholder (side chat and main chat). Now only a still-streaming placeholder is removed. If upstream accepts the interrupt but never sends a terminal, the side chat stops locally after 3s and saves the partial as cancelled instead of staying busy. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016hW799u5Ji5k9khDAwu688
…to claude/project-thread-20fk2r
…to claude/project-thread-1zv9nc # Conflicts: # modules/ui-system/side-pane/git/git-view.js # modules/ui-system/side-pane/modelTrajectorySideProvider.js
States now go error, then loading, then empty (as ZCode GitPane does), so a load in progress or a failed read is never shown as "nothing here": - Git: no "还没有工作区 / 添加工作区" before the workspace list arrives or when it fails; the error has a retry. - Code viewer picker: a failed workspace read says so; the file list error has an in-place retry. - Trajectory: no "请先选择智能体和话题" flash while subscribing. - Tool output: a failed command list shows the error with a retry instead of "还没有命令记录"; a rejected output read lands on the error bar instead of hanging on "加载中…"; a failed reload during a running command keeps the output on screen. - Side chat: the intro stays hidden while history loads; the history retry works from the keyboard. Layout and feedback: - Git diff: an open diff keeps the previous text while an edited file is refetched instead of collapsing to one line; failed diffs are not cached and have a retry. - Plan: retry buttons switch to "正在重试…" at once; a background refresh with a filter swaps results in place; filter errors retry. - Code viewer: reloading a shown file keeps it until the new text arrives; its loading icon actually spins. Docs: dormancy notes follow #14 (side chats stay mounted). Tests for the new cases use waitFor instead of fixed sleeps. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019DFRovoZHMzeheXKkLNWKG
… listener owner
…t send owner tests in test:workbench Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UCGGgmHsCToooXTgSXpmq9
…yAsahi/VCPChat into claude/project-thread-8tpfrr
- Plan detail: a focus target from the status panel is applied once and
then cleared from the saved tab, so restart or wake no longer jumps back.
- Side chat restore keeps the saved createdAt, so lists keep their order.
- A local-only side chat draft that cannot be read now warns the user
instead of silently restoring blank.
- Width persistence logs a {success:false} settings save result.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019DFRovoZHMzeheXKkLNWKG
The trajectory tab sleeps after 5 minutes hidden. Waking it reset every collapsed row and jumped the view to the bottom. It now captures its expansion overrides, commands and scroll position, and restores them when the remount still shows the same conversation. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019DFRovoZHMzeheXKkLNWKG
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by Roxy · project thread
Before:
window.openin a bare window.After: each of these behaves as in ZCode/DSH. Reopening mid-animation continues from the current width. Topic tabs only show, and only close, inside their own conversation. Side chats don't count toward the live-view limit. Browser tabs only sleep when that limit is exceeded, and the current tab stays awake while the pane is collapsed. Closing a side chat that has messages asks first. A message button respects the busy gate and leaves the draft alone. The filter waits for the input method's composition to end. Terminal links open http(s) only, in the side browser. Ctrl+C copies the selection.
npm run test:side-paneruns in the Chat Kernel and UI job.How: each fix has a JSDOM behaviour test that fails on the old code. Existing tests that opened topic tabs without a current conversation, or that relied on collapsing the pane to put a tab to sleep, now set the parent or switch tabs first. Tab-id lookup and tab-strip arrow-key focus were left to #3, which fixes the same thing. This branch merges cleanly with #3, and the combined
test:side-panepasses 451/451 locally.The findings and the order of the remaining fixes are in the project's review report (
sidebar/side-pane-adversarial-review-20261007.md).🤖 Generated with Claude Code
https://claude.ai/code/session_019DFRovoZHMzeheXKkLNWKG