dsh-md-notes 功能设计文档

September 23, 2026 · View on GitHub

本插件为 DSH 提供 MD 笔记管理:在侧边栏常驻入口,打开全屏笔记管理界面; 支持把任意一条回答及其提问直接记入指定笔记;并可选地把笔记同步到 Git 仓库 (共享仓库 / 独立仓库双模式)。

措辞对照:本文说的 host / client 即「后端(跑在 dsh 的 Node 进程里)/ 前端(跑在浏览器 页面里)」,详见 architecture.md §1。

1. 功能总览

功能入口说明
打开笔记管理侧边栏「笔记」入口全屏面板:左侧按工作区分组的列表 + 右侧编辑/预览
新建笔记激活工作区行「+」弹窗填标题(默认「未命名笔记 日期」)+ 可选文件名,重名拦截,随即打开编辑
编辑笔记管理器右侧「编辑」Tabmarkdown 源码编辑,点「保存」写入
预览笔记管理器右侧「预览」Tabdsh MarkdownText(GFM / TeX 公式 / 代码高亮,安全内置)
笔记互链预览内 `笔记名` / [[笔记名]]命中即渲染为可点链接,点击跳转目标笔记(含跨工作区;工作区/笔记名 限定消歧;重名标题 tooltip 提示)
删除笔记列表项 🗑页面内确认弹窗(Modal)后删除文件
笔记搜索管理器顶部搜索框跨工作区标题 + 正文全文搜索(token AND / 大小写不敏感);分组结果 + 命中行高亮,点命中行 located open(见 search.md)
记入笔记每条回答下方的笔记图标选择/新建目标笔记,即时追加该回答及提问(文本由 client 从会话快照提取,host 只写文件)
笔记写入互斥—同一笔记写入期间跨会话锁定;入口 / 弹窗 / 管理器三处状态联动(见 §2.8)
Git 同步工作区 Git 同步卡片「更新/推送」笔记本地固定 <工作区>/.dsh-notes,可同步到远程仓库
版本更新提示侧边栏入口 / 管理器标题栏npm 有新版时显示黄色「有新版本需要更新」tag
笔记引用进对话对话输入框 @引用笔记,host 把内容注入模型上下文(见 context.md)
插入图片编辑器内粘贴 / 拖入图片存入 .dsh-notes/assets/,插入 ![](assets/…) 相对引用;预览内联 + 点击灯箱(本机文件,不同步)
设置管理器标题栏 ⚙ / dsh 设置面板「MD 笔记」模式、仓库 URL、分支、自动拉取、作者等

2. 功能详述

2.1 侧边栏入口(笔记)

  • 位置:左侧栏底部区域,通过 sidebar.footer.action slot 注册。
  • 点击打开笔记管理全屏面板(shell.overlay)。
  • 有笔记正在写入时,入口尾部(版本更新 tag 前)显示 loading,hover 提示「{count} 个笔记正在写入」。

2.2 笔记管理界面

布局:全屏遮罩 + 居中面板(shell.overlay 注册),分左右两栏。

左栏 — 笔记列表(按工作区分组)

  • 每个 dsh 工作区一个分组,展示该工作区 .dsh-notes/ 下的笔记(标题 + 更新时间倒序)。
  • 工作区行:文件夹图标(点击折叠/展开该组)、工作区名、笔记计数、git 图标(配置了仓库才显示, 点击展开/收起 Git 卡片)、「+」新建。
  • 工作区 Git 同步卡片(0.7.0 改版):工作区组内、笔记列表之后(配置了仓库时渲染), 默认收起,点工作区行的 git 图标展开/收起——标题「Git 同步 · 整个工作区」+ 状态 pill (「已同步」/「未推送 N 处」,按本地与仓库差异 unpushed 计算)+ 分支 / 子路径 / 最近提交
    • 更新(拉取)/ 推送(提交)按钮 + 冲突提示位;远端领先时显示「远端有更新」提示 (remoteAhead,拉取时刷新 origin refs,0.7.1 后新增)。
  • 列表项:点击打开右侧预览(默认);🗑 删除(页面内 Modal 确认)。正在写入的笔记行尾显示 loading、删除按钮隐藏(仍可点击查看)。
  • 空态:无工作区时显示引导提示;某工作区无笔记时显示空提示。

顶部搜索框(未发布)

  • 顶部栏标题与关闭按钮之间;输入即搜(250ms 防抖 + 每代 AbortController),Esc / ✕ 清空。
  • 全工作区标题 + 正文全文搜索(token AND、大小写不敏感子串;上限:每笔记 5 命中行、 全响应 50 篇 + truncated 提示,设计见 search.md)。
  • query 非空时左栏替换为分组结果(工作区 → 笔记(标题命中徽标 + N 处命中)→ 命中行 (L 行号 + 片段 <mark> 高亮));打字期间保留上一批结果,不闪空列表。
  • 点命中行 located open:编辑态打开 + 命中 token 选中(原生 selection 即高亮)+ 命中行滚到视口中部;正在写入的笔记退回预览打开。host 侧只读现扫(无索引), 逐工作区串行。

右栏 — 编辑器

  • 「编辑 / 预览」双 Tab(预览在前);「保存」写入本地文件。预览用 dsh MarkdownText(GFM / 公式 / 代码高亮)。编辑未保存时工具栏显示「未保存」pill(0.7.0)。
  • 正在写入的笔记(当前选中时):编辑 Tab、更新、保存、推送全部禁用,更新按钮前的提示位 显示「正在写入文件」(优先级高于「远端有更新」提示)。
  • 「推送」后按钮行切换成提交信息行(输入框 + 确认/取消,默认「笔记更新 <时间>」),确认后提交并推送;取消则退回按钮行。
  • 底部全局 Git 状态行(0.7.0 改版):「Git 同步 · 全局」+「未推送 N 处」(跨工作区求和)+ 「X 个工作区 · Y 个待同步」;单个工作区的分支/最近提交只在各自 Git 卡片展示。

2.3 记入笔记

  • 每条已完成的回答下方操作行有一个笔记图标(插件 logo)。

  • 点击弹出选择面板(shell.overlay):

    • 按工作区分组列出所有工作区的笔记(可折叠浏览),支持跨工作区记入;
    • 每个工作区行有「+」按钮,可现场新建(弹窗填标题 + 可选文件名,与管理器共用 CreateNoteDialog);
    • 正在写入的笔记不可选中(行尾 loading);
    • 点「写入笔记」→ 即时追加:提问 + 回答文本由 client 从浏览器会话快照提取 (与复制按钮同源,见 context.md §3.5),host 只做格式化 + 写文件; 写入中按钮显示「写入中…」,成功后按钮左侧出现绿色「已写入 ✓」再自动关闭(约 0.9 秒)。
  • 追加内容格式:

    ---
    
    ## <会话标题> -- <时间戳>
    
    ### 👤 <用户标签>
    <提问内容>
    
    ### 🤖 <助手标签>
    <回答内容>
    

    段落标签(用户/助手/图片/空内容占位)由 client 按当前界面语言本地化后传入 (中文「用户/DSH/(无)/[图片]」,英文对应),因此笔记内容跟随 dsh 的语言设置。 思考内容(reasoning)不记入——只保留最终回答,与 dsh 界面(Think 为临时展示)一致。

2.4 Git 同步

笔记本地永远保存在各工作区 <工作区>/.dsh-notes;Git 同步是可选能力,通过 URL 驱动的仓库(插件自动 git clone 到 $DSH_HOME/md-notes-repos/<url-hash>/)。 两种互斥模式:

  • 共享仓库(gitMode: 'shared'):一个仓库 URL(+ 可选分支,默认 main), 所有工作区笔记推到该仓库该分支、各自一个子目录——子目录名在首次推送时固定(仓库根 .dsh-notes-workspaces.json 记录 ws.id → 目录名),工作区改名后目录不变、不孤儿化。
  • 独立仓库(gitMode: 'own'):每工作区配仓库 URL + 分支(默认 main)+ 仓库内子路径(默认根)。

交互:

  • 保存 = 写入本地 .md(不碰 git);推送 = 镜像同步本地笔记到仓库目标目录 (含删除同步)→ commit → push;更新 = 拉取远端分支 → 同步回本地。
  • 入口:管理器左栏工作区 Git 同步卡片(§2.2)——每个工作区一张卡片(默认收起,点 工作区行的 git 图标展开),含状态 pill(已同步 / 未推送 N 处)、分支/子路径/最近提交、 更新/推送按钮;底部为全局 Git 汇总行(跨工作区未推送求和)。
  • 共享仓库隔离(0.7.0):shared 模式下 Git 状态与提交按工作区子目录隔离, 每个工作区的推送/更新只作用于自己名下的子目录,互不串扰。
  • 冲突交用户(三向判定):推送/更新前用 base(上次同步态)/ local / remote 三方比较, 只有「远端相对上次同步变了且本地也变了(或本地删了远端还在)」才弹确认窗「用本地覆盖远端」 /取消;本地单方面改动不算冲突。更新时本地已修改的文件保守跳过,弹确认「用远端覆盖本地」。
  • 自动拉取:打开笔记时按 gitAutoPull(默认开)先保守拉取;有冲突时提示手动更新。
  • 完整设计见 git.md。

2.5 设置面板「MD 笔记」分区

dsh 设置面板(settings.section 注册)提供完整配置,表单控件与 dsh 一致 (DshInput / DshSelect,token 化配色、暗黑模式适配):

  • 模式三态:关闭 / 共享仓库 / 独立仓库;
  • 共享仓库:仓库 URL + 分支(默认 main);
  • 独立仓库:每工作区 仓库 URL + 分支 + 仓库内子路径;
  • 全局:自动拉取开关、提交作者名/邮箱;
  • 顶部提示面板:说明笔记本地保存在 <工作区>/.dsh-notes,Git 同步不影响本地位置。

2.6 版本更新提示

插件初始化后(侧边栏入口或管理器首次挂载时)检查 npm 上 dsh-md-notes 的最新版本:

  • host 查询 https://registry.npmjs.org/dsh-md-notes/latest,与当前安装版本(包根 package.json)比较, 结果缓存 10 分钟;网络失败/读版本失败静默跳过。
  • 有新版时:
    • 侧边栏笔记入口按钮尾部显示黄色 tag「有新版本需要更新」(hover 显示最新版本号);
    • 笔记管理器标题栏设置按钮旁同款 tag。
  • 配色用 dsh 主题 warn token(amber 黄色系,20% 透明度背景 + 深黄文字),明暗主题自适应。

2.7 笔记加入对话上下文(@ 引用,已实现)

在对话输入框通过 @ 触发器引用笔记:选中后输入框出现 chip,发送时host 把笔记内容注入 模型上下文(agent/pre-step,§2.7.1)——模型直接拿到内容,不依赖它自己调用 read。 与「记入笔记」形成闭环。依赖 dsh 的 ui-input-trigger 引用管线。

交互流程:

  1. 输入 @ → 弹出候选菜单,列出当前会话工作区的笔记(行首为标题、副行为文件名, 行首前缀插件 logo——dsh 0.1.5-rc 起 icon 字段接受组件,此前为通用文件图标); 无工作区的会话不弹候选(静默)。
  2. 上下键/点击选中 → 输入框出现笔记 chip(占位符),可多选;点击已插入的 chip 在右侧栏打开该笔记的查看器(dsh 0.1.5-rc 的 openReference 手势,发送前即可确认; 无 Sidebar 时点击无副作用);chip 为 dsh 原生 4em 单元格, 标签前置截断(>4 字符 → 前 4 字符 + …,显示开头而非中间一截;短标题完整显示), chip 前置插件 logo(0.7.0,保留 appearance='notes' 作用域 + 注入 scoped 样式绘制图标)。
  3. 跨工作区:输入部分工作区名(如 @dsh-pl)→ 候选出现工作区行(dsh-plugin/, 文件夹图标 + 「工作区」说明)和当前工作区过滤后的笔记;点击工作区行自动补全 @工作区名/ 并弹出该工作区笔记,继续输入即过滤(已进入工作区后只显示该工作区; 中文(无空格)工作区名已支持,带空格的工作区名无法文本触发)。跨工作区笔记的 副行带 工作区名 · 文件名。
  4. 发送 → 每个 chip 经 codec.serialize 序列化为标准 markdown 链接语法(随界面语言),如 引用笔记 [标题](.dsh-notes/xxx.md)(同工作区)或 引用笔记 [标题](../dsh-work/.dsh-notes/xxx.md) (跨工作区,相对会话工作区根);host 端在模型请求前把笔记内容直接注入上下文 (agent/pre-step,界面显示为注入上下文行)——模型无需调用 read 也能引用。
  5. 引用失效:被引用笔记已删除/移动时发送被阻断(dsh 合约),提示 「<笔记名> 无法找到,请删除引用」,draft 与 chip 保留,不静默降级。
  6. 纯文本 @笔记名 仅为装饰(lexicon 高亮,仅 ASCII 标题生效),不注入上下文; 真正引用必须走菜单 chip。

2.7.1 注入行为(host 端)

  • 发送后 host 监听 agent/pre-step(dsh 原生机制,agent-instructions 同款):扫描已认领消息中的 笔记路径(.dsh-notes/… 相对会话 cwd 解析),读取内容并作为注入上下文消息 (source.kind: 'md-notes')折叠进模型请求——模型直接拿到内容,无需调用 read。
  • 注入内容头部带中英双语引用约定:回答中如需引用本笔记,用 markdown 链接格式 [标题](路径)(与用户消息的序列化语法一致)。
  • 注入消息随该 step 持久化进会话日志(渲染为对话里的「上下文注入」折叠行),一次引用注入一次 (按 source.path 去重),并会留在会话上下文中直到 dsh 压缩旧历史(追问无需重新引用)。
  • 引用失效:笔记已删除则跳过注入,用户消息里的路径行保留(模型可尝试 read 或说明缺失)。
  • 内容边界:只读取 .dsh-notes 目录内的文件。

完整设计见 context.md。

2.8 笔记写入互斥(写锁 + 全局写入状态)

对笔记的写操作(保存 / 记入笔记 / 删除)跨会话互斥——同一笔记写入期间,任何会话对其 再写都被拒绝(host 键控锁 KeyedLock,冲突返回 note-writing;client 端 busy 镜像负责 UI):

  • 记入弹窗:写入中的笔记不可选中 + 行尾 loading;
  • 管理器:该笔记行尾 loading + 删除隐藏;操作栏编辑 / 更新 / 保存 / 推送禁用、 提示位显示「正在写入文件」;
  • 笔记入口:有任一笔记在写即显示 loading + tooltip「{count} 个笔记正在写入」 (位于版本更新 tag 前);
  • 写入完成(成功 / 失败)自动还原全部位置。

状态模型(通用 busy 切片,可扩展至 git / 导出等未来任务域)与方案详见 state.md 与 write-lock.md。

2.9 右侧 Sidebar 笔记查看器(dsh 0.1.5+,已实现)

在 dsh 的右侧停靠面板(right Sidebar)里以 markdown 渲染打开笔记:

  • 认领规则:注册 md-notes tab 类型(extension 优先级,压过内置纯文本查看器), 认领路径以 /.dsh-notes/<名>.md 结尾的文件资源地址(dsh-resource://file/…, 会话相对与绝对两种作用域都支持,跨工作区 ../ 引用走绝对地址)。同一地址复用同一 tab。
  • 入口:凡经 dsh 原生「文件打开」管线点到笔记文件的地方(对话中的文件链接/提及、 文件树等),都路由到本查看器;笔记互链(`名` / [[名]])点击也在侧栏开新 tab (目标笔记的绝对地址);管理器搜索结果的笔记行另有「在侧栏查看」动作 (noteFileAddress 建地址 → openResource → 关闭管理器,见 search.md §2)。
  • 本地图片:笔记里的本地路径图片(![](img.png)、../shot.png、绝对路径)经 MarkdownText.pathImages 词汇表改写为同源认证的 /api/file?path=… URL 后渲染 (features/path-images.ts,目标相对笔记所在 .dsh-notes 目录解析;管理器预览与 查看器同词汇表;Electron file:// 等非 HTTP 载体不启用,退回 alt 文本)。
  • 正文:MarkdownText 渲染(与管理器预览同一词汇表:代码块复制、脚注、互链), 头部为笔记标题 + 工作区 chip + 手动刷新;读取走插件自身 list/read API(地址 → 工作区 + 笔记名映射见 NoteViewer/address.ts,纯逻辑含测试)。
  • 边界:查看器只读(编辑仍在管理器);dsh 构建没有右侧 Sidebar(< 0.1.5)时该功能 整体静默停用(可选服务,不影响其它功能)。

2.10 曾评估过:把笔记当 agent 记忆(已放弃,代码已移除)

  • 试过什么:给模型三个笔记工具(note_search / note_read / note_write),并每会话 注入一条「笔记库存在、何时该查」的提示,让模型自己去查与记,而不是等用户 @。
  • 为什么放弃:这条能力要成立,前提是「它能提升问答效果」,而这个主张没有实测结论 (评测设计与预先定好的判决线曾写好,最终未执行)。在价值未验证的情况下默认开启等于让用户 当实验,因此代码已整体移除(不是默认关闭)。
  • 留下的部分:@ 引用注入(§2.7)不受影响;「笔记能否被 agent 自己用起来」这件事的判断 与理由记在 TODO.md §0.9,避免将来重复论证。

3. 交互状态与反馈

场景反馈
新建成功列表刷新 + 自动打开新笔记 + 提示「已创建 ✓」
保存成功提示「已保存」
写入笔记成功按钮前绿色「已写入 ✓」,900ms 后自动关闭弹窗
写入进行中按钮显示「写入中…」;该笔记在弹窗不可选 + 行尾 loading;入口 loading + tooltip
更新成功提示「已更新 ✓」并刷新列表
推送/更新冲突页面内 Modal 确认(推送「用本地覆盖远端」/ 更新「用远端覆盖本地」)
删除页面内 Modal 确认(红色确认按钮,提示不可撤销)
任一操作失败面板内显示本地化错误信息(host 返回错误码 + client i18n 渲染)

4. 数据模型

笔记 = 各工作区 <工作区>/.dsh-notes/ 下的普通 .md 文件:

<工作区>/.dsh-notes/
├── <note-name>.md      # 笔记内容(markdown)
└── meta.json           # { "<note-name>.md": { title, updatedAt } } 标题与更新时间缓存(不入库)
  • 文件名默认由标题规范化而来(非法字符转 -,强制 .md 后缀),创建时也可显式指定;同一工作区内文件名唯一(创建弹窗查重拦截),标题可重名(标题只是 # heading 展示名,非身份)。
  • meta.json 为最佳努力缓存:缺失或损坏时自动重建(0.7.0——从笔记标题与文件 mtime 恢复,无需手动修复);正常读取时也按标题/更新时间缓存。
  • 笔记本地位置恒定 <工作区>/.dsh-notes(v3/v4 定案):Git 仓库只是同步目标, 笔记位置不随仓库配置变化,不存在目录迁移问题。
  • 笔记深度绑定工作区:没有工作区归属的会话无法读写笔记(list 返回 noWorkspaces,界面提示先新建工作区)。

5. 非功能需求

  • 普通文件可访问:笔记就是 .md 文件,用户可直接在文件系统编辑,插件下次读取即生效。
  • 无外部依赖:Host/Client 通信走插件自带 HTTP 路由,不依赖 typert/Remote 工具链。
  • 主题适配:样式使用 DSH 主题 CSS 变量(--dsw-alias-*)并带静态兜底,明暗主题均可读; 表单控件(输入框/下拉框)用 DshInput/DshSelect 与 dsh 原生表单一致。
  • i18n:所有 UI 文案中英双语(zh.ts 源字典 + en.ts 映射类型强制同键); host 错误以错误码 + detail 返回,client 用 gitErrorText 渲染本地化文案。
  • HMR 安全:所有副作用(路由注册、样式注入、slot 注册)均挂在 ctx.effect 上,卸载时自动清理。

6. Git 同步(已实现)

Git 同步已实现(v3/v4 模型),详见 git.md:笔记本地固定 <工作区>/.dsh-notes, 仓库由 URL 驱动(插件自动 clone)、共享/独立双模式、镜像同步(含删除)、冲突确认弹窗、 自动拉取、i18n 错误码。设计要点:

  • URL 驱动:仓库只配 URL,插件管理本地 clone($DSH_HOME/md-notes-repos/<url-hash>/), 无路径配置、无授权流程。
  • 互斥模式:shared(一个仓库管所有工作区,按工作区子目录隔离)/ own(每工作区三件套)。
  • 推送 = 镜像同步:复制本地 .md 到仓库目标目录 + 删除本地已删文件 → commit → push。
  • 更新 = 反向同步:拉取远端分支 → 目标目录 .md 复制回本地(不覆盖本地已修改文件)。
  • 冲突交用户:推送/更新的覆盖都经页面内确认弹窗;non-fast-forward 提供「合并远端并重试」。
  • 状态可见(0.7.0):工作区 Git 卡片 + 全局汇总条展示「未推送 N 处」(unpushed, 按本地与仓库差异计算)。