dsh-artifact-linker 0.4.4 → 0.4.6
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/CHANGELOG.md +30 -0
- package/lib/client.js +78 -1
- package/lib/index.js +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.6 — 2026-11-07
|
|
4
|
+
|
|
5
|
+
### 修复:常驻注入第 2 条铁律要求「正文只写文件名」,导致正文产物路径被省略
|
|
6
|
+
|
|
7
|
+
**现象**:用户反馈「还是有正文不输出完整绝对路径的情况」。交付清单里多个产物共用同一长前缀时,正文被写成「三个点 + 一段路径」的省略形式——既不是完整绝对路径、也不是纯文件名,无法直接复制。
|
|
8
|
+
|
|
9
|
+
**根因**:`RULES_TEXT` 第 2 条原文「正文叙述给用户看时只写文件名,不要贴绝对路径……正文出现长串绝对路径属于违规」,与「正文要看到完整绝对路径」的需求相反;模型为兼顾该约束,把共用前缀省略成三点形式。另第 4 条「present 交付的产物正文不重复罗列路径」也会让正文看不到路径。**这与注入频率无关**——`ctx.systemPrompt.section` 注册一次即每轮自动生效(每个模型步之前组装),无需逐段注入。
|
|
10
|
+
|
|
11
|
+
**修复**:
|
|
12
|
+
- 第 2 条改为「正文/清单/表格列产物时,每个文件都必须给完整绝对路径;严禁用省略号(三个点 / …)或共用前缀省略任何一段路径——每行都要能直接复制粘贴使用」,并注明文件名仍会被渲染成可点芯片、写完整路径不影响点击预览。
|
|
13
|
+
- 第 4 条改为「用 present 交付的产物,正文清单里仍要列出其完整绝对路径(卡片供预览,正文路径供复制)」。
|
|
14
|
+
|
|
15
|
+
**验证**:`node --check lib/index.js` 通过、无 BOM;重启后实测交付清单每行为完整绝对路径。
|
|
16
|
+
|
|
17
|
+
**生效方式**:改的是宿主端 `lib/index.js` → **彻底退出 DSH 客户端再启动**(Ctrl+R / Ctrl+Shift+R 均无效,宿主是 Node 进程无热重载)。
|
|
18
|
+
|
|
19
|
+
## 0.4.5 — 2026-11-07
|
|
20
|
+
|
|
21
|
+
### 修复:宿主渲染的「文件芯片 / 交付卡片」右键只出官方菜单
|
|
22
|
+
|
|
23
|
+
**现象**:聊天里由宿主渲染的文件芯片(markdown 文件链接、present 交付卡片,例如表格里的蓝色 `追光.mp3` 芯片),右键只弹出宿主官方菜单(在侧边栏打开 / 在浮窗中打开 / 在文件夹显示 / 用 XX 打开 / 复制文件路径 / 另存为…),插件的 8 项菜单不出现;而正文里的纯文本绝对路径(如 `…\lib\client.js`)右键正常。
|
|
24
|
+
|
|
25
|
+
**根因**:宿主把文件渲染成 `<button class="…fileMention …fileLink" title="<路径>">`(markdown 文件链接)与 `<div data-presented-file><button class="cardPreview" title="<绝对路径>">`(present 卡片),右键由宿主 `ctx.contextMenus` 的官方 `open-in-app.file` provider 接管;这些节点正文里没有「含路径的纯文本节点」,`hitAt` 只能看到裸文件名、`/resolve` 失败 → 放行 → 只出官方菜单。
|
|
26
|
+
|
|
27
|
+
**修复**:在 `contextmenu` 捕获期先识别宿主文件芯片/交付卡片,从其 `title` 读取真实路径(相对路径按当前会话 cwd 补成绝对路径),随即 `preventDefault()+stopPropagation()` 拦下官方菜单并弹出插件自己的完整 8 项菜单;纯文本路径仍走原 `caretRangeFromPoint` 逻辑。同时 `click` 对芯片直接让路给宿主,避免左键双开。
|
|
28
|
+
|
|
29
|
+
**验证**:`node --check lib/client.js` 通过、无 BOM;DOM-stub 探针 `probe_chip_menu.cjs` 8/8(绝对路径芯片拦截、相对路径按 cwd 补全、普通文本放行)。
|
|
30
|
+
|
|
31
|
+
**生效方式**:本次只改客户端 bundle `lib/client.js` → **Ctrl+Shift+R 硬刷新**即可(Ctrl+R 不重新读盘)。
|
|
32
|
+
|
|
3
33
|
## 0.4.4 — 2026-11-05
|
|
4
34
|
|
|
5
35
|
### 对齐官方交付卡片:左键改为侧栏预览,右键 8 项菜单
|
package/lib/client.js
CHANGED
|
@@ -29,7 +29,7 @@ window.__ModuleLoader__.load({
|
|
|
29
29
|
var exports = module.exports;
|
|
30
30
|
|
|
31
31
|
var GUARD = "__dshArtifactLinkerV1";
|
|
32
|
-
var VERSION = "0.4.
|
|
32
|
+
var VERSION = "0.4.5";
|
|
33
33
|
// 路由前缀必须与宿主的 ROUTE_PREFIX 一致;永远根相对,绝不拼宿主 origin
|
|
34
34
|
var ROUTE = "/dsh-artifact-linker";
|
|
35
35
|
|
|
@@ -290,6 +290,66 @@ window.__ModuleLoader__.load({
|
|
|
290
290
|
return null;
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
+
// ── 宿主「文件芯片 / 交付卡片」路径读取(2026-11-07 修复)──────────────────
|
|
294
|
+
// 背景:宿主把 markdown 文件链接渲染成
|
|
295
|
+
// <button class="…fileMention …fileLink" title="<路径>">,
|
|
296
|
+
// 把 present 交付卡片渲染成
|
|
297
|
+
// <div data-presented-file><button class="cardPreview" title="<绝对路径>">。
|
|
298
|
+
// 这两类节点的右键由宿主 contextMenus 的官方 provider 接管,正文里**没有**能
|
|
299
|
+
// 被 caretRangeFromPoint 命中的「含路径文本节点」→ 老版 hitAt 只能看到裸文件名、
|
|
300
|
+
// 解析失败 → 放行 → 用户只看到官方默认菜单(本次病根)。这里直接从 title 取真路径。
|
|
301
|
+
var FILE_LINK_SELECTOR = 'button[class*="fileMention"],button[class*="fileLink"]';
|
|
302
|
+
function chipPathAt(target) {
|
|
303
|
+
try {
|
|
304
|
+
var el = null;
|
|
305
|
+
if (target && target.closest) {
|
|
306
|
+
el = target.closest(FILE_LINK_SELECTOR);
|
|
307
|
+
if (!el) {
|
|
308
|
+
var card = target.closest("[data-presented-file]");
|
|
309
|
+
if (card && card.querySelector) el = card.querySelector("button[title]");
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if (!el) return null;
|
|
313
|
+
var t = el.getAttribute && el.getAttribute("title");
|
|
314
|
+
if (!t) return null;
|
|
315
|
+
t = String(t).trim();
|
|
316
|
+
if (!t || t.length > 1024 || /[\r\n]/.test(t)) return null;
|
|
317
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(t)) return null; // 排除 http(s):// 等 URL 协议头
|
|
318
|
+
if (looksAbsoluteClient(t)) return t;
|
|
319
|
+
if (/\.[A-Za-z0-9]{1,12}$/.test(t)) return t; // 相对路径,须带扩展名
|
|
320
|
+
return null;
|
|
321
|
+
} catch (e) { return null; }
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// 当前会话工作区根目录(把芯片上的相对路径补成绝对路径;拿不到就返回空串)
|
|
325
|
+
function currentCwd() {
|
|
326
|
+
try {
|
|
327
|
+
if (!rootCtx || typeof rootCtx.get !== "function") return "";
|
|
328
|
+
var sessions = rootCtx.get("sessions");
|
|
329
|
+
if (!sessions || !sessions.list || typeof sessions.list.getSnapshot !== "function") return "";
|
|
330
|
+
var snap = sessions.list.getSnapshot();
|
|
331
|
+
var byId = (snap && snap.byId) || {};
|
|
332
|
+
var sid = null;
|
|
333
|
+
var sidebar = rootCtx.get("sidebarRight");
|
|
334
|
+
var mounted = sidebar && sidebar.mounted;
|
|
335
|
+
if (mounted && typeof mounted.get === "function") {
|
|
336
|
+
var m = mounted.get();
|
|
337
|
+
if (m && m.sessionId) sid = m.sessionId;
|
|
338
|
+
} else if (mounted && mounted.sessionId) {
|
|
339
|
+
sid = mounted.sessionId;
|
|
340
|
+
}
|
|
341
|
+
if (!sid) { for (var k in byId) { if (byId[k] && byId[k].cwd) { sid = k; break; } } }
|
|
342
|
+
return sid && byId[sid] ? String(byId[sid].cwd || "") : "";
|
|
343
|
+
} catch (e) { return ""; }
|
|
344
|
+
}
|
|
345
|
+
function joinCwd(p) {
|
|
346
|
+
if (looksAbsoluteClient(p)) return p;
|
|
347
|
+
var cwd = currentCwd();
|
|
348
|
+
if (!cwd) return p;
|
|
349
|
+
var sep = cwd.indexOf("\\") >= 0 ? "\\" : "/";
|
|
350
|
+
return cwd.replace(/[/\\]+$/, "") + sep + String(p).replace(/^(?:\.?[/\\])+/, "");
|
|
351
|
+
}
|
|
352
|
+
|
|
293
353
|
// ── 提示与菜单 ──────────────────────────────────────────────────────────
|
|
294
354
|
function toast(msg) {
|
|
295
355
|
if (!document || !document.body) return;
|
|
@@ -704,6 +764,8 @@ window.__ModuleLoader__.load({
|
|
|
704
764
|
if (disposed) return;
|
|
705
765
|
if (ev.button !== 0) return;
|
|
706
766
|
if (menuEl && menuEl.contains && menuEl.contains(ev.target)) return;
|
|
767
|
+
// 宿主文件芯片 / 交付卡片:左键交给宿主自己的 openFile / onPreview,避免双开
|
|
768
|
+
if (chipPathAt(ev.target) !== null) return;
|
|
707
769
|
var hit = hitAt(ev.clientX, ev.clientY);
|
|
708
770
|
if (!hit) return;
|
|
709
771
|
// 官方 present 交付卡片本身已内置「点卡片 → 侧边栏预览」,交给官方自己开,避免双开
|
|
@@ -772,6 +834,21 @@ window.__ModuleLoader__.load({
|
|
|
772
834
|
ev.stopPropagation();
|
|
773
835
|
return;
|
|
774
836
|
}
|
|
837
|
+
// ① 宿主渲染的文件芯片 / 交付卡片:直接从 title 取真实路径,弹自己的完整菜单,
|
|
838
|
+
// 并在捕获期拦下事件、阻止宿主 contextMenus 的官方菜单(这是 2026-11-07 的修复点)。
|
|
839
|
+
var chipRaw = chipPathAt(ev.target);
|
|
840
|
+
if (chipRaw !== null) {
|
|
841
|
+
var chipAbs = joinCwd(chipRaw);
|
|
842
|
+
if (looksAbsoluteClient(chipAbs)) {
|
|
843
|
+
ev.preventDefault();
|
|
844
|
+
ev.stopPropagation();
|
|
845
|
+
showMenu(ev.clientX, ev.clientY, chipAbs);
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
// 相对路径且拿不到会话 cwd → 补不成绝对路径,不抢,交给宿主官方菜单兜底
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
// ② 正文里的纯文本路径:沿用原有 caretRangeFromPoint 命中逻辑
|
|
775
852
|
var hit = hitAt(ev.clientX, ev.clientY);
|
|
776
853
|
if (!hit) return; // 非产物处右键 → 放行宿主原生菜单
|
|
777
854
|
var c = cached(hit.raw);
|
package/lib/index.js
CHANGED
|
@@ -51,9 +51,9 @@ var RULES_SECTION_ORDER = 200;
|
|
|
51
51
|
var RULES_TEXT = [
|
|
52
52
|
"【产物路径铁律 · 由 dsh-artifact-linker 插件常驻注入,无需加载任何技能】",
|
|
53
53
|
"1. 凡本轮**新建/写入/下载/生成/导出**的文件与目录,工具参数与内部记录一律使用**完整绝对路径**(Windows 形如 E:\\proj\\out\\a.xlsx)。",
|
|
54
|
-
"2.
|
|
54
|
+
"2. **正文、清单、表格里向用户列产物时,每个文件都必须给出完整绝对路径**(如 `D:\\proj\\out\\a.xlsx`);**严禁用 `...\\`、`…/`、省略号或“共用前缀”省略任何一段路径**——每行都要能直接复制粘贴使用。文件名照旧会被渲染成可点芯片,写完整路径不影响点击预览。",
|
|
55
55
|
"3. 不重复罗列非产物:读入的输入文件、依赖包、缓存、临时文件、系统路径一律不报。",
|
|
56
|
-
"4.
|
|
56
|
+
"4. 用 `present` 交付的产物,正文清单里**仍要列出其完整绝对路径**(卡片供预览,正文路径供复制)。",
|
|
57
57
|
"5. 🔴 无法确定绝对路径时,先实际核实(`pwd`、列目录、读工具返回值)再写,**绝不编造路径**——编造的路径点开就是死链,比不给路径更糟。",
|
|
58
58
|
"6. 路径含空格或中文时,用反引号包裹成行内代码(如 `E:\\工作 目录\\结果.xlsx`),避免被正文断词截断。"
|
|
59
59
|
].join("\n");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-artifact-linker",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.6",
|
|
4
4
|
"description": "DSH 产物路径联动器:常驻注入「产物路径铁律」让模型输出完整绝对路径,同时把聊天正文里的文件名/路径渲染成一键可点的产物芯片——左键在右侧边栏预览(对齐官方交付卡片,绝对路径 0 次解析直通,侧栏认领不了自动退回系统默认程序),Ctrl/Shift/Cmd+左键用系统默认程序打开,Alt+左键复制绝对路径,右键 8 项菜单(在侧栏打开 / 在浮窗中打开 / 在文件夹中显示 / 用默认程序打开 / 另存为… / 复制文件 / 复制文件路径 / 添加到对话框)。宿主端提供回环只读路由与 /saveas /copyfile 两个文件搬运动作(Shell COM 揭示、WinForms 保存对话框、CF_HDROP 剪贴板),客户端零侵入、不改 React 节点、不接管 markdown 图片语法。解决「产物要打开得先复制路径再粘到文件管理器」的反复割裂。/ Turn declared artifact paths into one-click chips: resident system-prompt rules force absolute paths, the client half renders file names as clickable chips, left click previews in DSH's built-in sidebar (absolute paths resolve in 0 round-trips; unclaimed types fall back to the default app), Ctrl/Shift/Cmd+click opens with the system default app, Alt+click copies the absolute path, and the right-click menu offers eight actions (open in sidebar / open in floating window / reveal in Explorer / open with default app / save-as / copy-file / copy-path / insert-into-composer). Host routes are loopback-only; zero telemetry.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|