fix: 处理 Copilot 二轮 review 的 10 条评论

- BrowserView: 即使 host 在白名单,GUI 自身 origin 永不给 allow-same-origin(裸 host 条目覆盖所有端口时可提权)
- intercept: revealInExplorer 相对路径经 resolveSidebarPath 解析后再 revealPaths(交付物路径是相对的)
- intercept: ctx.betterSidebar 改为 ctx.get(#357 纤维链读取约定)
- FileTree/EditorHost/TreePanel/tabs: 把 revealed 贯通到树行渲染 + 高亮类 + scrollIntoView(此前高亮永不渲染)
- state.revealPaths: 重建祖先目录时保留前导分隔符(POSIX/UNC 此前目录永远折叠)
- state.schedulePersist: 仅活动会话写入全局宽度(targeted open 不再用非活动会话的过期宽度覆盖拖拽值)
- browser.probe: GET 回退显式取消 body(否则流式/大响应挂住 socket)
- terminal-font: leading generic 前只放 symbols-only 字体;fully-patched 放 generic 后(避免 hijack 度量字体)
- sidebar.module.css: 恢复拖拽反馈(#130 的 silent resize 与 PR 描述冲突,取 HEAD)
- Sidebar: 剩余 4 处 ctx.betterSidebar 改 ctx.get
- tests: 补 revealPaths/iframeSandboxFor/terminal-font 回归测试 + EditorHost/FileTree 测试补 revealed prop
This commit is contained in:
HuanLinOTO
2026-08-24 18:40:04 +08:00
parent 9048dbe42d
commit 699ce448a1
19 changed files with 171 additions and 27 deletions
+13 -3
View File
@@ -730,8 +730,13 @@ export function revealPaths(state: SidebarState, cwd: string | undefined, files:
revealed.push(file)
const parts = file.split(/[\\/]+/).filter(part => part !== '' && part !== '.')
const separator = file.includes('\\') ? '\\' : '/'
// Keep the original leading separator(s) when rebuilding ancestor dirs:
// FileTree matches expansion against ABSOLUTE paths, so dropping the
// root (POSIX `/w/src` �W `w/src`) or a UNC prefix (`\\server\share`)
// would leave every ancestor collapsed and the row unreachable.
const prefix = file.startsWith('/') ? '/' : file.startsWith('\\\\') ? '\\\\' : file.startsWith('\\') ? '\\' : ''
for (let i = rootParts.length; i < parts.length - 1; i++) {
expanded.add(parts.slice(0, i + 1).join(separator))
expanded.add(prefix + parts.slice(0, i + 1).join(separator))
}
}
if (revealed.length === 0) return state
@@ -1454,8 +1459,13 @@ export class SidebarStore {
private schedulePersist(sessionId: string, state: SidebarState): void {
// Keep the cross-session width in sync: any width change (drag, fullscreen
// toggle) becomes the shared width for every conversation.
writeGlobalWidth(state.width)
// toggle) on the ACTIVE session becomes the shared width for every
// conversation. A targeted open persists an INACTIVE session (reduceFor)
// and must not clobber that global — its width is stale by definition,
// so writing it would break "last drag wins".
if (sessionId === this.snapshot.sessionId) {
writeGlobalWidth(state.width)
}
// Per-session debounce timers: one session's pending write must never
// cancel another's (targeted opens schedule writes for INACTIVE
// sessions while the active session may already have one pending —