@dsh-electron/dsh-plugin-multi-root-workspace 0.1.9 → 0.1.11

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 (62) hide show
  1. package/CHANGELOG.en.md +26 -1
  2. package/CHANGELOG.md +26 -1
  3. package/README.en.md +24 -8
  4. package/README.md +24 -8
  5. package/cordis.patch.yml +4 -0
  6. package/lib/auto-review.js +1 -1
  7. package/lib/client.js +727 -209
  8. package/lib/client.js.map +1 -1
  9. package/lib/{command-Bwd6EGNV.js → command-BGdz7VBH.js} +65 -5
  10. package/lib/command.js +1 -1
  11. package/lib/{compat-CagUNzRB.js → compat-BiW-m_q0.js} +14 -1
  12. package/lib/compat.js +1 -1
  13. package/lib/{contract-DZ4zczDT.js → contract-CStSEOcK.js} +44 -1
  14. package/lib/index.js +6 -5
  15. package/lib/{instructions-CGNS3H0H.js → instructions-MePoRigy.js} +2 -206
  16. package/lib/instructions.js +1 -1
  17. package/lib/llm-message-fYORacvh.js +206 -0
  18. package/lib/metadata-DgqSdVX_.js +35 -0
  19. package/lib/{registry-BClm6wfh.js → registry-KSsmU393.js} +60 -7
  20. package/lib/registry.js +1 -1
  21. package/lib/{review-context-D-ohSVmA.js → review-context-AH-SXHmj.js} +1 -1
  22. package/lib/review-context.js +1 -1
  23. package/lib/scope.js +6 -2
  24. package/lib/skills.js +790 -0
  25. package/lib/types/client/WorkspaceFoldersAction.d.ts +1 -1
  26. package/lib/types/client/locales.d.ts +26 -16
  27. package/lib/types/client/open-in-app/OpenInAppAction.d.ts +5 -3
  28. package/lib/types/client/open-in-app/OpenTargetButton.d.ts +16 -10
  29. package/lib/types/client/open-in-app/adapter.d.ts +5 -1
  30. package/lib/types/client/open-in-app/controller.d.ts +5 -2
  31. package/lib/types/client/open-in-app/device.d.ts +11 -0
  32. package/lib/types/client/open-in-app/locales.d.ts +1 -1
  33. package/lib/types/client/open-in-app/open-path.d.ts +6 -5
  34. package/lib/types/client/open-in-app/open-toast.d.ts +39 -0
  35. package/lib/types/client/open-in-app/outcome.d.ts +23 -0
  36. package/lib/types/client/skills-client.d.ts +23 -0
  37. package/lib/types/client/skills-settings.d.ts +10 -0
  38. package/lib/types/client/skills.d.ts +36 -0
  39. package/lib/types/client/styles.d.ts +1 -1
  40. package/lib/types/command.d.ts +1 -1
  41. package/lib/types/compat/client-session.d.ts +2 -0
  42. package/lib/types/compat/dsh-version.d.ts +5 -5
  43. package/lib/types/compat/open-in-app-device.d.ts +23 -0
  44. package/lib/types/compat/skill-content.d.ts +3 -0
  45. package/lib/types/compat/skills-client.d.ts +33 -0
  46. package/lib/types/compat/skills-settings.d.ts +8 -0
  47. package/lib/types/compat/skills.d.ts +13 -0
  48. package/lib/types/compat/storage-zod.d.ts +1 -0
  49. package/lib/types/contract.d.ts +12 -1
  50. package/lib/types/registry.d.ts +7 -0
  51. package/lib/types/roots.d.ts +4 -1
  52. package/lib/types/scope.d.ts +2 -0
  53. package/lib/types/skills/catalog.d.ts +34 -0
  54. package/lib/types/skills/invocation.d.ts +7 -0
  55. package/lib/types/skills/metadata.d.ts +13 -0
  56. package/lib/types/skills/pool.d.ts +19 -0
  57. package/lib/types/skills/reader.d.ts +3 -0
  58. package/lib/types/skills/routing.d.ts +2 -0
  59. package/lib/types/skills-contract.d.ts +39 -0
  60. package/lib/types/skills.d.ts +43 -0
  61. package/package.json +98 -85
  62. package/lib/types/client/open-in-app/open-failure-toast.d.ts +0 -11
package/lib/client.js CHANGED
@@ -36,12 +36,12 @@ window.__ModuleLoader__.load({
36
36
  }) : target, mod));
37
37
  //#endregion
38
38
  let react = require("react");
39
- let react_dom = require("react-dom");
40
39
  let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
41
40
  _deepseek_ai_dsh_client_ui_primitives = __toESM(_deepseek_ai_dsh_client_ui_primitives, 1);
41
+ let react_jsx_runtime = require("react/jsx-runtime");
42
+ let react_dom = require("react-dom");
42
43
  require("@deepseek-ai/cordis");
43
44
  let _deepseek_ai_dsh_client_store = require("@deepseek-ai/dsh-client-store");
44
- let react_jsx_runtime = require("react/jsx-runtime");
45
45
  //#region src/compat/client-session.ts
46
46
  /**
47
47
  * The session the composed client is showing, when it can name one.
@@ -62,6 +62,17 @@ window.__ModuleLoader__.load({
62
62
  const list = sessions.list;
63
63
  return typeof list?.subscribe === "function" ? list.subscribe(listener) : () => {};
64
64
  }
65
+ /** Selection changes only: activity/title updates must not close a Skill preview. */
66
+ function subscribeCurrentSession(sessions, listener) {
67
+ let previous = currentSessionIdOf(sessions);
68
+ return subscribeSessions(sessions, () => {
69
+ const current = currentSessionIdOf(sessions);
70
+ if (current !== previous) {
71
+ previous = current;
72
+ listener();
73
+ }
74
+ });
75
+ }
65
76
  /**
66
77
  * The list snapshot, when the service actually exposes one.
67
78
  *
@@ -5248,6 +5259,34 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5248
5259
  return /* @__PURE__ */ _superRefine(fn, params);
5249
5260
  }
5250
5261
  //#endregion
5262
+ //#region src/skills-contract.ts
5263
+ /** Browser-safe Skills wire shapes. Session IDs select authority; clients never send paths. */
5264
+ const nameSchema = string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*$/);
5265
+ object({
5266
+ sessionId: string().min(1),
5267
+ name: nameSchema.optional(),
5268
+ generation: number().int().nonnegative().optional()
5269
+ }).strict();
5270
+ const status = {
5271
+ sessionId: string(),
5272
+ generation: number().int().nonnegative(),
5273
+ enabled: boolean()
5274
+ };
5275
+ const skillsStatusSchema = object(status);
5276
+ const skillsCatalogSchema = object({
5277
+ ...status,
5278
+ skills: array(object({
5279
+ name: nameSchema,
5280
+ description: string(),
5281
+ modelInvocable: boolean()
5282
+ }))
5283
+ });
5284
+ const skillsPreviewSchema = object({
5285
+ ...status,
5286
+ name: nameSchema,
5287
+ content: string()
5288
+ });
5289
+ //#endregion
5251
5290
  //#region src/contract.ts
5252
5291
  /**
5253
5292
  * The wire contract between the browser panel and this plugin's host half.
@@ -5291,6 +5330,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5291
5330
  }).strict().optional(),
5292
5331
  id: string().optional(),
5293
5332
  path: string().optional(),
5333
+ aliasId: string().optional(),
5334
+ skillsEnabled: boolean().optional(),
5294
5335
  alias: string().optional(),
5295
5336
  access: _enum([
5296
5337
  "inherit",
@@ -5314,6 +5355,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5314
5355
  ordinal: number().int().positive(),
5315
5356
  id: string(),
5316
5357
  path: string(),
5358
+ aliasId: string().optional(),
5359
+ skillsEnabled: boolean().optional(),
5317
5360
  alias: string().optional(),
5318
5361
  addedAt: string(),
5319
5362
  state: _enum([
@@ -5424,6 +5467,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5424
5467
  state: value.state,
5425
5468
  access: value.access ?? "inherit",
5426
5469
  ...value.effectiveAccess === void 0 ? {} : { effectiveAccess: value.effectiveAccess },
5470
+ ...value.aliasId === void 0 ? {} : { aliasId: value.aliasId },
5471
+ ...value.skillsEnabled === void 0 ? {} : { skillsEnabled: value.skillsEnabled },
5427
5472
  ...value.alias === void 0 ? {} : { alias: value.alias },
5428
5473
  ...value.detail === void 0 ? {} : { detail: value.detail }
5429
5474
  };
@@ -5450,13 +5495,20 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5450
5495
  const NS$2 = "multiRootWorkspace";
5451
5496
  /** Chinese copy (the canonical shape). */
5452
5497
  const zh = {
5453
- "panel.mode": "当前会话权限:",
5454
- "panel.effectiveAccess": "当前有效:",
5498
+ "skills.globalEnable": "启用附属根 Skills",
5499
+ "skills.globalHint": "控制所有附属目录的 Skill 发现与新调用;保留每个目录的独立选择。",
5500
+ "skills.settingsFailed": "设置保存失败,请重试。",
5501
+ "skills.userOnly": "仅用户调用",
5502
+ "panel.skills": "目录技能",
5503
+ "panel.skillsEnabled": "此目录技能已启用,点击停用",
5504
+ "panel.skillsDisabled": "此目录技能已停用,点击启用",
5505
+ "panel.aliasIdEdit": "编辑 Skill 命名空间",
5506
+ "panel.aliasIdPlaceholder": "唯一的 kebab-case 标识",
5507
+ "panel.aliasIdInvalid": "命名空间需要修复",
5508
+ "error.invalid-alias-id": "Skill 命名空间须为唯一的 kebab-case,最长 48 字符。",
5509
+ "panel.accessEffective": "{access}({effective})",
5455
5510
  "panel.toolCaps": "工具 {tools} 对附属根始终只读。",
5456
5511
  "panel.invalidCap": "工具权限配置无效,附属根写入全部被拒绝。",
5457
- "mode.read-only": "只读",
5458
- "mode.workspace-write": "读写",
5459
- "mode.danger-full-access": "完全权限(Full-access)",
5460
5512
  "effective.read-only": "只读",
5461
5513
  "effective.read-write": "读写",
5462
5514
  "effective.full-access": "完全权限(Full-access)",
@@ -5465,11 +5517,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5465
5517
  "action.title": "管理本会话的附属工作目录",
5466
5518
  "switcher.manage": "管理工作区目录…",
5467
5519
  "panel.title": "工作区目录",
5468
- "panel.subtitle": "主目录与「跟随会话」的附加目录遵循当前沙箱模式;目录也可固定为只读或读写。",
5469
- "panel.primary": "主根",
5520
+ "panel.primary": "主要工作目录",
5470
5521
  "panel.primaryNote": "会话原始项目,不可移除",
5471
- "panel.cwd": "当前执行目录",
5472
- "panel.cwdNote": "切换目录不会增加授权",
5522
+ "panel.currentAgentCwd": "当前Agent工作目录",
5473
5523
  "panel.additional": "附属工作目录",
5474
5524
  "panel.limitReached": "已达到附加目录数量上限,请先移除一个再添加。",
5475
5525
  "panel.empty": "还没有附属工作目录。",
@@ -5495,12 +5545,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5495
5545
  "panel.moveUp": "上移",
5496
5546
  "panel.moveDown": "下移",
5497
5547
  "access.inherit": "跟随会话",
5498
- "access.readWrite": "固定读写",
5499
- "access.readOnly": "只读",
5548
+ "access.readWrite": "工作区内修改",
5549
+ "access.readOnly": "仅可查看",
5500
5550
  "panel.accessLabel": "访问权限",
5501
5551
  "panel.accessInherit": "设为跟随会话",
5502
- "panel.accessReadOnly": "设为只读",
5503
- "panel.accessReadWrite": "设为固定读写",
5552
+ "panel.accessReadOnly": "仅可查看",
5553
+ "panel.accessReadWrite": "工作区内修改",
5504
5554
  "panel.accessUpgradeConfirm": "将该目录的权限改为「{access}」?在某些沙箱模式下,这将允许 Agent 修改其中的文件。",
5505
5555
  "panel.accessUpgradeTitle": "扩大访问权限",
5506
5556
  "panel.accessUpgradeDo": "修改",
@@ -5532,13 +5582,20 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5532
5582
  };
5533
5583
  /** English copy, pinned to the Chinese key set. */
5534
5584
  const en = {
5535
- "panel.mode": "Current session access:",
5536
- "panel.effectiveAccess": "Effective:",
5585
+ "skills.globalEnable": "Enable additional-root Skills",
5586
+ "skills.globalHint": "Controls discovery and new invocations for all additional folders, preserving each folder’s choice.",
5587
+ "skills.settingsFailed": "Could not save settings. Please try again.",
5588
+ "skills.userOnly": "User invocation only",
5589
+ "panel.skills": "Folder skills",
5590
+ "panel.skillsEnabled": "Skills enabled for this folder. Click to disable.",
5591
+ "panel.skillsDisabled": "Skills disabled for this folder. Click to enable.",
5592
+ "panel.aliasIdEdit": "Edit Skill namespace",
5593
+ "panel.aliasIdPlaceholder": "Unique kebab-case identifier",
5594
+ "panel.aliasIdInvalid": "Namespace needs repair",
5595
+ "error.invalid-alias-id": "Skill namespace must be unique kebab-case, up to 48 characters.",
5596
+ "panel.accessEffective": "{access} ({effective})",
5537
5597
  "panel.toolCaps": "The tools {tools} always have read-only access to additional roots.",
5538
5598
  "panel.invalidCap": "Invalid tool access configuration refuses all additional-root writes.",
5539
- "mode.read-only": "Read-only",
5540
- "mode.workspace-write": "Read-write",
5541
- "mode.danger-full-access": "Full-access",
5542
5599
  "effective.read-only": "Read-only",
5543
5600
  "effective.read-write": "Read-write",
5544
5601
  "effective.full-access": "Full-access",
@@ -5547,11 +5604,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5547
5604
  "action.title": "Manage this session's secondary working directories",
5548
5605
  "switcher.manage": "Manage workspace folders…",
5549
5606
  "panel.title": "Workspace folders",
5550
- "panel.subtitle": "The primary root and inherit folders follow the current sandbox mode; a folder can also be pinned read-only or read-write.",
5551
- "panel.primary": "Primary root",
5607
+ "panel.primary": "Primary working directory",
5552
5608
  "panel.primaryNote": "The session's original project; it cannot be removed",
5553
- "panel.cwd": "Current working directory",
5554
- "panel.cwdNote": "Changing directory does not grant access",
5609
+ "panel.currentAgentCwd": "Current Agent working directory",
5555
5610
  "panel.additional": "Secondary working directories",
5556
5611
  "panel.limitReached": "The additional-folder limit is reached; remove one before adding another.",
5557
5612
  "panel.empty": "No secondary working directories yet.",
@@ -5577,12 +5632,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5577
5632
  "panel.moveUp": "Move up",
5578
5633
  "panel.moveDown": "Move down",
5579
5634
  "access.inherit": "Inherit",
5580
- "access.readWrite": "Read-write",
5581
- "access.readOnly": "Read-only",
5635
+ "access.readWrite": "Workspace Write",
5636
+ "access.readOnly": "Read Only",
5582
5637
  "panel.accessLabel": "Access",
5583
5638
  "panel.accessInherit": "Make inherit",
5584
- "panel.accessReadOnly": "Make read-only",
5585
- "panel.accessReadWrite": "Make read-write",
5639
+ "panel.accessReadOnly": "Read Only",
5640
+ "panel.accessReadWrite": "Workspace Write",
5586
5641
  "panel.accessUpgradeConfirm": "Change this folder's access to \"{access}\"? Under some sandbox modes this lets the agent modify files in it.",
5587
5642
  "panel.accessUpgradeTitle": "Widen access",
5588
5643
  "panel.accessUpgradeDo": "Change",
@@ -5931,7 +5986,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5931
5986
 
5932
5987
  /* ── files-tree fork (verbatim rules, renamed classes) ─────────────────────
5933
5988
  Copied from upstream's FilesBody.module.css (ui-sidebar-files @
5934
- 0.2.1-alpha.2) with local names mapped to mrfw-ft* so the rules cannot
5989
+ 0.2.2-alpha.2) with local names mapped to mrfw-ft* so the rules cannot
5935
5990
  collide with host DOM — and so the fork's own body (which must NOT carry
5936
5991
  data-files-body) stays visible while the rules above hide the native one.
5937
5992
  The header-only rules (root/header/path/tool/status/statusLine/titleIcon)
@@ -5988,8 +6043,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5988
6043
  cursor: pointer;
5989
6044
  }
5990
6045
 
5991
- .mrfw-ftRow:hover {
5992
- background: var(--dsw-alias-interactive-bg-hover);
6046
+ @media (hover: hover) {
6047
+ .mrfw-ftRow:hover {
6048
+ background: var(--dsw-alias-interactive-bg-hover);
6049
+ }
5993
6050
  }
5994
6051
 
5995
6052
  /* The folder glyph rides the tertiary ink, as the workspace tree's does. */
@@ -6017,8 +6074,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6017
6074
  cursor: default;
6018
6075
  }
6019
6076
 
6020
- .mrfw-ftOther:hover {
6021
- background: transparent;
6077
+ @media (hover: hover) {
6078
+ .mrfw-ftOther:hover {
6079
+ background: transparent;
6080
+ }
6022
6081
  }
6023
6082
 
6024
6083
  .mrfw-ftNote {
@@ -6193,13 +6252,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6193
6252
  padding: 0 24px 24px;
6194
6253
  }
6195
6254
 
6196
- .mrfw-description {
6197
- margin: 0 0 8px;
6198
- font-size: 14px;
6199
- line-height: 22px;
6200
- color: var(--dsw-alias-label-tertiary);
6201
- }
6202
-
6203
6255
  .mrfw-alert {
6204
6256
  margin: 0 0 8px;
6205
6257
  font-size: 14px;
@@ -6251,6 +6303,23 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6251
6303
 
6252
6304
  /* A root entry's two lines: the display name on top, the absolute path under
6253
6305
  it in the tertiary voice (the workspace rows recipe of the host sidebar). */
6306
+ /* Match upstream General Settings' DeveloperToolsRow layout. */
6307
+ .mrfw-skillsSettings {
6308
+ display: flex;
6309
+ align-items: center;
6310
+ justify-content: space-between;
6311
+ gap: 24px;
6312
+ padding: 16px 0;
6313
+ border-bottom: 0.5px solid var(--dsw-alias-border-l2);
6314
+ }
6315
+ .mrfw-skillsSettingsText { min-width: 0; }
6316
+ .mrfw-skillsSettingsTitle { font-size: 14px; line-height: 20px; }
6317
+ .mrfw-skillsSettingsDescription {
6318
+ margin-top: 4px;
6319
+ color: var(--dsw-alias-label-secondary);
6320
+ font-size: 12px;
6321
+ line-height: 18px;
6322
+ }
6254
6323
  .mrfw-rootText {
6255
6324
  display: flex;
6256
6325
  flex: 1 1 240px;
@@ -6259,15 +6328,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6259
6328
  min-width: 0;
6260
6329
  }
6261
6330
 
6262
- /* The name line is a flex row: the truncating name text first, the access
6263
- badge beside it never shrinking — baseline-aligned so the badge's smaller
6264
- voice sits on the name's baseline rather than its middle. */
6331
+ /* Keep status tags and permission glyphs visible beside the alias, wrapping
6332
+ onto another line when the dialog is narrow. */
6265
6333
  .mrfw-rootName {
6266
6334
  display: flex;
6267
- align-items: baseline;
6335
+ align-items: center;
6336
+ flex-wrap: wrap;
6268
6337
  gap: 6px;
6269
6338
  min-width: 0;
6270
- overflow: hidden;
6271
6339
  font-size: 14px;
6272
6340
  line-height: 20px;
6273
6341
  font-weight: 500;
@@ -6349,6 +6417,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6349
6417
  transition: background-color var(--ds-transition-duration-fast) var(--ds-ease-in-out);
6350
6418
  }
6351
6419
 
6420
+ .mrfw-btn[aria-busy='true'] {
6421
+ cursor: wait;
6422
+ }
6423
+
6352
6424
  .mrfw-btn:disabled {
6353
6425
  cursor: not-allowed;
6354
6426
  opacity: 0.4;
@@ -6442,8 +6514,23 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6442
6514
  padding: 0 12px;
6443
6515
  }
6444
6516
 
6445
- .mrfw-accessBadge,
6446
- .mrfw-effectiveAccess {
6517
+ .mrfw-cwdTag {
6518
+ flex: none;
6519
+ padding: 0 6px;
6520
+ border-radius: 4px;
6521
+ background: var(--dsw-alias-interactive-bg-hover);
6522
+ color: var(--dsw-alias-label-secondary);
6523
+ font-size: 11px;
6524
+ line-height: 18px;
6525
+ }
6526
+
6527
+ .mrfw-accessBadge {
6528
+ display: inline-flex;
6529
+ align-items: center;
6530
+ gap: 4px;
6531
+ }
6532
+
6533
+ .mrfw-accessBadge {
6447
6534
  flex: none;
6448
6535
  font-size: 12px;
6449
6536
  line-height: 18px;
@@ -6477,6 +6564,336 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6477
6564
  width: 32px;
6478
6565
  }
6479
6566
  `;
6567
+ //#endregion
6568
+ //#region src/compat/skills-client.ts
6569
+ function bindSkillsForm(ctx) {
6570
+ const modern = ctx.get("configForms");
6571
+ if (modern !== void 0) return modern.get("multi-root-skills");
6572
+ return ctx.get("settingsScope")?.bind({ namespace: "multi-root-skills" });
6573
+ }
6574
+ function clientInject(ctx, services, callback) {
6575
+ if (typeof ctx.inject === "function") ctx.inject(services, callback);
6576
+ }
6577
+ function registerNamespaceRule(ctx, rule) {
6578
+ const conversation = ctx.get("uiConversation");
6579
+ return typeof conversation?.userText?.register === "function" ? conversation.userText.register(rule) : void 0;
6580
+ }
6581
+ //#endregion
6582
+ //#region src/client/skills-client.ts
6583
+ /** Session-addressed Connection cache. Every refresh and change notification fences old replies. */
6584
+ var SkillsClient = class {
6585
+ connection;
6586
+ states = /* @__PURE__ */ new Map();
6587
+ closed = false;
6588
+ constructor(connection) {
6589
+ this.connection = connection;
6590
+ }
6591
+ state(id) {
6592
+ let state = this.states.get(id);
6593
+ if (state === void 0) {
6594
+ state = {
6595
+ epoch: 0,
6596
+ listeners: /* @__PURE__ */ new Set()
6597
+ };
6598
+ this.states.set(id, state);
6599
+ }
6600
+ return state;
6601
+ }
6602
+ async call(endpoint, payload, signal) {
6603
+ signal?.throwIfAborted();
6604
+ const result = await this.connection.rpc.call(PANEL_CHANNEL, endpoint, payload, signal);
6605
+ signal?.throwIfAborted();
6606
+ if (!result.ok) throw new Error(result.error?.message ?? "Skills request failed");
6607
+ return result.value;
6608
+ }
6609
+ async list(sessionId, signal) {
6610
+ if (this.closed) throw new Error("Skills client is closed");
6611
+ const state = this.state(sessionId);
6612
+ if (state.pending === void 0) {
6613
+ const epoch = ++state.epoch;
6614
+ state.request?.abort();
6615
+ const request = new AbortController();
6616
+ state.request = request;
6617
+ const pending = (async () => {
6618
+ const value = skillsCatalogSchema.parse(await this.call("skillsList", { sessionId }, request.signal));
6619
+ if (this.closed || value.sessionId !== sessionId || epoch !== state.epoch) throw new Error("Stale Skills response");
6620
+ const changed = JSON.stringify(state.value) !== JSON.stringify(value);
6621
+ state.value = value;
6622
+ if (changed) for (const listener of state.listeners) listener();
6623
+ return value;
6624
+ })();
6625
+ state.pending = pending;
6626
+ pending.then(() => {
6627
+ if (state.pending === pending) delete state.pending;
6628
+ }, () => {
6629
+ if (state.pending === pending) delete state.pending;
6630
+ });
6631
+ }
6632
+ return await withAbort(state.pending, signal);
6633
+ }
6634
+ snapshot(sessionId) {
6635
+ return this.states.get(sessionId)?.value;
6636
+ }
6637
+ async preview(sessionId, name, signal) {
6638
+ const state = this.state(sessionId), epoch = state.epoch;
6639
+ const value = skillsPreviewSchema.parse(await this.call("skillsPreview", {
6640
+ sessionId,
6641
+ name
6642
+ }, signal));
6643
+ if (this.closed || epoch !== state.epoch || value.sessionId !== sessionId || value.name !== name) throw new Error("Stale Skills preview");
6644
+ return value;
6645
+ }
6646
+ subscribe(sessionId, listener) {
6647
+ const state = this.state(sessionId);
6648
+ state.listeners.add(listener);
6649
+ if (state.watching === void 0) {
6650
+ const lifetime = new AbortController();
6651
+ state.watching = lifetime;
6652
+ this.watch(sessionId, state, lifetime.signal);
6653
+ }
6654
+ return () => {
6655
+ state.listeners.delete(listener);
6656
+ if (state.listeners.size === 0) {
6657
+ state.watching?.abort();
6658
+ delete state.watching;
6659
+ state.request?.abort();
6660
+ this.states.delete(sessionId);
6661
+ }
6662
+ };
6663
+ }
6664
+ invalidate(state) {
6665
+ state.epoch++;
6666
+ state.request?.abort();
6667
+ delete state.pending;
6668
+ delete state.value;
6669
+ for (const listener of state.listeners) listener();
6670
+ }
6671
+ reset() {
6672
+ for (const state of this.states.values()) this.invalidate(state);
6673
+ }
6674
+ dispose() {
6675
+ this.closed = true;
6676
+ for (const state of this.states.values()) {
6677
+ this.invalidate(state);
6678
+ state.watching?.abort();
6679
+ }
6680
+ this.states.clear();
6681
+ }
6682
+ async watch(id, state, signal) {
6683
+ while (!signal.aborted && !this.closed) try {
6684
+ const current = state.value ?? await this.list(id, signal);
6685
+ const next = skillsStatusSchema.parse(await this.call("skillsChanges", {
6686
+ sessionId: id,
6687
+ generation: current.generation
6688
+ }, signal));
6689
+ if (next.sessionId !== id) throw new Error("Wrong Skills Session");
6690
+ if (next.generation !== current.generation) this.invalidate(state);
6691
+ } catch {
6692
+ if (signal.aborted || this.closed) return;
6693
+ this.invalidate(state);
6694
+ await new Promise((resolve) => {
6695
+ const finish = () => {
6696
+ clearTimeout(timer);
6697
+ signal.removeEventListener("abort", finish);
6698
+ resolve();
6699
+ };
6700
+ const timer = setTimeout(finish, 1e3);
6701
+ signal.addEventListener("abort", finish, { once: true });
6702
+ });
6703
+ }
6704
+ }
6705
+ };
6706
+ async function withAbort(promise, signal) {
6707
+ signal?.throwIfAborted();
6708
+ if (signal === void 0) return await promise;
6709
+ return await new Promise((resolve, reject) => {
6710
+ const abort = () => reject(signal.reason);
6711
+ signal.addEventListener("abort", abort, { once: true });
6712
+ promise.then((value) => {
6713
+ signal.removeEventListener("abort", abort);
6714
+ if (!signal.aborted) resolve(value);
6715
+ }, (error) => {
6716
+ signal.removeEventListener("abort", abort);
6717
+ reject(error);
6718
+ });
6719
+ });
6720
+ }
6721
+ //#endregion
6722
+ //#region src/client/skills.tsx
6723
+ const namespaceSkillRule = {
6724
+ id: "multi-root-skill",
6725
+ order: 19,
6726
+ match(text, from) {
6727
+ const regex = /(?<!\S)\/([a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*)(?!\S)/g;
6728
+ regex.lastIndex = from;
6729
+ const match = regex.exec(text);
6730
+ return match === null ? void 0 : {
6731
+ id: "multi-root-skill",
6732
+ start: match.index,
6733
+ end: match.index + match[0].length,
6734
+ data: { name: match[1] }
6735
+ };
6736
+ }
6737
+ };
6738
+ function namespaceSource(client, t) {
6739
+ return {
6740
+ trigger: "/",
6741
+ name: "multi-root-skills",
6742
+ order: 3,
6743
+ async candidates({ sessionId }, { query, signal }) {
6744
+ try {
6745
+ const view = client.snapshot(sessionId) ?? await client.list(sessionId, signal);
6746
+ signal.throwIfAborted();
6747
+ return view.skills.filter((skill) => skill.name.toLowerCase().includes(query.toLowerCase())).map((skill) => ({
6748
+ name: skill.name,
6749
+ description: skill.modelInvocable ? skill.description : `${t("skills.userOnly")} · ${skill.description}`
6750
+ }));
6751
+ } catch (error) {
6752
+ if (signal.aborted) return [];
6753
+ throw error;
6754
+ }
6755
+ },
6756
+ onPick({ candidate }) {
6757
+ return { text: `/${candidate.name} ` };
6758
+ },
6759
+ lexicon({ sessionId }) {
6760
+ return client.snapshot(sessionId)?.skills.map((skill) => skill.name);
6761
+ },
6762
+ subscribeLexicon({ sessionId }, listener) {
6763
+ return client.subscribe(sessionId, listener);
6764
+ }
6765
+ };
6766
+ }
6767
+ function installSkillsClient(ctx, t, subscribeSession) {
6768
+ const connection = ctx.get("connection");
6769
+ if (connection === void 0) return;
6770
+ const client = new SkillsClient(connection);
6771
+ ctx.effect(() => () => client.dispose());
6772
+ ctx.effect(() => subscribeSession(() => {
6773
+ client.reset();
6774
+ }));
6775
+ const source = namespaceSource(client, t);
6776
+ clientInject(ctx, ["inputTriggers"], (scope) => {
6777
+ return scope.get("inputTriggers").registerSource(source);
6778
+ });
6779
+ clientInject(ctx, ["uiConversation"], (scope) => {
6780
+ const stop = registerNamespaceRule(scope, namespaceSkillRule);
6781
+ if (stop === void 0) return;
6782
+ const slots = scope.slots;
6783
+ const stopRenderer = slots.inject("conversation.user.text.node", () => slots.register({
6784
+ name: "conversation.user.text.node",
6785
+ key: namespaceSkillRule.id,
6786
+ priority: 100
6787
+ }, ({ node }) => `/${node.data.name}`));
6788
+ return () => {
6789
+ stopRenderer();
6790
+ stop();
6791
+ };
6792
+ });
6793
+ if (typeof ctx.on === "function") ctx.on("connection/reset", () => {
6794
+ client.reset();
6795
+ });
6796
+ }
6797
+ //#endregion
6798
+ //#region src/client/skills-settings.tsx
6799
+ const BUNDLE_KEY = "@dsh-electron/dsh-plugin-multi-root-workspace";
6800
+ function SkillsSettingsCard({ form, t }) {
6801
+ const snapshot = (0, react.useSyncExternalStore)((listener) => form.subscribe(listener), () => form.getSnapshot());
6802
+ const [busy, setBusy] = (0, react.useState)(false), [error, setError] = (0, react.useState)();
6803
+ const mounted = (0, react.useRef)(true);
6804
+ (0, react.useEffect)(() => {
6805
+ mounted.current = true;
6806
+ return () => {
6807
+ mounted.current = false;
6808
+ };
6809
+ }, []);
6810
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6811
+ className: "mrfw-skillsSettings",
6812
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6813
+ className: "mrfw-skillsSettingsText",
6814
+ children: [
6815
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6816
+ className: "mrfw-skillsSettingsTitle",
6817
+ children: t("skills.globalEnable")
6818
+ }),
6819
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6820
+ className: "mrfw-skillsSettingsDescription",
6821
+ children: t("skills.globalHint")
6822
+ }),
6823
+ error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6824
+ role: "alert",
6825
+ children: error
6826
+ }) : null
6827
+ ]
6828
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
6829
+ label: t("skills.globalEnable"),
6830
+ checked: snapshot.value?.enabled ?? true,
6831
+ disabled: busy || snapshot.status !== "ready" || !snapshot.writable,
6832
+ onChange: (enabled) => {
6833
+ setBusy(true);
6834
+ setError(void 0);
6835
+ form.mutate([{
6836
+ op: "set",
6837
+ path: ["enabled"],
6838
+ value: enabled
6839
+ }], snapshot.revision).then((ok) => {
6840
+ if (mounted.current && !ok) setError(t("skills.settingsFailed"));
6841
+ }).catch(() => {
6842
+ if (mounted.current) setError(t("skills.settingsFailed"));
6843
+ }).finally(() => {
6844
+ if (mounted.current) setBusy(false);
6845
+ });
6846
+ }
6847
+ })]
6848
+ });
6849
+ }
6850
+ function installSkillsSettingsCard(ctx, t) {
6851
+ let current;
6852
+ let stop;
6853
+ const candidates = /* @__PURE__ */ new Map();
6854
+ const update = () => {
6855
+ const chosen = [...candidates.entries()].sort(([a], [b]) => Number(b.get("configForms") !== void 0) - Number(a.get("configForms") !== void 0))[0];
6856
+ if (chosen?.[1] === current) return;
6857
+ stop?.();
6858
+ stop = void 0;
6859
+ current = chosen?.[1];
6860
+ if (!chosen) return;
6861
+ const [scope, form] = chosen;
6862
+ const slots = scope.slots;
6863
+ const disposers = [slots.inject("settings.plugin.item", () => slots.register({
6864
+ name: "settings.plugin.item",
6865
+ key: "multi-root-skills",
6866
+ inject: () => ({
6867
+ form,
6868
+ t
6869
+ })
6870
+ }, SkillsSettingsCard)), slots.inject("plugins.bundle.config", () => slots.register({
6871
+ name: "plugins.bundle.config",
6872
+ key: BUNDLE_KEY,
6873
+ inject: () => ({
6874
+ form,
6875
+ t
6876
+ })
6877
+ }, SkillsSettingsCard))];
6878
+ stop = () => {
6879
+ for (const dispose of disposers) dispose();
6880
+ };
6881
+ };
6882
+ for (const service of ["settingsScope", "configForms"]) clientInject(ctx, [service], (scope) => {
6883
+ const form = bindSkillsForm(scope);
6884
+ if (form === void 0) return;
6885
+ candidates.set(scope, form);
6886
+ update();
6887
+ return () => {
6888
+ candidates.delete(scope);
6889
+ update();
6890
+ };
6891
+ });
6892
+ ctx.effect(() => () => {
6893
+ stop?.();
6894
+ candidates.clear();
6895
+ });
6896
+ }
6480
6897
  //#endregion
6481
6898
  //#region src/compat/client-icons.ts
6482
6899
  /**
@@ -6485,7 +6902,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6485
6902
  * Named imports cannot spell both releases: each name is absent from the
6486
6903
  * other release's declaration, so the compiler is told once, here.
6487
6904
  */
6488
- const installed$1 = _deepseek_ai_dsh_client_ui_primitives;
6905
+ const installed$2 = _deepseek_ai_dsh_client_ui_primitives;
6489
6906
  /**
6490
6907
  * Pick the first export name this release actually provides.
6491
6908
  * @param module - the icon module to read.
@@ -6508,7 +6925,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6508
6925
  */
6509
6926
  function maybeIcon(names) {
6510
6927
  for (const name of names) {
6511
- const found = installed$1[name];
6928
+ const found = installed$2[name];
6512
6929
  if (found !== void 0) return found;
6513
6930
  }
6514
6931
  }
@@ -6523,7 +6940,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6523
6940
  }
6524
6941
  /** Resolved once, against the installed module. */
6525
6942
  function icon(pixelName, regularName) {
6526
- return selectIcon(installed$1, [pixelName, regularName]);
6943
+ return selectIcon(installed$2, [pixelName, regularName]);
6527
6944
  }
6528
6945
  const IconChevronDown = icon("IconChevronDownOutline14", "IconChevronDownOutlineRegular");
6529
6946
  const IconChevronUp = icon("IconChevronUpOutline14", "IconChevronUpOutlineRegular");
@@ -6536,11 +6953,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6536
6953
  const IconRefresh = icon("IconRefreshOutline16", "IconRefreshOutlineRegular");
6537
6954
  const IconSettings = icon("IconSettingsOutline16", "IconSettingsOutlineRegular");
6538
6955
  const IconTrash = icon("IconTrashOutline16", "IconTrashOutlineRegular");
6539
- const IconAccessReadWrite = maybeIcon(["IconWorkspaceWriteRegular"]);
6540
- const IconAccessReadOnly = maybeIcon(["IconReadOnlyRegular"]);
6956
+ const IconAccessReadWrite = maybeIcon(["PermissionIconWorkspaceWriteRegular", "IconWorkspaceWriteRegular"]);
6957
+ const IconAccessReadOnly = maybeIcon(["PermissionIconReadOnlyRegular", "IconReadOnlyRegular"]);
6541
6958
  const IconAccessInherit = maybeIcon(["IconWorkspaceTreeOutline16", "IconWorkspaceTreeOutlineRegular"]);
6542
6959
  //#endregion
6543
- //#region node_modules/.pnpm/@deepseek-ai+dsh-typert-protocol@0.2.1-rc.2_@deepseek-ai+cordis@4.0.5/node_modules/@deepseek-ai/dsh-typert-protocol/lib/index.js
6960
+ //#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
6544
6961
  /** The one Remote failure class shared by owners, the Gateway, and consumers. */
6545
6962
  /**
6546
6963
  * One Remote call failure: a real Error carrying its stable code and typed
@@ -7050,7 +7467,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7050
7467
  return n;
7051
7468
  }
7052
7469
  //#endregion
7053
- //#region node_modules/.pnpm/@deepseek-ai+dsh-util-workspace-path@0.2.1-rc.2_@deepseek-ai+cordis@4.0.5/node_modules/@deepseek-ai/dsh-util-workspace-path/lib/index.js
7470
+ //#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
7054
7471
  /**
7055
7472
  * The `dsh-resource://file/…` address grammar: how a file is named across the
7056
7473
  * Sidebar and the resource model, built and parsed without touching a
@@ -7472,7 +7889,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7472
7889
  };
7473
7890
  }
7474
7891
  //#endregion
7475
- //#region node_modules/.pnpm/@deepseek-ai+dsh-host-open-in-app@0.2.1-rc.2_@deepseek-ai+cordis@4.0.5_@deepseek-ai+dsh_90dfa79a7f5aae0c8b5a66b41d5c318b/node_modules/@deepseek-ai/dsh-host-open-in-app/lib/types/shared.js
7892
+ //#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
7476
7893
  var shared_exports = /* @__PURE__ */ __exportAll({
7477
7894
  OPEN_IN_APP_APPS_PATH: () => OPEN_IN_APP_APPS_PATH,
7478
7895
  OPEN_IN_APP_APPS_ROUTE: () => OPEN_IN_APP_APPS_ROUTE,
@@ -7528,7 +7945,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7528
7945
  * Named imports cannot spell both eras: each name is absent from the other
7529
7946
  * era's declaration, so the compiler is told once, here.
7530
7947
  */
7531
- const installed = shared_exports;
7948
+ const installed$1 = shared_exports;
7532
7949
  /**
7533
7950
  * Pick the first export name this release actually provides.
7534
7951
  * @param module - the shared module to read.
@@ -7550,7 +7967,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7550
7967
  * `iconUrl` on runtimes whose glyph gate already passed, and on the pixel era
7551
7968
  * the absolute form is exactly what upstream's own client fetched.
7552
7969
  */
7553
- const OPEN_IN_APP_ICON_URL_PREFIX = selectSharedRoute(installed, ["OPEN_IN_APP_ICON_PREFIX_ROUTE", "OPEN_IN_APP_ICON_PREFIX"]);
7970
+ const OPEN_IN_APP_ICON_URL_PREFIX = selectSharedRoute(installed$1, ["OPEN_IN_APP_ICON_PREFIX_ROUTE", "OPEN_IN_APP_ICON_PREFIX"]);
7554
7971
  //#endregion
7555
7972
  //#region src/client/open-in-app/applications.ts
7556
7973
  /** Label keys for applications supported by this client. */
@@ -7646,10 +8063,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7646
8063
  * @param appId - catalog id from the availability list.
7647
8064
  * @param path - the session's absolute workspace directory.
7648
8065
  * Concurrent gestures are ignored until the current Host request settles.
7649
- * @returns after the host acknowledged the launch; rejects on any failure.
8066
+ * @returns the acknowledged handoff, or `{ kind: 'skipped' }` while another
8067
+ * launch holds the operation, or a named failure when the Host refuses or
8068
+ * cannot be reached. Host and transport failures resolve without rejecting.
7650
8069
  */
7651
8070
  async launch(appId, path) {
7652
- if (this.operation.getSnapshot().phase === "busy") return;
8071
+ if (this.operation.getSnapshot().phase === "busy") return { kind: "skipped" };
7653
8072
  this.operation.set({
7654
8073
  phase: "busy",
7655
8074
  path
@@ -7669,12 +8088,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7669
8088
  phase: "idle",
7670
8089
  path
7671
8090
  });
7672
- } catch (error) {
8091
+ return { kind: "opened" };
8092
+ } catch (_error) {
7673
8093
  this.operation.set({
7674
8094
  phase: "error",
7675
8095
  path
7676
8096
  });
7677
- throw error;
8097
+ return {
8098
+ kind: "failed",
8099
+ failure: "openError"
8100
+ };
7678
8101
  }
7679
8102
  }
7680
8103
  async run() {
@@ -7694,36 +8117,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7694
8117
  /** Dictionary namespace owned by upstream's plugin (verbatim). */
7695
8118
  const NS = "open-in-app";
7696
8119
  //#endregion
7697
- //#region src/client/open-in-app/open-failure-toast.tsx
7698
- /** Per-control failure banner for path gestures. */
7699
- /**
7700
- * Transient failure banner owned by the control that initiated the gesture,
7701
- * so one failed request announces once, from the control the user pressed.
7702
- * @returns `toast` owned by the control and `show` to announce one
7703
- * failure with resolved copy; announcing again replays the banner.
7704
- */
7705
- function useOpenFailureToast() {
7706
- const seq = (0, react.useRef)(0);
7707
- const [banner, setBanner] = (0, react.useState)(null);
7708
- const dismiss = (0, react.useCallback)(() => {
7709
- setBanner(null);
7710
- }, []);
7711
- return {
7712
- toast: banner === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
7713
- text: banner.text,
7714
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutlineRegular, {}),
7715
- onDone: dismiss
7716
- }, banner.seq),
7717
- show: (text) => {
7718
- seq.current += 1;
7719
- setBanner({
7720
- seq: seq.current,
7721
- text
7722
- });
7723
- }
7724
- };
7725
- }
7726
- //#endregion
7727
8120
  //#region src/client/open-in-app/OpenTargetButton.module.css.ts
7728
8121
  /**
7729
8122
  * Fork-facing shim for upstream `OpenTargetButton.module.css`.
@@ -7755,27 +8148,24 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7755
8148
  };
7756
8149
  //#endregion
7757
8150
  //#region src/client/open-in-app/OpenTargetButton.tsx
7758
- /** Shared file and directory opener: one default action, application menu, and per-gesture feedback. */
8151
+ /** Shared file and directory opener: one default action, application menu, and one reported outcome per gesture. */
7759
8152
  /**
7760
- * Serialize gestures and announce their failures through the initiating control's toast.
7761
- * @param execute - target adapter that returns the failure to announce, or null.
7762
- * @param t - localized control copy.
7763
- * @returns the pending state, feedback, and guarded action callback.
8153
+ * Serialize gestures and report the outcome each one settles on to the app-wide
8154
+ * announcer, which outlives this control and the panel holding it.
8155
+ * @param execute - target adapter that settles on the gesture's outcome.
8156
+ * @param report - announcement seat the settled outcome is sent to.
8157
+ * @returns the pending state and the guarded action callback.
7764
8158
  */
7765
- function useOpenTargetGesture(execute, t) {
8159
+ function useOpenTargetGesture(execute, report) {
7766
8160
  const [pending, setPending] = (0, react.useState)(false);
7767
8161
  const inFlight = (0, react.useRef)(false);
7768
- const { toast, show } = useOpenFailureToast();
7769
8162
  return {
7770
8163
  pending,
7771
- toast,
7772
8164
  act: (operation) => {
7773
8165
  if (inFlight.current) return;
7774
8166
  inFlight.current = true;
7775
8167
  setPending(true);
7776
- execute(operation).then((failure) => {
7777
- if (failure !== null) show(t(`path.${failure}`));
7778
- }).finally(() => {
8168
+ execute(operation).then(report).finally(() => {
7779
8169
  inFlight.current = false;
7780
8170
  setPending(false);
7781
8171
  });
@@ -7801,12 +8191,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7801
8191
  * Render identical split buttons for files and directories. File reveal always
7802
8192
  * stays last; it is the default only when no application is registered.
7803
8193
  * @param props - target applications, default selection, and operations.
7804
- * @returns the control and its transient failure feedback.
8194
+ * @returns the control alone: its settled outcomes are announced by the seat it reports to.
7805
8195
  */
7806
8196
  function OpenTargetButton(props) {
7807
8197
  const { applications, defaultId, kind, t } = props;
7808
8198
  const [menuOpen, setMenuOpen] = (0, react.useState)(false);
7809
- const { pending, toast, act } = useOpenTargetGesture(props.execute, t);
8199
+ const { pending, act } = useOpenTargetGesture(props.execute, props.report);
7810
8200
  const preferred = kind === "file" ? applications.find((app) => app.id === defaultId) ?? applications[0] : applications.find((app) => app.id === defaultId);
7811
8201
  const disabled = pending || props.busy === true || props.loading === true;
7812
8202
  const hasMenu = props.loading === true || props.failed || applications.length + (kind === "file" ? 1 : 0) > 1;
@@ -7842,7 +8232,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7842
8232
  source: preferred?.icon ?? null,
7843
8233
  size: props.prominent ? 18 : 13
7844
8234
  }, preferred?.icon);
7845
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
8235
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
7846
8236
  className: css.menuAnchor,
7847
8237
  open: menuOpen && !disabled && hasMenu,
7848
8238
  autoFocus: true,
@@ -7910,13 +8300,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7910
8300
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { size: props.prominent ? 14 : 10 })
7911
8301
  })]
7912
8302
  })
7913
- }), toast] });
8303
+ });
7914
8304
  }
7915
8305
  //#endregion
7916
8306
  //#region src/client/open-in-app/OpenInAppAction.tsx
7917
8307
  /**
7918
8308
  * Adapt the installed directory catalog to the shared opening control.
7919
- * @param props - displayed directory, installed catalog, and launch operations.
8309
+ * @param props - displayed directory, installed catalog, launch operations, and the announcement seat.
7920
8310
  * @returns the shared control, or null without an eligible application.
7921
8311
  */
7922
8312
  function OpenInAppAction(props) {
@@ -7943,19 +8333,101 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7943
8333
  t,
7944
8334
  busy: operation.phase === "busy",
7945
8335
  shortcut,
8336
+ report: props.report,
7946
8337
  execute: async (operation) => {
7947
8338
  const id = operation.kind === "application" ? operation.id : preferred.id;
7948
- try {
7949
- await props.launch(id, absolutePath);
7950
- } catch (_error) {
7951
- return "openError";
7952
- }
7953
- if (operation.kind === "application") props.choose(id);
7954
- return null;
8339
+ const outcome = await props.launch(id, absolutePath);
8340
+ if (outcome.kind === "opened" && operation.kind === "application") props.choose(id);
8341
+ return outcome;
7955
8342
  }
7956
8343
  }, absolutePath);
7957
8344
  }
7958
8345
  //#endregion
8346
+ //#region src/compat/open-in-app-device.ts
8347
+ /**
8348
+ * Compatibility adapter for `isMobileDevice` from
8349
+ * `@deepseek-ai/dsh-client-ui-primitives`.
8350
+ *
8351
+ * ```text
8352
+ * 0.1.5-rc.2 through 0.2.1-rc.4 no `isMobileDevice` export
8353
+ * 0.2.2-alpha.2 + `isMobileDevice` exported
8354
+ * ```
8355
+ *
8356
+ * The open-in-app fork's verbatim `open-toast.tsx` spells the detector as a
8357
+ * primitives import — a name absent before `0.2.2-alpha.2` — so the lookup
8358
+ * is by export name, not by release string (the `client-icons.ts` pattern).
8359
+ * An older runtime falls back to upstream's identical detection: mobile
8360
+ * userAgent, or a touch-capable MacIntel platform, memoized like upstream's.
8361
+ *
8362
+ * @module @dsh-electron/dsh-plugin-multi-root-workspace/compat/open-in-app-device
8363
+ */
8364
+ /** The primitives module, addressed by export name. */
8365
+ const installed = _deepseek_ai_dsh_client_ui_primitives;
8366
+ /** Upstream's own detector when the installed release carries it (verbatim logic below otherwise). */
8367
+ const upstream = typeof installed.isMobileDevice === "function" ? installed.isMobileDevice : void 0;
8368
+ let fallbackResult;
8369
+ /**
8370
+ * Whether this browser is a mobile device — upstream's detection verbatim.
8371
+ * @returns true for a mobile userAgent or a touch-capable macOS surface.
8372
+ */
8373
+ function isMobileDeviceFallback() {
8374
+ fallbackResult ??= /Android|iPhone|iPad|iPod/i.test(navigator.userAgent) || navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1;
8375
+ return fallbackResult;
8376
+ }
8377
+ /**
8378
+ * Resolve the installed release's `isMobileDevice`, or the identical local
8379
+ * fallback on releases that predate the export.
8380
+ * @returns the mobile-device verdict.
8381
+ */
8382
+ function isMobileDevice() {
8383
+ return upstream === void 0 ? isMobileDeviceFallback() : upstream();
8384
+ }
8385
+ //#endregion
8386
+ //#region src/client/open-in-app/open-toast.tsx
8387
+ /**
8388
+ * Create the one outcome store the overlay entry shows.
8389
+ * @returns the observable notice with its report and dismiss actions.
8390
+ */
8391
+ function createOpenInAppToastSource() {
8392
+ const store = (0, _deepseek_ai_dsh_client_store.createSnapshotStore)(null);
8393
+ let seq = 0;
8394
+ return {
8395
+ hooks: { toast: store },
8396
+ report: (outcome) => {
8397
+ if (outcome.kind === "skipped") return;
8398
+ if (outcome.kind !== "failed" && !isMobileDevice()) return;
8399
+ seq += 1;
8400
+ store.set({
8401
+ ...outcome,
8402
+ seq
8403
+ });
8404
+ },
8405
+ dismiss: () => {
8406
+ store.set(null);
8407
+ }
8408
+ };
8409
+ }
8410
+ /**
8411
+ * Render a mobile handoff confirmation or a native-open failure.
8412
+ * @param props - the notice hook, its dismissal, and the locale seat.
8413
+ * @returns the banner on display, or null.
8414
+ */
8415
+ function OpenInAppToast({ useToast, dismiss, t }) {
8416
+ const toast = useToast((current) => current);
8417
+ if (toast === null) return null;
8418
+ const key = `open-in-app-${String(toast.seq)}`;
8419
+ if (toast.kind === "failed") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
8420
+ text: t(toast.failure === "revealError" ? "path.revealError" : "path.openError"),
8421
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutlineRegular, {}),
8422
+ onDone: dismiss
8423
+ }, key);
8424
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
8425
+ text: t(toast.kind === "opened" ? "path.opened" : "path.revealed"),
8426
+ tone: "success",
8427
+ onDone: dismiss
8428
+ }, key);
8429
+ }
8430
+ //#endregion
7959
8431
  //#region src/client/open-in-app/adapter.tsx
7960
8432
  /**
7961
8433
  * The plugin-owned wiring around the verbatim open-in-app fork.
@@ -8020,17 +8492,22 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8020
8492
  if (!REQUIRED_GLYPHS.every((name) => maybeIcon([name]) !== void 0)) return void 0;
8021
8493
  const controller = new OpenInAppController();
8022
8494
  controller.load();
8495
+ const toast = createOpenInAppToastSource();
8023
8496
  return {
8024
- useOpenInAppApps: bindSnapshot(controller.apps),
8025
- useOpenInAppChoice: bindSnapshot(controller.choice),
8026
- useOpenInAppLaunch: bindSnapshot(controller.operation),
8027
- useShortcuts: bindSnapshot(deps.shortcuts ?? NO_SHORTCUTS),
8028
- launch: (appId, path) => controller.launch(appId, path),
8029
- choose: (appId) => {
8030
- controller.choose(appId);
8031
- },
8032
- iconUrl: (appId) => `${OPEN_IN_APP_ICON_URL_PREFIX}/${appId}`,
8033
- t: deps.t
8497
+ toast,
8498
+ adapter: {
8499
+ useOpenInAppApps: bindSnapshot(controller.apps),
8500
+ useOpenInAppChoice: bindSnapshot(controller.choice),
8501
+ useOpenInAppLaunch: bindSnapshot(controller.operation),
8502
+ useShortcuts: bindSnapshot(deps.shortcuts ?? NO_SHORTCUTS),
8503
+ launch: (appId, path) => controller.launch(appId, path),
8504
+ report: toast.report,
8505
+ choose: (appId) => {
8506
+ controller.choose(appId);
8507
+ },
8508
+ iconUrl: (appId) => `${OPEN_IN_APP_ICON_URL_PREFIX}/${appId}`,
8509
+ t: deps.t
8510
+ }
8034
8511
  };
8035
8512
  }
8036
8513
  //#endregion
@@ -8039,7 +8516,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8039
8516
  * The Workspace Folders panel: one sidebar footer action that opens a dialog
8040
8517
  * listing this session's workspace root and the additional roots registered for
8041
8518
  * it, with the actions the milestone requires — Add (through the composed
8042
- * directory picker), Remove, Alias, Copy Path, Reveal, and reordering.
8519
+ * directory picker), Remove, Alias, Copy Path, and Reveal.
8043
8520
  *
8044
8521
  * Why a footer action and its own dialog, instead of a first-class panel: the
8045
8522
  * slots that can host a full panel (`sidebar.panellist` + the keyed `main`
@@ -8065,14 +8542,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8065
8542
  primary: "mrfw-btnPrimary",
8066
8543
  danger: "mrfw-btnDanger"
8067
8544
  };
8068
- /** One small capsule button matching the panel's own styling. */
8545
+ /** One small capsule button matching the panel's own styling.
8546
+ * Pending requests block activation through aria-disabled without changing
8547
+ * opacity, hover or keyboard focus. Native disabled is for unavailable actions.
8548
+ */
8069
8549
  function Action(props) {
8070
8550
  const variant = props.variant ?? "ghost";
8071
8551
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8072
8552
  type: "button",
8073
8553
  className: `mrfw-btn ${VARIANT_CLASS[variant]}`,
8074
- onClick: props.onClick,
8554
+ onClick: props.pending === true ? void 0 : props.onClick,
8075
8555
  disabled: props.disabled === true,
8556
+ "aria-disabled": props.disabled === true || props.pending === true || void 0,
8557
+ "aria-busy": props.pending === true || void 0,
8076
8558
  children: props.children
8077
8559
  });
8078
8560
  }
@@ -8085,13 +8567,35 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8085
8567
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8086
8568
  type: "button",
8087
8569
  className: `mrfw-btn mrfw-iconBtn ${VARIANT_CLASS[variant]}`,
8088
- title: props.label,
8570
+ title: props.hint ?? props.label,
8089
8571
  "aria-label": props.label,
8090
- onClick: props.onClick,
8572
+ "aria-pressed": props.pressed,
8573
+ onClick: props.pending === true ? void 0 : props.onClick,
8091
8574
  disabled: props.disabled === true,
8575
+ "aria-disabled": props.disabled === true || props.pending === true || void 0,
8576
+ "aria-busy": props.pending === true || void 0,
8092
8577
  children: props.icon
8093
8578
  });
8094
8579
  }
8580
+ /** A skill is a reusable instruction document; the star denotes its capability. */
8581
+ function SkillsIcon({ enabled }) {
8582
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
8583
+ width: "16",
8584
+ height: "16",
8585
+ viewBox: "0 0 16 16",
8586
+ fill: "none",
8587
+ stroke: "currentColor",
8588
+ strokeWidth: "1",
8589
+ strokeLinecap: "round",
8590
+ strokeLinejoin: "round",
8591
+ "aria-hidden": "true",
8592
+ children: [
8593
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8 13.5H3a1 1 0 0 1-1-1v-10a1 1 0 0 1 1-1h8a1 1 0 0 1 1 1V7M4.5 4.5h5M4.5 7h3" }),
8594
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "m11.5 8 .85 2.65L15 11.5l-2.65.85-.85 2.65-.85-2.65L8 11.5l2.65-.85Z" }),
8595
+ enabled ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "m2 14 12-12" })
8596
+ ]
8597
+ });
8598
+ }
8095
8599
  /** The 16px close glyph of the dialog's header button. */
8096
8600
  function CloseIcon() {
8097
8601
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
@@ -8111,7 +8615,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8111
8615
  * and reveals a trailing icon on hover — clicking the text or the icon
8112
8616
  * copies the same canonical path. The accessible name swaps to "copied"
8113
8617
  * for the feedback window, the icon button's old recipe. Shared by the
8114
- * primary row, the current-directory line, and every additional root.
8618
+ * primary row and every additional root.
8115
8619
  */
8116
8620
  function PathCopy(props) {
8117
8621
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
@@ -8167,6 +8671,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8167
8671
  function displayNameOf(root) {
8168
8672
  return root.alias ?? basename(root.path);
8169
8673
  }
8674
+ /** Display-only path matching; the host remains the sole authority for grants. */
8675
+ function workingRootOf(view) {
8676
+ if (view?.effectiveCwd === void 0) return void 0;
8677
+ const normalize = (path) => {
8678
+ return (/^[a-z]:[\\/]/i.test(path) || path.startsWith("\\") ? path.replace(/\\/g, "/").toLowerCase() : path).replace(/\/+$/, "") || "/";
8679
+ };
8680
+ const cwd = normalize(view.effectiveCwd);
8681
+ return [view.primaryRoot, ...view.roots.map((root) => root.path)].filter((path) => {
8682
+ const root = normalize(path);
8683
+ return cwd === root || cwd.startsWith(root.endsWith("/") ? root : `${root}/`);
8684
+ }).sort((a, b) => normalize(b).length - normalize(a).length)[0];
8685
+ }
8170
8686
  /** The sidebar footer action plus the dialog it opens. */
8171
8687
  function WorkspaceFoldersAction(props) {
8172
8688
  const [open, setOpen] = (0, react.useState)(false);
@@ -8243,6 +8759,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8243
8759
  const [upgradeFor, setUpgradeFor] = (0, react.useState)(void 0);
8244
8760
  const [aliasFor, setAliasFor] = (0, react.useState)(void 0);
8245
8761
  const [aliasDraft, setAliasDraft] = (0, react.useState)("");
8762
+ const [aliasField, setAliasField] = (0, react.useState)("alias");
8246
8763
  const [copiedId, setCopiedId] = (0, react.useState)(void 0);
8247
8764
  const [menuFor, setMenuFor] = (0, react.useState)(void 0);
8248
8765
  const dialogRef = (0, react.useRef)(null);
@@ -8521,7 +9038,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8521
9038
  }));
8522
9039
  }
8523
9040
  }, []);
8524
- const move = (0, react.useCallback)(async (root, before) => {
9041
+ (0, react.useCallback)(async (root, before) => {
8525
9042
  await mutate("move", before === void 0 ? { entry: entryOf(root) } : {
8526
9043
  entry: entryOf(root),
8527
9044
  beforeEntry: entryOf(before)
@@ -8531,7 +9048,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8531
9048
  const atLimit = state.view !== void 0 && state.view.count >= state.view.limit;
8532
9049
  const IconAccessTrigger = addAccess === "read-only" ? IconAccessReadOnly : addAccess === "read-write" ? IconAccessReadWrite : IconAccessInherit;
8533
9050
  const primaryPath = state.view?.primaryRoot;
8534
- const cwdPath = state.view?.effectiveCwd;
9051
+ const workingRoot = workingRootOf(state.view);
8535
9052
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8536
9053
  className: "mrfw-overlay",
8537
9054
  role: "presentation",
@@ -8568,18 +9085,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8568
9085
  className: "mrfw-note",
8569
9086
  children: t("panel.noSession")
8570
9087
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
8571
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8572
- className: "mrfw-description",
8573
- children: t("panel.subtitle")
8574
- }),
8575
- state.view?.mode === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
8576
- className: "mrfw-note",
8577
- children: [
8578
- t("panel.mode"),
8579
- " ",
8580
- t(`mode.${state.view.mode}`)
8581
- ]
8582
- }),
8583
9088
  state.view?.toolAccessInvalid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8584
9089
  className: "mrfw-note",
8585
9090
  children: t("panel.invalidCap")
@@ -8612,12 +9117,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8612
9117
  className: "mrfw-row mrfw-rowBare",
8613
9118
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8614
9119
  className: "mrfw-rootText",
8615
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9120
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
8616
9121
  className: "mrfw-rootName",
8617
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9122
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8618
9123
  className: "mrfw-rootNameText",
8619
9124
  children: state.view === void 0 ? "…" : state.view.primaryName ?? basename(state.view.primaryRoot)
8620
- })
9125
+ }), primaryPath !== void 0 && workingRoot === primaryPath ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9126
+ className: "mrfw-cwdTag",
9127
+ children: t("panel.currentAgentCwd")
9128
+ }) : null]
8621
9129
  }), primaryPath === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8622
9130
  className: "mrfw-rootPath",
8623
9131
  children: "…"
@@ -8632,26 +9140,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8632
9140
  })
8633
9141
  })]
8634
9142
  }),
8635
- cwdPath !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
8636
- className: "mrfw-section",
8637
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8638
- className: "mrfw-sectionTitleRow",
8639
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8640
- className: "mrfw-sectionTitle",
8641
- children: t("panel.cwd")
8642
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8643
- className: "mrfw-sectionNote",
8644
- children: t("panel.cwdNote")
8645
- })]
8646
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PathCopy, {
8647
- path: cwdPath,
8648
- copied: copiedId === "cwd",
8649
- onCopy: () => {
8650
- copyPath("cwd", cwdPath);
8651
- },
8652
- t
8653
- })]
8654
- }),
8655
9143
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
8656
9144
  className: "mrfw-section",
8657
9145
  children: [
@@ -8674,8 +9162,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8674
9162
  t("panel.emptyHint")
8675
9163
  ]
8676
9164
  }) : null,
8677
- roots.map((root, index) => {
9165
+ roots.map((root) => {
8678
9166
  const key = rowKey(root);
9167
+ const IconAccess = root.access === "read-only" ? IconAccessReadOnly : root.access === "read-write" ? IconAccessReadWrite : IconAccessInherit;
8679
9168
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8680
9169
  className: "mrfw-row",
8681
9170
  children: [
@@ -8688,17 +9177,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8688
9177
  className: "mrfw-rootNameText",
8689
9178
  children: displayNameOf(root)
8690
9179
  }),
8691
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9180
+ workingRoot === root.path ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9181
+ className: "mrfw-cwdTag",
9182
+ children: t("panel.currentAgentCwd")
9183
+ }) : null,
9184
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
8692
9185
  className: "mrfw-accessBadge",
8693
- children: t(accessKey(root.access))
8694
- }),
8695
- root.effectiveAccess === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
8696
- className: "mrfw-effectiveAccess",
8697
- children: [
8698
- t("panel.effectiveAccess"),
8699
- " ",
8700
- t(`effective.${root.effectiveAccess}`)
8701
- ]
9186
+ children: [IconAccess === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconAccess, { size: 14 }), root.effectiveAccess === void 0 ? t(accessKey(root.access)) : t("panel.accessEffective", {
9187
+ access: t(accessKey(root.access)),
9188
+ effective: t(`effective.${root.effectiveAccess}`)
9189
+ })]
8702
9190
  })
8703
9191
  ]
8704
9192
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PathCopy, {
@@ -8716,36 +9204,33 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8716
9204
  children: t(stateKey(root.state))
8717
9205
  }),
8718
9206
  root.state === "available" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconButton, {
8719
- disabled: state.busy,
9207
+ pending: state.busy,
8720
9208
  onClick: () => {
8721
9209
  reRegister(root);
8722
9210
  },
8723
9211
  icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconRefresh, {}),
8724
9212
  label: t("panel.reRegister")
8725
9213
  }),
8726
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconButton, {
8727
- disabled: state.busy,
9214
+ root.skillsEnabled === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconButton, {
9215
+ pending: state.busy,
9216
+ pressed: root.skillsEnabled,
8728
9217
  onClick: () => {
8729
- reveal(root);
9218
+ mutate("setSkillsEnabled", {
9219
+ entry: entryOf(root),
9220
+ skillsEnabled: !root.skillsEnabled
9221
+ });
8730
9222
  },
8731
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconFolderOpen, {}),
8732
- label: t("panel.reveal")
9223
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillsIcon, { enabled: root.skillsEnabled }),
9224
+ label: t("panel.skills"),
9225
+ hint: t(root.skillsEnabled ? "panel.skillsEnabled" : "panel.skillsDisabled")
8733
9226
  }),
8734
9227
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconButton, {
8735
- disabled: state.busy || index === 0,
9228
+ pending: state.busy,
8736
9229
  onClick: () => {
8737
- move(root, roots[index - 1]);
8738
- },
8739
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconChevronUp, {}),
8740
- label: t("panel.moveUp")
8741
- }),
8742
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconButton, {
8743
- disabled: state.busy || index === roots.length - 1,
8744
- onClick: () => {
8745
- move(root, roots[index + 2]);
9230
+ reveal(root);
8746
9231
  },
8747
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconChevronDown, {}),
8748
- label: t("panel.moveDown")
9232
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconFolderOpen, {}),
9233
+ label: t("panel.reveal")
8749
9234
  }),
8750
9235
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
8751
9236
  open: menuFor === key,
@@ -8758,19 +9243,27 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8758
9243
  label: t("panel.rename"),
8759
9244
  icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconEdit, {})
8760
9245
  },
9246
+ {
9247
+ id: "alias-id",
9248
+ label: t("panel.aliasIdEdit"),
9249
+ disabled: state.busy
9250
+ },
8761
9251
  {
8762
9252
  id: "access:inherit",
8763
9253
  label: t("panel.accessInherit"),
9254
+ icon: IconAccessInherit === void 0 ? void 0 : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconAccessInherit, {}),
8764
9255
  disabled: state.busy || root.access === "inherit"
8765
9256
  },
8766
9257
  {
8767
9258
  id: "access:read-only",
8768
9259
  label: t("panel.accessReadOnly"),
9260
+ icon: IconAccessReadOnly === void 0 ? void 0 : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconAccessReadOnly, {}),
8769
9261
  disabled: state.busy || root.access === "read-only"
8770
9262
  },
8771
9263
  {
8772
9264
  id: "access:read-write",
8773
9265
  label: t("panel.accessReadWrite"),
9266
+ icon: IconAccessReadWrite === void 0 ? void 0 : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconAccessReadWrite, {}),
8774
9267
  disabled: state.busy || root.access === "read-write"
8775
9268
  },
8776
9269
  {
@@ -8783,7 +9276,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8783
9276
  ],
8784
9277
  onSelect: (id) => {
8785
9278
  setMenuFor(void 0);
9279
+ if (id === "alias-id") {
9280
+ setAliasField("aliasId");
9281
+ setAliasFor(key);
9282
+ setAliasDraft(root.aliasId ?? "");
9283
+ }
8786
9284
  if (id === "rename") {
9285
+ setAliasField("alias");
8787
9286
  setAliasFor(key);
8788
9287
  setAliasDraft(root.alias ?? "");
8789
9288
  }
@@ -8807,7 +9306,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8807
9306
  align: "end",
8808
9307
  closeOnPointerLeave: true,
8809
9308
  anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconButton, {
8810
- disabled: state.busy,
9309
+ pending: state.busy,
8811
9310
  onClick: () => {
8812
9311
  setMenuFor((current) => current === key ? void 0 : key);
8813
9312
  },
@@ -8822,19 +9321,20 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8822
9321
  autoFocus: true,
8823
9322
  className: "mrfw-input mrfw-inputInline",
8824
9323
  value: aliasDraft,
8825
- placeholder: t("panel.aliasPlaceholder"),
9324
+ placeholder: t(aliasField === "aliasId" ? "panel.aliasIdPlaceholder" : "panel.aliasPlaceholder"),
8826
9325
  onChange: (event) => {
8827
9326
  setAliasDraft(event.target.value);
8828
9327
  }
8829
9328
  }),
8830
9329
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Action, {
8831
- disabled: state.busy,
9330
+ pending: state.busy,
8832
9331
  onClick: () => {
8833
- mutate("alias", {
9332
+ mutate(aliasField === "aliasId" ? "setAliasId" : "alias", {
8834
9333
  entry: entryOf(root),
8835
- alias: aliasDraft
9334
+ [aliasField]: aliasDraft
9335
+ }).then((ok) => {
9336
+ if (ok) setAliasFor(void 0);
8836
9337
  });
8837
- setAliasFor(void 0);
8838
9338
  },
8839
9339
  children: t("panel.aliasSave")
8840
9340
  }),
@@ -8856,7 +9356,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8856
9356
  children: [
8857
9357
  state.noSession || props.pickDirectory === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconButton, {
8858
9358
  variant: "outline",
8859
- disabled: state.busy || atLimit,
9359
+ pending: state.busy,
9360
+ disabled: atLimit,
8860
9361
  onClick: () => {
8861
9362
  addViaPicker();
8862
9363
  },
@@ -8918,9 +9419,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8918
9419
  "aria-label": t("panel.accessLabel"),
8919
9420
  "aria-haspopup": "menu",
8920
9421
  "aria-expanded": accessMenuOpen,
8921
- disabled: state.busy || atLimit,
9422
+ disabled: atLimit,
9423
+ "aria-disabled": state.busy || atLimit || void 0,
9424
+ "aria-busy": state.busy || void 0,
8922
9425
  onClick: () => {
8923
- setAccessMenuOpen((open) => !open);
9426
+ if (!state.busy) setAccessMenuOpen((open) => !open);
8924
9427
  },
8925
9428
  children: [
8926
9429
  IconAccessTrigger === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconAccessTrigger, {}),
@@ -8933,7 +9436,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8933
9436
  onClick: () => {
8934
9437
  addManualPath();
8935
9438
  },
8936
- disabled: state.busy || atLimit || manualPath.trim() === "",
9439
+ pending: state.busy,
9440
+ disabled: atLimit || manualPath.trim() === "",
8937
9441
  variant: "primary",
8938
9442
  children: t("panel.addConfirm")
8939
9443
  })
@@ -8946,7 +9450,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8946
9450
  onClick: () => {
8947
9451
  refresh();
8948
9452
  },
8949
- disabled: state.busy,
9453
+ pending: state.busy,
8950
9454
  variant: "outline",
8951
9455
  children: t("panel.retry")
8952
9456
  })
@@ -8969,7 +9473,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8969
9473
  children: t("panel.aliasCancel")
8970
9474
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Action, {
8971
9475
  variant: "danger",
8972
- disabled: state.busy,
9476
+ pending: state.busy,
8973
9477
  onClick: () => {
8974
9478
  const pending = upgradeFor;
8975
9479
  setUpgradeFor(void 0);
@@ -9301,10 +9805,24 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9301
9805
  const sessionId = () => currentSessionIdOf(readService(ctx, "sessions"));
9302
9806
  const subscribeSession = (listener) => subscribeSessions(readService(ctx, "sessions"), listener);
9303
9807
  const t = ctx.locale.bind(NS$2);
9808
+ installSkillsClient(ctx, t, (listener) => subscribeCurrentSession(readService(ctx, "sessions"), listener));
9809
+ installSkillsSettingsCard(ctx, t);
9304
9810
  const openInApp = createOpenInAppAdapter({
9305
9811
  t: ctx.locale.bind(NS),
9306
9812
  shortcuts: readService(ctx, "shortcuts")?.catalog
9307
9813
  });
9814
+ if (openInApp !== void 0) {
9815
+ const { toast } = openInApp;
9816
+ ctx.slots.inject("shell.overlay", () => ctx.slots.register({
9817
+ name: "shell.overlay",
9818
+ id: "multi-root-open-in-app.toast",
9819
+ locale: NS,
9820
+ inject: () => ({
9821
+ hooks: toast.hooks,
9822
+ dismiss: toast.dismiss
9823
+ })
9824
+ }, OpenInAppToast));
9825
+ }
9308
9826
  const filesTreeSeats = createFilesTreeSeats(ctx);
9309
9827
  const filesTreeT = ctx.locale.bind(NS$1);
9310
9828
  ctx.slots.inject("sidebar.footer.action", () => ctx.slots.register({
@@ -9348,7 +9866,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9348
9866
  sessionId: paneSessionId,
9349
9867
  subscribeSession,
9350
9868
  t,
9351
- openInApp,
9869
+ openInApp: openInApp?.adapter,
9352
9870
  filesTree
9353
9871
  });
9354
9872
  }));