Files
dsh-worktable/02_process/PRD.md
T

434 lines
32 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# dsh-worktable(工作台)PRD
> 版本:v0.2 草案 · 日期:2026-08-16 · 状态:v2 已实现并验收;§12 多项目分栏框架(v3)与 §13 乐高式工作区框架(v4)设计定案、待实现
> 关联项目:dsh-travelatlas(第一个入驻项目)、上游参考 dsh-reminder(文件夹结构)
## 1. 项目定位
**dsh-worktable 是 DeepSeek Harness Web GUI 的一个「工作台」容器插件**:在左侧侧边栏的「工作区」
(官方会话/工作区浏览区)下方划出一条分隔线,其下开辟「工作台」区块,用于收纳**不同于 DSH 默认模式的
agent 级项目**(如旅行图鉴 TravelAtlas),让用户可以像管理应用抽屉一样管理自己安装的项目。
- 一句话:**侧边栏里的「应用抽屉」,agent 级项目的家。**
- 与官方关系:纯增量插件,不替换、不禁用任何官方插件(与 dsh-plugin-ya-workspace-sidebar 的替换路线相反)。
## 2. 背景与问题
- DSH 的侧边栏只有「工作区」(会话浏览)与「设置」两层,没有承载用户自装项目的位置;
- 现有项目(如 dsh-travelatlas)只能挤在 `sidebar.footer.action` 底部,各自为政、无统一入口与元信息;
- 用户希望有一个与「工作区」对等的「工作台」区域,统一收纳、搜索、整理自己的 agent 级项目。
## 3. 目标 / 非目标
### 目标(v1 原型,本窗口已完成)
- [x] 侧边栏底部(会话列表下方、设置行上方)渲染「工作台」区块:分隔线 + 标题 + 三按钮 + 项目列表;
- [x] 标题左侧 ≡ 拖动手柄:按住上下拖动整个区块,松手停靠(浮动位置持久化);
- [x] 三按钮照抄官方「工作区」头部:搜索(展开输入框过滤项目)、视图选项(分组/排序)、添加(占位符);
- [x] 项目注册协议:子座位 `sidebar.worktable.project`,任何插件注册即可入驻;
- [x] dsh-travelatlas 迁入成为第一个项目(含工作台缺席时的降级回退)。
### 目标(v2,§10 定案,本窗口已完成)
- [x] 视图选项简化:取消分组方式,只留排序(手动/最近),旧 groupBy 状态忽略;
- [x] 卡片规范 v2 渐进上报协议(owner props 扩展,全部可选,v1 卡片零改动兼容);
- [x] 「管理项目…」编辑模式:显示名改名 / 隐藏 / 手动排序(拖拽 + ↑↓),持久化;
- [x] 添加(+) 真实逻辑:接入指引面板 + 本地快捷方式条目(新标签打开);
- [x] 使用埋点:卡片点击上报,「最近」排序生效;
- [x] 完整 zh/en 词典接入 dsh-client-locale(NS worktable);
- [x] dsh-travelatlas 卡片升级协议 v2(报到/埋点/排序/隐藏/改名)。
### 非目标(明确不做)
- 不替换 ui-sidebar / ui-workspace / 官方任何组件;
- 不做「工作台自身的独立路由主页」——管理能力并入区块内编辑模式(见 §5.5);
- 不做项目市场/安装器(生态里已有 dsh-plugin-hub / dshfind;工作台仅提供外链)。
## 4. 用户故事
- 作为用户,我想在侧边栏一个固定的地方看到我装的 agent 级项目,而不是散落各处;
- 作为用户,我想拖动工作台区块到侧边栏里更顺手的高度,并且下次打开还记得;
- 作为用户,我想像搜索会话一样搜索我的项目;
- 作为插件作者,我想用几行代码让我的项目入驻工作台(拿到卡片 + 打开逻辑)。
## 5. 功能规格
### 5.1 区块结构(自上而下)
```
══════════ 分隔线 ══════════
[≡] 工作台 [🔍][视图选项][+]
┌───────────────────────────┐
│ 项目卡片(0..n,来自子座位) │
└───────────────────────────┘
```
- 标题文案:`工作台`(locale 键 `worktable.title`,en: `Worktable`);
- 底部悬浮面板避让:停靠态下检测侧边栏列内贴底的 fixed 面板(如 dsh-usage 余额 dock),
与区块重叠时以 margin-bottom 整体让位到面板上方,双方互不遮挡、各自可拖动;停靠期间 2s 轮询跟随面板移动。
- ≡ 手柄:`pointerdown` 捕获,垂直拖动 >6px 进入浮动模式(position:fixed 跟随指针,限制在侧边栏列宽内、
顶部不低于品牌行下沿、底部不超出设置行上沿);松手:与默认停靠位(footer 原位)距离 <32px 则回弹停靠,
否则保持浮动位置;持久化键 `dsh.worktable.view.v1`(字段 `query/searchOpen/orderBy/dock/floatTop`,旧版 groupBy 字段忽略);
双击 ≡ 复位到默认停靠;标题与 ≡ 均可作为拖拽手柄;浮动上限按区块实际高度计算(停靠位紧邻其下);
松手时按指针落点判定——越出有效落点区(底部/顶部/侧边余量 24/24/80px)即回归拖前位置;
- 悬浮窗几何与 sidebar 联动(只宽度/水平定位,高度由拖拽决定):向上遍历父链识别 sidebar
(className 含 SidebarRoot/sidebar,或 aside/nav,到 body 为止);ResizeObserver 实时跟随;
dockWidth = sidebar 宽 − paddingLeft − paddingRight − 40px(每边内缩 20px);
left = sidebar 左边缘视口坐标 + paddingLeft + 20px;找不到 sidebar 或宽度 ≤0 时降级
left 固定 14px、宽度不设内联(交 CSS min-width:176px / max-width:264px);
侧边栏折叠/展开保持原停靠位置——折叠态以「项目图标框」(收纳所有项目 emoji)显示在拖前高度,
展开即复原;图标框在折叠动画结束后(320/750ms 双次重测)按收敛后的折叠列几何水平居中;
仅窗口尺寸变化时回弹 footer 停靠。
### 5.2 三按钮(照抄官方工作区头部,逻辑作用于项目列表)
| 按钮 | 图标(primitives) | 行为 |
| --- | --- | --- |
| 搜索 | 🔍 | 点击展开输入行(Esc / 点 ✕ 收起);输入即过滤项目卡片与快捷方式(query 经座位 owner props 传给每个卡片,卡片自行判断是否隐藏) |
| 设置(原「视图选项」,2026-08-17 更名) | 滑块 icon | 右侧 fixed 弹窗直接内嵌「排序方式(手动/最近,默认手动)+ 管理项目展开列表(含变更视图)」,不再二次点击进入管理面板 |
| 添加 | + | 展开「添加项目」面板:接入指引(注册即入驻说明 + 插件市场外链)+ 本地快捷方式表单(§5.6) |
> 官方工作区头部三按钮 = 搜索 / 视图选项(ViewOptionsMenu) / 添加工作区(+),已逆向确认。
### 5.3 项目注册协议(子座位,v2 卡片规范)
工作台组件在注册 `sidebar.footer.action` 时声明子座位:
```ts
ctx.slots.register({
name: 'sidebar.footer.action',
id: 'dsh-worktable',
order: 20,
children: { 'sidebar.worktable.project': { kind: 'list', scope: 'root', owner: ProjectOwnerProps } },
}, WorktableSection)
```
卡片注册约定:`id` 为项目唯一 id(如 `travelatlas`),`order` 为默认排序(注册序)。
**owner props v2(渐进上报协议,全部可选)**:
| 字段 | 类型 | 含义与卡片行为 |
| --- | --- | --- |
| `query` | string | 当前搜索词;卡片自行判断是否返回 null |
| `wide` | boolean | 侧边栏是否展开 |
| `order` | string[] | 当前排序下的 id 序列;卡片用 `style={{ order: indexOf(自身id) + 1000 }}` 参与排序(+1000 偏移保证未上报的 v1 卡片在前) |
| `hidden` | string[] | 被隐藏的 id 集;包含自身 id 时返回 null |
| `nameOverrides` | Record<string,string> | 显示名覆盖表;卡片优先显示覆盖名(编辑模式改名) |
| `managing` | boolean | 编辑模式标记(卡片可据此减弱交互) |
| `reportMeta(meta)` | 回调 | mount 时上报 `{ id, name, icon }`,供管理条渲染;回调引用稳定 |
| `reportUsed(id)` | 回调 | 点击时上报使用时间戳(「最近」排序埋点) |
兼容性:未上报元信息的 v1 卡片零改动照常显示(按注册序排在最前),只是不参与排序/隐藏/改名。
机制依据:列表座位渲染器输出 `display:contents` 锚点且错误边界不产生 DOM 包裹,卡片根节点即
`.dsh-wt_projects` 的直接 flex 子项,CSS order 生效(已核实 web-react 渲染器实现)。
参考实现:dsh-travelatlas `src/client/index.tsx` 的 `WorktableCard`。
### 5.4 降级回退协议(对项目插件)
项目插件应实现:先 `ctx.slots.inject('sidebar.worktable.project', ...)` 注册工作台卡片;若工作台插件未安装
(座位永不出现),超时(~2.5s)后回退到 `sidebar.footer.action` 注册独立入口。参考 dsh-travelatlas
`src/client/index.tsx` 的实现。
### 5.5 管理项目(编辑模式)
- 入口:视图选项菜单「管理项目…」;「完成」退出(编辑状态不持久化)。
- 管理条逐项目列出(含未上报元信息的卡片,名称回退为注册 id):≡ 拖拽排序(HTML5 drag)+ ↑↓ 按钮 +
改名输入框 + 隐藏/显示切换(🙈/👁,隐藏后卡片区消失、管理条内可恢复)。
- 快捷方式条目在管理条中显示并可删除(✕);「恢复默认」清空排序/隐藏/改名覆盖(保留 lastUsed 与快捷方式)。
- 编辑模式下项目卡片区整体弱化(opacity + pointer-events:none)。
- 所有变更写入 `dsh.worktable.projects.v1`(见 §6)。
### 5.6 本地快捷方式(添加面板)
- 「+」展开添加面板:接入指引(项目=插件、注册即入驻,协议见 §5.3)+ 插件市场外链
(https://github.com/hikariming/dshfind,已核实可访问;dshfind.com 未验证、不链死链)。
- 快捷方式表单:名称 + 图标(emoji,可选,默认 🔗)+ 链接(校验 http/https);提交后立即出现在
项目卡片区下方,标「本地」角标;点击新标签打开(noopener)。
- 快捷方式参与搜索过滤;只存 localStorage,无任何网络请求。
### 5.7 国际化
- 词典:`01_content/src/client/locales.ts`,NS `worktable`,zh 为键集唯一来源,en 全量对齐。
- 接入方式(照 dsh-reminder):client inject `['slots','locale']`,apply 中 `ctx.locale.register(NS, { zh, en })`;
package.json `dsh.client.inject` 声明 `@deepseek-ai/dsh-client-locale`(peerDependencies 可选)。
- 宿主 locale 服务缺席时 t 回退 zh 词典,保证工作台独立可用。
## 6. 架构与技术方案
- 插件包:`01_content/`(dsh.plugin.json + cordis.patch.yml + build.mjs,参照 dsh-travelatlas / dsh-usage);
- 服务端:最小 cordis 插件,注册 `GET /api/worktable/health`(`inject: ['webServer']`),无业务路由;
- 客户端:单文件 CJS(`window.__ModuleLoader__.load` 握手),external react / @deepseek-ai/*;
- 注入座位:`sidebar.footer.action`(order 20,位于 dsh-usage 之后);
- 服务注入:`['slots','locale']`(locale 词典见 §5.7;宿主缺席时回退 zh 词典);
- 图标为 emoji 字符(🔍/☰/+/≡);
- 排序机制:owner props 下发 order 序列,卡片以 CSS order 参与排序(渲染器 display:contents 锚点已核实,见 §5.3);
- 子座位注册跟踪:apply 中 `ctx.slots.subscribe` + `entries()` 维护模块级 id 序列;
- 持久化:
- `dsh.worktable.view.v1`:query/searchOpen/orderBy/dock/floatTop/consoleTheme
(控制室主题 dark|light|system,缺省 system);
- `dsh.worktable.projects.v1`:order/lastUsed/hidden/nameOverrides/iconOverrides/
removed/shortcuts/layouts/views/bindings/folders;
- views:入驻项目与「控制室」(wt-console)的视图覆盖(LayoutSpec);
- bindings:项目 → 绑定会话(含 wt-console 管理对话);
- folders:项目 → 项目文件夹(含 wt-console);
- 卡片上报的 meta 注册表仅存内存,不持久化;
- 更新检查:`dsh.worktable.lastUpdateCheck.v1`(上次成功检查时间戳,节流一天一次)、
`dsh.worktable.skipVersion.v1`(忽略的版本号)、`dsh.worktable.updateCheck.v1`(自动检查开关,'0'=关);
- 样式:暗色优先,跟随 `--dsw-alias-*` 设计变量(与 dsh-usage / dsh-travelatlas 一致)。
## 7. 与 dsh-travelatlas 的关系
- travelatlas 是「第一个入驻项目」,不是工作台的一部分;
- travelatlas 客户端(2026-08-16 重写):图鉴视图 = 官方 `conversation.view` 会话头标签页(iframe 到
/travelatlas/site/),工作台卡片与降级入口点击时程序化切到该标签页;工作台缺席时回退 `sidebar.footer.action` 独立入口;
- 卡片协议 v2 已接入(2026-08-16 与并行重写冲突后被覆盖,已重新应用并构建);
- 项目图标 🌏(地球·亚洲)为 travelatlas 官方 emoji(2026-08-16 定案);工作台折叠态图标框
按各项目上报的自身 emoji 展示(协议 §5.3 reportMeta.icon)。
## 8. 隐私与安全
- 无个人数据采集;所有状态仅存 localStorage;
- 搜索仅在本机过滤项目名;
- 不读写任何工作区文件、不请求任何网络资源(除插件自身静态资源)。
- 更新检查为可关闭的只读 GET(GitHub Releases API,自动每天最多一次 + 手动「立即检查」),不上传任何数据。
## 9. 验收清单(v1)
- [ ] 侧边栏底部出现分隔线 + 「工作台」标题 + 三按钮 + 项目卡片(🌍 旅行 Atlas);
- [ ] ≡ 拖动区块上下移动,松手停靠,刷新后位置保持;双击 ≡ 复位;
- [ ] 搜索框展开/收起正常,输入能过滤项目卡片,Esc / ✕ 可关;
- [ ] 视图选项下拉可切换分组/排序并持久化;
- [ ] 添加(+) 点击显示「待定」占位提示;
- [ ] 卸载 dsh-worktable 后,dsh-travelatlas 自动回退为底部独立入口(不白屏);
- [ ] 与 dsh-usage、dsh-reminder、官方侧边栏折叠态共存无异常。
### 验收清单(v2,§10 定案,待重启后 GUI 验证)
- [ ] 视图选项菜单只含排序(手动/最近)+ 管理项目入口,选择持久化;
- [ ] 切「最近」后点击 travelatlas 卡片,卡片置顶;
- [ ] 管理项目:改名/隐藏/拖拽或 ↑↓ 排序生效,刷新保持;「恢复默认」清空排序/隐藏/改名;
- [ ] 隐藏后卡片从卡片区消失,管理条中可恢复显示;
- [ ] 「+」面板:接入指引 + 市场外链打开正常;快捷方式校验生效,添加后新标签打开、搜索可过滤、编辑模式可删除;
- [ ] 语言切 en 时工作台文案跟随(标题/菜单/面板);
- [ ] v1 兼容:未上报卡片仍显示且排在已上报卡片之前;
- [ ] travelatlas 降级回退不受影响(卸载工作台后回退底部入口)。
## 10. 定案记录(v2,本窗口与用户讨论后定案)
> 以下条目原为「待设计内容」,已于 2026-08-16 与用户逐项讨论定案并实现,规格并入 §5/§6。
| # | 议题 | 定案 |
| --- | --- | --- |
| 1 | 工作台自身内容 | 不做独立路由页;☰ 菜单「管理项目…」→ 区块内编辑模式(改名/隐藏/排序),见 §5.5 |
| 2 | 添加(+) 逻辑 | 接入指引面板 + 本地快捷方式条目,见 §5.6 |
| 3 | 卡片规范 v2 | 渐进上报协议(owner props 扩展,全部可选),见 §5.3 |
| 4 | 分类 | **取消分类**(用户定案:每项目占一行、自成工作台,无需分组);视图菜单只留排序 |
| 5 | 国际化 | zh/en 词典接入 dsh-client-locale,见 §5.7 |
已知边界:未上报元信息的 v1 卡片按注册序排在最前、不参与排序/隐藏/改名(协议兼容取舍);
市场外链用 GitHub 仓库 https://github.com/hikariming/dshfind(已核实可访问;dshfind.com 未验证、不链死链)。
## 11. 已实现 vs 待实现(接手分界线)
| 部分 | 状态 | 位置 |
| --- | --- | --- |
| 侧边栏区块 + 三按钮 + ≡ 拖动 | ✅ v1 已实现 | `01_content/src/client/` |
| 项目子座位协议 + travelatlas 入驻 | ✅ v1 已实现 | 同上 + dsh-travelatlas/src/client |
| 服务端健康路由 | ✅ 已实现 | `01_content/src/index.ts` |
| 视图菜单去分组、编辑模式、添加面板、快捷方式、埋点、i18n | ✅ 本窗口(v2)已实现并验收 | `01_content/src/client/`(§5.3–§5.7) |
| travelatlas 卡片协议 v2 | ✅ 本窗口已实现并构建(并行重写覆盖后已重新应用) | `dsh-travelatlas/src/client/index.tsx` |
| 多项目分栏框架(openSplit 声明式多栏) | 📝 设计定案(§12),并入 §13 框架引擎 | PRD §12 |
| 乐高式工作区框架(tiling + 内容插件) | 🚧 M1 引擎已实现(openSplit/split.tsx);+ 面板拓扑选择器与 M2/M3 待实现 | `01_content/src/client/split.tsx`(§13) |
## 12. 多项目分栏框架(v3 设计,已定案、待实现)
> 状态:设计定案(2026-08-16 与用户讨论确认)。代码待第一个新项目(如建筑审图)开工时实现,
> travelatlas 可顺带迁入验证。本节为设计规格,不属于 v2 已实现范围。
### 12.1 背景与目标
- 用户后续项目(建筑审图 / 网页动画生成 / 机器人工作台等)都将以「内容栏并置 + 右侧对话」的
形式入驻工作台,栏数 1..n 各异(审图 3 栏、动画 4+ 栏、机器人 2 栏);
- 把 travelatlas 的分栏几何逻辑抽为工作台统一能力:**框架管几何,项目管声明**;
- 目标:新项目接入成本 ≈ 声明一个 SplitSpec(约 20 行),不复制任何几何代码。
### 12.2 openSplit 协议(owner props 扩展,向后兼容)
- owner props v2 增加可选回调 `openSplit(spec: SplitSpec)`(引用稳定);
- 项目卡片 onClick 时调用(与 `reportUsed(id)` 并列);
- 不调用 openSplit 的项目不受框架约束(路线 A 逃生舱):可自行注册 `shell.overlay`
实现任意自定义布局(travelatlas 现行分栏即属此类)。两条路线并存,互不排斥。
### 12.3 SplitSpec 声明
```ts
type SplitSpec = {
id: string // 项目 id(用于宽度持久化)
title: string // 分栏标题(左上角)
panes: SplitPane[] // 内容栏,从左到右 1..n
}
type SplitPane = {
id: string
title: string
width: { default: number; min: number; max: number }
content:
| { kind: 'iframe'; url: string } // 主推:同源站点路由(/xxx/site/)
| { kind: 'component'; component: any } // 预留:项目打包的 React 组件(实现时验证跨插件引用可行性)
}
```
示例(建筑审图 3 栏):`panes: [图纸, 规范]` + 自动对话栏。
### 12.4 框架职责
- 几何:查找会话根(`[data-phase]` 探测 + 结构化甄别,与 travelatlas 现行 hack 一致,
集中一处维护)、marginLeft 右挤对话区、分隔线拖宽(逐栏 width 约束)、Esc/✕ 退出;
- **会话切换行为(2026-08-16 用户需求定案)**:切换不同对话时分栏与左侧内容**保持不关闭**——
会话根变化时重新锚定(重算几何、改观察新根),iframe 组件保持挂载不卸载不刷新;
新会话过渡态(phase 非 active)短暂保持等待、不误关;关闭条件仅:✕ / 再次点击工作台卡片 /
Esc / 无任何活动会话;
- 对话栏:固定最右,可拖宽范围 = 240 起、上限为「列宽 − 左侧内容最小宽」(参考实现取 160px,
与 travelatlas 现行语义一致;不再设固定 480 上限);
- 持久化:`dsh.worktable.split.v1` = `{ [projectId]: { [paneId]: width, chat: width } }`;
- iframe 内容:同源路由约定 `/<project>/site/`(项目服务端自行托管,travelatlas 模式),
URL 校验 http/https,新标签打开入口同 travelatlas;
- 内容形式:`iframe` 为主;`component` 预留位,待深度交互项目出现时验证并实现。
### 12.5 实施时机
- 待第一个新项目开工时在本窗口实现;travelatlas 迁入(改为声明式)作为验证用例;
- 实现不改动 §5.3 卡片协议既有字段,仅新增 openSplit;v2 卡片与老项目不受影响。
## 13. 乐高式工作区框架(v4 设计,路线已定案、待实现)
> 状态:设计定稿(2026-08-16 与用户讨论确认);里程碑 M1–M3 分阶段实现。
> 定位:官方工作区只承载「对话与 Agent 内容」,工作台补上「项目工作区」——一个基座,
> 用户可在其上拼装 2/3/4 窗拓扑与任意内容窗,把项目视窗变成自己想要的样子。
### 13.1 目标与非目标
- 目标:
- 「+」新建工作区:拓扑预设选择(2/3/4 窗)→ 各窗内容指派 → 命名保存为布局;
- 其中一窗恒为聊天窗(继承工作区全部会话;切会话不关闭——§12.4 已实现重锚定);
- 其余窗为内容窗:浏览器 / 资源管理器 / 终端 / 任务管理 / 源代码管理 / 自定义(vibe 生成);
- 拖分隔线、布局持久化、Esc 退出;所有状态存 localStorage。
- 非目标:不搬动/替换官方会话组件;不支持聊天窗出现在非边缘位置(见 13.2 硬约束)。
### 13.2 硬约束:聊天窗必须贴右边缘或下边缘
- 聊天窗 = 官方会话视图区整体,插件仅能以 margin-left / margin-top 将其挤到右/下角,
无法把官方组件拆出来放进中间位置;
- 因此拓扑预设仅提供聊天窗位于右边缘或下边缘(含角)的形态:左右、上下、
3 横排(聊天最右)、上一下二(聊天右下)、井字 2×2(聊天右下)、3+1(聊天右列或下列);
- 上一下二与井字 = margin-left + margin-top 组合挤法(已推演可行);
- 长期观察项:若宿主未来提供「可嵌入的会话组件」,聊天窗即可任意摆位。
### 13.3 布局模型(分割树)
- 布局 = 二叉分割树:叶 = 内容窗(含聊天窗);内部节点 = 分割方向(水平/垂直)+ 比例;
- 预设拓扑均为分割树实例:上一下二 = 水平切 → 下区再垂直切;井字 = 横切 + 每区纵切;
- 状态结构 `LayoutSpec = { id, title, tree: SplitNode }`;叶 = `{ id, title, min/max, content }`;
- 渲染:递归渲染分割树;拖分隔线改比例;聊天窗叶走 margin 挤法 + §12.4 重锚定。
### 13.4 内容插件协议 PaneProvider
- 内容三态:`iframe`(同源 URL,主推)/ `component`(插件打包 React 组件)/ `builtin`(工作台内置);
- builtin 注册表:工作台内置「浏览器」;资源管理器 / 终端 / 任务管理作为后续内容插件逐个接入;
- 内容插件 = 独立 DSH 插件包,经专用座位或注册表挂载,不硬编码进工作台本体。
### 13.5 「+」面板改版
- 「+」点击后**向右侧弹出悬浮面板**(fixed 锚定 sidebar 右边缘与工作台区块顶部,320px 宽、
视口内钳制;透明遮罩点击关闭),不再使用侧边栏内展开式下拉;面板内容仅「选择布局 + 填名称」
(快捷方式表单暂移除,存量快捷方式条目仍保留展示/删除);
- **拓扑预设八个**(3 列网格,末尾第 9 格为「+自定义」磁贴,永远最后)
(聊天窗蓝色 💬 标注;2026-08-18 更新:删「上一下二」,新增第 7/8 预设与自定义磁贴):
①左右两栏 ②三栏横排 ③左二右一 ④井字四栏 ⑤左品右聊 ⑥左1大下3小
⑦田字格(左侧 2×2 四窗均等,右聊天通高整列,topHeightRatio 0.5 + 顶行宽默认扣除聊天列)
⑧上2下3(左列上排 2 窗 + 下排 3 窗宽度均分,右聊天通高整列,topHeightRatio 0.5);
左二右一 = 左侧上下两个内容窗 + 右侧聊天通高整列(chatFullHeight 几何,聊天可 ⇄ 翻转贴左);
左品右聊 = 左侧品字形(上一个、下两个内容窗)+ 右侧聊天通高整列(chatFullHeight 几何,
聊天可 ⇄ 翻转贴左);
- **预设字段**:leftCount/topCount/contentCount/chatFull + 可选 topHeightDefault(固定默认高)/
topHeightRatio(首次打开顶行占比,0.5=上下等分,缺省 0.35);行内窗宽由引擎均分,
横向/纵向分隔条均可独立拖动(「top」水平分隔 + 行内垂直分隔)。
- **+自定义磁贴**(第 9 格):右侧弹窗输入布局描述 → 「复制提示词到剪贴板」→ 生成的提示词
包含引擎规则与现有 8 预设清单,可粘贴到任意 DSH 对话让 agent 实现新预设(追加到 PRESET_DEFS
末尾、加号之前)。
- **窗口编号**:窗格标题「窗口N」,N = 布局中按「左栏 → 顶行 → 主行」顺序的第 N 个内容窗
(如田字格:窗口1/2 = 顶行左右,窗口3/4 = 底行左右;l13:窗口1 = 顶部大窗,窗口2/3/4 =
底部三小窗)。用户说「窗口N」即指该窗。
- **对话绑定**:每个项目卡片(布局卡 + 入驻卡)中间偏右有 ○○/●● 按钮,点击弹面板(按工作区
分组、与发送到会话同源),绑定后打开该项目时右侧对话窗自动切换(sessions.open);解绑即
不再切换;绑定关系存 projects.v1.bindings(项目 id → 会话 id)。
- **任务完成/待决提醒镜像**(2026-08-18):绑定会话在宿主快照 byId 里 completed=true → 项目卡
双圆点绿色发光(data-bound=done);pendingInteraction != null → 黄色发光(data-bound=need),
与原生对话小绿点/小黄点同步;点开项目即确认(ack,notifyAck.v1 按会话存状态)恢复常态实心;
状态切换(完成↔待决)会重新点亮。数据源 sessionsSnapshotStore(syncSessionScope 推送完整
快照并通知监听)。
- **项目×对话联动**(2026-08-18):① 打开项目时记录「打开前会话」;② 项目打开期间切到非该
项目绑定的会话 → 自动关闭项目(保留用户新选的会话);③ ✕/反选关闭项目 → 自动回切「打开前
会话」。未绑定项目以「打开前会话」为归属会话(任何切换都会关掉它)。
- **项目文件夹(工作目录)**(2026-08-18):新建项目时强制填写(父目录必填 + 文件夹名留空 = 用
项目名,保存时经 /api/worktable/mkdir 建目录);绑定面板「绑定对话」上方可随时更改;存
projects.v1.folders(项目 id → 绝对路径)。自定义窗口新建会话时若未选分组则以该文件夹为
cwd(sessions.create({cwd})),窗口提示词携带文件夹路径 + 「所有产出放进该文件夹」指令。
- **窗口任务提示词升级**(2026-08-18):携带窗口身份(项目名 + 窗口N,窗口N = 窗格标题)、项目
文件夹与「插件知识包」(窗模型/内容类型/服务端路由/构建方式,注明「不要重新侦察插件源码」),
避免接收会话从头侦察源码导致响应过慢。
- **自动挂载**(2026-08-18):提示词第 6 条要求 agent 完成后写 widget-result.json(window/path/
kind);客户端在绑定会话 completed 时读取并自动把产物挂进对应窗口(项目开着直接挂、没开
暂存补挂);用户不再需要手动去资源管理器点开产物。
- **提示词零泄漏硬约束**(2026-08-18):任何对外生成的提示词(窗口任务提示词 / 剪贴板布局提示词)
禁止写入用户的个人工作区分组名(Projects / DeepseekHarness 等)、他人项目名与私人路径;
分组下拉只用于会话创建工作区,绝不进入提示词文本;剪贴板提示词只含插件通用知识。
- 保存的布局以「布局条目」形式出现在项目区(布局 = 一种工作台项目),点击打开 tiling 工作区,
再次点击 / ✕ / Esc 关闭。
### 13.6 持久化
- `dsh.worktable.layouts.v1` = `{ [layoutId]: LayoutSpec + 分隔线比例 }`;
- 聊天窗宽度语义同 §12.4:240 起、上限 = 行/列尺寸 − 相邻内容窗最小宽。
- 窗格折叠:每个内容窗的 `collapsed` 布尔直接存在窗格对象上(随 LayoutSpec 一起持久化);
折叠态 = 隐藏窗格标题栏 + 标签栏,内容占满窗格,右上角浮动展开按钮;控制室窗(单控制室标签)不渲染折叠按钮。
### 13.7 内容窗可行性记录(2026-08-16)
| 内容窗 | 可行性 | 依据 |
| --- | --- | --- |
| 浏览器 | ✅ 已实现 | iframe + 地址栏 |
| 资源管理器 | ✅ 已实现(第一版) | 服务端 /api/worktable/fs 目录列表(参考 better-sidebar 架构);文件点击开预览标签(2026-08-17):.html → /api/worktable/site 目录级静态托管(相对资源随目录解析,本地网页完整渲染;PDF 已回退原生 iframe 阅读器)、.md markdown-it 渲染、.txt/.log 纯文本、常见图片居中展示 |
| 源代码管理 | ✅ 已实现(第一版) | 服务端 /api/worktable/git(porcelain v1 -z);diff/暂存/提交待后续 |
| 终端 | ✅ 已实现(第一版) | WS /api/worktable/term + node-pty + xterm(宿主缺 node-pty 时降级提示) |
| 任务管理 | ✅ 已实现(第一版) | 客户端 sessions 快照 jobsBySession(后台任务列表,2s 刷新) |
| 自定义 vibe | ✅ 可闭环 | 描述需求 → agent 生成新项目(插件/站点)→ 注册进工作台(UI 已留 ✨ 入口)。✨ 自定义窗两模式(新建对话/发送到会话)点发送后调用宿主 sessions.open(会话id),右侧对话窗自动切到目标会话(2026-08-18 无头探针实测 switched=true) |
### 13.8 里程碑
- **M1 布局引擎**:✅ 核心已实现(2026-08-16)——`01_content/src/client/split.tsx` 通用分栏引擎
(本版布局模型 = 标题栏 + 顶部通栏行(可选) + 主行内容窗 + 右下聊天窗;聊天窗 marginLeft+marginTop
组合挤法;会话切换重锚定不关闭;chat/top/pane 三级分隔线拖拽;`dsh.worktable.split.v1` 持久化),
owner props 新增 `openSplit(spec)`;
✅ 「+」面板「新建工作区」(2026-08-16):接入指引移除,第一步为**可视化拓扑缩略图选择**
(左右两栏/三栏横排/上一下二/井字四栏,聊天窗蓝色标注)→ 只填布局名称 → 进入工作区;
窗内容在工作区内指派:每窗 6 选 1(浏览器/资源管理器/源代码管理/任务管理/终端 + 自定义 URL,
前四项为占位、浏览器可用);标题栏拖拽可换窗位(同行/跨行);工具栏 ⇄ 切换聊天窗左右(左下/右下,
marginLeft/marginRight 双挤法);内容与聊天位置变更实时回写 `dsh.worktable.projects.v1.layouts`;
- **互斥规则(2026-08-16 用户反馈定案)**:同一时刻仅一个分栏工作区——
① 反选:同一项目卡片再点 = 关闭;② 替换:不同项目互斥(选 B 关 A);
③ 实现:引擎内开前先关旧;对外广播 `dsh:split-claim` 共享协议并监听让位;
对未接入协议的引擎(travelatlas 现行实现)用运行时兼容桥(点击其关闭按钮)+ 让位观察器
(视图区 margin 被外部改写即让位),不改动其代码,待其迁入引擎后移除;
④ 多项目并行 = 用户开多个浏览器窗口(网页窗口只容纳一个项目);
- **M2 内容插件协议**:PaneProvider 三态接口;travelatlas 图鉴作为第一个内容窗迁入验证;
- **M3 内容插件库**:资源管理器 / 终端 / 任务管理逐个接入;SCM 视 API 情况;自定义 vibe 闭环。
### 13.9 与既有协议的关系
- §5.3 卡片协议 v2 不变:项目插件仍注册卡片(reportMeta/reportUsed/openSplit);
- §12 的 openSplit 声明式多栏并入本框架:项目预设布局 = 一份固定 LayoutSpec,
用户自建布局 = 同一引擎的运行时产物,共用 tiling 引擎与持久化;
- 本地快捷方式、接入指引保留于「+」面板第二入口。