# PRD:dsh-design(DSH 原型画布) | 字段 | 值 | | --- | --- | | 产品 | dsh-design | | 版本 | MVP 0.1 | | 状态 | Approved for implementation | | 仓库 | 本仓库(开源 MIT bundle) | | 日期 | 2026-08-20 | ## 1. Executive Summary **问题。** 在 DeepSeek Harness 里做界面,Agent 只能在聊天里丢 HTML 代码块。用户看不见真实交互,也无法自己点一遍登录、切 tab、弹窗。Tutti 的 Prototype Design(`vibe-design`)解决了「可点原型」,但它是第二套 Agent 运行时,和 DSH 会话抢控制权。 **方案。** 做一个官方形态的 DSH bundle:聊天仍只用当前 DSH 会话。插件提供 Agent 可调用的工具,把可点击的 HTML 写进当前 workspace;预览挂在已安装的 [dsh-better-sidebar](https://github.com/omdsh-dev/DSH-better-sidebar) 的「原型」tab。用户在侧边栏点交互,要改就回到同一个输入框。 **成功标准。** 用户说「做个登录页」→ Agent 调用工具写出 HTML → 侧边栏原型 tab 能打开并能点提交/校验;用户说「按钮再大」→ Agent 覆写同一文件 → 预览更新。全程不出现第二套对话框,不拉起 Claude / Codex。 ## 2. Problem Definition ### 2.1 用户问题 - **Who**:在 DSH 里同时写产品和前端的开发者 / 设计师 / PM;已装 better-sidebar。 - **What**:需要「能点的原型」,而不是聊天里的代码块。 - **When**:探索界面、对齐交互、给 Agent 视觉反馈的时候。 - **Where**:DSH Desktop / web profile 的当前 workspace。 - **Why**:DSH 有模型和写文件,没有设计画布;把 vibe-design 整仓搬过来会引入第二套 Agent。 - **Impact**:不解决就继续复制粘贴、截图、口头描述按钮位置。 ### 2.2 非目标市场 不是 Tutti 多 Agent 工作台,也不是 Figma。不做多人实时、不做设计系统后台、不做从零实现的独立 IDE。 ## 3. Solution Overview ### 3.1 一句话 **聊天还是 DSH,画布挂在 better-sidebar。** ### 3.2 用户旅程 ``` 用户在 DSH 输入框:「做个移动端登录页,要有忘记密码」 │ ▼ 当前会话 Agent design_status → design_create → design_write(index.html) → design_open │ ▼ workspace/.dsh-design/projects//index.html │ ▼ better-sidebar「原型」tab(+ 菜单可手动打开) 拉取 snapshot → loopback 静态预览 iframe 渲染 → 用户自己点 │ ▼ 用户:「主按钮再大,错误态用红字」 │ ▼ 同一会话 Agent 再 design_write → 预览按 revision 刷新 ``` ### 3.3 In Scope(MVP / P0) | ID | 能力 | 优先级 | | --- | --- | --- | | FR1 | Host 工具:创建项目、写 HTML、列出文件、打开预览、查看状态 | P0 | | FR2 | 产物落在当前 workspace 的 `.dsh-design/`,可进 git | P0 | | FR3 | `ctx.systemPrompt.section` 指导 Agent:自包含可运行 HTML,不要空谈 | P0 | | FR4 | better-sidebar 注册单实例 tab `dsh-design:prototype` | P0 | | FR5 | tab 内 iframe 预览当前页,用户可点 | P0 | | FR6 | 未安装 better-sidebar 时插件不崩,工具仍能写文件 | P0 | | FR7 | 官方 bundle 契约:`dsh.bundle.patch` + `dsh.client`,不改 DSH 源码 | P0 | ### 3.4 Out of Scope - 第二套 Agent / ACP / 再拉起 Claude Code 或 Codex - 插件内聊天窗、项目仪表盘、属性检查器、画布就地改 HTML - 任何官方 UI 槽:`sidebar.footer.action`、`shell.overlay`、官方 sidebar。**唯一入口是 better-sidebar 的「原型」tab** - 16 套设计系统管理后台(MVP 只在 system prompt 里给生成约束) - 批注钉点:已做坐标 sidecar;截图 / DOM 锚定仍不在本迭代 - 多页相对资源:已做;最多两层目录(css/app.css、assets/logo.png) - value-import `dsh-better-sidebar` 或 `@deepseek-ai/schemastery` ### 3.5 MVP 完成定义 1. `pnpm test`(或 `node --test`)在无 DSH peer 时跳过 Host 注册、在有 store 时全绿。 2. 工具能在临时目录创建项目并写出可解析的 HTML。 3. Client bundle 是 `window.__ModuleLoader__.load`,软挂 `betterSidebar`。 4. README 写清:聊天框设计 → 侧边栏点。 ## 4. User Stories ### US1 — 用聊天生成可点原型 As a DSH 用户,I want 在输入框里让当前 Agent 设计界面,So that 我不用离开会话去别的设计工具。 Acceptance: - [ ] Agent 使用 `design_*` 工具,而不是只在聊天里贴一大段未落盘代码 - [ ] `.dsh-design/projects//` 下有 `project.json` 和至少一个 `.html` - [ ] 不出现插件自己的 composer ### US2 — 在侧边栏点交互 As a 已安装 better-sidebar 的用户,I want 在右侧「原型」tab 里点这个页面,So that 我能验证登录、tab、弹窗是否真的能用。 Acceptance: - [ ] `+` 菜单有「原型」/ Prototype - [ ] 当前打开的 HTML 在沙箱 iframe 里运行脚本和表单 - [ ] tab 不可见时不轮询 ### US3 — 同一会话里改稿 As a 用户,I want 看完再说「改这里」,So that 上下文不丢。 Acceptance: - [ ] Agent 覆写同一文件后 `current.revision` 变化 - [ ] 打开的 tab 刷新预览,不必手动重建项目 ### US4 — 没装 better-sidebar 也不炸 As a 只装了本插件的用户,I want Agent 仍然能把 HTML 写进仓库,So that 我可以用浏览器或别的预览打开文件。 Acceptance: - [ ] `betterSidebar` 不在静态 `inject` 里 - [ ] 服务缺失时注册跳过 - [ ] Host 工具与 RPC 仍可用 ## 5. Functional Requirements | ID | 需求 | 优先级 | | --- | --- | --- | | FR-T1 | `design_status`:当前 workspace 的项目列表与 current 预览 | P0 | | FR-T2 | `design_create`:按标题建项目 | P0 | | FR-T3 | `design_write`:向项目写入文本文件(默认 `.html`);`open` 默认 true | P0 | | FR-T4 | `design_list`:列出项目内页面 | P0 | | FR-T5 | `design_open`:把某 HTML 标为当前预览并 bump revision | P0 | | FR-P1 | 路径限制在 `/.dsh-design/**`,拒绝 `..` 与绝对路径 | P0 | | FR-P2 | 文件名 `^[A-Za-z0-9._-]{1,128}$`,扩展名 `html\|css\|js\|svg\|png\|jpg\|gif\|webp\|ico` | P0 | | FR-P3 | 单文件上限 512KiB;超限失败,不截断装成成功 | P0 | | FR-U1 | Client RPC `snapshot` / `file`,payload 带 `cwd` | P0 | | FR-U2 | iframe `sandbox="allow-scripts allow-forms"`,无 `allow-same-origin` | P0 | | FR-U3 | 预览用 loopback 静态服务 iframe `src`(相对资源可解析) | P0 | | FR-S1 | better-sidebar tab id `dsh-design:prototype`,`single: true`,order 55 | P0 | | FR-S2 | 通过 `ctx.plugin({ inject: ['betterSidebar'] })` 子 fiber 挂载 | P0 | ## 6. Design & UX ### 6.1 原则 - 生成内容是主角:chrome 让路给 iframe。 - 文案说用户能做什么,不说 RPC / bundle。 - 空状态给下一步:「在聊天里描述一个界面」而不是「暂无数据」。 - 失败说清:cwd 没有、文件超限、项目不存在。 ### 6.2 原型 tab 视觉 工作台是「灯箱」:暗色舞台托住一张白纸原型,而不是再做一套聊天皮肤。 | Token | Hex | 用途 | | --- | --- | --- | | stage | `#252A32` | tab 底 | | brass | `#C9A36A` | 唯一强调:当前页标记、空状态短线 | | ink | `#E8EDF2` | 主文字 | | dim | `#8B95A1` | 次级文字 | | rule | `#3A424C` | 分割 | | paper | `#F7F5F2` | iframe 周围纸边 | 字体:界面用系统 UI 栈;文件名用 `ui-monospace`。不加载网页字体。 结构: ``` ┌ title · 页名 刷新 ┐ │ [page] [page] │ │ ┌──────────────────────────────────┐ │ │ │ iframe stage │ │ │ └──────────────────────────────────┘ │ ``` 签名元素:舞台四角的铜质定位十字,让它像印前灯箱而不是普通网页预览器。 ### 6.3 空 / 错 - 无项目:「还没有原型。在左边聊天里说要做什么界面。」 - 无 better-sidebar:不渲染 tab;工具结果里提示文件路径。 - RPC 失败:「读不了当前工作区的原型文件。」 + 重试。 ## 7. Technical Specifications ### 7.1 契约 - `package.json`:`dsh.bundle.patch`、`dsh.client.platform=web`、`exports["."]` 与 `exports["./client"]` - Host:`name`、`inject: ["tools","systemPrompt"]`、`apply`;**省略 `Config`,不 import schemastery** - YAML `config` 仍可作为 plain object 进入 `apply` - Client:`window.__ModuleLoader__.load` lazy-CJS;`inject: ["connection"]` - `dsh-better-sidebar`:optional,运行时探测,禁止 value-import - 不改 DSH 源码、不改 better-sidebar 源码 ### 7.2 数据 ``` /.dsh-design/ current.json { projectId, file, title, revision, updatedAt } projects// project.json { id, title, entry, createdAt, updatedAt } index.html ``` `revision` 为单调整数,每次 `design_write(open)` / `design_open` 加一,供 UI 刷新。 ### 7.3 RPC Channel `/design`,`authority: "loopback"`,永不抛出;返回 `{ ok, value | error }`。 | endpoint | payload | value | | --- | --- | --- | | `snapshot` | `{ cwd }` | 项目列表 + current | | `file` | `{ cwd, projectId, name }` | `{ content, mime }` | ### 7.4 Agent 工具约定 写出的 HTML 必须: - 完整文档(``) - CSS/JS 内联,可单独在 iframe 里点 - 真实控件,而不是截图 - 语言跟随用户 禁止: - 再开 Agent - 只回复「这是设计思路」而不写文件 - 把文件写到 `.dsh-design` 之外(除非用户明确要求落地到产品代码,那时用内置 fs,不走本插件) ## 8. Risks | 风险 | 缓解 | | --- | --- | | `link:` 安装解析不到 `@deepseek-ai/dsh-tools` | 不 import 该包;`ctx.tools.register` 直接挂普通对象 | | better-sidebar 未装或版本无 `registerTab` | 子 fiber + 特性探测,失败跳过 | | Desktop webview 拦 loopback | iframe 走 `127.0.0.1` + token 路径;失败再考虑降级 | | iframe 脚本攻击父页 | 无 `allow-same-origin` | | 范围膨胀成 vibe-design | 本 PRD 的 Out of Scope 作为拒绝清单 | ## 9. 后续(非本迭代) - 发现流程:第一份 HTML 前必须有 platform / viewport / look(已做) - 多页相对资源:loopback 预览 + 最多两层目录(已做) - 设计系统样例页 paper/ink(已做);管理后台仍不做 - 批注选择器(已做);截图仍不做 - 只读设计系统目录(从 vibe-design 思路借鉴,不复制资产除非许可清晰) - 批注、多页资源、设计系统目录仍走 Prototype tab,不新增官方 slot ## 10. 决策记录 1. **一套 Agent**:只使用当前 DSH 会话。 2. **UI 入口只在 better-sidebar**:tab `dsh-design:prototype`。禁止 `sidebar.footer.action` / `shell.overlay` / 官方 sidebar。未装 better-sidebar 时不注册 UI,Host 工具仍写文件。 3. **不是 vibe-design fork**:新 bundle,MIT。 4. **文件在 workspace**:`.dsh-design/`,不用插件私有 SQLite。