dsh-explainer(快问)
快问:选中文字、右键,或在右侧栏文件里划词,就地得到答案。一次 completion、最低思考档、流式输出;沿用主窗口模型,继承只读上下文,不写回主会话。
English: select text in the DSH Web GUI and ask about it in place, or open a quick-ask window. Answers stream from a one-shot completion that follows the main window's model, inherits a read-only context snapshot, and reads files the question references. Nothing is written back to the main session.
功能
- 划词问:选中对话里的文字 → 出现「✦ 问这段」→ 点击、右键或
Ctrl/Cmd+Shift+E打开窗口。 - 右侧栏文件也能用:原生 document preview 和 better-sidebar 的编辑器里都能划词/右键,默认读取该文件全文作为上下文(仅工作区内,只读)。HTML 预览是不透明 iframe,浏览器不允许读取其中的选区;请用文档工具栏的「✦ 问一下」(读取整个文件)或切到源码视图再划词。
- 快问:输入框右侧「✦ 快问」或
Ctrl/Cmd+Shift+K打开同一张卡片,不带引用。 - 模型与主窗口一致:读取主窗口的
modelSelection投影,每次请求都带同一个provider/model;主窗口切换后立即跟随。 - 零点击追问:输入框常驻卡片底部,答案结束后直接回车继续问,沿用同一个隔离线程。
- 上下文继承 + 隔离:新建线程时把主会话最近消息做一次只读快照;之后只写宿主内存里的线程,主会话日志不变。
- 文件快照(只读):问题或上下文里提到的文件(路径或文件名)会自动读取修改时间、大小和内容片段,随提示词一起发给模型;因此能回答“这个文件什么时候更新”这类问题,而不需要工具。
- 答案动作:复制、插入主输入框、发主会话(升级深答)、重试。
- 轻量窗口:可拖动,点窗口外 /
Esc/ 再点一次「✦ 快问」关闭,深浅色自适应,中英双语。
快捷操作
| 操作 | 键 / 手势 |
|---|---|
| 划词问 | 选中文字 → 点「✦ 问这段」,或直接右键 |
| 划词问(键盘) | 选中文字 → Ctrl/Cmd+Shift+E |
| 快问 | Ctrl/Cmd+Shift+K 或输入框「✦ 快问」 |
| 发送 / 追问 | Enter |
| 换行 | Shift+Enter |
| 停止 | Esc(流式中先停止) |
| 关闭窗口 | 点窗口外、Esc、或再点一次「✦ 快问」 |
安装
从插件市场
在 DSH 的「插件 → 发现」里搜索 dsh-explainer 安装(需要先在索引源里添加市场索引,见 DSH 插件市场)。
或者用 CLI:
dsh plugin --profile <profile> add dsh-explainer
安装后在 DSH 里重启一次(host 半需要重启;浏览器半随页面加载)。
本地开发安装
git clone https://github.com/wzciczw/dsh-explainer.git
cd dsh-explainer
npm install
npm run build
把仓库用 link: 挂进 profile 的 package.json:
{
"dependencies": { "dsh-explainer": "link:D:\\path\\to\\dsh-explainer" },
"dsh": { "profile": { "bundles": ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "dsh-explainer"] } }
}
然后重启 DSH。link:(而不是 file:)会让 profile 直接引用源码目录,改完 npm run build 重启即可生效,不需要重装。
配置
写在 cordis.patch.yml 的 explainer 行:
- id: explainer
name: "dsh-explainer"
config:
maxTokens: 800 # 单次输出上限
temperature: 0.3
inheritContext: true # 划词问默认继承对话上下文
maxTurns: 3 # 追问保留轮数
contextMaxMessages: 8 # 快照最多几条消息
contextMaxChars: 8000 # 快照总字数
selectionMaxChars: 6000 # 选段上限
timeoutMs: 30000
maxConcurrent: 4
rightClick: true # 选中文字后右键直接打开
fileAccess: true # 注入问题中提到的文件(只读)
fileContextMaxChars: 20000 # 右侧栏文件作为上下文时的全文上限
fileMaxFiles: 4
fileMaxChars: 12000
fileExcerptChars: 4000
# provider / model 仅在拿不到主窗口模型时兜底;默认与主窗口一致
# reasoningEffort 留空则取最低可用档(off / minimal / low)
架构
选中文字 / 右键 ──▶ client store ──▶ POST /explainer/ask (SSE)
│
├─ 读主会话最近消息(只读快照,仅新建线程时)
├─ 解析模型(主窗口优先)+ 最低 effort
├─ 读取问题中提到的文件(工作区内,只读)
└─ ctx.llm.prepareCall + call.stream
│
meta → delta* → done / error
▼
卡片内流式渲染
- 模型:浏览器从
useProjection('modelSelection')读主窗口的持久选择,随每次请求发送;宿主优先级 request → 主会话 request/header → 本线程上次 → 插件配置 → 部署默认。 - 文件:右侧栏文件选择会带上绝对路径,宿主读取该文件全文(上限
fileContextMaxChars)作为上下文;普通对话选择则从选区/问题/继承对话里提取路径/文件名,只在会话工作区内realpath校验后读取 mtime、大小和片段;越界路径忽略。 - Host:
src/host,Cordis 插件;有界线程表、一次性模型调用、SSE 路由。 - Client:
src/client,两个 slot ——shell.overlay(浮动层)与conversation.input.right(启动按钮 + 会话桥)。 - 不 import
@deepseek-ai/*运行时;宿主能力走ctx.get(...)的结构化接口,跨小版本更稳。 - 客户端 bundle 运行时只依赖 harness 注入的
react,构建脚本会拒绝其他裸require。
开发
npm install
npm run build # tsc host + client,再合成单个 lib/client.js
npm run typecheck
npm test # 构建 + node --test test/*.test.mjs
测试覆盖(21 项):配置边界、会话快照、线程 LRU/TTL、引擎(继承一次/追问/模型优先级/并发/effort/会话日志不变)、文件快照与越界拒绝、右侧栏文件全文上下文、HTTP 路由与 SSE、bundle 冒烟、jsdom UI(流式/追问/插入/点外关闭/右键打开/原生预览路径/better-sidebar 编辑器路径/工具栏问文件)。
端到端(真实浏览器)
e2e/ 下有 Chrome CDP 脚本和一个确定性的 fake 模型,需要一个独立的测试 profile(不要动正在运行的桌面实例):
- 复制
~/.dsh/profiles/web为~/.dsh/profiles/explainertest package.json的 bundles 追加dsh-explainer,node_modules/dsh-explainer建 junction 指向本仓库cordis.patch.yml加入:- id: agent-default-model config: { provider: fake, model: fake, reasoningEffort: off } - insert: - id: fake-llm name: "<绝对路径>/e2e/fake-llm.mjs"dsh --profile explainertest --port 3097 --host 127.0.0.1 --no-openchrome --headless=new --remote-debugging-port=9223 ...EXPLAINER_TOKEN='...' node e2e/browser.mjs
验证内容:启动页、/explainer/config、SSE、选区胶囊、划词引用、右键打开、追问、点外关闭、Esc。
安全与隐私
- 所有路由过
ctx.connection.requestRejection(req);连接服务缺失时 fail closed。 - 选段与答案不落盘、不进 Session;host 日志只记长度/耗时/结束原因。
- 答案用 token → React 渲染,不用
innerHTML。 - 上下文继承会把最近对话发给当前模型 provider;文件快照只读会话工作区,且只读问题中提到的文件。
已知限制
- 不支持图片、多选、跨消息多段引用。
- 线程只在宿主内存,刷新页面即丢。
- 快问不挂工具,不能自己探索仓库;只会读取问题中提到的工作区文件。要深入探索请用「发主会话」。
- HTML 预览用 DSH 的不透明 sandbox iframe 渲染,父页面读不到其中的选区;这是浏览器安全边界,不是插件能绕过的。用工具栏「✦ 问一下」或切到源码视图。
License
MIT © wzciczw
No comments yet. Be the first to write one.