# dsh-plan-build-toggle DSH 网页客户端插件:把 plan mode 的入口变成 **OpenChamber 式的常驻 `Plan|Build` 切换控件**,挂在对话输入框的模式控件区(原生 plan chip 的同一座位 `conversation.input.plan`)。 [English](README.md) | 中文 ## 为什么 DSH 本身已端到端内建 plan mode([`@deepseek-ai/dsh-plan-mode`](https://github.com/deepseek-ai/deepseek-harness):`/plan`、`/plan off` 命令、按会话的 `plan` 投影、`exit_plan_mode` 评审退出),也有 composer 控件(`dsh-client-ui-plan`)——但原生 chip **只在 plan mode 启动后**才显示「Plan ×」,默认的 Build 态下输入框上看不到任何模式控件。 OpenChamber 的形式不同:输入条上常驻模式控件,且裸 **Tab** 键即可在 Build/Plan 间切换。本插件补上这个形式差:同一座位、同一命令通道、不另立第二套模式词汇。 ## 功能 - **常驻二段胶囊**(`Plan|Build`):当前模式高亮(Plan 用 warn 色系,Build 用中性色),一眼看出所处模式。 - **双向切换**:点 Plan 执行 `/plan`、点 Build 执行 `/plan off`——与手敲命令走同一条 `command.execute` 通道。 - **Tab / Shift+Tab 切换**(OpenChamber/opencode 惯例):光标停在本会话输入框内即生效。约束从严: - 只认裸 Tab——带 ⌘/Ctrl/Alt 的复合键一律放行; - 更早的处理器已 `preventDefault` 时自动让位(例如未来的自动补全菜单用 Tab 确认); - 焦点必须在本会话自己的编辑器内(设置弹窗、搜索框、其他会话都不触发); - `plan` 投影不存在(草稿、未组合 plan-mode 的 preset)或切换请求在途时不拦截,Tab 保持原生行为。 - **状态真源是 host 投影**:显示的目标态取 `pending ? !active : active`(host 折叠值,非客户端乐观态);agent 运行中切换进入 pending,投影帧抵达即自动纠正。 - **失败内联提示**:命令准入失败时在控件旁显示错误,不吞错。 - **干净取代原生 chip**:bundle patch 同时 `disable` 内建 `ui-plan` 行,单实例座位不冲突;卸载即还原。 ## 边界(与 DSH plan mode 一致) - Plan mode 是**软引导**(policy 提示段),不是强制沙箱;要硬性只读,请另行组合 sandbox / 审批 preset。 - 草稿会话(尚无 sessionId)不显示控件——plan 状态按会话记录。 - 退出评审仍由模型的 `exit_plan_mode` 触发,走 Web 的 plan-review 专用渲染。 ## 安装 ### 经应用内 Community Market 发布后可在 **市场 → 可安装** 里选中来源浏览到本插件(npm 支撑、仓库回链验证),或直接用官方 CLI: ```bash dsh plugin --profile desktop add dsh-plan-build-toggle@0.1.0 ``` ### 源码本地安装(无需发 npm) ```bash git clone https://github.com/jdqingm/dsh-plan-build-toggle.git cd dsh-plan-build-toggle bash scripts/install.sh desktop # 拷入 profile 并注册 bundle ``` 两种方式装完后都需**重启 DSH 应用**——浏览器 roster 在 host 启动时计算。 ## 卸载 1. 从 `~/.dsh/profiles//package.json` 的 `dsh.profile.bundles` 移除 `dsh-plan-build-toggle`(经 Market/CLI 装的从那里卸载); 2. 删除 `~/.dsh/profiles//node_modules/dsh-plan-build-toggle`; 3. 重启应用——原生「Plan ×」chip 自动回来(`ui-plan` 行不再被 disable)。 ## 实现结构 两个半边,无构建步骤: - `lib/client.js`——浏览器半边:手写 `window.__ModuleLoader__.load` 载荷(与官方客户端插件同一种模块格式)。React 取自共享模块图;CSS 在 `data-plugin-css` 守卫下注入一次。 - `lib/index.js`——host 侧空 `apply()`,让包出现在 host roster 行上;`package.json` 的 `dsh.client` 声明浏览器半边及其加载依赖。 - `cordis.patch.yml`——bundle 补丁层:插入自己这行并停用内建 `ui-plan` 行(座位交接)。经 `dsh.bundle.patch` 生效,把包名加进 `dsh.profile.bundles` 即完成挂载。 迭代:改 `lib/client.js` → 重跑 `scripts/install.sh` → 刷新页面。插件资源以 `cache-control: no-cache` 提供,roster 不变就无需重启应用。 ## 许可 MIT