[English](README.md) | 中文 # @benz-ai-x/dsh-md-preview DSH Web GUI 插件:点击对话中出现的 Markdown 文档,在对话右侧打开渲染后的预览面板;支持受守卫编辑与工作区目录树浏览。 [![npm](https://img.shields.io/npm/v/@benz-ai-x/dsh-md-preview)](https://www.npmjs.com/package/@benz-ai-x/dsh-md-preview) [![GitHub](https://img.shields.io/badge/repo-benz--ai--x%2Fdsh--md--preview-24292e?logo=github)](https://github.com/benz-ai-x/dsh-md-preview) ## 效果 - 回合产出文件 chip 行中的 `.md` / `.markdown` 文档:点击打开右侧预览面板,渲染 GFM、代码高亮和 TeX。 - 每条助手消息的操作区新增「预览文档」按钮,列出该回合产出的 Markdown 文档。 - 非 Markdown 产出文件保持原有行为(交给系统打开)。 - **文档侧边栏**:宽屏展开时让对话区自动缩窄,低于 1056px 或全屏时覆盖展开。头部「Session 日志」右侧使用原生回形针图标,一键开合工作区浏览,面板右上 × 关闭;未保存修改仍先询问。宽度默认取半屏、最多 720px,记忆 360–1200px 的手动拖宽偏好,并按可用空间钳制。原生导航与工具详情保留各自操作;Esc 关闭后焦点返回入口。布局适配依赖锁定基线,见 [ADR-0004](docs/adr/0004-dock-preview-beside-the-harness-frame.md)。 - 没有预览目标时面板不渲染。 - **字体与主题对齐 Harness**:导航主文字 14px/20px、父目录路径 12px/18px,统一图标与控件尺寸。搜索固定在浏览区顶部,条目独立滚动;导航初始宽 220px,保留手动宽度偏好。面板从框架顶部停靠,自身保留工作区/返回与关闭入口;聚焦文档身份可查看完整路径和版本。编辑器使用平台浅/深主题与代码字体,正文在各种宽度下均保持平台 Markdown 段落节奏。 - **编辑**:面板「编辑」进入 CodeMirror 编辑器(行号、GFM 高亮、Cmd/Ctrl-S 保存),「保存」写回工作区、显示「✓ 已保存」提示并回到渲染视图,「取消编辑」丢弃草稿;只编辑已存在的文件。保存失败(非冲突)会显示错误码并提供【重试】。 - **冲突保护**:保存时若文件已被其它方(agent、其它会话、外部编辑器)修改,提示「文件已变化」,由你选择【重新加载】或【强制覆盖】;带未保存修改关闭面板会先询问。 - **工作区浏览**:面板头部「工作区」进入目录树(懒展开、加载/空/失败三态);单击 `.md` 富渲染、`.txt` 等纯文本等宽展示、其它类型明确提示不支持;当前文档在树中高亮并自动定位;支持方向键/Enter 键盘遍历;头部路径面包屑。已展开目录在每次重进浏览脸时**静默重验**(树工具栏也有刷新按钮)——agent 会在会话中途持续产出文件,刷新失败绝不清空现有列表。 - **大纲导航**:头部「大纲」弹层列出文档的 ATX 标题(代码围栏内的 `#` 不算);点击后查看脸滚动到渲染标题、编辑脸跳转光标到源行。 - **侧栏 rail(≥640px)**:文件树与大纲常驻面板左侧(「文件|大纲」记忆选择),文档不再被浏览替换;<640px 回退换脸/弹层;Mod-Shift-O/E 直达、Esc 收弹层。 - **分段控件与编辑反馈**:「预览|编辑」常驻切换(脏草稿切回走守卫);状态栏实时 Ln/Col/字数与常驻「已保存」时间;撤销/重做按钮;Mod-B/I/K 排版键(? 查看键位);含内联 HTML 的文档进编辑时一次性预警。 - **工作区搜索**:浏览区搜索框按名称搜索**整个**会话工作区的文档——未展开目录也覆盖——由宿主可取消遍历完成,绝不读取正文。名称不区分大小写子串匹配;每条结果同时显示名称与工作区相对路径(同名文档可分辨);打开结果仍走完整读取与守卫。状态诚实可辨:搜索中…;「没有结果」只在完整搜索且零命中时出现;「结果不完整」会说明原因(目录读取失败/遍历或数量上限)并保留已得结果;失败给出错误码与重试。输入新词即取消旧搜索,清空后精确恢复原浏览展开状态。(与编辑器内查找是两回事。) - **快捷入口**:树上方两小节——「当前回合产出」列出最新回合产出的可预览文档(owning service 回合事实、按关闭序号截断、回合进行中实时更新),「最近阅读」按近期使用列出本会话读过的文档。两节均显示名称加路径,打开走同一守卫路径并重新读取;来源为空时该节隐藏;「继续阅读」入口保持自己的独立席位与语义。 - **阅读位置跟踪**:大纲弹层高亮当前阅读位置所在的节——查看脸按滚动位置、编辑脸按光标源行推导,并保持该条目在弹层视野内。头部在草稿未保存时显示脏点,版本号折入路径提示,查找/保存按钮标注快捷键;查找面板已本地化并显示匹配计数(n/m)。 - **编辑器查找**:编辑脸带 CodeMirror 搜索面板(头部按钮与 Mod/Ctrl-F)。 - **Mermaid 图表**:` ```mermaid ` 围栏块在文档渲染定型后增强为图表;块横幅保留(复制仍取源码),任何失败回退纯代码块。mermaid 内联进 client bundle 但**惰性求值**(首个图表才付解析成本;bundle 约 3.9 MB minified / 1.1 MB gzip)。 ## 安装 要求 DSH 基线 `0.1.2-rc.1`(即 peerDependencies 所列版本)和 web profile。 ```sh dsh plugin --profile add @benz-ai-x/dsh-md-preview dsh --profile --dump-config # 应出现 id: md-preview 的行 dsh --profile # 打开 Web GUI;回合产出 .md 后点击 chip 预览 dsh plugin --profile remove @benz-ai-x/dsh-md-preview ``` ### 三种安装形态(依 [DSH 打包与安装规范](https://github.com/deepseek-ai/deepseek-harness/blob/main/docs/user/develop/basic/publish.zh.md)) | 形态 | 命令 | 状态 | | --- | --- | --- | | npm(推荐) | `dsh plugin --profile add @benz-ai-x/dsh-md-preview` | ✅ 预构建产物,即装即用 | | tarball | `dsh plugin --profile add ./benz-ai-x-dsh-md-preview-.tgz`(tarball 由源码 `pnpm pack:publishable` 产出) | ✅ 预构建产物,无需任何构建授权 | | Git 直装 | `dsh plugin --profile add github:benz-ai-x/dsh-md-preview#` | ✅ 由包自带的自包含 `prepare` 从源码构建(纯转译,此形态不带类型声明)——见下文 | git 安装拉取的是源码,pnpm 在得到显式允许前拒绝运行 git 依赖的 `prepare`。首次 `add` 失败后,把 pnpm 打印的确切包键复制进该 profile 的 `pnpm-workspace.yaml`: ```yaml allowBuilds: '@benz-ai-x/dsh-md-preview': true ``` 然后重新执行 `add`。授权构建 = 允许该包的代码在安装时于你的机器上执行 —— 只对可信源码授权,并用 `#` 锁定 commit,让后续推送无法悄悄改变实际运行的内容。 ## 配置 ```yaml - id: md-preview name: '@benz-ai-x/dsh-md-preview' config: maxBytes: 1048576 # 单文件读取上限(字节) allowedExtensions: ['.md', '.markdown'] ``` | 字段 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | `maxBytes` | number | `1048576` | 单文件读/写上限,超出返回 `too-large` | | `allowedExtensions` | string[] | `[".md", ".markdown"]` | 可编辑的扩展名白名单 | | `previewExtensions` | string[] | `[".md", ".markdown", ".txt"]` | 可预览的扩展名(可编辑集的超集;纯文本成员只读展示) | ## 失败码 读取失败时面板显示 `md-preview/`。所有失败码: | 码 | 含义 | | --- | --- | | `md-preview/bad-request` | path 为空或非法;或保存时既无指纹也未强制 | | `md-preview/unknown-session` | 会话不存在 | | `md-preview/no-workspace` | 会话没有工作目录 | | `md-preview/unsupported-extension` | 扩展名不在白名单 | | `md-preview/forbidden` | 路径超出会话工作区 | | `md-preview/not-found` | 文件不存在(编辑只针对已存在文件) | | `md-preview/too-large` | 文件(读取)或内容(写入)超过 `maxBytes` | | `md-preview/conflict` | 保存时文件已变化(指纹不匹配且未强制) | | `md-preview/unavailable` | 读/写过程发生 IO 错误 | ## 已知限制 - 正文中内联提到的 `.md` 文件名仍走系统打开(归 ui-deliverables 所有,不归本插件)。 - 文档侧边栏通过锁定基线的可释放布局适配预留空间,窄屏覆盖展开;升级 Harness 需重新验证布局适配,原生工具详情贡献保留。 - 用户上传的文档附件不可预览(目前没有对应的会话面)。 - 大纲只收 ATX 标题(setext 下划线式标题会渲染但不进弹层)。 - Mermaid 用默认主题;混用缩进代码块与围栏块的文档整体跳过图表增强(顺序对齐安全检查)。 ## 开发(source-linked) ```sh pnpm install pnpm verify # context:check:strict + typecheck + test + build + built:check pnpm context:link # 需要源码联调时:重写 link: 指向 Harness 检出并刷新 lockfile(默认 registry) pnpm watch:client # 客户端 bundle 热构建 ``` ### 结构 | 部分 | 位置 | 说明 | | --- | --- | --- | | Host Remote | `src/remote.ts` | `mdPreview/read(sessionId, path, signal)`;工作区限域、扩展名白名单、字节上限 | | Remote contribution | `src/typert/remote-client.ts` | 手工维护的浏览器端描述符(生成器产物的等价物) | | 浏览器入口 | `src/client/index.ts` | 挂载 Remote + 注册三个 Slot 贡献 | | 预览面板 | `src/client/PreviewOverlay.tsx` | `shell.overlay`(list,增量);仅渲染 + 几何 | | 会话机器 | `src/client/preview-session.ts` | 纯 reducer:读取/编辑/保存/提示的完整状态代数 | | 编辑器 | `src/client/editor.tsx` | CodeMirror 6(精选扩展集含搜索面板,构建期内联) | | 工作区树 | `src/client/WorkspaceBrowser.tsx` | 懒树:高亮/自动定位/键盘遍历 + 静默重验 | | 大纲 | `src/client/outline.ts` | ATX 标题扫描 + 渲染标题定位 | | 图表增强 | `src/client/diagrams.ts` | 渲染后 mermaid 增强,失败回退代码块 | | chip 行接管 | `src/client/MdChips.tsx` | `conversation.chat.turnTail`(chain,仅认领含 Markdown 的回合) | | 消息操作 | `src/client/PreviewAction.tsx` | `conversation.chat.assistant-actions`(list,增量) | ### 真实 profile 验证(本地检出) ```sh pnpm build dsh plugin --profile add ./dsh-md-preview dsh --profile --dump-config dsh --profile # 打开 Web GUI,写一个 README.md 产出并点击 chip dsh plugin --profile remove @benz-ai-x/dsh-md-preview ``` ### 打包与发布 ```sh pnpm pack:publishable # 打出净化 manifest 的 tarball 并复检(无 devDeps、无 link:/workspace:) pnpm publish:registry # 以同一净化流程发布到 npm ``` source-linked 验证证明与固定 Harness 检出(见 `dsh-reference.lock.json`)的兼容性;发布形态以 packed tarball 在干净 profile 中的安装/启动/移除冒烟为准。 ## 许可证 MIT