dsh-vscode-mode 0.15.6 → 0.15.8

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
@@ -465,13 +465,13 @@ profile 请替换为实际目标,不要把示例目录当成当前运行 profi
465
465
 
466
466
  ```bash
467
467
  # ① Git 安装(clone + prepare 构建;推荐打固定 tag)
468
- dsh plugin --profile web add github:Lenonss/DSH_VsCodeMode#v0.15.6
468
+ dsh plugin --profile web add github:Lenonss/DSH_VsCodeMode#v0.15.8
469
469
 
470
470
  # ② npm 注册表(发布到 npm 后)
471
- dsh plugin --profile web add dsh-vscode-mode@0.15.6
471
+ dsh plugin --profile web add dsh-vscode-mode@0.15.7
472
472
 
473
473
  # ③ GitHub Release tgz 直装(Release asset 可用后)
474
- dsh plugin --profile web add https://github.com/Lenonss/DSH_VsCodeMode/releases/download/v0.15.6/dsh-vscode-mode-0.15.6.tgz
474
+ dsh plugin --profile web add https://github.com/Lenonss/DSH_VsCodeMode/releases/download/v0.15.8/dsh-vscode-mode-0.15.8.tgz
475
475
  ```
476
476
 
477
477
  > **DSH 版本政策(0.14.10 起)**:六项 DSH peer 统一声明为 `*`,不再维护逐版
@@ -809,6 +809,8 @@ Keep All / Undo All 却是亮的。修复:单文件 Keep / Undo 覆盖冲突
809
809
  完整变更见 [GitHub Releases](https://github.com/Lenonss/DSH_VsCodeMode/releases)。
810
810
  近期关键版本:
811
811
 
812
+ - **v0.15.8**:**跨端「会话不存在」兼容**——桌面端与 Web 端各自运行独立 DSH 实例、会话列表互通但会话仅在一端激活时,另一端文件编辑器不再卡在「文件加载失败:会话不存在 0%」:中央区显示「会话在当前端不可用」终态提示(说明成因、保留重试),资源管理器红条同样替换为友好文案。host 三处会话前置错误文案收敛为 shared 常量,client 按常量识别归类。详见 [0.15.8 发布说明](docs/release-0.15.8.md)。
813
+ - **v0.15.7**:**Lua 成员链跳转与枚举引用**——规避 EmmyLua 标识符词边界解析问题,从引用结果推导跨文件声明;Lua 表字面量枚举键 Ctrl+点击打开引用 Peek,使用处仍按原生定义跳转。真实 GUI 点击尚未完成验收,用户接受此缺口;发布不自动安装或重启当前实例,详见 [0.15.7 发布说明](docs/release-0.15.7.md)。
812
814
  - **v0.15.6**:**统一 SVN 项目更新入口**——资源管理器顶部 ↓ 与 SVN 变更页全局 ↓ 均打开插件自带 SVN 更新窗口,目标为当前项目根目录;非 SVN、能力未知或 CLI 不可用时不显示虚假的 SVN 读取提示。保留只读远端检查与本地刷新;本次真实 GUI 未验证,用户已接受此发布缺口,不自动安装或重启,详见 [0.15.6 发布说明](docs/release-0.15.6.md)。
813
815
 
814
816
  - **v0.15.5**:**浮窗性能与客户端适配**——拖动/缩放改为逐帧直写几何、结束单次提交,多窗口共享视口监听;SVN 日志行表 memo 与选中集优化,冲突状态读取按批 4 并发。LSP 去抖发送前惰性读取全文,隐藏页面心跳目标周期降为 15 秒;兼容性摘要按版本、安装事实与用户选择显示适用诊断,补充搜索刷新回归。保留 0.15.4 全部功能;本次真实 GUI 未重测,用户已接受此发布缺口,未安装/重启当前实例,详见 [0.15.5 发布说明](docs/release-0.15.5.md)。
package/lib/client.js CHANGED
@@ -2765,6 +2765,13 @@ window.__ModuleLoader__.load({
2765
2765
  /** PDF 统一 MIME(host base64 响应与 client PDF 面板同源,避免两处漂移)。 */
2766
2766
  const PDF_MIME = "application/pdf";
2767
2767
  /**
2768
+ * 会话级前置错误的规范文案:host requireSession 系与 client 错误识别共用同一常量,
2769
+ * 避免文案漂移导致 client 端分类失效。跨端场景(桌面端/Web 端各自运行独立 DSH 实例、
2770
+ * 会话列表互通但会话仅在一端激活)下,本端 host 取不到会话即返回「会话不存在」。
2771
+ * @author ddj 2026年10月16号
2772
+ */
2773
+ const SESSION_MISSING_ERROR = "会话不存在";
2774
+ /**
2768
2775
  * 路径 → 二进制预览 MIME:PDF 特判 {@link PDF_MIME},其余走图片表回退 octet-stream。
2769
2776
  * @author ddj 2026年09月22号
2770
2777
  * @param path 文件路径(`/` 或 `\` 分隔均可)
@@ -13651,7 +13658,7 @@ window.__ModuleLoader__.load({
13651
13658
  * dsh-vscode-mode client — Monaco LSP provider 注册 + 跳转/引用命令。
13652
13659
  * 数据层:Definition/Reference/DocumentSymbol/Hover provider(同一文件内跳转 Monaco 原生可用)。
13653
13660
  * 交互层:edrv.goToDefinition(F12)/ edrv.findReferences(Shift+F12 / 右键)均走 Monaco 原生命令;
13654
- * Ctrl+点击同样交给原生(多结果弹 Peek 让用户选、无定义时按 alternativeDefinitionCommand 降级),
13661
+ * Ctrl+点击使用处交给原生;Lua 表枚举字段声明处改为打开引用 Peek,
13655
13662
  * 数据经 host 的 LSP RPC 提供(registerEditorOpener 接管 edrv:// 点击跳转)。
13656
13663
  * 作者 ddj 2026-08-27 / 2026-09-11
13657
13664
  */
@@ -13915,6 +13922,69 @@ window.__ModuleLoader__.load({
13915
13922
  return false;
13916
13923
  }
13917
13924
  }
13925
+ /**
13926
+ * 判定 Ctrl+点击是否落在 Lua 表的字段声明键上(`TaskStateChange = 50000`)。
13927
+ * 仅拦截表字面量内的键,不改动使用处的原生跳转行为。
13928
+ * @author ddj 2026年10月16号
13929
+ * @param model 当前 Monaco 文档
13930
+ * @param position 鼠标命中位置
13931
+ * @returns 是否为表字段声明键
13932
+ */
13933
+ function isLuaTableKey(model, position) {
13934
+ if (model?.getLanguageId?.() !== "lua" || !position) return false;
13935
+ const line = model.getLineContent(position.lineNumber);
13936
+ const key = model.getWordAtPosition(position);
13937
+ if (!key || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(key.word)) return false;
13938
+ if (!/^\s*=\s*(?!=)/.test(line.slice(key.endColumn - 1))) return false;
13939
+ if (!/^\s*$/.test(line.slice(0, key.startColumn - 1))) return false;
13940
+ for (let lineNumber = position.lineNumber - 1; lineNumber >= Math.max(1, position.lineNumber - 100); lineNumber--) {
13941
+ const previous = model.getLineContent(lineNumber).trim();
13942
+ if (!previous || previous.startsWith("--")) continue;
13943
+ if (/^(?:[A-Za-z_][A-Za-z0-9_]*\s*\.\s*)*[A-Za-z_][A-Za-z0-9_]*\s*=\s*\{\s*(?:--.*)?$/.test(previous)) return true;
13944
+ if (!/^[A-Za-z_][A-Za-z0-9_]*\s*=\s*[^{}]*,?\s*(?:--.*)?$/.test(previous)) return false;
13945
+ }
13946
+ return false;
13947
+ }
13948
+ /**
13949
+ * 仅在 Lua 表字段声明处把 Ctrl+点击交给引用 Peek;其他点击留给 Monaco 原生导航。
13950
+ * 使用 DOM capture 阶段阻止原生 gotoDefinitionAtPosition 同时触发,避免跳转和 Peek 竞态。
13951
+ * @author ddj 2026年10月16号
13952
+ * @param ed Monaco 编辑器
13953
+ * @param monaco Monaco API
13954
+ * @returns 解绑回调
13955
+ */
13956
+ function bindLuaKeyRefs(ed, monaco) {
13957
+ const node = ed?.getDomNode?.();
13958
+ if (!node) return () => {};
13959
+ const keyAt = (event) => {
13960
+ if (event.button !== 0 || !event.ctrlKey && !event.metaKey || event.altKey || event.shiftKey) return null;
13961
+ const target = ed.getTargetAtClientPoint?.(event.clientX, event.clientY);
13962
+ const content = monaco.editor?.MouseTargetType?.CONTENT_TEXT ?? 6;
13963
+ return target?.type === content && isLuaTableKey(ed.getModel(), target.position) ? target.position : null;
13964
+ };
13965
+ let suppressUp = false;
13966
+ const onDown = (event) => {
13967
+ const position = keyAt(event);
13968
+ if (!position) return;
13969
+ suppressUp = true;
13970
+ event.preventDefault();
13971
+ event.stopPropagation();
13972
+ ed.setPosition(position);
13973
+ triggerReferencePeek(ed);
13974
+ };
13975
+ const onUp = (event) => {
13976
+ if (!suppressUp) return;
13977
+ suppressUp = false;
13978
+ event.preventDefault();
13979
+ event.stopPropagation();
13980
+ };
13981
+ node.addEventListener("mousedown", onDown, true);
13982
+ node.addEventListener("mouseup", onUp, true);
13983
+ return () => {
13984
+ node.removeEventListener("mousedown", onDown, true);
13985
+ node.removeEventListener("mouseup", onUp, true);
13986
+ };
13987
+ }
13918
13988
  /** Shift+F12 / 右键「查找所有引用」:走 Monaco 原生 References Peek(两栏引用视图)。 */
13919
13989
  async function runFindReferences(ed) {
13920
13990
  const position = ed.getPosition();
@@ -21983,6 +22053,41 @@ window.__ModuleLoader__.load({
21983
22053
  return uriPath ? decodeURIComponent(String(uriPath).replace(/^\//, "")) : null;
21984
22054
  }
21985
22055
  //#endregion
22056
+ //#region src/client/sessionError.ts
22057
+ /**
22058
+ * dsh-vscode-mode client — 会话缺失错误识别与友好文案。
22059
+ * 跨端场景:桌面端与 Web 端各自运行独立 DSH 实例,会话列表跨实例可见,
22060
+ * 但会话仅在一端激活;另一端发起的 edrv.* RPC 会被 host requireSession
22061
+ * 以「会话不存在」拒绝。此处把该错误归类为终态提示(非反复重试可解),
22062
+ * 并给出引导文案,替代裸错误卡死观感。
22063
+ * 作者 ddj 2026年10月16号
22064
+ */
22065
+ /**
22066
+ * 判断错误文案是否为「会话不存在」类错误。
22067
+ * @author ddj 2026年10月16号
22068
+ * @param text 任意错误文本(host 业务 error、异常 message 等原始形态)
22069
+ * @returns 命中规范文案(含拼接形态,如「会话不存在:路径」)返回 true;空值安全
22070
+ */
22071
+ function isSessionMissingError(text) {
22072
+ return String(text ?? "").includes(SESSION_MISSING_ERROR);
22073
+ }
22074
+ /**
22075
+ * 会话缺失提示标题(编辑区终态面板与侧栏红条共用)。
22076
+ * @author ddj 2026年10月16号
22077
+ * @returns 简短标题文案
22078
+ */
22079
+ function sessionMissingTitle() {
22080
+ return "会话在当前端不可用";
22081
+ }
22082
+ /**
22083
+ * 会话缺失提示详情:说明成因并给出引导(去持有端查看,或在本端打开会话后重试)。
22084
+ * @author ddj 2026年10月16号
22085
+ * @returns 多句详情文案
22086
+ */
22087
+ function sessionMissingDetail() {
22088
+ return "该会话可能正被其他 DSH 实例(桌面端 / Web 端)占用,或尚未在当前实例激活。请在持有该会话的一端查看,或在本端打开该会话后重试。";
22089
+ }
22090
+ //#endregion
21986
22091
  //#region src/client/ui/EditorView.ts
21987
22092
  /**
21988
22093
  * dsh-vscode-mode client — EditorView:中央 VSCode 式文件编辑器(编排层)。
@@ -24394,6 +24499,8 @@ window.__ModuleLoader__.load({
24394
24499
  });
24395
24500
  syncSvnMenuActs(ed);
24396
24501
  bindLspUnderline(ed, m);
24502
+ const unbindLuaKeyRefs = bindLuaKeyRefs(ed, m);
24503
+ ed.onDidDispose(unbindLuaKeyRefs);
24397
24504
  trackAiEditor(ed);
24398
24505
  ed.addCommand(m.KeyMod.Alt | m.KeyCode.Backslash, () => {
24399
24506
  ed.trigger("edrv-ai", "editor.action.inlineSuggest.trigger", null);
@@ -26338,6 +26445,26 @@ window.__ModuleLoader__.load({
26338
26445
  }
26339
26446
  }, "重试"));
26340
26447
  else if (!active) body = react.default.createElement("div", { className: "edrv-empty" }, react.default.createElement("div", null, "暂无打开的文件"), react.default.createElement("div", { style: { fontSize: 12 } }, "使用右上搜索框 (" + (chordOf("edrv.quickOpen") ?? "Ctrl+P") + ") 打开工作区文件;" + (chordOf("edrv.showCommands") ?? "Ctrl+Shift+P") + " 打开命令栏;agent 修改文件后顶部会出现差异角标"));
26448
+ else if (content === null && loadError && isSessionMissingError(loadError)) body = react.default.createElement("div", {
26449
+ className: "edrv-empty edrv-loading",
26450
+ "aria-live": "polite"
26451
+ }, react.default.createElement("div", { className: "edrv-loading-title" }, sessionMissingTitle()), react.default.createElement("div", { style: {
26452
+ fontSize: 12,
26453
+ maxWidth: 420,
26454
+ lineHeight: 1.6
26455
+ } }, sessionMissingDetail()), react.default.createElement("button", {
26456
+ className: "edrv-pill edrv-pill-ghost",
26457
+ onClick: () => {
26458
+ setLoadError(null);
26459
+ setContent(null);
26460
+ setContentPath(null);
26461
+ setLoadStage({
26462
+ progress: 10,
26463
+ message: "重新读取文件…"
26464
+ });
26465
+ loadContent(active, sessionId);
26466
+ }
26467
+ }, "重试"));
26341
26468
  else if (content === null && loadError) body = loadingBody("文件加载失败:" + loadError, 0, () => {
26342
26469
  setLoadError(null);
26343
26470
  setContent(null);
@@ -32715,7 +32842,9 @@ window.__ModuleLoader__.load({
32715
32842
  }
32716
32843
  const updateHint = !ctx?.svn?.svnCli ? "SVN CLI 不可用" : !ctx?.openSvnUpdate ? "SVN 更新窗口不可用" : "更新项目(打开 SVN 更新窗口)";
32717
32844
  const errorList = Object.entries(errors);
32718
- const errorText = errorList.length ? errorList[0][1] : null;
32845
+ const rawErrorText = errorList.length ? errorList[0][1] : null;
32846
+ const sessionMissing = isSessionMissingError(rawErrorText);
32847
+ const errorText = rawErrorText === null ? null : sessionMissing ? sessionMissingTitle() + ":" + sessionMissingDetail() : rawErrorText;
32719
32848
  return react.default.createElement("div", {
32720
32849
  className: "edrv-side-panel",
32721
32850
  onContextMenu: onPanelContext