dsh-code-marks
Select code or a sentence in a DeepSeek Harness conversation, tag it, and jump back to that exact line later — from a rail of deep-blue bars on the conversation's right edge, or from a right-sidebar tab. Marks live outside the transcript, so the next twenty follow-up questions cannot push them away.
在 DSH 对话里选中一段代码或一句话 → 打个标签,它就固定下来:对话右边缘长出一根深蓝短条,右侧栏的「标记」页签里也有一条记录。点任意一处,那段原文瞬时跳到可视区正中间、闪一下并选中。
解决的是这件事:追问一多,正确题解就被顶上去,回看要翻很久。标记存在对话流之外,所以永远顶不掉。
- 标注对象:助手回答里的代码段与文字(跨行选也支持)
- 标签自定义:可新建 / 改名 / 换色 / 删除,6 种颜色
- 跳转精确到字符,不是"大约回到那条消息"
- 不上传、不同步、不进会话日志:全部存在本机
localStorage
装上
需要 DSH 0.2.0-rc.2 或更新(桌面端或 dsh web)。
装的是"插件组合包",所以 DSH 只认 安装 spec(不是搜索词)。在桌面端走:侧栏 插件 → 安装 → 粘贴下面任一 spec;或者用 CLI dsh plugin --profile desktop add "<spec>"。装完不用重启,刷新一次页面即可。
一 · 从 GitHub 装(不需要 npm 账号)
三种写法,效果一样,按你的网络和工具选:
# 1) git 协议(pnpm 会浅克隆)
dsh plugin --profile desktop add "github:petex7238/DSH-Tag-Plugin#main"
# 2) tarball 直链(没有 git 也能装,https 一次下载)
dsh plugin --profile desktop add "https://github.com/petex7238/DSH-Tag-Plugin/archive/refs/heads/main.tar.gz"
# 3) 下载 ZIP → 解压 → 用本地路径装
# 浏览器打开仓库 → 绿色 Code 按钮 → Download ZIP → 解压到任意目录
dsh plugin --profile desktop add "D:/tools/DSH-Tag-Plugin"
三点提醒:
- 网络:GitHub 装法要能访问 github.com。国内环境下 git 协议还需要给 git 配代理(
git config --global http.proxy http://127.0.0.1:7890),tarball 直链走的是 https,通常更容易通。
- 固定版本:把
#main 换成 #<commit sha> 或 #v0.1.0 就不会被后续改动带着跑。
- 更新:
github: / tarball 装的不会自动升级,重新执行一次安装命令即可(同一包名会覆盖)。
二 · 从 npm 装(发布后可用)
dsh plugin --profile desktop add dsh-code-marks
或在插件页填 dsh-code-marks。npm 的版本号即包版本,升级同样重装一次。
三 · 本地路径(改代码 / 开发)
git clone https://github.com/petex7238/DSH-Tag-Plugin.git
dsh plugin --profile desktop add "/absolute/path/to/DSH-Tag-Plugin"
仓库目录会被 link 进 profile,改 plugin/client.js 会被客户端 HMR 直接热替换,不用重启 DSH。
装完怎么确认
右上/侧栏 插件 列表里出现 dsh-code-marks 且为启用状态;在对话里选中一段助手回答,右边缘会冒出一根深蓝短条。
怎么用
- 在助手的回答里,用鼠标选中一段代码或一句话(也支持跨行选)。
- 松手后浮出标签条,点一个标签(
核心逻辑 / 疑问 / 易错点 / 待复习,或点 + 现建一个)。
- 松手处冒出一句「已标记「核心逻辑」」,1.5 秒后自己消失;对话右边缘的上部同时长出一根深蓝短条。
不会自动打开右栏——标记不该抢你的视线和焦点。确认只发生在原地,要跳回去就点右边那根条。
回到标记有两条路:对话右边缘的深蓝短条(不用开右栏,悬停看名称、点击跳转),或右栏「标记」页签里的条目——两者跳的是同一处。
面板关掉以后怎么找回来:右栏页签条的 添加 按钮 → 引导页 → 「标记」胶囊。
面板里能做什么
| 操作 | 说明 |
|---|
| 点一条标记 | 跳回原文并居中(旧内容没加载时会自动点「加载更早」把历史拉回来,最多 16 次) |
| 悬停一条标记 | 在对话里临时高亮它(不跳转) |
| 标签 chip 行 | 按标签筛选,括号里是该标签的条数 |
| 搜索框 | 按选中原文或备注过滤 |
| ✎ | 给这条标记写/改备注(比如「这里为什么要 -1」) |
| × | 删除这条标记 |
| ⚙ | 标签管理:新建、改名、换颜色、删除(删除标签时它的标记会转到第一个标签) |
高亮颜色跟着标签走,6 种颜色可选,深浅色主题都用同一套半透明底色。
右边缘的深蓝色短条
对话右边缘的上部会挂一排深蓝色短条,一条对应一个标记,从上到下就是对话里的先后顺序。它和 DSH 自己那条"轮次刻度"是同一套形状(20×2 圆角、10px 节距、右对齐),所以看起来是对话的一部分,不是外挂。
- 不用打开右栏:鼠标挪过去就能用,点击直接跳到那段原文(该翻页会自己翻)。
- 悬停:左边浮出小卡片,显示标签名、原文前几行、第几轮;同时对话里那一段会亮起来,方便确认是不是你要找的那条。
- 右键菜单:在长条上点右键弹出小菜单——「跳转到这条标记」和「删除这条标记」。右键本身不删任何东西,误触只是弹个菜单;真删掉后还有 5 秒「撤销」兜底。键盘也通:Tab 聚焦长条后按
Delete/Backspace(同样有撤销)。
- 不会打架:长条只占对话列上部约 42% 的高度,标记多了在轨道内部滚动;右栏开合、窗口缩放时它会跟着对话列移动。
- 当前会话一条标记都没有时,整排长条不出现。
这两个浮层(框选后弹出的标签条、长条悬停出的小卡片)固定用 #232324 深底 + 浅色字,不跟随主题:深色浮层在浅色页面上更抓眼,浅色主题下也不会变成白底黑字看不清。文字对比度都在 4.5:1 以上(正文 7:1 以上)。
数据与隐私
都在这台机器的浏览器存储里(localStorage),不上传、不进会话日志、不产生任何网络请求:
dsh.code.marks.v2.<sessionId>:这个会话的标记(原文、位置、标签、备注、时间)
dsh.code.marks.v1.tags:标签表(全局共享,所有会话通用)
每个会话的标记互相独立;换会话就看那个会话的标记。清空:清站点数据,或删掉这两个 key。
插件本身没有 Host 半侧逻辑:plugin/index.mjs 是一个空 apply(),只为了让 DSH 有一个可加载的 Loader 条目、从而把浏览器半侧的 bundle 发出去。
它是怎么做到「跳得准」的
一条标记不存 DOM 节点(React 会重渲染、代码块会从 plain 换成高亮版、流式输出一直在追加),只存四样东西:
data-chat-node-key / data-chat-anchor-key:这条消息在对话里的身份(原样存,不解析)
- 消息内文本偏移 + 选中的原文
- 前后各 48 个字符的上下文
data-chat-turn:第几轮(兜底用)
解析时按四级降级:精确锚点 → 同 node 的行 → 精确原文 → 上下文打分(含「空白全部忽略」的兜底,所以重新缩进的代码也能认出来)。
高亮用 CSS Custom Highlight API(CSS.highlights + ::highlight()),只建 Range 不改 DOM,所以不会被 React 的 diff 冲掉;对话变化时以 240ms 去抖重建 Range。
跳转是按"标记文本的 Range"算的,不是按消息行:一条长回答可以几千像素高,行居中会让人"跳过去却看不见"。落点对齐到对话可视带的中线(扣掉底部输入框占位),并且瞬时完成、不做滚动动画。
已知限制
- 折叠起来的过程组里的文字选不到;要先展开它。标记落在被折叠的内容里时,点标记会尝试点开那个组,失败则提示「先往上滚一点」。
- 原文被改写(比如重新生成过这条回答)到认不出来的程度时,只能定位到这条消息,高亮位置对不上——面板会明确提示,不会瞎标。
- 跳转是程序化滚动,DSH 会认为「读者自己滚了」,于是暂停「跟随最新」;对话自己跳轮次也是这样。
- 只对助手的回答(
assistant-step)开放标记;工具调用、终端输出暂不支持。
- 标记只存在本机浏览器,不跨设备同步;也没有导出/导入。
卸载
DSH 设置 → 插件 → 找到 dsh-code-marks → 停用或卸载。卸载后浏览器里的标记数据仍在,重装即回来。
开发
插件是手写的浏览器 bundle,没有构建步骤:plugin/client.js 就是一个 window.__ModuleLoader__.load({ id, factory }) 的经典脚本,DSH 的模块系统直接发它。
npm install # 只为测试台装 puppeteer-core
npm test # 100 项断言,headless Chrome
测试台 tools/harness/ 用一份合成的对话 DOM 跑真实 Chromium,覆盖:锚定与四级降级、重复原文、代码块 plain→Shiki 换 DOM、流式追加、翻页、悬浮标签条、边缘长条(含右键菜单/撤销)、深色浮层对比度、跳转居中与瞬时落位、面板渲染。
CHROME_PATH 可指定浏览器;否则自动找 Chrome/Edge/Chromium。
- 没有
puppeteer-core 时会退而使用 DSH profile 里已有的那一份(需要 DSH_HOME + DSH_PROFILE)。
设计说明.md 记录了每个设计决策的理由与验证矩阵(中文)。
发布(维护者)
包名 dsh-code-marks,publishConfig.access 已是 public。两条路,选一条:
A. 本机发(需要先 npm login):
npm publish
B. 让 GitHub 代发(本机网络/登录不顺时用这条):
- 在 npmjs.com 生成 Automation 类型 token(不会弹 OTP),权限 Read and write;
- 仓库 → Settings → Secrets and variables → Actions → 新建 secret
NPM_TOKEN;
- 发一个 GitHub Release,或手动跑
publish workflow。
publish workflow 会先 npm pack --dry-run 打印将发布的文件清单,再 npm publish;版本冲突时 npm 会直接报错。
发新版:npm version patch(或 minor/major)→ push → 再走上面任一方式。
许可
MIT,见 LICENSE。