dsh-mini 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -118,17 +118,18 @@ npm install dsh-mini
118
118
 
119
119
  装完不需要额外配置:插件会在 DSH 启动 2.5 秒后自己拉起常驻服务(划词药丸 + 全局热键 + 托盘),小窗由标题栏的「弹出此窗口」按需打开。**前提是机器上已有 WebView2 Runtime**(见上面「系统要求」)。
120
120
 
121
- ### B. 本机 link(改代码时用)
121
+ ### B. 源码 link(改代码时用)
122
122
 
123
- profile 走 `link:` 依赖,和 `tju-plugin` 一样:
123
+ 改代码不必每次 `npm pack`,把 profile 里的依赖指向你本地这份仓库即可:
124
124
 
125
125
  ```jsonc
126
126
  // ~/.dsh/profiles/desktop/package.json
127
- "dependencies": { "dsh-mini": "link:C:/Users/xiaoxin3309/Documents/deepseek-harness/default-workspace/dsh-hud-window" },
127
+ "dependencies": { "dsh-mini": "link:<本仓库在你机器上的绝对路径>" },
128
128
  "dsh": { "profile": { "bundles": [ ..., "dsh-mini" ] } }
129
129
  ```
130
130
 
131
- `node_modules/dsh-mini` 是指向本目录的 junction,改代码不需要重新安装。
131
+ 把 `<本仓库在你机器上的绝对路径>` 换成你 clone 下来的那个目录(例如 `C:/code/dsh-mini`),
132
+ 然后 `npm install` 让 npm 建好 `node_modules/dsh-mini` 这层链接;之后改代码不需要重新安装。
132
133
  宿主半身(`lib/index.js`)的改动需要 `plugin_manager set_bundle` 关一次再开一次(或重启 DSH);
133
134
  浏览器半身(`lib/client.js`)的改动需要**刷新页面**(`F5`)——bundle 的 `rev` 由内容算出,刷新即取新版。
134
135
 
@@ -179,12 +180,17 @@ cd app
179
180
 
180
181
  * 实现在 `app/SelectionWatcher.cs`:全局鼠标轮询(90ms)→ 松开时若拖动距离 ≥6 逻辑像素、
181
182
  且不在小窗/药丸自己身上 → 后台线程等 130ms(双击选词等 320ms)→ 校验仍在前台 → 取选区。
182
- * **取选区走系统辅助功能(UIA `TextPattern`),不碰键盘也不碰剪贴板**:拿到文本的同时还能拿到
183
- 选区最后一行/一个字的矩形,药丸才能精确贴在它右下角。绝大多数现代程序(Notepad、Chrome/Edge、
184
- Word、WPS、VS Code、DSH 自己)都支持。
185
- * 只有用户显式打开设置里的**兼容模式**,UIA 读不到时才补一次读剪贴板:先给焦点控件发 `WM_COPY`
186
- (不合成按键),仍不行才真的发一次 `Ctrl+C`。**任何情况下都不再做「备份 → 复制 → 还原」**——
187
- 之前那套会把你自己的 `Ctrl+C` 覆盖掉,这正是"选中后复制不上"的元凶。
183
+ * **取选区三级阶梯**(`SelectionReader.ReadHit`),从最无侵入的一档开始:
184
+ 1. **UI Automation**(`TextPattern`)—— 不碰键盘、不碰剪贴板,还能拿到选区最后一行/一个字的矩形,
185
+ 药丸因此能精确贴在它右下角。Notepad、Word、WPS、VS Code、WinForms/WPF、DSH 自己都走这一档。
186
+ 2. **`WM_COPY`** —— 发给焦点控件,不发按键。部分老式编辑框只认这条。
187
+ 3. **合成一次 `Ctrl+C`** —— 只有前两档都拿不到时才用。**浏览器正文(Chromium)、部分 Electron 应用、
188
+ 某些游戏/自绘控件根本没有 `TextPattern` 也没有 `WM_COPY`,这是唯一可行的手段**。
189
+ * **绝不做「备份剪贴板 → 复制 → 还原」**。之前那套是"选中后复制不上"的元凶:还原那一步会把你刚按的
190
+ `Ctrl+C` 覆盖回旧内容。现在第三档会**把选区留在剪贴板里**(你在浏览器里划一下就发现 `Ctrl+V` 有内容了),
191
+ 代价是<u>占用</u>剪贴板,而不是<u>破坏</u>它 —— 这是读取 Chromium 选区的唯一代价,取舍写在明处。
192
+ * 设置页的「选区读取:辅助功能读不到时改用复制」**默认打开**;关掉后浏览器正文里就不会再弹按钮,
193
+ 但剪贴板绝对不会被碰(适合只用 Office/记事本、或对剪贴板敏感的场景)。
188
194
  * 保护:按住 Ctrl/Alt/Shift 时的拖动直接跳过(那是复制/扩选,不是重新选词);终端
189
195
  (console/cascadia/mintty/putty)不发按键;资源管理器/桌面/任务栏(Progman/WorkerW/CabinetWClass…)
190
196
  一律跳过(否则会真的复制文件、污染 `Win+V` 历史);用户按着修饰键期间完全不介入。
@@ -192,8 +198,9 @@ cd app
192
198
  `%LOCALAPPDATA%\dsh-hud\ask.txt`。
193
199
  * 因此**标题条 ✕ 只是收进托盘**(不是退出)——watcher 活在这个进程里,退出了就没法划词。
194
200
  彻底退出用托盘菜单「退出」。
195
- * 诊断:`hud.log` 里 `[pill]` 开头的行会写每次 `kick / skip / read N chars`;
196
- 往 `%LOCALAPPDATA%\dsh-hud\seltest.txt` 写任意内容可触发一次「只读不弹」的自检。
201
+ * 诊断:`hud.log` 里 `[pill]` 开头的行会写每次 `kick / skip / read N chars`,以及实际走了哪一档:
202
+ `copy: wm_copy N chars`、`copy: key_chord N chars seq A->B`(`0 chars` 且序号没动,说明那儿没有真选中
203
+ 或目标程序忽略了合成按键);往 `%LOCALAPPDATA%\dsh-hud\seltest.txt` 写任意内容可触发一次「只读不弹」的自检。
197
204
 
198
205
  ## 常驻划词服务、全局快捷键与设置页
199
206
 
@@ -261,9 +268,12 @@ cd ..; npm pack # → dsh-mini-0.1.0.tgz(约 600 KB)
261
268
  * 截图只有「整屏」和「框选/吸附窗口」两种,没有滚动长截图;框选吸附依赖窗口标题非空,
262
269
  无标题窗口(部分全屏游戏、自绘界面)会退化成「该点所在的整块屏幕」。
263
270
  (滚动长截图评估过:要拼接滚动帧 + 去重 + 处理懒加载,工作量明显大于本轮其它几项,先没做。)
264
- * 划词优先走 UI Automation `TextPattern.GetSelection()`(无侵入、能拿到选区矩形),
265
- 只有 UIA 读不到时才回退「发 Ctrl+C 读剪贴板」;终端(console/cascadia/mintty/putty)
266
- 和桌面/资源管理器窗口直接跳过,避免 Ctrl+C 变成 SIGINT 或真的复制文件。
271
+ * 划词按三级阶梯取选区:UIA `TextPattern`(无侵入、带选区矩形)→ `WM_COPY`(发消息、不发按键)→
272
+ 合成一次 `Ctrl+C`。**Chromium 正文没有 `TextPattern`、也不响应 `WM_COPY`**(我实测过 UIA 与
273
+ IAccessible2 两条路都拿不到选区),所以浏览器里必然走到第三档,会占用剪贴板;不想被碰可在设置页关掉
274
+ 「选区读取:辅助功能读不到时改用复制」。终端(console/cascadia/mintty/putty)和桌面/资源管理器窗口
275
+ 直接跳过,避免 Ctrl+C 变成 SIGINT 或真的复制文件。**任何情况都不再还原旧剪贴板内容**——那正是以前
276
+ 「选中后 Ctrl+C 复制不上」的根因。
267
277
  * 主题色与小窗边框:导航完成后同步两次,另外每 6 秒复查一次页面背景色;
268
278
  跨显示器拖到不同 DPI 的屏会收到 `WM_DPICHANGED` 并重算标题条/图标/最小尺寸。
269
279
  * 标题条图标是 `Segoe MDL2 Assets` 字体,字号偏小(E8BB 关闭键取 8.5f 才和其它图标一样大)。
@@ -318,12 +318,17 @@ namespace DshHud
318
318
  internal static class SelectionReader
319
319
  {
320
320
  /// <summary>
321
- /// Off by default. UIA answers for every modern text surface without touching the
322
- /// keyboard or the clipboard; the copy-based path is only needed by legacy controls,
323
- /// and it necessarily competes with the user's own Ctrl+C. Users can enable it from
324
- /// the settings page.
321
+ /// On by default, but only ever consulted after UIA came back empty — so modern text
322
+ /// surfaces (Notepad, WinForms, WPF, Office, the browser's own chrome) never touch the
323
+ /// clipboard. Chromium content and legacy controls expose no TextPattern at all, and for
324
+ /// those a copy is the only way to read the selection.
325
+ ///
326
+ /// Crucially the copy is *not* undone: the old implementation restored the previous
327
+ /// clipboard contents afterwards, and that restore raced the user's own Ctrl+C and
328
+ /// overwrote what they had just copied. Losing the previous clipboard contents is the
329
+ /// price of reading a selection here; breaking Ctrl+C is not.
325
330
  /// </summary>
326
- internal static bool ClipboardFallback;
331
+ internal static bool ClipboardFallback = true;
327
332
 
328
333
  /// <summary>
329
334
  /// Read the current selection. UIA first: it is passive (no keystrokes, no clipboard
@@ -453,14 +458,21 @@ namespace DshHud
453
458
  if (ShellWin.IsModifierDown()) return "";
454
459
 
455
460
  uint seq = ShellWin.GetClipboardSequenceNumber();
456
- bool asked = ShellWin.SendCopyMessage();
457
- if (!asked) return "";
461
+ // WM_COPY is best effort only: Win32 edits answer it, Chromium and most
462
+ // modern renderers ignore it, and SendMessageTimeout also returns false
463
+ // when the target window is busy. Never treat "no answer" as "no selection".
464
+ ShellWin.SendCopyMessage();
458
465
 
459
466
  string text = WaitForClipboardText(seq, 320);
460
- if (!string.IsNullOrEmpty(text)) return Finish(text);
467
+ if (!string.IsNullOrEmpty(text))
468
+ {
469
+ SelectionWatcher.Log("copy: wm_copy " + text.Length + " chars");
470
+ return Finish(text);
471
+ }
461
472
 
462
- // WM_COPY is ignored by a few legacy controls; only then fall back to a real
463
- // key chord, and only when the user has opted in.
473
+ // A real key chord is the only thing that works for Chromium content and
474
+ // legacy controls. Only when the user has opted in, and never while they
475
+ // are holding a modifier themselves.
464
476
  if (!ClipboardFallback || ShellWin.IsModifierDown()) return "";
465
477
  try
466
478
  {
@@ -471,6 +483,8 @@ namespace DshHud
471
483
  return "";
472
484
  }
473
485
  text = WaitForClipboardText(seq, 700);
486
+ SelectionWatcher.Log("copy: key_chord " + (text == null ? 0 : text.Length) +
487
+ " chars seq " + seq + "->" + ShellWin.GetClipboardSequenceNumber());
474
488
  return string.IsNullOrEmpty(text) ? "" : Finish(text);
475
489
  }
476
490
 
package/app/Service.cs CHANGED
@@ -27,7 +27,7 @@ namespace DshHud
27
27
  Ask = true;
28
28
  HotkeyFull = "Ctrl+Shift+1";
29
29
  HotkeyRegion = "Ctrl+Shift+2";
30
- TextFallback = false;
30
+ TextFallback = true;
31
31
  }
32
32
 
33
33
  internal static string Dir
package/lib/client.js CHANGED
@@ -551,8 +551,8 @@ window.__ModuleLoader__.load({
551
551
  react.createElement('h3', { style: { fontSize: '14px', margin: '0 0 4px' } }, '小窗与截图'),
552
552
  react.createElement('p', { style: hintStyle }, '设置对原生小窗和后台划词服务同时生效,保存后约 3 秒内应用。'),
553
553
  checkbox('copyShot', '截图自动复制到剪贴板', '全屏、框选两种截图都会复制'),
554
- checkbox('textFallback', '兼容模式:读不到选区时模拟 Ctrl+C',
555
- '默认关闭。关闭时只走系统辅助功能读选区,完全不碰剪贴板、不发按键;打开后老式控件才读得到,但可能和你自己的 Ctrl+C 抢剪贴板。'),
554
+ checkbox('textFallback', '选区读取:辅助功能读不到时改用复制',
555
+ '默认打开。先走系统辅助功能(不发按键、不碰剪贴板);浏览器正文、老控件没有这个接口,只能复制一次来读选区 —— 会占用剪贴板,但不会再还原,所以不会覆盖你刚按的 Ctrl+C。关掉后这些地方就不出按钮。'),
556
556
  hotkey('hotkeyFull', '全屏截图快捷键'),
557
557
  hotkey('hotkeyRegion', '框选截图快捷键'),
558
558
  react.createElement('p', { style: hintStyle }, '写法:Ctrl+Shift+1、Ctrl+Shift+S、Alt+PrintScreen、F8 等。组合里必须带 Ctrl/Alt/Shift/Win 之一。'),
package/lib/index.js CHANGED
@@ -202,7 +202,7 @@ function advertise(ctx) {
202
202
 
203
203
  const CONFIG_PATH = join(WORK_DIR, 'config.ini')
204
204
 
205
- const CONFIG_DEFAULTS = { copyShot: true, ask: true, textFallback: false, hotkeyFull: 'Ctrl+Shift+1', hotkeyRegion: 'Ctrl+Shift+2' }
205
+ const CONFIG_DEFAULTS = { copyShot: true, ask: true, textFallback: true, hotkeyFull: 'Ctrl+Shift+1', hotkeyRegion: 'Ctrl+Shift+2' }
206
206
 
207
207
  /**
208
208
  * Settings live in a flat `key=value` file because the window shell (C# 5) and this
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-mini",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "DSH 常驻小窗插件:把当前会话一键弹出为独立置顶小窗(WebView2 原生窗口),含整屏/框选截图、划词「问大肥鱼」、全局快捷键与截图自动复制",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",