快速问答(dsh-quick-qa)
课堂上的"随手一问":框选屏幕上的题目 → 发送 → 结合工作区资料秒出答案。
入口在左侧栏「新会话」下方(快速问答)。按键:鼠标键4 开始截图、鼠标键5 发送、Alt+Q 开关快捷栏。
快捷栏与缩略图
答案框
怎么用
- 点左侧栏 快速问答 → 桌面弹出横向小细条(快捷栏,类似输入法候选框)。
第一次点击会顺带启动一个独立的 PowerShell 小进程(它负责窗口、全局热键和鼠标键)。
- 按
鼠标键4(鼠标侧键靠后的那颗)→ 光标变十字,屏幕只画选区的边框,不遮暗、不加蒙版。
按住左键拖出题目(拖动时实时画框),松开就截图;只点一下不算(无效操作,继续等),
右键或 Esc 取消。截图成功后快捷栏上方出现缩略图(含选区尺寸);
这时按 鼠标键5(或回车)才发送 —— 不满意就 Esc 丢掉重框。
- 1~9 秒后细条下方弹出答案框:答案在最前,下面是简要解释、依据(引用到的资料)、
参考资料列表和各阶段耗时。答案框比细条宽一点、限高可滚动。
也可以不截图:直接在细条的输入框里手打题目,回车或 鼠标键5 发送(这条路径最快,通常 ~3 秒)。
随时开关快捷栏:按 Alt+Q 一次把细条显示出来(回到上次关闭时的位置),再按一次收起
(连答案框一起收)。这些按键都跟那个小进程绑定,进程在它们就在。
(鼠标键4/5 走的是全局低级鼠标钩子,默认会把这两颗键吞掉 —— 浏览器、资源管理器里就不会再前进/后退;
不想要这个副作用就把快捷栏脚本的 -MouseSwallow 传 0。)
窗口怎么摆:按着细条上任何不是按钮的地方(标题文字、空白、缩略图都行)拖动就能移动,
位置会被记住,下次框选不会被拉回原位;把鼠标挪到细条左右边缘(离边框 9 像素内,光标会变成
左右调整)按住拖就能改宽度(480 ~ 1320),拖左缘时右边界不动,宽度同样记住。
右侧 ⚙ 调整体透明度(快捷栏和答案框一起,最低 30%,默认 100%;点一下展开、再点一下收起,
拖那条轨道的圆形把手改),✕ 收起整个快捷栏
(Alt+Q 仍然可用)。答案框始终跟着快捷栏走(水平居中、比栏宽 120),但可以单独用它的圆形 ✕ 关掉
(关掉的这一条不会再自己弹回来,下一条新答案才会重新出现)。
工作区
- 细条上的 工作区胶囊按钮(默认显示当前工作区名)点开就是工作区列表,可随时切换;
左侧栏面板里的下拉框是同一份绑定。
- 作答时会把该工作区里的 文档 / PDF / PPTX(还有 docx、xlsx、md、txt)抽成文本建索引,
8 段最相关资料随题目一起给模型。面板上能看到「已索引片段 / 资料文件 / 索引耗时」。
- 索引按文件修改时间缓存,改过资料后点面板的 重建索引 即可。
外观
快捷栏 / 答案框跟随 DSH 主题:客户端半边每 2.5 秒把 DSH 的 --dsw-alias-* token 推给宿主,
快捷栏按 token 上色(深色/浅色自动切)。有几处 token 在 DOM 里读不到(--dsw-alias-border-*
基本读不出来,浅色下三层底色又都是纯白),所以:
- 底色优先取
bg-base(DSH 的应用基础面),不拿 overlay(那是压暗层,浅色下是灰蓝 #e9ecf2,
跟 DSH 不搭);
- 边框按
底色 + 文字色 混 15%,内层小面(工作区胶囊 / 输入框)混 7%;
- 值统一归一成 6 位 hex —— DSH 常给
#fff 这种三位简写,不归一混色会静默失败。
窗口本身没有阴影(DropShadowEffect 已去掉),圆角靠外框 CornerRadius +
内层 Border 圆角(WPF 的 Button/TextBox 自己没有圆角属性,所以胶囊和输入框都是
"透明控件 + 圆角 Border 外壳")。
实测速度(DeepSeek-V4.1-Flash,本机)
| 路径 | 读题 | 检索 | 作答 | 合计 |
|---|
| 手打题目 | — | 4 ms | 0.7 ~ 2.9 s | ~1 ~ 3 s |
| 截图框选 | 2.1 ~ 6.4 s | 6 ms | 1.5 ~ 4.6 s | 3.5 ~ 9 s |
要的就是"别等":答案是先出的,解释和参考资料跟在后面。
结构
| 文件 | 作用 |
|---|
lib/impl.js | 宿主半边:HTTP 路由、建索引/检索、读题与作答、历史、启动快捷栏进程 |
lib/client.js | 客户端半边:左侧栏入口 + 面板(工作区切换、统计、最近问答、主题同步) |
lib/quickbar.ps1 | 快捷栏本体:Windows PowerShell 5.1 + WPF,独立进程,全局热键、框选层、答案框 |
lib/extract.py | 资料抽取(pypdf / pypdfium2 / python-pptx / python-docx / openpyxl) |
dev/reload.ps1 | 开发用:热重载宿主半边(改宿主代码后跑它) |
dev/spawn-test.cjs | 开发用:复现/排查 spawn 行为的小脚本 |
几个关键设计点:
- 进程隔离:快捷栏跑在独立的 PowerShell 进程里,崩了也带不动 DSH 本体;
宿主与它之间只靠 HTTP 轮询通信(不用 HttpListener、不需要管理员)。
- 框选层:
AllowsTransparency 的窗口里完全透明的像素在系统层面是"点击穿透"的,
所以背景用了 #01000000(alpha = 1/255,肉眼等同全透明,但能接收鼠标)。
- 热键:
HwndSource 的消息钩子必须在 C# 里挂(PowerShell 没法给钩子的 ref bool handled
赋值,异常会冲穿消息泵把进程带崩),钩子里只置标志,真正的框选交给 UI 定时器。
- 鼠标键4/5:
RegisterHotKey 只认键盘,鼠标侧键只能走全局低级钩子(WH_MOUSE_LL)。
低级钩子的回调跑在"装钩的那条线程"上,Windows 对它还有超时限制(回调慢了会直接摘掉钩子、
甚至让鼠标发顿),所以回调里只置静态布尔、一个 PowerShell 语句都不执行,真正的活同样交给
UI 定时器;装钩的线程必须有消息泵(这里是 WPF 的 Dispatcher)。按下和松开都吞掉,免得前台
程序收到半个 XBUTTONUP;-MouseSwallow 0 可让这两颗键照旧触发前进/后退。
- 空正文重试:当前模型没有 reasoning effort 开关,会先写推理;预算被推理吃光时正文会是空的。
所以识别和作答各带一次"加预算重试",正常路径不触发、不拖慢。
- 拖动不打架:整个条形区都在
BarRoot 上接鼠标事件 —— 贴着左右边缘 9 像素内按下算"改宽度"
(光标位移 ÷ DPI 缩放 = 宽度变化,拖左缘时左边界和宽度同时改、右边界钉住),
其它地方按下算"移动窗口"(DragMove()),按钮/输入框/滑块这些交互控件会先被排除掉;
两者都在改动期间置 Positioning 标志,免得 LocationChanged 回调把答案框摆错位置。
- 点一下不算拖动:
DragMove() 只在鼠标真的移出 4 像素之后才启动。否则点到细条空白处
会把输入框的焦点抢走 —— 之后再敲键盘、按回车就全部没反应(踩过)。
- 下拉菜单自己画:工作区下拉和 ⚙ 透明度面板都是自绘的
Popup + 圆角 Border。
用系统 ContextMenu 会带一层系统蓝底 + 蓝色高亮(跟 DSH 的白底完全不搭),
自己画之后悬停用的是 DSH 的 layer-2,选中项前面一个实心点。
- 自绘 Popup 的"开关"只能靠自己的标志位:
StaysOpen=False 时鼠标一按下 Popup 就先被关掉,
Click 是抬起之后才跑 ⇒ 处理函数里 $popup.IsOpen 必然已经是 false,Closed 事件也分辨不出
"刚才是被按钮关掉的" ⇒ 想做成"按一下展开、再按一下收起"只能用脚本自己的计数标志
($script:OpacityMenuShown,和工作区菜单同一套),不要依赖 Popup 自身状态或鼠标坐标判断(踩过)。
- 透明度面板的圆把手:自定义
Slider.Template —— Grid 里架 Border(轨道)+ Track x:Name="PART_Track"
(没有它滑块不能拖)+ 13×13 的 Ellipse(把手);模板里的元素不在窗口命名域,FindName 取不到,
颜色只能靠 TemplateBinding 引 Slider 自身的 Background(轨道底)/ BorderBrush(轨道边)/ Foreground(把手),
两个 RepeatButton 用透明模板占位,让点轨道两端也能跳值。
- 提示只露一次:宿主上的
note("模型返回空正文"之类)会一直留着,所以快捷栏显示过一次就
回头把它清掉,9 秒后自己收回;启动时也先清一遍,免得上一轮崩溃留下的那条一直挂在状态栏上。
- :提交后满 15 秒仍未完成,答案区显示 ,隐藏模式也只显示这段等待提示;后台继续读题、查资料和作答,完成后自动替换为答案。
资料准备、原生识图和作答共用 90 秒总预算,不会在 20 秒或 45 秒时提前结束。达到总上限时显示超时结果;快捷栏的忙状态兜底也调整为 90 秒。
已知限制
- 按键依赖那个 PowerShell 小进程,而它是在第一次点开「快速问答」时启动的
(DSH 冷启动时不会拉起任何东西)。所以 DSH 重启后先点一次入口,鼠标键4/5 和 Alt+Q 才生效。
- 模型通道是共用的:如果同时有别的长任务在占着同一个模型(比如正在跑一个很长的对话),
这条问答会在"排队/作答"里等,墙上时间可能远大于插件自己统计的秒数。超时兜底会兜住它,
但真正变快只能靠错开时间。
- 读题耗时随模型负载波动(2 ~ 6 s),偶发一次超过 10 s 会显示"截图里没识别到题目",
重框一次即可。
- 截图路径的准确率取决于框选是否把题干和选项都框进去(框选区里如果压着别的窗口,也会一起截进去)。
- 边缘的"只点一下"不会被当成拖动(需要真的拖出宽度变化才落盘)。
- 插件面板上的「快捷栏在运行 / 框选 X / 开关 Y」靠心跳判断:心跳是这次新加的宿主路由,
要重启一次 DSH 才会认识它(在此之前面板仍可能显示"快捷栏未运行"和旧按键)。
安装
需要 DeepSeek Harness(DSH)。插件不用编译,装法是"一个 profile 依赖 + 一行 patch":
-
把本仓库放进 profile 的 node_modules(file: 或 npm 皆可),例如
%USERPROFILE%\.dsh\profiles\desktop\node_modules\dsh-quick-qa。
-
在 %USERPROFILE%\.dsh\profiles\desktop\package.json 的 dependencies 里加上 "dsh-quick-qa"。
-
在 %USERPROFILE%\.dsh\profiles\desktop\cordis.patch.yml 里加:
- insert:
- id: dsh-quick-qa
name: dsh-quick-qa
-
重启 DSH,左侧栏「新会话」下方出现「快速问答」。
文档抽取(PDF / PPTX / DOCX / XLSX)需要一个装了 pypdf、python-pptx、python-docx、
openpyxl 的 Python。默认按 DSH_QUICK_QA_PYTHON → 本机 venv → PATH 里的 python 依次尝试;
都找不到时只降级成"只索引纯文本资料",不影响插件启动。
卸载
停用面板后,删掉 profile 里的两处引用即可(也可用插件管理器移除):
%USERPROFILE%\.dsh\profiles\desktop\package.json 里的 "dsh-quick-qa" 依赖
%USERPROFILE%\.dsh\profiles\desktop\cordis.patch.yml 里 id: dsh-quick-qa 的 insert 行
开发接口与日志
宿主路由(前缀 /dsh-quick-qa/v1):
GET state、POST show | hide | exit | bind | theme | note | ask | index | selftest | probe | matrix
- 快捷栏日志:
%TEMP%\dsh-quick-qa\quickbar.log
- 框选出来的选区图:
%TEMP%\dsh-quick-qa\sel-*.png
- 快捷栏记住的位置与宽度:
%TEMP%\dsh-quick-qa\bar-pos.txt(格式 左,上,宽,删掉就回到默认居中)
- 改
quickbar.ps1:杀掉 quickbar.ps1 进程后下次 POST /show 生效(想立刻生效就直接
重起一个:powershell -File lib\quickbar.ps1 -ToggleHotkey Alt+Q,它会照常轮询 /state 干活)
—— 注意别传空字符串参数(-Hotkey '' 会被命令行吞掉,PowerShell 就把下一个 token 当成它的值)
- 改宿主代码(
lib/impl.js):powershell -File dev\reload.ps1 会去动 profile 的 patch 行试图
触发热重载;如果监听不响应,宿主半边的改动就只能靠重启 DSH 生效。
只改快捷栏(UI/交互/配色)不受影响,重起那个小进程就够了。