@dsh-electron/dsh-plugin-multi-root-workspace 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.en.md +19 -2
  2. package/CHANGELOG.md +19 -2
  3. package/README.en.md +17 -13
  4. package/README.md +16 -14
  5. package/cordis.patch.yml +4 -0
  6. package/lib/client.js +320 -18
  7. package/lib/client.js.map +1 -1
  8. package/lib/{command-DyOWujPo.js → command-CTdNvwt1.js} +107 -6
  9. package/lib/command.js +1 -1
  10. package/lib/{compat-PNEKXGa6.js → compat-B6ZqbQBy.js} +23 -3
  11. package/lib/compat.js +1 -1
  12. package/lib/index.js +5 -5
  13. package/lib/{instructions-Cn1Jj5Xi.js → instructions-DfzN-wVf.js} +106 -26
  14. package/lib/instructions.js +1 -1
  15. package/lib/lsp.js +38 -0
  16. package/lib/method-wrapper-Zc-JlDP3.js +51 -0
  17. package/lib/sandbox-BRXl_FpF.js +132 -0
  18. package/lib/sandbox.js +1 -1
  19. package/lib/scope.js +12 -6
  20. package/lib/session-directory-BLOstut1.js +9 -0
  21. package/lib/tool-context-CpgIFcCB.js +6 -0
  22. package/lib/types/client/RootFileTree.d.ts +12 -0
  23. package/lib/types/client/WorkspaceFoldersAction.d.ts +3 -0
  24. package/lib/types/client/index.d.ts +13 -2
  25. package/lib/types/client/locales.d.ts +14 -0
  26. package/lib/types/client/styles.d.ts +1 -1
  27. package/lib/types/compat/client-icons.d.ts +1 -1
  28. package/lib/types/compat/client-session.d.ts +2 -0
  29. package/lib/types/compat/dsh-version.d.ts +5 -3
  30. package/lib/types/compat/instruction-overlap.d.ts +7 -0
  31. package/lib/types/compat/session-directory.d.ts +4 -0
  32. package/lib/types/compat/tool-context.d.ts +9 -0
  33. package/lib/types/compat/tool-result.d.ts +1 -1
  34. package/lib/types/compat/workspace-files.d.ts +5 -0
  35. package/lib/types/contract.d.ts +24 -5
  36. package/lib/types/index.d.ts +2 -2
  37. package/lib/types/lsp.d.ts +4 -0
  38. package/lib/types/method-wrapper.d.ts +2 -0
  39. package/lib/types/panel-files.d.ts +3 -0
  40. package/lib/types/sandbox.d.ts +2 -11
  41. package/lib/types/scope.d.ts +3 -0
  42. package/lib/types/windows-profile.d.ts +13 -0
  43. package/lib/types/windows-runner.d.ts +1 -0
  44. package/lib/types/workspace-files.d.ts +4 -0
  45. package/lib/types/workspace-routing.d.ts +11 -0
  46. package/lib/{sandbox-D8Fw95FM.js → windows-profile-CiFsFYIV.js} +64 -138
  47. package/lib/windows-runner.js +68 -0
  48. package/lib/workspace-files.js +68 -0
  49. package/package.json +90 -47
package/lib/client.js CHANGED
@@ -44,6 +44,12 @@ window.__ModuleLoader__.load({
44
44
  if (typeof current === "string" && current !== "") return current;
45
45
  return mainViewSessionId(snapshot.byId);
46
46
  }
47
+ /** Observe native catalog changes when this surface provides a subscribable store. */
48
+ function subscribeSessions(sessions, listener) {
49
+ if (sessions === null || typeof sessions !== "object") return () => {};
50
+ const list = sessions.list;
51
+ return typeof list?.subscribe === "function" ? list.subscribe(listener) : () => {};
52
+ }
47
53
  /**
48
54
  * The list snapshot, when the service actually exposes one.
49
55
  *
@@ -1111,6 +1117,7 @@ window.__ModuleLoader__.load({
1111
1117
  }
1112
1118
  const anyString = /^[\s\S]{0,}$/;
1113
1119
  const number$1 = /^-?\d+(?:\.\d+)?$/;
1120
+ const boolean$1 = /^(?:true|false)$/i;
1114
1121
  const lowercase = /^[^A-Z]*$/;
1115
1122
  const uppercase = /^[^a-z]*$/;
1116
1123
  //#endregion
@@ -1898,6 +1905,24 @@ window.__ModuleLoader__.load({
1898
1905
  $ZodCheckNumberFormat.init(inst, def);
1899
1906
  $ZodNumber.init(inst, def);
1900
1907
  });
1908
+ const $ZodBoolean = /*@__PURE__*/ $constructor("$ZodBoolean", (inst, def) => {
1909
+ $ZodType.init(inst, def);
1910
+ inst._zod.pattern = boolean$1;
1911
+ inst._zod.parse = (payload, _ctx) => {
1912
+ if (def.coerce) try {
1913
+ payload.value = Boolean(payload.value);
1914
+ } catch (_) {}
1915
+ const input = payload.value;
1916
+ if (typeof input === "boolean") return payload;
1917
+ payload.issues.push({
1918
+ expected: "boolean",
1919
+ code: "invalid_type",
1920
+ input,
1921
+ inst
1922
+ });
1923
+ return payload;
1924
+ };
1925
+ });
1901
1926
  const $ZodUnknown = /*@__PURE__*/ $constructor("$ZodUnknown", (inst, def) => {
1902
1927
  $ZodType.init(inst, def);
1903
1928
  inst._zod.parse = (payload) => payload;
@@ -3340,6 +3365,13 @@ window.__ModuleLoader__.load({
3340
3365
  });
3341
3366
  }
3342
3367
  // @__NO_SIDE_EFFECTS__
3368
+ function _boolean(Class, params) {
3369
+ return new Class({
3370
+ type: "boolean",
3371
+ ...normalizeParams(params)
3372
+ });
3373
+ }
3374
+ // @__NO_SIDE_EFFECTS__
3343
3375
  function _unknown(Class) {
3344
3376
  return new Class({ type: "unknown" });
3345
3377
  }
@@ -4118,6 +4150,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4118
4150
  if (rest.length) json.allOf = [...json.allOf ?? [], ...rest.map((m) => ({ multipleOf: m }))];
4119
4151
  }
4120
4152
  };
4153
+ const booleanProcessor = (_schema, _ctx, json, _params) => {
4154
+ json.type = "boolean";
4155
+ };
4121
4156
  const neverProcessor = (_schema, _ctx, json, _params) => {
4122
4157
  json.not = {};
4123
4158
  };
@@ -4865,6 +4900,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4865
4900
  function int(params) {
4866
4901
  return /* @__PURE__ */ _int(ZodNumberFormat, params);
4867
4902
  }
4903
+ const ZodBoolean = /*@__PURE__*/ $constructor("ZodBoolean", (inst, def) => {
4904
+ $ZodBoolean.init(inst, def);
4905
+ ZodType.init(inst, def);
4906
+ inst._zod.processJSONSchema = (ctx, json, params) => booleanProcessor(inst, ctx, json, params);
4907
+ });
4908
+ function boolean(params) {
4909
+ return /* @__PURE__ */ _boolean(ZodBoolean, params);
4910
+ }
4868
4911
  const ZodUnknown = /*@__PURE__*/ $constructor("ZodUnknown", (inst, def) => {
4869
4912
  $ZodUnknown.init(inst, def);
4870
4913
  ZodType.init(inst, def);
@@ -5208,8 +5251,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5208
5251
  * - **One shape per endpoint.** {@link PanelResponseMap} is the single mapping
5209
5252
  * from endpoint to response type, so the panel cannot silently decode one
5210
5253
  * endpoint's answer as another's. `reveal` answers with
5211
- * {@link RevealedView}; every other endpoint answers with a whole
5212
- * {@link RootsView}. The host builds those answers in `command.ts` and the
5254
+ * {@link RevealedView}; root-management endpoints answer with
5255
+ * {@link RootsView}, and browsing returns {@link FilesView} or {@link FilePreview}.
5256
+ * The host builds those answers in `command.ts` and the
5213
5257
  * panel parses them here — one definition, two consumers.
5214
5258
  * - **Runtime validation at the boundary.** The payload arrives from a browser
5215
5259
  * and the answer arrives from the host; neither is trusted to match the
@@ -5266,10 +5310,47 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5266
5310
  const rootsViewSchema = object({
5267
5311
  primaryRoot: string(),
5268
5312
  primaryName: string().optional(),
5313
+ effectiveCwd: string().optional(),
5269
5314
  roots: array(rootViewSchema),
5270
5315
  unavailable: string().optional()
5271
5316
  });
5272
5317
  const revealedViewSchema = object({ revealed: string() });
5318
+ const filesViewSchema = object({
5319
+ path: string(),
5320
+ entries: array(object({
5321
+ name: string().min(1).refine((name) => name !== "." && name !== ".." && !/[\\/]/.test(name) && !name.includes(String.fromCharCode(0))),
5322
+ type: _enum([
5323
+ "file",
5324
+ "directory",
5325
+ "other"
5326
+ ])
5327
+ })),
5328
+ truncated: boolean()
5329
+ });
5330
+ const filePreviewSchema = object({
5331
+ text: string(),
5332
+ eof: boolean()
5333
+ });
5334
+ function parseFilesView(value) {
5335
+ const parsed = filesViewSchema.safeParse(value);
5336
+ return parsed.success ? {
5337
+ ok: true,
5338
+ value: parsed.data
5339
+ } : {
5340
+ ok: false,
5341
+ message: firstIssue(parsed.error)
5342
+ };
5343
+ }
5344
+ function parseFilePreview(value) {
5345
+ const parsed = filePreviewSchema.safeParse(value);
5346
+ return parsed.success ? {
5347
+ ok: true,
5348
+ value: parsed.data
5349
+ } : {
5350
+ ok: false,
5351
+ message: firstIssue(parsed.error)
5352
+ };
5353
+ }
5273
5354
  const errorViewSchema = object({
5274
5355
  code: string(),
5275
5356
  message: string(),
@@ -5292,6 +5373,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5292
5373
  value: {
5293
5374
  primaryRoot: view.primaryRoot,
5294
5375
  ...view.primaryName === void 0 ? {} : { primaryName: view.primaryName },
5376
+ ...view.effectiveCwd === void 0 ? {} : { effectiveCwd: view.effectiveCwd },
5295
5377
  roots: view.roots.map(narrowRootView),
5296
5378
  ...view.unavailable === void 0 ? {} : { unavailable: view.unavailable }
5297
5379
  }
@@ -5360,15 +5442,22 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5360
5442
  const NS = "multiRootWorkspace";
5361
5443
  /** Chinese copy (the canonical shape). */
5362
5444
  const zh = {
5445
+ "files.browse": "浏览文件",
5446
+ "files.reload": "刷新文件树",
5447
+ "files.empty": "目录为空",
5448
+ "files.truncated": "条目过多,列表已截断",
5449
+ "files.previewTruncated": "仅显示前 200 行",
5363
5450
  "action.label": "工作区目录",
5364
5451
  "action.title": "管理本会话的附属工作目录",
5365
5452
  "panel.title": "工作区目录",
5366
5453
  "panel.subtitle": "主目录和附加目录遵循当前沙箱模式;workspace-write 可写,read-only 下都不可写。",
5367
- "panel.primary": "主工作目录",
5368
- "panel.primaryNote": "当前工作区主工作目录,不可移除",
5454
+ "panel.primary": "主根",
5455
+ "panel.primaryNote": "会话原始项目,不可移除",
5456
+ "panel.cwd": "当前执行目录",
5457
+ "panel.cwdNote": "切换目录不会增加授权",
5369
5458
  "panel.additional": "附属工作目录",
5370
5459
  "panel.empty": "还没有附属工作目录。",
5371
- "panel.emptyHint": "添加后,Agent 可在同一会话中读写该目录;macOS/Linux 的受限 bash 同步生效,Windows 当前仅覆盖 fs 写路径。",
5460
+ "panel.emptyHint": "添加后,Agent 可在同一会话中读写该目录,受限 shell 和终端同步生效。",
5372
5461
  "panel.noSession": "当前没有活动会话",
5373
5462
  "panel.loading": "正在读取…",
5374
5463
  "panel.retry": "重试",
@@ -5412,15 +5501,22 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5412
5501
  };
5413
5502
  /** English copy, pinned to the Chinese key set. */
5414
5503
  const en = {
5504
+ "files.browse": "Browse files",
5505
+ "files.reload": "Refresh files",
5506
+ "files.empty": "Empty directory",
5507
+ "files.truncated": "More entries were omitted",
5508
+ "files.previewTruncated": "Showing the first 200 lines",
5415
5509
  "action.label": "Folders",
5416
5510
  "action.title": "Manage this session's secondary working directories",
5417
5511
  "panel.title": "Workspace folders",
5418
5512
  "panel.subtitle": "The primary and additional folders follow the current sandbox mode: writable in workspace-write and read-only in read-only.",
5419
- "panel.primary": "Primary working directory",
5420
- "panel.primaryNote": "This workspace's primary working directory; it cannot be removed",
5513
+ "panel.primary": "Primary root",
5514
+ "panel.primaryNote": "The session's original project; it cannot be removed",
5515
+ "panel.cwd": "Current working directory",
5516
+ "panel.cwdNote": "Changing directory does not grant access",
5421
5517
  "panel.additional": "Secondary working directories",
5422
5518
  "panel.empty": "No secondary working directories yet.",
5423
- "panel.emptyHint": "Once added, the agent can use the folder in this session; confined bash follows on macOS/Linux, while Windows currently covers fs writes only.",
5519
+ "panel.emptyHint": "Once added, the agent can use the folder in this session, including confined shells and terminals.",
5424
5520
  "panel.noSession": "No active session",
5425
5521
  "panel.loading": "Loading…",
5426
5522
  "panel.retry": "Retry",
@@ -5517,7 +5613,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5517
5613
  * @throws {PanelError} `panel/bad-response` when the value is not that shape.
5518
5614
  */
5519
5615
  function expectShape(endpoint, value) {
5520
- const parsed = endpoint === "reveal" ? parseRevealedView(value) : parseRootsView(value);
5616
+ const parsed = endpoint === "reveal" ? parseRevealedView(value) : endpoint === "files" ? parseFilesView(value) : endpoint === "readFile" ? parseFilePreview(value) : parseRootsView(value);
5521
5617
  if (!parsed.ok) throw new PanelError("panel/bad-response", parsed.message);
5522
5618
  return parsed.value;
5523
5619
  }
@@ -5893,6 +5989,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5893
5989
  border-radius: 16px;
5894
5990
  }
5895
5991
 
5992
+ .mrfw-fileTree { flex-basis: 100%; min-width: 0; }
5993
+ .mrfw-fileLevel { list-style: none; padding-left: 16px; margin: 4px 0; }
5994
+ .mrfw-fileEntry { display: block; border: 0; background: transparent; color: var(--dsw-alias-label-primary); font: inherit; text-align: left; cursor: pointer; padding: 4px; overflow-wrap: anywhere; }
5995
+ .mrfw-fileEntry:focus-visible { outline: 1px solid var(--dsw-alias-brand-primary); }
5996
+ .mrfw-filePreview { max-height: 320px; overflow: auto; padding: 8px; background: var(--dsw-alias-bg-layer-1); color: var(--dsw-alias-label-primary); font-size: 12px; }
5896
5997
  .mrfw-actions .mrfw-iconBtn {
5897
5998
  width: 32px;
5898
5999
  }
@@ -5935,6 +6036,149 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5935
6036
  const IconPlus = icon("IconPlusOutline16", "IconPlusOutlineRegular");
5936
6037
  const IconTrash = icon("IconTrashOutline16", "IconTrashOutlineRegular");
5937
6038
  //#endregion
6039
+ //#region src/client/RootFileTree.tsx
6040
+ /** Lazy additional-root tree and bounded text previews; requests carry a session and exact root. */
6041
+ function RootFileTree(props) {
6042
+ const [open, setOpen] = (0, react.useState)(false);
6043
+ const [generation, setGeneration] = (0, react.useState)(0);
6044
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6045
+ className: "mrfw-fileTree",
6046
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
6047
+ className: "mrfw-btn mrfw-btnGhost",
6048
+ type: "button",
6049
+ "aria-expanded": open,
6050
+ onClick: () => {
6051
+ setOpen((value) => !value);
6052
+ },
6053
+ children: props.t("files.browse")
6054
+ }), open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
6055
+ className: "mrfw-btn mrfw-btnGhost",
6056
+ type: "button",
6057
+ onClick: () => {
6058
+ setGeneration((value) => value + 1);
6059
+ },
6060
+ children: props.t("files.reload")
6061
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Directory, {
6062
+ ...props,
6063
+ path: "."
6064
+ }, generation)] })]
6065
+ });
6066
+ }
6067
+ function Directory(props) {
6068
+ const [listing, setListing] = (0, react.useState)();
6069
+ const [error, setError] = (0, react.useState)();
6070
+ (0, react.useEffect)(() => {
6071
+ let active = true;
6072
+ setListing(void 0);
6073
+ setError(void 0);
6074
+ const session = props.sessionId();
6075
+ if (session === void 0) {
6076
+ setError(props.t("panel.noSession"));
6077
+ return;
6078
+ }
6079
+ props.panel.call("files", {
6080
+ sessionId: session,
6081
+ entry: props.entry,
6082
+ path: props.path
6083
+ }).then((value) => {
6084
+ if (active && props.sessionId() === session) setListing(value);
6085
+ }, (failure) => {
6086
+ if (active) setError(failure instanceof Error ? failure.message : String(failure));
6087
+ });
6088
+ return () => {
6089
+ active = false;
6090
+ };
6091
+ }, [
6092
+ props.panel,
6093
+ props.entry,
6094
+ props.path,
6095
+ props.sessionId,
6096
+ props.t
6097
+ ]);
6098
+ if (error !== void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6099
+ className: "mrfw-alert",
6100
+ role: "alert",
6101
+ children: error
6102
+ });
6103
+ if (listing === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6104
+ className: "mrfw-note",
6105
+ children: props.t("panel.loading")
6106
+ });
6107
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("ul", {
6108
+ className: "mrfw-fileLevel",
6109
+ children: [
6110
+ listing.entries.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
6111
+ className: "mrfw-note",
6112
+ children: props.t("files.empty")
6113
+ }),
6114
+ [...listing.entries].sort((a, b) => Number(b.type === "directory") - Number(a.type === "directory") || a.name.localeCompare(b.name, void 0, { numeric: true })).map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FileEntry, {
6115
+ ...props,
6116
+ path: props.path === "." ? entry.name : `${props.path}/${entry.name}`,
6117
+ name: entry.name,
6118
+ kind: entry.type
6119
+ }, entry.name)),
6120
+ listing.truncated && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
6121
+ className: "mrfw-note",
6122
+ children: props.t("files.truncated")
6123
+ })
6124
+ ]
6125
+ });
6126
+ }
6127
+ function FileEntry(props) {
6128
+ const [open, setOpen] = (0, react.useState)(false);
6129
+ const [preview, setPreview] = (0, react.useState)();
6130
+ const [error, setError] = (0, react.useState)();
6131
+ const epoch = (0, react.useRef)(0);
6132
+ (0, react.useEffect)(() => () => {
6133
+ epoch.current += 1;
6134
+ }, []);
6135
+ const toggle = async () => {
6136
+ setOpen(!open);
6137
+ const current = ++epoch.current;
6138
+ if (open || props.kind !== "file") return;
6139
+ setPreview(void 0);
6140
+ setError(void 0);
6141
+ const session = props.sessionId();
6142
+ if (session === void 0) {
6143
+ setError(props.t("panel.noSession"));
6144
+ return;
6145
+ }
6146
+ try {
6147
+ const value = await props.panel.call("readFile", {
6148
+ sessionId: session,
6149
+ entry: props.entry,
6150
+ path: props.path
6151
+ });
6152
+ if (current === epoch.current && props.sessionId() === session) setPreview(value);
6153
+ } catch (failure) {
6154
+ if (current === epoch.current) setError(failure instanceof Error ? failure.message : String(failure));
6155
+ }
6156
+ };
6157
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
6158
+ className: "mrfw-fileEntry",
6159
+ type: "button",
6160
+ disabled: props.kind === "other",
6161
+ "aria-expanded": open,
6162
+ onClick: () => {
6163
+ toggle();
6164
+ },
6165
+ children: [props.kind === "directory" ? open ? "▾ " : "▸ " : "", props.name]
6166
+ }), open && (props.kind === "directory" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Directory, { ...props }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: error !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6167
+ role: "alert",
6168
+ className: "mrfw-alert",
6169
+ children: error
6170
+ }) : preview === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6171
+ className: "mrfw-note",
6172
+ children: props.t("panel.loading")
6173
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
6174
+ className: "mrfw-filePreview",
6175
+ children: preview.text
6176
+ }), !preview.eof && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6177
+ className: "mrfw-note",
6178
+ children: props.t("files.previewTruncated")
6179
+ })] }) }))] });
6180
+ }
6181
+ //#endregion
5938
6182
  //#region src/client/WorkspaceFoldersAction.tsx
5939
6183
  /**
5940
6184
  * The Workspace Folders panel: one sidebar footer action that opens a dialog
@@ -6029,10 +6273,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6029
6273
  const [open, setOpen] = (0, react.useState)(false);
6030
6274
  const rail = props.wide === false;
6031
6275
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6032
- className: rail ? "mrfw-triggerRow mrfw-railRow" : "mrfw-triggerRow",
6276
+ className: props.compact ? "mrfw-fileLauncher" : rail ? "mrfw-triggerRow mrfw-railRow" : "mrfw-triggerRow",
6033
6277
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
6034
6278
  type: "button",
6035
- className: rail ? "mrfw-trigger mrfw-triggerRail" : "mrfw-trigger",
6279
+ className: props.compact ? "mrfw-btn mrfw-btnGhost" : rail ? "mrfw-trigger mrfw-triggerRail" : "mrfw-trigger",
6036
6280
  title: props.t("action.title"),
6037
6281
  "aria-haspopup": "dialog",
6038
6282
  "aria-expanded": open,
@@ -6156,7 +6400,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6156
6400
  }));
6157
6401
  try {
6158
6402
  const view = await panel.call("list", base);
6159
- if (!mounted.current || epoch !== requestEpoch.current) return;
6403
+ if (!mounted.current || epoch !== requestEpoch.current || props.sessionId?.() !== base.sessionId) return;
6160
6404
  setState({
6161
6405
  view,
6162
6406
  error: void 0,
@@ -6164,7 +6408,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6164
6408
  noSession: false
6165
6409
  });
6166
6410
  } catch (error) {
6167
- if (!mounted.current || epoch !== requestEpoch.current) return;
6411
+ if (!mounted.current || epoch !== requestEpoch.current || props.sessionId?.() !== base.sessionId) return;
6168
6412
  setState({
6169
6413
  view: void 0,
6170
6414
  error: asPanelError(error),
@@ -6176,6 +6420,28 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6176
6420
  (0, react.useEffect)(() => {
6177
6421
  refresh();
6178
6422
  }, [refresh]);
6423
+ (0, react.useEffect)(() => {
6424
+ let previous = props.sessionId?.();
6425
+ return props.subscribeSession?.(() => {
6426
+ const current = props.sessionId?.();
6427
+ if (current === previous) return;
6428
+ previous = current;
6429
+ requestEpoch.current += 1;
6430
+ setState({
6431
+ view: void 0,
6432
+ error: void 0,
6433
+ busy: false,
6434
+ noSession: current === void 0
6435
+ });
6436
+ setMenuFor(void 0);
6437
+ setAliasFor(void 0);
6438
+ refresh();
6439
+ });
6440
+ }, [
6441
+ props.subscribeSession,
6442
+ props.sessionId,
6443
+ refresh
6444
+ ]);
6179
6445
  /**
6180
6446
  * Submit one change to the host and adopt the view it answers with.
6181
6447
  * @returns whether the host accepted it (the caller decides whether to clear
@@ -6204,7 +6470,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6204
6470
  ...base,
6205
6471
  ...payload
6206
6472
  });
6207
- if (!mounted.current || epoch !== requestEpoch.current) return false;
6473
+ if (!mounted.current || epoch !== requestEpoch.current || props.sessionId?.() !== base.sessionId) return false;
6208
6474
  setState({
6209
6475
  view,
6210
6476
  error: void 0,
@@ -6213,7 +6479,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6213
6479
  });
6214
6480
  return true;
6215
6481
  } catch (error) {
6216
- if (!mounted.current || epoch !== requestEpoch.current) return false;
6482
+ if (!mounted.current || epoch !== requestEpoch.current || props.sessionId?.() !== base.sessionId) return false;
6217
6483
  setState((previous) => ({
6218
6484
  ...previous,
6219
6485
  error: asPanelError(error),
@@ -6277,14 +6543,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6277
6543
  ...base,
6278
6544
  entry: entryOf(root)
6279
6545
  });
6280
- if (!mounted.current || epoch !== requestEpoch.current) return;
6546
+ if (!mounted.current || epoch !== requestEpoch.current || props.sessionId?.() !== base.sessionId) return;
6281
6547
  setState((previous) => ({
6282
6548
  ...previous,
6283
6549
  error: void 0,
6284
6550
  busy: false
6285
6551
  }));
6286
6552
  } catch (error) {
6287
- if (!mounted.current || epoch !== requestEpoch.current) return;
6553
+ if (!mounted.current || epoch !== requestEpoch.current || props.sessionId?.() !== base.sessionId) return;
6288
6554
  setState((previous) => ({
6289
6555
  ...previous,
6290
6556
  error: asPanelError(error),
@@ -6394,6 +6660,22 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6394
6660
  })
6395
6661
  })]
6396
6662
  }),
6663
+ state.view?.effectiveCwd !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
6664
+ className: "mrfw-section",
6665
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6666
+ className: "mrfw-sectionTitleRow",
6667
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6668
+ className: "mrfw-sectionTitle",
6669
+ children: t("panel.cwd")
6670
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6671
+ className: "mrfw-sectionNote",
6672
+ children: t("panel.cwdNote")
6673
+ })]
6674
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6675
+ className: "mrfw-rootPath",
6676
+ children: state.view.effectiveCwd
6677
+ })]
6678
+ }),
6397
6679
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
6398
6680
  className: "mrfw-section",
6399
6681
  children: [
@@ -6533,7 +6815,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6533
6815
  children: t("panel.aliasCancel")
6534
6816
  })
6535
6817
  ]
6536
- }) : null
6818
+ }) : null,
6819
+ root.state === "available" && panel !== void 0 && props.sessionId !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RootFileTree, {
6820
+ panel,
6821
+ entry: entryOf(root),
6822
+ sessionId: props.sessionId,
6823
+ t
6824
+ })
6537
6825
  ]
6538
6826
  }, key);
6539
6827
  })
@@ -6631,6 +6919,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6631
6919
  return await workspace.pickDirectory();
6632
6920
  };
6633
6921
  const sessionId = () => currentSessionIdOf(readService(ctx, "sessions"));
6922
+ const subscribeSession = (listener) => subscribeSessions(readService(ctx, "sessions"), listener);
6634
6923
  const t = ctx.locale.bind(NS);
6635
6924
  ctx.slots.inject("sidebar.footer.action", () => ctx.slots.register({
6636
6925
  name: "sidebar.footer.action",
@@ -6644,10 +6933,23 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6644
6933
  panel,
6645
6934
  pickDirectory,
6646
6935
  sessionId,
6936
+ subscribeSession,
6647
6937
  t,
6648
6938
  ...wide === void 0 ? {} : { wide }
6649
6939
  });
6650
6940
  }));
6941
+ ctx.slots.inject("sidebar.right.tab.files.actions", () => ctx.slots.register({
6942
+ name: "sidebar.right.tab.files.actions",
6943
+ id: SLOT_ID,
6944
+ order: 50
6945
+ }, () => (0, react.createElement)(WorkspaceFoldersAction, {
6946
+ panel,
6947
+ pickDirectory,
6948
+ sessionId,
6949
+ subscribeSession,
6950
+ t,
6951
+ compact: true
6952
+ })));
6651
6953
  }
6652
6954
  /**
6653
6955
  * Read one optional service by name.