dsh-vscode-mode 0.1.1

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.
Files changed (163) hide show
  1. package/README.md +95 -0
  2. package/assets/compare_idle.png +0 -0
  3. package/assets/compare_select.png +0 -0
  4. package/assets/vendor/monaco/vs/abap-D-t0cyap.js +1 -0
  5. package/assets/vendor/monaco/vs/apex-CcIm7xu6.js +1 -0
  6. package/assets/vendor/monaco/vs/assets/css.worker-URu8fCFR.js +97 -0
  7. package/assets/vendor/monaco/vs/assets/editor.worker-lj3bdIIn.js +30 -0
  8. package/assets/vendor/monaco/vs/assets/editorWebWorkerMain-CA_vMoUU.js +30 -0
  9. package/assets/vendor/monaco/vs/assets/html.worker-D1SL3iM8.js +474 -0
  10. package/assets/vendor/monaco/vs/assets/json.worker-CoJx_OPf.js +62 -0
  11. package/assets/vendor/monaco/vs/assets/ts.worker-BWKtMYOk.js +67722 -0
  12. package/assets/vendor/monaco/vs/azcli-BA0tQDCg.js +1 -0
  13. package/assets/vendor/monaco/vs/basic-languages/monaco.contribution.js +1 -0
  14. package/assets/vendor/monaco/vs/bat-C397hTD6.js +1 -0
  15. package/assets/vendor/monaco/vs/bicep-DF5aW17k.js +2 -0
  16. package/assets/vendor/monaco/vs/cameligo-plsz8qhj.js +1 -0
  17. package/assets/vendor/monaco/vs/clojure-Y2auQMzK.js +1 -0
  18. package/assets/vendor/monaco/vs/coffee-Bu45yuWE.js +1 -0
  19. package/assets/vendor/monaco/vs/cpp-CkKPQIni.js +1 -0
  20. package/assets/vendor/monaco/vs/csharp-CX28MZyh.js +1 -0
  21. package/assets/vendor/monaco/vs/csp-D8uWnyxW.js +1 -0
  22. package/assets/vendor/monaco/vs/css-CaeNmE3S.js +3 -0
  23. package/assets/vendor/monaco/vs/css.worker-CyhWkhHo.js +1 -0
  24. package/assets/vendor/monaco/vs/cssMode-CV6Ay48H.js +1 -0
  25. package/assets/vendor/monaco/vs/cypher-DVThT8BS.js +1 -0
  26. package/assets/vendor/monaco/vs/dart-CmGfCvrO.js +1 -0
  27. package/assets/vendor/monaco/vs/dockerfile-CZqqYdch.js +1 -0
  28. package/assets/vendor/monaco/vs/ecl-30fUercY.js +1 -0
  29. package/assets/vendor/monaco/vs/editor/editor.main.css +1 -0
  30. package/assets/vendor/monaco/vs/editor/editor.main.js +1 -0
  31. package/assets/vendor/monaco/vs/editor/editor.worker.js +1 -0
  32. package/assets/vendor/monaco/vs/editor-KLE6jdfb.js +831 -0
  33. package/assets/vendor/monaco/vs/editor.js +1 -0
  34. package/assets/vendor/monaco/vs/editorWorkerHost-fVE1cjcC.js +35 -0
  35. package/assets/vendor/monaco/vs/elixir-xjPaIfzF.js +1 -0
  36. package/assets/vendor/monaco/vs/flow9-DqtmStfK.js +1 -0
  37. package/assets/vendor/monaco/vs/freemarker2-FWreY7v7.js +3 -0
  38. package/assets/vendor/monaco/vs/fsharp-BOMdg4U1.js +1 -0
  39. package/assets/vendor/monaco/vs/go-D_hbi-Jt.js +1 -0
  40. package/assets/vendor/monaco/vs/graphql-CKUU4kLG.js +1 -0
  41. package/assets/vendor/monaco/vs/handlebars-CZi4pHCH.js +1 -0
  42. package/assets/vendor/monaco/vs/hcl-DTaboeZW.js +1 -0
  43. package/assets/vendor/monaco/vs/html-j_7ZTNRU.js +1 -0
  44. package/assets/vendor/monaco/vs/html.worker-CA3iAimZ.js +1 -0
  45. package/assets/vendor/monaco/vs/htmlMode-27by_KM4.js +1 -0
  46. package/assets/vendor/monaco/vs/index-049xrXrp.js +1 -0
  47. package/assets/vendor/monaco/vs/index-CBVt3dzv.js +5 -0
  48. package/assets/vendor/monaco/vs/index.js +1 -0
  49. package/assets/vendor/monaco/vs/ini-CsNwO04R.js +1 -0
  50. package/assets/vendor/monaco/vs/initialize-DL0l1TGY.js +1 -0
  51. package/assets/vendor/monaco/vs/java-CI4ZMsH9.js +1 -0
  52. package/assets/vendor/monaco/vs/javascript-DTJl4Jn1.js +1 -0
  53. package/assets/vendor/monaco/vs/json.worker-BizpAl9O.js +1 -0
  54. package/assets/vendor/monaco/vs/jsonMode-DLUiMUrI.js +1 -0
  55. package/assets/vendor/monaco/vs/julia-BwzEvaQw.js +1 -0
  56. package/assets/vendor/monaco/vs/kotlin-IUYPiTV8.js +1 -0
  57. package/assets/vendor/monaco/vs/language/css/css.worker.js +66 -0
  58. package/assets/vendor/monaco/vs/language/css/monaco.contribution.js +1 -0
  59. package/assets/vendor/monaco/vs/language/html/html.worker.js +443 -0
  60. package/assets/vendor/monaco/vs/language/html/monaco.contribution.js +1 -0
  61. package/assets/vendor/monaco/vs/language/json/json.worker.js +17 -0
  62. package/assets/vendor/monaco/vs/language/json/monaco.contribution.js +1 -0
  63. package/assets/vendor/monaco/vs/language/typescript/monaco.contribution.js +1 -0
  64. package/assets/vendor/monaco/vs/language/typescript/ts.worker.js +67693 -0
  65. package/assets/vendor/monaco/vs/less-C0eDYdqa.js +2 -0
  66. package/assets/vendor/monaco/vs/lexon-iON-Kj97.js +1 -0
  67. package/assets/vendor/monaco/vs/liquid-CLJYelW6.js +1 -0
  68. package/assets/vendor/monaco/vs/loader.js +1368 -0
  69. package/assets/vendor/monaco/vs/lspLanguageFeatures-BIkJOWLw.js +2 -0
  70. package/assets/vendor/monaco/vs/lua-DtygF91M.js +1 -0
  71. package/assets/vendor/monaco/vs/m3-CsR4AuFi.js +1 -0
  72. package/assets/vendor/monaco/vs/main-BEx-Fmlo.js +15 -0
  73. package/assets/vendor/monaco/vs/main-DsK8pnKg.js +3 -0
  74. package/assets/vendor/monaco/vs/markdown-C_rD0bIw.js +1 -0
  75. package/assets/vendor/monaco/vs/mdx-CpzZIPHF.js +1 -0
  76. package/assets/vendor/monaco/vs/mips-CiYP61RB.js +1 -0
  77. package/assets/vendor/monaco/vs/monaco.contribution-9cKT3C7t.js +1 -0
  78. package/assets/vendor/monaco/vs/monaco.contribution-BE88ZNGY.js +1 -0
  79. package/assets/vendor/monaco/vs/monaco.contribution-BPhsneLd.js +1 -0
  80. package/assets/vendor/monaco/vs/monaco.contribution-BgRy6xDf.js +1 -0
  81. package/assets/vendor/monaco/vs/msdax-C38-sJlp.js +1 -0
  82. package/assets/vendor/monaco/vs/mysql-CdtbpvbG.js +1 -0
  83. package/assets/vendor/monaco/vs/nls/lang/zh-cn.d.ts +1 -0
  84. package/assets/vendor/monaco/vs/nls/lang/zh-cn.js +9 -0
  85. package/assets/vendor/monaco/vs/nls.messages-loader.js +1 -0
  86. package/assets/vendor/monaco/vs/objective-c-CntZFaHX.js +1 -0
  87. package/assets/vendor/monaco/vs/pascal-r6kuqfl_.js +1 -0
  88. package/assets/vendor/monaco/vs/pascaligo-BiXoTmXh.js +1 -0
  89. package/assets/vendor/monaco/vs/perl-DABw_TcH.js +1 -0
  90. package/assets/vendor/monaco/vs/pgsql-me_jFXeX.js +1 -0
  91. package/assets/vendor/monaco/vs/php-D_kh-9LK.js +1 -0
  92. package/assets/vendor/monaco/vs/pla-VfZjczW0.js +1 -0
  93. package/assets/vendor/monaco/vs/postiats-BBSzz8Pk.js +1 -0
  94. package/assets/vendor/monaco/vs/powerquery-Dt-g_2cc.js +1 -0
  95. package/assets/vendor/monaco/vs/powershell-B-7ap1zc.js +1 -0
  96. package/assets/vendor/monaco/vs/protobuf-BmtuEB1A.js +2 -0
  97. package/assets/vendor/monaco/vs/pug-BRpRNeEb.js +1 -0
  98. package/assets/vendor/monaco/vs/python-CqWUUgfu.js +1 -0
  99. package/assets/vendor/monaco/vs/qsharp-BzsFaUU9.js +1 -0
  100. package/assets/vendor/monaco/vs/r-f8dDdrp4.js +1 -0
  101. package/assets/vendor/monaco/vs/razor-hok5y9Km.js +1 -0
  102. package/assets/vendor/monaco/vs/redis-fvZQY4PI.js +1 -0
  103. package/assets/vendor/monaco/vs/redshift-45Et0LQi.js +1 -0
  104. package/assets/vendor/monaco/vs/restructuredtext-C7UUFKFD.js +1 -0
  105. package/assets/vendor/monaco/vs/ruby-CZO8zYTz.js +1 -0
  106. package/assets/vendor/monaco/vs/rust-Bfetafyc.js +1 -0
  107. package/assets/vendor/monaco/vs/sb-3GYllVck.js +1 -0
  108. package/assets/vendor/monaco/vs/scala-foMgrKo1.js +1 -0
  109. package/assets/vendor/monaco/vs/scheme-CHdMtr7p.js +1 -0
  110. package/assets/vendor/monaco/vs/scss-C1cmLt9V.js +3 -0
  111. package/assets/vendor/monaco/vs/shell-ClXCKCEW.js +1 -0
  112. package/assets/vendor/monaco/vs/solidity-MZ6ExpPy.js +1 -0
  113. package/assets/vendor/monaco/vs/sophia-DWkuSsPQ.js +1 -0
  114. package/assets/vendor/monaco/vs/sparql-AUGFYSyk.js +1 -0
  115. package/assets/vendor/monaco/vs/sql-32GpJSV2.js +1 -0
  116. package/assets/vendor/monaco/vs/st-CuDFIVZ_.js +1 -0
  117. package/assets/vendor/monaco/vs/swift-t-PfMj7W.js +1 -0
  118. package/assets/vendor/monaco/vs/systemverilog-Ch4vA8Yt.js +1 -0
  119. package/assets/vendor/monaco/vs/tcl-D74tq1nH.js +1 -0
  120. package/assets/vendor/monaco/vs/toggleHighContrast-qGX7E9o7.js +61 -0
  121. package/assets/vendor/monaco/vs/ts.worker-2QLmBukE.js +1 -0
  122. package/assets/vendor/monaco/vs/tsMode-B0S_kp2q.js +11 -0
  123. package/assets/vendor/monaco/vs/twig-C6taOxMV.js +1 -0
  124. package/assets/vendor/monaco/vs/typescript-_2t_511t.js +1 -0
  125. package/assets/vendor/monaco/vs/typespec-8OfoLt6R.js +1 -0
  126. package/assets/vendor/monaco/vs/vb-Dyb2648j.js +1 -0
  127. package/assets/vendor/monaco/vs/wgsl-BhLXMOR0.js +298 -0
  128. package/assets/vendor/monaco/vs/workers-BBttULjf.js +1 -0
  129. package/assets/vendor/monaco/vs/xml-cZjNDqDT.js +1 -0
  130. package/assets/vendor/monaco/vs/yaml-A1fOIdH6.js +1 -0
  131. package/cordis.patch.yml +5 -0
  132. package/lib/client.js +1914 -0
  133. package/lib/client.js.map +1 -0
  134. package/lib/index.js +1298 -0
  135. package/lib/index.js.map +1 -0
  136. package/package.json +60 -0
  137. package/src/capture.ts +73 -0
  138. package/src/client/events.ts +36 -0
  139. package/src/client/index.ts +37 -0
  140. package/src/client/monaco/diffRender.ts +162 -0
  141. package/src/client/monaco/loader.ts +71 -0
  142. package/src/client/rpc.ts +28 -0
  143. package/src/client/state/records.ts +90 -0
  144. package/src/client/state/regions.ts +81 -0
  145. package/src/client/styles/css.d.ts +2 -0
  146. package/src/client/styles/editor.css +87 -0
  147. package/src/client/ui/DiffBadge.ts +51 -0
  148. package/src/client/ui/DiffBarEmpty.ts +37 -0
  149. package/src/client/ui/DiffBox.ts +79 -0
  150. package/src/client/ui/DiffLauncher.ts +124 -0
  151. package/src/client/ui/EditorView.ts +577 -0
  152. package/src/client/ui/QuickOpen.ts +91 -0
  153. package/src/client/ui/shared.ts +28 -0
  154. package/src/index.ts +43 -0
  155. package/src/model.ts +197 -0
  156. package/src/registry.ts +35 -0
  157. package/src/revert.ts +107 -0
  158. package/src/routes.ts +138 -0
  159. package/src/rpc.ts +295 -0
  160. package/src/shared/rpc.ts +67 -0
  161. package/src/shared/types.ts +121 -0
  162. package/src/store.ts +244 -0
  163. package/src/workspace.ts +74 -0
package/lib/client.js ADDED
@@ -0,0 +1,1914 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "dsh-vscode-mode",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ //#region \0rolldown/runtime.js
8
+ var __create = Object.create;
9
+ var __defProp = Object.defineProperty;
10
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
11
+ var __getOwnPropNames = Object.getOwnPropertyNames;
12
+ var __getProtoOf = Object.getPrototypeOf;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __copyProps = (to, from, except, desc) => {
15
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
16
+ key = keys[i];
17
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
18
+ get: ((k) => from[k]).bind(null, key),
19
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
20
+ });
21
+ }
22
+ return to;
23
+ };
24
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
25
+ value: mod,
26
+ enumerable: true
27
+ }) : target, mod));
28
+ //#endregion
29
+ let react = require("react");
30
+ react = __toESM(react, 1);
31
+ //#region \0dsh-css:D:\Work\ToolsDev\DeepSeekHarnessPlugin\packages\dsh-edit-review\src\client\styles\editor.css.mjs
32
+ const css = "/* dsh-vscode-mode 编辑区样式(原 ensureEdrvStyles 字符串,落盘为真 .css,由 tsdown CSS-inline 注入) */\n/* 浅色主题(对齐参考图:#f8f8f8 近白底 + 薄荷绿药丸 + 青绿文字 + 三文鱼 undo + 浅灰边框):\n 在 [data-edrv-view] 作用域内重定义 --dsw-alias-*,级联命中组件内联样式与 CSS。 */\n[data-edrv-view] { --edrv-tab-h: 34px; --dsw-alias-bg-base: #f8f8f8; --dsw-alias-bg-layer-1: #ffffff; --dsw-alias-bg-layer-2: #f0f4f4; --dsw-alias-bg-overlay: #ffffff; --dsw-alias-label-primary: #1f2933; --dsw-alias-label-secondary: #52606d; --dsw-alias-label-tertiary: #9aa5b1; --dsw-alias-border-l1: #e0e6e8; --dsw-alias-border-l2: #cbd2d9; --dsw-alias-brand-primary: #0f9d58; --dsw-alias-state-success-primary: #0f9d58; --dsw-alias-state-error-primary: #d9534f; --dsw-alias-state-warn-primary: #d97706; --dsw-alias-interactive-bg-hover: rgba(15,157,88,.08); }\n\n[data-edrv-view] .edrv-tabs { display: flex; align-items: center; gap: 2px; padding: 2px 8px 0; background: var(--dsw-alias-bg-layer-1, transparent); overflow-x: auto; scrollbar-width: thin; flex-shrink: 0; }\n[data-edrv-view] .edrv-tab { display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 8px 0 12px; border: none; border-radius: 6px 6px 0 0; background: transparent; color: var(--dsw-alias-label-secondary, #aaa); font-size: 12px; cursor: pointer; white-space: nowrap; flex-shrink: 0; }\n[data-edrv-view] .edrv-tab:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.06)); }\n[data-edrv-view] .edrv-tab.edrv-tab-active { background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); box-shadow: inset 0 -2px 0 var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-tab .edrv-tab-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--dsw-alias-brand-primary, #4f8cff); flex-shrink: 0; }\n[data-edrv-view] .edrv-tab .edrv-tab-x { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; border-radius: 4px; color: var(--dsw-alias-label-tertiary, #888); font-size: 11px; }\n[data-edrv-view] .edrv-tab .edrv-tab-x:hover { background: rgba(255,255,255,.12); color: var(--dsw-alias-label-primary, #eee); }\n[data-edrv-view] .edrv-tab-add { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; margin-left: 2px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, #aaa); font-size: 14px; cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-tab-add:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.06)); }\n[data-edrv-view] .edrv-pathbar { display: flex; align-items: center; gap: 8px; height: 26px; padding: 0 10px; background: var(--dsw-alias-bg-layer-1, #ffffff); border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); font-size: 12px; flex-shrink: 0; overflow: hidden; }\n[data-edrv-view] .edrv-pathbar .edrv-pb-name { font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); white-space: nowrap; }\n[data-edrv-view] .edrv-pathbar .edrv-pb-full { flex: 1; min-width: 0; color: var(--dsw-alias-label-tertiary, #9aa5b1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; direction: rtl; text-align: left; }\n[data-edrv-view] .edrv-path-input { flex: 1; min-width: 140px; height: 24px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 12px; outline: none; }\n[data-edrv-view] .edrv-search-wrap { position: relative; flex-shrink: 0; display: flex; align-items: center; padding: 2px 8px 2px 4px; }\n[data-edrv-view] .edrv-search { width: 190px; height: 24px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 12px; outline: none; }\n[data-edrv-view] .edrv-search:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-search-pop { position: absolute; top: calc(100% + 4px); right: 8px; width: min(460px, calc(100vw - 24px)); max-height: 300px; overflow: auto; background: var(--dsw-alias-bg-overlay, #1c1c1c); border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 10px; box-shadow: 0 6px 20px rgba(0,0,0,.4); z-index: 60; padding: 4px; }\n[data-edrv-view] .edrv-search-item { display: flex; flex-direction: column; gap: 1px; padding: 5px 8px; border-radius: 6px; cursor: pointer; }\n[data-edrv-view] .edrv-search-item:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.07)); }\n[data-edrv-view] .edrv-search-item .n { color: var(--dsw-alias-label-primary, #eee); font-size: 12px; word-break: break-all; }\n[data-edrv-view] .edrv-search-item .d { color: var(--dsw-alias-label-tertiary, #888); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-search-empty { padding: 8px; font-size: 12px; color: var(--dsw-alias-label-tertiary, #888); }\n[data-edrv-view] .edrv-btn-mini { font-size: 11px; padding: 2px 9px; border: none; border-radius: 999px; cursor: pointer; color: #fff; font-weight: 600; white-space: nowrap; }\n[data-edrv-view] .edrv-btn-keep { background: var(--dsw-alias-state-success-primary, #2e9e44); }\n[data-edrv-view] .edrv-btn-undo { background: var(--dsw-alias-state-warn-primary, #d97706); }\n[data-edrv-view] .edrv-btn-mini:disabled { opacity: .45; cursor: default; }\n[data-edrv-view] .edrv-statusbar { display: flex; align-items: center; gap: 10px; padding: 3px 10px; border-top: 1px solid var(--dsw-alias-border-l1, #333); background: var(--dsw-alias-bg-layer-1, transparent); font-size: 11px; color: var(--dsw-alias-label-tertiary, #888); flex-shrink: 0; white-space: nowrap; overflow: hidden; }\n[data-edrv-view] .edrv-statusbar .edrv-sp-path { flex: 1; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-diffchip { font-size: 11px; padding: 2px 10px; border: 1px solid var(--dsw-alias-border-l1, #333); border-radius: 999px; background: var(--dsw-alias-bg-layer-2, transparent); color: var(--dsw-alias-state-warn-primary, #b7791f); cursor: pointer; white-space: nowrap; flex-shrink: 0; }\n[data-edrv-view] .edrv-chip-btn { font-size: 12px; padding: 2px 6px; border: none; background: transparent; color: var(--dsw-alias-label-secondary, #aaa); cursor: pointer; border-radius: 4px; flex-shrink: 0; }\n[data-edrv-view] .edrv-chip-btn:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.06)); }\n[data-edrv-view] .edrv-mn-add-line { background: rgba(15,157,88,.10); }\n[data-edrv-view] .edrv-mn-gutter-add { color: #0f9d58 !important; font-weight: 700; display: flex !important; align-items: center; justify-content: center; }\n[data-edrv-view] .edrv-mn-gutter-add::before { content: \"+\"; }\n[data-edrv-view] .edrv-del-zone { background: #fdeaea; border-top: 1px solid rgba(217,83,79,.35); border-bottom: 1px solid rgba(217,83,79,.35); box-shadow: inset 0 0 0 1px rgba(217,83,79,.12); }\n[data-edrv-view] .edrv-del-row { white-space: pre; font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 12.5px; line-height: 20px; color: #c0392b; }\n[data-edrv-view] .edrv-del-text { display: inline-block; white-space: pre; }\n[data-edrv-view] .edrv-minus-overlay { position: absolute; left: 0; top: 0; pointer-events: none; z-index: 6; }\n[data-edrv-view] .edrv-minus-item { position: absolute; width: 16px; text-align: center; color: #d9534f; font-weight: 700; font-size: 12.5px; line-height: 20px; }\n[data-edrv-view] .edrv-monaco-host { flex: 1; min-height: 0; position: relative; }\n[data-edrv-view] .edrv-monaco-host > div { position: absolute; inset: 0; }\n[data-edrv-view] .edrv-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; height: 100%; color: var(--dsw-alias-label-tertiary, #777); font-size: 13px; }\n[data-edrv-view] .edrv-diffbar { position: absolute; left: 0; right: 0; bottom: 10px; margin: 0 auto; z-index: 25; width: fit-content; max-width: calc(100% - 20px); border-radius: 10px; background: var(--dsw-alias-bg-overlay, #ffffff); border: 1px solid var(--dsw-alias-border-l2, #cbd2d9); box-shadow: 0 4px 16px rgba(31,41,51,.12); overflow: visible; display: flex; flex-direction: column; }\n[data-edrv-view] .edrv-diffbar-row { display: flex; align-items: center; gap: 6px; height: 34px; padding: 0 8px; flex-wrap: nowrap; }\n[data-edrv-view] .edrv-diffbar-count { font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); min-width: 42px; text-align: center; white-space: nowrap; cursor: pointer; }\n[data-edrv-view] .edrv-diffbar-count.edrv-count-file { min-width: 64px; cursor: default; }\n[data-edrv-view] .edrv-diffbar-file { flex: 1; min-width: 0; font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-pill { font-size: 11px; padding: 3px 10px; border: none; border-radius: 999px; cursor: pointer; white-space: nowrap; font-weight: 600; }\n[data-edrv-view] .edrv-pill-keep { background: #e8f8e8; color: #0f7a45; border: 1px solid rgba(15,157,88,.4); }\n[data-edrv-view] .edrv-pill-undo { background: #fdeaea; color: #c0392b; border: 1px solid rgba(217,83,79,.4); }\n[data-edrv-view] .edrv-pill-ghost { background: #ffffff; color: var(--dsw-alias-label-secondary, #52606d); border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-pill:disabled { opacity: .45; cursor: default; }\n[data-edrv-view] .edrv-diffbar-menu { position: absolute; right: 8px; bottom: calc(100% + 6px); z-index: 45; min-width: 160px; border-radius: 10px; background: var(--dsw-alias-bg-overlay, #ffffff); border: 1px solid var(--dsw-alias-border-l2, #cbd2d9); box-shadow: 0 6px 20px rgba(31,41,51,.16); padding: 4px; display: flex; flex-direction: column; gap: 2px; }\n[data-edrv-view] .edrv-diffmenu-item { display: flex; align-items: center; gap: 8px; font-size: 12px; padding: 6px 10px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-primary, #1f2933); cursor: pointer; text-align: left; }\n[data-edrv-view] .edrv-diffmenu-item:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-diffmenu-item.danger { color: var(--dsw-alias-state-error-primary, #d9534f); }\n[data-edrv-view] .edrv-diffmenu-sep { height: 1px; margin: 2px 4px; background: var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-diffbar-body { max-height: 130px; max-width: min(560px, calc(100vw - 40px)); overflow: auto; padding: 4px 8px 8px; display: flex; flex-direction: column; gap: 4px; border-top: 1px solid var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-diffrow { display: flex; align-items: center; gap: 8px; padding: 3px 6px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); cursor: pointer; flex-wrap: wrap; }\n[data-edrv-view] .edrv-diffrow:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-diffrow-l { font-size: 11px; color: var(--dsw-alias-label-primary, #1f2933); min-width: 74px; font-family: var(--ds-font-family-code, ui-monospace, monospace); }\n[data-edrv-view] .edrv-diffrow-o { font-size: 11px; color: var(--dsw-alias-state-error-primary, #d9534f); font-family: var(--ds-font-family-code, ui-monospace, monospace); }\n[data-edrv-view] .edrv-diffrow-n { font-size: 11px; color: var(--dsw-alias-state-success-primary, #0f9d58); font-family: var(--ds-font-family-code, ui-monospace, monospace); }\n[data-edrv-view] .edrv-diffrow-tag { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-diffrow-stale { border-color: rgba(217,119,6,.5); cursor: default; }\n[data-edrv-view] .edrv-diffbox-others { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; padding: 4px 2px 0; border-top: 1px solid var(--dsw-alias-border-l1, #e0e6e8); margin-top: 2px; }\n[data-edrv-view] .edrv-diffrow-file { font-size: 11px; padding: 2px 10px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-secondary, #52606d); cursor: pointer; white-space: nowrap; max-width: 200px; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-diffrow-file:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); color: var(--dsw-alias-label-primary, #1f2933); }\n/* 编辑区 hover 差异块的 Keep/Undo 浮层 */\n[data-edrv-view] .edrv-hoveract { position: absolute; z-index: 30; display: flex; align-items: center; gap: 4px; padding: 2px 6px; border-radius: 8px; background: var(--dsw-alias-bg-overlay, #ffffff); border: 1px solid var(--dsw-alias-border-l2, #cbd2d9); box-shadow: 0 4px 14px rgba(31,41,51,.14); }\n[data-edrv-view] .edrv-launch { position: absolute; left: 12px; right: 12px; bottom: 12px; z-index: 40; border-radius: 10px; background: var(--dsw-alias-bg-overlay, #1c1c1c); border: 1px solid var(--dsw-alias-border-l2, #555); box-shadow: 0 6px 24px rgba(0,0,0,.5); display: flex; flex-direction: column; max-height: 60%; overflow: hidden; }\n[data-edrv-view] .edrv-launch-head { display: flex; align-items: center; gap: 6px; padding: 6px 8px; border-bottom: 1px solid var(--dsw-alias-border-l1, #333); }\n[data-edrv-view] .edrv-launch-tab { font-size: 12px; padding: 3px 12px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, #aaa); cursor: pointer; }\n[data-edrv-view] .edrv-launch-tab.on { background: var(--dsw-alias-bg-layer-2, transparent); color: var(--dsw-alias-label-primary, #eee); font-weight: 600; }\n[data-edrv-view] .edrv-launch-body { flex: 1; min-height: 0; overflow: auto; padding: 6px 8px; display: flex; flex-direction: column; gap: 4px; }\n[data-edrv-view] .edrv-launch-row { display: flex; align-items: center; gap: 8px; padding: 3px 6px; border: 1px solid var(--dsw-alias-border-l1, #333); border-radius: 6px; cursor: pointer; flex-wrap: wrap; }\n[data-edrv-view] .edrv-launch-row:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.06)); }\n[data-edrv-view] .edrv-launch-path { flex: 1; min-width: 0; font-size: 12px; color: var(--dsw-alias-label-primary, #eee); word-break: break-all; }\n[data-edrv-view] .edrv-launch-cnt { font-size: 11px; color: var(--dsw-alias-label-tertiary, #888); white-space: nowrap; }\n[data-edrv-view] .edrv-launch-arch { display: flex; flex-direction: column; gap: 6px; }\n[data-edrv-view] .monaco-editor, [data-edrv-view] .monaco-editor .margin, [data-edrv-view] .monaco-editor-background { background: transparent !important; }\n[data-edrv-view] .monaco-editor .scrollbar .slider { background: rgba(128,128,128,.3) !important; }\n";
33
+ const tagId = "dsh-vscode-mode/editor.css";
34
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
35
+ const tag = document.createElement("style");
36
+ tag.dataset.plugin = "dsh-vscode-mode";
37
+ tag.dataset.pluginCss = tagId;
38
+ tag.textContent = css;
39
+ document.head.appendChild(tag);
40
+ }
41
+ //#endregion
42
+ //#region src/shared/rpc.ts
43
+ /** webServer 精确路由。 */
44
+ const RPC_PATH = "/edrv/rpc";
45
+ //#endregion
46
+ //#region src/client/rpc.ts
47
+ /**
48
+ * dsh-vscode-mode client — 类型化 RPC 包装 + 诊断日志。
49
+ * 迁移自原 src/client/index.ts 的 rpc/dbg,语义不改。
50
+ * 作者 ddj 2026-08-20
51
+ */
52
+ /** 同源 RPC 调用(host /edrv/rpc 精确路由)。 */
53
+ function rpc(method, args) {
54
+ return fetch(RPC_PATH, {
55
+ method: "POST",
56
+ headers: { "content-type": "application/json" },
57
+ body: JSON.stringify({
58
+ method,
59
+ args
60
+ })
61
+ }).then((res) => res.json());
62
+ }
63
+ /**
64
+ * 诊断日志:统一走 host edrv.debug 落盘,避免 console 刷屏。
65
+ * @author ddj 2026年08月20号
66
+ * @param sessionId 会话 id
67
+ * @param text 日志文本
68
+ */
69
+ function dbg(sessionId, text) {
70
+ try {
71
+ rpc("edrv.debug", {
72
+ sessionId,
73
+ text
74
+ }).catch(() => {});
75
+ } catch (e) {}
76
+ }
77
+ //#endregion
78
+ //#region src/client/events.ts
79
+ /**
80
+ * dsh-vscode-mode client — 跨组件/跨 slot 的窗口事件助手。
81
+ * 迁移自原 src/client/index.ts 的 openEditorView 与事件派发,语义不改。
82
+ * 作者 ddj 2026-08-20
83
+ */
84
+ /**
85
+ * 打开中央「文件编辑」页签:点击 shell 会话头部的视图页签(DOM 级,无需 store actions)。
86
+ * @author ddj 2026年08月20号
87
+ * @param path 要打开的路径(可空)
88
+ */
89
+ function openEditorView(path) {
90
+ const tabs = Array.from(document.querySelectorAll("div[role=\"tablist\"] button[role=\"tab\"]"));
91
+ for (const b of tabs) if (b.textContent && b.textContent.includes("文件编辑")) {
92
+ b.click();
93
+ break;
94
+ }
95
+ const p = path ?? null;
96
+ setTimeout(() => {
97
+ window.dispatchEvent(new CustomEvent("edrv:open-editor", { detail: { path: p } }));
98
+ }, 80);
99
+ }
100
+ /** 差异状态变化:触发角标/DiffBox 重拉。 */
101
+ function emitRefresh() {
102
+ window.dispatchEvent(new CustomEvent("edrv:refresh"));
103
+ }
104
+ /** 拉起 DiffLauncher。 */
105
+ function emitShowLauncher() {
106
+ window.dispatchEvent(new CustomEvent("edrv:show-launcher"));
107
+ }
108
+ //#endregion
109
+ //#region src/client/monaco/loader.ts
110
+ /**
111
+ * dsh-vscode-mode client — Monaco 加载与语言映射(AMD 构建,离线随包分发)。
112
+ * 迁移自原 src/client/index.ts 的 MONACO_BASE/LANG_BY_EXT/langOf/loadMonaco,语义不改。
113
+ * 作者 ddj 2026-08-20
114
+ */
115
+ const MONACO_BASE = "/edrv/vendor/monaco/vs";
116
+ let monacoPromise = null;
117
+ /** 扩展名 → Monaco language id(常见语言;未知回退 plaintext)。 */
118
+ const LANG_BY_EXT = {
119
+ js: "javascript",
120
+ jsx: "javascript",
121
+ mjs: "javascript",
122
+ cjs: "javascript",
123
+ ts: "typescript",
124
+ tsx: "typescript",
125
+ mts: "typescript",
126
+ cts: "typescript",
127
+ json: "json",
128
+ jsonc: "json",
129
+ md: "markdown",
130
+ markdown: "markdown",
131
+ mdx: "mdx",
132
+ css: "css",
133
+ scss: "scss",
134
+ less: "less",
135
+ html: "html",
136
+ htm: "html",
137
+ vue: "html",
138
+ xml: "xml",
139
+ yaml: "yaml",
140
+ yml: "yaml",
141
+ toml: "ini",
142
+ ini: "ini",
143
+ cfg: "ini",
144
+ py: "python",
145
+ sh: "shell",
146
+ bash: "shell",
147
+ zsh: "shell",
148
+ ps1: "powershell",
149
+ lua: "lua",
150
+ java: "java",
151
+ c: "c",
152
+ h: "c",
153
+ cpp: "cpp",
154
+ cc: "cpp",
155
+ cxx: "cpp",
156
+ hpp: "cpp",
157
+ cs: "csharp",
158
+ go: "go",
159
+ rs: "rust",
160
+ rb: "ruby",
161
+ php: "php",
162
+ sql: "sql",
163
+ swift: "swift",
164
+ kt: "kotlin",
165
+ kts: "kotlin",
166
+ dart: "dart",
167
+ dockerfile: "dockerfile"
168
+ };
169
+ /**
170
+ * 路径 → Monaco language id:取 basename 扩展名(dockerfile/Dockerfile 特判)。
171
+ * @author ddj 2026年08月20号
172
+ * @param path 文件路径
173
+ * @returns language id
174
+ */
175
+ function langOf(path) {
176
+ const base = String(path || "").split(/[\\/]/).pop() || "";
177
+ if (/^dockerfile$/i.test(base)) return "dockerfile";
178
+ const dot = base.lastIndexOf(".");
179
+ if (dot <= 0) return "plaintext";
180
+ return LANG_BY_EXT[base.slice(dot + 1).toLowerCase()] ?? "plaintext";
181
+ }
182
+ /**
183
+ * 加载 Monaco Editor(AMD 构建,随插件包离线分发):注入 loader.js → require.config → editor.main。
184
+ * @author ddj 2026年08月20号
185
+ * @returns Promise<object> window.monaco
186
+ */
187
+ function loadMonaco() {
188
+ if (!monacoPromise) monacoPromise = new Promise((resolve, reject) => {
189
+ const boot = () => {
190
+ try {
191
+ window.require.config({ paths: { vs: MONACO_BASE } });
192
+ window.require(["vs/editor/editor.main"], () => resolve(window.monaco), (err) => {
193
+ monacoPromise = null;
194
+ reject(/* @__PURE__ */ new Error("Monaco 模块加载失败:" + String(err)));
195
+ });
196
+ } catch (error) {
197
+ monacoPromise = null;
198
+ reject(error);
199
+ }
200
+ };
201
+ if (document.querySelector("script[data-edrv-monaco-loader]")) boot();
202
+ else {
203
+ const s = document.createElement("script");
204
+ s.src = "/edrv/vendor/monaco/vs/loader.js";
205
+ s.dataset.edrvMonacoLoader = "1";
206
+ s.onload = boot;
207
+ s.onerror = () => {
208
+ monacoPromise = null;
209
+ reject(/* @__PURE__ */ new Error("Monaco loader 加载失败"));
210
+ };
211
+ document.head.appendChild(s);
212
+ }
213
+ });
214
+ return monacoPromise;
215
+ }
216
+ //#endregion
217
+ //#region src/client/monaco/diffRender.ts
218
+ /**
219
+ * dsh-vscode-mode client — 行内差异自绘(新增行绿底 + glyph margin '+';删除块 view zone 红底)。
220
+ * 迁移自原 src/client/index.ts 的 diff-render effect,语义不改。
221
+ * 作者 ddj 2026-08-20
222
+ */
223
+ /**
224
+ * 差异渲染器(每 EditorView 挂载一个,内部持有 zone/decoration 状态)。
225
+ * @param log 诊断日志函数((sessionId, text) => void)
226
+ */
227
+ function createDiffRenderer(log) {
228
+ let viewZoneIds = [];
229
+ let decorations = [];
230
+ /**
231
+ * 渲染当前 pending 差异:绿底 decoration + 删除块 view zones + '-' 号 overlay(滚动同步)。
232
+ * @param monaco window.monaco
233
+ * @param editor Monaco 编辑器实例
234
+ * @param pendingRegions 待处理差异区域
235
+ * @param sessionId 会话 id
236
+ */
237
+ const render = (monaco, editor, pendingRegions, sessionId) => {
238
+ const renderT0 = Date.now();
239
+ const callIdAttr = (callId, idx) => String(callId) + ":" + String(idx);
240
+ log(sessionId, "[diff-render] begin regs=" + pendingRegions.length + " " + pendingRegions.map((r) => callIdAttr(r.callId, r.idx) + "@" + (r.start ?? "?") + "-" + (r.end ?? "?") + "(-" + (r.oldLines ? r.oldLines.length : 0) + "/+" + (r.newLines ? r.newLines.length : 0) + ")").join("|"));
241
+ const decos = [];
242
+ for (const r of pendingRegions) {
243
+ if (r.start === void 0 || r.end === void 0) continue;
244
+ if (!r.create && r.newLines && r.newLines.length) decos.push({
245
+ range: new monaco.Range(r.start, 1, Math.max(r.start, r.end - 1), 1),
246
+ options: {
247
+ isWholeLine: true,
248
+ className: "edrv-mn-add-line",
249
+ linesDecorationsClassName: "edrv-mn-gutter-add"
250
+ }
251
+ });
252
+ }
253
+ decorations = editor.deltaDecorations(decorations, decos);
254
+ let createdZones = [];
255
+ editor.changeViewZones((accessor) => {
256
+ for (const id of viewZoneIds) try {
257
+ accessor.removeZone(id);
258
+ } catch (e) {}
259
+ viewZoneIds = [];
260
+ const zones = [];
261
+ for (const r of pendingRegions) {
262
+ if (r.create) continue;
263
+ const oldLines = r.oldLines && r.oldLines.length ? r.oldLines : null;
264
+ if (!oldLines) continue;
265
+ const after = Math.max(0, (r.start ?? 1) - 1);
266
+ const domNode = document.createElement("div");
267
+ domNode.className = "edrv-del-zone";
268
+ domNode.dataset.edrvHunk = callIdAttr(r.callId, r.idx);
269
+ for (const t of oldLines) {
270
+ const row = document.createElement("div");
271
+ row.className = "edrv-del-row";
272
+ const text = document.createElement("span");
273
+ text.className = "edrv-del-text";
274
+ text.textContent = t;
275
+ row.appendChild(text);
276
+ domNode.appendChild(row);
277
+ }
278
+ const id = accessor.addZone({
279
+ afterLineNumber: after,
280
+ heightInLines: oldLines.length,
281
+ domNode,
282
+ suppressMouseDown: false
283
+ });
284
+ viewZoneIds.push(id);
285
+ zones.push({
286
+ domNode,
287
+ after,
288
+ n: oldLines.length
289
+ });
290
+ }
291
+ createdZones = zones;
292
+ });
293
+ if (createdZones.length) {
294
+ const root = editor.getDomNode();
295
+ if (root) {
296
+ let overlay = root.querySelector(".edrv-minus-overlay");
297
+ if (!overlay) {
298
+ overlay = document.createElement("div");
299
+ overlay.className = "edrv-minus-overlay";
300
+ root.appendChild(overlay);
301
+ }
302
+ overlay.innerHTML = "";
303
+ const lineH = editor.getOption(monaco.editor.EditorOption.lineHeight) || 20;
304
+ let li = null;
305
+ try {
306
+ li = editor.getLayoutInfo();
307
+ } catch (e) {}
308
+ const left = li ? li.decorationsLeft : 0;
309
+ const width = li && li.decorationsWidth > 0 ? li.decorationsWidth : 16;
310
+ const lineCount = editor.getModel().getLineCount();
311
+ let itemIdx = 0;
312
+ let zi = 0;
313
+ for (const z of createdZones) {
314
+ const anchor = Math.min(z.after + 1, lineCount);
315
+ let zoneBottom = editor.getTopForLineNumber(anchor);
316
+ if (z.after >= lineCount) zoneBottom += lineH;
317
+ const zoneTop = zoneBottom - z.n * lineH;
318
+ for (let i = 0; i < z.n; i++) {
319
+ const item = document.createElement("div");
320
+ item.className = "edrv-minus-item";
321
+ item.textContent = "-";
322
+ item.style.width = width + "px";
323
+ item.style.height = lineH + "px";
324
+ item.dataset.zone = String(zi);
325
+ item.dataset.row = String(i);
326
+ item.style.top = zoneTop + i * lineH + "px";
327
+ item.style.left = left + "px";
328
+ overlay.appendChild(item);
329
+ itemIdx++;
330
+ }
331
+ zi++;
332
+ }
333
+ log(sessionId, "[diff-render] placed zones=" + createdZones.length + " items=" + itemIdx + " left=" + left + " w=" + width + " t+" + (Date.now() - renderT0) + "ms");
334
+ const syncScroll = () => {
335
+ const s2 = editor.getScrollTop() || 0;
336
+ if (overlay) overlay.style.transform = "translateY(" + -s2 + "px)";
337
+ };
338
+ syncScroll();
339
+ overlay.__edrvSync = syncScroll;
340
+ if (!overlay.__edrvBound) {
341
+ overlay.__edrvBound = true;
342
+ editor.onDidScrollChange(syncScroll);
343
+ }
344
+ setTimeout(() => {
345
+ try {
346
+ const edRect = root.getBoundingClientRect();
347
+ const st = editor.getScrollTop() || 0;
348
+ const items = overlay.querySelectorAll(".edrv-minus-item");
349
+ for (const item of items) {
350
+ const zIndex = Number(item.dataset.zone);
351
+ const rIndex = Number(item.dataset.row);
352
+ const z = createdZones[zIndex];
353
+ if (!z || !z.domNode.isConnected) continue;
354
+ const rowEl = z.domNode.querySelectorAll(".edrv-del-row")[rIndex];
355
+ if (!rowEl) continue;
356
+ const rowRect = rowEl.getBoundingClientRect();
357
+ if (rowRect.height > 0) item.style.top = rowRect.top - edRect.top + st + "px";
358
+ }
359
+ } catch (e) {
360
+ log(sessionId, "[diff-render] correct fail " + String(e));
361
+ }
362
+ }, 50);
363
+ }
364
+ } else {
365
+ const root = editor.getDomNode();
366
+ const overlay = root && root.querySelector(".edrv-minus-overlay");
367
+ if (overlay) overlay.innerHTML = "";
368
+ }
369
+ };
370
+ /** 卸载清理(EditorView unmount 时调用)。 */
371
+ const dispose = () => {
372
+ viewZoneIds = [];
373
+ decorations = [];
374
+ };
375
+ return {
376
+ render,
377
+ dispose
378
+ };
379
+ }
380
+ //#endregion
381
+ //#region src/client/state/records.ts
382
+ /** 差异决策状态。 */
383
+ const ST = {
384
+ PENDING: "pending",
385
+ ACCEPTED: "accepted",
386
+ REJECTED: "rejected"
387
+ };
388
+ /** hunk 的 DOM 定位 key(callId:idx)。 */
389
+ function callIdAttr(callId, idx) {
390
+ return String(callId) + ":" + String(idx);
391
+ }
392
+ /** 某 hunk 的当前决策状态(缺省 pending)。 */
393
+ function statusAt(rec, idx) {
394
+ if (!rec) return ST.PENDING;
395
+ if (Array.isArray(rec.decisions?.perHunk) && rec.decisions.perHunk[idx] !== void 0) return rec.decisions.perHunk[idx];
396
+ return rec.decisions?.call ?? ST.PENDING;
397
+ }
398
+ /** hunk 是否为空差异(old===new,无实际内容变化,不可操作)。create 记录不视为空差异。 */
399
+ function noopHunk(rec, h) {
400
+ if (!rec || rec.create === true) return false;
401
+ const precise = rec.toolName === "edit" && rec.callHunk ? rec.callHunk : h;
402
+ const oldText = precise?.oldText ?? null;
403
+ const newText = precise?.newText ?? null;
404
+ return oldText !== null && oldText === newText;
405
+ }
406
+ /** 记录是否仍有待处理差异(superseded / 全部已决策 = false)。 */
407
+ function isRecPending(rec) {
408
+ if (!rec || rec.superseded === true) return false;
409
+ const perHunk = Array.isArray(rec.decisions?.perHunk) ? rec.decisions.perHunk : [];
410
+ if (perHunk.length) {
411
+ for (let i = 0; i < perHunk.length; i++) if (perHunk[i] !== "accepted" && perHunk[i] !== "rejected" && !noopHunk(rec, (rec.hunks || [])[i])) return true;
412
+ return false;
413
+ }
414
+ return rec.decisions.call === "pending" || rec.decisions.call === void 0;
415
+ }
416
+ /** 记录待处理差异"处"数(按 hunk,跳过空差异)。 */
417
+ function pendingCount(recs) {
418
+ let n = 0;
419
+ for (const rec of recs) {
420
+ if (!isRecPending(rec)) continue;
421
+ const perHunk = Array.isArray(rec.decisions?.perHunk) ? rec.decisions.perHunk : [];
422
+ if (perHunk.length) {
423
+ for (let i = 0; i < perHunk.length; i++) if (perHunk[i] === "pending" && !noopHunk(rec, (rec.hunks || [])[i])) n++;
424
+ } else {
425
+ const hunks = Array.isArray(rec.hunks) ? rec.hunks : [];
426
+ for (const h of hunks) if (!noopHunk(rec, h)) n++;
427
+ if (!hunks.length && !noopHunk(rec, rec.callHunk)) n = Math.max(n, 1);
428
+ }
429
+ }
430
+ return n;
431
+ }
432
+ /** 按文件分组的差异摘要(角标/Launcher 用)。 */
433
+ function summarize(records) {
434
+ const byPath = /* @__PURE__ */ new Map();
435
+ for (const r of records) {
436
+ if (!byPath.has(r.path)) byPath.set(r.path, []);
437
+ byPath.get(r.path).push(r);
438
+ }
439
+ const files = [];
440
+ for (const [path, recs] of byPath) files.push({
441
+ path,
442
+ recs,
443
+ pending: pendingCount(recs)
444
+ });
445
+ files.sort((a, b) => a.path < b.path ? -1 : 1);
446
+ const pendingFiles = files.filter((f) => f.pending > 0);
447
+ return {
448
+ files,
449
+ pendingFiles,
450
+ totalFiles: pendingFiles.length
451
+ };
452
+ }
453
+ //#endregion
454
+ //#region src/client/state/regions.ts
455
+ /** 统计 index 之前(不含)的换行数 → 0 基行号。 */
456
+ function countLinesBefore(text, index) {
457
+ let n = 0;
458
+ for (let i = 0; i < index; i++) if (text.charCodeAt(i) === 10) n++;
459
+ return n;
460
+ }
461
+ /**
462
+ * 行级公共前缀/后缀裁剪:old/new 首尾相同的行视为未变化(上下文),只保留真正变更的中间段。
463
+ * @author ddj 2026年08月20号
464
+ * @param oldLines 替换前内容按行拆分
465
+ * @param newLines 替换后内容按行拆分
466
+ * @returns 裁剪后的变更段与公共前缀行数
467
+ */
468
+ function trimCommonLines(oldLines, newLines) {
469
+ let prefix = 0;
470
+ const maxPrefix = Math.min(oldLines.length, newLines.length);
471
+ while (prefix < maxPrefix && oldLines[prefix] === newLines[prefix]) prefix++;
472
+ let suffix = 0;
473
+ const maxSuffix = Math.min(oldLines.length - prefix, newLines.length - prefix);
474
+ while (suffix < maxSuffix && oldLines[oldLines.length - 1 - suffix] === newLines[newLines.length - 1 - suffix]) suffix++;
475
+ return {
476
+ oldLines: oldLines.slice(prefix, oldLines.length - suffix),
477
+ newLines: newLines.slice(prefix, newLines.length - suffix),
478
+ shift: prefix
479
+ };
480
+ }
481
+ /** 计算文件内各差异区域(行范围 + old/new + 状态),用于行内绿标注与 DiffBox。 */
482
+ function diffRegions(records, content) {
483
+ const regions = [];
484
+ if (content === null) return regions;
485
+ const lines = content.split("\n");
486
+ for (const rec of records) for (let i = 0; i < rec.hunks.length; i++) {
487
+ const h = rec.hunks[i];
488
+ if (noopHunk(rec, h)) continue;
489
+ const status = statusAt(rec, i);
490
+ if (rec.create) {
491
+ regions.push({
492
+ callId: rec.callId,
493
+ idx: i,
494
+ start: 1,
495
+ end: lines.length,
496
+ oldLines: [],
497
+ newLines: lines.slice(),
498
+ whole: true,
499
+ status,
500
+ create: true,
501
+ rec,
502
+ superseded: rec.superseded === true
503
+ });
504
+ continue;
505
+ }
506
+ const precise = rec.toolName === "edit" && rec.callHunk ? rec.callHunk : h;
507
+ const newText = precise.newText ?? "";
508
+ const at = content.indexOf(newText);
509
+ if (at < 0) {
510
+ regions.push({
511
+ callId: rec.callId,
512
+ idx: i,
513
+ stale: true,
514
+ status,
515
+ create: false,
516
+ oldLines: [],
517
+ newLines: [],
518
+ rec,
519
+ superseded: rec.superseded === true
520
+ });
521
+ continue;
522
+ }
523
+ const start = countLinesBefore(content, at) + 1;
524
+ const trimmed = trimCommonLines(precise.oldText === null ? [] : precise.oldText.split("\n"), newText.split("\n"));
525
+ const regionStart = start + trimmed.shift;
526
+ regions.push({
527
+ callId: rec.callId,
528
+ idx: i,
529
+ start: regionStart,
530
+ end: regionStart + trimmed.newLines.length,
531
+ oldLines: trimmed.oldLines,
532
+ newLines: trimmed.newLines,
533
+ status,
534
+ create: false,
535
+ rec,
536
+ superseded: rec.superseded === true
537
+ });
538
+ }
539
+ regions.sort((a, b) => (a.start ?? Infinity) - (b.start ?? Infinity));
540
+ return regions;
541
+ }
542
+ //#endregion
543
+ //#region src/client/ui/QuickOpen.ts
544
+ /**
545
+ * dsh-vscode-mode client — QuickOpen:顶部搜索框(Ctrl+P 打开文件)。
546
+ * 迁移自原 src/client/index.ts 的 QuickOpen,语义不改。
547
+ * 作者 ddj 2026-08-20
548
+ */
549
+ /**
550
+ * 快速打开搜索框:Ctrl+P 聚焦,输入 ≥2 字符经 edrv.searchFiles 搜索,
551
+ * 结果列表点击/回车打开文件。
552
+ * @param props.sessionId 会话 id
553
+ * @param props.onOpen 打开回调(path)
554
+ */
555
+ function QuickOpen(props) {
556
+ const sessionId = props?.sessionId;
557
+ const onOpen = props?.onOpen;
558
+ const [q, setQ] = react.default.useState("");
559
+ const [results, setResults] = react.default.useState(null);
560
+ const [open, setOpen] = react.default.useState(false);
561
+ const inputRef = react.default.useRef(null);
562
+ const seq = react.default.useRef(0);
563
+ const timer = react.default.useRef(null);
564
+ const search = (query) => {
565
+ const s = ++seq.current;
566
+ const clean = String(query || "").trim();
567
+ if (clean.length < 2) {
568
+ setResults(null);
569
+ return;
570
+ }
571
+ rpc("edrv.searchFiles", {
572
+ sessionId,
573
+ query: clean
574
+ }).then((res) => {
575
+ if (s !== seq.current) return;
576
+ setResults(res && res.ok && Array.isArray(res.files) ? res.files : []);
577
+ }).catch(() => {
578
+ if (s === seq.current) setResults([]);
579
+ });
580
+ };
581
+ const onChange = (v) => {
582
+ setQ(v);
583
+ setOpen(true);
584
+ if (timer.current) clearTimeout(timer.current);
585
+ timer.current = setTimeout(() => search(v), 180);
586
+ };
587
+ const pick = (p) => {
588
+ onOpen(p);
589
+ setQ("");
590
+ setResults(null);
591
+ setOpen(false);
592
+ inputRef.current?.blur?.();
593
+ };
594
+ react.default.useEffect(() => {
595
+ const onKey = (e) => {
596
+ if ((e.ctrlKey || e.metaKey) && String(e.key).toLowerCase() === "p") {
597
+ e.preventDefault();
598
+ e.stopPropagation();
599
+ inputRef.current?.focus?.();
600
+ setOpen(true);
601
+ } else if (e.key === "Escape") {
602
+ setOpen(false);
603
+ inputRef.current?.blur?.();
604
+ }
605
+ };
606
+ window.addEventListener("keydown", onKey, true);
607
+ return () => window.removeEventListener("keydown", onKey, true);
608
+ }, []);
609
+ const dirText = (p) => {
610
+ const base = String(p).split(/[\\/]/).pop() || String(p);
611
+ return String(p).slice(0, Math.max(0, String(p).length - base.length)).replace(/[\\/]+$/, "") || String(p);
612
+ };
613
+ let pop = null;
614
+ if (open && Array.isArray(results) && q.trim().length >= 2) {
615
+ if (results.length) {
616
+ const items = results.map((p) => react.default.createElement("div", {
617
+ key: p,
618
+ className: "edrv-search-item",
619
+ onClick: () => pick(p)
620
+ }, react.default.createElement("span", { className: "n" }, String(p).split(/[\\/]/).pop() || p), react.default.createElement("span", { className: "d" }, dirText(p))));
621
+ pop = react.default.createElement("div", { className: "edrv-search-pop" }, ...items);
622
+ } else pop = react.default.createElement("div", { className: "edrv-search-pop" }, react.default.createElement("div", { className: "edrv-search-empty" }, "无匹配文件"));
623
+ }
624
+ return react.default.createElement("div", { className: "edrv-search-wrap" }, react.default.createElement("input", {
625
+ ref: inputRef,
626
+ className: "edrv-search",
627
+ placeholder: "搜索文件 (Ctrl+P)",
628
+ value: q,
629
+ onChange: (e) => onChange(e.target.value),
630
+ onFocus: () => {
631
+ if (q.trim().length >= 2 && !results) setOpen(true);
632
+ },
633
+ onKeyDown: (e) => {
634
+ if (e.key === "Enter" && Array.isArray(results) && results.length) pick(results[0]);
635
+ if (e.key === "Escape") setOpen(false);
636
+ }
637
+ }), pop);
638
+ }
639
+ //#endregion
640
+ //#region src/client/ui/shared.ts
641
+ /**
642
+ * dsh-vscode-mode client — UI 共用小工具(按钮样式/状态文案/等宽字体)。
643
+ * 迁移自原 src/client/index.ts 的 btn/badgeOf/MONO,语义不改。
644
+ * 作者 ddj 2026-08-20
645
+ */
646
+ /** 次级按钮内联样式(accept=true 绿色 / false 红色)。 */
647
+ function btn(accept) {
648
+ return {
649
+ fontSize: 11,
650
+ padding: "1px 8px",
651
+ border: "1px solid var(--dsw-alias-border-l2,#555)",
652
+ borderRadius: 4,
653
+ cursor: "pointer",
654
+ background: "var(--dsw-alias-bg-layer-1,transparent)",
655
+ color: accept ? "var(--dsw-alias-state-success-primary,#2e9e44)" : "var(--dsw-alias-state-error-primary,#d9534f)"
656
+ };
657
+ }
658
+ /** 状态中文文案。 */
659
+ function badgeOf(status) {
660
+ return status === ST.ACCEPTED ? "已采纳" : status === ST.REJECTED ? "已拒绝" : "待处理";
661
+ }
662
+ /** 等宽字体常量(差异行渲染用)。 */
663
+ const MONO = {
664
+ fontFamily: "var(--ds-font-family-code, ui-monospace, monospace)",
665
+ fontSize: 12,
666
+ lineHeight: 1.55
667
+ };
668
+ //#endregion
669
+ //#region src/client/ui/DiffBox.ts
670
+ /**
671
+ * dsh-vscode-mode client — DiffBox:每个文件底部紧凑差异条(per-file hunk + ⋮ 二级菜单)。
672
+ * 迁移自原 src/client/index.ts 的 DiffBox,语义不改。
673
+ * 作者 ddj 2026-08-20
674
+ */
675
+ /**
676
+ * 差异条:当前文件内 diff 导航(↑↓←→)、Keep/Undo、展开 hunk 列表、⋮ 二级菜单
677
+ * (Keep All / Undo All / 差异总览 / 归档 / 回滚 / 刷新)+ 其他差异文件小节。
678
+ */
679
+ function DiffBox(props) {
680
+ const { pendingRegions, staleRegions, onAct, onAcceptFile, onUndoFile, onAcceptAllFiles, onUndoAllFiles, allPendingCount, onRollback, onJump, otherFiles, onOpenOther, onOpenLauncher, onRefresh, activePath, diffIdx, diffTotal, fileIdx, fileTotal, onPrevDiff, onNextDiff, onPrevFile, onNextFile } = props;
681
+ const [expanded, setExpanded] = react.default.useState(false);
682
+ const [menuOpen, setMenuOpen] = react.default.useState(false);
683
+ const canAct = pendingRegions.length > 0;
684
+ const base = String(activePath || "").split(/[\\/]/).pop() || "";
685
+ let bodyEl = null;
686
+ if (expanded) {
687
+ const rows = pendingRegions.map((r) => {
688
+ const key = callIdAttr(r.callId, r.idx);
689
+ return react.default.createElement("div", {
690
+ key,
691
+ className: "edrv-diffrow",
692
+ "data-edrv-hunk": key,
693
+ title: "跳转到 L" + (r.start ?? "?"),
694
+ onClick: () => onJump(r)
695
+ }, react.default.createElement("span", { className: "edrv-diffrow-l" }, "L" + (r.start ?? "?") + "-" + (r.end ?? "?")), r.oldLines.length ? react.default.createElement("span", { className: "edrv-diffrow-o" }, "-" + r.oldLines.length) : null, r.newLines.length ? react.default.createElement("span", { className: "edrv-diffrow-n" }, "+" + r.newLines.length) : null, r.create ? react.default.createElement("span", { className: "edrv-diffrow-tag" }, "新建") : null, react.default.createElement("span", { style: { flex: 1 } }));
696
+ });
697
+ const stale = staleRegions.map((r) => react.default.createElement("div", {
698
+ key: "stale" + callIdAttr(r.callId, r.idx),
699
+ className: "edrv-diffrow edrv-diffrow-stale",
700
+ title: "差异无法定位(文件可能已被手动修改)"
701
+ }, react.default.createElement("span", { className: "edrv-diffrow-l" }, "L" + (r.start ?? "?") + " 无法定位"), react.default.createElement("span", { className: "edrv-diffrow-tag" }, badgeOf(r.status))));
702
+ let othersEl = null;
703
+ if (otherFiles.length) othersEl = react.default.createElement("div", { className: "edrv-diffbox-others" }, react.default.createElement("span", { className: "edrv-diffrow-tag" }, "其他差异文件 (" + otherFiles.length + "):"), otherFiles.map((f) => react.default.createElement("button", {
704
+ key: f.path,
705
+ className: "edrv-diffrow-file",
706
+ title: f.path,
707
+ onClick: () => onOpenOther(f.path)
708
+ }, String(f.path).split(/[\\/]/).pop() + " (" + f.pending + ")")));
709
+ bodyEl = react.default.createElement("div", { className: "edrv-diffbar-body" }, ...rows, ...stale, othersEl);
710
+ }
711
+ const menu = menuOpen ? react.default.createElement(react.default.Fragment, null, react.default.createElement("div", {
712
+ style: {
713
+ position: "fixed",
714
+ inset: 0,
715
+ zIndex: 44
716
+ },
717
+ onClick: () => setMenuOpen(false)
718
+ }), react.default.createElement("div", { className: "edrv-diffbar-menu" }, react.default.createElement("button", {
719
+ className: "edrv-diffmenu-item",
720
+ disabled: !allPendingCount,
721
+ onClick: () => {
722
+ onAcceptAllFiles();
723
+ setMenuOpen(false);
724
+ }
725
+ }, "✓ Keep All(全部文件采纳)"), react.default.createElement("button", {
726
+ className: "edrv-diffmenu-item danger",
727
+ disabled: !allPendingCount,
728
+ onClick: () => {
729
+ onUndoAllFiles();
730
+ setMenuOpen(false);
731
+ }
732
+ }, "↩ Undo All(全部文件不采纳)"), react.default.createElement("div", { className: "edrv-diffmenu-sep" }), react.default.createElement("button", {
733
+ className: "edrv-diffmenu-item",
734
+ onClick: () => {
735
+ onOpenLauncher("pending");
736
+ setMenuOpen(false);
737
+ }
738
+ }, "🗂 差异总览"), react.default.createElement("button", {
739
+ className: "edrv-diffmenu-item",
740
+ onClick: () => {
741
+ onOpenLauncher("archive");
742
+ setMenuOpen(false);
743
+ }
744
+ }, "📁 归档"), react.default.createElement("div", { className: "edrv-diffmenu-sep" }), react.default.createElement("button", {
745
+ className: "edrv-diffmenu-item",
746
+ disabled: !canAct,
747
+ onClick: () => {
748
+ onRollback();
749
+ setMenuOpen(false);
750
+ }
751
+ }, "⟲ 回滚文件"), react.default.createElement("button", {
752
+ className: "edrv-diffmenu-item",
753
+ onClick: () => {
754
+ onRefresh();
755
+ setMenuOpen(false);
756
+ }
757
+ }, "⟳ 刷新"))) : null;
758
+ return react.default.createElement("div", {
759
+ className: "edrv-diffbar",
760
+ "data-edrv-diffbox": "1"
761
+ }, react.default.createElement("div", { className: "edrv-diffbar-row" }, react.default.createElement("button", {
762
+ className: "edrv-pill edrv-pill-ghost",
763
+ title: "上一个差异",
764
+ disabled: !canAct,
765
+ onClick: onPrevDiff
766
+ }, "↑"), react.default.createElement("span", {
767
+ className: "edrv-diffbar-count",
768
+ title: "当前文件内差异位置(点击展开/收起差异列表)",
769
+ onClick: () => setExpanded((v) => !v)
770
+ }, diffTotal ? diffIdx + 1 + "/" + diffTotal : "0/0"), react.default.createElement("button", {
771
+ className: "edrv-pill edrv-pill-ghost",
772
+ title: "下一个差异",
773
+ disabled: !canAct,
774
+ onClick: onNextDiff
775
+ }, "↓"), react.default.createElement("button", {
776
+ className: "edrv-pill edrv-pill-ghost",
777
+ title: "上一个差异文件",
778
+ disabled: !fileTotal,
779
+ onClick: onPrevFile
780
+ }, "←"), react.default.createElement("span", {
781
+ className: "edrv-diffbar-count edrv-count-file",
782
+ title: "差异文件位置"
783
+ }, fileTotal ? fileIdx + 1 + "/" + fileTotal + " 文件" : "0/0 文件"), react.default.createElement("button", {
784
+ className: "edrv-pill edrv-pill-ghost",
785
+ title: "下一个差异文件",
786
+ disabled: !fileTotal,
787
+ onClick: onNextFile
788
+ }, "→"), react.default.createElement("span", {
789
+ className: "edrv-diffbar-file",
790
+ title: activePath || ""
791
+ }, base), react.default.createElement("button", {
792
+ className: "edrv-pill edrv-pill-keep",
793
+ title: "采纳当前文件的全部差异",
794
+ disabled: !canAct,
795
+ onClick: onAcceptFile
796
+ }, "✓ Keep"), react.default.createElement("button", {
797
+ className: "edrv-pill edrv-pill-undo",
798
+ title: "不采纳当前文件的全部差异(回滚)",
799
+ disabled: !canAct,
800
+ onClick: onUndoFile
801
+ }, "↩ Undo"), react.default.createElement("button", {
802
+ className: "edrv-pill edrv-pill-ghost",
803
+ title: "更多操作",
804
+ onClick: () => setMenuOpen((v) => !v)
805
+ }, "⋮")), bodyEl, menu);
806
+ }
807
+ //#endregion
808
+ //#region src/client/ui/DiffBarEmpty.ts
809
+ /**
810
+ * dsh-vscode-mode client — DiffBarEmpty:无文件打开 / 当前文件无差异但有差异文件时的空态浮窗。
811
+ * 迁移自原 src/client/index.ts 的 DiffBarEmpty,语义不改。
812
+ * 作者 ddj 2026-08-20
813
+ */
814
+ /**
815
+ * 空态差异条:提示当前状态 + 查看下一个差异文件 + ⋮(总览/归档/刷新)。
816
+ */
817
+ function DiffBarEmpty(props) {
818
+ const { sum, active, staleCount, onNextFile, onOpenLauncher, onRefresh } = props;
819
+ const [menuOpen, setMenuOpen] = react.default.useState(false);
820
+ const n = sum?.totalFiles ?? 0;
821
+ const tip = active ? staleCount > 0 ? "当前文件有 " + staleCount + " 处差异无法定位(可能已被后续修改影响)" : "当前文件无待处理差异" : "未打开文件";
822
+ const menu = menuOpen ? react.default.createElement(react.default.Fragment, null, react.default.createElement("div", {
823
+ style: {
824
+ position: "fixed",
825
+ inset: 0,
826
+ zIndex: 44
827
+ },
828
+ onClick: () => setMenuOpen(false)
829
+ }), react.default.createElement("div", { className: "edrv-diffbar-menu" }, react.default.createElement("button", {
830
+ className: "edrv-diffmenu-item",
831
+ onClick: () => {
832
+ onOpenLauncher("pending");
833
+ setMenuOpen(false);
834
+ }
835
+ }, "🗂 差异总览"), react.default.createElement("button", {
836
+ className: "edrv-diffmenu-item",
837
+ onClick: () => {
838
+ onOpenLauncher("archive");
839
+ setMenuOpen(false);
840
+ }
841
+ }, "📁 归档"), react.default.createElement("div", { className: "edrv-diffmenu-sep" }), react.default.createElement("button", {
842
+ className: "edrv-diffmenu-item",
843
+ onClick: () => {
844
+ onRefresh();
845
+ setMenuOpen(false);
846
+ }
847
+ }, "⟳ 刷新"))) : null;
848
+ return react.default.createElement("div", {
849
+ className: "edrv-diffbar",
850
+ "data-edrv-diffbox": "1"
851
+ }, react.default.createElement("div", { className: "edrv-diffbar-row" }, react.default.createElement("span", { className: "edrv-diffbar-count" }, "⚠ 差异 " + n + " 文件"), react.default.createElement("span", { className: "edrv-diffbar-file" }, tip), react.default.createElement("button", {
852
+ className: "edrv-pill edrv-pill-keep",
853
+ title: "打开并跳转到下一个差异文件",
854
+ onClick: onNextFile
855
+ }, "查看下一个差异文件"), react.default.createElement("button", {
856
+ className: "edrv-pill edrv-pill-ghost",
857
+ title: "更多操作",
858
+ onClick: () => setMenuOpen((v) => !v)
859
+ }, "⋮")), menu);
860
+ }
861
+ //#endregion
862
+ //#region src/client/ui/DiffLauncher.ts
863
+ /**
864
+ * dsh-vscode-mode client — DiffLauncher:全局差异总览 + 归档 圆角下拉。
865
+ * 迁移自原 src/client/index.ts 的 DiffLauncher,语义不改。
866
+ * 作者 ddj 2026-08-20
867
+ */
868
+ /**
869
+ * 全局差异面板:待处理文件列表(点击打开并跳转)+ 归档浏览/批次回滚。
870
+ * 由状态栏差异 chip / header 角标触发。
871
+ */
872
+ function DiffLauncher(props) {
873
+ const sessionId = props?.sessionId;
874
+ const sum = props?.sum;
875
+ const onClose = props?.onClose;
876
+ const onOpenFile = props?.onOpenFile;
877
+ const [tab, setTab] = react.default.useState(props?.tab === "archive" ? "archive" : "pending");
878
+ const [archives, setArchives] = react.default.useState(null);
879
+ const [archiveDetail, setArchiveDetail] = react.default.useState(null);
880
+ const [archivePath, setArchivePath] = react.default.useState(null);
881
+ const [archiveErr, setArchiveErr] = react.default.useState(null);
882
+ const [error, setError] = react.default.useState(null);
883
+ const loadArchives = () => {
884
+ if (!sessionId) {
885
+ setArchiveErr("会话不存在");
886
+ return;
887
+ }
888
+ rpc("edrv.archiveList", { sessionId }).then((res) => {
889
+ if (res && res.ok && Array.isArray(res.entries)) {
890
+ setArchives(res.entries);
891
+ setArchiveErr(null);
892
+ } else setArchiveErr(res?.error ? String(res.error) : "归档列表读取失败");
893
+ }).catch((e) => setArchiveErr("归档异常:" + String(e)));
894
+ };
895
+ const loadArchiveDetail = (path) => {
896
+ if (!sessionId) return;
897
+ setArchiveDetail(null);
898
+ rpc("edrv.archiveRead", {
899
+ sessionId,
900
+ path
901
+ }).then((res) => {
902
+ if (res && res.ok && Array.isArray(res.batches)) setArchiveDetail(res.batches);
903
+ else setArchiveErr(res?.error ? String(res.error) : "归档详情读取失败");
904
+ }).catch((e) => setArchiveErr("归档异常:" + String(e)));
905
+ };
906
+ react.default.useEffect(() => {
907
+ if (tab !== "archive") return;
908
+ setArchives(null);
909
+ setArchiveDetail(null);
910
+ setArchivePath(null);
911
+ setArchiveErr(null);
912
+ loadArchives();
913
+ }, [tab, sessionId]);
914
+ let body;
915
+ if (tab === "pending") body = sum.pendingFiles.length === 0 ? react.default.createElement("div", { className: "edrv-search-empty" }, "暂无待处理差异(已全部处理)") : sum.pendingFiles.map((f) => react.default.createElement("div", {
916
+ key: f.path,
917
+ className: "edrv-launch-row",
918
+ title: "打开并跳转到差异 " + f.path,
919
+ onClick: () => onOpenFile(f.path)
920
+ }, react.default.createElement("span", { className: "edrv-launch-path" }, f.path), react.default.createElement("span", { className: "edrv-launch-cnt" }, "待处理 " + f.pending + " 处"), react.default.createElement("button", {
921
+ className: "edrv-btn-mini edrv-btn-keep",
922
+ onClick: (e) => {
923
+ e.stopPropagation();
924
+ onOpenFile(f.path);
925
+ }
926
+ }, "打开")));
927
+ else if (archiveErr) body = react.default.createElement("div", {
928
+ className: "edrv-search-empty",
929
+ style: { color: "var(--dsw-alias-state-error-primary,#d9534f)" }
930
+ }, "错误:" + String(archiveErr));
931
+ else if (archives === null) body = react.default.createElement("div", { className: "edrv-search-empty" }, "加载中…");
932
+ else if (archives.length === 0) body = react.default.createElement("div", { className: "edrv-search-empty" }, "暂无归档记录");
933
+ else {
934
+ const inner = archives.map((e) => {
935
+ const open = archivePath === e.path;
936
+ let detailEl = null;
937
+ if (open) {
938
+ if (archiveDetail === null) detailEl = react.default.createElement("div", { style: {
939
+ padding: 8,
940
+ fontSize: 11,
941
+ color: "var(--dsw-alias-label-tertiary,#888)"
942
+ } }, "加载中…");
943
+ else detailEl = archiveDetail.filter((b) => b.path === e.path).map((b) => (Array.isArray(b.records) ? b.records : []).map((rec) => {
944
+ const lines = [];
945
+ const hunks = Array.isArray(rec.hunks) ? rec.hunks : [];
946
+ for (const h of hunks) {
947
+ const oldLs = (h.oldText ?? "").split("\n");
948
+ const newLs = (h.newText ?? "").split("\n");
949
+ for (const t of oldLs) lines.push({
950
+ k: "old",
951
+ t
952
+ });
953
+ if (oldLs.length && newLs.length) lines.push({
954
+ k: "sep",
955
+ t: ""
956
+ });
957
+ for (const t of newLs) lines.push({
958
+ k: "new",
959
+ t
960
+ });
961
+ }
962
+ const sm = rec.summary || {
963
+ accepted: 0,
964
+ rejected: 0,
965
+ pending: 0,
966
+ superseded: false
967
+ };
968
+ return react.default.createElement("div", {
969
+ key: rec.callId,
970
+ style: {
971
+ padding: "5px 8px",
972
+ borderTop: "1px solid var(--dsw-alias-border-l1,#333)"
973
+ }
974
+ }, react.default.createElement("div", { style: {
975
+ display: "flex",
976
+ gap: 8,
977
+ alignItems: "center",
978
+ flexWrap: "wrap",
979
+ fontSize: 11,
980
+ marginBottom: 2
981
+ } }, react.default.createElement("span", { style: {
982
+ color: "var(--dsw-alias-label-primary,#ddd)",
983
+ fontWeight: 600
984
+ } }, rec.toolName === "write" ? rec.create ? "新建" : "写入" : "编辑"), react.default.createElement("span", { style: { color: "var(--dsw-alias-state-success-primary,#2e9e44)" } }, "采纳 " + sm.accepted), react.default.createElement("span", { style: { color: "var(--dsw-alias-state-error-primary,#d9534f)" } }, "拒绝 " + sm.rejected), rec.superseded ? react.default.createElement("span", { style: { color: "var(--dsw-alias-state-warn-primary,#b7791f)" } }, "被覆盖") : null, react.default.createElement("span", { style: { color: "var(--dsw-alias-label-tertiary,#888)" } }, new Date(rec.at).toLocaleTimeString())), react.default.createElement("pre", { style: {
985
+ margin: 0,
986
+ whiteSpace: "pre",
987
+ fontFamily: MONO.fontFamily,
988
+ fontSize: 11,
989
+ lineHeight: 1.5,
990
+ overflowX: "auto"
991
+ } }, lines.map((ln, i) => react.default.createElement("div", {
992
+ key: i,
993
+ style: {
994
+ background: ln.k === "old" ? "rgba(239,68,68,.10)" : ln.k === "new" ? "rgba(34,197,94,.10)" : "transparent",
995
+ color: ln.k === "old" ? "var(--dsw-alias-state-error-primary,#d9534f)" : ln.k === "new" ? "var(--dsw-alias-state-success-primary,#2e9e44)" : "var(--dsw-alias-label-tertiary,#888)"
996
+ }
997
+ }, (ln.k === "old" ? "- " : ln.k === "new" ? "+ " : "") + ln.t))));
998
+ }));
999
+ }
1000
+ return react.default.createElement("div", {
1001
+ key: e.at + e.path,
1002
+ style: {
1003
+ border: "1px solid var(--dsw-alias-border-l1,#333)",
1004
+ borderRadius: 8,
1005
+ overflow: "hidden"
1006
+ }
1007
+ }, react.default.createElement("div", {
1008
+ style: {
1009
+ display: "flex",
1010
+ gap: 8,
1011
+ alignItems: "center",
1012
+ padding: "6px 8px",
1013
+ background: "var(--dsw-alias-bg-layer-1,transparent)",
1014
+ cursor: "pointer",
1015
+ flexWrap: "wrap"
1016
+ },
1017
+ onClick: () => {
1018
+ if (open) {
1019
+ setArchivePath(null);
1020
+ setArchiveDetail(null);
1021
+ } else {
1022
+ setArchivePath(e.path);
1023
+ loadArchiveDetail(e.path);
1024
+ }
1025
+ }
1026
+ }, react.default.createElement("span", { style: {
1027
+ flex: 1,
1028
+ fontSize: 12,
1029
+ color: "var(--dsw-alias-label-primary,#ddd)",
1030
+ wordBreak: "break-all"
1031
+ } }, e.path), e.batch != null ? react.default.createElement("span", { style: {
1032
+ fontSize: 11,
1033
+ color: "var(--dsw-alias-label-tertiary,#888)"
1034
+ } }, "批次 " + e.batch) : null, e.reason ? react.default.createElement("span", { style: {
1035
+ fontSize: 11,
1036
+ color: "var(--dsw-alias-state-warn-primary,#b7791f)"
1037
+ } }, e.reason) : null, react.default.createElement("span", { style: {
1038
+ fontSize: 11,
1039
+ color: "var(--dsw-alias-label-tertiary,#888)"
1040
+ } }, new Date(e.at).toLocaleString()), react.default.createElement("span", { style: {
1041
+ fontSize: 11,
1042
+ color: "var(--dsw-alias-label-tertiary,#888)"
1043
+ } }, "采纳 " + e.summary.accepted + " · 拒绝 " + e.summary.rejected + (e.summary.superseded ? " · 覆盖 " + e.summary.superseded : "")), react.default.createElement("button", {
1044
+ onClick: (ev) => {
1045
+ ev.stopPropagation();
1046
+ if (e.batch == null) return;
1047
+ if (!window.confirm("将文件恢复到批次 " + e.batch + " 之前的状态?(当前活跃差异将一并归档)")) return;
1048
+ rpc("edrv.rollback", {
1049
+ sessionId,
1050
+ path: e.path,
1051
+ batch: e.batch
1052
+ }).then((res) => {
1053
+ if (res && res.ok) {
1054
+ setError(null);
1055
+ window.dispatchEvent(new CustomEvent("edrv:refresh"));
1056
+ setArchivePath(null);
1057
+ setArchiveDetail(null);
1058
+ loadArchives();
1059
+ } else setError(res?.error ? String(res.error) : "回滚失败");
1060
+ }).catch((err) => setError("回滚异常:" + String(err)));
1061
+ },
1062
+ style: btn(false)
1063
+ }, "回滚")), detailEl);
1064
+ });
1065
+ body = react.default.createElement("div", { className: "edrv-launch-arch" }, inner);
1066
+ }
1067
+ return react.default.createElement("div", {
1068
+ className: "edrv-launch",
1069
+ "data-edrv-launch": "1"
1070
+ }, react.default.createElement("div", { className: "edrv-launch-head" }, react.default.createElement("span", { style: {
1071
+ fontSize: 12,
1072
+ fontWeight: 600,
1073
+ color: "var(--dsw-alias-label-primary,#eee)",
1074
+ marginRight: 4
1075
+ } }, "差异"), react.default.createElement("button", {
1076
+ className: "edrv-launch-tab" + (tab === "pending" ? " on" : ""),
1077
+ onClick: () => setTab("pending")
1078
+ }, "待处理" + (sum.totalFiles ? " (" + sum.totalFiles + ")" : "")), react.default.createElement("button", {
1079
+ className: "edrv-launch-tab" + (tab === "archive" ? " on" : ""),
1080
+ onClick: () => setTab("archive")
1081
+ }, "归档"), react.default.createElement("span", { style: { flex: 1 } }), error ? react.default.createElement("span", { style: {
1082
+ fontSize: 11,
1083
+ color: "var(--dsw-alias-state-error-primary,#d9534f)",
1084
+ overflow: "hidden",
1085
+ textOverflow: "ellipsis",
1086
+ whiteSpace: "nowrap",
1087
+ maxWidth: "40%"
1088
+ } }, "错误:" + String(error)) : null, react.default.createElement("button", {
1089
+ onClick: onClose,
1090
+ style: btn(false)
1091
+ }, "✕ 关闭")), react.default.createElement("div", { className: "edrv-launch-body" }, body));
1092
+ }
1093
+ //#endregion
1094
+ //#region src/client/ui/EditorView.ts
1095
+ /**
1096
+ * dsh-vscode-mode client — EditorView:中央 VSCode 式文件编辑器(编排层)。
1097
+ * 迁移自原 src/client/index.ts 的 EditorView,语义不改;差异自绘已抽到 monaco/diffRender。
1098
+ * 职责:页签/QuickOpen/Monaco 编辑器/差异审查(DiffBox/Launcher/Badge 事件装配)/状态栏/自动保存。
1099
+ * 作者 ddj 2026-08-20
1100
+ */
1101
+ /**
1102
+ * 中央编辑区:文件页签(脏点/关闭/打开路径)+ Ctrl+P 搜索 + Monaco 编辑器 +
1103
+ * 底部差异条(DiffBox/空态)+ 全局差异下拉(DiffLauncher)+ 状态栏。
1104
+ * @param props.sessionId 会话 id
1105
+ * @param props.schedule 延时调度(ctx.timeout)
1106
+ */
1107
+ function EditorView(props) {
1108
+ const sessionId = props?.sessionId;
1109
+ const schedule = props.schedule;
1110
+ const [monaco, setMonaco] = react.default.useState(null);
1111
+ const [monacoErr, setMonacoErr] = react.default.useState(null);
1112
+ const [records, setRecords] = react.default.useState({});
1113
+ const [tabs, setTabs] = react.default.useState([]);
1114
+ const [active, setActive] = react.default.useState(null);
1115
+ const [dirtyMap, setDirtyMap] = react.default.useState({});
1116
+ const [content, setContent] = react.default.useState(null);
1117
+ const [status, setStatus] = react.default.useState("");
1118
+ const [error, setError] = react.default.useState(null);
1119
+ const [openInput, setOpenInput] = react.default.useState(false);
1120
+ const [pathDraft, setPathDraft] = react.default.useState("");
1121
+ const [cursor, setCursor] = react.default.useState("");
1122
+ const [launcherOpen, setLauncherOpen] = react.default.useState(false);
1123
+ const [launcherTab, setLauncherTab] = react.default.useState("pending");
1124
+ const [hoverAct, setHoverAct] = react.default.useState(null);
1125
+ const [diffIdx, setDiffIdx] = react.default.useState(0);
1126
+ const [fileIdx, setFileIdx] = react.default.useState(0);
1127
+ const editorRef = react.default.useRef(null);
1128
+ const monacoRef = react.default.useRef(null);
1129
+ const modelsRef = react.default.useRef(/* @__PURE__ */ new Map());
1130
+ const saveTimerRef = react.default.useRef(null);
1131
+ const loadSeqRef = react.default.useRef(0);
1132
+ const programmaticRef = react.default.useRef(false);
1133
+ const bootRef = react.default.useRef(false);
1134
+ const pendingFocusRef = react.default.useRef(null);
1135
+ const hoverRegionsRef = react.default.useRef([]);
1136
+ const lineRegionMapRef = react.default.useRef(/* @__PURE__ */ new Map());
1137
+ const hoverKeyRef = react.default.useRef(null);
1138
+ const hoverTopRef = react.default.useRef(null);
1139
+ const hideTimerRef = react.default.useRef(null);
1140
+ const batchBusyRef = react.default.useRef(false);
1141
+ const diffRendererRef = react.default.useRef(null);
1142
+ if (!diffRendererRef.current) diffRendererRef.current = createDiffRenderer((sid, t) => dbg(sid, t));
1143
+ const currentRecords = react.default.useMemo(() => {
1144
+ const list = [];
1145
+ for (const rec of Object.values(records)) if (rec.path === active) list.push(rec);
1146
+ return list;
1147
+ }, [records, active]);
1148
+ const regions = react.default.useMemo(() => diffRegions(currentRecords, content).filter((r) => !r.superseded), [currentRecords, content]);
1149
+ const pendingRegions = react.default.useMemo(() => regions.filter((r) => r.status === ST.PENDING && !r.stale), [regions]);
1150
+ const staleRegions = react.default.useMemo(() => regions.filter((r) => r.status === ST.PENDING && r.stale), [regions]);
1151
+ hoverRegionsRef.current = pendingRegions;
1152
+ lineRegionMapRef.current = react.default.useMemo(() => {
1153
+ const map = /* @__PURE__ */ new Map();
1154
+ for (const r of pendingRegions) {
1155
+ if (r.start === void 0 || r.end === void 0) continue;
1156
+ const last = Math.max(r.start, r.end - 1);
1157
+ for (let ln = r.start; ln <= last; ln++) if (!map.has(ln)) map.set(ln, r);
1158
+ }
1159
+ return map;
1160
+ }, [pendingRegions]);
1161
+ const sum = react.default.useMemo(() => summarize(Object.values(records)), [records]);
1162
+ const addTab = (path, select) => {
1163
+ setTabs((prev) => {
1164
+ if (prev.some((t) => t.path === path)) return prev;
1165
+ return prev.concat([{ path }]);
1166
+ });
1167
+ if (select) setActive(path);
1168
+ };
1169
+ const closeTab = (path) => {
1170
+ flushSave();
1171
+ setTabs((prev) => {
1172
+ const idx = prev.findIndex((t) => t.path === path);
1173
+ if (idx < 0) return prev;
1174
+ const next = prev.filter((t) => t.path !== path);
1175
+ if (active === path) setActive(next[idx] ? next[idx].path : next[idx - 1] ? next[idx - 1].path : null);
1176
+ return next;
1177
+ });
1178
+ };
1179
+ const refreshRecords = () => {
1180
+ if (!sessionId) return;
1181
+ rpc("edrv.list", { sessionId }).then((res) => {
1182
+ if (!res || !res.ok || !Array.isArray(res.records)) return;
1183
+ const map = {};
1184
+ for (const r of res.records) map[r.callId] = r;
1185
+ setRecords(map);
1186
+ }).catch((e) => setError("list异常:" + String(e)));
1187
+ };
1188
+ const loadContent = (path, sid) => {
1189
+ const seq = ++loadSeqRef.current;
1190
+ rpc("edrv.read", {
1191
+ sessionId: sid,
1192
+ path
1193
+ }).then((res) => {
1194
+ if (seq !== loadSeqRef.current || path !== active) return;
1195
+ if (res && res.ok) {
1196
+ setContent(res.content);
1197
+ setStatus("已加载");
1198
+ } else {
1199
+ setError(res?.error ? String(res.error) : "读取失败");
1200
+ setStatus("读取失败");
1201
+ }
1202
+ }).catch((e) => {
1203
+ if (seq === loadSeqRef.current) {
1204
+ setError("read异常:" + String(e));
1205
+ setStatus("读取失败");
1206
+ }
1207
+ });
1208
+ };
1209
+ react.default.useEffect(() => {
1210
+ if (!sessionId) return;
1211
+ refreshRecords();
1212
+ const t = setInterval(refreshRecords, 5e3);
1213
+ const onRefresh = () => refreshRecords();
1214
+ window.addEventListener("edrv:refresh", onRefresh);
1215
+ return () => {
1216
+ clearInterval(t);
1217
+ window.removeEventListener("edrv:refresh", onRefresh);
1218
+ };
1219
+ }, [sessionId]);
1220
+ react.default.useEffect(() => {
1221
+ const onOpen = (e) => {
1222
+ const p = e?.detail?.path;
1223
+ if (p) addTab(p, true);
1224
+ };
1225
+ const onShowLauncher = () => setLauncherOpen(true);
1226
+ window.addEventListener("edrv:open-editor", onOpen);
1227
+ window.addEventListener("edrv:show-launcher", onShowLauncher);
1228
+ return () => {
1229
+ window.removeEventListener("edrv:open-editor", onOpen);
1230
+ window.removeEventListener("edrv:show-launcher", onShowLauncher);
1231
+ };
1232
+ }, [sessionId]);
1233
+ react.default.useEffect(() => {
1234
+ if (bootRef.current || !sessionId) return;
1235
+ bootRef.current = true;
1236
+ try {
1237
+ const raw = localStorage.getItem("edrv.editor.v2." + String(sessionId));
1238
+ if (raw) {
1239
+ const saved = JSON.parse(raw);
1240
+ if (Array.isArray(saved.tabs) && saved.tabs.length) {
1241
+ setTabs(saved.tabs.map((p) => ({ path: p })));
1242
+ if (typeof saved.active === "string") setActive(saved.active);
1243
+ }
1244
+ }
1245
+ } catch (e) {}
1246
+ }, [sessionId]);
1247
+ react.default.useEffect(() => {
1248
+ if (!sessionId) return;
1249
+ try {
1250
+ localStorage.setItem("edrv.editor.v2." + String(sessionId), JSON.stringify({
1251
+ tabs: tabs.map((t) => t.path),
1252
+ active
1253
+ }));
1254
+ } catch (e) {}
1255
+ }, [
1256
+ tabs,
1257
+ active,
1258
+ sessionId
1259
+ ]);
1260
+ react.default.useEffect(() => {
1261
+ if (!active) return;
1262
+ setContent(null);
1263
+ setStatus("加载中…");
1264
+ loadContent(active, sessionId);
1265
+ }, [active, sessionId]);
1266
+ react.default.useEffect(() => {
1267
+ if (monaco || monacoErr) return;
1268
+ let alive = true;
1269
+ loadMonaco().then((m) => {
1270
+ if (alive) setMonaco(m);
1271
+ }).catch((e) => {
1272
+ if (alive) {
1273
+ setMonacoErr(String(e?.message ?? e));
1274
+ setStatus("Monaco 不可用");
1275
+ }
1276
+ });
1277
+ return () => {
1278
+ alive = false;
1279
+ };
1280
+ }, [monaco, monacoErr]);
1281
+ monacoRef.current = monaco;
1282
+ const getModel = (path, text) => {
1283
+ const cache = modelsRef.current;
1284
+ let model = cache.get(path);
1285
+ if (!model) {
1286
+ model = window.monaco.editor.createModel(text ?? "", langOf(path), window.monaco.Uri.parse("edrv:///" + encodeURI(path)));
1287
+ cache.set(path, model);
1288
+ } else if (text !== void 0 && model.getValue() !== text) {
1289
+ programmaticRef.current = true;
1290
+ model.setValue(text);
1291
+ programmaticRef.current = false;
1292
+ }
1293
+ return model;
1294
+ };
1295
+ const flushSave = () => {
1296
+ if (saveTimerRef.current) {
1297
+ saveTimerRef.current();
1298
+ saveTimerRef.current = null;
1299
+ }
1300
+ };
1301
+ const doSave = (silent) => {
1302
+ const ed = editorRef.current;
1303
+ if (!active || !ed) return;
1304
+ const text = ed.getValue();
1305
+ if (!silent) setStatus("保存中…");
1306
+ rpc("edrv.save", {
1307
+ sessionId,
1308
+ path: active,
1309
+ content: text
1310
+ }).then((res) => {
1311
+ if (res && res.ok) {
1312
+ setStatus("已保存 " + (/* @__PURE__ */ new Date()).toTimeString().slice(0, 8));
1313
+ setContent(text);
1314
+ setDirtyMap((d) => Object.assign({}, d, { [active]: false }));
1315
+ refreshRecords();
1316
+ emitRefresh();
1317
+ } else {
1318
+ setStatus("保存失败");
1319
+ setError(res?.error ? String(res.error) : "保存失败");
1320
+ }
1321
+ }).catch((e) => {
1322
+ setStatus("保存失败");
1323
+ setError("保存异常:" + String(e));
1324
+ });
1325
+ };
1326
+ const onEdit = () => {
1327
+ if (!editorRef.current || !active) return;
1328
+ setDirtyMap((d) => Object.assign({}, d, { [active]: true }));
1329
+ setStatus("编辑中…");
1330
+ if (saveTimerRef.current) saveTimerRef.current();
1331
+ saveTimerRef.current = schedule(() => doSave(true), 700);
1332
+ };
1333
+ react.default.useEffect(() => {
1334
+ if (!monaco || !editorRef.current || !active || content === null) return;
1335
+ const ed = editorRef.current;
1336
+ const model = getModel(active, content);
1337
+ if (ed.getModel() !== model) ed.setModel(model);
1338
+ }, [
1339
+ monaco,
1340
+ active,
1341
+ content
1342
+ ]);
1343
+ react.default.useEffect(() => {
1344
+ if (!monaco || !editorRef.current || !active || content === null) return;
1345
+ diffRendererRef.current.render(monaco, editorRef.current, pendingRegions, sessionId);
1346
+ }, [
1347
+ monaco,
1348
+ active,
1349
+ content,
1350
+ pendingRegions
1351
+ ]);
1352
+ react.default.useEffect(() => () => {
1353
+ flushSave();
1354
+ diffRendererRef.current?.dispose?.();
1355
+ if (editorRef.current) {
1356
+ editorRef.current.dispose();
1357
+ editorRef.current = null;
1358
+ }
1359
+ for (const m of modelsRef.current.values()) m.dispose();
1360
+ modelsRef.current.clear();
1361
+ const root = editorRef.current && editorRef.current.getDomNode ? editorRef.current.getDomNode() : null;
1362
+ if (root) {
1363
+ const ov = root.querySelector(".edrv-minus-overlay");
1364
+ if (ov) ov.remove();
1365
+ }
1366
+ }, []);
1367
+ const ensureEditor = react.default.useCallback((node) => {
1368
+ if (!node) {
1369
+ if (editorRef.current) {
1370
+ editorRef.current.dispose();
1371
+ editorRef.current = null;
1372
+ }
1373
+ return;
1374
+ }
1375
+ if (editorRef.current || !monacoRef.current) return;
1376
+ const m = monacoRef.current;
1377
+ const ed = m.editor.create(node, {
1378
+ value: "",
1379
+ language: "plaintext",
1380
+ theme: "vs",
1381
+ fontFamily: "var(--ds-font-family-code, ui-monospace, monospace)",
1382
+ fontSize: 13,
1383
+ lineHeight: 20,
1384
+ minimap: {
1385
+ enabled: true,
1386
+ scale: 1
1387
+ },
1388
+ glyphMargin: true,
1389
+ lineDecorationsWidth: 16,
1390
+ automaticLayout: true,
1391
+ scrollBeyondLastLine: false,
1392
+ wordWrap: "off",
1393
+ tabSize: 2,
1394
+ renderWhitespace: "selection",
1395
+ smoothScrolling: true,
1396
+ cursorBlinking: "smooth",
1397
+ padding: { top: 8 }
1398
+ });
1399
+ ed.addCommand(m.KeyMod.CtrlCmd | m.KeyCode.KeyS, () => {
1400
+ flushSave();
1401
+ doSave(false);
1402
+ });
1403
+ ed.onDidChangeModelContent(() => {
1404
+ if (!ed.getModel() || programmaticRef.current) return;
1405
+ onEdit();
1406
+ });
1407
+ ed.onDidChangeCursorPosition((e) => {
1408
+ setCursor("Ln " + e.position.lineNumber + ", Col " + e.position.column);
1409
+ });
1410
+ const hideSoon = () => {
1411
+ if (hideTimerRef.current) return;
1412
+ hideTimerRef.current = setTimeout(() => {
1413
+ hideTimerRef.current = null;
1414
+ hoverKeyRef.current = null;
1415
+ hoverTopRef.current = null;
1416
+ setHoverAct(null);
1417
+ }, 180);
1418
+ };
1419
+ ed.onMouseMove((e) => {
1420
+ const line = e?.target?.position?.lineNumber;
1421
+ const map = lineRegionMapRef.current;
1422
+ if (!line || !map.size) {
1423
+ hideSoon();
1424
+ return;
1425
+ }
1426
+ const hit = map.get(line);
1427
+ if (!hit) {
1428
+ hideSoon();
1429
+ return;
1430
+ }
1431
+ const key = callIdAttr(hit.callId, hit.idx);
1432
+ const top = Math.max(0, ed.getTopForLineNumber(Math.max(1, hit.start)) - ed.getScrollTop());
1433
+ if (hoverKeyRef.current === key && hoverTopRef.current === top) return;
1434
+ hoverKeyRef.current = key;
1435
+ hoverTopRef.current = top;
1436
+ if (hideTimerRef.current) {
1437
+ clearTimeout(hideTimerRef.current);
1438
+ hideTimerRef.current = null;
1439
+ }
1440
+ setHoverAct({
1441
+ region: hit,
1442
+ top
1443
+ });
1444
+ });
1445
+ ed.onMouseLeave(hideSoon);
1446
+ editorRef.current = ed;
1447
+ }, []);
1448
+ react.default.useEffect(() => {
1449
+ const pf = pendingFocusRef.current;
1450
+ if (!pf || pf.path !== active || content === null) return;
1451
+ const target = pf.region || pendingRegions[0];
1452
+ const ed = editorRef.current;
1453
+ if (!target || !ed) return;
1454
+ pendingFocusRef.current = null;
1455
+ ed.revealLineInCenter(Math.max(1, target.start ?? 1));
1456
+ ed.setPosition({
1457
+ lineNumber: Math.max(1, target.start ?? 1),
1458
+ column: 1
1459
+ });
1460
+ ed.focus();
1461
+ }, [
1462
+ active,
1463
+ content,
1464
+ pendingRegions
1465
+ ]);
1466
+ const jumpTo = (region) => {
1467
+ if (editorRef.current) {
1468
+ editorRef.current.revealLineInCenter(Math.max(1, region?.start ?? 1));
1469
+ editorRef.current.setPosition({
1470
+ lineNumber: Math.max(1, region?.start ?? 1),
1471
+ column: 1
1472
+ });
1473
+ editorRef.current.focus();
1474
+ } else pendingFocusRef.current = {
1475
+ path: active,
1476
+ region
1477
+ };
1478
+ };
1479
+ react.default.useEffect(() => {
1480
+ if (diffIdx >= pendingRegions.length && pendingRegions.length > 0) setDiffIdx(pendingRegions.length - 1);
1481
+ else if (pendingRegions.length === 0) setDiffIdx(0);
1482
+ }, [pendingRegions.length]);
1483
+ react.default.useEffect(() => {
1484
+ const i = sum.pendingFiles.findIndex((f) => f.path === active);
1485
+ if (i >= 0) {
1486
+ if (i !== fileIdx) setFileIdx(i);
1487
+ }
1488
+ }, [sum.pendingFiles, active]);
1489
+ const gotoDiff = (delta) => {
1490
+ if (!pendingRegions.length) return;
1491
+ const next = (diffIdx + delta + pendingRegions.length) % pendingRegions.length;
1492
+ setDiffIdx(next);
1493
+ jumpTo(pendingRegions[next]);
1494
+ };
1495
+ const gotoFile = (delta) => {
1496
+ if (!sum.pendingFiles.length) return;
1497
+ const next = (fileIdx + delta + sum.pendingFiles.length) % sum.pendingFiles.length;
1498
+ setFileIdx(next);
1499
+ openFile(sum.pendingFiles[next].path, true);
1500
+ };
1501
+ const reloadFile = () => {
1502
+ if (!active) return;
1503
+ loadContent(active, sessionId);
1504
+ refreshRecords();
1505
+ };
1506
+ /**
1507
+ * 对单个差异区域执行采纳/不采纳。
1508
+ * @author ddj 2026年08月20号
1509
+ * @param region 差异区域(callId/idx/create)
1510
+ * @param reject true=不采纳(回滚),false=采纳
1511
+ * @param silent 批量时抑制中间 reload/报错,由批量方统一收尾
1512
+ * @returns Promise<boolean> 是否成功
1513
+ */
1514
+ const actHunk = (region, reject, silent) => {
1515
+ return rpc(reject ? "edrv.reject" : "edrv.accept", {
1516
+ sessionId,
1517
+ callId: region.callId,
1518
+ scope: region.create ? "call" : "hunk",
1519
+ hunkIndex: region.idx
1520
+ }).then((res) => {
1521
+ if (res && res.ok) {
1522
+ setRecords((prev) => Object.assign({}, prev, { [region.callId]: res.record }));
1523
+ if (!silent) {
1524
+ reloadFile();
1525
+ emitRefresh();
1526
+ }
1527
+ return true;
1528
+ }
1529
+ if (!silent) setError(res?.error ? String(res.error) : "操作失败");
1530
+ return false;
1531
+ }).catch((e) => {
1532
+ if (!silent) setError("操作异常:" + String(e));
1533
+ return false;
1534
+ });
1535
+ };
1536
+ const acceptFile = () => {
1537
+ for (const r of pendingRegions) actHunk(r, false);
1538
+ };
1539
+ const undoFile = () => {
1540
+ for (const r of [...pendingRegions].reverse()) actHunk(r, true);
1541
+ };
1542
+ const allPending = react.default.useMemo(() => {
1543
+ const out = [];
1544
+ for (const rec of Object.values(records)) {
1545
+ if (rec.superseded === true) continue;
1546
+ const perHunk = Array.isArray(rec.decisions?.perHunk) ? rec.decisions.perHunk : [];
1547
+ const hunks = Array.isArray(rec.hunks) ? rec.hunks : [];
1548
+ for (let i = 0; i < hunks.length; i++) if ((perHunk.length ? perHunk[i] : rec.decisions.call) === "pending" && !noopHunk(rec, hunks[i])) out.push({
1549
+ callId: rec.callId,
1550
+ idx: i,
1551
+ create: rec.create === true,
1552
+ at: rec.at
1553
+ });
1554
+ }
1555
+ return out;
1556
+ }, [records]);
1557
+ /**
1558
+ * 批量处理所有差异文件:采纳全部 / 不采纳全部。不采纳按 at 降序(新改先回滚)串行执行。
1559
+ * @author ddj 2026年08月20号
1560
+ * @param reject true=全部不采纳(回滚),false=全部采纳
1561
+ */
1562
+ const actAllPending = (reject) => {
1563
+ if (batchBusyRef.current || !allPending.length) return;
1564
+ batchBusyRef.current = true;
1565
+ const list = reject ? [...allPending].sort((a, b) => a.at < b.at ? 1 : a.at > b.at ? -1 : b.idx - a.idx) : allPending;
1566
+ let ok = 0, fail = 0;
1567
+ const run = async () => {
1568
+ for (const r of list) if (await actHunk(r, reject, true)) ok++;
1569
+ else fail++;
1570
+ batchBusyRef.current = false;
1571
+ reloadFile();
1572
+ emitRefresh();
1573
+ setStatus((reject ? "已不采纳 " : "已采纳 ") + ok + " 处差异" + (fail ? "," + fail + " 处失败" : ""));
1574
+ if (fail) setError(fail + " 处差异处理失败(可能已被后续修改影响),可刷新后重试");
1575
+ };
1576
+ run();
1577
+ };
1578
+ const acceptAllFiles = () => actAllPending(false);
1579
+ const undoAllFiles = () => actAllPending(true);
1580
+ const rollbackFile = () => {
1581
+ if (!active) return;
1582
+ if (!window.confirm("回滚当前文件到修改前状态?(相关差异将归档)")) return;
1583
+ rpc("edrv.rollback", {
1584
+ sessionId,
1585
+ path: active
1586
+ }).then((res) => {
1587
+ if (res && res.ok) {
1588
+ setStatus("已回滚");
1589
+ reloadFile();
1590
+ emitRefresh();
1591
+ } else setError(res?.error ? String(res.error) : "回滚失败");
1592
+ }).catch((e) => setError("回滚异常:" + String(e)));
1593
+ };
1594
+ const openFile = (path, focusDiff) => {
1595
+ if (!path) return;
1596
+ addTab(path, true);
1597
+ if (focusDiff) pendingFocusRef.current = {
1598
+ path,
1599
+ region: null
1600
+ };
1601
+ };
1602
+ const openPath = () => {
1603
+ const p = (pathDraft || "").trim();
1604
+ if (!p) return;
1605
+ setStatus("打开中…");
1606
+ rpc("edrv.read", {
1607
+ sessionId,
1608
+ path: p
1609
+ }).then((res) => {
1610
+ if (res && res.ok) {
1611
+ openFile(p, false);
1612
+ setOpenInput(false);
1613
+ setPathDraft("");
1614
+ setStatus("已打开");
1615
+ setError(null);
1616
+ } else {
1617
+ setStatus("打开失败");
1618
+ setError(res?.error ? String(res.error) : "打开失败");
1619
+ }
1620
+ }).catch((e) => {
1621
+ setStatus("打开失败");
1622
+ setError("打开异常:" + String(e));
1623
+ });
1624
+ };
1625
+ const tabsEl = react.default.createElement("div", {
1626
+ className: "edrv-tabs",
1627
+ style: {
1628
+ flex: "1 1 auto",
1629
+ minWidth: 0
1630
+ }
1631
+ }, tabs.map((t) => react.default.createElement("div", {
1632
+ key: t.path,
1633
+ className: "edrv-tab" + (t.path === active ? " edrv-tab-active" : ""),
1634
+ title: t.path,
1635
+ onClick: () => {
1636
+ if (t.path !== active) {
1637
+ flushSave();
1638
+ setActive(t.path);
1639
+ }
1640
+ }
1641
+ }, react.default.createElement("span", { style: {
1642
+ overflow: "hidden",
1643
+ textOverflow: "ellipsis",
1644
+ maxWidth: 180
1645
+ } }, t.path.split(/[\\/]/).pop() || t.path), dirtyMap[t.path] ? react.default.createElement("span", { className: "edrv-tab-dot" }) : null, react.default.createElement("span", {
1646
+ className: "edrv-tab-x",
1647
+ onClick: (e) => {
1648
+ e.stopPropagation();
1649
+ closeTab(t.path);
1650
+ }
1651
+ }, "×"))), openInput ? react.default.createElement("input", {
1652
+ className: "edrv-path-input",
1653
+ autoFocus: true,
1654
+ placeholder: "输入工作区相对/绝对路径,回车打开",
1655
+ value: pathDraft,
1656
+ onChange: (e) => setPathDraft(e.target.value),
1657
+ onKeyDown: (e) => {
1658
+ if (e.key === "Enter") openPath();
1659
+ if (e.key === "Escape") setOpenInput(false);
1660
+ }
1661
+ }) : react.default.createElement("button", {
1662
+ className: "edrv-tab-add",
1663
+ title: "打开文件(输入路径)",
1664
+ onClick: () => setOpenInput(true)
1665
+ }, "+"));
1666
+ const pathBar = react.default.createElement("div", {
1667
+ className: "edrv-pathbar",
1668
+ title: active || ""
1669
+ }, react.default.createElement("span", { className: "edrv-pb-name" }, active ? String(active).split(/[\\/]/).pop() : "未打开文件"), react.default.createElement("span", { className: "edrv-pb-full" }, active || "使用右上搜索框 (Ctrl+P) 打开文件"));
1670
+ const tabRow = react.default.createElement("div", { style: {
1671
+ display: "flex",
1672
+ alignItems: "center",
1673
+ background: "var(--dsw-alias-bg-layer-1,transparent)",
1674
+ flexShrink: 0
1675
+ } }, tabsEl, react.default.createElement(QuickOpen, {
1676
+ sessionId,
1677
+ onOpen: (p) => openFile(p, false)
1678
+ }));
1679
+ const statusBar = react.default.createElement("div", { className: "edrv-statusbar" }, react.default.createElement("span", null, "编辑"), react.default.createElement("span", null, active ? langOf(active) : ""), react.default.createElement("span", null, cursor), status ? react.default.createElement("span", null, status) : null, react.default.createElement("span", { style: { flex: 1 } }), sum.totalFiles > 0 ? react.default.createElement("button", {
1680
+ className: "edrv-diffchip",
1681
+ title: "打开差异总览/归档",
1682
+ onClick: () => setLauncherOpen((o) => !o)
1683
+ }, "⚠ 差异 " + sum.totalFiles + " 文件") : null, react.default.createElement("button", {
1684
+ className: "edrv-chip-btn",
1685
+ title: "刷新",
1686
+ onClick: reloadFile
1687
+ }, "⟳"));
1688
+ const otherFiles = sum.pendingFiles.filter((f) => f.path !== active);
1689
+ let body;
1690
+ if (!monaco && !monacoErr) body = react.default.createElement("div", { className: "edrv-empty" }, "正在加载 Monaco 编辑器…");
1691
+ else if (monacoErr) body = react.default.createElement("div", { className: "edrv-empty" }, react.default.createElement("div", null, "Monaco 编辑器加载失败:" + String(monacoErr)), react.default.createElement("div", { style: { fontSize: 11 } }, "请确认插件包 assets/vendor/monaco 完整(/edrv/vendor 路由可达)"));
1692
+ else if (!active) body = react.default.createElement("div", { className: "edrv-empty" }, react.default.createElement("div", null, "暂无打开的文件"), react.default.createElement("div", { style: { fontSize: 12 } }, "使用右上搜索框 (Ctrl+P) 打开工作区文件;agent 修改文件后顶部会出现差异角标"));
1693
+ else if (content === null) body = react.default.createElement("div", { className: "edrv-empty" }, "加载中…");
1694
+ else body = react.default.createElement("div", { className: "edrv-monaco-host" }, react.default.createElement("div", { ref: ensureEditor }));
1695
+ const hoverEl = hoverAct && !launcherOpen ? react.default.createElement("div", {
1696
+ className: "edrv-hoveract",
1697
+ style: {
1698
+ top: hoverAct.top,
1699
+ right: 12
1700
+ },
1701
+ onMouseEnter: () => {
1702
+ if (hideTimerRef.current) {
1703
+ clearTimeout(hideTimerRef.current);
1704
+ hideTimerRef.current = null;
1705
+ }
1706
+ },
1707
+ onMouseLeave: () => {
1708
+ const t = setTimeout(() => {
1709
+ hideTimerRef.current = null;
1710
+ hoverKeyRef.current = null;
1711
+ hoverTopRef.current = null;
1712
+ setHoverAct(null);
1713
+ }, 180);
1714
+ hideTimerRef.current = t;
1715
+ }
1716
+ }, react.default.createElement("button", {
1717
+ className: "edrv-pill edrv-pill-keep",
1718
+ onClick: () => actHunk(hoverAct.region, false)
1719
+ }, "✓ Keep"), react.default.createElement("button", {
1720
+ className: "edrv-pill edrv-pill-undo",
1721
+ onClick: () => actHunk(hoverAct.region, true)
1722
+ }, "↩ Undo")) : null;
1723
+ const overlay = launcherOpen ? react.default.createElement(react.default.Fragment, null, react.default.createElement("div", {
1724
+ style: {
1725
+ position: "fixed",
1726
+ inset: 0,
1727
+ zIndex: 30
1728
+ },
1729
+ onClick: () => setLauncherOpen(false)
1730
+ }), react.default.createElement(DiffLauncher, {
1731
+ sessionId,
1732
+ sum,
1733
+ tab: launcherTab,
1734
+ onClose: () => setLauncherOpen(false),
1735
+ onOpenFile: (p) => {
1736
+ openFile(p, true);
1737
+ setLauncherOpen(false);
1738
+ }
1739
+ })) : pendingRegions.length > 0 ? react.default.createElement(DiffBox, {
1740
+ pendingRegions,
1741
+ staleRegions,
1742
+ onAct: actHunk,
1743
+ onAcceptFile: acceptFile,
1744
+ onUndoFile: undoFile,
1745
+ onAcceptAllFiles: acceptAllFiles,
1746
+ onUndoAllFiles: undoAllFiles,
1747
+ allPendingCount: allPending.length,
1748
+ onRollback: rollbackFile,
1749
+ onJump: jumpTo,
1750
+ otherFiles,
1751
+ onOpenOther: (p) => openFile(p, true),
1752
+ onOpenLauncher: (tab) => {
1753
+ setLauncherTab(tab || "pending");
1754
+ setLauncherOpen(true);
1755
+ },
1756
+ onRefresh: reloadFile,
1757
+ activePath: active,
1758
+ diffIdx,
1759
+ diffTotal: pendingRegions.length,
1760
+ fileIdx,
1761
+ fileTotal: sum.pendingFiles.length,
1762
+ onPrevDiff: () => gotoDiff(-1),
1763
+ onNextDiff: () => gotoDiff(1),
1764
+ onPrevFile: () => gotoFile(-1),
1765
+ onNextFile: () => gotoFile(1)
1766
+ }) : sum.pendingFiles.length > 0 ? react.default.createElement(DiffBarEmpty, {
1767
+ sum,
1768
+ active,
1769
+ staleCount: staleRegions.length,
1770
+ onNextFile: () => {
1771
+ if (sum.pendingFiles.length) openFile(sum.pendingFiles[0].path, true);
1772
+ },
1773
+ onOpenLauncher: (tab) => {
1774
+ setLauncherTab(tab || "pending");
1775
+ setLauncherOpen(true);
1776
+ },
1777
+ onRefresh: reloadFile
1778
+ }) : null;
1779
+ return react.default.createElement("div", {
1780
+ "data-edrv-view": "1",
1781
+ style: {
1782
+ height: "100%",
1783
+ minHeight: 0,
1784
+ display: "flex",
1785
+ flexDirection: "column",
1786
+ background: "var(--dsw-alias-bg-base,transparent)",
1787
+ overflow: "hidden"
1788
+ }
1789
+ }, pathBar, tabRow, react.default.createElement("div", { style: {
1790
+ position: "relative",
1791
+ flex: 1,
1792
+ minHeight: 0,
1793
+ display: "flex",
1794
+ flexDirection: "column"
1795
+ } }, body, hoverEl, overlay), statusBar);
1796
+ }
1797
+ //#endregion
1798
+ //#region src/client/ui/DiffBadge.ts
1799
+ /**
1800
+ * dsh-vscode-mode client — DiffBadge:header 差异角标(仅工作区有差异时显示)。
1801
+ * 迁移自原 src/client/index.ts 的 DiffBadge,语义不改。
1802
+ * 作者 ddj 2026-08-20
1803
+ */
1804
+ /**
1805
+ * 会话 header 差异角标:5s 轮询 edrv.list,有差异时显示数字徽章,点击打开编辑区并拉起 DiffLauncher。
1806
+ */
1807
+ function DiffBadge(props) {
1808
+ const sessionId = props?.sessionId;
1809
+ const [n, setN] = react.default.useState(0);
1810
+ const seq = react.default.useRef(0);
1811
+ const load = react.default.useCallback(() => {
1812
+ if (!sessionId) return;
1813
+ const s = ++seq.current;
1814
+ rpc("edrv.list", { sessionId }).then((res) => {
1815
+ if (s !== seq.current || !res || !res.ok || !Array.isArray(res.records)) return;
1816
+ setN(summarize(res.records).totalFiles);
1817
+ }).catch(() => {});
1818
+ }, [sessionId]);
1819
+ react.default.useEffect(() => {
1820
+ load();
1821
+ const t = setInterval(load, 5e3);
1822
+ const onRefresh = () => load();
1823
+ window.addEventListener("edrv:refresh", onRefresh);
1824
+ return () => {
1825
+ clearInterval(t);
1826
+ window.removeEventListener("edrv:refresh", onRefresh);
1827
+ };
1828
+ }, [load]);
1829
+ if (!n) return null;
1830
+ const click = () => {
1831
+ openEditorView(null);
1832
+ setTimeout(() => {
1833
+ emitShowLauncher();
1834
+ }, 120);
1835
+ };
1836
+ return react.default.createElement("button", {
1837
+ onClick: click,
1838
+ title: n + " 个文件有差异,点击在编辑区查看",
1839
+ style: {
1840
+ position: "relative",
1841
+ display: "inline-flex",
1842
+ alignItems: "center",
1843
+ justifyContent: "center",
1844
+ width: 30,
1845
+ height: 30,
1846
+ padding: 0,
1847
+ background: "transparent",
1848
+ border: "none",
1849
+ borderRadius: 6,
1850
+ cursor: "pointer"
1851
+ }
1852
+ }, react.default.createElement("img", {
1853
+ src: "/edrv/assets/compare-select.png",
1854
+ alt: "差异",
1855
+ style: {
1856
+ width: 22,
1857
+ height: 22,
1858
+ display: "block"
1859
+ }
1860
+ }), react.default.createElement("span", { style: {
1861
+ position: "absolute",
1862
+ top: 1,
1863
+ right: 0,
1864
+ minWidth: 15,
1865
+ height: 15,
1866
+ padding: "0 3px",
1867
+ borderRadius: 8,
1868
+ background: "var(--dsw-alias-state-warn-primary,#d97706)",
1869
+ color: "#fff",
1870
+ fontSize: 10,
1871
+ lineHeight: "15px",
1872
+ textAlign: "center",
1873
+ fontWeight: 700,
1874
+ boxSizing: "border-box"
1875
+ } }, String(n)));
1876
+ }
1877
+ //#endregion
1878
+ //#region src/client/index.ts
1879
+ /**
1880
+ * dsh-vscode-mode client — 浏览器半入口:slot 注册 + 装配。
1881
+ * 挂点:conversation.view「文件编辑」页签(中央 Monaco 编辑器)+ header 差异角标。
1882
+ * 与 Host 通信:同源 fetch('/edrv/rpc')(shared/rpc 契约)。
1883
+ * 作者 ddj 2026-08-20
1884
+ */
1885
+ const inject = ["slots", "timer"];
1886
+ /**
1887
+ * 装配客户端:注册中央编辑区视图与 header 差异角标。
1888
+ * @author ddj 2026年08月20号
1889
+ * @param ctx 客户端根上下文(slots + timer 服务)
1890
+ */
1891
+ function apply(ctx) {
1892
+ const schedule = (fn, ms) => ctx.timeout(fn, ms);
1893
+ ctx.slots.register({
1894
+ name: "conversation.view",
1895
+ id: "edrv-editor",
1896
+ order: 5,
1897
+ label: "文件编辑",
1898
+ inject: (sessionId) => ({ sessionId })
1899
+ }, (props) => react.default.createElement(EditorView, Object.assign({}, props, { schedule })));
1900
+ ctx.slots.register({
1901
+ name: "conversation.session.header.utilities",
1902
+ id: "edrv-diff-badge",
1903
+ order: 90,
1904
+ label: "差异"
1905
+ }, (props) => react.default.createElement(DiffBadge, Object.assign({}, props)));
1906
+ }
1907
+ //#endregion
1908
+ exports.apply = apply;
1909
+ exports.inject = inject;
1910
+ return module.exports;
1911
+ }
1912
+ });
1913
+
1914
+ //# sourceMappingURL=client.js.map