@dsh-electron/dsh-plugin-multi-root-workspace 0.1.13 → 0.1.15

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 (84) hide show
  1. package/CHANGELOG.en.md +36 -1
  2. package/CHANGELOG.md +36 -1
  3. package/README.en.md +27 -10
  4. package/README.md +27 -10
  5. package/cordis.patch.yml +5 -1
  6. package/detail/DETAIL.en.md +1 -0
  7. package/detail/DETAIL.md +1 -0
  8. package/detail/preview.svg +1 -0
  9. package/detail/preview.zh.svg +1 -0
  10. package/lib/{llm-message-fYORacvh.js → agent-instructions-BXuKhOzr.js} +1 -84
  11. package/lib/auto-review.js +2 -2
  12. package/lib/client.js +584 -73
  13. package/lib/client.js.map +1 -1
  14. package/lib/{command-B9855H7y.js → command-y5tfcAeC.js} +45 -12
  15. package/lib/command.js +1 -1
  16. package/lib/compat-CTJbpCxu.js +267 -0
  17. package/lib/compat.js +1 -1
  18. package/lib/{dialects-Dr4rDR-H.js → dialects-CdlG3RF9.js} +1 -1
  19. package/lib/{compat-Bg2HFVMX.js → dsh-version-DCwF7REk.js} +5 -265
  20. package/lib/{fs-CfzJq2wd.js → fs-Uws0ZpmV.js} +38 -0
  21. package/lib/fs.js +1 -1
  22. package/lib/index.js +11 -9
  23. package/lib/{instructions-KsUwUFE0.js → instructions-Ck9wp4J4.js} +695 -26
  24. package/lib/instructions.js +1 -1
  25. package/lib/llm-message-CepyzEBQ.js +84 -0
  26. package/lib/lsp.js +3 -3
  27. package/lib/{metadata-DgqSdVX_.js → metadata-99jgViHQ.js} +1 -1
  28. package/lib/{method-wrapper-CFY3p3j2.js → method-wrapper-D4oUCsEF.js} +22 -2
  29. package/lib/{native-win32-2waCRkPI.js → native-win32-C5oMtt7f.js} +2 -1
  30. package/lib/ops-compatibility.js +58 -0
  31. package/lib/ops-settings-UN2oOKEp.js +40 -0
  32. package/lib/ops-tools-k4JYaOrM.js +152 -0
  33. package/lib/process-fence.js +100 -24
  34. package/lib/{protocol-CJOvAvOS.js → protocol-CwDnFEyK.js} +54 -8
  35. package/lib/{registry-Bv431TfO.js → registry-DyDNDSJZ.js} +235 -79
  36. package/lib/{registry-lease-win32-DVTq9d4D.js → registry-lease-win32-r4puac9m.js} +2 -1
  37. package/lib/registry.js +2 -2
  38. package/lib/{review-context-NDpKeMCc.js → review-context-B-oHkU6a.js} +24 -7
  39. package/lib/review-context.js +1 -1
  40. package/lib/{roots-Ca_W1S9n.js → roots-CmaWxRVy.js} +112 -22
  41. package/lib/{sandbox-C-vCJCp3.js → sandbox-CZElTNPz.js} +1 -1
  42. package/lib/sandbox.js +1 -1
  43. package/lib/scope.js +72 -9
  44. package/lib/skills-utyuvmwr.js +104 -0
  45. package/lib/skills.js +4 -105
  46. package/lib/{tool-context-CpgIFcCB.js → tool-context-CCeY3mrK.js} +1 -1
  47. package/lib/types/authority/protocol.d.ts +5 -1
  48. package/lib/types/client/RootSwitcher.d.ts +2 -0
  49. package/lib/types/client/auto-review-settings.d.ts +18 -0
  50. package/lib/types/client/files-tree/FilesTree.d.ts +4 -1
  51. package/lib/types/client/files-tree/locales.d.ts +1 -1
  52. package/lib/types/client/locales.d.ts +40 -0
  53. package/lib/types/client/open-in-app/locales.d.ts +1 -1
  54. package/lib/types/client/ops-settings.d.ts +6 -0
  55. package/lib/types/client/skills-settings.d.ts +13 -0
  56. package/lib/types/client/styles.d.ts +1 -1
  57. package/lib/types/command.d.ts +3 -3
  58. package/lib/types/compat/auto-review-client.d.ts +58 -0
  59. package/lib/types/compat/connection-rpc.d.ts +12 -0
  60. package/lib/types/compat/dsh-version.d.ts +1 -1
  61. package/lib/types/compat/files-tree.d.ts +36 -0
  62. package/lib/types/compat/fs-patch.d.ts +6 -0
  63. package/lib/types/compat/instruction-activity.d.ts +32 -1
  64. package/lib/types/compat/koffi.d.ts +2 -0
  65. package/lib/types/compat/ops-confirmation.d.ts +17 -0
  66. package/lib/types/compat/ops-settings.d.ts +11 -0
  67. package/lib/types/compat/ops-tools.d.ts +25 -0
  68. package/lib/types/compat/skills-client.d.ts +1 -0
  69. package/lib/types/compat/tool-context.d.ts +4 -0
  70. package/lib/types/compat.d.ts +2 -2
  71. package/lib/types/fs.d.ts +3 -1
  72. package/lib/types/instructions.d.ts +6 -1
  73. package/lib/types/ops-compatibility.d.ts +34 -0
  74. package/lib/types/registry.d.ts +67 -8
  75. package/lib/types/review-context.d.ts +2 -0
  76. package/lib/types/roots.d.ts +114 -20
  77. package/lib/types/scope.d.ts +28 -2
  78. package/lib/workspace-files.js +3 -3
  79. package/lib/{workspace-routing-YPxaQ4V3.js → workspace-routing-D_qwMUh-.js} +4 -4
  80. package/locale/en.json +7 -0
  81. package/locale/zh.json +7 -0
  82. package/package.json +93 -82
  83. package/src/auto-review/README.en.md +1 -1
  84. package/src/auto-review/README.md +1 -1
package/lib/client.js CHANGED
@@ -123,6 +123,48 @@ window.__ModuleLoader__.load({
123
123
  return typeof count === "number" && count > 0;
124
124
  }
125
125
  //#endregion
126
+ //#region src/compat/files-tree.ts
127
+ function nativeTreeRenderer(value) {
128
+ return typeof value === "function" ? value : void 0;
129
+ }
130
+ /** The factory consumes absolute, slash-separated keys and already-localized errors. */
131
+ function nativeTreeInput(state, failureLine, toggle, open) {
132
+ const root = state.root.replace(/[/\\]+$/, "");
133
+ const keyOf = (path) => path === state.root ? root : path;
134
+ const entries = [];
135
+ const directories = {};
136
+ for (const path of state.expanded) directories[keyOf(path)] = { loading: true };
137
+ for (const [path, level] of Object.entries(state.levels)) {
138
+ const key = keyOf(path);
139
+ if (level.kind === "loading") directories[key] = { loading: true };
140
+ else if (level.kind === "failed") directories[key] = { failure: failureLine(level.failure) };
141
+ else {
142
+ const status = {};
143
+ if (level.failure !== void 0) status.failure = failureLine(level.failure);
144
+ if (level.level.truncated) status.truncated = true;
145
+ directories[key] = status;
146
+ for (const entry of level.level.entries) entries.push({
147
+ path: `${path.replace(/[/\\]+$/, "")}/${entry.name}`,
148
+ name: entry.name,
149
+ meta: { kind: entry.type }
150
+ });
151
+ }
152
+ }
153
+ return {
154
+ root,
155
+ entries,
156
+ directories,
157
+ expanded: state.expanded.map(keyOf),
158
+ onToggle: (entry) => {
159
+ const parent = entry.path.slice(0, entry.path.lastIndexOf("/"));
160
+ toggle(parent === root ? state.root : parent, entry.path);
161
+ },
162
+ onOpen: (entry) => {
163
+ open(entry.path);
164
+ }
165
+ };
166
+ }
167
+ //#endregion
126
168
  //#region node_modules/.pnpm/zod@4.6.7/node_modules/zod/v4/core/util.js
127
169
  function getEnumValues(entries) {
128
170
  const numericValues = Object.values(entries).filter((v) => typeof v === "number");
@@ -5545,6 +5587,22 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5545
5587
  "skills.globalEnable": "启用附属根 Skills",
5546
5588
  "skills.globalHint": "控制所有附属目录的 Skill 发现与新调用;保留每个目录的独立选择。",
5547
5589
  "skills.settingsFailed": "设置保存失败,请重试。",
5590
+ "autoReview.title": "多根 Auto Review",
5591
+ "autoReview.experimental": "实验性",
5592
+ "autoReview.hint": "为附加根保留审核上下文并接管 Auto 审查;与插件管理页同名 Loader 行开关等价,默认关闭,仅适配 DSH 0.2.1-rc.2。",
5593
+ "autoReview.loadFailed": "无法读取插件状态。",
5594
+ "autoReview.restartRequired": "更改将在重启后生效。",
5595
+ "autoReview.settingsFailed": "设置保存失败,请重试。",
5596
+ "ops.compatibilityEnable": "OPS 兼容模式",
5597
+ "ops.experimental": "实验性",
5598
+ "ops.compatibilityHint": "仅限 DSH 0.2.*。启用 OPS 写保护、实时活动与 Review 增强。受限模式可能拒绝附加根操作(包括读取),后台缓存可能残留;关闭时显式只读附加根可能被 OPS 写入。",
5599
+ "ops.settingsFailed": "设置保存失败,请重试。",
5600
+ "ops.confirmTitle": "开启 OPS 实验性兼容?",
5601
+ "ops.confirmDescription": "这仍是有限的实验性适配。开启后,附加根的合法读写及部分 PTC 操作可能被拒绝;Windows 存在只读边界时,OPS 读取和执行也可能被拒绝。Linux 批处理或结果读取、后台任务及清理可能不可用,并留下缓存。活动记录尚不持久化,恢复会话或审查动态操作时,Review 可能因证据不足而失败。目前尚未实现 OPS 全功能兼容,请仅在能接受这些限制时开启。",
5602
+ "ops.confirmAcknowledge": "我已了解上述限制,仍要开启实验性兼容",
5603
+ "ops.confirmCancel": "暂不开启",
5604
+ "ops.confirmClose": "关闭并保持未开启",
5605
+ "ops.confirmEnable": "确认开启",
5548
5606
  "skills.userOnly": "仅用户调用",
5549
5607
  "panel.skills": "目录技能",
5550
5608
  "panel.skillsEnabled": "此目录技能已启用,点击停用",
@@ -5589,6 +5647,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5589
5647
  "panel.copied": "已复制",
5590
5648
  "panel.reveal": "在文件管理器中显示",
5591
5649
  "panel.reRegister": "重新登记(保留原访问级别)",
5650
+ "panel.redirectedHint": "目标已改变:请移除该登记后再显式添加新目录。",
5592
5651
  "panel.moveUp": "上移",
5593
5652
  "panel.moveDown": "下移",
5594
5653
  "access.inherit": "跟随会话",
@@ -5625,6 +5684,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5625
5684
  "error.copy-failed": "无法写入剪贴板,请检查浏览器的剪贴板权限。",
5626
5685
  "error.session-not-found": "找不到该会话;请先打开一个工作区会话。",
5627
5686
  "error.unsupported-path": "该路径是网络或设备路径,Windows ACL 隔离无法覆盖它。",
5687
+ "error.protected-path": "该目录与 DSH 主目录或根登记存储目录重叠,不能登记。",
5688
+ "error.protected-boundary-unknown": "暂时无法确定 DSH 主目录等受保护目录的位置;恢复之前不能新增登记。",
5689
+ "error.revive-target-changed": "该登记现在指向另一个目录;请先移除它,再显式添加新目录。",
5628
5690
  "error.fallback": "操作失败。"
5629
5691
  };
5630
5692
  /** English copy, pinned to the Chinese key set. */
@@ -5632,6 +5694,22 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5632
5694
  "skills.globalEnable": "Enable additional-root Skills",
5633
5695
  "skills.globalHint": "Controls discovery and new invocations for all additional folders, preserving each folder’s choice.",
5634
5696
  "skills.settingsFailed": "Could not save settings. Please try again.",
5697
+ "autoReview.title": "Multi-root Auto Review",
5698
+ "autoReview.experimental": "Experimental",
5699
+ "autoReview.hint": "Preserves multi-root context for Auto review and takes over the reviewer; equivalent to the same Loader row switch in the Plugin Manager. Off by default; adapted to DSH 0.2.1-rc.2 only.",
5700
+ "autoReview.loadFailed": "Could not read plugin state.",
5701
+ "autoReview.restartRequired": "The change takes effect after restart.",
5702
+ "autoReview.settingsFailed": "Could not save settings. Please try again.",
5703
+ "ops.compatibilityEnable": "OPS compatibility mode",
5704
+ "ops.experimental": "Experimental",
5705
+ "ops.compatibilityHint": "Requires DSH 0.2.*. Enables OPS write protection, live activity, and Review enhancements. Restricted modes may reject additional-root operations, including reads, and background caches may remain. When off, OPS may write to an explicitly read-only additional root.",
5706
+ "ops.settingsFailed": "Could not save settings. Please try again.",
5707
+ "ops.confirmTitle": "Enable experimental OPS compatibility?",
5708
+ "ops.confirmDescription": "This is a limited experimental adaptation. Valid additional-root reads/writes and some PTC calls may be rejected; Windows may also block OPS reads and execution when read-only boundaries apply. Linux batch/result operations, background tasks, and cleanup may fail or leave caches behind. Activity is not persisted: after resume, or when reviewing dynamic operations, Review may fail for lack of evidence. Full OPS compatibility is not available. Enable only if you accept these limitations.",
5709
+ "ops.confirmAcknowledge": "I understand these limitations and want to enable experimental compatibility",
5710
+ "ops.confirmCancel": "Keep disabled",
5711
+ "ops.confirmClose": "Close and keep disabled",
5712
+ "ops.confirmEnable": "Confirm and enable",
5635
5713
  "skills.userOnly": "User invocation only",
5636
5714
  "panel.skills": "Folder skills",
5637
5715
  "panel.skillsEnabled": "Skills enabled for this folder. Click to disable.",
@@ -5676,6 +5754,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5676
5754
  "panel.copied": "Copied",
5677
5755
  "panel.reveal": "Reveal in file manager",
5678
5756
  "panel.reRegister": "Re-register (keeps its access level)",
5757
+ "panel.redirectedHint": "The target changed: remove this registration, then add the new folder explicitly.",
5679
5758
  "panel.moveUp": "Move up",
5680
5759
  "panel.moveDown": "Move down",
5681
5760
  "access.inherit": "Inherit",
@@ -5712,6 +5791,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5712
5791
  "error.copy-failed": "Could not write to the clipboard; check the browser's clipboard permission.",
5713
5792
  "error.session-not-found": "That session is not active; open a workspace session first.",
5714
5793
  "error.unsupported-path": "That is a network or device path, which Windows ACL confinement cannot cover.",
5794
+ "error.protected-path": "That directory overlaps the DSH home or the registry storage directories; it cannot be registered.",
5795
+ "error.protected-boundary-unknown": "The protected DSH directories cannot be resolved right now; no folder can be registered until they can.",
5796
+ "error.revive-target-changed": "That registration now resolves to a different directory; remove it, then add the new folder explicitly.",
5715
5797
  "error.fallback": "The operation failed."
5716
5798
  };
5717
5799
  //#endregion
@@ -5928,6 +6010,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5928
6010
  display: none;
5929
6011
  }
5930
6012
 
6013
+ /* Newer Files pages place the navigator beside the header instead of
6014
+ wrapping both in data-files-root. Target the page without hiding the
6015
+ portaled replacement or any of its rows. */
6016
+ [data-files-page]:has(.mrfw-fileLauncher) [data-files-path]:not([data-files-entry]) { order: -2; }
6017
+ [data-files-page]:has(.mrfw-rootSwitcher) [data-files-path]:not([data-files-entry]) { display: none; }
6018
+ [data-files-page]:has(.mrfw-rootSwitcher[data-root-kind="additional"]) [data-files-content],
6019
+ [data-files-page]:has(.mrfw-rootSwitcher[data-root-kind="additional"]) [data-files-reload] { display: none; }
6020
+ [data-files-page]:has(.mrfw-rootSwitcher[data-root-kind="additional"]) [data-slot="sidebar.right.tab.files.actions"] > *:has([data-open-target="directory"]:not(.mrfw-openSplit)),
6021
+ [data-files-page]:has(.mrfw-rootSwitcher[data-root-kind="additional"]) [data-open-target="directory"]:not(.mrfw-openSplit) { display: none; }
6022
+
5931
6023
  /* While the switcher names an additional root, the native open-in-app split
5932
6024
  button hides too: its slot props carry the pane's primary root, so it
5933
6025
  would always open the primary root no matter what the switcher selected.
@@ -6360,6 +6452,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6360
6452
  border-bottom: 0.5px solid var(--dsw-alias-border-l2);
6361
6453
  }
6362
6454
  .mrfw-skillsSettingsText { min-width: 0; }
6455
+ .mrfw-settingsTitleRow {
6456
+ display: flex;
6457
+ align-items: center;
6458
+ gap: 8px;
6459
+ }
6363
6460
  .mrfw-skillsSettingsTitle { font-size: 14px; line-height: 20px; }
6364
6461
  .mrfw-skillsSettingsDescription {
6365
6462
  margin-top: 4px;
@@ -6613,10 +6710,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6613
6710
  `;
6614
6711
  //#endregion
6615
6712
  //#region src/compat/skills-client.ts
6616
- function bindSkillsForm(ctx) {
6713
+ function bindSettingsForm(ctx, entryId) {
6617
6714
  const modern = ctx.get("configForms");
6618
- if (modern !== void 0) return modern.get("multi-root-skills");
6619
- return ctx.get("settingsScope")?.bind({ namespace: "multi-root-skills" });
6715
+ if (modern !== void 0) return modern.get(entryId);
6716
+ return ctx.get("settingsScope")?.bind({ namespace: entryId });
6717
+ }
6718
+ function bindSkillsForm(ctx) {
6719
+ return bindSettingsForm(ctx, "multi-root-skills");
6620
6720
  }
6621
6721
  function clientInject(ctx, services, callback) {
6622
6722
  if (typeof ctx.inject === "function") ctx.inject(services, callback);
@@ -6842,102 +6942,503 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6842
6942
  });
6843
6943
  }
6844
6944
  //#endregion
6945
+ //#region src/compat/auto-review-client.ts
6946
+ /** The module specifier `cordis.patch.yml` gives the optional row. */
6947
+ const AUTO_REVIEW_MODULE = "@dsh-electron/dsh-plugin-multi-root-workspace/auto-review";
6948
+ var AutoReviewSettingsForm = class {
6949
+ snapshot = { status: "unavailable" };
6950
+ listeners = /* @__PURE__ */ new Set();
6951
+ manager;
6952
+ stopEvents;
6953
+ epoch = 0;
6954
+ refreshSeq = 0;
6955
+ getSnapshot() {
6956
+ return this.snapshot;
6957
+ }
6958
+ subscribe(listener) {
6959
+ this.listeners.add(listener);
6960
+ return () => {
6961
+ this.listeners.delete(listener);
6962
+ };
6963
+ }
6964
+ publish(next) {
6965
+ this.snapshot = next;
6966
+ for (const listener of [...this.listeners]) listener();
6967
+ }
6968
+ attach(manager, remote) {
6969
+ this.detach();
6970
+ this.manager = manager;
6971
+ this.epoch++;
6972
+ this.publish({ status: "loading" });
6973
+ if (remote !== void 0) try {
6974
+ this.stopEvents = remote.$on("plugin-manager/changed", () => {
6975
+ this.refresh();
6976
+ });
6977
+ } catch {
6978
+ this.stopEvents = void 0;
6979
+ }
6980
+ this.refresh();
6981
+ }
6982
+ detach() {
6983
+ this.stopEvents?.();
6984
+ this.stopEvents = void 0;
6985
+ this.manager = void 0;
6986
+ this.epoch++;
6987
+ this.publish({ status: "unavailable" });
6988
+ }
6989
+ async refresh() {
6990
+ const manager = this.manager;
6991
+ const epoch = this.epoch;
6992
+ const seq = ++this.refreshSeq;
6993
+ if (manager === void 0) return;
6994
+ const result = await manager.listPlugins();
6995
+ if (manager !== this.manager || epoch !== this.epoch || seq !== this.refreshSeq) return;
6996
+ if (!result.ok) {
6997
+ this.publish({ status: "error" });
6998
+ return;
6999
+ }
7000
+ const entry = result.value.find((item) => item.moduleName === AUTO_REVIEW_MODULE);
7001
+ this.publish(entry === void 0 ? { status: "ready" } : {
7002
+ status: "ready",
7003
+ row: {
7004
+ entryId: entry.entryId,
7005
+ enabled: entry.enabled,
7006
+ writable: entry.readOnlyReason === void 0,
7007
+ ...entry.diagnostic === void 0 ? {} : { diagnostic: entry.diagnostic }
7008
+ }
7009
+ });
7010
+ }
7011
+ async setEnabled(enabled) {
7012
+ const manager = this.manager;
7013
+ const row = this.snapshot.status === "ready" ? this.snapshot.row : void 0;
7014
+ if (manager === void 0 || row === void 0) return "failed";
7015
+ const result = await manager.setPluginEnabled(row.entryId, enabled);
7016
+ await this.refresh();
7017
+ if (!result.ok) return "failed";
7018
+ const application = result.value.application;
7019
+ return application === "applied" || application === "restart-required" ? application : "failed";
7020
+ }
7021
+ };
7022
+ /**
7023
+ * Create the card's Auto Review handle and attach it when the host's
7024
+ * `pluginManager` namespace arrives. The returned form exists immediately with
7025
+ * status `unavailable` and publishes on every attach/detach/read, so callers
7026
+ * only need `subscribe` — no separate availability callback.
7027
+ * @param ctx - the client root context.
7028
+ * @returns the stable form the settings row renders.
7029
+ */
7030
+ function installAutoReviewSettings(ctx) {
7031
+ const form = new AutoReviewSettingsForm();
7032
+ clientInject(ctx, ["remote.pluginManager"], (scope) => {
7033
+ const manager = scope.get("remote.pluginManager");
7034
+ if (manager === void 0) return;
7035
+ const remote = scope.get("remote");
7036
+ form.attach(manager, remote);
7037
+ return () => {
7038
+ form.detach();
7039
+ };
7040
+ });
7041
+ return form;
7042
+ }
7043
+ //#endregion
7044
+ //#region src/client/auto-review-settings.tsx
7045
+ /**
7046
+ * Full-opacity hold for the card's message toasts — the same window upstream
7047
+ * gives a refusal banner, since these strings name what broke rather than
7048
+ * reporting a one-line limit.
7049
+ */
7050
+ const TOAST_HOLD_MS = 8e3;
7051
+ /**
7052
+ * The optional Auto Review row of the plugin settings card.
7053
+ *
7054
+ * Its switch is the Loader row's own enablement — the same write the Plugin
7055
+ * Manager's row switch performs — so the card can never disagree with the row
7056
+ * list about whether the companion is on. Feedback is a transient `Toast`
7057
+ * banner, never inline text: state-driven messages (a failed read, an entry
7058
+ * diagnostic) fire on entry and clear when their source resolves, while write
7059
+ * outcomes report once and fade. The row stays hidden entirely on hosts
7060
+ * without the `pluginManager` namespace or when the composition does not list
7061
+ * the optional entry.
7062
+ */
7063
+ function AutoReviewSettingsCard({ form, t }) {
7064
+ const snapshot = (0, react.useSyncExternalStore)((listener) => form.subscribe(listener), () => form.getSnapshot());
7065
+ const [busy, setBusy] = (0, react.useState)(false);
7066
+ const mounted = (0, react.useRef)(true);
7067
+ const toastSeq = (0, react.useRef)(0);
7068
+ const [toast, setToast] = (0, react.useState)(null);
7069
+ (0, react.useEffect)(() => {
7070
+ mounted.current = true;
7071
+ return () => {
7072
+ mounted.current = false;
7073
+ };
7074
+ }, []);
7075
+ const showToast = (0, react.useCallback)((kind, text) => {
7076
+ toastSeq.current += 1;
7077
+ setToast({
7078
+ seq: toastSeq.current,
7079
+ kind,
7080
+ text
7081
+ });
7082
+ }, []);
7083
+ const dismissToast = (0, react.useCallback)((kind) => {
7084
+ setToast((current) => current?.kind === kind ? null : current);
7085
+ }, []);
7086
+ const status = snapshot.status;
7087
+ const diagnostic = status === "ready" ? snapshot.row?.diagnostic : void 0;
7088
+ (0, react.useEffect)(() => {
7089
+ if (status === "error") showToast("load", t("autoReview.loadFailed"));
7090
+ else dismissToast("load");
7091
+ }, [
7092
+ status,
7093
+ t,
7094
+ showToast,
7095
+ dismissToast
7096
+ ]);
7097
+ (0, react.useEffect)(() => {
7098
+ if (diagnostic !== void 0 && diagnostic !== "") showToast("diagnostic", diagnostic);
7099
+ else dismissToast("diagnostic");
7100
+ }, [
7101
+ diagnostic,
7102
+ showToast,
7103
+ dismissToast
7104
+ ]);
7105
+ if (status === "unavailable") return null;
7106
+ const row = status === "ready" ? snapshot.row : void 0;
7107
+ if (status === "ready" && row === void 0) return null;
7108
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7109
+ className: "mrfw-skillsSettings",
7110
+ children: [
7111
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7112
+ className: "mrfw-skillsSettingsText",
7113
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7114
+ className: "mrfw-settingsTitleRow",
7115
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7116
+ className: "mrfw-skillsSettingsTitle",
7117
+ children: t("autoReview.title")
7118
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
7119
+ tone: "info",
7120
+ children: t("autoReview.experimental")
7121
+ })]
7122
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7123
+ className: "mrfw-skillsSettingsDescription",
7124
+ children: t("autoReview.hint")
7125
+ })]
7126
+ }),
7127
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
7128
+ label: t("autoReview.title"),
7129
+ checked: row?.enabled ?? false,
7130
+ disabled: busy || row?.writable !== true,
7131
+ onChange: (enabled) => {
7132
+ setBusy(true);
7133
+ form.setEnabled(enabled).then((result) => {
7134
+ if (!mounted.current) return;
7135
+ if (result === "restart-required") showToast("write", t("autoReview.restartRequired"));
7136
+ else if (result === "failed") showToast("write", t("autoReview.settingsFailed"));
7137
+ }).catch(() => {
7138
+ if (mounted.current) showToast("write", t("autoReview.settingsFailed"));
7139
+ }).finally(() => {
7140
+ if (mounted.current) setBusy(false);
7141
+ });
7142
+ }
7143
+ }),
7144
+ toast === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
7145
+ text: toast.text,
7146
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutlineRegular, {}),
7147
+ holdMs: TOAST_HOLD_MS,
7148
+ onDone: () => {
7149
+ setToast(null);
7150
+ }
7151
+ }, toast.seq)
7152
+ ]
7153
+ });
7154
+ }
7155
+ //#endregion
7156
+ //#region src/compat/ops-confirmation.ts
7157
+ function opsRiskConfirmation() {
7158
+ return _deepseek_ai_dsh_client_ui_primitives.RiskConfirmation;
7159
+ }
7160
+ //#endregion
7161
+ //#region src/client/ops-settings.tsx
7162
+ function OpsCompatibilitySettingsCard({ form, t }) {
7163
+ const snapshot = (0, react.useSyncExternalStore)((listener) => form.subscribe(listener), () => form.getSnapshot());
7164
+ const [busy, setBusy] = (0, react.useState)(false), [error, setError] = (0, react.useState)();
7165
+ const [confirmation, setConfirmation] = (0, react.useState)();
7166
+ const [acknowledged, setAcknowledged] = (0, react.useState)(false);
7167
+ const saving = (0, react.useRef)(false);
7168
+ const mounted = (0, react.useRef)(true);
7169
+ const RiskConfirmation = opsRiskConfirmation();
7170
+ const canConfirm = confirmation !== void 0 && confirmation.form === form && confirmation.revision === snapshot.revision && snapshot.status === "ready" && snapshot.writable && snapshot.value?.enabled !== true && RiskConfirmation !== void 0;
7171
+ const dismiss = () => {
7172
+ setConfirmation(void 0);
7173
+ setAcknowledged(false);
7174
+ };
7175
+ (0, react.useEffect)(() => {
7176
+ mounted.current = true;
7177
+ return () => {
7178
+ mounted.current = false;
7179
+ };
7180
+ }, []);
7181
+ (0, react.useEffect)(() => {
7182
+ if (confirmation !== void 0 && !canConfirm) {
7183
+ setConfirmation(void 0);
7184
+ setAcknowledged(false);
7185
+ }
7186
+ }, [confirmation, canConfirm]);
7187
+ const save = async (enabled, revision) => {
7188
+ if (saving.current) return;
7189
+ saving.current = true;
7190
+ setBusy(true);
7191
+ setError(void 0);
7192
+ try {
7193
+ const ok = await form.mutate([{
7194
+ op: "set",
7195
+ path: ["enabled"],
7196
+ value: enabled
7197
+ }], revision);
7198
+ if (mounted.current && !ok) setError(t("ops.settingsFailed"));
7199
+ } catch {
7200
+ if (mounted.current) setError(t("ops.settingsFailed"));
7201
+ } finally {
7202
+ saving.current = false;
7203
+ if (mounted.current) setBusy(false);
7204
+ }
7205
+ };
7206
+ if (snapshot.status === "unavailable") return null;
7207
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7208
+ className: "mrfw-skillsSettings",
7209
+ children: [
7210
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7211
+ className: "mrfw-skillsSettingsText",
7212
+ children: [
7213
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7214
+ className: "mrfw-settingsTitleRow",
7215
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7216
+ className: "mrfw-skillsSettingsTitle",
7217
+ children: t("ops.compatibilityEnable")
7218
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
7219
+ tone: "info",
7220
+ children: t("ops.experimental")
7221
+ })]
7222
+ }),
7223
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7224
+ className: "mrfw-skillsSettingsDescription",
7225
+ children: t("ops.compatibilityHint")
7226
+ }),
7227
+ error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7228
+ role: "alert",
7229
+ children: error
7230
+ }) : null
7231
+ ]
7232
+ }),
7233
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
7234
+ label: t("ops.compatibilityEnable"),
7235
+ checked: snapshot.value?.enabled === true,
7236
+ disabled: busy || confirmation !== void 0 || snapshot.status !== "ready" || !snapshot.writable || snapshot.value?.enabled !== true && RiskConfirmation === void 0,
7237
+ onChange: (enabled) => {
7238
+ if (!enabled) {
7239
+ save(false, snapshot.revision);
7240
+ return;
7241
+ }
7242
+ setError(void 0);
7243
+ setAcknowledged(false);
7244
+ setConfirmation({
7245
+ form,
7246
+ revision: snapshot.revision
7247
+ });
7248
+ }
7249
+ }),
7250
+ RiskConfirmation === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RiskConfirmation, {
7251
+ open: canConfirm,
7252
+ title: t("ops.confirmTitle"),
7253
+ description: t("ops.confirmDescription"),
7254
+ acknowledgeLabel: t("ops.confirmAcknowledge"),
7255
+ cancelLabel: t("ops.confirmCancel"),
7256
+ closeLabel: t("ops.confirmClose"),
7257
+ confirmLabel: t("ops.confirmEnable"),
7258
+ acknowledged,
7259
+ disabled: busy || !canConfirm,
7260
+ onAcknowledgedChange: setAcknowledged,
7261
+ onCancel: dismiss,
7262
+ onConfirm: () => {
7263
+ if (!canConfirm || !acknowledged || saving.current) return;
7264
+ const revision = confirmation.revision;
7265
+ dismiss();
7266
+ save(true, revision);
7267
+ }
7268
+ })
7269
+ ]
7270
+ });
7271
+ }
7272
+ //#endregion
6845
7273
  //#region src/client/skills-settings.tsx
6846
7274
  const BUNDLE_KEY = "@dsh-electron/dsh-plugin-multi-root-workspace";
6847
7275
  function SkillsSettingsCard({ form, t }) {
6848
7276
  const snapshot = (0, react.useSyncExternalStore)((listener) => form.subscribe(listener), () => form.getSnapshot());
6849
- const [busy, setBusy] = (0, react.useState)(false), [error, setError] = (0, react.useState)();
7277
+ const [busy, setBusy] = (0, react.useState)(false);
6850
7278
  const mounted = (0, react.useRef)(true);
7279
+ const toastSeq = (0, react.useRef)(0);
7280
+ const [toast, setToast] = (0, react.useState)(null);
6851
7281
  (0, react.useEffect)(() => {
6852
7282
  mounted.current = true;
6853
7283
  return () => {
6854
7284
  mounted.current = false;
6855
7285
  };
6856
7286
  }, []);
7287
+ const showToast = (0, react.useCallback)((text) => {
7288
+ toastSeq.current += 1;
7289
+ setToast({
7290
+ seq: toastSeq.current,
7291
+ text
7292
+ });
7293
+ }, []);
6857
7294
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6858
7295
  className: "mrfw-skillsSettings",
6859
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6860
- className: "mrfw-skillsSettingsText",
6861
- children: [
6862
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7296
+ children: [
7297
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7298
+ className: "mrfw-skillsSettingsText",
7299
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6863
7300
  className: "mrfw-skillsSettingsTitle",
6864
7301
  children: t("skills.globalEnable")
6865
- }),
6866
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7302
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6867
7303
  className: "mrfw-skillsSettingsDescription",
6868
7304
  children: t("skills.globalHint")
6869
- }),
6870
- error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6871
- role: "alert",
6872
- children: error
6873
- }) : null
6874
- ]
6875
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
6876
- label: t("skills.globalEnable"),
6877
- checked: snapshot.value?.enabled ?? true,
6878
- disabled: busy || snapshot.status !== "ready" || !snapshot.writable,
6879
- onChange: (enabled) => {
6880
- setBusy(true);
6881
- setError(void 0);
6882
- form.mutate([{
6883
- op: "set",
6884
- path: ["enabled"],
6885
- value: enabled
6886
- }], snapshot.revision).then((ok) => {
6887
- if (mounted.current && !ok) setError(t("skills.settingsFailed"));
6888
- }).catch(() => {
6889
- if (mounted.current) setError(t("skills.settingsFailed"));
6890
- }).finally(() => {
6891
- if (mounted.current) setBusy(false);
6892
- });
6893
- }
6894
- })]
7305
+ })]
7306
+ }),
7307
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
7308
+ label: t("skills.globalEnable"),
7309
+ checked: snapshot.value?.enabled ?? true,
7310
+ disabled: busy || snapshot.status !== "ready" || !snapshot.writable,
7311
+ onChange: (enabled) => {
7312
+ setBusy(true);
7313
+ form.mutate([{
7314
+ op: "set",
7315
+ path: ["enabled"],
7316
+ value: enabled
7317
+ }], snapshot.revision).then((ok) => {
7318
+ if (mounted.current && !ok) showToast(t("skills.settingsFailed"));
7319
+ }).catch(() => {
7320
+ if (mounted.current) showToast(t("skills.settingsFailed"));
7321
+ }).finally(() => {
7322
+ if (mounted.current) setBusy(false);
7323
+ });
7324
+ }
7325
+ }),
7326
+ toast === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
7327
+ text: toast.text,
7328
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutlineRegular, {}),
7329
+ holdMs: 8e3,
7330
+ onDone: () => {
7331
+ setToast(null);
7332
+ }
7333
+ }, toast.seq)
7334
+ ]
6895
7335
  });
6896
7336
  }
7337
+ /**
7338
+ * The bundle's one `plugins.bundle.config` cell: every plugin-level switch the
7339
+ * package offers, in display order. Each row owns its own form and write path —
7340
+ * Skills and OPS through their settings namespaces, Auto Review through the Plugin
7341
+ * Manager's Loader-row enablement — so one cell never serializes their state.
7342
+ */
7343
+ function BundleSettingsCard({ form, autoReviewForm, opsForm, t }) {
7344
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
7345
+ form !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillsSettingsCard, {
7346
+ form,
7347
+ t
7348
+ }) : null,
7349
+ autoReviewForm !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AutoReviewSettingsCard, {
7350
+ form: autoReviewForm,
7351
+ t
7352
+ }) : null,
7353
+ opsForm !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OpsCompatibilitySettingsCard, {
7354
+ form: opsForm,
7355
+ t
7356
+ }) : null
7357
+ ] });
7358
+ }
6897
7359
  function installSkillsSettingsCard(ctx, t) {
6898
- let current;
7360
+ let currentSkills;
7361
+ let currentOps;
7362
+ let currentAutoReview = false;
6899
7363
  let stop;
6900
7364
  const candidates = /* @__PURE__ */ new Map();
6901
7365
  const update = () => {
6902
7366
  const chosen = [...candidates.entries()].sort(([a], [b]) => Number(b.get("configForms") !== void 0) - Number(a.get("configForms") !== void 0))[0];
6903
- if (chosen?.[1] === current) return;
7367
+ const skillsForm = chosen?.[1].skills;
7368
+ const opsCandidate = chosen?.[1].ops;
7369
+ const opsForm = opsCandidate?.getSnapshot().status === "unavailable" ? void 0 : opsCandidate;
7370
+ const autoSnapshot = autoForm.getSnapshot();
7371
+ const autoAvailable = autoSnapshot.status === "loading" || autoSnapshot.status === "error" || autoSnapshot.status === "ready" && autoSnapshot.row !== void 0;
7372
+ if (skillsForm === void 0 && opsForm === void 0 && !autoAvailable) {
7373
+ if (currentSkills !== void 0 || currentOps !== void 0 || currentAutoReview) {
7374
+ stop?.();
7375
+ stop = void 0;
7376
+ currentSkills = void 0;
7377
+ currentOps = void 0;
7378
+ currentAutoReview = false;
7379
+ }
7380
+ return;
7381
+ }
7382
+ if (currentSkills === skillsForm && currentOps === opsForm && currentAutoReview === autoAvailable) return;
7383
+ const slots = ctx.slots;
7384
+ if (slots === void 0) return;
6904
7385
  stop?.();
6905
7386
  stop = void 0;
6906
- current = chosen?.[1];
6907
- if (!chosen) return;
6908
- const [scope, form] = chosen;
6909
- const slots = scope.slots;
6910
- const disposers = [slots.inject("settings.plugin.item", () => slots.register({
7387
+ currentSkills = skillsForm;
7388
+ currentOps = opsForm;
7389
+ currentAutoReview = autoAvailable;
7390
+ const disposers = [slots.inject("plugins.bundle.config", () => slots.register({
7391
+ name: "plugins.bundle.config",
7392
+ key: BUNDLE_KEY,
7393
+ inject: () => ({
7394
+ form: skillsForm,
7395
+ autoReviewForm: autoForm,
7396
+ opsForm,
7397
+ t
7398
+ })
7399
+ }, BundleSettingsCard))];
7400
+ if (skillsForm !== void 0) disposers.push(slots.inject("settings.plugin.item", () => slots.register({
6911
7401
  name: "settings.plugin.item",
6912
7402
  key: "multi-root-skills",
6913
7403
  inject: () => ({
6914
- form,
7404
+ form: skillsForm,
6915
7405
  t
6916
7406
  })
6917
- }, SkillsSettingsCard)), slots.inject("plugins.bundle.config", () => slots.register({
6918
- name: "plugins.bundle.config",
6919
- key: BUNDLE_KEY,
7407
+ }, SkillsSettingsCard)));
7408
+ if (opsForm !== void 0) disposers.push(slots.inject("settings.plugin.item", () => slots.register({
7409
+ name: "settings.plugin.item",
7410
+ key: "multi-root-ops-compatibility",
6920
7411
  inject: () => ({
6921
- form,
7412
+ form: opsForm,
6922
7413
  t
6923
7414
  })
6924
- }, SkillsSettingsCard))];
7415
+ }, OpsCompatibilitySettingsCard)));
6925
7416
  stop = () => {
6926
7417
  for (const dispose of disposers) dispose();
6927
7418
  };
6928
7419
  };
7420
+ const autoForm = installAutoReviewSettings(ctx);
7421
+ const stopAutoWatch = autoForm.subscribe(update);
7422
+ update();
6929
7423
  for (const service of ["settingsScope", "configForms"]) clientInject(ctx, [service], (scope) => {
6930
- const form = bindSkillsForm(scope);
6931
- if (form === void 0) return;
6932
- candidates.set(scope, form);
7424
+ const skills = bindSkillsForm(scope);
7425
+ const ops = scope.get("configForms")?.get("multi-root-ops-compatibility");
7426
+ if (skills === void 0 && ops === void 0) return;
7427
+ const stopOpsWatch = ops?.subscribe(update);
7428
+ candidates.set(scope, {
7429
+ skills,
7430
+ ops
7431
+ });
6933
7432
  update();
6934
7433
  return () => {
7434
+ stopOpsWatch?.();
6935
7435
  candidates.delete(scope);
6936
7436
  update();
6937
7437
  };
6938
7438
  });
6939
7439
  ctx.effect(() => () => {
6940
7440
  stop?.();
7441
+ stopAutoWatch();
6941
7442
  candidates.clear();
6942
7443
  });
6943
7444
  }
@@ -7004,7 +7505,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7004
7505
  const IconAccessReadOnly = maybeIcon(["PermissionIconReadOnlyRegular", "IconReadOnlyRegular"]);
7005
7506
  const IconAccessInherit = maybeIcon(["IconWorkspaceTreeOutline16", "IconWorkspaceTreeOutlineRegular"]);
7006
7507
  //#endregion
7007
- //#region node_modules/.pnpm/@deepseek-ai+dsh-typert-protocol@0.2.2-alpha.2_@deepseek-ai+cordis@4.0.6-alpha.1/node_modules/@deepseek-ai/dsh-typert-protocol/lib/index.js
7508
+ //#region node_modules/.pnpm/@deepseek-ai+dsh-typert-protocol@0.2.2-rc.1_@deepseek-ai+cordis@4.1.0/node_modules/@deepseek-ai/dsh-typert-protocol/lib/index.js
7008
7509
  /** The one Remote failure class shared by owners, the Gateway, and consumers. */
7009
7510
  /**
7010
7511
  * One Remote call failure: a real Error carrying its stable code and typed
@@ -7514,7 +8015,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7514
8015
  return n;
7515
8016
  }
7516
8017
  //#endregion
7517
- //#region node_modules/.pnpm/@deepseek-ai+dsh-util-workspace-path@0.2.2-alpha.2_@deepseek-ai+cordis@4.0.6-alpha.1/node_modules/@deepseek-ai/dsh-util-workspace-path/lib/index.js
8018
+ //#region node_modules/.pnpm/@deepseek-ai+dsh-util-workspace-path@0.2.2-rc.1_@deepseek-ai+cordis@4.1.0/node_modules/@deepseek-ai/dsh-util-workspace-path/lib/index.js
7518
8019
  /**
7519
8020
  * The `dsh-resource://file/…` address grammar: how a file is named across the
7520
8021
  * Sidebar and the resource model, built and parsed without touching a
@@ -7760,7 +8261,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7760
8261
  * has opened under it — the upstream body's scroll, seed, and render logic
7761
8262
  * verbatim, with the root arriving as a prop instead of `useSessions(cwd)`.
7762
8263
  */
7763
- function FilesTree({ tabId, sessionId, root, signal, useStore, actions, start, toggle, onOpen, t }) {
8264
+ function FilesTree({ tabId, sessionId, root, signal, useStore, actions, start, toggle, onOpen, t, nativeTree }) {
7764
8265
  const state = useStore((store) => store.byTab[tabId]);
7765
8266
  const bodyRef = (0, react.useRef)(null);
7766
8267
  const scrollTopRef = (0, react.useRef)(0);
@@ -7801,19 +8302,21 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7801
8302
  },
7802
8303
  t
7803
8304
  };
8305
+ const legacy = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
8306
+ className: css$1.level,
8307
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Level, {
8308
+ path: state.root,
8309
+ tree
8310
+ })
8311
+ });
8312
+ const content = nativeTree === void 0 ? legacy : nativeTree("sidebar.files.tree", nativeTreeInput(state, (failure) => failureLine(t, failure), tree.onToggle, tree.onOpen), { fallback: legacy });
7804
8313
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7805
8314
  ref: bodyRef,
7806
8315
  className: css$1.body,
7807
8316
  onScroll: (event) => {
7808
8317
  scrollTopRef.current = event.currentTarget.scrollTop;
7809
8318
  },
7810
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
7811
- className: css$1.level,
7812
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Level, {
7813
- path: state.root,
7814
- tree
7815
- })
7816
- })
8319
+ children: content
7817
8320
  });
7818
8321
  }
7819
8322
  //#endregion
@@ -7986,7 +8489,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7986
8489
  };
7987
8490
  }
7988
8491
  //#endregion
7989
- //#region node_modules/.pnpm/@deepseek-ai+dsh-host-open-in-app@0.2.2-alpha.2_@deepseek-ai+cordis@4.0.6-alpha.1_@deep_d76f08f4e35ff3a756a48d7d013379f6/node_modules/@deepseek-ai/dsh-host-open-in-app/lib/types/shared.js
8492
+ //#region node_modules/.pnpm/@deepseek-ai+dsh-host-open-in-app@0.2.2-rc.1_@deepseek-ai+cordis@4.1.0_@deepseek-ai+dsh_33f3d65190f3ff17e4e9438df89ed28d/node_modules/@deepseek-ai/dsh-host-open-in-app/lib/types/shared.js
7990
8493
  var shared_exports = /* @__PURE__ */ __exportAll({
7991
8494
  OPEN_IN_APP_APPS_PATH: () => OPEN_IN_APP_APPS_PATH,
7992
8495
  OPEN_IN_APP_APPS_ROUTE: () => OPEN_IN_APP_APPS_ROUTE,
@@ -9067,12 +9570,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9067
9570
  accessChosen
9068
9571
  ]);
9069
9572
  /**
9070
- * Re-register one withheld or unusable root in place. The request carries
9071
- * the stored path and deliberately NO access: `registry.add` treats the call
9072
- * as a revive — it keeps the record's access, alias, and identity instead of
9573
+ * Re-register one MISSING root in place. The request carries the stored
9574
+ * path and deliberately NO access: `registry.add` treats the call as a
9575
+ * revive — it keeps the record's access, alias, and identity instead of
9073
9576
  * creating a new registration — which is also why the root limit must not
9074
- * gate this button: a revive consumes no slot, and it is the panel's only
9075
- * repair route for a redirected entry.
9577
+ * gate this button: a revive consumes no slot. Only `missing` rows get it:
9578
+ * a `redirected` registration now names a different directory, and revive
9579
+ * never changes the grant's target — replacing it is the explicit
9580
+ * remove-then-add flow the redirected hint describes (ADR-0027).
9076
9581
  */
9077
9582
  const reRegister = (0, react.useCallback)(async (root) => {
9078
9583
  await mutate("add", { path: root.path });
@@ -9300,14 +9805,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9300
9805
  className: "mrfw-stateWarn",
9301
9806
  children: t(stateKey(root.state))
9302
9807
  }),
9303
- root.state === "available" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconButton, {
9808
+ root.state === "missing" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconButton, {
9304
9809
  pending: state.busy,
9305
9810
  onClick: () => {
9306
9811
  reRegister(root);
9307
9812
  },
9308
9813
  icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconRefresh, {}),
9309
9814
  label: t("panel.reRegister")
9310
- }),
9815
+ }) : null,
9816
+ root.state === "redirected" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9817
+ className: "mrfw-note",
9818
+ children: t("panel.redirectedHint")
9819
+ }) : null,
9311
9820
  root.skillsEnabled === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconButton, {
9312
9821
  pending: state.busy,
9313
9822
  pressed: root.skillsEnabled,
@@ -9668,7 +10177,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9668
10177
  sectionRef.current = wrapper?.closest("[data-dockkit-content]") ?? null;
9669
10178
  const content = sectionRef.current?.dataset.dockkitContent;
9670
10179
  setTabId(content !== void 0 && content !== "" ? content : fallbackTabId.current ??= `mrfw-${Math.random().toString(36).slice(2)}`);
9671
- setPane(wrapper?.closest("[data-files-root]") ?? null);
10180
+ setPane(wrapper?.closest("[data-files-page]") ?? wrapper?.closest("[data-files-root]") ?? null);
9672
10181
  }, []);
9673
10182
  const selected = (filesTree?.useTreeSelection ?? noTreeSelection)((selection) => tabId === void 0 ? void 0 : selection[tabId]) ?? "primary";
9674
10183
  const selectRoot = filesTree?.selectRoot;
@@ -9867,7 +10376,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9867
10376
  start: filesTree.face.start,
9868
10377
  toggle: filesTree.face.toggle,
9869
10378
  onOpen: filesTree.openFile,
9870
- t: filesTree.t
10379
+ t: filesTree.t,
10380
+ nativeTree: pane.hasAttribute("data-files-page") ? props.nativeTree : void 0
9871
10381
  }), pane) : null,
9872
10382
  dialogOpen ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WorkspaceFoldersDialog, {
9873
10383
  panel: props.panel,
@@ -9990,7 +10500,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9990
10500
  subscribeSession,
9991
10501
  t,
9992
10502
  openInApp: openInApp?.adapter,
9993
- filesTree
10503
+ filesTree,
10504
+ nativeTree: nativeTreeRenderer(props.renderFactorySlot)
9994
10505
  });
9995
10506
  }));
9996
10507
  }