Repository navigation
Conversation
RoxyAsahi
force-pushed
the
pr/side-pane-workbench
branch
from
October 6, 2026 00:00
7b75a5a to
b662f58
Compare
Owner
|
感觉这么做有很多问题 |
Owner
|
核心在于,怎么渐进化的声明周期和回收周期,以及收缩将来v工程界面和侧栏界面的广播一致性。 |
…ssion info Saving the session info wrote the browser draft first and gave up when that write threw, so a model switch or a parent snapshot refresh was lost whenever browser storage was full. The session info is now saved regardless. While the draft is not in browser storage, the input stays in the file instead of being stripped as already migrated, and the "draft not saved" retry stays visible. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ails or hangs Waiting on the model refresh meant a refresh error failed the whole list, and a server that never answers left the picker loading indefinitely because the models request has no timeout. A failed refresh now falls back to the cache, the wait is capped at 10 seconds, and opening the picker again while a refresh is running reuses it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The note derived "KB" from the character count, which understates the size of non-ASCII text (three bytes per CJK character). It now gives the file's line count and how many lines are shown. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rs and subscriptions in the view scope Releasing the view scope alone now tears down everything the two tabs registered, so a tab the controller drops without calling dispose leaves nothing behind. Closing the trajectory tab while the push registration is still pending no longer leaves a watch counted in the main process. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The button sits right of the create-topic button and only appears while the panel has something to show for the current conversation. Hiding is remembered across restarts; data refreshes never reopen a panel the user closed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The bar was a fixed 64px stub beside "N/M 已完成", which read as decoration. The count line now carries the percentage on the right, and a 4px bar below it fills the full width. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The side pane diagnostics now list, per mounted tab, how many listeners, timers, observers and subscriptions its view scope and child scopes hold, and the shared source diagnostics name the holders that keep a source alive. Both report counts and labels only, never payloads. A listener registered through a scope no longer runs once that scope has started releasing: release is asynchronous, so events that arrived while it was still in progress used to reach handlers of a view already being torn down. Timers behaved this way already. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n the view scope Each of these tabs now hangs its listeners, timers, observers and source subscriptions on a scope under the view scope the controller passes, so releasing the view (a tab put to sleep, a mount cancelled midway) tears all of it down even when the provider's dispose is never called. Their own bookkeeping (cleanup arrays, disposed flags, generation counters) goes away; whether a view is still current is read from its scope. A code viewer released while it is still loading stops instead of mounting into a view that is gone. The two listener adapters of the code viewer no longer exist, so their entries leave the contract registry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…passes The chat tab type now forwards the mount context, and the side chat surface creates its scope as a child of the view scope instead of the pane-wide one. Releasing the view alone therefore tears the surface down (stream listeners, composer, sync subscriptions), while dispose still runs the same teardown once and only then releases the scope, so the draft the controller captures on close is unaffected. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The command run records move out of PowerShellExecutor.js into commandRunStore.js, which has no load side effects: no Electron, no IPC registration, no config reads, no watchers. Output sanitising moves with it into terminalOutputSanitizer.js. The executor requires both and still exports the same functions, so existing callers keep working, and since Node caches the module both sides share one set of records. The terminal IPC handlers read and watch runs through the store, so opening the command output tab or the status panel no longer loads the executor (and with it node-pty) just to list runs. The executor's sibling modules are now listed in the packaged files; command-output-parser.js, which the executor already required, was missing there. The packaging test derives the list from the executor's local requires so a new sibling cannot be left out again. This changes the plugin's file layout, so it is offered as a proposal for the plugin maintainer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two more choices for the tool presentation setting, next to the original cards, compact rows and grouped folds: - 单行合并 (inline): one borderless row per call. A result that directly follows its request with the same tool name is shown on the request row; failures show as a problem label with the first error line on hover. Consecutive commands fold into a terminal group, consecutive reads and searches into a lookup group. - 整轮折叠 (process): the same borderless rows with an icon that turns into a chevron on hover and the first error line in red. After the reply finishes, everything from the first to the last tool block, including the text between them, folds into one "已处理" row; the final answer stays outside. Pairing is visual only: both blocks keep their own keys, states, disclosure memory and delete buttons. The turn fold is not marked as a protocol block, so read-aloud and context extraction keep its text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When an embedded app tab (V工程) covers the whole chat, the Git page has no layout, so pushes only mark it stale. Switching back fired none of the events that clear the flag (pointer enter, window focus, plan page switch), so a commit made in the other tab stayed on screen until the mouse moved over the list. A ResizeObserver on the page now re-reads once when it gets a size again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…h the other domains Git is registered eagerly: the module only wraps execFile, and the status panel reads the workspace list on its first frame, so deferring the require saves nothing. Repository watching still waits for the first window to subscribe. Quit now releases Git through disposeAll like every other domain. The handle wrapper keeps a separate ipcMain.handle(channel) branch so the contract scan still attributes every git: channel to this module. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The terminal ignored the view scope the controller passes, so the lifecycle inspector showed a live terminal view holding nothing while it had three listeners, a ResizeObserver and a debounce timer. They now go through the view scope and are released with it when the view sleeps or the tab closes; the shell session still follows the tab occurrence and keeps running in the stash. Regenerates the chat event graph for the current sources. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Upstream moved the window pin button out of the injected script in windowPinService into preloads/behaviors/pinButton.js; the Lucide pin icon from this branch now lives there. The vcp-ui dynamic listener sites moved by one line and the chat event graph is regenerated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…on moved The listener helpers in messageRenderer and appearance-studio are unchanged; only their line numbers shifted, which left the contract gate failing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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
…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
21a0a48 moved restart onto the app confirm dialog, which is async and focuses its own button, but the click handler still called term.focus() right after restart(). Focus went back to the xterm behind the dialog, so typing reached the shared shell while the dialog was open. The handler now leaves focus alone when a confirm will be shown; the dialog returns focus to the restart button when it closes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KnTGLLVUgUjQG3f4cWBHJw
…to claude/project-thread-1zv9nc # Conflicts: # modules/ui-system/side-pane/git/git-view.js # modules/ui-system/side-pane/modelTrajectorySideProvider.js
After #14 side chats use dormancy 'keep', so the type note that said unsent input should use isBusy() instead of keep, and the isBusy examples that named a sending side chat, no longer matched the code. The side chat handle's setVisible, removed as dead code in 21a0a48, came back with #14's revert and is removed again. The architecture doc's terminal example used a deps.notify that does not exist, and the side chat owner is about 770 lines, not 560. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KnTGLLVUgUjQG3f4cWBHJw
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
…s to keep Mutation runs against #15's tests (5ff688f) found three regressions they no longer catch: - regenerate's removeMessageById(id, true): the mock stopped recording the save flag, so a renderer removal that saves again (and can write to the renderer's current topic) passed. Both delete and regenerate assert [id, false] again. - terminal toolbar listeners bypassing the view scope: `listener > 0` goes back to the exact count of 3. - the pane's active / collapsed classes after a reversed animation: they drive the layout (side-pane-shell.css) and the visibility check, so the four class asserts come back. Nothing tested that side chats stay mounted (#14 dropped the sleep tests only); setting the chat type back to 'none' passed every test. A draft-restore case now hides a side chat past hiddenMs and checks the same handle, draft and references are still there. Also drops the duplicate busy mark the #15 merge left in side-pane-dormancy. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KnTGLLVUgUjQG3f4cWBHJw
…yAsahi/VCPChat into claude/project-thread-8tpfrr
The source editor rejected only a literal .git segment. On Windows the 8.3 short name GIT~1, trailing dots/spaces and the ::$INDEX_ALLOCATION stream name reach the same directory (git's is_ntfs_dotgit), and on any platform a workspace symlink pointing at .git let source:write-file overwrite .git/config (core.fsmonitor, hooks path). Segments are now checked with git's NTFS rules, and the real parent directory is checked for .git too. The Git panel uses the same segment check, and the reveal fallback compares paths with path.relative instead of a case-sensitive prefix. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016hW799u5Ji5k9khDAwu688
…low it Opening the side terminal tab resized the shared PTY to the narrow pane even while the terminal window (and AI commands) were using it at full width, and a side view without focus drew the PTY's output at its own width. - terminal:create only presets the size when no PTY is running yet, and returns the PTY's real cols/rows. - PowerShellExecutor tells mirror views when the PTY size changes; the main process forwards it as terminal:resized (after flushing buffered output). - The side view claims the size only while it has focus (and only when it differs); without focus it draws at the PTY's size, as DSH's read-only views do, and fits to its own width again when the user clicks in. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012AP71nAxrCFEpH7st6asf9
…s it Sending in the side chat disabled its textarea until the reply finished. Focus fell to <body>, so anything typed during the reply was dropped, the next message needed another click into the box, and Esc could not stop the reply. Clicking the stop button also left focus on <body> because the button hides itself. The textarea now stays enabled the way the main chat's does; the existing busy guard still keeps Enter from starting a second round, so the next question can be written while waiting. Esc in the box stops the reply, like ZCode's Esc-to-stop, and the stop button hands focus back to the box. If a pending question is retracted while the user has written something new, it is put back in front of that draft instead of being dropped. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019L2bZomLn4NVoxS4Rv5Bkv
…sion guards # Conflicts: # modules/ui-system/side-pane/side-pane-types.js
Move/up/cancel were only heard on the strip, so releasing outside it (or losing pointer capture when the dragged node was replaced by a render) left the drag session alive and every later press was ignored. They are now heard on the document for the length of a press. Renders that arrive during a drag (a browser tab changing its title or icon while loading) are held until the drop, so the dragged node is never swapped out. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012AP71nAxrCFEpH7st6asf9
- 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 restart button called session.restart() and then term.focus(). When the shell is running, restart() first opens the confirm dialog, which focuses its own button; the terminal then took focus straight back. Esc still closed the dialog through its document listener, but the keystroke also reached xterm and bash read it as a Meta prefix, so the next character typed was eaten (typing "echo still" ran "cho still"). Focus the terminal first and then restart: the dialog takes focus from the terminal and hands it back when it closes, and Enter on it confirms without a stray newline in the shell. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019L2bZomLn4NVoxS4Rv5Bkv
…llapses it At window widths of 960px or less the pane floats over the conversation and covers the send button, and the only way out was the collapse button or Ctrl+Alt+B. A click in the conversation now collapses it like a drawer, unless that click itself opened or switched a side tab (code block "副屏", ask in side chat). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012AP71nAxrCFEpH7st6asf9
…ix Windows file manager fallback
Opening the terminal tab and typing right away lost every key until the PTY attached. Input typed during the connection is now held (up to 4K chars) and sent in order once it connects; it is dropped if the connection fails, so a command meant for a terminal that never started is not replayed later. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012AP71nAxrCFEpH7st6asf9
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
…orkbench The .git alias and symlink regression test lives in source-service.test.js, which no CI job ran. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UCGGgmHsCToooXTgSXpmq9
…inal restart confirm keeps focus # Conflicts: # modules/ui-system/side-pane/terminalSideProvider.js # tests/side-pane-terminal-lifecycle.test.mjs
…row window overlay # Conflicts: # package.json
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UCGGgmHsCToooXTgSXpmq9
… leak, performance and QA threads Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UCGGgmHsCToooXTgSXpmq9
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.
Warning
还没做完,大概还要几天。 先开成 draft 大概看下整体方向,功能和样式还在调。很多联动都还没做完也还没测。。。后续也建议先开个独立分支合并。。。
概述
这个 PR 给 VCPChat 的聊天窗口加了一套右侧侧栏工作台。原来要切窗口、开新页面才能做的事情,现在都放进聊天旁边的标签页里:侧边聊天、浏览器、终端、模型调用轨迹、V工程计划与 Git 改动、通知与审批。侧栏跟着当前会话/话题走,打开的标签、宽度和选中状态都会持久化。
同时,聊天区加了一块浮动的会话状态面板(Git 改动 / 计划进度),回复下面多了文件改动摘要,聊天头部可以换成胶囊样式,还有 messenger 气泡模式。
截图里的聊天内容做了模糊处理,本机路径换成了
C:\workspace\…。功能一览
1. 侧栏外壳与标签条
prefers-reduced-motion。2. 新标签页 / 启动页
3. 通知与一键审批
通知中心放进了启动页的「通知」分段,待审批的工具调用和普通通知在同一个列表里分组显示(待审批 / 通知 / 已处理)。
截图里的审批请求是为了演示塞进去的样例,没有真的发出审批。
4. 侧边聊天(per-topic side chat)
5. 内置浏览器
本质是想联动后端的,还没弄。。
6. 终端与命令输出
![终端]
我知道原来前端里已经有这个了。。
(https://raw.githubusercontent.com/RoxyAsahi/VCPChat/659c53f3eea11b6a46c16a0a7267ecde1b8df4d9/terminal.png)
7. 模型调用轨迹(调用轨迹)
8. V工程计划详情 + Git
9. 会话状态面板
10. 聊天区相关改动
11. 主聊天UI尝试
试了试比较激进的UI改动,取消了标题行,只保留胶囊显示当前助手,兼打开关闭侧栏功能。抄grok bot的,让界面更简洁。


反正外观控制台里提供个可选项。。默认关闭的。。标题行上别的小按钮还没全部考虑好放在哪里。
架构
完整说明见
docs/SIDE_PANE_ARCHITECTURE.md。styles/ui-system/side-pane-*.css按区域拆分并有固定顺序,只用主题 token,不写硬编码颜色和!important,由scripts/check-ui-system.mjs守卫。资源生命周期与数据一致性
根据 review 意见补了一轮,主要是两件事:侧栏的资源不再只有「全开 / 全关」两档,而是按需逐级启停;同一份数据只有一个来源。都建在上游已有的
LifecycleScope、VCPStateChannels、VCPContributions和VCPLifecycleInspector之上,没有另起一套。逐级启停
import()。终端、调用轨迹等主进程 IPC 领域也是先登记通道,第一次调用才require并初始化。一个数据一个来源
createSharedSource按持有者计数,第一个持有者出现时启动,最后一个离开后延迟一会儿再停。同一个窗口里的多个界面共用一份。命令运行记录、Git 变化、ProjectForge 变化、选中的 Git 工作区都改成了这种方式。state:subscribe/state:unsubscribe声明自己要什么,主进程只推给订阅了的窗口,不再按页面 URL 猜谁需要。第一个窗口订阅时才启动对应资源(例如对这个仓库的文件监听),最后一个离开时停掉。.git、监听挂不上或中途出错)会告诉订阅方,退回到「窗口获得焦点时补读」。看得见
顺带修掉的问题
push.default等配置影响。这一轮的打磨
测试与验证
npm run verify:development的测试和检查全部通过:297 个测试文件、1802 个测试,0 失败、0 跳过、0 超时;之后的 UI 系统、chat 证据、设置归属、发布面、Chat Kernel 使用方等检查也全部通过。其中重新构建 Rust 数据服务的冒烟检查这一轮没跑(本机正在运行的应用占用了那个二进制),这一轮也没有改 Rust 代码。node scripts/check-ui-system.mjs通过。main(build(projectforge): 补齐依赖,消除编译warning #232、fix(next-ui): 修复内嵌子应用分页切回时卡死在加载遮罩及黑屏问题 #233 合入之后),无冲突。合并时处理了 4 个文件:main.js(两边的窗口配置都保留)、main.html(铃铛按钮直接放进它最终的位置,侧栏按钮保留在它前面)、PowerShellExecutor.js(保留单独包装脚本,并加上关闭分页器的设置)、composerModelSelect.js(采用模型名完整显示)。single-chat-request-orchestrator测试在main上因为新加的相对路径 import 无法从data:URL 解析而失败,这里顺手修了。还没做完 / 已知问题
这是 draft,下面这些我知道还没做完,合并前会逐项处理或者单独说明。
要在合并前处理
npm run verify:development一条命令跑完全部测试和检查。docs/SIDE_PANE_ARCHITECTURE.md里 Git 一节还在讲独立的gitSideProvider.js标签;实际上 Git 已经变成计划标签里的一页,入口是git/git-view.js。stream_options.include_usage,用来拿真实的 token 用量(调用方已经指定时不会覆盖)。个别不常见的上游可能不接受这个字段,需要确认能否兼容,或者做成开关。已知问题(会继续改)
side-pane-types.js),没有 TypeScript 检查。vendor/xterm,打包流程需要一起带上,还没在打包产物里验证过。🤖 Generated with Claude Code