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 +25 -15
- package/app/SelectionWatcher.cs +24 -10
- package/app/Service.cs +1 -1
- package/app/bin/DSH/345/260/217/347/252/227.exe +0 -0
- package/lib/client.js +2 -2
- package/lib/index.js +1 -1
- package/package.json +1 -1
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.
|
|
121
|
+
### B. 源码 link(改代码时用)
|
|
122
122
|
|
|
123
|
-
|
|
123
|
+
改代码不必每次 `npm pack`,把 profile 里的依赖指向你本地这份仓库即可:
|
|
124
124
|
|
|
125
125
|
```jsonc
|
|
126
126
|
// ~/.dsh/profiles/desktop/package.json
|
|
127
|
-
"dependencies": { "dsh-mini": "link
|
|
127
|
+
"dependencies": { "dsh-mini": "link:<本仓库在你机器上的绝对路径>" },
|
|
128
128
|
"dsh": { "profile": { "bundles": [ ..., "dsh-mini" ] } }
|
|
129
129
|
```
|
|
130
130
|
|
|
131
|
-
`
|
|
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
|
-
*
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
265
|
-
|
|
266
|
-
|
|
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 才和其它图标一样大)。
|
package/app/SelectionWatcher.cs
CHANGED
|
@@ -318,12 +318,17 @@ namespace DshHud
|
|
|
318
318
|
internal static class SelectionReader
|
|
319
319
|
{
|
|
320
320
|
/// <summary>
|
|
321
|
-
///
|
|
322
|
-
///
|
|
323
|
-
///
|
|
324
|
-
/// the
|
|
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
|
-
|
|
457
|
-
|
|
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))
|
|
467
|
+
if (!string.IsNullOrEmpty(text))
|
|
468
|
+
{
|
|
469
|
+
SelectionWatcher.Log("copy: wm_copy " + text.Length + " chars");
|
|
470
|
+
return Finish(text);
|
|
471
|
+
}
|
|
461
472
|
|
|
462
|
-
//
|
|
463
|
-
//
|
|
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
|
Binary file
|
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', '
|
|
555
|
-
'
|
|
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:
|
|
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
|