Menghuan1918
0995a58454
fix: 回应独立 CR 的 P2/P3 意见(0×P1)
...
- P2-1 onOpen 收到真正落地的 tab:url 新建场景从 landed 态按 id 反查
(补丁后对象),回调不再拿到未打 url 的 tab
- P2-2 插件设置行显式取值源:FeatureSettingsRows 新增 valueSource prop,
插件行读自己的 pluginSettings blob——插件键与宿主 pref 同名时不再
静默读宿主值(原投影合并有读宿主写插件的读写不对称);prop 命名避开
Object.prototype.valueOf 冲突
- P2-3 consumer 类型检查接入工程:package.json 新增 check:consumer-types
脚本 + CI 在 build 后执行;脚本补 lib/types 存在性前置检查(防假绿)
- P3-1 url seed 只在新建时应用:dedupe 聚焦的 tab 不再被覆写 path
- P3-2 closeTab/activateTab 未知 id 严格 no-op:store.reduce 对同引用
结果短路(不 notify、不重写 localStorage),根因修复 + 回归测试
- P3-3 closeTab/activateTab 增加可选 scope(cwd 随回调传递),Sidebar
双树激活/关闭路径传入 { sessionId, cwd }
- P3-4 定向打开不再自动展开不可见 session 的面板
- P3-5 Sidebar 终端 pty 释放查找覆盖双树(底部面板终端不再漏掉 HTTP
兜底)
- P3-6/P3-7 与 Nits:AGENTS.md/指南文档化(dedupeKey 纯函数、meta 用
null 清除、回调 scope 的 cwd 语义);consumer fixture 补常量导出覆盖
测试 +6(url 落地保真 / url 不覆写聚焦 / 未知 id 无 notify / 定向打开
不展开 / scope 随回调 / valueSource 键隔离),464 全绿;typecheck、
build、consumer 校验全部通过。
2026-08-14 21:28:41 +08:00
Menghuan1918
319d34ed09
fix: 回应 Codex 审查的 4 条意见(2×P1 + 2×P2)
...
- P1 state.ts reduceFor:恢复共享 uid 计数器到调用前水平(目标 session 的
max 可能低于激活 session 已用 id,否则下次 mint 撞 id 损坏布局);
回归测试实证:无修复时 split 产生重复 pane id
- P1 service.ts 生命周期分类:dedupe 聚焦判定改为基于输入态的存在性
(dedupeKey 匹配或 id 匹配都算聚焦)——按 path 去重 + 新 id 打开时
不再对从未插入的幽灵 tab 触发 onOpen(资源泄漏源)
- P2 SideCardSection:乐观 prefs ref(optimisticRef)驱动嵌套 map 合并
(tabsEnabled/viewersEnabled/pluginSettings),同 tick 多次写入不再
互相丢弃;mergePluginSetting 抽纯函数 + 单测
- P2 state.ts schedulePersist:每 session 独立防抖定时器(persistTimers
Map),定向打开不再取消其他 session 的待写;单测验证双写都落盘
2026-08-14 21:13:53 +08:00
Menghuan1918
82a69546d2
feat: 公共 API 基座化(v0.12.0,纯增量)— 类型/能力/设置 seam 全面补齐
...
- 类型生态:service 模块 re-export SidebarTab/SidebarState/SidebarStore/
SidebarSnapshot/SidebarDiffRef/TabType/SessionScope/SidebarPrefs 等公共
状态词汇(此前 TS2459 无法命名);context-types 去 Node 依赖(结构镜像
SidebarHttpRequest/Response/UpgradeSocket/Head),client 可达声明图零
node:* 引用,外部消费者无 @types/node + skipLibCheck:false 可编译
(scripts/check-consumer-types.sh 守护)
- 服务能力:version/features 版本与能力探测(单调只增)、getSnapshot/
subscribeState 状态订阅、updateTab(title/path/meta)、activateTab、
openFile(路径派生 id 与内置拦截一致)、openTab(seed, scope?) 定向到
指定 session(store.reduceFor 不扰动 UI)
- TabDescriptor:badge 角标(99+ 封顶,抛错吞掉)、onOpen/onActivate/
onClose 生命周期回调(service 路径派发,dedupe 聚焦触发 onActivate
而非 onOpen,抛错不打断流程);SidebarTab.meta JSON 可序列化跨刷新
持久化(sanitize 宽松保留)
- 设置 seam 开放:PrefsSchema 增 pluginSettings 开放嵌套 map(z.dict),
settings.pluginToggles(插件自有行,同 switch/text/number 控件)与
settings.render(自定义面板,含 store/service/prefs/updatePluginSetting/
close props);viewer 卡片 v0.12.0 起也有齿轮设置弹窗
- FileViewerDescriptor.load 增第三参 AbortSignal(EditorHost 生命周期中止)
- UI 接线:tab 激活/关闭改走 service(跨树激活修复 + 回调触发)、+ 菜单
传 scope、TabBar badge pill 渲染;active-session scope 走原 reduce 路径
保证 notify
- 测试 +23:生命周期/定向打开/updateTab/activateTab/openFile/meta 持久化/
reduceFor/能力常量(与 package.json 锁步)、consumer-types.ts 编译门、
api-surface.spec.ts(client 图零 node 依赖守护);既有 430 测试全绿
- 文档:AGENTS.md 修 available 三参示例并补全部新 API;external-plugin-
guide.md 刷新至 v0.12.0(7 tab + 9 viewer、设置 seam 开放、类型导入
路径建议);README×2 服务化小节同步
未发版 npm(用户决定);分支 feat/plugin-api-v012。
2026-08-14 20:44:43 +08:00
Menghuan1918
9aeb0464ce
fix: sidebar crash #31 — layout-push CSS-variable leak + per-tab error containment
...
Two failure modes of a sidebar render crash, fixed together:
1. Layout-push leak (root cause): the layout-push effect wrote
--dsh-sidebar-width/--dsh-sidebar-height on document.documentElement
without a cleanup, so ANY unmount of the Sidebar (error-boundary swap,
plugin disable, HMR) left the variables behind and layout.css kept
squeezing #root with a stale margin — "the sidebar cannot be hidden"
until a full reload. The effect now removes both properties on cleanup
(the CSS fallback 0px restores the layout).
2. Crash containment (UX): the root error boundary previously replaced the
WHOLE sidebar — toggle cluster included — with an error strip on any tab
crash. A new shared RenderBoundary (src/client/RenderBoundary.tsx) now
scopes containment per tab: a crashing viewer/editor shows a dismissible
strip inside its own pane while the toggle cluster, the other tabs, and
the panel stay alive. The descriptor is rendered as a real element
(createElement) so even top-level component throws land in the boundary,
not in TabContent's own render. The root boundary remains as the last
resort for Workbench/shell errors.
Tests (tests/sidebar-crash.spec.tsx, jsdom + real Sidebar/store harness):
- unmount clears both CSS variables (red before the fix: 307px leaked)
- a crashing tab is contained: strip in-pane, collapse button alive,
layout push still live
- the retry button recovers a tab whose crash has since been fixed
Full suite: 425 tests pass; tsc --noEmit clean.
2026-08-14 18:29:31 +08:00
Menghuan1918
37d91befdb
fix: 终端延迟到容器有尺寸后再 open(零尺寸初始化崩溃,WKWebView 底部面板展开后终端空白,#25)
2026-08-14 14:53:03 +08:00
Menghuan1918
d83490c93e
feat: 终端二级设置支持自定义字体(字体族 + 9–32px 字号,实时生效)
...
- SidebarPrefs 新增 terminalFontFamily(空 = 跟随主题等宽字体)/ terminalFontSize(9–32,默认 13),PrefsSchema 与 parsePrefs 同步校验
- 声明式设置 API 扩展:SidebarSettingToggle 支持 type: 'switch' | 'text' | 'number'(缺省 switch,向后兼容)+ min/max/placeholder/unit
- 终端卡片齿轮弹窗新增「终端字体」「终端字号」两行:blur/Enter 提交、number 钳制、提交失败回滚草稿
- TerminalView 经 store 订阅实时应用字体(options + re-fit + resize),懒加载 chunk 无新增依赖
- 新增 resolveTerminalFont 纯函数(回退链:用户字体 → 主题 token → 内置等宽栈)并单测
- 版本 0.11.0,README/AGENTS.md/设计文档同步
2026-08-14 14:36:49 +08:00
Menghuan1918
047f48337d
feat: 侧边卡片设置页视觉重构——分组容器卡片、计数徽标、自定义开关、加宽设置弹窗(描述 + 完成按钮),卡片齿轮与勾选徽标纵向对齐同尺寸
2026-08-14 14:07:52 +08:00
Menghuan1918
0f9af851ae
fix: 聊天产出文件行拦截改用 slots.inject 延迟注册(issue #15)
...
conversation.chat.turnTail 是宿主 ui-conversation 在 conversation.chat.node
children 表中声明的子插槽,直接 slots.register 会早于声明而抛
"not declared (a parent entry's children table must declare it)",
导致拦截注册失败回退默认 deliverables 行为并弹诊断条。
改用 ctx.slots.inject 等待声明落账后再注册(与官方
dsh-client-ui-deliverables 同款写法):已声明则同步注册,未声明则
在声明提交时注册,声明撤销自动卸载、重声明重新注册,fiber 卸载
幂等清理。editor tab 禁用门控与 openInSidebar 座位不变。
新增 tests/turn-tail-intercept.spec.ts 覆盖已声明/待声明/提前销毁
三种注册时机、editor 禁用回退与 openInSidebar 座位接线。
2026-08-14 00:41:08 +08:00
Menghuan1918
0a07cff6a3
fix: 适配 DSH 0.1.x 的 [data-slot] DOM 结构,恢复 Session log 挤压与布局
...
DSH 0.1.x 把 slot 宿主包进 [data-slot] 容器:
#root > div[data-slot=root] > div.frame > div.centerCol
> div[data-slot=conversation] > div.root[data-phase]
> div[data-slot=conversation.session.header] > header
旧选择器 #root > div > div:nth-child(2) 选中的是 data-slot=root 包装
层的第 2 子元素(不存在,其唯一子元素是 frame),导致:
- 折叠态 header 右 padding 挤压失效(Session log 胶囊被按钮簇盖住)
- 底部面板纵向挤压 / 拖动过渡 / 减少动效全部失效
- Sidebar.tsx 中间列定位器(#root > div 的 children[1])失效,
底部面板宽度测量回退为 0
修复:全部改用 [data-slot] 语义锚点——
- 中间列:#root > div[data-slot=root] > div > div:nth-child(2)
- header:body[data-dsh-sidebar-collapsed]
[data-slot=conversation.session.header] > header
- Sidebar.tsx 定位器:#root [data-slot=conversation] 的 parentElement
(= centerCol,探针实测确认)
v0.10.1
2026-08-13 22:53:31 +08:00
Menghuan1918
b31a9a601f
fix: md 预览代码块复制按钮跟随 DSH i18n(MarkdownText 补传 codeLabels)
2026-08-13 10:26:57 +08:00
Menghuan1918
1bfabbbfc2
feat: 适配 DSH 命名契约重命名(jobs/terminal/webServer)并处理 Session log 按钮位置
...
- tasks→jobs:ctx.jobs / job_output / job_id / session/jobs / jobsBySession / Job* 词汇
- httpServer→webServer(inject + augmentation + register/registerUpgrade)
- autoOpenTasks→autoOpenJobs 偏好键
- 右上角开关簇回到角落,注入 CSS 挤开 header 的「Session log」胶囊
2026-08-13 09:06:55 +08:00
Menghuan1918
ce052faee8
feat: 预览区划选文字浮窗「添加到对话」(md 预览 + 兜底 code 查看器)
2026-08-13 01:39:59 +08:00
Menghuan1918
a2bb04bce0
fix: 底栏宽度直测中间列,修复启动竞态下底栏跑到最左边
...
- 底栏 left/right 由「appSidebarWidth + detailsWidth + 面板宽反推」改为直接测量 AppFrame 中间列的 bounding rect(唯一真源,展开/收起动画期间跟随 margin-right 挤压)
- 拖动期间暂停测量(avoid per-frame 重渲染卡顿),拖动结束显式重测
- #root MutationObserver 自愈启动竞态:旧实现一次查询失败后永远卡在零尺寸回退(表现为 1px 细条贴在视口最左)
2026-08-13 01:17:01 +08:00
Menghuan1918
9c5293c94b
feat: 界面文案跟随 DSH 的 i18n(ctx.locale 语言偏好、实时切换、词典入注册表)
2026-08-13 00:43:28 +08:00
Menghuan1918
88f116338a
feat: 任务管理页二级开关「有新后台任务时自动展开」(autoOpenTasks)
...
- 新 pref autoOpenTasks(默认开):当前会话出现新后台任务(任意新任务 id,
非仅首个)时自动展开侧边栏并打开任务管理页,与 autoOpenSubagent 并列
- 注册为 subagent tab 的声明式二级开关(设置页齿轮弹窗,key 在 PrefsSchema)
- Sidebar 新增独立基线 effect(镜像子代理自动展开;刷新不触发、开关关闭/类型
禁用不触发);detectNewTask 纯函数(settling/同 id/他会话不触发)
- 测试:detectNewTask 用例、prefs 解析断言、builtins 开关清单、schema 默认值
断言;全量 371 通过
2026-08-12 23:04:30 +08:00
Menghuan1918
b90251cce5
fix: 遵守仓库硬约束(禁止修改 DSH 源码)——tasks.output 改为会话事件回放
...
- 回退对 harness peek 缝隙的依赖:输出查看不再调用注册表,改为从属主
会话事件日志回放 task_output 的 tool/call+tool/result 对(模型已读内容)
- read:false 时面板提示「模型尚未读取」;'(no new output)' 增量与错误
结果过滤;截断上限不变;kill 走注册表原生 API(缺席时 503)
- AGENTS.md 新增 §0 硬约束:禁止修改 DSH 源码、挂载只走 cordis.patch.yml
+ profile 机制;设计文档同步实施偏差
- 测试:tasks-routes 重写为回放语义(拼接/过滤/read 标志/截断/503),
视图新增 read:false 提示用例;全量 369 通过
2026-08-12 23:04:30 +08:00
Menghuan1918
8c521be26e
chore: v0.8.0 — 任务管理页(子代理拓扑 + 后台命令底部输出 Dock、两击确认终止)
...
- tab 更名为任务管理:同页显示当前树全部后台命令(bash/pwsh 徽标、状态、时长、属主、退出码)
- 点击任务行在侧边栏底部共享 Dock 显示输出(sticky 终端式、自动跟随、单一轮询实例)
- 运行中任务两击确认强制终止;大量任务时列表保持紧凑、排序变动不跳动
- 新增 /sidebar/api tasks.output(非消费 peek,不偷模型 task_output 字节)与 tasks.kill 路由
- 修复任务镜像清空时的 React #300 hook 顺序崩溃(useCallback 移到提前 return 之前)+ 回归测试
2026-08-12 23:04:30 +08:00
Menghuan1918
cb9a7cf751
feat: 内容型打开自动展开承载面板(宽视口)
...
宽视口下 openTab 带 path/url seed 时,承载落点 pane 的面板折叠则自动展开:
落点在底部树展开底部面板,否则展开右侧面板;去重聚焦既有 tab 同样展开
(打开必须落在视野内)。窄视口抽屉逻辑不变;类型型打开(+ 菜单、agent
终端自动补 tab)不展开。同步 AGENTS.md / README 文档与测试。
2026-08-12 18:39:17 +08:00
Menghuan1918
ffc2270d77
fix: 移动端合并改为底部标签直接并入右侧栏(迁移式,删除双工作台堆叠方案)+ 窄屏断点收紧至 768px(不对齐宿主 1024)
2026-08-12 18:27:28 +08:00
Menghuan1918
b391ebffed
feat: 移动端侧边栏体验 — 窄视口(<1024px)双工作台合并为单面板抽屉(可拖分隔条、高度与底部面板互通)、不提供底部面板按钮、文件/外链打开自动展开、新会话默认收起
2026-08-12 18:20:34 +08:00
Menghuan1918
bab659f8bd
feat: 客户端懒加载分块 — 重依赖(Univer/xterm/CodeMirror/Office)拆独立 bundle 按需下发(/sidebar/bundle 路由 + __dshChunks__ 注册表),核心启动仅 ~325KB
2026-08-12 18:19:44 +08:00
Menghuan1918
c7af7dccdb
feat: 侧边栏双面板 — 右上角按钮簇、底部面板(首展自动终端/关闭按钮)、跨面板拖 Tab、拖动性能优化
...
- 右上角持久按钮簇:横向排列、独立 glyph(底栏/侧拉)、始终钉在视口右上角(侧边栏展开时位于其标签条右端,真实挤占 Tab 空间)
- 底部面板:独立工作台,只挤占中间 Agent 输出区(不覆盖左右侧边栏),右缘分割线、关闭 x、首展自动开终端(二级设置开关,默认开)
- 两面板拐角双向拖动调节尺寸;拖动改为 rAF 节流直写 DOM,消除卡顿;面板尺寸互钳制防丢失
- 跨面板拖 Tab(合并/拆分落点),moveTab/moveTabToEdge 支持跨树移动
- 欢迎卡片改响应式网格;布局/几何全部按会话持久化,旧状态平滑迁移
2026-08-12 16:43:24 +08:00
Menghuan1918
31b78f7941
fix: IME 组合期按键守卫 — document capture 拦截,防内联第三方组件劫持方向键(#562)
...
Univer InputNumber 的 keydown 对 ArrowUp/Down 直接 preventDefault 且无
isComposing 守卫,中文输入法组合期候选词方向键被吞(#562 回归,同类 #535)。
新增 src/client/ime-guard.ts:document capture 阶段对组合期按键(isComposing
或 keyCode 229)stopPropagation,运行于 React 委托与任何原生监听之前,覆盖
内联依赖的全部劫持路径;apply 时经 ctx.effect 注册(HMR-safe)。
- tests/ime-guard.spec.ts:12 个用例,覆盖纯决策、原生 document/input 监听、
React 合成事件路径与 disposer 恢复(新增 jsdom devDependency)
- 全量 280 测试通过,typecheck 干净
2026-08-12 01:16:20 +08:00
Menghuan1918
302c20109f
feat: 内置 HTML 预览器 + 沙箱浏览器 tab(v0.5.0)
...
- html viewer:.html/.htm 沙箱 iframe 预览(相对资源经 /sidebar/html 路径编码路由加载,CSP sandbox + 信任围栏 + cwd 边界),预览/编辑切换
- browser tab:+ 菜单多开(browser:<n>),地址栏后退/前进/刷新/在浏览器中打开(15px 图标),URL 与标题按会话持久化(patchTab + nextBrowser)
- 安全:iframe 一律不透明源沙箱(无 allow-same-origin/top-navigation、no-referrer、权限策略全禁、禁 srcdoc);地址栏仅 http(s)、拒绝 localhost 等本机地址;界面实时沙箱状态(关闭红色警示)+ 临时解锁/恢复
- 设置:htmlViewerNoSandbox / htmlViewerDefaultUnsafe / browserNoSandbox / browserInterceptLinks 四个声明式二级设置(默认安全)
- 外链拦截:聊天/界面 http(s) 外链默认在侧边栏打开(openTab seed.url),Ctrl/Cmd 点击放行
- 嵌入拒绝面板:browser.probe 路由探测 X-Frame-Options/frame-ancestors,被拒站点显示原因 + 在浏览器中打开/仍然加载
- 设置页可滚动;内置 7 tab + 9 viewer;新增设计文档;268 个测试全绿
2026-08-11 19:28:48 +08:00
Menghuan1918
67ec136e98
feat: 拦截聊天区文件打开 — 聊天里的文件点击默认开进侧边栏编辑器
...
包装 ctx.workspaces.openPath(聊天区文件打开的唯一漏斗),接管时
经 openSidebarFile 开进 sidebar 编辑器而非系统默认应用;由
interceptOpenPath pref(默认开)与 editor tab 启用态双重门控,
拒接时原样放行,dispose 还原原始方法引用(HMR 安全)。
- prefs-shared/config/prefs: 新增 interceptOpenPath 字段(默认 true)
- openpath-intercept.ts: 零依赖包装器,纯插件层面实现,不改 DSH
- intercept.tsx: registerOpenPathInterception 接线(当前会话 scope)
- SideCardSection: 常规区新增开关卡片(zh/en 文案)
- 测试: 包装器单测 6 个 + 接线集成测试 + pref 解析/设置页断言更新
2026-08-11 16:48:59 +08:00
Menghuan1918
0e20413641
feat(settings): 小卡片响应式网格 + 二级设置原生弹窗
...
- 清单改为小卡片,置于 repeat(auto-fill, minmax(148px,1fr)) 响应式网格,
一行自适应排多个、随宽度换行;卡片 = 顶行(图标+标题省略+勾选徽标
钉在最右端)+ 类型/扩展名描述行(省略号 + title 完整值)
- 声明了 settings.toggles 的卡片右下角渲染齿轮角标(IconSettingsOutline16,
仅父级启用时显示),点击打开原生 Modal 弹窗:DSH 原生设置行
(标题/描述 + 品牌强调原生复选框 + hairline 分隔)
- 弹窗行体抽为 FeatureSettingsRows 供直测;Modal 条件挂载
(常闭挂载在测试 dual-react 分裂下会崩)
- 全部 token 使用 DSH 原生别名(interactive-bg-active/button-primary-fill/
label-* 等);规格测试改为断言网格卡片与齿轮
- 文档(README/AGENTS.md/设计文档偏差记录 5/6)同步
2026-08-11 14:27:16 +08:00
Menghuan1918
1b0d2085bb
feat(settings): 开关改为可点击卡片 — 高亮即启用
...
- 清单项(侧边栏内容/文件预览)、嵌套相关设置与「新会话默认打开」
全部从复选框行改为 <button aria-pressed> 卡片:整卡点击切换,
视觉状态即状态(启用 = 品牌描边 + 交互选中填充 + 勾选徽标;
禁用 = 中性 hairline + 文字降级),悬停仅在禁用卡上显示
- 嵌套相关设置渲染为更小的缩进卡片(仍仅父级启用时显示);
宽度输入保持原生设置行(数值非开关)
- 设置导航齿轮为宿主 shell navIcon 硬编码(无插件注入点),
按用户选择不改宿主、保持现状
- 新增 settingsGeneralTitle 文案;规格测试改为断言 aria-pressed
- 文档(README/AGENTS.md/设计文档偏差记录)同步
2026-08-11 14:19:18 +08:00
Menghuan1918
89ddf8a9e7
feat(settings): 侧边栏设置声明式化 — 注册表驱动的开关清单 + 内置支持插头化
...
- SidebarPrefs 新增 tabsEnabled / viewersEnabled 开放 map(缺省 = 启用),
PrefsSchema 用 z.dict 校验,旧文档零迁移
- TabDescriptor/FileViewerDescriptor 新增声明式设置面:viewer 补 title/icon,
两者补 settings.toggles(子代理声明 autoOpenSubagent、终端声明 agentTerminalTools)
- 服务新增 isTabEnabled/isViewerEnabled;openTab 拒绝禁用类型、matchFileViewer
跳过禁用 viewer
- SideCardSection 重写为注册表驱动清单:侧边栏内容(图标+类型id+开关+嵌套开关)
+ 文件预览(图标+标题+扩展名+开关),开关持久化 + 乐观提交回滚
- 消费点 gating:+ 菜单过滤、子代理自动展开、agent 终端 reconcile、
产出文件拦截(editor 关闭时回落默认行)、新会话 explorer 预置
- 内置 viewer 补 6 个手绘 outline 图标;store 快照携带 prefs 使开关变更即时生效
- 测试:service gating / builtins 声明 / parsePrefs map 校验 / schema 断言 /
新增 SideCardSection renderToString 规格
- 文档:README 设置小节、AGENTS.md 描述符与服务方法、新增设计文档;版本 0.5.0
2026-08-11 14:08:55 +08:00
Menghuan1918
39455ff18e
docs+fix: exports 加 ./client/api 别名;catch-all+detect 纯嗅探规则;设计文档偏差记录
...
- package.json exports 新增 './client/api' 别名指向 service.d.ts(设计文档
§7 的公共导入路径,api.ts 已被 fetch 助手占用故不换文件名)
- matchFileViewer 补充 sniff-only 规则:exts: [] + detect 的 descriptor 在
无 head 时不得盲认领(AGENTS.md 的 magic-sniffer 示例原实现会吞掉
图片/PDF 等真实 viewer 的文件);有 head 时只认领 detect 命中的。
新增测试断言三种情形(无 head / magic 命中 / magic miss)
- docs/plans/2026-08-11-service-registry-design.md 追加 §17 实施偏差记录:
dedupeKey+single 语法糖、available 三参、单趟匹配+嗅探规则、EditorHost/
TextEditor 拆分、fs.read head 字段、导出别名、+ 菜单 disabled 行、
保留的 store 级操作、内置清单、测试
- AGENTS.md:available 三参、single 语法糖、viewerId、单趟匹配+head 语义、
8 viewer 内置清单、openTab seed.title 可选、参考实现指向 builtins/ 目录
- README 服务化章节:6 viewer → 8 viewer,标注全注册表驱动
2026-08-11 11:25:37 +08:00
Menghuan1918
1b24bc8ff2
refactor(builtins): 目录重组为 builtins/{tabs,viewers,index},清理死代码与内联样式
...
- 新 src/client/builtins/ 目录:tabs.tsx(6 个 tab descriptor +
TERMINAL_LIMIT/uiTerminalCount,terminal 配额知识离开 state.ts)、
viewers.tsx(8 个 viewer descriptor)、index.ts(registerBuiltins 聚合
注册 + 幂等 disposer);删除旧 builtins.tsx
- icons.tsx:删除零调用者的 tabTypeIcon 及多余 import(图标已归
descriptor.icon,映射随 tab 类型一起注册化)
- OrphanedTab:内联 style → sidebar.module.css 新增 .orphanedType 类
- 新增 tests/builtins.spec.ts(6 tab + 8 viewer 注册断言、code catch-all、
binary-download NUL detect、office 优先级、disposer 幂等)
- 新增 tests/orphaned-tab.spec.tsx(sanitize 保留未注册类型 tab、
OrphanedTab 占位渲染);tests/browser-globals.ts 供拉入 xterm/
CodeMirror 的 spec 首个 import(模块求值期需要 self/document/navigator)
- service.spec 补 openDiffTab 与 diff dedupeKey 一致性用例
2026-08-11 11:22:28 +08:00
Menghuan1918
0467101faf
feat(editor): 拆分 EditorHost/TextEditor — code/markdown 成为注册 viewer,fs.read 增加 head 字节
...
- 新 src/client/EditorHost.tsx:'editor' tab 组件。matchFileViewer →
按 fetchStrategy 分发(纯逻辑在 editor-load.ts)→ 渲染 viewer.component;
binary 无匹配时渲染共享 BinaryDownload。标题栏保留在 host
- 新 src/client/TextEditor.tsx:code/markdown 的 viewer 组件(CodeMirror +
保存/脏点/截断横幅 + md 预览/编辑切换),内容由 host 经 fsRead 传入,
自身不再 fetch;按 viewerId === 'markdown' 分支(不再解析扩展名)
- 新 src/client/editor-load.ts:planFirstMatch / planFsReadOutcome /
decodeHead 纯函数,策略分发可单测
- 新 src/client/binary-download.tsx:共享下载兜底组件(viewer 注册与
host fallback 同一 UI)
- host src/index.ts:fs.read 二进制响应增加 head 字段(base64,前 4KB,
唯一 host 触碰,设计文档 detect 机制所需);客户端 FsBinaryResult 同步
- builtins:注册 8 个 viewer(新增 markdown exts md/markdown、code
catch-all -100);binary-download 增加 NUL detect + 复用 BinaryDownload
- 删除 src/client/EditorView.tsx(职责一分为二);陈旧注释同步
- 新增 tests/editor-load.spec.ts(12 用例)
2026-08-11 11:16:36 +08:00
Menghuan1918
db0ba36610
fix(service): 注册表核心修正 — seed.title 优先、single 语法糖、available 三参、单趟匹配、dedupe 委托
...
- service.openTab 的 seed.title 优先于 descriptor.title(修复编辑器 tab 标题
从文件名变成 t('editor') 的回归);title 变为可选,descriptor.title 兜底
- TabDescriptor 新增 single?: boolean 语法糖(展开为 dedupeKey: () => id),
dedupeKey 显式给出时优先
- available 签名升级为 (ctx, scope, state) 三参(设计文档 §5.2 的超集),
Sidebar.buildNewTabOptions 与 terminal 配额检查适配
- matchFileViewer 从三趟全局遍历改为设计文档 §8.2 的单趟 per-descriptor 算法:
priority 降序,每个 descriptor 先 detect(head 可用时)再 exts,[] 为 catch-all
- applyDedupe 的落位逻辑委托给 state.openTabInActivePane(消除重复实现),
同时 id 安全网现在也覆盖 service.openTab 路径(同 id 打开聚焦既有 tab)
- state.ts:删除零引用的 SIDEBAR_BUILTIN_TAB_TYPES;TERMINAL_LIMIT 移入
builtins.tsx(state 不再关心 terminal);openTab reducer 改名 openTabInActivePane
- Sidebar.tsx 删除 TERMINAL_LIMIT/allLeaves 死 import;openDiffTab 补充
dedupeKey 一致性注释(其按 id 去重与 diff descriptor 的 dedupeKey 同规则)
- tests/service.spec.ts 重写匹配/去重断言并新增 title/single/available 用例
2026-08-11 11:12:29 +08:00
HuanLinOTO
e40039ceec
refactor: 完全解耦内置 tab — dedupe 归 service,图标归 descriptor
...
- state.openTab 简化为 '添加到 pane + id dedupe',移除 isSingle/editor-path/diff-id 三分支
(dedupe 策略归 service.openTab 通过 descriptor.dedupeKey 统一)
- intercept.tsx openSidebarFile 改走 service.openTab(editor descriptor 的 dedupeKey 接管 per-path 去重)
- Sidebar.tsx 两处 subagent 触发(auto-activation + jump-back)改走 service.openTab
- service.openTab seed 加可选 id 字段(editor 需要 path-derived id)
- TabBar.tsx 加 getTabIcon 回调,tab 标签图标查 descriptor.icon 替代 tabTypeIcon
- split-pane.tsx 移除 tabTypeIcon 兜底(buildNewTabOptions 已从 descriptor 填 icon)
- Workbench/NodeView/LeafView 透传 getTabIcon
- tests/unit.spec.ts 更新 openTab 测试(id dedupe 替代 type dedupe)
2026-08-11 01:39:20 +08:00
HuanLinOTO
291158a3ba
feat: 服务化改造 — ctx.betterSidebar 注册表(registerTab/registerFileViewer),内置 6 tab + 6 viewer 迁移到 registry,v0.4.0
...
- 新增 src/client/service.ts: BetterSidebarService 接口 + createBetterSidebarService 工厂
- registerTab/registerFileViewer 返回 disposer(HMR-safe,挂到 cordis fiber)
- matchFileViewer 三段匹配: priority desc → detect(内容嗅探) → exts
- openTab 走 descriptor.dedupeKey 统一去重(替代 isSingle/editor-path/diff-id 三分支)
- createTab 回调支持自定义 tab 创建(terminal 的 nextTerminal 递增)
- 新增 src/client/builtins.tsx: 6 内置 tab + 6 内置 viewer 自我注册(吃狗粮)
- tab: editor(hidden) / explorer / git / subagent / terminal / diff(hidden)
- viewer: image / pdf / docx / xlsx / pptx / binary-download
- code/markdown 保持 EditorView 内联兜底(matchFileViewer 返回 undefined 时走 fsRead+CodeMirror)
- 新增 src/client/OrphanedTab.tsx: 未注册 type 的占位组件(插件未加载时降级)
- 改造 src/client/index.tsx: ctx.provide('betterSidebar') + registerBuiltins
- 改造 src/client/Sidebar.tsx: TabContent/buildNewTabOptions/onNewTab 查表替代 switch
- 改造 src/client/EditorView.tsx: 预览路由查 matchFileViewer 替代 if/else 链
- 改造 src/client/state.ts: TabType 联合字面量→string,sanitizeNode 接受所有 string type
- 改造 src/context-types.ts: declare module 'cordis' 加 betterSidebar 字段
- 改造 src/index.ts: re-export Context + 服务类型(consumer import type 触发 augmentation)
- package.json: 加 ./client/service exports 子路径,version 0.4.0
- tests/service.spec.ts: 12 个新测试覆盖 register/dispose/matchFileViewer/dedupe/createTab
- tests/unit.spec.ts: sanitizeState 测试更新(未知 type 不再被拒绝,渲染 OrphanedTab)
2026-08-11 00:31:23 +08:00
Menghuan1918
069c754877
feat: Git 面板真正的 diff 显示 + VSCode 式历史与右键高级操作,v0.3.0
...
- 真正的统一 diff 显示:hunk 头 + 双行号 + 红绿着色,长行换行无横向滚动,untracked 全新增视图
- 点击变更/历史自动打开独立 diff tab(首次在下半栏新增分栏,同变更去重聚焦,临时页签刷新不恢复)
- 历史行:分支徽章 + 作者/相对时间,懒加载分页(20 条/页 + 加载更多)
- 右键菜单:文件行(打开/暂存/放弃更改/复制路径)、历史行(复制哈希/信息/还原/捡取),破坏性操作确认弹窗
- 修复暂存/未暂存分类错误(' M' 被误标为已暂存导致 diff 恒空)
- 修复 git 相对路径解析(会话在仓库子目录时 diff/discard/show/read 可用)
- host 新增 git.commit-diff / git.discard / git.revert / git.cherry-pick 路由与 log 分页
2026-08-10 22:29:24 +08:00
Menghuan1918
8edfbfde44
fix: 修复 agent 终端工具审查问题(terminal_list 输出契约、跨会话归属、UTF-8 截断边界等),工具注入改为设置面板开关(默认关闭)
2026-08-10 21:43:58 +08:00
HuanLinOTO
6f62d19905
feat: 为 agent 暴露 8 个终端工具(terminal_create/list/send/read/wait_for/resize/signal/close),侧边栏自动同步 agent 终端 tab,v0.2.0
2026-08-10 19:08:58 +08:00
Menghuan1918
61b18cbefe
feat: Subagent 拓扑页面(主代理根节点 + 分层树 + 实时工具调用 + 自动激活设置),v0.1.9
2026-08-10 17:24:42 +08:00
HuanLinOTO
eb5059574e
fix: route images before binary detection
2026-08-10 16:54:13 +08:00
HuanLinOTO
a6befc8b6b
feat: add Office, PDF, and PPTX previews
2026-08-10 16:46:01 +08:00
Menghuan1918
4060bd53e1
fix: 分割线改为 1px 发丝线(保留 7px 拖拽热区),v0.1.3
...
- 分割线视觉从 7px 实底色块改为居中 1px 发丝线(::after 伪元素)
- 悬停/拖拽时线变色为强调色,不再整条填充
- 拖拽热区保持 7px 不变;reduced-motion 下伪元素过渡一并禁用
- version 同步 0.1.3(package.json + dsh.plugin.json)
2026-08-09 23:53:52 +08:00
Menghuan1918
9b00135de4
feat: DSH-native flat chrome + Side card settings (plugin-owned settings wire)
...
- Remove every box-shadow; panel uses the native sidebar fill surface
(--dsw-specific-sidebar-fill) with a hairline border, the collapse
control becomes the native 36px circular rail button, the active tab
uses the native interactive-bg-active fill, and rows/typography follow
the native 34px/14px session-row recipe.
- New 'Side card' settings section in the DSH Settings shell: open-by-
default for new conversations (boolean) and default width share as a
percent of the window (20-60). New sessions seed from these prefs;
existing per-session layouts stay untouched.
- The settings namespace is read/written through the plugin's own fenced
/sidebar routes calling the settings seam in-process: the DSH settings
RPC domain only serves allowlisted namespaces to configuration clients,
so third-party namespaces cannot use connection.api.settings (reads are
silently filtered, writes refused with 'not exposed'). The seam still
persists to the user settings document with revision-guarded writes.
2026-08-09 23:49:58 +08:00
Menghuan1918
81ab0c5312
fix: cross-platform path handling and tests
...
- fs-tree joins listed paths with the platform separator (Windows rows
previously leaked mixed separators)
- media route containment check goes through isWithin(): separator-
tolerant and case-insensitive on win32, so case-mismatched Windows
paths are not misclassified
- relativeTo compares case-insensitively (Windows / case-insensitive
macOS volumes) while keeping the caller's own casing in the output
- tests: platform-conditional path assertions, os.tmpdir() instead of
/tmp, isWithin/relativeTo/resolveSidebarPath win32 coverage
2026-08-09 22:07:03 +08:00
Menghuan1918
9b153844e9
feat: explorer file download via the host route; editor line numbers
...
- explorer row menu gains a download action (files only); the host route
serves raw bytes with Content-Disposition: attachment, binary-safe
- editor enables line numbers
2026-08-09 22:07:03 +08:00
Menghuan1918
e2ee427ea3
feat: per-session store + configurable host limits; fix divider drag runaway
...
- state: side-store per conversation, injected as a prop instead of a module singleton
- host: schemastery Config (read/media/list/terminal limits) resolved from cordis config
- shape: types/exports/peerDependencies for the web profile + plugin-shape spec
- fix: split divider emits incremental deltas so drags track the cursor 1:1
- docs: README refresh
2026-08-09 22:07:03 +08:00
Menghuan1918
fec53cef0d
fix: split panes duplicate when a file opens — stale uid collisions with persisted ids
...
The uid counter is module-global and resets on every reload, while the
persisted split tree keeps its old ids (pane:1, split:1, ...). A split
created AFTER a reload minted a fresh "pane:1" beside the persisted
"pane:1"; mapLeaf then visited BOTH leaves, so every open (explorer click,
produced-files chip) landed the tab in both panes of a top-bottom split.
- loadState seeds the counter past the raw persisted ids before sanitize
- setSession re-seeds on cache hits (another session's smaller max must
not leave the counter below this session's ids)
- sanitize re-ids stale duplicate pane/split ids (heals states already
corrupted) and follows the activePane rename
- openTab falls back to the first pane when the active pane is gone
instead of silently dropping the open
Unit tests cover the re-id heal and the stale-activePane fallback;
verified end-to-end in headless Chrome: a state with two "pane:1" leaves
loads healed and a file click lands in exactly one pane.
2026-08-09 00:14:27 +08:00
Menghuan1918
c81de3374f
fix: @-reference button no-ops — read conversation via ctx.get, not the ctx property
...
Cordis only grants property reads for services declared in the plugin's
inject list; every other read throws "cannot get property X without
inject" (the app's own plugins read 'conversation' through ctx.get for
this reason). referenceInChat now resolves the conversation service lazily
via ctx.get('conversation') and no-ops when absent. Verified end-to-end in
headless Chrome: clicking the @file button on root/dir/file rows now
appends the at-relative-path mention to the composer draft with no
console errors.
2026-08-09 00:01:56 +08:00
Menghuan1918
f091ac322b
fix: sidebar crash #310 — referenceInChat hook sat behind the no-session early return
...
The new useCallback was declared after the early return for the no-session
state, so the first render (no session) ran fewer hooks than the follow-up
render once a session arrived — React error #310 , boundary strip on load.
Moved the hook above the early return (with a sessionId guard) and verified
headlessly: the error no longer appears and the panel renders fully.
2026-08-08 23:51:42 +08:00
Menghuan1918
6e36a99c88
feat: explorer copy-path & @-reference actions; icons on tabs and + menu
...
- Tabs, the + menu, and empty-pane cards carry per-type icons (folder /
branch / terminal / code file); a terminal glyph joins the icon set
- Hovering an explorer row reveals an @文件 button at the far right that
appends @<relative path> to the composer draft (session-scoped input
service, space-separated append)
- Right-clicking a row opens a context menu: copy relative / absolute
path (writeClipboard + transient in-row "copied" label)
- Rows become div role=button (Enter/Space) to host the inner button;
relativeTo helper unit-tested
2026-08-08 23:46:14 +08:00
Menghuan1918
439adf3967
ui: align the sidebar with the app's own chrome (rows, buttons, motion)
...
Explorer rows become the app's tree-row cell — 34px, radius 8, inset by
the scroll padding, 22px indent step, and the session-row mount fade —
and git rows get the same cell plus the proper selected fill
(interactive-bg-active). Icon buttons switch to the app's 28px circle
pattern (ui-workspace), headers settle at the 36px section rhythm, and
keyboard focus gets a visible ring everywhere per the styling policy,
with a prefers-reduced-motion block turning off the slides and fades.
2026-08-08 17:30:45 +08:00