dsh-skill-importer 0.2.3 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -225,14 +225,24 @@ window.__ModuleLoader__.load({
225
225
  });
226
226
  }
227
227
  /** The settings section's page body. */
228
- function SkillImporterSection({ t, useSkills, useWorkspaces, actions }) {
228
+ function SkillImporterSection({ t, useSkills, useSkillIssues, useWorkspaces, actions }) {
229
229
  const skills = useSkills((value) => value);
230
+ const skillIssues = useSkillIssues((value) => value);
230
231
  const workspaces = useWorkspaces((value) => value);
231
- const workspace = (0, react.useMemo)(() => workspaces.items.find((item) => item.workspaceId === workspaces.recentWorkspaceId) ?? workspaces.items[0], [workspaces.items, workspaces.recentWorkspaceId]);
232
+ const [selectedWorkspaceId, setSelectedWorkspaceId] = (0, react.useState)(() => workspaces.recentWorkspaceId ?? workspaces.items[0]?.workspaceId);
233
+ (0, react.useEffect)(() => {
234
+ if (selectedWorkspaceId !== void 0 && workspaces.items.some((item) => item.workspaceId === selectedWorkspaceId)) return;
235
+ setSelectedWorkspaceId(workspaces.recentWorkspaceId ?? workspaces.items[0]?.workspaceId);
236
+ }, [
237
+ selectedWorkspaceId,
238
+ workspaces.items,
239
+ workspaces.recentWorkspaceId
240
+ ]);
241
+ const workspace = (0, react.useMemo)(() => workspaces.items.find((item) => item.workspaceId === selectedWorkspaceId), [selectedWorkspaceId, workspaces.items]);
232
242
  const [listState, setListState] = (0, react.useState)("idle");
233
243
  const [listError, setListError] = (0, react.useState)();
234
244
  const [listEpoch, setListEpoch] = (0, react.useState)(0);
235
- const [collapsedSources, setCollapsedSources] = (0, react.useState)(() => /* @__PURE__ */ new Set());
245
+ const [expandedGroups, setExpandedGroups] = (0, react.useState)(() => /* @__PURE__ */ new Set());
236
246
  (0, react.useEffect)(() => {
237
247
  let cancelled = false;
238
248
  setListState("loading");
@@ -249,7 +259,10 @@ window.__ModuleLoader__.load({
249
259
  };
250
260
  }, [listEpoch, actions]);
251
261
  const [mode, setMode] = (0, react.useState)("file");
252
- const [target, setTarget] = (0, react.useState)("project-agents");
262
+ const [target, setTarget] = (0, react.useState)("project-dsh");
263
+ (0, react.useEffect)(() => {
264
+ if (workspaces.items.length === 0 && target.startsWith("project-")) setTarget("user-dsh");
265
+ }, [target, workspaces.items.length]);
253
266
  const [fileDraft, setFileDraft] = (0, react.useState)();
254
267
  const [fileError, setFileError] = (0, react.useState)();
255
268
  const [url, setUrl] = (0, react.useState)("");
@@ -262,8 +275,41 @@ window.__ModuleLoader__.load({
262
275
  const [batchResults, setBatchResults] = (0, react.useState)();
263
276
  const [replaceNames, setReplaceNames] = (0, react.useState)(() => /* @__PURE__ */ new Set());
264
277
  const [pendingDelete, setPendingDelete] = (0, react.useState)();
278
+ const [locationOpen, setLocationOpen] = (0, react.useState)(false);
279
+ const [workspaceQuery, setWorkspaceQuery] = (0, react.useState)("");
280
+ const [draftTarget, setDraftTarget] = (0, react.useState)("project-dsh");
281
+ const [draftWorkspaceId, setDraftWorkspaceId] = (0, react.useState)();
282
+ const [updateState, setUpdateState] = (0, react.useState)("checking");
283
+ const [updateStatus, setUpdateStatus] = (0, react.useState)();
284
+ const [updateDialogOpen, setUpdateDialogOpen] = (0, react.useState)(false);
285
+ const [commandCopied, setCommandCopied] = (0, react.useState)(false);
265
286
  const [batchConfirmation, setBatchConfirmation] = (0, react.useState)();
266
287
  const fileInput = (0, react.useRef)(null);
288
+ const checkForUpdates = () => {
289
+ setUpdateState("checking");
290
+ actions.checkForUpdates().then((status) => {
291
+ setUpdateStatus(status);
292
+ setUpdateState(status.error === void 0 ? "ready" : "error");
293
+ }, () => setUpdateState("error"));
294
+ };
295
+ (0, react.useEffect)(() => {
296
+ let cancelled = false;
297
+ actions.checkForUpdates().then((status) => {
298
+ if (cancelled) return;
299
+ setUpdateStatus(status);
300
+ setUpdateState(status.error === void 0 ? "ready" : "error");
301
+ }, () => {
302
+ if (!cancelled) setUpdateState("error");
303
+ });
304
+ return () => {
305
+ cancelled = true;
306
+ };
307
+ }, [actions]);
308
+ const copyUpdateCommand = async () => {
309
+ if (updateStatus?.command === void 0) return;
310
+ await navigator.clipboard.writeText(updateStatus.command);
311
+ setCommandCopied(true);
312
+ };
267
313
  const onFilePicked = async (file) => {
268
314
  setFileDraft(void 0);
269
315
  setFileError(void 0);
@@ -314,7 +360,7 @@ window.__ModuleLoader__.load({
314
360
  if (await actions.deleteSkill({
315
361
  name: skill.name,
316
362
  source: skill.source,
317
- workspacePath: workspace?.path
363
+ workspacePath: skill.workspacePath
318
364
  })) {
319
365
  setMessage(t("deleted"));
320
366
  setPendingDelete(void 0);
@@ -454,7 +500,17 @@ window.__ModuleLoader__.load({
454
500
  setTarget(next);
455
501
  if (mode === "batch") resetBatchPreflight();
456
502
  };
457
- const canSubmit = busy || mode === "file" && fileDraft === void 0 || mode === "url" && (url.trim().length === 0 || !/^https?:\/\//i.test(url.trim())) || mode === "batch" && batchPath === void 0 || target !== "user" && workspace === void 0;
503
+ const chooseDestination = (nextTarget, nextWorkspaceId) => {
504
+ if (nextWorkspaceId !== void 0) setSelectedWorkspaceId(nextWorkspaceId);
505
+ changeTarget(nextTarget);
506
+ setLocationOpen(false);
507
+ };
508
+ const locationSummary = target === "project-dsh" ? t("locationSummaryProjectDsh", { title: workspace?.title ?? "" }) : target === "project-agents" ? t("locationSummaryProjectAgents", { title: workspace?.title ?? "" }) : target === "user-dsh" ? t("locationSummaryUserDsh") : t("locationSummaryUserAgents");
509
+ const filteredWorkspaces = workspaces.items.filter((item) => {
510
+ const query = workspaceQuery.trim().toLowerCase();
511
+ return query.length === 0 || item.title.toLowerCase().includes(query) || item.path.toLowerCase().includes(query);
512
+ });
513
+ const canSubmit = busy || mode === "file" && fileDraft === void 0 || mode === "url" && (url.trim().length === 0 || !/^https?:\/\//i.test(url.trim())) || mode === "batch" && batchPath === void 0 || target.startsWith("project-") && workspace === void 0;
458
514
  const runPrimaryAction = () => {
459
515
  if (mode !== "batch") {
460
516
  submit();
@@ -468,13 +524,33 @@ window.__ModuleLoader__.load({
468
524
  if (!skill.modelInvocable && skill.userInvocable) return t("userOnly");
469
525
  if (skill.modelInvocable && !skill.userInvocable) return t("modelOnly");
470
526
  };
471
- const toggleSource = (source) => {
472
- setCollapsedSources((current) => {
527
+ const toggleGroup = (groupKey) => {
528
+ setExpandedGroups((current) => {
473
529
  const next = new Set(current);
474
- if (!next.delete(source)) next.add(source);
530
+ if (!next.delete(groupKey)) next.add(groupKey);
475
531
  return next;
476
532
  });
477
533
  };
534
+ const skillGroups = [
535
+ ...workspaces.items.flatMap((item) => ["project-dsh", "project-agents"].map((source) => ({
536
+ key: `${item.workspaceId}:${source}`,
537
+ source,
538
+ workspacePath: item.path,
539
+ name: source === "project-dsh" ? t("targetProjectDsh", { title: item.title }) : t("targetProjectAgents", { title: item.title })
540
+ }))),
541
+ {
542
+ key: "user-dsh",
543
+ source: "user-dsh",
544
+ workspacePath: void 0,
545
+ name: t("targetUserDsh")
546
+ },
547
+ {
548
+ key: "user-agents",
549
+ source: "user-agents",
550
+ workspacePath: void 0,
551
+ name: t("targetUserAgents")
552
+ }
553
+ ];
478
554
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
479
555
  style: {
480
556
  display: "flex",
@@ -529,23 +605,33 @@ window.__ModuleLoader__.load({
529
605
  listError
530
606
  ]
531
607
  }) : null,
608
+ listState !== "error" && skillIssues.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
609
+ style: {
610
+ ...descriptionStyle,
611
+ color: "var(--dsw-alias-state-warn-label)"
612
+ },
613
+ children: [
614
+ t("installedPartial", { count: skillIssues.length }),
615
+ " ",
616
+ skillIssues[0]?.message
617
+ ]
618
+ }) : null,
532
619
  listState === "idle" && skills.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
533
620
  style: descriptionStyle,
534
621
  children: t("installedEmpty")
535
622
  }) : null,
536
623
  skills.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
537
624
  style: skillGroupsStyle,
538
- children: ["project-agents", "user"].map((source) => {
539
- const rows = skills.filter((skill) => skill.source === source);
625
+ children: skillGroups.map((group) => {
626
+ const rows = skills.filter((skill) => skill.source === group.source && (!group.source.startsWith("project-") || skill.workspacePath === group.workspacePath));
540
627
  if (rows.length === 0) return null;
541
- const sourceName = source === "user" ? t("targetUser") : workspace === void 0 ? t("targetProjectNoWorkspace") : t("targetProjectAgents", { title: workspace.title });
542
- const open = !collapsedSources.has(source);
628
+ const open = expandedGroups.has(group.key);
543
629
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
544
630
  style: skillGroupCardStyle,
545
631
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
546
632
  type: "button",
547
633
  "aria-expanded": open,
548
- onClick: () => toggleSource(source),
634
+ onClick: () => toggleGroup(group.key),
549
635
  style: {
550
636
  ...skillGroupHeaderStyle,
551
637
  borderBottom: open ? skillGroupHeaderStyle.borderBottom : "none"
@@ -559,7 +645,7 @@ window.__ModuleLoader__.load({
559
645
  style: skillGroupIdentityStyle,
560
646
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
561
647
  style: skillGroupNameStyle,
562
- children: sourceName
648
+ children: group.name
563
649
  })
564
650
  }),
565
651
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
@@ -607,10 +693,10 @@ window.__ModuleLoader__.load({
607
693
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TrashIcon, {})
608
694
  })
609
695
  ]
610
- }, skill.source + ":" + skill.name);
696
+ }, skill.source + ":" + (skill.workspacePath ?? "") + ":" + skill.name);
611
697
  })
612
698
  }) : null]
613
- }, source);
699
+ }, group.key);
614
700
  })
615
701
  }) : null
616
702
  ]
@@ -1014,17 +1100,28 @@ window.__ModuleLoader__.load({
1014
1100
  style: fieldLabelStyle,
1015
1101
  children: t("targetLabel")
1016
1102
  }),
1017
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
1018
- value: target,
1019
- onChange: (event) => changeTarget(event.target.value),
1020
- style: selectStyle,
1021
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1022
- value: "project-agents",
1023
- disabled: workspace === void 0,
1024
- children: workspace === void 0 ? t("targetProjectNoWorkspace") : t("targetProjectAgents", { title: workspace.title })
1025
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1026
- value: "user",
1027
- children: t("targetUser")
1103
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1104
+ type: "button",
1105
+ onClick: () => {
1106
+ setWorkspaceQuery("");
1107
+ setDraftTarget(target);
1108
+ setDraftWorkspaceId(selectedWorkspaceId);
1109
+ setLocationOpen(true);
1110
+ },
1111
+ style: locationSummaryStyle,
1112
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1113
+ style: {
1114
+ minWidth: 0,
1115
+ flex: 1,
1116
+ overflow: "hidden",
1117
+ textOverflow: "ellipsis",
1118
+ whiteSpace: "nowrap",
1119
+ textAlign: "left"
1120
+ },
1121
+ children: locationSummary
1122
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1123
+ style: secondaryButtonStyle,
1124
+ children: t("locationChange")
1028
1125
  })]
1029
1126
  }),
1030
1127
  workspace === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
@@ -1057,6 +1154,191 @@ window.__ModuleLoader__.load({
1057
1154
  ]
1058
1155
  })
1059
1156
  }),
1157
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1158
+ style: versionFooterStyle,
1159
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1160
+ style: versionCopyStyle,
1161
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1162
+ style: versionLabelStyle,
1163
+ children: updateStatus === void 0 ? t("versionChecking") : t("versionCurrent", { version: updateStatus.currentVersion })
1164
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1165
+ style: descriptionStyle,
1166
+ children: updateState === "checking" ? t("versionChecking") : updateState === "error" ? t("versionCheckFailed") : updateStatus?.updateAvailable === true ? t("versionAvailable", { version: updateStatus.latestVersion ?? "" }) : t("versionLatest")
1167
+ })]
1168
+ }), updateState === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1169
+ type: "button",
1170
+ onClick: checkForUpdates,
1171
+ style: pillStyle,
1172
+ children: t("versionRetry")
1173
+ }) : updateStatus?.updateAvailable === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1174
+ type: "button",
1175
+ onClick: () => {
1176
+ setCommandCopied(false);
1177
+ setUpdateDialogOpen(true);
1178
+ },
1179
+ style: updateButtonStyle,
1180
+ children: t("versionUpdate")
1181
+ }) : null]
1182
+ }),
1183
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
1184
+ open: locationOpen,
1185
+ onClose: () => setLocationOpen(false),
1186
+ title: t("locationTitle"),
1187
+ closeLabel: t("close"),
1188
+ footer: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1189
+ variant: "outline",
1190
+ onClick: () => setLocationOpen(false),
1191
+ children: t("cancel")
1192
+ }),
1193
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1194
+ style: locationPanelStyle,
1195
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1196
+ style: locationScopeStyle,
1197
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1198
+ type: "button",
1199
+ onClick: () => {
1200
+ if (!draftTarget.startsWith("project-")) setDraftTarget("project-dsh");
1201
+ },
1202
+ style: {
1203
+ ...locationScopeButtonStyle,
1204
+ ...draftTarget.startsWith("project-") ? locationScopeButtonActiveStyle : {}
1205
+ },
1206
+ children: t("locationProject")
1207
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1208
+ type: "button",
1209
+ onClick: () => {
1210
+ if (draftTarget.startsWith("project-")) setDraftTarget("user-dsh");
1211
+ },
1212
+ style: {
1213
+ ...locationScopeButtonStyle,
1214
+ ...!draftTarget.startsWith("project-") ? locationScopeButtonActiveStyle : {}
1215
+ },
1216
+ children: t("locationGlobal")
1217
+ })]
1218
+ }), draftTarget.startsWith("project-") ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1219
+ type: "search",
1220
+ value: workspaceQuery,
1221
+ onChange: (event) => setWorkspaceQuery(event.target.value),
1222
+ placeholder: t("workspaceSearch"),
1223
+ style: controlStyle
1224
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1225
+ style: locationWorkspaceListStyle,
1226
+ children: [filteredWorkspaces.map((item) => {
1227
+ const selected = item.workspaceId === draftWorkspaceId;
1228
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1229
+ style: {
1230
+ ...locationWorkspaceGroupStyle,
1231
+ ...selected ? locationWorkspaceActiveStyle : {}
1232
+ },
1233
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1234
+ type: "button",
1235
+ onClick: (event) => {
1236
+ const group = event.currentTarget.parentElement;
1237
+ setDraftWorkspaceId(selected ? void 0 : item.workspaceId);
1238
+ if (!selected) window.requestAnimationFrame(() => group?.scrollIntoView({
1239
+ block: "nearest",
1240
+ behavior: "smooth"
1241
+ }));
1242
+ },
1243
+ style: locationWorkspaceStyle,
1244
+ "aria-expanded": selected,
1245
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1246
+ style: {
1247
+ minWidth: 0,
1248
+ flex: 1,
1249
+ display: "flex",
1250
+ flexDirection: "column",
1251
+ gap: 2
1252
+ },
1253
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1254
+ style: skillNameStyle,
1255
+ children: item.title
1256
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1257
+ style: skillDescriptionStyle,
1258
+ children: item.path
1259
+ })]
1260
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1261
+ style: skillGroupChevronStyle,
1262
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChevronIcon, { open: selected })
1263
+ })]
1264
+ }), selected ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1265
+ style: locationExpandedRootsStyle,
1266
+ children: [
1267
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1268
+ style: locationRootHintStyle,
1269
+ children: t("chooseRootHint")
1270
+ }),
1271
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1272
+ type: "button",
1273
+ onClick: () => chooseDestination("project-dsh", item.workspaceId),
1274
+ style: locationOptionStyle,
1275
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1276
+ style: locationOptionCopyStyle,
1277
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("rootProjectDsh") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", {
1278
+ style: descriptionStyle,
1279
+ children: ".dsh/skills"
1280
+ })]
1281
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1282
+ style: priorityHighStyle,
1283
+ children: t("priorityHighest")
1284
+ })]
1285
+ }),
1286
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1287
+ type: "button",
1288
+ onClick: () => chooseDestination("project-agents", item.workspaceId),
1289
+ style: locationOptionStyle,
1290
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1291
+ style: locationOptionCopyStyle,
1292
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("rootProjectAgents") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", {
1293
+ style: descriptionStyle,
1294
+ children: ".agents/skills"
1295
+ })]
1296
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1297
+ style: priorityMediumStyle,
1298
+ children: t("priorityHigher")
1299
+ })]
1300
+ })
1301
+ ]
1302
+ }) : null]
1303
+ }, item.workspaceId);
1304
+ }), filteredWorkspaces.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1305
+ style: descriptionStyle,
1306
+ children: t("workspaceNoMatch")
1307
+ }) : null]
1308
+ })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1309
+ style: locationRootsStyle,
1310
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1311
+ type: "button",
1312
+ onClick: () => chooseDestination("user-dsh"),
1313
+ style: locationOptionStyle,
1314
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1315
+ style: locationOptionCopyStyle,
1316
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("rootUserDsh") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", {
1317
+ style: descriptionStyle,
1318
+ children: "~/.dsh/skills"
1319
+ })]
1320
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1321
+ style: priorityLowStyle,
1322
+ children: t("priorityLower")
1323
+ })]
1324
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1325
+ type: "button",
1326
+ onClick: () => chooseDestination("user-agents"),
1327
+ style: locationOptionStyle,
1328
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1329
+ style: locationOptionCopyStyle,
1330
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("rootUserAgents") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", {
1331
+ style: descriptionStyle,
1332
+ children: "~/.agents/skills"
1333
+ })]
1334
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1335
+ style: priorityLowStyle,
1336
+ children: t("priorityLowest")
1337
+ })]
1338
+ })]
1339
+ })]
1340
+ })
1341
+ }),
1060
1342
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
1061
1343
  open: pendingDelete !== void 0,
1062
1344
  onClose: () => {
@@ -1100,6 +1382,34 @@ window.__ModuleLoader__.load({
1100
1382
  },
1101
1383
  children: busy ? t("batchWorking") : t("batchCommit")
1102
1384
  })] })
1385
+ }),
1386
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
1387
+ open: updateDialogOpen,
1388
+ onClose: () => setUpdateDialogOpen(false),
1389
+ title: t("updateTitle"),
1390
+ closeLabel: t("close"),
1391
+ description: t("updateDescription"),
1392
+ footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1393
+ variant: "outline",
1394
+ onClick: () => setUpdateDialogOpen(false),
1395
+ children: t("cancel")
1396
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1397
+ variant: "primary",
1398
+ onClick: () => {
1399
+ copyUpdateCommand();
1400
+ },
1401
+ children: commandCopied ? t("commandCopied") : t("copyCommand")
1402
+ })] }),
1403
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1404
+ style: updateDialogBodyStyle,
1405
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
1406
+ style: updateCommandStyle,
1407
+ children: updateStatus?.command
1408
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1409
+ style: descriptionStyle,
1410
+ children: t("restartHint")
1411
+ })]
1412
+ })
1103
1413
  })
1104
1414
  ]
1105
1415
  });
@@ -1128,6 +1438,58 @@ window.__ModuleLoader__.load({
1128
1438
  lineHeight: "18px",
1129
1439
  color: "var(--dsw-alias-label-caption)"
1130
1440
  };
1441
+ const versionFooterStyle = {
1442
+ minHeight: 58,
1443
+ padding: "12px 0",
1444
+ display: "flex",
1445
+ alignItems: "center",
1446
+ gap: 12,
1447
+ borderTop: "1px solid var(--dsw-alias-border-l2)"
1448
+ };
1449
+ const versionCopyStyle = {
1450
+ flex: 1,
1451
+ minWidth: 0,
1452
+ display: "flex",
1453
+ flexDirection: "column",
1454
+ gap: 2
1455
+ };
1456
+ const versionLabelStyle = {
1457
+ fontSize: 12,
1458
+ lineHeight: "18px",
1459
+ fontWeight: 500,
1460
+ color: "var(--dsw-alias-label-secondary)"
1461
+ };
1462
+ const updateButtonStyle = {
1463
+ display: "inline-flex",
1464
+ alignItems: "center",
1465
+ height: 28,
1466
+ padding: "0 12px",
1467
+ border: "1px solid var(--dsw-alias-interactive-border-primary)",
1468
+ borderRadius: 14,
1469
+ background: "transparent",
1470
+ font: "inherit",
1471
+ fontSize: 13,
1472
+ borderColor: "var(--dsw-alias-interactive-border-primary)",
1473
+ color: "var(--dsw-alias-interactive-label-primary)",
1474
+ cursor: "pointer"
1475
+ };
1476
+ const updateDialogBodyStyle = {
1477
+ display: "flex",
1478
+ flexDirection: "column",
1479
+ gap: 10
1480
+ };
1481
+ const updateCommandStyle = {
1482
+ display: "block",
1483
+ padding: "10px 12px",
1484
+ border: "1px solid var(--dsw-alias-border-l2)",
1485
+ borderRadius: 8,
1486
+ background: "var(--dsw-alias-bg-module-platform)",
1487
+ color: "var(--dsw-alias-label-primary)",
1488
+ fontSize: 12,
1489
+ lineHeight: "18px",
1490
+ whiteSpace: "pre-wrap",
1491
+ wordBreak: "break-all"
1492
+ };
1131
1493
  const pillStyle = {
1132
1494
  display: "inline-flex",
1133
1495
  alignItems: "center",
@@ -1305,18 +1667,7 @@ window.__ModuleLoader__.load({
1305
1667
  color: "var(--dsw-alias-label-primary)",
1306
1668
  outline: "none"
1307
1669
  };
1308
- /** Select variant: replace the OS arrow with the shared 12px chevron inset
1309
- * (same treatment as the composer chips / models page selects). */
1310
- const selectStyle = {
1311
- ...controlStyle,
1312
- appearance: "none",
1313
- paddingRight: 32,
1314
- backgroundImage: "url(\"data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2712%27 height=%2712%27 viewBox=%270 0 12 12%27 fill=%27none%27%3E%3Cpath d=%27M3 4.5L6 7.5L9 4.5%27 stroke=%27%2381858C%27 stroke-width=%271.5%27 stroke-linecap=%27round%27 stroke-linejoin=%27round%27/%3E%3C/svg%3E\")",
1315
- backgroundRepeat: "no-repeat",
1316
- backgroundPosition: "right 12px center",
1317
- backgroundSize: "12px 12px",
1318
- cursor: "pointer"
1319
- };
1670
+ ({ ...controlStyle });
1320
1671
  const entryGridStyle = {
1321
1672
  display: "grid",
1322
1673
  gridTemplateColumns: "repeat(3, minmax(0, 1fr))",
@@ -1417,6 +1768,127 @@ window.__ModuleLoader__.load({
1417
1768
  fontSize: 12,
1418
1769
  lineHeight: "18px"
1419
1770
  };
1771
+ const locationSummaryStyle = {
1772
+ ...controlStyle,
1773
+ display: "flex",
1774
+ alignItems: "center",
1775
+ gap: 10,
1776
+ cursor: "pointer"
1777
+ };
1778
+ const locationPanelStyle = {
1779
+ display: "flex",
1780
+ flexDirection: "column",
1781
+ gap: 14,
1782
+ minWidth: 0
1783
+ };
1784
+ const locationScopeStyle = {
1785
+ display: "grid",
1786
+ gridTemplateColumns: "1fr 1fr",
1787
+ gap: 4,
1788
+ padding: 4,
1789
+ borderRadius: 10,
1790
+ background: "var(--dsw-alias-bg-module-platform)"
1791
+ };
1792
+ const locationScopeButtonStyle = {
1793
+ border: "none",
1794
+ borderRadius: 7,
1795
+ padding: "8px 12px",
1796
+ background: "transparent",
1797
+ color: "var(--dsw-alias-label-secondary)",
1798
+ cursor: "pointer",
1799
+ fontWeight: 500
1800
+ };
1801
+ const locationScopeButtonActiveStyle = {
1802
+ background: "var(--dsw-alias-bg-layer-1)",
1803
+ color: "var(--dsw-alias-label-primary)",
1804
+ boxShadow: "0 1px 3px rgba(0,0,0,.08)"
1805
+ };
1806
+ const locationWorkspaceListStyle = {
1807
+ display: "flex",
1808
+ flexDirection: "column",
1809
+ gap: 4,
1810
+ maxHeight: "min(440px, 52vh)",
1811
+ overflowY: "auto",
1812
+ scrollPadding: "8px 0"
1813
+ };
1814
+ const locationWorkspaceGroupStyle = {
1815
+ overflow: "hidden",
1816
+ borderRadius: 10,
1817
+ transition: "background 140ms ease"
1818
+ };
1819
+ const locationWorkspaceStyle = {
1820
+ width: "100%",
1821
+ display: "flex",
1822
+ alignItems: "center",
1823
+ gap: 8,
1824
+ border: "none",
1825
+ borderRadius: 8,
1826
+ padding: "9px 10px",
1827
+ background: "transparent",
1828
+ cursor: "pointer",
1829
+ textAlign: "left"
1830
+ };
1831
+ const locationWorkspaceActiveStyle = { background: "var(--dsw-alias-bg-module-platform)" };
1832
+ const locationExpandedRootsStyle = {
1833
+ display: "flex",
1834
+ flexDirection: "column",
1835
+ gap: 8,
1836
+ padding: "2px 10px 10px 18px"
1837
+ };
1838
+ const locationRootHintStyle = {
1839
+ fontSize: 11,
1840
+ lineHeight: "16px",
1841
+ fontWeight: 500,
1842
+ color: "var(--dsw-alias-label-tertiary)"
1843
+ };
1844
+ const locationRootsStyle = {
1845
+ display: "flex",
1846
+ flexDirection: "column",
1847
+ gap: 8
1848
+ };
1849
+ const locationOptionStyle = {
1850
+ display: "flex",
1851
+ alignItems: "center",
1852
+ gap: 12,
1853
+ width: "100%",
1854
+ border: "1px solid var(--dsw-alias-border-l2)",
1855
+ borderRadius: 10,
1856
+ padding: "10px 12px",
1857
+ background: "var(--dsw-alias-bg-layer-1)",
1858
+ color: "var(--dsw-alias-label-primary)",
1859
+ cursor: "pointer",
1860
+ textAlign: "left"
1861
+ };
1862
+ const locationOptionCopyStyle = {
1863
+ minWidth: 0,
1864
+ flex: 1,
1865
+ display: "flex",
1866
+ flexDirection: "column",
1867
+ gap: 2
1868
+ };
1869
+ const priorityBaseStyle = {
1870
+ flex: "none",
1871
+ borderRadius: 999,
1872
+ padding: "2px 7px",
1873
+ fontSize: 11,
1874
+ lineHeight: "16px",
1875
+ fontWeight: 600
1876
+ };
1877
+ const priorityHighStyle = {
1878
+ ...priorityBaseStyle,
1879
+ color: "var(--dsw-alias-state-success-primary)",
1880
+ background: "var(--dsw-alias-bg-module-platform)"
1881
+ };
1882
+ const priorityMediumStyle = {
1883
+ ...priorityBaseStyle,
1884
+ color: "var(--dsw-alias-label-primary)",
1885
+ background: "var(--dsw-alias-bg-module-platform)"
1886
+ };
1887
+ const priorityLowStyle = {
1888
+ ...priorityBaseStyle,
1889
+ color: "var(--dsw-alias-label-secondary)",
1890
+ background: "var(--dsw-alias-bg-module-platform)"
1891
+ };
1420
1892
  const previewStyle = {
1421
1893
  display: "flex",
1422
1894
  flexDirection: "column",
@@ -1630,28 +2102,108 @@ window.__ModuleLoader__.load({
1630
2102
  * with `/name ` — the native text gesture — highlighted via the plugin's
1631
2103
  * lexicon contribution.
1632
2104
  */
1633
- /** Find the one composer textarea owned by the picker nearest in the slot tree. */
1634
- function composerTextareaFor(root) {
2105
+ /** Find the one legacy textarea or Lexical contenteditable owned by this picker. */
2106
+ function composerInputFor(root) {
1635
2107
  let node = root?.parentElement;
1636
2108
  while (node !== null && node !== void 0) {
1637
- const textareas = node.querySelectorAll("textarea");
1638
- if (textareas.length > 0) return textareas.length === 1 ? textareas[0] : void 0;
2109
+ const inputs = node.querySelectorAll("textarea, [data-composer-input]");
2110
+ if (inputs.length > 0) return inputs.length === 1 ? inputs[0] : void 0;
1639
2111
  node = node.parentElement;
1640
2112
  }
1641
2113
  }
2114
+ function composerText(input) {
2115
+ return input instanceof HTMLTextAreaElement ? input.value : input.textContent ?? "";
2116
+ }
2117
+ function composerCaret(input) {
2118
+ if (input instanceof HTMLTextAreaElement) {
2119
+ if (input.selectionStart === null || input.selectionEnd === null) return void 0;
2120
+ return {
2121
+ start: input.selectionStart,
2122
+ end: input.selectionEnd
2123
+ };
2124
+ }
2125
+ const selection = window.getSelection();
2126
+ if (selection === null || selection.rangeCount !== 1) return void 0;
2127
+ const range = selection.getRangeAt(0);
2128
+ if (!input.contains(range.startContainer) || !input.contains(range.endContainer)) return void 0;
2129
+ const offsetOf = (container, offset) => {
2130
+ const prefix = range.cloneRange();
2131
+ prefix.selectNodeContents(input);
2132
+ prefix.setEnd(container, offset);
2133
+ return prefix.toString().length;
2134
+ };
2135
+ return {
2136
+ start: offsetOf(range.startContainer, range.startOffset),
2137
+ end: offsetOf(range.endContainer, range.endOffset)
2138
+ };
2139
+ }
2140
+ function setComposerCaret(input, caret) {
2141
+ input.focus({ preventScroll: true });
2142
+ if (input instanceof HTMLTextAreaElement) {
2143
+ input.setSelectionRange(caret, caret);
2144
+ return;
2145
+ }
2146
+ const range = document.createRange();
2147
+ let remaining = caret;
2148
+ const walker = document.createTreeWalker(input, NodeFilter.SHOW_TEXT);
2149
+ let text = walker.nextNode();
2150
+ while (text !== null) {
2151
+ const length = text.textContent?.length ?? 0;
2152
+ if (remaining <= length) {
2153
+ range.setStart(text, remaining);
2154
+ range.collapse(true);
2155
+ const selection = window.getSelection();
2156
+ selection?.removeAllRanges();
2157
+ selection?.addRange(range);
2158
+ return;
2159
+ }
2160
+ remaining -= length;
2161
+ text = walker.nextNode();
2162
+ }
2163
+ range.selectNodeContents(input);
2164
+ range.collapse(false);
2165
+ const selection = window.getSelection();
2166
+ selection?.removeAllRanges();
2167
+ selection?.addRange(range);
2168
+ }
1642
2169
  /** One row of the composer skill picker. */
1643
- function SkillsPicker({ t, sessionId, useSkills, useInput, inputActions, actions }) {
1644
- const skills = useSkills((value) => value);
2170
+ function SkillsPicker({ t, sessionId, useInput, inputActions, actions }) {
1645
2171
  const input = useInput((value) => value.draft);
2172
+ const [skills, setSkills] = (0, react.useState)([]);
1646
2173
  const [open, setOpen] = (0, react.useState)(false);
2174
+ const [loading, setLoading] = (0, react.useState)(true);
1647
2175
  const [query, setQuery] = (0, react.useState)("");
1648
2176
  const rootRef = (0, react.useRef)(null);
1649
2177
  const draftRef = (0, react.useRef)(input);
1650
2178
  const skillNamesRef = (0, react.useRef)([]);
1651
2179
  const caretFrameRef = (0, react.useRef)();
1652
2180
  (0, react.useEffect)(() => {
1653
- actions.refreshSkills().catch(() => {});
1654
- }, [actions]);
2181
+ let cancelled = false;
2182
+ const refresh = (showLoading) => {
2183
+ if (showLoading) setLoading(true);
2184
+ actions.refreshSkillsForSession(sessionId).then((rows) => {
2185
+ if (!cancelled) {
2186
+ setSkills(rows);
2187
+ setLoading(false);
2188
+ }
2189
+ }, () => {
2190
+ if (!cancelled) {
2191
+ setSkills([]);
2192
+ setLoading(false);
2193
+ }
2194
+ });
2195
+ };
2196
+ refresh(true);
2197
+ const timer = open ? window.setInterval(() => refresh(false), 2e3) : void 0;
2198
+ return () => {
2199
+ cancelled = true;
2200
+ if (timer !== void 0) window.clearInterval(timer);
2201
+ };
2202
+ }, [
2203
+ actions,
2204
+ sessionId,
2205
+ open
2206
+ ]);
1655
2207
  (0, react.useEffect)(() => {
1656
2208
  if (!open) return;
1657
2209
  const onDown = (event) => {
@@ -1667,13 +2219,12 @@ window.__ModuleLoader__.load({
1667
2219
  draftRef.current = input;
1668
2220
  skillNamesRef.current = usable.map((skill) => skill.name);
1669
2221
  const scheduleComposerFocus = (expectedDraft, caret) => {
1670
- const textarea = composerTextareaFor(rootRef.current);
2222
+ const input = composerInputFor(rootRef.current);
1671
2223
  if (caretFrameRef.current !== void 0) window.cancelAnimationFrame(caretFrameRef.current);
1672
2224
  caretFrameRef.current = window.requestAnimationFrame(() => {
1673
2225
  caretFrameRef.current = void 0;
1674
- if (textarea === void 0 || !textarea.isConnected || textarea.value !== expectedDraft) return;
1675
- textarea.focus({ preventScroll: true });
1676
- textarea.setSelectionRange(caret, caret);
2226
+ if (input === void 0 || !input.isConnected || composerText(input) !== expectedDraft) return;
2227
+ setComposerCaret(input, caret);
1677
2228
  });
1678
2229
  };
1679
2230
  (0, react.useEffect)(() => {
@@ -1695,18 +2246,20 @@ window.__ModuleLoader__.load({
1695
2246
  if (event.defaultPrevented || event.isComposing || event.keyCode === 229) return;
1696
2247
  if (event.metaKey || event.ctrlKey || event.altKey) return;
1697
2248
  if (event.shiftKey && event.key !== "Backspace") return;
1698
- const textarea = event.target;
1699
- if (!(textarea instanceof HTMLTextAreaElement) || document.activeElement !== textarea) return;
1700
- if (composerTextareaFor(rootRef.current) !== textarea || textarea.value !== draftRef.current) return;
1701
- const start = textarea.selectionStart;
1702
- const end = textarea.selectionEnd;
1703
- if (start === null || end === null || start !== end) return;
2249
+ const target = event.target;
2250
+ if (!(target instanceof Node)) return;
2251
+ const input = composerInputFor(rootRef.current);
2252
+ if (input === void 0 || document.activeElement !== input || !input.contains(target)) return;
2253
+ if (composerText(input) !== draftRef.current) return;
2254
+ const selection = composerCaret(input);
2255
+ if (selection === void 0 || selection.start !== selection.end) return;
2256
+ const start = selection.start;
1704
2257
  if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
1705
2258
  const caret = atomicSkillArrow(draftRef.current, start, event.key === "ArrowLeft" ? "left" : "right", skillNamesRef.current);
1706
2259
  if (caret === void 0) return;
1707
2260
  event.preventDefault();
1708
2261
  event.stopPropagation();
1709
- textarea.setSelectionRange(caret, caret);
2262
+ setComposerCaret(input, caret);
1710
2263
  return;
1711
2264
  }
1712
2265
  const edit = atomicSkillBackspace(draftRef.current, start, skillNamesRef.current);
@@ -1717,8 +2270,8 @@ window.__ModuleLoader__.load({
1717
2270
  if (caretFrameRef.current !== void 0) window.cancelAnimationFrame(caretFrameRef.current);
1718
2271
  caretFrameRef.current = window.requestAnimationFrame(() => {
1719
2272
  caretFrameRef.current = void 0;
1720
- if (!textarea.isConnected || document.activeElement !== textarea || textarea.value !== edit.draft) return;
1721
- textarea.setSelectionRange(edit.caret, edit.caret);
2273
+ if (!input.isConnected || document.activeElement !== input || composerText(input) !== edit.draft) return;
2274
+ setComposerCaret(input, edit.caret);
1722
2275
  });
1723
2276
  };
1724
2277
  document.addEventListener("keydown", onKeyDown, true);
@@ -1727,7 +2280,6 @@ window.__ModuleLoader__.load({
1727
2280
  if (caretFrameRef.current !== void 0) window.cancelAnimationFrame(caretFrameRef.current);
1728
2281
  };
1729
2282
  }, [inputActions]);
1730
- if (usable.length === 0) return null;
1731
2283
  const filtered = query.trim().length === 0 ? usable : usable.filter((skill) => skill.name.toLowerCase().includes(query.trim().toLowerCase()));
1732
2284
  const onSelect = (name) => {
1733
2285
  const prefix = `/${name} `;
@@ -1743,7 +2295,10 @@ window.__ModuleLoader__.load({
1743
2295
  type: "button",
1744
2296
  "aria-label": t("pickerLabel"),
1745
2297
  "aria-expanded": open,
1746
- onClick: () => setOpen((value) => !value),
2298
+ onClick: () => {
2299
+ if (!open) setLoading(true);
2300
+ setOpen((value) => !value);
2301
+ },
1747
2302
  style: triggerStyle,
1748
2303
  children: [
1749
2304
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
@@ -1775,7 +2330,10 @@ window.__ModuleLoader__.load({
1775
2330
  placeholder: t("pickerSearchPlaceholder"),
1776
2331
  style: searchStyle
1777
2332
  }),
1778
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
2333
+ loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2334
+ style: emptyStyle,
2335
+ children: t("pickerLoading")
2336
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
1779
2337
  style: listStyle,
1780
2338
  children: filtered.map((skill) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1781
2339
  type: "button",
@@ -1785,13 +2343,19 @@ window.__ModuleLoader__.load({
1785
2343
  style: avatarStyle,
1786
2344
  "aria-hidden": "true",
1787
2345
  children: skill.name.charAt(0).toUpperCase()
1788
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1789
- style: rowLabelStyle,
1790
- children: skill.name
2346
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2347
+ style: rowCopyStyle,
2348
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2349
+ style: rowLabelStyle,
2350
+ children: skill.name
2351
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2352
+ style: rowDetailStyle,
2353
+ children: skill.source === "project-dsh" ? t("pickerSourceProjectDsh") : skill.source === "project-agents" ? t("pickerSourceProjectAgents") : skill.source === "user-dsh" ? t("pickerSourceUserDsh") : t("pickerSourceUserAgents")
2354
+ })]
1791
2355
  })]
1792
2356
  }) }, skill.name))
1793
2357
  }),
1794
- filtered.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2358
+ !loading && filtered.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1795
2359
  style: emptyStyle,
1796
2360
  children: t("pickerEmpty")
1797
2361
  }) : null
@@ -1906,12 +2470,41 @@ window.__ModuleLoader__.load({
1906
2470
  minWidth: 0,
1907
2471
  overflow: "hidden"
1908
2472
  };
2473
+ const rowCopyStyle = {
2474
+ display: "flex",
2475
+ flexDirection: "column",
2476
+ minWidth: 0,
2477
+ flex: 1,
2478
+ lineHeight: "18px"
2479
+ };
2480
+ const rowDetailStyle = {
2481
+ overflow: "hidden",
2482
+ textOverflow: "ellipsis",
2483
+ whiteSpace: "nowrap",
2484
+ color: "var(--dsw-alias-label-caption)",
2485
+ fontSize: 11,
2486
+ lineHeight: "16px"
2487
+ };
1909
2488
  const emptyStyle = {
1910
2489
  padding: "8px 10px",
1911
2490
  fontSize: 12,
1912
2491
  color: "var(--dsw-alias-label-caption)"
1913
2492
  };
1914
2493
  //#endregion
2494
+ //#region src/client/effectiveSkills.ts
2495
+ const SOURCE_RANK = {
2496
+ "project-dsh": 100,
2497
+ "project-agents": 200,
2498
+ "user-dsh": 400,
2499
+ "user-agents": 500
2500
+ };
2501
+ /** Mirror DSH's name-based winner selection for one session-scoped catalog. */
2502
+ function effectiveSkills(rows) {
2503
+ const winners = /* @__PURE__ */ new Map();
2504
+ for (const row of [...rows].sort((a, b) => SOURCE_RANK[a.source] - SOURCE_RANK[b.source])) if (!winners.has(row.name)) winners.set(row.name, row);
2505
+ return [...winners.values()].sort((a, b) => a.name.localeCompare(b.name));
2506
+ }
2507
+ //#endregion
1915
2508
  //#region src/client/locales.ts
1916
2509
  /**
1917
2510
  * Locale dictionaries for the skill importer settings section.
@@ -1928,7 +2521,12 @@ window.__ModuleLoader__.load({
1928
2521
  pickerLabel: "技能",
1929
2522
  pickerPlaceholder: "技能",
1930
2523
  pickerSearchPlaceholder: "筛选技能…",
2524
+ pickerLoading: "正在同步当前项目技能…",
1931
2525
  pickerEmpty: "没有匹配的技能",
2526
+ pickerSourceProjectDsh: "当前项目 · .dsh",
2527
+ pickerSourceProjectAgents: "当前项目 · .agents",
2528
+ pickerSourceUserDsh: "全局 · .dsh",
2529
+ pickerSourceUserAgents: "全局 · .agents",
1932
2530
  commandDescription: "选择技能:弹出技能列表,选中后填入 /<名称>",
1933
2531
  entryTitle: "技能入口",
1934
2532
  entryDescription: "通过输入框工具行的技能下拉或 /skills 命令选择技能;也可直接输入 /<名称>。选中的技能会高亮并注入指令。",
@@ -1944,6 +2542,7 @@ window.__ModuleLoader__.load({
1944
2542
  deleteFailed: "删除失败",
1945
2543
  deleted: "已删除。",
1946
2544
  installedFailed: "无法读取技能列表",
2545
+ installedPartial: "有 {count} 个技能目录暂时无法读取,其他技能已正常显示。",
1947
2546
  userOnly: "仅斜杠可用",
1948
2547
  modelOnly: "仅模型可用",
1949
2548
  importTitle: "导入技能",
@@ -1994,15 +2593,48 @@ window.__ModuleLoader__.load({
1994
2593
  batchStatusSkipped: "已跳过",
1995
2594
  batchStatusError: "失败",
1996
2595
  targetLabel: "安装到",
1997
- targetUser: "全局技能(~/.dsh/skills)",
1998
- targetProjectAgents: "项目技能({title} · .agents/skills)",
2596
+ targetProjectDsh: "DSH 项目技能({title})",
2597
+ targetProjectAgents: "通用项目技能({title})",
2598
+ targetUserDsh: "DSH 全局技能",
2599
+ targetUserAgents: "通用全局技能",
2600
+ locationChange: "更改",
2601
+ locationTitle: "选择安装位置",
2602
+ locationProject: "项目技能",
2603
+ locationGlobal: "全局技能",
2604
+ workspaceSearch: "搜索工作区…",
2605
+ workspaceNoMatch: "没有匹配的工作区",
2606
+ chooseRootHint: "选择技能目录",
2607
+ rootProjectDsh: "DSH 项目技能",
2608
+ rootProjectAgents: "通用项目技能",
2609
+ rootUserDsh: "DSH 全局技能",
2610
+ rootUserAgents: "通用全局技能",
2611
+ priorityHighest: "最高",
2612
+ priorityHigher: "较高",
2613
+ priorityLower: "较低",
2614
+ priorityLowest: "最低",
2615
+ locationSummaryProjectDsh: "项目技能 · {title} · .dsh/skills",
2616
+ locationSummaryProjectAgents: "项目技能 · {title} · .agents/skills",
2617
+ locationSummaryUserDsh: "全局技能 · ~/.dsh/skills",
2618
+ locationSummaryUserAgents: "全局技能 · ~/.agents/skills",
1999
2619
  targetProjectNoWorkspace: "项目技能(需先添加工作区)",
2000
2620
  noWorkspace: "未注册工作区:请先在会话页选择/创建工作区,或改用用户技能目录。",
2001
2621
  import: "导入",
2002
2622
  importing: "导入中…",
2003
2623
  imported: "已写入:",
2004
2624
  importFailed: "导入失败",
2005
- hostUnreachable: "无法连接 dsh 服务(插件路由未生效)。请确认插件已安装,并重启 dsh web 后刷新页面。"
2625
+ hostUnreachable: "无法连接 dsh 服务(插件路由未生效)。请确认插件已安装,并重启 dsh web 后刷新页面。",
2626
+ versionChecking: "正在检查更新…",
2627
+ versionCurrent: "当前版本 v{version}",
2628
+ versionLatest: "已是最新版本",
2629
+ versionAvailable: "发现新版本 v{version}",
2630
+ versionCheckFailed: "暂时无法检查更新",
2631
+ versionRetry: "重试",
2632
+ versionUpdate: "更新",
2633
+ updateTitle: "更新 dsh-skill-importer",
2634
+ updateDescription: "复制下面的精确版本命令,在终端运行后重启 dsh web。",
2635
+ copyCommand: "复制更新命令",
2636
+ commandCopied: "已复制",
2637
+ restartHint: "更新完成后需要重启 dsh web 才会生效。"
2006
2638
  };
2007
2639
  /** English dictionary, checked complete against the zh key set. */
2008
2640
  const en = {
@@ -2010,7 +2642,12 @@ window.__ModuleLoader__.load({
2010
2642
  pickerLabel: "Skills",
2011
2643
  pickerPlaceholder: "Skills",
2012
2644
  pickerSearchPlaceholder: "Filter skills…",
2645
+ pickerLoading: "Syncing skills for the current project…",
2013
2646
  pickerEmpty: "No matching skills",
2647
+ pickerSourceProjectDsh: "Current project · .dsh",
2648
+ pickerSourceProjectAgents: "Current project · .agents",
2649
+ pickerSourceUserDsh: "Global · .dsh",
2650
+ pickerSourceUserAgents: "Global · .agents",
2014
2651
  commandDescription: "Pick a skill: opens a popup list; selecting fills /<name>",
2015
2652
  entryTitle: "Skill entry points",
2016
2653
  entryDescription: "Pick a skill from the composer tool-row dropdown or the /skills command; or type /<name> directly. The picked skill is highlighted and its instructions are injected.",
@@ -2026,6 +2663,7 @@ window.__ModuleLoader__.load({
2026
2663
  deleteFailed: "Failed to delete",
2027
2664
  deleted: "Deleted.",
2028
2665
  installedFailed: "Unable to read the skill list",
2666
+ installedPartial: "{count} skill directories are temporarily unreadable; other skills are still shown.",
2029
2667
  userOnly: "Slash only",
2030
2668
  modelOnly: "Model only",
2031
2669
  importTitle: "Import a skill",
@@ -2076,24 +2714,91 @@ window.__ModuleLoader__.load({
2076
2714
  batchStatusSkipped: "Skipped",
2077
2715
  batchStatusError: "Failed",
2078
2716
  targetLabel: "Install into",
2079
- targetUser: "Global skills (~/.dsh/skills)",
2080
- targetProjectAgents: "Project skills ({title} · .agents/skills)",
2717
+ targetProjectDsh: "DSH project skills ({title})",
2718
+ targetProjectAgents: "Shared project skills ({title})",
2719
+ targetUserDsh: "DSH global skills",
2720
+ targetUserAgents: "Shared global skills",
2721
+ locationChange: "Change",
2722
+ locationTitle: "Choose install location",
2723
+ locationProject: "Project skills",
2724
+ locationGlobal: "Global skills",
2725
+ workspaceSearch: "Search workspaces…",
2726
+ workspaceNoMatch: "No matching workspaces",
2727
+ chooseRootHint: "Choose a skill directory",
2728
+ rootProjectDsh: "DSH project skills",
2729
+ rootProjectAgents: "Shared project skills",
2730
+ rootUserDsh: "DSH global skills",
2731
+ rootUserAgents: "Shared global skills",
2732
+ priorityHighest: "Highest",
2733
+ priorityHigher: "Higher",
2734
+ priorityLower: "Lower",
2735
+ priorityLowest: "Lowest",
2736
+ locationSummaryProjectDsh: "Project · {title} · .dsh/skills",
2737
+ locationSummaryProjectAgents: "Project · {title} · .agents/skills",
2738
+ locationSummaryUserDsh: "Global · ~/.dsh/skills",
2739
+ locationSummaryUserAgents: "Global · ~/.agents/skills",
2081
2740
  targetProjectNoWorkspace: "Project skills (add a workspace first)",
2082
2741
  noWorkspace: "No registered workspace: create/select one in a session, or use the user skill directory.",
2083
2742
  import: "Import",
2084
2743
  importing: "Importing…",
2085
2744
  imported: "Written to: ",
2086
2745
  importFailed: "Import failed",
2087
- hostUnreachable: "Cannot reach the dsh service (plugin routes are not active). Make sure the plugin is installed, restart dsh web, and refresh the page."
2746
+ hostUnreachable: "Cannot reach the dsh service (plugin routes are not active). Make sure the plugin is installed, restart dsh web, and refresh the page.",
2747
+ versionChecking: "Checking for updates…",
2748
+ versionCurrent: "Current version v{version}",
2749
+ versionLatest: "Up to date",
2750
+ versionAvailable: "Version v{version} is available",
2751
+ versionCheckFailed: "Unable to check for updates",
2752
+ versionRetry: "Retry",
2753
+ versionUpdate: "Update",
2754
+ updateTitle: "Update dsh-skill-importer",
2755
+ updateDescription: "Copy this exact-version command, run it in a terminal, then restart dsh web.",
2756
+ copyCommand: "Copy update command",
2757
+ commandCopied: "Copied",
2758
+ restartHint: "Restart dsh web after the update for it to take effect."
2088
2759
  };
2089
2760
  //#endregion
2090
2761
  //#region src/client/index.ts
2091
- /** Required services (cordis fiber inject). */
2762
+ /** Every client service used during apply is explicit; alpha DSH no longer tolerates load-order access. */
2092
2763
  const inject = [
2093
2764
  "slots",
2094
2765
  "locale",
2095
- "connection"
2766
+ "connection",
2767
+ "remote",
2768
+ "remote.skills",
2769
+ "remote.directoryPicker",
2770
+ "inputTriggers",
2771
+ "commandUi",
2772
+ "sessions",
2773
+ "conversation"
2096
2774
  ];
2775
+ const popupEscapeHandlers = /* @__PURE__ */ new WeakMap();
2776
+ /** Restore keyboard ownership when DSH opens this plugin's popup from the composer. */
2777
+ function focusSkillsPopupSearch(options) {
2778
+ const focus = () => {
2779
+ for (const input of document.querySelectorAll("input[type=\"text\"]")) {
2780
+ let node = input;
2781
+ while (node !== null) {
2782
+ if ((node.getAttribute("aria-label") ?? "").toLowerCase().includes("skills")) {
2783
+ const previous = popupEscapeHandlers.get(input);
2784
+ if (previous !== void 0) input.removeEventListener("keydown", previous);
2785
+ const onKeyDown = (event) => {
2786
+ if (event.key !== "Escape") return;
2787
+ window.setTimeout(options.onEscape, 0);
2788
+ };
2789
+ popupEscapeHandlers.set(input, onKeyDown);
2790
+ input.addEventListener("keydown", onKeyDown);
2791
+ input.focus({ preventScroll: true });
2792
+ options.resetActive();
2793
+ return;
2794
+ }
2795
+ node = node.parentElement;
2796
+ }
2797
+ }
2798
+ };
2799
+ window.requestAnimationFrame(focus);
2800
+ window.setTimeout(focus, 0);
2801
+ }
2097
2802
  /** Same-origin route literals (mirrors the host half's ROUTES). */
2098
2803
  const ROUTES = {
2099
2804
  list: "/skill-importer/list",
@@ -2101,7 +2806,8 @@ window.__ModuleLoader__.load({
2101
2806
  importUrl: "/skill-importer/import-url",
2102
2807
  delete: "/skill-importer/delete",
2103
2808
  batchScan: "/skill-importer/batch/scan",
2104
- batchCommit: "/skill-importer/batch/commit"
2809
+ batchCommit: "/skill-importer/batch/commit",
2810
+ updateStatus: "/skill-importer/update-status"
2105
2811
  };
2106
2812
  /** Fetch one route and unwrap the JSON envelope; throws on transport/HTTP errors. */
2107
2813
  async function call(path, body) {
@@ -2134,7 +2840,10 @@ window.__ModuleLoader__.load({
2134
2840
  en
2135
2841
  }), "dsh-skill-importer: dictionaries");
2136
2842
  const t = ctx.locale.bind(NS);
2843
+ const sourceLabel = (source) => source === "project-dsh" ? t("pickerSourceProjectDsh") : source === "project-agents" ? t("pickerSourceProjectAgents") : source === "user-dsh" ? t("pickerSourceUserDsh") : t("pickerSourceUserAgents");
2137
2844
  let catalog = [];
2845
+ let issues = [];
2846
+ const sessionCatalogs = /* @__PURE__ */ new Map();
2138
2847
  const listeners = /* @__PURE__ */ new Set();
2139
2848
  const notify = () => {
2140
2849
  for (const listener of [...listeners]) try {
@@ -2143,13 +2852,11 @@ window.__ModuleLoader__.load({
2143
2852
  console.error("[dsh-skill-importer] listener failed:", error);
2144
2853
  }
2145
2854
  };
2146
- const connection = ctx.get("connection");
2147
- const skillsApi = connection.api.skills;
2148
- skillsApi.list.bind(skillsApi);
2149
- skillsApi.list = ((_request, _signal) => {
2150
- return Promise.reject(/* @__PURE__ */ new Error("skill group hidden: use the composer skill picker or /skills"));
2151
- });
2152
- const usableNames = () => catalog.filter((skill) => skill.userInvocable).map((skill) => skill.name);
2855
+ const legacyApi = ctx.get("connection").api;
2856
+ const remote = ctx.remote ?? ctx.get("remote");
2857
+ const skillsApi = remote?.skills ?? legacyApi?.skills;
2858
+ if (skillsApi !== void 0) skillsApi.list = () => Promise.reject(/* @__PURE__ */ new Error("skill group hidden: use the composer skill picker or /skills"));
2859
+ const usableNames = () => [...new Set([...sessionCatalogs.values()].flat().filter((skill) => skill.userInvocable).map((skill) => skill.name))];
2153
2860
  ctx.effect(() => {
2154
2861
  return ctx.get("inputTriggers").registerSource({
2155
2862
  trigger: "/",
@@ -2173,42 +2880,79 @@ window.__ModuleLoader__.load({
2173
2880
  }, "dsh-skill-importer: lexicon source");
2174
2881
  const commandUi = ctx.get("commandUi");
2175
2882
  const sessions = ctx.get("sessions");
2176
- const conversation = ctx.get("conversation");
2177
- if (commandUi !== void 0 && sessions !== void 0 && conversation !== void 0) ctx.effect(() => {
2883
+ if (commandUi !== void 0 && sessions !== void 0) ctx.effect(() => {
2178
2884
  return commandUi.register({
2179
2885
  name: "skills",
2180
2886
  description: t("commandDescription"),
2181
2887
  available: () => true,
2182
2888
  ui: {
2183
2889
  kind: "popupSelect",
2184
- async options(_session, _signal) {
2185
- return catalog.filter((skill) => skill.userInvocable).map((skill) => ({
2890
+ async options(session, _signal) {
2891
+ const rows = await refreshSkillsForSession(session.sessionId);
2892
+ const restoreComposerFocus = () => {
2893
+ const actx = sessions.scope(session.sessionId);
2894
+ if (actx === void 0) return;
2895
+ const conversation = actx.get("conversation");
2896
+ if (conversation === void 0) return;
2897
+ const draft = conversation.input.for(actx).state.getSnapshot().draft;
2898
+ requestComposerFocus({
2899
+ sessionId: session.sessionId,
2900
+ expectedDraft: draft,
2901
+ caret: draft.length
2902
+ });
2903
+ };
2904
+ const resetActive = () => {
2905
+ const actx = sessions.scope(session.sessionId);
2906
+ if (actx === void 0) return;
2907
+ commandUi.popupFor(actx).highlight?.(0);
2908
+ };
2909
+ focusSkillsPopupSearch({
2910
+ onEscape: restoreComposerFocus,
2911
+ resetActive
2912
+ });
2913
+ return rows.filter((skill) => skill.userInvocable).map((skill) => ({
2186
2914
  id: skill.name,
2187
- label: skill.name
2915
+ label: `${skill.name} · ${sourceLabel(skill.source)}`
2188
2916
  }));
2189
2917
  },
2190
2918
  onSelect(option, session) {
2191
2919
  const actx = sessions.scope(session.sessionId);
2192
- if (actx === void 0) return;
2193
- const input = conversation.input.for(actx);
2194
- const rest = input.state.getSnapshot().draft.replace(/^\s*\/[a-zA-Z][a-zA-Z0-9-]*\s*/, "");
2920
+ if (actx === void 0) throw new Error("this session is no longer available");
2921
+ const conversation = actx.get("conversation");
2922
+ if (conversation === void 0) throw new Error("conversation input is unavailable");
2923
+ const rest = conversation.input.for(actx).state.getSnapshot().draft.replace(/^\s*\/[a-zA-Z][a-zA-Z0-9-]*\s*/, "");
2195
2924
  const prefix = `/${option.id} `;
2196
2925
  const nextDraft = `${prefix}${rest}`;
2197
- input.setDraft(nextDraft);
2198
- requestComposerFocus({
2199
- sessionId: session.sessionId,
2200
- expectedDraft: nextDraft,
2201
- caret: prefix.length
2202
- });
2926
+ window.setTimeout(() => {
2927
+ const liveScope = sessions.scope(session.sessionId);
2928
+ if (liveScope === void 0) return;
2929
+ const liveConversation = liveScope.get("conversation");
2930
+ if (liveConversation === void 0) return;
2931
+ liveConversation.input.for(liveScope).setDraft(nextDraft);
2932
+ requestComposerFocus({
2933
+ sessionId: session.sessionId,
2934
+ expectedDraft: nextDraft,
2935
+ caret: prefix.length
2936
+ });
2937
+ }, 0);
2203
2938
  }
2204
2939
  }
2205
2940
  });
2206
2941
  }, "dsh-skill-importer: /skills popupSelect command");
2207
- const refreshSkills = async () => {
2208
- catalog = (await call(ROUTES.list)).skills;
2942
+ const refreshSkills = async (workspacePath) => {
2943
+ const result = await call(workspacePath === void 0 ? ROUTES.list : `${ROUTES.list}?workspacePath=${encodeURIComponent(workspacePath)}`);
2944
+ catalog = result.skills;
2945
+ issues = result.issues;
2209
2946
  notify();
2210
2947
  return catalog;
2211
2948
  };
2949
+ const refreshSkillsForSession = async (sessionId) => {
2950
+ const cwd = sessions.list.getSnapshot().byId[sessionId]?.cwd;
2951
+ const effective = effectiveSkills((await call(cwd === void 0 ? `${ROUTES.list}?scope=global` : `${ROUTES.list}?workspacePath=${encodeURIComponent(cwd)}`)).skills);
2952
+ sessionCatalogs.set(sessionId, effective);
2953
+ notify();
2954
+ return effective;
2955
+ };
2212
2956
  const importSkill = async (request) => {
2213
2957
  return (await call(ROUTES.import, request)).path;
2214
2958
  };
@@ -2219,30 +2963,50 @@ window.__ModuleLoader__.load({
2219
2963
  return (await call(ROUTES.delete, request)).removed;
2220
2964
  };
2221
2965
  const pickDirectory = async () => {
2222
- const response = await connection.api.host.pickDirectory({});
2966
+ if (remote?.directoryPicker !== void 0) {
2967
+ const result = await remote.directoryPicker.pick();
2968
+ if (!result.ok) throw new Error(result.error.message);
2969
+ return result.value;
2970
+ }
2971
+ if (legacyApi?.host === void 0) throw new Error("当前 DSH 未提供目录选择能力");
2972
+ const response = await legacyApi.host.pickDirectory({});
2223
2973
  if (!response.result.ok) throw new Error(response.result.error.message);
2224
2974
  return response.result.value.path;
2225
2975
  };
2226
2976
  const scanBatch = (request) => call(ROUTES.batchScan, request);
2227
2977
  const commitBatch = (request) => call(ROUTES.batchCommit, request);
2978
+ const checkForUpdates = () => call(ROUTES.updateStatus);
2228
2979
  const face = () => ({
2229
- hooks: { skills: {
2230
- getSnapshot: () => catalog,
2231
- subscribe: (listener) => {
2232
- listeners.add(listener);
2233
- return () => {
2234
- listeners.delete(listener);
2235
- };
2980
+ hooks: {
2981
+ skills: {
2982
+ getSnapshot: () => catalog,
2983
+ subscribe: (listener) => {
2984
+ listeners.add(listener);
2985
+ return () => {
2986
+ listeners.delete(listener);
2987
+ };
2988
+ }
2989
+ },
2990
+ skillIssues: {
2991
+ getSnapshot: () => issues,
2992
+ subscribe: (listener) => {
2993
+ listeners.add(listener);
2994
+ return () => {
2995
+ listeners.delete(listener);
2996
+ };
2997
+ }
2236
2998
  }
2237
- } },
2999
+ },
2238
3000
  actions: {
2239
3001
  refreshSkills,
3002
+ refreshSkillsForSession,
2240
3003
  importSkill,
2241
3004
  importUrl,
2242
3005
  deleteSkill,
2243
3006
  pickDirectory,
2244
3007
  scanBatch,
2245
- commitBatch
3008
+ commitBatch,
3009
+ checkForUpdates
2246
3010
  }
2247
3011
  });
2248
3012
  ctx.slots.inject("settings.section", () => ctx.slots.register({