dsh-md-notes 功能设计文档
September 23, 2026 · View on GitHub
本插件为 DSH 提供 MD 笔记管理:在侧边栏常驻入口,打开全屏笔记管理界面; 支持把任意一条回答及其提问直接记入指定笔记;并可选地把笔记同步到 Git 仓库 (共享仓库 / 独立仓库双模式)。
措辞对照:本文说的 host / client 即「后端(跑在 dsh 的 Node 进程里)/ 前端(跑在浏览器 页面里)」,详见 architecture.md §1。
1. 功能总览
| 功能 | 入口 | 说明 |
|---|---|---|
| 打开笔记管理 | 侧边栏「笔记」入口 | 全屏面板:左侧按工作区分组的列表 + 右侧编辑/预览 |
| 新建笔记 | 激活工作区行「+」 | 弹窗填标题(默认「未命名笔记 日期」)+ 可选文件名,重名拦截,随即打开编辑 |
| 编辑笔记 | 管理器右侧「编辑」Tab | markdown 源码编辑,点「保存」写入 |
| 预览笔记 | 管理器右侧「预览」Tab | dsh 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/,插入  相对引用;预览内联 + 点击灯箱(本机文件,不同步) |
| 设置 | 管理器标题栏 ⚙ / dsh 设置面板「MD 笔记」 | 模式、仓库 URL、分支、自动拉取、作者等 |
2. 功能详述
2.1 侧边栏入口(笔记)
- 位置:左侧栏底部区域,通过
sidebar.footer.actionslot 注册。 - 点击打开笔记管理全屏面板(
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 引用管线。
交互流程:
- 输入
@→ 弹出候选菜单,列出当前会话工作区的笔记(行首为标题、副行为文件名, 行首前缀插件 logo——dsh 0.1.5-rc 起icon字段接受组件,此前为通用文件图标); 无工作区的会话不弹候选(静默)。 - 上下键/点击选中 → 输入框出现笔记 chip(占位符),可多选;点击已插入的 chip
在右侧栏打开该笔记的查看器(dsh 0.1.5-rc 的
openReference手势,发送前即可确认; 无 Sidebar 时点击无副作用);chip 为 dsh 原生 4em 单元格, 标签前置截断(>4 字符 → 前 4 字符 + …,显示开头而非中间一截;短标题完整显示), chip 前置插件 logo(0.7.0,保留appearance='notes'作用域 + 注入 scoped 样式绘制图标)。 - 跨工作区:输入部分工作区名(如
@dsh-pl)→ 候选出现工作区行(dsh-plugin/, 文件夹图标 + 「工作区」说明)和当前工作区过滤后的笔记;点击工作区行自动补全@工作区名/并弹出该工作区笔记,继续输入即过滤(已进入工作区后只显示该工作区; 中文(无空格)工作区名已支持,带空格的工作区名无法文本触发)。跨工作区笔记的 副行带工作区名 · 文件名。 - 发送 → 每个 chip 经
codec.serialize序列化为标准 markdown 链接语法(随界面语言),如引用笔记 [标题](.dsh-notes/xxx.md)(同工作区)或引用笔记 [标题](../dsh-work/.dsh-notes/xxx.md)(跨工作区,相对会话工作区根);host 端在模型请求前把笔记内容直接注入上下文 (agent/pre-step,界面显示为注入上下文行)——模型无需调用read也能引用。 - 引用失效:被引用笔记已删除/移动时发送被阻断(dsh 合约),提示 「<笔记名> 无法找到,请删除引用」,draft 与 chip 保留,不静默降级。
- 纯文本
@笔记名仅为装饰(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-notestab 类型(extension优先级,压过内置纯文本查看器), 认领路径以/.dsh-notes/<名>.md结尾的文件资源地址(dsh-resource://file/…, 会话相对与绝对两种作用域都支持,跨工作区../引用走绝对地址)。同一地址复用同一 tab。 - 入口:凡经 dsh 原生「文件打开」管线点到笔记文件的地方(对话中的文件链接/提及、
文件树等),都路由到本查看器;笔记互链(
`名`/[[名]])点击也在侧栏开新 tab (目标笔记的绝对地址);管理器搜索结果的笔记行另有「在侧栏查看」动作 (noteFileAddress建地址 →openResource→ 关闭管理器,见 search.md §2)。 - 本地图片:笔记里的本地路径图片(
、../shot.png、绝对路径)经MarkdownText.pathImages词汇表改写为同源认证的/api/file?path=…URL 后渲染 (features/path-images.ts,目标相对笔记所在.dsh-notes目录解析;管理器预览与 查看器同词汇表;Electronfile://等非 HTTP 载体不启用,退回 alt 文本)。 - 正文:
MarkdownText渲染(与管理器预览同一词汇表:代码块复制、脚注、互链), 头部为笔记标题 + 工作区 chip + 手动刷新;读取走插件自身list/readAPI(地址 → 工作区 + 笔记名映射见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, 按本地与仓库差异计算)。