dsh-plugin-capabilities 0.1.3 → 0.1.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/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![npm version](https://img.shields.io/npm/v/dsh-plugin-capabilities)](https://www.npmjs.com/package/dsh-plugin-capabilities)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE)
5
5
 
6
- 在 dsh 设置页管理技能与 MCP 服务器。本插件在插件区新增「技能」「MCP」两个标签页,技能目录和 profile 的 MCP 服务器行都能直接在页面上查看与维护,不必手工编辑文件;来自 Claude Code、Codex 等其他 agent 的技能与 MCP 配置也能一并纳入。`dsh web` 与 [DSH Desktop](https://github.com/qinyre/dsh-Desktop) 均可使用。
6
+ 在 dsh 设置页管理技能与 MCP 服务器。本插件在设置里新增一级分区「技能与 MCP」(与「通用设置」「模型」并列),内含「技能」「MCP」两个标签页,技能目录和 profile 的 MCP 服务器行都能直接在页面上查看与维护,不必手工编辑文件;来自 Claude Code、Codex 等其他 agent 的技能与 MCP 配置也能一并纳入。`dsh web` 与 [DSH Desktop](https://github.com/qinyre/dsh-Desktop) 均可使用。
7
7
 
8
8
  ## 技能
9
9
 
@@ -19,7 +19,7 @@
19
19
 
20
20
  「MCP」页管理 profile patch 中的 MCP 服务器行,每行对应一个 `@deepseek-ai/dsh-mcp-client` 实例。stdio 服务器填写命令与参数,streamable-http 服务器填写 URL,编辑、停用、移除都在页面上完成。YAML 读写采用文档级 API,文件中的其他行与注释不受影响。新行写在一个 `- insert:` 块里——加载器只会挂载 insert 形式的行,裸的 `- id:` 条目是对已有行的覆盖,目标不存在时会被跳过;0.1.3 之前写入的裸行会在下一次保存时自动迁入 insert 块。
21
21
 
22
- 也可以从其他 agent 导入:一键扫描 Claude Code(`~/.claude.json`、`~/.claude/settings.json`)与 Codex(`~/.codex/config.toml`)的 MCP 配置,勾选所需条目后转为本 profile 的服务器行。stdio 与 http 两种传输都会处理,已存在的同名服务器置灰跳过。需要注意的是,Claude 配置里的 `${VAR}` 环境变量引用按字面值导入,如有需要请在导入后手动改回。
22
+ 也可以从其他 agent 导入:一键扫描 Claude Code(`~/.claude.json`、`~/.claude/settings.json`)与 Codex(`~/.codex/config.toml`)的 MCP 配置,勾选所需条目后转为本 profile 的服务器行。弹窗按来源分成 Claude Code 和 Codex 两组,各自带数量与「全选」;stdio 与 http 两种传输都会处理,已存在的同名服务器置灰跳过。需要注意的是,Claude 配置里的 `${VAR}` 环境变量引用按字面值导入,如有需要请在导入后手动改回。
23
23
 
24
24
  MCP 行的变更需要重启 dsh 才会进入组合。MCP 页头部有常驻的「重启」按钮,变更后无需离开界面:在 [DSH Desktop](https://github.com/qinyre/dsh-Desktop) 中由桌面壳层重启受监督的 sidecar,完成后窗口自动重载;直接运行 `dsh web` 时插件会拉起一个替代进程再退出自身,页面在恢复后自动刷新——若启动时端口是随机的,按横幅提示在终端查看新地址。重启会中断正在进行的回合,点击后会先弹出确认。
25
25
 
@@ -29,7 +29,7 @@ MCP 行的变更需要重启 dsh 才会进入组合。MCP 页头部有常驻的
29
29
  dsh plugin --profile web add dsh-plugin-capabilities
30
30
  ```
31
31
 
32
- 安装后打开 设置 → 插件,即可看到新增的「技能」与「MCP」标签页。开发时也可以直接安装本地源码检出:`dsh plugin --profile web add file:/path/to/dsh-plugin-capabilities`,包内的 `prepare` 脚本会自动构建出 `lib/`。
32
+ 安装后打开设置,即可在一级导航里看到「技能与 MCP」分区(位于「模型」与「插件」之间)。开发时也可以直接安装本地源码检出:`dsh plugin --profile web add file:/path/to/dsh-plugin-capabilities`,包内的 `prepare` 脚本会自动构建出 `lib/`。
33
33
 
34
34
  ## 工作原理
35
35
 
package/lib/client.js CHANGED
@@ -28,15 +28,23 @@ __export(index_exports, {
28
28
  name: () => name
29
29
  });
30
30
  module.exports = __toCommonJS(index_exports);
31
- var import_react3 = require("react");
31
+ var import_react4 = require("react");
32
32
 
33
- // src/client/McpTab.tsx
34
- var import_react = require("react");
35
- var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
33
+ // src/client/CapabilitiesSection.tsx
34
+ var import_react3 = require("react");
36
35
 
37
36
  // src/client/css.ts
38
37
  var CSS = `
39
38
  .dpc-section{display:flex;flex-direction:column;gap:14px;width:100%;max-width:760px;color:var(--dsw-alias-label-primary)}
39
+ .dpc-heading{margin:0;font-size:18px;line-height:26px;font-weight:600}
40
+ /* Top-level section's internal tabs \u2014 same underline-tab look as the host's
41
+ Plugins section so the promoted placement still reads as one family. */
42
+ .dpc-tabs{display:flex;align-items:flex-end;gap:22px;border-bottom:1px solid var(--dsw-alias-border-l2);margin-top:2px}
43
+ .dpc-tab{position:relative;border:0;padding:7px 1px 9px;background:transparent;color:var(--dsw-alias-label-tertiary);font:inherit;font-size:13px;line-height:20px;cursor:pointer}
44
+ .dpc-tab:hover,.dpc-tab[data-active='true']{color:var(--dsw-alias-label-primary)}
45
+ .dpc-tab[data-active='true']::after,.dpc-tab:focus-visible::after{position:absolute;right:0;bottom:-1px;left:0;height:2px;border-radius:2px 2px 0 0;background:var(--dsw-alias-label-primary);content:''}
46
+ .dpc-tab:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px;border-radius:2px;color:var(--dsw-alias-label-primary)}
47
+ .dpc-tabPanel{min-width:0;padding-top:2px}
40
48
  .dpc-head{display:flex;align-items:center;gap:8px}
41
49
  .dpc-head h3{margin:0;font-size:13px;line-height:20px;font-weight:600}
42
50
  .dpc-head>svg{flex:none;color:var(--dsw-alias-label-tertiary)}
@@ -70,17 +78,49 @@ var CSS = `
70
78
  .dpc-label{display:flex;flex-direction:column;gap:4px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary)}
71
79
  .dpc-label>span:first-child{color:var(--dsw-alias-label-tertiary)}
72
80
  .dpc-input,.dpc-textarea,.dpc-select{width:100%;box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:7px 10px;outline:none;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px}
73
- .dpc-textarea{min-height:120px;resize:vertical;font-family:var(--ds-font-family-code);line-height:1.5}
74
- .dpc-textarea[data-short='true']{min-height:64px}
81
+ .dpc-textarea{min-height:240px;resize:vertical;font-family:var(--ds-font-family-code);line-height:1.5}
82
+ .dpc-textarea[data-short='true']{min-height:96px}
75
83
  .dpc-input:focus-visible,.dpc-textarea:focus-visible,.dpc-select:focus-visible{border-color:var(--dsw-alias-state-business-primary);box-shadow:0 0 0 2px color-mix(in srgb,var(--dsw-alias-state-business-primary) 18%,transparent)}
76
84
  .dpc-checks{display:flex;gap:16px;font-size:13px;line-height:20px}
77
85
  .dpc-checks label{display:inline-flex;align-items:center;gap:6px;cursor:pointer}
78
86
  .dpc-formError{margin:0;color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px}
87
+ /* Import dialog: wider than the host's 380px default; the server list gets
88
+ its own scroll so title, intro, and footer stay pinned. The doubled class
89
+ beats the host dialog's module CSS regardless of injection order. */
90
+ .dpc-modalWide.dpc-modalWide{width:min(680px,100%)}
91
+ /* Editor dialogs (new skill / server): 640px wide so markdown bodies and
92
+ command/arg/env lines stop wrapping mid-token; the content column scrolls
93
+ on short viewports instead of clipping past the dialog edge. */
94
+ .dpc-modalForm.dpc-modalForm{width:min(640px,100%)}
95
+ .dpc-modalScroll.dpc-modalScroll{max-height:calc(100vh - 160px);overflow-y:auto}
96
+ .dpc-importScroll{display:flex;flex-direction:column;gap:14px;max-height:min(400px,52vh);overflow-y:auto;padding:2px 4px 2px 2px}
97
+ .dpc-importGroup{display:flex;flex-direction:column;gap:8px}
98
+ .dpc-importHead{display:flex;align-items:center;gap:8px;padding:0 2px}
99
+ .dpc-importCount{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums}
100
+ .dpc-importAll{margin-left:auto;display:inline-flex;align-items:center;gap:6px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary);cursor:pointer}
79
101
  @media(max-width:680px){.dpc-cards{grid-template-columns:minmax(0,1fr)}}
80
102
  `;
81
103
 
82
104
  // src/client/McpTab.tsx
105
+ var import_react = require("react");
106
+ var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
83
107
  var import_jsx_runtime = require("react/jsx-runtime");
108
+ function importGroups(items) {
109
+ const label = (agent) => agent === "claude-code" ? "Claude Code" : agent === "codex" ? "Codex" : agent;
110
+ const order = ["claude-code", "codex"];
111
+ const agents = [...new Set(items.map((item) => item.server.agent))].sort((a, b) => {
112
+ const rank = (agent) => {
113
+ const at = order.indexOf(agent);
114
+ return at === -1 ? order.length : at;
115
+ };
116
+ return rank(a) - rank(b) || a.localeCompare(b);
117
+ });
118
+ return agents.map((agent) => ({
119
+ agent,
120
+ label: label(agent),
121
+ items: items.map((item, index) => ({ item, index })).filter(({ item }) => item.server.agent === agent)
122
+ }));
123
+ }
84
124
  function parsePairs(text, separator) {
85
125
  const map = {};
86
126
  for (const line of text.split(/\r?\n/)) {
@@ -310,6 +350,8 @@ function McpTab(props) {
310
350
  open: editor !== null,
311
351
  onClose: () => setEditor(null),
312
352
  title: editor !== null && editor.id !== "" ? t("editServer") : t("addServer"),
353
+ className: "dpc-modalForm",
354
+ contentClassName: "dpc-modalScroll",
313
355
  children: editor !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-form", children: [
314
356
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "dpc-label", children: [
315
357
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: t("serverName") }),
@@ -404,34 +446,62 @@ function McpTab(props) {
404
446
  open: importOpen,
405
447
  onClose: () => setImportOpen(false),
406
448
  title: t("importServers"),
449
+ className: "dpc-modalWide",
407
450
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-form", children: [
408
451
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-intro", style: { margin: 0 }, children: t("importIntro") }),
409
452
  importItems === null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-empty", children: t("loading") }),
410
453
  importItems !== null && importItems.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-empty", children: t("importEmpty") }),
411
- importItems !== null && importItems.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: "dpc-cards", style: { gridTemplateColumns: "minmax(0, 1fr)" }, children: importItems.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: "dpc-card", style: { opacity: item.existing ? 0.55 : 1 }, children: [
412
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-cardTop", children: [
413
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: { display: "inline-flex", alignItems: "center", gap: 8, minWidth: 0, cursor: item.existing ? "default" : "pointer" }, children: [
414
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
415
- "input",
416
- {
417
- type: "checkbox",
418
- checked: item.checked,
419
- disabled: item.existing,
420
- onChange: (event) => {
421
- const next = importItems.slice();
422
- next[index] = { ...item, checked: event.target.checked };
423
- setImportItems(next);
454
+ importItems !== null && importItems.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dpc-importScroll", children: importGroups(importItems).map((group) => {
455
+ const selectable = group.items.filter(({ item }) => !item.existing).map(({ index }) => index);
456
+ const allChecked = selectable.length > 0 && selectable.every((index) => importItems[index].checked);
457
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "dpc-importGroup", children: [
458
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-importHead", children: [
459
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-tag", "data-kind": "source", children: group.label }),
460
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-importCount", children: group.items.length }),
461
+ selectable.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "dpc-importAll", children: [
462
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
463
+ "input",
464
+ {
465
+ type: "checkbox",
466
+ checked: allChecked,
467
+ onChange: (event) => {
468
+ const next = importItems.slice();
469
+ for (const index of selectable) next[index] = { ...next[index], checked: event.target.checked };
470
+ setImportItems(next);
471
+ }
424
472
  }
425
- }
426
- ),
427
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { className: "dpc-cardTitle", title: item.server.name, children: item.server.name })
473
+ ),
474
+ t("importSelectAll")
475
+ ] })
428
476
  ] }),
429
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-tag", "data-kind": "source", children: item.server.agent === "claude-code" ? "Claude Code" : "Codex" }),
430
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-tag", children: item.server.transport }),
431
- item.existing && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-tag", children: t("importExisting") })
432
- ] }),
433
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-cardDesc", children: item.server.transport === "stdio" ? `${item.server.command ?? ""} ${(item.server.args ?? []).join(" ")}` : item.server.url ?? "" })
434
- ] }, `${item.server.agent}/${item.server.name}`)) }),
477
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: "dpc-cards", style: { gridTemplateColumns: "minmax(0, 1fr)" }, children: group.items.map(({ item, index }) => {
478
+ const command = item.server.transport === "stdio" ? `${item.server.command ?? ""} ${(item.server.args ?? []).join(" ")}`.trim() : item.server.url ?? "";
479
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: "dpc-card", style: { opacity: item.existing ? 0.55 : 1 }, children: [
480
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-cardTop", children: [
481
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: { display: "inline-flex", alignItems: "center", gap: 8, minWidth: 0, cursor: item.existing ? "default" : "pointer" }, children: [
482
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
483
+ "input",
484
+ {
485
+ type: "checkbox",
486
+ checked: item.checked,
487
+ disabled: item.existing,
488
+ onChange: (event) => {
489
+ const next = importItems.slice();
490
+ next[index] = { ...item, checked: event.target.checked };
491
+ setImportItems(next);
492
+ }
493
+ }
494
+ ),
495
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { className: "dpc-cardTitle", title: item.server.name, children: item.server.name })
496
+ ] }),
497
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-tag", children: item.server.transport }),
498
+ item.existing && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-tag", children: t("importExisting") })
499
+ ] }),
500
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-cardDesc", title: command, children: command })
501
+ ] }, `${item.server.agent}/${item.server.name}`);
502
+ }) })
503
+ ] }, group.agent);
504
+ }) }),
435
505
  formError !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-formError", children: formError }),
436
506
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-cardRow", children: [
437
507
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-spacer" }),
@@ -611,6 +681,8 @@ function SkillsTab(props) {
611
681
  open: editor !== null,
612
682
  onClose: () => setEditor(null),
613
683
  title: editor === null ? "" : editor.mode === "create" ? t("newSkill") : editor.mode === "edit" ? t("editSkill") : t("viewSkill"),
684
+ className: "dpc-modalForm",
685
+ contentClassName: "dpc-modalScroll",
614
686
  children: editor !== null && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-form", children: [
615
687
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "dpc-label", children: [
616
688
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: t("skillName") }),
@@ -712,8 +784,94 @@ function SkillsTab(props) {
712
784
  ] });
713
785
  }
714
786
 
787
+ // src/client/CapabilitiesSection.tsx
788
+ var import_jsx_runtime3 = require("react/jsx-runtime");
789
+ function CapabilitiesSection(props) {
790
+ const { t, skills, mcp } = props;
791
+ const tabsId = (0, import_react3.useId)();
792
+ const tabRefs = (0, import_react3.useRef)([]);
793
+ const rows = [
794
+ { id: "skills", label: t("skillsTab") },
795
+ { id: "mcp", label: t("mcpTab") }
796
+ ];
797
+ const [activeId, setActiveId] = (0, import_react3.useState)("skills");
798
+ const [visitedIds, setVisitedIds] = (0, import_react3.useState)(() => /* @__PURE__ */ new Set(["skills"]));
799
+ (0, import_react3.useEffect)(() => {
800
+ setVisitedIds((previous) => {
801
+ if (previous.has(activeId)) return previous;
802
+ return /* @__PURE__ */ new Set([...previous, activeId]);
803
+ });
804
+ }, [activeId]);
805
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dpc-section", children: [
806
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("style", { children: CSS }),
807
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { className: "dpc-heading", children: t("sectionNav") }),
808
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dpc-tabs", role: "tablist", "aria-label": t("sectionNav"), children: rows.map((row, index) => {
809
+ const selected = row.id === activeId;
810
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
811
+ "button",
812
+ {
813
+ ref: (element) => {
814
+ tabRefs.current[index] = element;
815
+ },
816
+ id: `${tabsId}-tab-${row.id}`,
817
+ type: "button",
818
+ role: "tab",
819
+ className: "dpc-tab",
820
+ "aria-selected": selected,
821
+ "aria-controls": `${tabsId}-panel-${row.id}`,
822
+ "data-active": selected ? "true" : void 0,
823
+ tabIndex: selected ? 0 : -1,
824
+ onClick: () => {
825
+ setActiveId(row.id);
826
+ },
827
+ onKeyDown: (event) => {
828
+ let nextIndex;
829
+ switch (event.key) {
830
+ case "ArrowRight":
831
+ nextIndex = (index + 1) % rows.length;
832
+ break;
833
+ case "ArrowLeft":
834
+ nextIndex = (index - 1 + rows.length) % rows.length;
835
+ break;
836
+ case "Home":
837
+ nextIndex = 0;
838
+ break;
839
+ case "End":
840
+ nextIndex = rows.length - 1;
841
+ break;
842
+ default:
843
+ return;
844
+ }
845
+ event.preventDefault();
846
+ setActiveId(rows[nextIndex]?.id ?? "skills");
847
+ tabRefs.current[nextIndex]?.focus();
848
+ },
849
+ children: row.label
850
+ },
851
+ row.id
852
+ );
853
+ }) }),
854
+ rows.filter((row) => row.id === activeId || visitedIds.has(row.id)).map((row) => {
855
+ const selected = row.id === activeId;
856
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
857
+ "div",
858
+ {
859
+ id: `${tabsId}-panel-${row.id}`,
860
+ className: "dpc-tabPanel",
861
+ role: "tabpanel",
862
+ "aria-labelledby": `${tabsId}-tab-${row.id}`,
863
+ hidden: !selected,
864
+ children: row.id === "skills" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SkillsTab, { t, injected: skills }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(McpTab, { t, injected: mcp })
865
+ },
866
+ row.id
867
+ );
868
+ })
869
+ ] });
870
+ }
871
+
715
872
  // src/client/locales.ts
716
873
  var zh = {
874
+ sectionNav: "\u6280\u80FD\u4E0E MCP",
717
875
  skillsTab: "\u6280\u80FD",
718
876
  mcpTab: "MCP",
719
877
  skillsTitle: "\u6280\u80FD\u7BA1\u7406",
@@ -769,6 +927,7 @@ var zh = {
769
927
  importServers: "\u4ECE\u5176\u4ED6 Agent \u5BFC\u5165",
770
928
  importIntro: "\u626B\u63CF Claude Code\uFF08~/.claude.json\uFF09\u4E0E Codex\uFF08~/.codex/config.toml\uFF09\u7684 MCP \u670D\u52A1\u5668\u914D\u7F6E\uFF0C\u52FE\u9009\u540E\u5BFC\u5165\u4E3A\u672C profile \u7684\u670D\u52A1\u5668\u884C\u3002",
771
929
  importEmpty: "\u6CA1\u6709\u53D1\u73B0\u53EF\u5BFC\u5165\u7684 MCP \u670D\u52A1\u5668",
930
+ importSelectAll: "\u5168\u9009",
772
931
  importExisting: "\u5DF2\u5B58\u5728",
773
932
  importSelected: "\u5BFC\u5165\u9009\u4E2D\u9879",
774
933
  restartNeeded: "\u914D\u7F6E\u5DF2\u5199\u5165\uFF0C\u91CD\u542F dsh \u540E\u751F\u6548\u3002",
@@ -783,6 +942,7 @@ var zh = {
783
942
  failed: "\u64CD\u4F5C\u5931\u8D25"
784
943
  };
785
944
  var en = {
945
+ sectionNav: "Skills & MCP",
786
946
  skillsTab: "Skills",
787
947
  mcpTab: "MCP",
788
948
  skillsTitle: "Skills",
@@ -838,6 +998,7 @@ var en = {
838
998
  importServers: "Import from other agents",
839
999
  importIntro: "Scans Claude Code (~/.claude.json) and Codex (~/.codex/config.toml) MCP server configs; selected entries become server rows in this profile.",
840
1000
  importEmpty: "No importable MCP servers found",
1001
+ importSelectAll: "Select all",
841
1002
  importExisting: "already here",
842
1003
  importSelected: "Import selected",
843
1004
  restartNeeded: "Configuration written \u2014 restart dsh to apply.",
@@ -891,25 +1052,14 @@ function apply(ctx) {
891
1052
  },
892
1053
  desktop: window.dshDesktop !== void 0
893
1054
  };
894
- ctx.slots.inject("settings.plugins.tab", () => {
895
- return ctx.slots.register({
896
- name: "settings.plugins.tab",
897
- id: "capabilities-skills",
898
- order: 30,
899
- label: () => t("skillsTab"),
900
- locale: NS,
901
- inject: () => skillsInjected
902
- }, () => (0, import_react3.createElement)(SkillsTab, { t, injected: skillsInjected }));
903
- });
904
- ctx.slots.inject("settings.plugins.tab", () => {
1055
+ ctx.slots.inject("settings.section", () => {
905
1056
  return ctx.slots.register({
906
- name: "settings.plugins.tab",
907
- id: "capabilities-mcp",
908
- order: 40,
909
- label: () => t("mcpTab"),
910
- locale: NS,
911
- inject: () => mcpInjected
912
- }, () => (0, import_react3.createElement)(McpTab, { t, injected: mcpInjected }));
1057
+ name: "settings.section",
1058
+ id: "capabilities",
1059
+ order: 12,
1060
+ label: () => t("sectionNav"),
1061
+ locale: NS
1062
+ }, () => (0, import_react4.createElement)(CapabilitiesSection, { t, skills: skillsInjected, mcp: mcpInjected }));
913
1063
  });
914
1064
  }
915
1065
 
package/lib/client.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/client/index.ts", "../src/client/McpTab.tsx", "../src/client/css.ts", "../src/client/SkillsTab.tsx", "../src/client/locales.ts"],
4
- "sourcesContent": ["/** dsh-plugin-capabilities client entry: contributes the \u201C\u6280\u80FD/Skills\u201D and\n * \u201CMCP\u201D tabs into Settings \u2192 Plugins. Calls the host routes with fetch. */\n\nimport { createElement as h } from 'react'\nimport { McpTab, type McpInjected, type McpRow } from './McpTab.tsx'\nimport { SkillsTab, type SkillsInjected, type SkillRowView } from './SkillsTab.tsx'\nimport { zh, en } from './locales.ts'\n\n/** Locale dictionary namespace owned by this plugin. */\nexport const NS = 'settings.pluginCapabilities'\n\n/** The `t` function bound by the locale service. */\nexport interface Translate {\n (key: string): string\n}\n\n/** Minimal structural subset of the slots service. */\ninterface SlotsService {\n inject(slot: string, register: () => unknown): void\n register(meta: Record<string, unknown>, component: () => unknown): unknown\n}\n\n/** Minimal structural subset of the locale service. */\ninterface LocaleService {\n register(namespace: string, dicts: { zh: Record<string, string>; en: Record<string, string> }): unknown\n bind(namespace: string): Translate\n}\n\n/** The client cordis context this plugin relies on (structural). */\ninterface CapabilitiesClientContext {\n effect(callback: () => unknown, label?: string): void\n locale: LocaleService\n slots: SlotsService\n}\n\n/** Same-origin fetch of the manager's host routes. */\nasync function fetchJson<T>(path: string, init?: RequestInit): Promise<T> {\n const response = await fetch(path, init)\n const body = (await response.json()) as T & { error?: string }\n if (!response.ok) throw new Error(body.error ?? `HTTP ${response.status}`)\n return body\n}\n\nconst post = (path: string, body: unknown): Promise<unknown> =>\n fetchJson(path, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body) })\n\nexport const name = 'dsh-plugin-capabilities'\nexport const inject = ['slots', 'locale']\n\nexport interface SkillBody { content: string }\n\n/** One foreign-agent server from the import scan. */\nexport interface ImportedServerView {\n agent: 'claude-code' | 'codex'\n name: string\n transport: 'stdio' | 'streamable-http'\n command?: string\n args?: string[]\n env?: Record<string, string>\n url?: string\n headers?: Record<string, string>\n}\n\nexport function apply(ctx: CapabilitiesClientContext): void {\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dsh-plugin-capabilities: dictionaries')\n const t = ctx.locale.bind(NS)\n\n const skillsInjected: SkillsInjected = {\n list: () => fetchJson<{ skills: SkillRowView[] }>('/dsh-plugin-capabilities/skills'),\n get: (name: string) => fetchJson<SkillBody>(`/dsh-plugin-capabilities/skill?name=${encodeURIComponent(name)}`),\n save: (input: unknown) => post('/dsh-plugin-capabilities/skill/save', input) as Promise<{ ok: boolean }>,\n remove: (name: string) => post('/dsh-plugin-capabilities/skill/delete', { name }) as Promise<{ ok: boolean }>,\n }\n\n const mcpInjected: McpInjected = {\n list: () => fetchJson<{ servers: McpRow[] }>('/dsh-plugin-capabilities/mcp'),\n save: (input: unknown) => post('/dsh-plugin-capabilities/mcp/save', input) as Promise<{ ok: boolean; id: string }>,\n toggle: (id: string, disabled: boolean) => post('/dsh-plugin-capabilities/mcp/toggle', { id, disabled }) as Promise<{ ok: boolean }>,\n remove: (id: string) => post('/dsh-plugin-capabilities/mcp/remove', { id }) as Promise<{ ok: boolean }>,\n scanImport: () => fetchJson<{ servers: ImportedServerView[]; existing: string[] }>('/dsh-plugin-capabilities/import/scan'),\n applyImport: (items: Array<{ agent: string; name: string }>) =>\n post('/dsh-plugin-capabilities/import/apply', { items }) as Promise<{ ok: boolean; results: Array<{ name: string; ok: boolean; error?: string }> }>,\n restart: async (): Promise<void> => {\n if (window.dshDesktop !== undefined) {\n window.dshDesktop.restartSidecar?.()\n return\n }\n // \u72EC\u7ACB dsh web\uFF1A\u81EA\u91CD\u542F\u8DEF\u7531\u3002\u8FDE\u63A5\u5728\u5173\u505C\u9014\u4E2D\u65AD\u5F00\u5C5E\u9884\u671F\uFF0C\u4E0D\u7B97\u5931\u8D25\u3002\n try { await post('/dsh-plugin-capabilities/restart', {}) } catch { /* dying mid-restart is expected */ }\n },\n desktop: window.dshDesktop !== undefined,\n }\n\n ctx.slots.inject('settings.plugins.tab', () => {\n return ctx.slots.register({\n name: 'settings.plugins.tab',\n id: 'capabilities-skills',\n order: 30,\n label: () => t('skillsTab'),\n locale: NS,\n inject: () => skillsInjected,\n }, () => h(SkillsTab, { t, injected: skillsInjected }))\n })\n\n ctx.slots.inject('settings.plugins.tab', () => {\n return ctx.slots.register({\n name: 'settings.plugins.tab',\n id: 'capabilities-mcp',\n order: 40,\n label: () => t('mcpTab'),\n locale: NS,\n inject: () => mcpInjected,\n }, () => h(McpTab, { t, injected: mcpInjected }))\n })\n}\n", "/** Settings \u2192 Plugins \u201CMCP\u201D tab: manage the profile's mcp-client rows.\n * Mutations rewrite the profile patch and need a dsh restart \u2014 the banner\n * hands the restart to the desktop shell when one is present. */\n\nimport { useEffect, useState } from 'react'\nimport type { ReactElement } from 'react'\nimport { Button, IconApiOutline14, IconRefreshOutline14, Modal, StateDot } from '@deepseek-ai/dsh-client-ui-primitives'\nimport { CSS } from './css.ts'\nimport type { ImportedServerView, Translate } from './index.ts'\n\nexport interface McpRow {\n id: string\n serverName: string\n transport: 'stdio' | 'streamable-http'\n disabled: boolean\n command?: string\n args?: string[]\n env?: Record<string, string>\n cwd?: string\n url?: string\n headers?: Record<string, string>\n}\n\nexport interface McpInjected {\n list(): Promise<{ servers: McpRow[] }>\n save(input: Record<string, unknown>): Promise<{ ok: boolean; id: string }>\n toggle(id: string, disabled: boolean): Promise<{ ok: boolean }>\n remove(id: string): Promise<{ ok: boolean }>\n scanImport(): Promise<{ servers: ImportedServerView[]; existing: string[] }>\n applyImport(items: Array<{ agent: string; name: string }>): Promise<{ ok: boolean; results: Array<{ name: string; ok: boolean; error?: string }> }>\n restart(): Promise<void>\n desktop: boolean\n}\n\n/** Editor dialog state; null when closed. Textareas hold line-based values. */\ninterface EditorState {\n id: string\n serverName: string\n transport: 'stdio' | 'streamable-http'\n command: string\n args: string\n env: string\n url: string\n headers: string\n}\n\n/** KEY=VALUE / KEY: VALUE lines to a map. */\nfunction parsePairs(text: string, separator: ':' | '='): Record<string, string> {\n const map: Record<string, string> = {}\n for (const line of text.split(/\\r?\\n/)) {\n const trimmed = line.trim()\n if (trimmed === '' || trimmed.startsWith('#')) continue\n const at = trimmed.indexOf(separator)\n if (at <= 0) continue\n map[trimmed.slice(0, at).trim()] = trimmed.slice(at + 1).trim()\n }\n return map\n}\n\nfunction mapToPairs(map: Record<string, string> | undefined, separator: string): string {\n if (map === undefined) return ''\n return Object.entries(map).map(([key, value]) => `${key}${separator}${value.includes('\\n') ? JSON.stringify(value) : value}`).join('\\n')\n}\n\nexport function McpTab(props: { t: Translate; injected: McpInjected }): ReactElement {\n const { t, injected } = props\n const [servers, setServers] = useState<McpRow[] | null>(null)\n const [editor, setEditor] = useState<EditorState | null>(null)\n const [confirmId, setConfirmId] = useState<string | null>(null)\n const [importOpen, setImportOpen] = useState(false)\n const [importItems, setImportItems] = useState<Array<{ server: ImportedServerView; existing: boolean; checked: boolean }> | null>(null)\n const [busy, setBusy] = useState(false)\n const [pending, setPending] = useState(false)\n const [restartConfirm, setRestartConfirm] = useState(false)\n const [restarting, setRestarting] = useState(false)\n const [outcome, setOutcome] = useState<{ ok: boolean; text: string } | null>(null)\n const [formError, setFormError] = useState<string | null>(null)\n const [reload, setReload] = useState(0)\n\n const openImport = async (): Promise<void> => {\n setImportOpen(true)\n setImportItems(null)\n try {\n const body = await injected.scanImport()\n const existing = new Set(body.existing)\n setImportItems(body.servers.map(server => ({\n server,\n existing: existing.has(server.name),\n checked: !existing.has(server.name),\n })))\n } catch (error) {\n setImportItems([])\n setOutcome({ ok: false, text: `${t('failed')}: ${String(error instanceof Error ? error.message : error)}` })\n }\n }\n\n const doImport = async (): Promise<void> => {\n if (importItems === null) return\n const items = importItems.filter(item => item.checked && !item.existing).map(item => ({ agent: item.server.agent, name: item.server.name }))\n setBusy(true)\n try {\n const body = await injected.applyImport(items)\n const failed = body.results.filter(item => !item.ok)\n setOutcome(failed.length === 0\n ? { ok: true, text: t('restartNeeded') }\n : { ok: false, text: `${t('failed')}: ${failed.map(item => `${item.name} (${item.error})`).join(', ')}` })\n setImportOpen(false)\n setPending(true)\n setReload((value) => value + 1)\n } catch (error) {\n setOutcome({ ok: false, text: `${t('failed')}: ${String(error instanceof Error ? error.message : error)}` })\n } finally {\n setBusy(false)\n }\n }\n\n const reloadList = (showPending: boolean): void => {\n setReload((value) => value + 1)\n if (showPending) setPending(true)\n }\n\n useEffect(() => {\n let current = true\n void injected.list().then(\n (body) => { if (current) setServers(body.servers) },\n (error: Error) => { if (current) { setServers([]); setOutcome({ ok: false, text: `${t('failed')}: ${String(error.message ?? error)}` }) } },\n )\n return () => { current = false }\n }, [injected, reload, t])\n\n const openCreate = (): void => {\n setFormError(null)\n setEditor({ id: '', serverName: '', transport: 'stdio', command: '', args: '', env: '', url: '', headers: '' })\n }\n\n const openEdit = (row: McpRow): void => {\n setFormError(null)\n setEditor({\n id: row.id,\n serverName: row.serverName,\n transport: row.transport,\n command: row.command ?? '',\n args: (row.args ?? []).join('\\n'),\n env: mapToPairs(row.env, '='),\n url: row.url ?? '',\n headers: mapToPairs(row.headers, ':'),\n })\n }\n\n const doSave = async (): Promise<void> => {\n if (editor === null) return\n setBusy(true)\n setFormError(null)\n try {\n await injected.save({\n id: editor.id,\n serverName: editor.serverName.trim(),\n transport: editor.transport,\n ...(editor.transport === 'stdio'\n ? {\n command: editor.command.trim(),\n args: editor.args.split(/\\r?\\n/).map(line => line.trim()).filter(line => line !== ''),\n env: parsePairs(editor.env, '='),\n }\n : {\n url: editor.url.trim(),\n headers: parsePairs(editor.headers, ':'),\n }),\n })\n setEditor(null)\n setOutcome({ ok: true, text: t('restartNeeded') })\n reloadList(true)\n } catch (error) {\n setFormError(String(error instanceof Error ? error.message : error))\n } finally {\n setBusy(false)\n }\n }\n\n const doToggle = async (row: McpRow): Promise<void> => {\n setBusy(true)\n try {\n await injected.toggle(row.id, !row.disabled)\n setOutcome({ ok: true, text: t('restartNeeded') })\n reloadList(true)\n } catch (error) {\n setOutcome({ ok: false, text: `${t('failed')}: ${String(error instanceof Error ? error.message : error)}` })\n } finally {\n setBusy(false)\n }\n }\n\n const doRemove = async (): Promise<void> => {\n if (confirmId === null) return\n setBusy(true)\n try {\n await injected.remove(confirmId)\n setOutcome({ ok: true, text: t('restartNeeded') })\n reloadList(true)\n } catch (error) {\n setOutcome({ ok: false, text: `${t('failed')}: ${String(error instanceof Error ? error.message : error)}` })\n } finally {\n setBusy(false)\n setConfirmId(null)\n }\n }\n\n const doRestart = (): void => {\n setRestartConfirm(false)\n setRestarting(true)\n void injected.restart()\n // \u684C\u9762\u6A21\u5F0F\uFF1A\u58F3\u5C42\u91CD\u542F\u5B8C\u6210\u540E\u4F1A\u91CD\u8F7D\u7A97\u53E3\u3002\u72EC\u7ACB\u6A21\u5F0F\uFF1A\u8F6E\u8BE2\u672C\u6E90\uFF0C\u6062\u590D\u5373\u5237\u65B0\u3002\n if (injected.desktop) return\n const deadline = Date.now() + 60_000\n const poll = (): void => {\n if (Date.now() > deadline) return\n window.setTimeout(() => {\n void injected.list().then(\n () => { window.location.reload() },\n () => { poll() },\n )\n }, 1500)\n }\n window.setTimeout(poll, 3000)\n }\n\n const restartBanner = (\n <div className=\"dpc-banner\" data-kind=\"info\" role=\"status\">\n <StateDot state=\"ongoing\" size={10} />\n <div className=\"dpc-bannerBody\">\n <span>{restarting ? t('restarting') : t('restartNeeded')}</span>\n <span className=\"dpc-bannerHint\">\n {restarting\n ? (!injected.desktop && t('restartPortHint'))\n : injected.desktop\n ? <>{t('restartDesktopHint')}{' '}<Button variant=\"outline\" size=\"sm\" onClick={() => setRestartConfirm(true)}>{t('restartNow')}</Button></>\n : t('restartOtherHint')}\n </span>\n </div>\n </div>\n )\n\n return (\n <div className=\"dpc-section\">\n <style>{CSS}</style>\n\n <div className=\"dpc-head\">\n <IconApiOutline14 aria-hidden=\"true\" />\n <h3>{t('mcpTitle')}</h3>\n <span className=\"dpc-spacer\" />\n <Button variant=\"ghost\" size=\"sm\" disabled={restarting} onClick={() => setRestartConfirm(true)}>{t('restart')}</Button>\n <Button variant=\"ghost\" size=\"sm\" onClick={() => void openImport()}>{t('importServers')}</Button>\n <Button variant=\"primary\" size=\"sm\" onClick={openCreate}>{t('addServer')}</Button>\n </div>\n <p className=\"dpc-intro\">{t('mcpIntro')}</p>\n\n {outcome !== null && (\n <div className=\"dpc-banner\" data-kind={outcome.ok ? 'ok' : 'error'} role=\"status\">\n <StateDot state={outcome.ok ? 'done' : 'error'} size={10} />\n <div className=\"dpc-bannerBody\"><span>{outcome.text}</span></div>\n </div>\n )}\n {(pending || restarting) && restartBanner}\n\n <div className=\"dpc-listHead\">\n <h3>{t('mcpTab')}</h3>\n {servers !== null && <span className=\"dpc-count\">{servers.length}</span>}\n <span className=\"dpc-spacer\" />\n <button type=\"button\" className=\"dpc-refresh\" aria-label={t('view')} title={t('view')} disabled={busy} onClick={() => setReload((value) => value + 1)}>\n <IconRefreshOutline14 size={14} aria-hidden=\"true\" />\n </button>\n </div>\n\n {servers === null && <p className=\"dpc-empty\">{t('loading')}</p>}\n {servers !== null && servers.length === 0 && <p className=\"dpc-empty\">{t('emptyMcp')}</p>}\n {servers !== null && servers.length > 0 && (\n <ul className=\"dpc-cards\">\n {servers.map((row) => (\n <li className=\"dpc-card\" key={row.id}>\n <div className=\"dpc-cardTop\">\n <strong className=\"dpc-cardTitle\" title={row.id}>{row.serverName}</strong>\n <span className=\"dpc-tag\">{row.transport}</span>\n <span className=\"dpc-tag\" data-kind={row.disabled ? 'off' : undefined}>{row.disabled ? t('disabled') : t('enabled')}</span>\n </div>\n <p className=\"dpc-cardDesc\">\n {row.transport === 'stdio' ? `${row.command ?? ''} ${(row.args ?? []).join(' ')}` : row.url ?? ''}\n </p>\n <div className=\"dpc-cardRow\">\n <span className=\"dpc-spacer\" />\n <Button variant=\"ghost\" size=\"sm\" disabled={busy} onClick={() => void doToggle(row)}>{t('toggle')}</Button>\n <Button variant=\"ghost\" size=\"sm\" disabled={busy} onClick={() => openEdit(row)}>{t('edit')}</Button>\n <Button variant=\"ghost\" size=\"sm\" disabled={busy} onClick={() => setConfirmId(row.id)}>{t('delete')}</Button>\n </div>\n </li>\n ))}\n </ul>\n )}\n\n <Modal\n open={editor !== null}\n onClose={() => setEditor(null)}\n title={editor !== null && editor.id !== '' ? t('editServer') : t('addServer')}\n >\n {editor !== null && (\n <div className=\"dpc-form\">\n <label className=\"dpc-label\">\n <span>{t('serverName')}</span>\n <input\n className=\"dpc-input\"\n value={editor.serverName}\n disabled={editor.id !== ''}\n onChange={(event) => setEditor({ ...editor, serverName: event.target.value })}\n />\n </label>\n <label className=\"dpc-label\">\n <span>{t('transport')}</span>\n <select\n className=\"dpc-select\"\n value={editor.transport}\n disabled={editor.id !== ''}\n onChange={(event) => setEditor({ ...editor, transport: event.target.value as EditorState['transport'] })}\n >\n <option value=\"stdio\">{t('transportStdio')}</option>\n <option value=\"streamable-http\">{t('transportHttp')}</option>\n </select>\n </label>\n {editor.transport === 'stdio' ? (\n <>\n <label className=\"dpc-label\">\n <span>{t('command')}</span>\n <input className=\"dpc-input\" value={editor.command} onChange={(event) => setEditor({ ...editor, command: event.target.value })} />\n </label>\n <label className=\"dpc-label\">\n <span>{t('args')}</span>\n <textarea className=\"dpc-textarea\" data-short=\"true\" value={editor.args} onChange={(event) => setEditor({ ...editor, args: event.target.value })} />\n </label>\n <label className=\"dpc-label\">\n <span>{t('envPairs')}</span>\n <textarea className=\"dpc-textarea\" data-short=\"true\" value={editor.env} onChange={(event) => setEditor({ ...editor, env: event.target.value })} />\n </label>\n </>\n ) : (\n <>\n <label className=\"dpc-label\">\n <span>{t('url')}</span>\n <input className=\"dpc-input\" value={editor.url} onChange={(event) => setEditor({ ...editor, url: event.target.value })} />\n </label>\n <label className=\"dpc-label\">\n <span>{t('headersPairs')}</span>\n <textarea className=\"dpc-textarea\" data-short=\"true\" value={editor.headers} onChange={(event) => setEditor({ ...editor, headers: event.target.value })} />\n </label>\n </>\n )}\n {formError !== null && <p className=\"dpc-formError\">{formError}</p>}\n <div className=\"dpc-cardRow\">\n <span className=\"dpc-spacer\" />\n <Button variant=\"ghost\" onClick={() => setEditor(null)}>{t('cancel')}</Button>\n <Button variant=\"primary\" disabled={busy} onClick={() => void doSave()}>{t('save')}</Button>\n </div>\n </div>\n )}\n </Modal>\n\n <Modal\n open={confirmId !== null}\n onClose={() => setConfirmId(null)}\n title={t('confirmRemove')}\n description={confirmId ?? undefined}\n footer={\n <>\n <Button variant=\"ghost\" onClick={() => setConfirmId(null)}>{t('cancel')}</Button>\n <Button variant=\"primary\" disabled={busy} onClick={() => void doRemove()}>{t('delete')}</Button>\n </>\n }\n >\n <p>{t('removeWarn')}</p>\n </Modal>\n\n <Modal\n open={restartConfirm}\n onClose={() => setRestartConfirm(false)}\n title={t('restartConfirmTitle')}\n footer={\n <>\n <Button variant=\"ghost\" onClick={() => setRestartConfirm(false)}>{t('cancel')}</Button>\n <Button variant=\"primary\" onClick={doRestart}>{t('restartNow')}</Button>\n </>\n }\n >\n <p>{t('restartConfirmBody')}</p>\n </Modal>\n\n <Modal\n open={importOpen}\n onClose={() => setImportOpen(false)}\n title={t('importServers')}\n >\n <div className=\"dpc-form\">\n <p className=\"dpc-intro\" style={{ margin: 0 }}>{t('importIntro')}</p>\n {importItems === null && <p className=\"dpc-empty\">{t('loading')}</p>}\n {importItems !== null && importItems.length === 0 && <p className=\"dpc-empty\">{t('importEmpty')}</p>}\n {importItems !== null && importItems.length > 0 && (\n <ul className=\"dpc-cards\" style={{ gridTemplateColumns: 'minmax(0, 1fr)' }}>\n {importItems.map((item, index) => (\n <li className=\"dpc-card\" key={`${item.server.agent}/${item.server.name}`} style={{ opacity: item.existing ? 0.55 : 1 }}>\n <div className=\"dpc-cardTop\">\n <label style={{ display: 'inline-flex', alignItems: 'center', gap: 8, minWidth: 0, cursor: item.existing ? 'default' : 'pointer' }}>\n <input\n type=\"checkbox\"\n checked={item.checked}\n disabled={item.existing}\n onChange={(event) => {\n const next = importItems.slice()\n next[index] = { ...item, checked: event.target.checked }\n setImportItems(next)\n }}\n />\n <strong className=\"dpc-cardTitle\" title={item.server.name}>{item.server.name}</strong>\n </label>\n <span className=\"dpc-tag\" data-kind=\"source\">{item.server.agent === 'claude-code' ? 'Claude Code' : 'Codex'}</span>\n <span className=\"dpc-tag\">{item.server.transport}</span>\n {item.existing && <span className=\"dpc-tag\">{t('importExisting')}</span>}\n </div>\n <p className=\"dpc-cardDesc\">\n {item.server.transport === 'stdio'\n ? `${item.server.command ?? ''} ${(item.server.args ?? []).join(' ')}`\n : item.server.url ?? ''}\n </p>\n </li>\n ))}\n </ul>\n )}\n {formError !== null && <p className=\"dpc-formError\">{formError}</p>}\n <div className=\"dpc-cardRow\">\n <span className=\"dpc-spacer\" />\n <Button variant=\"ghost\" onClick={() => setImportOpen(false)}>{t('cancel')}</Button>\n <Button\n variant=\"primary\"\n disabled={busy || importItems === null || !importItems.some(item => item.checked && !item.existing)}\n onClick={() => void doImport()}\n >\n {t('importSelected')}\n </Button>\n </div>\n </div>\n </Modal>\n </div>\n )\n}\n", "/** Shared scoped stylesheet for the capabilities tabs \u2014 rides the host's\n * --dsw-* tokens (same design language as the plugin-inventory and install\n * tabs) so light and dark themes both stay correct. Prefix: dpc-. */\n\nexport const CSS = `\n.dpc-section{display:flex;flex-direction:column;gap:14px;width:100%;max-width:760px;color:var(--dsw-alias-label-primary)}\n.dpc-head{display:flex;align-items:center;gap:8px}\n.dpc-head h3{margin:0;font-size:13px;line-height:20px;font-weight:600}\n.dpc-head>svg{flex:none;color:var(--dsw-alias-label-tertiary)}\n.dpc-intro{margin:0;font-size:13px;line-height:20px;color:var(--dsw-alias-label-tertiary)}\n.dpc-listHead{display:flex;align-items:baseline;gap:7px;padding:0 2px;margin-top:2px}\n.dpc-listHead h3{margin:0;font-size:13px;line-height:20px;font-weight:600}\n.dpc-count{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums}\n.dpc-spacer{flex:1}\n.dpc-refresh{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;border:0;border-radius:6px;background:transparent;color:var(--dsw-alias-label-tertiary);cursor:pointer}\n.dpc-refresh:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}\n.dpc-refresh:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-2px}\n.dpc-empty{margin:0;font-size:13px;line-height:20px;color:var(--dsw-alias-label-tertiary)}\n.dpc-cards{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));align-items:stretch;gap:10px;margin:0;padding:0;list-style:none}\n.dpc-card{display:flex;flex-direction:column;gap:8px;min-width:0;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-alias-bg-layer-3);padding:12px 14px}\n.dpc-card:hover{background:var(--dsw-alias-interactive-bg-hover)}\n.dpc-cardTop{display:flex;align-items:center;gap:8px}\n.dpc-cardTitle{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:14px;line-height:20px;font-weight:600;font-family:var(--ds-font-family-code)}\n.dpc-cardDesc{margin:0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}\n.dpc-cardRow{display:flex;align-items:center;gap:6px;flex-wrap:wrap}\n.dpc-tag{display:inline-flex;align-items:center;min-height:20px;border-radius:5px;padding:1px 6px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);font-size:11px;line-height:16px;white-space:nowrap}\n.dpc-tag[data-kind='source']{background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 10%,transparent);color:var(--dsw-alias-state-business-primary)}\n.dpc-tag[data-kind='off']{background:color-mix(in srgb,var(--dsw-alias-state-warning-primary,var(--dsw-alias-label-tertiary)) 12%,transparent);color:var(--dsw-alias-label-secondary)}\n.dpc-cardActions{display:flex;align-items:center;gap:6px;margin-left:auto}\n.dpc-banner{display:flex;align-items:flex-start;gap:8px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:10px 12px;background:var(--dsw-alias-bg-layer-3);font-size:13px;line-height:20px}\n.dpc-banner[data-kind='ok']{border-color:color-mix(in srgb,var(--dsw-alias-state-success-primary) 35%,transparent);background:color-mix(in srgb,var(--dsw-alias-state-success-primary) 8%,transparent)}\n.dpc-banner[data-kind='error']{border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 35%,transparent);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 8%,transparent)}\n.dpc-banner[data-kind='info']{border-color:color-mix(in srgb,var(--dsw-alias-state-business-primary) 35%,transparent);background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 8%,transparent)}\n.dpc-bannerBody{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}\n.dpc-bannerHint{display:flex;align-items:center;gap:8px;flex-wrap:wrap;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}\n.dpc-form{display:flex;flex-direction:column;gap:10px}\n.dpc-label{display:flex;flex-direction:column;gap:4px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary)}\n.dpc-label>span:first-child{color:var(--dsw-alias-label-tertiary)}\n.dpc-input,.dpc-textarea,.dpc-select{width:100%;box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:7px 10px;outline:none;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px}\n.dpc-textarea{min-height:120px;resize:vertical;font-family:var(--ds-font-family-code);line-height:1.5}\n.dpc-textarea[data-short='true']{min-height:64px}\n.dpc-input:focus-visible,.dpc-textarea:focus-visible,.dpc-select:focus-visible{border-color:var(--dsw-alias-state-business-primary);box-shadow:0 0 0 2px color-mix(in srgb,var(--dsw-alias-state-business-primary) 18%,transparent)}\n.dpc-checks{display:flex;gap:16px;font-size:13px;line-height:20px}\n.dpc-checks label{display:inline-flex;align-items:center;gap:6px;cursor:pointer}\n.dpc-formError{margin:0;color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px}\n@media(max-width:680px){.dpc-cards{grid-template-columns:minmax(0,1fr)}}\n`\n", "/** Settings \u2192 Plugins \u201C\u6280\u80FD/Skills\u201D tab: view the discovered catalog, edit the\n * user-owned root. Pure presentation-layer \u2014 data arrives through props. */\n\nimport { useEffect, useState } from 'react'\nimport type { ReactElement } from 'react'\nimport { Button, IconRefreshOutline14, IconSkillOutline16, Modal, StateDot } from '@deepseek-ai/dsh-client-ui-primitives'\nimport { CSS } from './css.ts'\nimport type { Translate } from './index.ts'\n\n/** One skill as the host reports it (plus the editable flag the route adds). */\nexport interface SkillRowView {\n name: string\n description: string\n whenToUse?: string\n invocation: { modelInvocable: boolean; userInvocable: boolean }\n source: string\n provider: string\n editable: boolean\n}\n\nexport interface SkillsInjected {\n list(): Promise<{ skills: SkillRowView[] }>\n get(name: string): Promise<{ content: string }>\n save(input: { name: string; description: string; whenToUse?: string; modelInvocable: boolean; userInvocable: boolean; content: string }): Promise<{ ok: boolean }>\n remove(name: string): Promise<{ ok: boolean }>\n}\n\n/** Editor dialog state; null when closed. */\ninterface EditorState {\n mode: 'create' | 'edit' | 'view'\n name: string\n description: string\n whenToUse: string\n modelInvocable: boolean\n userInvocable: boolean\n content: string\n}\n\nconst SOURCE_KEYS: Record<string, string> = {\n 'project-dsh': 'sourceProjectDsh',\n 'project-agents': 'sourceProjectAgents',\n 'user-dsh': 'sourceUserDsh',\n 'user-agents': 'sourceUserAgents',\n runtime: 'sourceRuntime',\n bundled: 'sourceBundled',\n custom: 'sourceCustom',\n}\n\nexport function SkillsTab(props: { t: Translate; injected: SkillsInjected }): ReactElement {\n const { t, injected } = props\n const [skills, setSkills] = useState<SkillRowView[] | null>(null)\n const [editor, setEditor] = useState<EditorState | null>(null)\n const [confirmName, setConfirmName] = useState<string | null>(null)\n const [busy, setBusy] = useState(false)\n const [outcome, setOutcome] = useState<{ ok: boolean; text: string } | null>(null)\n const [formError, setFormError] = useState<string | null>(null)\n const [reload, setReload] = useState(0)\n\n useEffect(() => {\n let current = true\n void injected.list().then(\n (body) => { if (current) setSkills(body.skills) },\n (error: Error) => { if (current) { setSkills([]); setOutcome({ ok: false, text: `${t('failed')}: ${String(error.message ?? error)}` }) } },\n )\n return () => { current = false }\n }, [injected, reload, t])\n\n const openCreate = (): void => {\n setFormError(null)\n setEditor({ mode: 'create', name: '', description: '', whenToUse: '', modelInvocable: true, userInvocable: true, content: '' })\n }\n\n const openExisting = async (skill: SkillRowView): Promise<void> => {\n setBusy(true)\n setFormError(null)\n try {\n const body = await injected.get(skill.name)\n setEditor({\n mode: skill.editable ? 'edit' : 'view',\n name: skill.name,\n description: skill.description,\n whenToUse: skill.whenToUse ?? '',\n modelInvocable: skill.invocation.modelInvocable,\n userInvocable: skill.invocation.userInvocable,\n content: body.content,\n })\n } catch (error) {\n setOutcome({ ok: false, text: `${t('failed')}: ${String(error instanceof Error ? error.message : error)}` })\n } finally {\n setBusy(false)\n }\n }\n\n /**\n * After a write/delete, the watched directory takes a moment to invalidate\n * the catalog \u2014 poll until the change is visible (bounded), then stop.\n */\n const refreshUntil = (predicate: (skills: SkillRowView[]) => boolean, timeoutMs = 10_000): void => {\n const deadline = Date.now() + timeoutMs\n const tick = (): void => {\n if (Date.now() > deadline) return\n setTimeout(() => {\n void injected.list().then(\n (body) => {\n setSkills(body.skills)\n if (!predicate(body.skills)) tick()\n },\n () => tick(),\n )\n }, 1000)\n }\n tick()\n }\n\n const doSave = async (): Promise<void> => {\n if (editor === null) return\n setBusy(true)\n setFormError(null)\n try {\n const name = editor.name.trim()\n await injected.save({\n name,\n description: editor.description,\n whenToUse: editor.whenToUse.trim() === '' ? undefined : editor.whenToUse,\n modelInvocable: editor.modelInvocable,\n userInvocable: editor.userInvocable,\n content: editor.content,\n })\n setOutcome({ ok: true, text: t('saved') })\n setEditor(null)\n refreshUntil(skills => skills.some(skill => skill.name === name))\n } catch (error) {\n setFormError(String(error instanceof Error ? error.message : error))\n } finally {\n setBusy(false)\n }\n }\n\n const doDelete = async (): Promise<void> => {\n if (confirmName === null) return\n const name = confirmName\n setBusy(true)\n try {\n await injected.remove(name)\n setOutcome({ ok: true, text: t('saved') })\n refreshUntil(skills => !skills.some(skill => skill.name === name))\n } catch (error) {\n setOutcome({ ok: false, text: `${t('failed')}: ${String(error instanceof Error ? error.message : error)}` })\n } finally {\n setBusy(false)\n setConfirmName(null)\n }\n }\n\n const readOnly = editor?.mode === 'view'\n\n return (\n <div className=\"dpc-section\">\n <style>{CSS}</style>\n\n <div className=\"dpc-head\">\n <IconSkillOutline16 aria-hidden=\"true\" />\n <h3>{t('skillsTitle')}</h3>\n <span className=\"dpc-spacer\" />\n <Button variant=\"primary\" size=\"sm\" onClick={openCreate}>{t('newSkill')}</Button>\n </div>\n <p className=\"dpc-intro\">{t('skillsIntro')}</p>\n\n {outcome !== null && (\n <div className=\"dpc-banner\" data-kind={outcome.ok ? 'ok' : 'error'} role=\"status\">\n <StateDot state={outcome.ok ? 'done' : 'error'} size={10} />\n <div className=\"dpc-bannerBody\"><span>{outcome.text}</span></div>\n </div>\n )}\n\n <div className=\"dpc-listHead\">\n <h3>{t('skillsTab')}</h3>\n {skills !== null && <span className=\"dpc-count\">{skills.length}</span>}\n <span className=\"dpc-spacer\" />\n <button type=\"button\" className=\"dpc-refresh\" aria-label={t('view')} title={t('view')} disabled={busy} onClick={() => setReload((value) => value + 1)}>\n <IconRefreshOutline14 size={14} aria-hidden=\"true\" />\n </button>\n </div>\n\n {skills === null && <p className=\"dpc-empty\">{t('loading')}</p>}\n {skills !== null && skills.length === 0 && <p className=\"dpc-empty\">{t('emptySkills')}</p>}\n {skills !== null && skills.length > 0 && (\n <ul className=\"dpc-cards\">\n {skills.map((skill) => (\n <li className=\"dpc-card\" key={`${skill.source}/${skill.name}`}>\n <div className=\"dpc-cardTop\">\n <strong className=\"dpc-cardTitle\" title={skill.name}>{skill.name}</strong>\n <span className=\"dpc-tag\" data-kind=\"source\">{t(SOURCE_KEYS[skill.source] ?? 'sourceCustom')}</span>\n </div>\n <p className=\"dpc-cardDesc\" title={skill.description}>{skill.description}</p>\n <div className=\"dpc-cardRow\">\n {!skill.invocation.modelInvocable && <span className=\"dpc-tag\">\u2699</span>}\n {!skill.invocation.userInvocable && <span className=\"dpc-tag\">/</span>}\n <span className=\"dpc-spacer\" />\n <Button variant=\"ghost\" size=\"sm\" disabled={busy} onClick={() => void openExisting(skill)}>\n {skill.editable ? t('edit') : t('view')}\n </Button>\n {skill.editable && (\n <Button variant=\"ghost\" size=\"sm\" disabled={busy} onClick={() => setConfirmName(skill.name)}>{t('delete')}</Button>\n )}\n </div>\n </li>\n ))}\n </ul>\n )}\n\n <Modal\n open={editor !== null}\n onClose={() => setEditor(null)}\n title={editor === null ? '' : editor.mode === 'create' ? t('newSkill') : editor.mode === 'edit' ? t('editSkill') : t('viewSkill')}\n >\n {editor !== null && (\n <div className=\"dpc-form\">\n <label className=\"dpc-label\">\n <span>{t('skillName')}</span>\n <input\n className=\"dpc-input\"\n value={editor.name}\n disabled={readOnly || editor.mode === 'edit'}\n onChange={(event) => setEditor({ ...editor, name: event.target.value })}\n />\n </label>\n <label className=\"dpc-label\">\n <span>{t('skillDescription')}</span>\n <input\n className=\"dpc-input\"\n value={editor.description}\n disabled={readOnly}\n onChange={(event) => setEditor({ ...editor, description: event.target.value })}\n />\n </label>\n <label className=\"dpc-label\">\n <span>{t('skillWhenToUse')}</span>\n <input\n className=\"dpc-input\"\n value={editor.whenToUse}\n disabled={readOnly}\n onChange={(event) => setEditor({ ...editor, whenToUse: event.target.value })}\n />\n </label>\n <div className=\"dpc-checks\">\n <label>\n <input\n type=\"checkbox\"\n checked={editor.modelInvocable}\n disabled={readOnly}\n onChange={(event) => setEditor({ ...editor, modelInvocable: event.target.checked })}\n />\n {t('modelInvocable')}\n </label>\n <label>\n <input\n type=\"checkbox\"\n checked={editor.userInvocable}\n disabled={readOnly}\n onChange={(event) => setEditor({ ...editor, userInvocable: event.target.checked })}\n />\n {t('userInvocable')}\n </label>\n </div>\n <label className=\"dpc-label\">\n <span>{t('skillContent')}</span>\n <textarea\n className=\"dpc-textarea\"\n value={editor.content}\n readOnly={readOnly}\n onChange={(event) => setEditor({ ...editor, content: event.target.value })}\n />\n </label>\n {formError !== null && <p className=\"dpc-formError\">{formError}</p>}\n <div className=\"dpc-cardRow\">\n <span className=\"dpc-spacer\" />\n <Button variant=\"ghost\" onClick={() => setEditor(null)}>{readOnly ? t('close') : t('cancel')}</Button>\n {!readOnly && <Button variant=\"primary\" disabled={busy} onClick={() => void doSave()}>{t('save')}</Button>}\n </div>\n </div>\n )}\n </Modal>\n\n <Modal\n open={confirmName !== null}\n onClose={() => setConfirmName(null)}\n title={t('confirmDelete')}\n description={confirmName ?? undefined}\n footer={\n <>\n <Button variant=\"ghost\" onClick={() => setConfirmName(null)}>{t('cancel')}</Button>\n <Button variant=\"primary\" disabled={busy} onClick={() => void doDelete()}>{t('delete')}</Button>\n </>\n }\n >\n <p>{t('deleteWarn')}</p>\n </Modal>\n </div>\n )\n}\n", "/** zh/en dictionaries for the Settings capabilities tabs. */\n\nexport const zh = {\n skillsTab: '\u6280\u80FD',\n mcpTab: 'MCP',\n skillsTitle: '\u6280\u80FD\u7BA1\u7406',\n skillsIntro: '\u67E5\u770B\u4E0E\u7F16\u8F91 dsh \u53D1\u73B0\u7684\u6280\u80FD\uFF1B\u7528\u6237\u7EA7\u6280\u80FD\uFF08DSH_HOME/skills\uFF09\u53EF\u5728\u6B64\u65B0\u5EFA\u3001\u4FEE\u6539\u3001\u5220\u9664\uFF0C\u6587\u4EF6\u88AB\u76D1\u542C\uFF0C\u4FDD\u5B58\u5373\u751F\u6548\u3002\u82E5\u5B58\u5728 ~/.claude/skills \u6216 ~/.codex/skills\uFF0C\u4F1A\u81EA\u52A8\u7EB3\u5165\u626B\u63CF\uFF08\u96F6\u62F7\u8D1D\u3001\u5B9E\u65F6\u540C\u6B65\uFF09\u3002',\n newSkill: '\u65B0\u5EFA\u6280\u80FD',\n editSkill: '\u7F16\u8F91\u6280\u80FD',\n viewSkill: '\u67E5\u770B\u6280\u80FD',\n skillName: '\u540D\u79F0\uFF08kebab-case\uFF09',\n skillDescription: '\u63CF\u8FF0',\n skillWhenToUse: '\u4F7F\u7528\u65F6\u673A\uFF08\u53EF\u9009\uFF09',\n skillContent: '\u6B63\u6587\uFF08Markdown\uFF09',\n modelInvocable: '\u5141\u8BB8\u6A21\u578B\u8C03\u7528',\n userInvocable: '\u5141\u8BB8\u7528\u6237 / \u8C03\u7528',\n save: '\u4FDD\u5B58',\n cancel: '\u53D6\u6D88',\n delete: '\u5220\u9664',\n edit: '\u7F16\u8F91',\n view: '\u67E5\u770B',\n close: '\u5173\u95ED',\n emptySkills: '\u8FD8\u6CA1\u6709\u53D1\u73B0\u4EFB\u4F55\u6280\u80FD',\n loading: '\u8F7D\u5165\u4E2D\u2026',\n source: '\u6765\u6E90',\n sourceProjectDsh: '\u9879\u76EE',\n sourceProjectAgents: '\u9879\u76EE',\n sourceUserDsh: '\u7528\u6237',\n sourceUserAgents: '\u7528\u6237',\n sourceRuntime: '\u8FD0\u884C\u65F6',\n sourceBundled: '\u5185\u7F6E',\n sourceCustom: '\u81EA\u5B9A\u4E49',\n readOnly: '\u53EA\u8BFB',\n confirmDelete: '\u786E\u8BA4\u5220\u9664\u6280\u80FD\uFF1F',\n deleteWarn: '\u5C06\u5220\u9664 DSH_HOME/skills \u4E0B\u7684\u6574\u4E2A\u6280\u80FD\u76EE\u5F55\uFF0C\u6B64\u64CD\u4F5C\u4E0D\u53EF\u64A4\u9500\u3002',\n saved: '\u5DF2\u4FDD\u5B58\uFF0C\u6280\u80FD\u76EE\u5F55\u88AB\u76D1\u542C\uFF0C\u7A0D\u5019\u5373\u53EF\u5728\u4F1A\u8BDD\u4E2D\u4F7F\u7528\u3002',\n mcpTitle: 'MCP \u670D\u52A1\u5668',\n mcpIntro: '\u7BA1\u7406 profile \u4E2D\u7684 MCP \u670D\u52A1\u5668\u884C\uFF08@deepseek-ai/dsh-mcp-client\uFF09\u3002\u65B0\u589E\u3001\u4FEE\u6539\u6216\u5220\u9664\u540E\u9700\u8981\u91CD\u542F dsh \u624D\u751F\u6548\u3002',\n addServer: '\u6DFB\u52A0\u670D\u52A1\u5668',\n editServer: '\u7F16\u8F91\u670D\u52A1\u5668',\n serverName: '\u670D\u52A1\u5668\u540D\uFF08\u5DE5\u5177\u540D\u524D\u7F00\uFF09',\n transport: '\u4F20\u8F93\u65B9\u5F0F',\n transportStdio: 'stdio\uFF08\u672C\u5730\u547D\u4EE4\uFF09',\n transportHttp: 'streamable-http\uFF08URL\uFF09',\n command: '\u547D\u4EE4',\n args: '\u53C2\u6570\uFF08\u6BCF\u884C\u4E00\u4E2A\uFF09',\n envPairs: '\u73AF\u5883\u53D8\u91CF\uFF08\u6BCF\u884C KEY=VALUE\uFF09',\n url: 'URL',\n headersPairs: '\u8BF7\u6C42\u5934\uFF08\u6BCF\u884C KEY: VALUE\uFF09',\n disabled: '\u5DF2\u505C\u7528',\n enabled: '\u542F\u7528\u4E2D',\n toggle: '\u505C\u7528/\u542F\u7528',\n confirmRemove: '\u786E\u8BA4\u79FB\u9664\u8BE5\u670D\u52A1\u5668\uFF1F',\n removeWarn: '\u5C06\u4ECE profile \u914D\u7F6E\u4E2D\u79FB\u9664\u8FD9\u4E00\u884C\uFF0C\u91CD\u542F dsh \u540E\u5176\u5DE5\u5177\u4E0D\u518D\u51FA\u73B0\u3002',\n emptyMcp: '\u8FD8\u6CA1\u6709\u914D\u7F6E MCP \u670D\u52A1\u5668',\n importServers: '\u4ECE\u5176\u4ED6 Agent \u5BFC\u5165',\n importIntro: '\u626B\u63CF Claude Code\uFF08~/.claude.json\uFF09\u4E0E Codex\uFF08~/.codex/config.toml\uFF09\u7684 MCP \u670D\u52A1\u5668\u914D\u7F6E\uFF0C\u52FE\u9009\u540E\u5BFC\u5165\u4E3A\u672C profile \u7684\u670D\u52A1\u5668\u884C\u3002',\n importEmpty: '\u6CA1\u6709\u53D1\u73B0\u53EF\u5BFC\u5165\u7684 MCP \u670D\u52A1\u5668',\n importExisting: '\u5DF2\u5B58\u5728',\n importSelected: '\u5BFC\u5165\u9009\u4E2D\u9879',\n restartNeeded: '\u914D\u7F6E\u5DF2\u5199\u5165\uFF0C\u91CD\u542F dsh \u540E\u751F\u6548\u3002',\n restartDesktopHint: '\u91CD\u542F\u7531\u684C\u9762\u5E94\u7528\u8D1F\u8D23\uFF1A\u6258\u76D8\u83DC\u5355\u300C\u91CD\u542F\u670D\u52A1\u300D\u3002',\n restartOtherHint: '\u91CD\u542F\u65B9\u5F0F\uFF1A\u5173\u95ED\u5F53\u524D dsh \u8FDB\u7A0B\u540E\u91CD\u65B0\u8FD0\u884C\u3002',\n restartNow: '\u91CD\u542F\u670D\u52A1',\n restart: '\u91CD\u542F',\n restartConfirmTitle: '\u91CD\u542F dsh\uFF1F',\n restartConfirmBody: '\u91CD\u542F\u4F1A\u4E2D\u65AD\u6B63\u5728\u8FDB\u884C\u7684\u56DE\u5408\uFF0C\u672A\u4FDD\u5B58\u7684\u8F93\u5165\u53EF\u80FD\u4E22\u5931\uFF1BMCP \u884C\u7684\u53D8\u66F4\u5C06\u5728\u91CD\u542F\u540E\u751F\u6548\u3002',\n restarting: '\u6B63\u5728\u91CD\u542F\uFF0C\u6062\u590D\u540E\u5C06\u81EA\u52A8\u5237\u65B0\u9875\u9762\u2026',\n restartPortHint: '\u82E5\u91CD\u542F\u540E\u9875\u9762\u957F\u65F6\u95F4\u672A\u6062\u590D\uFF0C\u53EF\u80FD\u662F\u7AEF\u53E3\u5DF2\u53D8\u5316\uFF1A\u5728\u7EC8\u7AEF\u67E5\u770B\u65B0\u5730\u5740\u540E\u6253\u5F00\u3002',\n failed: '\u64CD\u4F5C\u5931\u8D25',\n}\n\nexport const en = {\n skillsTab: 'Skills',\n mcpTab: 'MCP',\n skillsTitle: 'Skills',\n skillsIntro: 'View and edit the skills dsh discovers; user-level skills (DSH_HOME/skills) can be created, edited, and deleted here \u2014 the directory is watched, saves apply without a restart. ~/.claude/skills and ~/.codex/skills are scanned too when present (zero-copy, live-synced).',\n newSkill: 'New skill',\n editSkill: 'Edit skill',\n viewSkill: 'View skill',\n skillName: 'Name (kebab-case)',\n skillDescription: 'Description',\n skillWhenToUse: 'When to use (optional)',\n skillContent: 'Body (Markdown)',\n modelInvocable: 'Model-invocable',\n userInvocable: 'User-invocable (/)',\n save: 'Save',\n cancel: 'Cancel',\n delete: 'Delete',\n edit: 'Edit',\n view: 'View',\n close: 'Close',\n emptySkills: 'No skills discovered yet',\n loading: 'Loading\u2026',\n source: 'Source',\n sourceProjectDsh: 'project',\n sourceProjectAgents: 'project',\n sourceUserDsh: 'user',\n sourceUserAgents: 'user',\n sourceRuntime: 'runtime',\n sourceBundled: 'bundled',\n sourceCustom: 'custom',\n readOnly: 'read-only',\n confirmDelete: 'Delete this skill?',\n deleteWarn: 'Removes the whole skill directory under DSH_HOME/skills. This cannot be undone.',\n saved: 'Saved. The skills directory is watched; the skill is usable in sessions shortly.',\n mcpTitle: 'MCP servers',\n mcpIntro: 'Manage the MCP server rows (@deepseek-ai/dsh-mcp-client) in this profile. Additions, edits, and removals take effect after a dsh restart.',\n addServer: 'Add server',\n editServer: 'Edit server',\n serverName: 'Server name (tool name prefix)',\n transport: 'Transport',\n transportStdio: 'stdio (local command)',\n transportHttp: 'streamable-http (URL)',\n command: 'Command',\n args: 'Arguments (one per line)',\n envPairs: 'Environment (KEY=VALUE per line)',\n url: 'URL',\n headersPairs: 'Headers (KEY: VALUE per line)',\n disabled: 'Disabled',\n enabled: 'Enabled',\n toggle: 'Enable/disable',\n confirmRemove: 'Remove this server?',\n removeWarn: 'Removes the row from the profile configuration; its tools disappear after the next dsh restart.',\n emptyMcp: 'No MCP servers configured yet',\n importServers: 'Import from other agents',\n importIntro: 'Scans Claude Code (~/.claude.json) and Codex (~/.codex/config.toml) MCP server configs; selected entries become server rows in this profile.',\n importEmpty: 'No importable MCP servers found',\n importExisting: 'already here',\n importSelected: 'Import selected',\n restartNeeded: 'Configuration written \u2014 restart dsh to apply.',\n restartDesktopHint: 'The desktop app owns restarts: use the tray \u201CRestart service\u201D.',\n restartOtherHint: 'Restart by closing this dsh process and running it again.',\n restartNow: 'Restart service',\n restart: 'Restart',\n restartConfirmTitle: 'Restart dsh?',\n restartConfirmBody: 'Restarting interrupts any running turn and may lose unsaved input; pending MCP changes apply after the restart.',\n restarting: 'Restarting \u2014 the page will reload once the host is back\u2026',\n restartPortHint: 'If the page does not recover, the port may have changed: check the terminal for the new URL.',\n failed: 'Operation failed',\n}\n"],
5
- "mappings": "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",
6
- "names": ["import_react", "import_react", "import_dsh_client_ui_primitives", "import_jsx_runtime", "name", "skills", "name", "h"]
3
+ "sources": ["../src/client/index.ts", "../src/client/CapabilitiesSection.tsx", "../src/client/css.ts", "../src/client/McpTab.tsx", "../src/client/SkillsTab.tsx", "../src/client/locales.ts"],
4
+ "sourcesContent": ["/** dsh-plugin-capabilities client entry: contributes the top-level\n * \u201C\u6280\u80FD\u4E0E MCP\u201D section into Settings (beside \u901A\u7528\u8BBE\u7F6E/\u6A21\u578B, with Skills and\n * MCP as internal tabs). Calls the host routes with fetch. */\n\nimport { createElement as h } from 'react'\nimport { CapabilitiesSection } from './CapabilitiesSection.tsx'\nimport type { McpInjected, McpRow } from './McpTab.tsx'\nimport type { SkillsInjected, SkillRowView } from './SkillsTab.tsx'\nimport { zh, en } from './locales.ts'\n\n/** Locale dictionary namespace owned by this plugin. */\nexport const NS = 'settings.pluginCapabilities'\n\n/** The `t` function bound by the locale service. */\nexport interface Translate {\n (key: string): string\n}\n\n/** Minimal structural subset of the slots service. */\ninterface SlotsService {\n inject(slot: string, register: () => unknown): void\n register(meta: Record<string, unknown>, component: () => unknown): unknown\n}\n\n/** Minimal structural subset of the locale service. */\ninterface LocaleService {\n register(namespace: string, dicts: { zh: Record<string, string>; en: Record<string, string> }): unknown\n bind(namespace: string): Translate\n}\n\n/** The client cordis context this plugin relies on (structural). */\ninterface CapabilitiesClientContext {\n effect(callback: () => unknown, label?: string): void\n locale: LocaleService\n slots: SlotsService\n}\n\n/** Same-origin fetch of the manager's host routes. */\nasync function fetchJson<T>(path: string, init?: RequestInit): Promise<T> {\n const response = await fetch(path, init)\n const body = (await response.json()) as T & { error?: string }\n if (!response.ok) throw new Error(body.error ?? `HTTP ${response.status}`)\n return body\n}\n\nconst post = (path: string, body: unknown): Promise<unknown> =>\n fetchJson(path, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body) })\n\nexport const name = 'dsh-plugin-capabilities'\nexport const inject = ['slots', 'locale']\n\nexport interface SkillBody { content: string }\n\n/** One foreign-agent server from the import scan. */\nexport interface ImportedServerView {\n agent: 'claude-code' | 'codex'\n name: string\n transport: 'stdio' | 'streamable-http'\n command?: string\n args?: string[]\n env?: Record<string, string>\n url?: string\n headers?: Record<string, string>\n}\n\nexport function apply(ctx: CapabilitiesClientContext): void {\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dsh-plugin-capabilities: dictionaries')\n const t = ctx.locale.bind(NS)\n\n const skillsInjected: SkillsInjected = {\n list: () => fetchJson<{ skills: SkillRowView[] }>('/dsh-plugin-capabilities/skills'),\n get: (name: string) => fetchJson<SkillBody>(`/dsh-plugin-capabilities/skill?name=${encodeURIComponent(name)}`),\n save: (input: unknown) => post('/dsh-plugin-capabilities/skill/save', input) as Promise<{ ok: boolean }>,\n remove: (name: string) => post('/dsh-plugin-capabilities/skill/delete', { name }) as Promise<{ ok: boolean }>,\n }\n\n const mcpInjected: McpInjected = {\n list: () => fetchJson<{ servers: McpRow[] }>('/dsh-plugin-capabilities/mcp'),\n save: (input: unknown) => post('/dsh-plugin-capabilities/mcp/save', input) as Promise<{ ok: boolean; id: string }>,\n toggle: (id: string, disabled: boolean) => post('/dsh-plugin-capabilities/mcp/toggle', { id, disabled }) as Promise<{ ok: boolean }>,\n remove: (id: string) => post('/dsh-plugin-capabilities/mcp/remove', { id }) as Promise<{ ok: boolean }>,\n scanImport: () => fetchJson<{ servers: ImportedServerView[]; existing: string[] }>('/dsh-plugin-capabilities/import/scan'),\n applyImport: (items: Array<{ agent: string; name: string }>) =>\n post('/dsh-plugin-capabilities/import/apply', { items }) as Promise<{ ok: boolean; results: Array<{ name: string; ok: boolean; error?: string }> }>,\n restart: async (): Promise<void> => {\n if (window.dshDesktop !== undefined) {\n window.dshDesktop.restartSidecar?.()\n return\n }\n // \u72EC\u7ACB dsh web\uFF1A\u81EA\u91CD\u542F\u8DEF\u7531\u3002\u8FDE\u63A5\u5728\u5173\u505C\u9014\u4E2D\u65AD\u5F00\u5C5E\u9884\u671F\uFF0C\u4E0D\u7B97\u5931\u8D25\u3002\n try { await post('/dsh-plugin-capabilities/restart', {}) } catch { /* dying mid-restart is expected */ }\n },\n desktop: window.dshDesktop !== undefined,\n }\n\n // One top-level nav entry (between \u6A21\u578B and \u63D2\u4EF6), not a tab under the\n // Plugins section \u2014 the section component owns its internal Skills/MCP\n // tabs directly, so nothing registers into settings.plugins.tab anymore.\n ctx.slots.inject('settings.section', () => {\n return ctx.slots.register({\n name: 'settings.section',\n id: 'capabilities',\n order: 12,\n label: () => t('sectionNav'),\n locale: NS,\n }, () => h(CapabilitiesSection, { t, skills: skillsInjected, mcp: mcpInjected }))\n })\n}\n", "/** Settings top-level \u201C\u6280\u80FD\u4E0E MCP\u201D section: one nav entry beside \u901A\u7528\u8BBE\u7F6E and\n * \u6A21\u578B, with the Skills and MCP pages as internal tabs. The two pages are the\n * same components the Plugins section used to host \u2014 promoted a level instead\n * of squeezed under \u63D2\u4EF6. Pure composition; data still arrives through the\n * injected faces. */\n\nimport { useEffect, useId, useRef, useState } from 'react'\nimport type { ReactElement } from 'react'\nimport { CSS } from './css.ts'\nimport { McpTab } from './McpTab.tsx'\nimport type { McpInjected } from './McpTab.tsx'\nimport { SkillsTab } from './SkillsTab.tsx'\nimport type { SkillsInjected } from './SkillsTab.tsx'\nimport type { Translate } from './index.ts'\n\nexport function CapabilitiesSection(props: {\n t: Translate\n skills: SkillsInjected\n mcp: McpInjected\n}): ReactElement {\n const { t, skills, mcp } = props\n const tabsId = useId()\n const tabRefs = useRef<Array<HTMLButtonElement | null>>([])\n const rows = [\n { id: 'skills', label: t('skillsTab') },\n { id: 'mcp', label: t('mcpTab') },\n ]\n const [activeId, setActiveId] = useState('skills')\n // The skills page mounts immediately; a tab mounts only when first\n // selected, then stays mounted while hidden so editor drafts and outcome\n // banners survive switching between the two views.\n const [visitedIds, setVisitedIds] = useState<ReadonlySet<string>>(() => new Set(['skills']))\n\n useEffect(() => {\n setVisitedIds((previous) => {\n if (previous.has(activeId)) return previous\n return new Set([...previous, activeId])\n })\n }, [activeId])\n\n return (\n <div className=\"dpc-section\">\n <style>{CSS}</style>\n\n <h2 className=\"dpc-heading\">{t('sectionNav')}</h2>\n <div className=\"dpc-tabs\" role=\"tablist\" aria-label={t('sectionNav')}>\n {rows.map((row, index) => {\n const selected = row.id === activeId\n return (\n <button\n key={row.id}\n ref={(element) => { tabRefs.current[index] = element }}\n id={`${tabsId}-tab-${row.id}`}\n type=\"button\"\n role=\"tab\"\n className=\"dpc-tab\"\n aria-selected={selected}\n aria-controls={`${tabsId}-panel-${row.id}`}\n data-active={selected ? 'true' : undefined}\n tabIndex={selected ? 0 : -1}\n onClick={() => { setActiveId(row.id) }}\n onKeyDown={(event) => {\n let nextIndex: number\n switch (event.key) {\n case 'ArrowRight': nextIndex = (index + 1) % rows.length; break\n case 'ArrowLeft': nextIndex = (index - 1 + rows.length) % rows.length; break\n case 'Home': nextIndex = 0; break\n case 'End': nextIndex = rows.length - 1; break\n default: return\n }\n event.preventDefault()\n setActiveId(rows[nextIndex]?.id ?? 'skills')\n tabRefs.current[nextIndex]?.focus()\n }}\n >\n {row.label}\n </button>\n )\n })}\n </div>\n {rows\n .filter(row => row.id === activeId || visitedIds.has(row.id))\n .map((row) => {\n const selected = row.id === activeId\n return (\n <div\n key={row.id}\n id={`${tabsId}-panel-${row.id}`}\n className=\"dpc-tabPanel\"\n role=\"tabpanel\"\n aria-labelledby={`${tabsId}-tab-${row.id}`}\n hidden={!selected}\n >\n {row.id === 'skills'\n ? <SkillsTab t={t} injected={skills} />\n : <McpTab t={t} injected={mcp} />}\n </div>\n )\n })}\n </div>\n )\n}\n", "/** Shared scoped stylesheet for the capabilities tabs \u2014 rides the host's\n * --dsw-* tokens (same design language as the plugin-inventory and install\n * tabs) so light and dark themes both stay correct. Prefix: dpc-. */\n\nexport const CSS = `\n.dpc-section{display:flex;flex-direction:column;gap:14px;width:100%;max-width:760px;color:var(--dsw-alias-label-primary)}\n.dpc-heading{margin:0;font-size:18px;line-height:26px;font-weight:600}\n/* Top-level section's internal tabs \u2014 same underline-tab look as the host's\n Plugins section so the promoted placement still reads as one family. */\n.dpc-tabs{display:flex;align-items:flex-end;gap:22px;border-bottom:1px solid var(--dsw-alias-border-l2);margin-top:2px}\n.dpc-tab{position:relative;border:0;padding:7px 1px 9px;background:transparent;color:var(--dsw-alias-label-tertiary);font:inherit;font-size:13px;line-height:20px;cursor:pointer}\n.dpc-tab:hover,.dpc-tab[data-active='true']{color:var(--dsw-alias-label-primary)}\n.dpc-tab[data-active='true']::after,.dpc-tab:focus-visible::after{position:absolute;right:0;bottom:-1px;left:0;height:2px;border-radius:2px 2px 0 0;background:var(--dsw-alias-label-primary);content:''}\n.dpc-tab:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px;border-radius:2px;color:var(--dsw-alias-label-primary)}\n.dpc-tabPanel{min-width:0;padding-top:2px}\n.dpc-head{display:flex;align-items:center;gap:8px}\n.dpc-head h3{margin:0;font-size:13px;line-height:20px;font-weight:600}\n.dpc-head>svg{flex:none;color:var(--dsw-alias-label-tertiary)}\n.dpc-intro{margin:0;font-size:13px;line-height:20px;color:var(--dsw-alias-label-tertiary)}\n.dpc-listHead{display:flex;align-items:baseline;gap:7px;padding:0 2px;margin-top:2px}\n.dpc-listHead h3{margin:0;font-size:13px;line-height:20px;font-weight:600}\n.dpc-count{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums}\n.dpc-spacer{flex:1}\n.dpc-refresh{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;border:0;border-radius:6px;background:transparent;color:var(--dsw-alias-label-tertiary);cursor:pointer}\n.dpc-refresh:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}\n.dpc-refresh:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-2px}\n.dpc-empty{margin:0;font-size:13px;line-height:20px;color:var(--dsw-alias-label-tertiary)}\n.dpc-cards{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));align-items:stretch;gap:10px;margin:0;padding:0;list-style:none}\n.dpc-card{display:flex;flex-direction:column;gap:8px;min-width:0;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-alias-bg-layer-3);padding:12px 14px}\n.dpc-card:hover{background:var(--dsw-alias-interactive-bg-hover)}\n.dpc-cardTop{display:flex;align-items:center;gap:8px}\n.dpc-cardTitle{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:14px;line-height:20px;font-weight:600;font-family:var(--ds-font-family-code)}\n.dpc-cardDesc{margin:0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}\n.dpc-cardRow{display:flex;align-items:center;gap:6px;flex-wrap:wrap}\n.dpc-tag{display:inline-flex;align-items:center;min-height:20px;border-radius:5px;padding:1px 6px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);font-size:11px;line-height:16px;white-space:nowrap}\n.dpc-tag[data-kind='source']{background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 10%,transparent);color:var(--dsw-alias-state-business-primary)}\n.dpc-tag[data-kind='off']{background:color-mix(in srgb,var(--dsw-alias-state-warning-primary,var(--dsw-alias-label-tertiary)) 12%,transparent);color:var(--dsw-alias-label-secondary)}\n.dpc-cardActions{display:flex;align-items:center;gap:6px;margin-left:auto}\n.dpc-banner{display:flex;align-items:flex-start;gap:8px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:10px 12px;background:var(--dsw-alias-bg-layer-3);font-size:13px;line-height:20px}\n.dpc-banner[data-kind='ok']{border-color:color-mix(in srgb,var(--dsw-alias-state-success-primary) 35%,transparent);background:color-mix(in srgb,var(--dsw-alias-state-success-primary) 8%,transparent)}\n.dpc-banner[data-kind='error']{border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 35%,transparent);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 8%,transparent)}\n.dpc-banner[data-kind='info']{border-color:color-mix(in srgb,var(--dsw-alias-state-business-primary) 35%,transparent);background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 8%,transparent)}\n.dpc-bannerBody{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}\n.dpc-bannerHint{display:flex;align-items:center;gap:8px;flex-wrap:wrap;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}\n.dpc-form{display:flex;flex-direction:column;gap:10px}\n.dpc-label{display:flex;flex-direction:column;gap:4px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary)}\n.dpc-label>span:first-child{color:var(--dsw-alias-label-tertiary)}\n.dpc-input,.dpc-textarea,.dpc-select{width:100%;box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:7px 10px;outline:none;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px}\n.dpc-textarea{min-height:240px;resize:vertical;font-family:var(--ds-font-family-code);line-height:1.5}\n.dpc-textarea[data-short='true']{min-height:96px}\n.dpc-input:focus-visible,.dpc-textarea:focus-visible,.dpc-select:focus-visible{border-color:var(--dsw-alias-state-business-primary);box-shadow:0 0 0 2px color-mix(in srgb,var(--dsw-alias-state-business-primary) 18%,transparent)}\n.dpc-checks{display:flex;gap:16px;font-size:13px;line-height:20px}\n.dpc-checks label{display:inline-flex;align-items:center;gap:6px;cursor:pointer}\n.dpc-formError{margin:0;color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px}\n/* Import dialog: wider than the host's 380px default; the server list gets\n its own scroll so title, intro, and footer stay pinned. The doubled class\n beats the host dialog's module CSS regardless of injection order. */\n.dpc-modalWide.dpc-modalWide{width:min(680px,100%)}\n/* Editor dialogs (new skill / server): 640px wide so markdown bodies and\n command/arg/env lines stop wrapping mid-token; the content column scrolls\n on short viewports instead of clipping past the dialog edge. */\n.dpc-modalForm.dpc-modalForm{width:min(640px,100%)}\n.dpc-modalScroll.dpc-modalScroll{max-height:calc(100vh - 160px);overflow-y:auto}\n.dpc-importScroll{display:flex;flex-direction:column;gap:14px;max-height:min(400px,52vh);overflow-y:auto;padding:2px 4px 2px 2px}\n.dpc-importGroup{display:flex;flex-direction:column;gap:8px}\n.dpc-importHead{display:flex;align-items:center;gap:8px;padding:0 2px}\n.dpc-importCount{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums}\n.dpc-importAll{margin-left:auto;display:inline-flex;align-items:center;gap:6px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary);cursor:pointer}\n@media(max-width:680px){.dpc-cards{grid-template-columns:minmax(0,1fr)}}\n`\n", "/** Settings \u2192 Plugins \u201CMCP\u201D tab: manage the profile's mcp-client rows.\n * Mutations rewrite the profile patch and need a dsh restart \u2014 the banner\n * hands the restart to the desktop shell when one is present. */\n\nimport { useEffect, useState } from 'react'\nimport type { ReactElement } from 'react'\nimport { Button, IconApiOutline14, IconRefreshOutline14, Modal, StateDot } from '@deepseek-ai/dsh-client-ui-primitives'\nimport { CSS } from './css.ts'\nimport type { ImportedServerView, Translate } from './index.ts'\n\nexport interface McpRow {\n id: string\n serverName: string\n transport: 'stdio' | 'streamable-http'\n disabled: boolean\n command?: string\n args?: string[]\n env?: Record<string, string>\n cwd?: string\n url?: string\n headers?: Record<string, string>\n}\n\nexport interface McpInjected {\n list(): Promise<{ servers: McpRow[] }>\n save(input: Record<string, unknown>): Promise<{ ok: boolean; id: string }>\n toggle(id: string, disabled: boolean): Promise<{ ok: boolean }>\n remove(id: string): Promise<{ ok: boolean }>\n scanImport(): Promise<{ servers: ImportedServerView[]; existing: string[] }>\n applyImport(items: Array<{ agent: string; name: string }>): Promise<{ ok: boolean; results: Array<{ name: string; ok: boolean; error?: string }> }>\n restart(): Promise<void>\n desktop: boolean\n}\n\n/** Editor dialog state; null when closed. Textareas hold line-based values. */\ninterface EditorState {\n id: string\n serverName: string\n transport: 'stdio' | 'streamable-http'\n command: string\n args: string\n env: string\n url: string\n headers: string\n}\n\n/** One import entry together with its index in the flat state array \u2014\n * checkbox handlers rewrite the flat array, so indices must survive grouping. */\ntype ImportItem = { server: ImportedServerView; existing: boolean; checked: boolean }\n\n/** Group import candidates by source agent, known agents first. */\nexport function importGroups(items: ImportItem[]): Array<{ agent: string; label: string; items: Array<{ item: ImportItem; index: number }> }> {\n const label = (agent: string) => agent === 'claude-code' ? 'Claude Code' : agent === 'codex' ? 'Codex' : agent\n const order = ['claude-code', 'codex']\n const agents = [...new Set(items.map(item => item.server.agent))]\n .sort((a, b) => {\n const rank = (agent: string) => { const at = order.indexOf(agent); return at === -1 ? order.length : at }\n return rank(a) - rank(b) || a.localeCompare(b)\n })\n return agents.map(agent => ({\n agent,\n label: label(agent),\n items: items.map((item, index) => ({ item, index })).filter(({ item }) => item.server.agent === agent),\n }))\n}\n\n/** KEY=VALUE / KEY: VALUE lines to a map. */\nfunction parsePairs(text: string, separator: ':' | '='): Record<string, string> {\n const map: Record<string, string> = {}\n for (const line of text.split(/\\r?\\n/)) {\n const trimmed = line.trim()\n if (trimmed === '' || trimmed.startsWith('#')) continue\n const at = trimmed.indexOf(separator)\n if (at <= 0) continue\n map[trimmed.slice(0, at).trim()] = trimmed.slice(at + 1).trim()\n }\n return map\n}\n\nfunction mapToPairs(map: Record<string, string> | undefined, separator: string): string {\n if (map === undefined) return ''\n return Object.entries(map).map(([key, value]) => `${key}${separator}${value.includes('\\n') ? JSON.stringify(value) : value}`).join('\\n')\n}\n\nexport function McpTab(props: { t: Translate; injected: McpInjected }): ReactElement {\n const { t, injected } = props\n const [servers, setServers] = useState<McpRow[] | null>(null)\n const [editor, setEditor] = useState<EditorState | null>(null)\n const [confirmId, setConfirmId] = useState<string | null>(null)\n const [importOpen, setImportOpen] = useState(false)\n const [importItems, setImportItems] = useState<Array<{ server: ImportedServerView; existing: boolean; checked: boolean }> | null>(null)\n const [busy, setBusy] = useState(false)\n const [pending, setPending] = useState(false)\n const [restartConfirm, setRestartConfirm] = useState(false)\n const [restarting, setRestarting] = useState(false)\n const [outcome, setOutcome] = useState<{ ok: boolean; text: string } | null>(null)\n const [formError, setFormError] = useState<string | null>(null)\n const [reload, setReload] = useState(0)\n\n const openImport = async (): Promise<void> => {\n setImportOpen(true)\n setImportItems(null)\n try {\n const body = await injected.scanImport()\n const existing = new Set(body.existing)\n setImportItems(body.servers.map(server => ({\n server,\n existing: existing.has(server.name),\n checked: !existing.has(server.name),\n })))\n } catch (error) {\n setImportItems([])\n setOutcome({ ok: false, text: `${t('failed')}: ${String(error instanceof Error ? error.message : error)}` })\n }\n }\n\n const doImport = async (): Promise<void> => {\n if (importItems === null) return\n const items = importItems.filter(item => item.checked && !item.existing).map(item => ({ agent: item.server.agent, name: item.server.name }))\n setBusy(true)\n try {\n const body = await injected.applyImport(items)\n const failed = body.results.filter(item => !item.ok)\n setOutcome(failed.length === 0\n ? { ok: true, text: t('restartNeeded') }\n : { ok: false, text: `${t('failed')}: ${failed.map(item => `${item.name} (${item.error})`).join(', ')}` })\n setImportOpen(false)\n setPending(true)\n setReload((value) => value + 1)\n } catch (error) {\n setOutcome({ ok: false, text: `${t('failed')}: ${String(error instanceof Error ? error.message : error)}` })\n } finally {\n setBusy(false)\n }\n }\n\n const reloadList = (showPending: boolean): void => {\n setReload((value) => value + 1)\n if (showPending) setPending(true)\n }\n\n useEffect(() => {\n let current = true\n void injected.list().then(\n (body) => { if (current) setServers(body.servers) },\n (error: Error) => { if (current) { setServers([]); setOutcome({ ok: false, text: `${t('failed')}: ${String(error.message ?? error)}` }) } },\n )\n return () => { current = false }\n }, [injected, reload, t])\n\n const openCreate = (): void => {\n setFormError(null)\n setEditor({ id: '', serverName: '', transport: 'stdio', command: '', args: '', env: '', url: '', headers: '' })\n }\n\n const openEdit = (row: McpRow): void => {\n setFormError(null)\n setEditor({\n id: row.id,\n serverName: row.serverName,\n transport: row.transport,\n command: row.command ?? '',\n args: (row.args ?? []).join('\\n'),\n env: mapToPairs(row.env, '='),\n url: row.url ?? '',\n headers: mapToPairs(row.headers, ':'),\n })\n }\n\n const doSave = async (): Promise<void> => {\n if (editor === null) return\n setBusy(true)\n setFormError(null)\n try {\n await injected.save({\n id: editor.id,\n serverName: editor.serverName.trim(),\n transport: editor.transport,\n ...(editor.transport === 'stdio'\n ? {\n command: editor.command.trim(),\n args: editor.args.split(/\\r?\\n/).map(line => line.trim()).filter(line => line !== ''),\n env: parsePairs(editor.env, '='),\n }\n : {\n url: editor.url.trim(),\n headers: parsePairs(editor.headers, ':'),\n }),\n })\n setEditor(null)\n setOutcome({ ok: true, text: t('restartNeeded') })\n reloadList(true)\n } catch (error) {\n setFormError(String(error instanceof Error ? error.message : error))\n } finally {\n setBusy(false)\n }\n }\n\n const doToggle = async (row: McpRow): Promise<void> => {\n setBusy(true)\n try {\n await injected.toggle(row.id, !row.disabled)\n setOutcome({ ok: true, text: t('restartNeeded') })\n reloadList(true)\n } catch (error) {\n setOutcome({ ok: false, text: `${t('failed')}: ${String(error instanceof Error ? error.message : error)}` })\n } finally {\n setBusy(false)\n }\n }\n\n const doRemove = async (): Promise<void> => {\n if (confirmId === null) return\n setBusy(true)\n try {\n await injected.remove(confirmId)\n setOutcome({ ok: true, text: t('restartNeeded') })\n reloadList(true)\n } catch (error) {\n setOutcome({ ok: false, text: `${t('failed')}: ${String(error instanceof Error ? error.message : error)}` })\n } finally {\n setBusy(false)\n setConfirmId(null)\n }\n }\n\n const doRestart = (): void => {\n setRestartConfirm(false)\n setRestarting(true)\n void injected.restart()\n // \u684C\u9762\u6A21\u5F0F\uFF1A\u58F3\u5C42\u91CD\u542F\u5B8C\u6210\u540E\u4F1A\u91CD\u8F7D\u7A97\u53E3\u3002\u72EC\u7ACB\u6A21\u5F0F\uFF1A\u8F6E\u8BE2\u672C\u6E90\uFF0C\u6062\u590D\u5373\u5237\u65B0\u3002\n if (injected.desktop) return\n const deadline = Date.now() + 60_000\n const poll = (): void => {\n if (Date.now() > deadline) return\n window.setTimeout(() => {\n void injected.list().then(\n () => { window.location.reload() },\n () => { poll() },\n )\n }, 1500)\n }\n window.setTimeout(poll, 3000)\n }\n\n const restartBanner = (\n <div className=\"dpc-banner\" data-kind=\"info\" role=\"status\">\n <StateDot state=\"ongoing\" size={10} />\n <div className=\"dpc-bannerBody\">\n <span>{restarting ? t('restarting') : t('restartNeeded')}</span>\n <span className=\"dpc-bannerHint\">\n {restarting\n ? (!injected.desktop && t('restartPortHint'))\n : injected.desktop\n ? <>{t('restartDesktopHint')}{' '}<Button variant=\"outline\" size=\"sm\" onClick={() => setRestartConfirm(true)}>{t('restartNow')}</Button></>\n : t('restartOtherHint')}\n </span>\n </div>\n </div>\n )\n\n return (\n <div className=\"dpc-section\">\n <style>{CSS}</style>\n\n <div className=\"dpc-head\">\n <IconApiOutline14 aria-hidden=\"true\" />\n <h3>{t('mcpTitle')}</h3>\n <span className=\"dpc-spacer\" />\n <Button variant=\"ghost\" size=\"sm\" disabled={restarting} onClick={() => setRestartConfirm(true)}>{t('restart')}</Button>\n <Button variant=\"ghost\" size=\"sm\" onClick={() => void openImport()}>{t('importServers')}</Button>\n <Button variant=\"primary\" size=\"sm\" onClick={openCreate}>{t('addServer')}</Button>\n </div>\n <p className=\"dpc-intro\">{t('mcpIntro')}</p>\n\n {outcome !== null && (\n <div className=\"dpc-banner\" data-kind={outcome.ok ? 'ok' : 'error'} role=\"status\">\n <StateDot state={outcome.ok ? 'done' : 'error'} size={10} />\n <div className=\"dpc-bannerBody\"><span>{outcome.text}</span></div>\n </div>\n )}\n {(pending || restarting) && restartBanner}\n\n <div className=\"dpc-listHead\">\n <h3>{t('mcpTab')}</h3>\n {servers !== null && <span className=\"dpc-count\">{servers.length}</span>}\n <span className=\"dpc-spacer\" />\n <button type=\"button\" className=\"dpc-refresh\" aria-label={t('view')} title={t('view')} disabled={busy} onClick={() => setReload((value) => value + 1)}>\n <IconRefreshOutline14 size={14} aria-hidden=\"true\" />\n </button>\n </div>\n\n {servers === null && <p className=\"dpc-empty\">{t('loading')}</p>}\n {servers !== null && servers.length === 0 && <p className=\"dpc-empty\">{t('emptyMcp')}</p>}\n {servers !== null && servers.length > 0 && (\n <ul className=\"dpc-cards\">\n {servers.map((row) => (\n <li className=\"dpc-card\" key={row.id}>\n <div className=\"dpc-cardTop\">\n <strong className=\"dpc-cardTitle\" title={row.id}>{row.serverName}</strong>\n <span className=\"dpc-tag\">{row.transport}</span>\n <span className=\"dpc-tag\" data-kind={row.disabled ? 'off' : undefined}>{row.disabled ? t('disabled') : t('enabled')}</span>\n </div>\n <p className=\"dpc-cardDesc\">\n {row.transport === 'stdio' ? `${row.command ?? ''} ${(row.args ?? []).join(' ')}` : row.url ?? ''}\n </p>\n <div className=\"dpc-cardRow\">\n <span className=\"dpc-spacer\" />\n <Button variant=\"ghost\" size=\"sm\" disabled={busy} onClick={() => void doToggle(row)}>{t('toggle')}</Button>\n <Button variant=\"ghost\" size=\"sm\" disabled={busy} onClick={() => openEdit(row)}>{t('edit')}</Button>\n <Button variant=\"ghost\" size=\"sm\" disabled={busy} onClick={() => setConfirmId(row.id)}>{t('delete')}</Button>\n </div>\n </li>\n ))}\n </ul>\n )}\n\n <Modal\n open={editor !== null}\n onClose={() => setEditor(null)}\n title={editor !== null && editor.id !== '' ? t('editServer') : t('addServer')}\n className=\"dpc-modalForm\"\n contentClassName=\"dpc-modalScroll\"\n >\n {editor !== null && (\n <div className=\"dpc-form\">\n <label className=\"dpc-label\">\n <span>{t('serverName')}</span>\n <input\n className=\"dpc-input\"\n value={editor.serverName}\n disabled={editor.id !== ''}\n onChange={(event) => setEditor({ ...editor, serverName: event.target.value })}\n />\n </label>\n <label className=\"dpc-label\">\n <span>{t('transport')}</span>\n <select\n className=\"dpc-select\"\n value={editor.transport}\n disabled={editor.id !== ''}\n onChange={(event) => setEditor({ ...editor, transport: event.target.value as EditorState['transport'] })}\n >\n <option value=\"stdio\">{t('transportStdio')}</option>\n <option value=\"streamable-http\">{t('transportHttp')}</option>\n </select>\n </label>\n {editor.transport === 'stdio' ? (\n <>\n <label className=\"dpc-label\">\n <span>{t('command')}</span>\n <input className=\"dpc-input\" value={editor.command} onChange={(event) => setEditor({ ...editor, command: event.target.value })} />\n </label>\n <label className=\"dpc-label\">\n <span>{t('args')}</span>\n <textarea className=\"dpc-textarea\" data-short=\"true\" value={editor.args} onChange={(event) => setEditor({ ...editor, args: event.target.value })} />\n </label>\n <label className=\"dpc-label\">\n <span>{t('envPairs')}</span>\n <textarea className=\"dpc-textarea\" data-short=\"true\" value={editor.env} onChange={(event) => setEditor({ ...editor, env: event.target.value })} />\n </label>\n </>\n ) : (\n <>\n <label className=\"dpc-label\">\n <span>{t('url')}</span>\n <input className=\"dpc-input\" value={editor.url} onChange={(event) => setEditor({ ...editor, url: event.target.value })} />\n </label>\n <label className=\"dpc-label\">\n <span>{t('headersPairs')}</span>\n <textarea className=\"dpc-textarea\" data-short=\"true\" value={editor.headers} onChange={(event) => setEditor({ ...editor, headers: event.target.value })} />\n </label>\n </>\n )}\n {formError !== null && <p className=\"dpc-formError\">{formError}</p>}\n <div className=\"dpc-cardRow\">\n <span className=\"dpc-spacer\" />\n <Button variant=\"ghost\" onClick={() => setEditor(null)}>{t('cancel')}</Button>\n <Button variant=\"primary\" disabled={busy} onClick={() => void doSave()}>{t('save')}</Button>\n </div>\n </div>\n )}\n </Modal>\n\n <Modal\n open={confirmId !== null}\n onClose={() => setConfirmId(null)}\n title={t('confirmRemove')}\n description={confirmId ?? undefined}\n footer={\n <>\n <Button variant=\"ghost\" onClick={() => setConfirmId(null)}>{t('cancel')}</Button>\n <Button variant=\"primary\" disabled={busy} onClick={() => void doRemove()}>{t('delete')}</Button>\n </>\n }\n >\n <p>{t('removeWarn')}</p>\n </Modal>\n\n <Modal\n open={restartConfirm}\n onClose={() => setRestartConfirm(false)}\n title={t('restartConfirmTitle')}\n footer={\n <>\n <Button variant=\"ghost\" onClick={() => setRestartConfirm(false)}>{t('cancel')}</Button>\n <Button variant=\"primary\" onClick={doRestart}>{t('restartNow')}</Button>\n </>\n }\n >\n <p>{t('restartConfirmBody')}</p>\n </Modal>\n\n <Modal\n open={importOpen}\n onClose={() => setImportOpen(false)}\n title={t('importServers')}\n className=\"dpc-modalWide\"\n >\n <div className=\"dpc-form\">\n <p className=\"dpc-intro\" style={{ margin: 0 }}>{t('importIntro')}</p>\n {importItems === null && <p className=\"dpc-empty\">{t('loading')}</p>}\n {importItems !== null && importItems.length === 0 && <p className=\"dpc-empty\">{t('importEmpty')}</p>}\n {importItems !== null && importItems.length > 0 && (\n <div className=\"dpc-importScroll\">\n {importGroups(importItems).map(group => {\n const selectable = group.items\n .filter(({ item }) => !item.existing)\n .map(({ index }) => index)\n const allChecked = selectable.length > 0\n && selectable.every(index => importItems[index].checked)\n return (\n <section className=\"dpc-importGroup\" key={group.agent}>\n <div className=\"dpc-importHead\">\n <span className=\"dpc-tag\" data-kind=\"source\">{group.label}</span>\n <span className=\"dpc-importCount\">{group.items.length}</span>\n {selectable.length > 0 && (\n <label className=\"dpc-importAll\">\n <input\n type=\"checkbox\"\n checked={allChecked}\n onChange={(event) => {\n const next = importItems.slice()\n for (const index of selectable) next[index] = { ...next[index], checked: event.target.checked }\n setImportItems(next)\n }}\n />\n {t('importSelectAll')}\n </label>\n )}\n </div>\n <ul className=\"dpc-cards\" style={{ gridTemplateColumns: 'minmax(0, 1fr)' }}>\n {group.items.map(({ item, index }) => {\n const command = item.server.transport === 'stdio'\n ? `${item.server.command ?? ''} ${(item.server.args ?? []).join(' ')}`.trim()\n : item.server.url ?? ''\n return (\n <li className=\"dpc-card\" key={`${item.server.agent}/${item.server.name}`} style={{ opacity: item.existing ? 0.55 : 1 }}>\n <div className=\"dpc-cardTop\">\n <label style={{ display: 'inline-flex', alignItems: 'center', gap: 8, minWidth: 0, cursor: item.existing ? 'default' : 'pointer' }}>\n <input\n type=\"checkbox\"\n checked={item.checked}\n disabled={item.existing}\n onChange={(event) => {\n const next = importItems.slice()\n next[index] = { ...item, checked: event.target.checked }\n setImportItems(next)\n }}\n />\n <strong className=\"dpc-cardTitle\" title={item.server.name}>{item.server.name}</strong>\n </label>\n <span className=\"dpc-tag\">{item.server.transport}</span>\n {item.existing && <span className=\"dpc-tag\">{t('importExisting')}</span>}\n </div>\n <p className=\"dpc-cardDesc\" title={command}>{command}</p>\n </li>\n )\n })}\n </ul>\n </section>\n )\n })}\n </div>\n )}\n {formError !== null && <p className=\"dpc-formError\">{formError}</p>}\n <div className=\"dpc-cardRow\">\n <span className=\"dpc-spacer\" />\n <Button variant=\"ghost\" onClick={() => setImportOpen(false)}>{t('cancel')}</Button>\n <Button\n variant=\"primary\"\n disabled={busy || importItems === null || !importItems.some(item => item.checked && !item.existing)}\n onClick={() => void doImport()}\n >\n {t('importSelected')}\n </Button>\n </div>\n </div>\n </Modal>\n </div>\n )\n}\n", "/** Settings \u2192 Plugins \u201C\u6280\u80FD/Skills\u201D tab: view the discovered catalog, edit the\n * user-owned root. Pure presentation-layer \u2014 data arrives through props. */\n\nimport { useEffect, useState } from 'react'\nimport type { ReactElement } from 'react'\nimport { Button, IconRefreshOutline14, IconSkillOutline16, Modal, StateDot } from '@deepseek-ai/dsh-client-ui-primitives'\nimport { CSS } from './css.ts'\nimport type { Translate } from './index.ts'\n\n/** One skill as the host reports it (plus the editable flag the route adds). */\nexport interface SkillRowView {\n name: string\n description: string\n whenToUse?: string\n invocation: { modelInvocable: boolean; userInvocable: boolean }\n source: string\n provider: string\n editable: boolean\n}\n\nexport interface SkillsInjected {\n list(): Promise<{ skills: SkillRowView[] }>\n get(name: string): Promise<{ content: string }>\n save(input: { name: string; description: string; whenToUse?: string; modelInvocable: boolean; userInvocable: boolean; content: string }): Promise<{ ok: boolean }>\n remove(name: string): Promise<{ ok: boolean }>\n}\n\n/** Editor dialog state; null when closed. */\ninterface EditorState {\n mode: 'create' | 'edit' | 'view'\n name: string\n description: string\n whenToUse: string\n modelInvocable: boolean\n userInvocable: boolean\n content: string\n}\n\nconst SOURCE_KEYS: Record<string, string> = {\n 'project-dsh': 'sourceProjectDsh',\n 'project-agents': 'sourceProjectAgents',\n 'user-dsh': 'sourceUserDsh',\n 'user-agents': 'sourceUserAgents',\n runtime: 'sourceRuntime',\n bundled: 'sourceBundled',\n custom: 'sourceCustom',\n}\n\nexport function SkillsTab(props: { t: Translate; injected: SkillsInjected }): ReactElement {\n const { t, injected } = props\n const [skills, setSkills] = useState<SkillRowView[] | null>(null)\n const [editor, setEditor] = useState<EditorState | null>(null)\n const [confirmName, setConfirmName] = useState<string | null>(null)\n const [busy, setBusy] = useState(false)\n const [outcome, setOutcome] = useState<{ ok: boolean; text: string } | null>(null)\n const [formError, setFormError] = useState<string | null>(null)\n const [reload, setReload] = useState(0)\n\n useEffect(() => {\n let current = true\n void injected.list().then(\n (body) => { if (current) setSkills(body.skills) },\n (error: Error) => { if (current) { setSkills([]); setOutcome({ ok: false, text: `${t('failed')}: ${String(error.message ?? error)}` }) } },\n )\n return () => { current = false }\n }, [injected, reload, t])\n\n const openCreate = (): void => {\n setFormError(null)\n setEditor({ mode: 'create', name: '', description: '', whenToUse: '', modelInvocable: true, userInvocable: true, content: '' })\n }\n\n const openExisting = async (skill: SkillRowView): Promise<void> => {\n setBusy(true)\n setFormError(null)\n try {\n const body = await injected.get(skill.name)\n setEditor({\n mode: skill.editable ? 'edit' : 'view',\n name: skill.name,\n description: skill.description,\n whenToUse: skill.whenToUse ?? '',\n modelInvocable: skill.invocation.modelInvocable,\n userInvocable: skill.invocation.userInvocable,\n content: body.content,\n })\n } catch (error) {\n setOutcome({ ok: false, text: `${t('failed')}: ${String(error instanceof Error ? error.message : error)}` })\n } finally {\n setBusy(false)\n }\n }\n\n /**\n * After a write/delete, the watched directory takes a moment to invalidate\n * the catalog \u2014 poll until the change is visible (bounded), then stop.\n */\n const refreshUntil = (predicate: (skills: SkillRowView[]) => boolean, timeoutMs = 10_000): void => {\n const deadline = Date.now() + timeoutMs\n const tick = (): void => {\n if (Date.now() > deadline) return\n setTimeout(() => {\n void injected.list().then(\n (body) => {\n setSkills(body.skills)\n if (!predicate(body.skills)) tick()\n },\n () => tick(),\n )\n }, 1000)\n }\n tick()\n }\n\n const doSave = async (): Promise<void> => {\n if (editor === null) return\n setBusy(true)\n setFormError(null)\n try {\n const name = editor.name.trim()\n await injected.save({\n name,\n description: editor.description,\n whenToUse: editor.whenToUse.trim() === '' ? undefined : editor.whenToUse,\n modelInvocable: editor.modelInvocable,\n userInvocable: editor.userInvocable,\n content: editor.content,\n })\n setOutcome({ ok: true, text: t('saved') })\n setEditor(null)\n refreshUntil(skills => skills.some(skill => skill.name === name))\n } catch (error) {\n setFormError(String(error instanceof Error ? error.message : error))\n } finally {\n setBusy(false)\n }\n }\n\n const doDelete = async (): Promise<void> => {\n if (confirmName === null) return\n const name = confirmName\n setBusy(true)\n try {\n await injected.remove(name)\n setOutcome({ ok: true, text: t('saved') })\n refreshUntil(skills => !skills.some(skill => skill.name === name))\n } catch (error) {\n setOutcome({ ok: false, text: `${t('failed')}: ${String(error instanceof Error ? error.message : error)}` })\n } finally {\n setBusy(false)\n setConfirmName(null)\n }\n }\n\n const readOnly = editor?.mode === 'view'\n\n return (\n <div className=\"dpc-section\">\n <style>{CSS}</style>\n\n <div className=\"dpc-head\">\n <IconSkillOutline16 aria-hidden=\"true\" />\n <h3>{t('skillsTitle')}</h3>\n <span className=\"dpc-spacer\" />\n <Button variant=\"primary\" size=\"sm\" onClick={openCreate}>{t('newSkill')}</Button>\n </div>\n <p className=\"dpc-intro\">{t('skillsIntro')}</p>\n\n {outcome !== null && (\n <div className=\"dpc-banner\" data-kind={outcome.ok ? 'ok' : 'error'} role=\"status\">\n <StateDot state={outcome.ok ? 'done' : 'error'} size={10} />\n <div className=\"dpc-bannerBody\"><span>{outcome.text}</span></div>\n </div>\n )}\n\n <div className=\"dpc-listHead\">\n <h3>{t('skillsTab')}</h3>\n {skills !== null && <span className=\"dpc-count\">{skills.length}</span>}\n <span className=\"dpc-spacer\" />\n <button type=\"button\" className=\"dpc-refresh\" aria-label={t('view')} title={t('view')} disabled={busy} onClick={() => setReload((value) => value + 1)}>\n <IconRefreshOutline14 size={14} aria-hidden=\"true\" />\n </button>\n </div>\n\n {skills === null && <p className=\"dpc-empty\">{t('loading')}</p>}\n {skills !== null && skills.length === 0 && <p className=\"dpc-empty\">{t('emptySkills')}</p>}\n {skills !== null && skills.length > 0 && (\n <ul className=\"dpc-cards\">\n {skills.map((skill) => (\n <li className=\"dpc-card\" key={`${skill.source}/${skill.name}`}>\n <div className=\"dpc-cardTop\">\n <strong className=\"dpc-cardTitle\" title={skill.name}>{skill.name}</strong>\n <span className=\"dpc-tag\" data-kind=\"source\">{t(SOURCE_KEYS[skill.source] ?? 'sourceCustom')}</span>\n </div>\n <p className=\"dpc-cardDesc\" title={skill.description}>{skill.description}</p>\n <div className=\"dpc-cardRow\">\n {!skill.invocation.modelInvocable && <span className=\"dpc-tag\">\u2699</span>}\n {!skill.invocation.userInvocable && <span className=\"dpc-tag\">/</span>}\n <span className=\"dpc-spacer\" />\n <Button variant=\"ghost\" size=\"sm\" disabled={busy} onClick={() => void openExisting(skill)}>\n {skill.editable ? t('edit') : t('view')}\n </Button>\n {skill.editable && (\n <Button variant=\"ghost\" size=\"sm\" disabled={busy} onClick={() => setConfirmName(skill.name)}>{t('delete')}</Button>\n )}\n </div>\n </li>\n ))}\n </ul>\n )}\n\n <Modal\n open={editor !== null}\n onClose={() => setEditor(null)}\n title={editor === null ? '' : editor.mode === 'create' ? t('newSkill') : editor.mode === 'edit' ? t('editSkill') : t('viewSkill')}\n className=\"dpc-modalForm\"\n contentClassName=\"dpc-modalScroll\"\n >\n {editor !== null && (\n <div className=\"dpc-form\">\n <label className=\"dpc-label\">\n <span>{t('skillName')}</span>\n <input\n className=\"dpc-input\"\n value={editor.name}\n disabled={readOnly || editor.mode === 'edit'}\n onChange={(event) => setEditor({ ...editor, name: event.target.value })}\n />\n </label>\n <label className=\"dpc-label\">\n <span>{t('skillDescription')}</span>\n <input\n className=\"dpc-input\"\n value={editor.description}\n disabled={readOnly}\n onChange={(event) => setEditor({ ...editor, description: event.target.value })}\n />\n </label>\n <label className=\"dpc-label\">\n <span>{t('skillWhenToUse')}</span>\n <input\n className=\"dpc-input\"\n value={editor.whenToUse}\n disabled={readOnly}\n onChange={(event) => setEditor({ ...editor, whenToUse: event.target.value })}\n />\n </label>\n <div className=\"dpc-checks\">\n <label>\n <input\n type=\"checkbox\"\n checked={editor.modelInvocable}\n disabled={readOnly}\n onChange={(event) => setEditor({ ...editor, modelInvocable: event.target.checked })}\n />\n {t('modelInvocable')}\n </label>\n <label>\n <input\n type=\"checkbox\"\n checked={editor.userInvocable}\n disabled={readOnly}\n onChange={(event) => setEditor({ ...editor, userInvocable: event.target.checked })}\n />\n {t('userInvocable')}\n </label>\n </div>\n <label className=\"dpc-label\">\n <span>{t('skillContent')}</span>\n <textarea\n className=\"dpc-textarea\"\n value={editor.content}\n readOnly={readOnly}\n onChange={(event) => setEditor({ ...editor, content: event.target.value })}\n />\n </label>\n {formError !== null && <p className=\"dpc-formError\">{formError}</p>}\n <div className=\"dpc-cardRow\">\n <span className=\"dpc-spacer\" />\n <Button variant=\"ghost\" onClick={() => setEditor(null)}>{readOnly ? t('close') : t('cancel')}</Button>\n {!readOnly && <Button variant=\"primary\" disabled={busy} onClick={() => void doSave()}>{t('save')}</Button>}\n </div>\n </div>\n )}\n </Modal>\n\n <Modal\n open={confirmName !== null}\n onClose={() => setConfirmName(null)}\n title={t('confirmDelete')}\n description={confirmName ?? undefined}\n footer={\n <>\n <Button variant=\"ghost\" onClick={() => setConfirmName(null)}>{t('cancel')}</Button>\n <Button variant=\"primary\" disabled={busy} onClick={() => void doDelete()}>{t('delete')}</Button>\n </>\n }\n >\n <p>{t('deleteWarn')}</p>\n </Modal>\n </div>\n )\n}\n", "/** zh/en dictionaries for the Settings capabilities tabs. */\n\nexport const zh = {\n sectionNav: '\u6280\u80FD\u4E0E MCP',\n skillsTab: '\u6280\u80FD',\n mcpTab: 'MCP',\n skillsTitle: '\u6280\u80FD\u7BA1\u7406',\n skillsIntro: '\u67E5\u770B\u4E0E\u7F16\u8F91 dsh \u53D1\u73B0\u7684\u6280\u80FD\uFF1B\u7528\u6237\u7EA7\u6280\u80FD\uFF08DSH_HOME/skills\uFF09\u53EF\u5728\u6B64\u65B0\u5EFA\u3001\u4FEE\u6539\u3001\u5220\u9664\uFF0C\u6587\u4EF6\u88AB\u76D1\u542C\uFF0C\u4FDD\u5B58\u5373\u751F\u6548\u3002\u82E5\u5B58\u5728 ~/.claude/skills \u6216 ~/.codex/skills\uFF0C\u4F1A\u81EA\u52A8\u7EB3\u5165\u626B\u63CF\uFF08\u96F6\u62F7\u8D1D\u3001\u5B9E\u65F6\u540C\u6B65\uFF09\u3002',\n newSkill: '\u65B0\u5EFA\u6280\u80FD',\n editSkill: '\u7F16\u8F91\u6280\u80FD',\n viewSkill: '\u67E5\u770B\u6280\u80FD',\n skillName: '\u540D\u79F0\uFF08kebab-case\uFF09',\n skillDescription: '\u63CF\u8FF0',\n skillWhenToUse: '\u4F7F\u7528\u65F6\u673A\uFF08\u53EF\u9009\uFF09',\n skillContent: '\u6B63\u6587\uFF08Markdown\uFF09',\n modelInvocable: '\u5141\u8BB8\u6A21\u578B\u8C03\u7528',\n userInvocable: '\u5141\u8BB8\u7528\u6237 / \u8C03\u7528',\n save: '\u4FDD\u5B58',\n cancel: '\u53D6\u6D88',\n delete: '\u5220\u9664',\n edit: '\u7F16\u8F91',\n view: '\u67E5\u770B',\n close: '\u5173\u95ED',\n emptySkills: '\u8FD8\u6CA1\u6709\u53D1\u73B0\u4EFB\u4F55\u6280\u80FD',\n loading: '\u8F7D\u5165\u4E2D\u2026',\n source: '\u6765\u6E90',\n sourceProjectDsh: '\u9879\u76EE',\n sourceProjectAgents: '\u9879\u76EE',\n sourceUserDsh: '\u7528\u6237',\n sourceUserAgents: '\u7528\u6237',\n sourceRuntime: '\u8FD0\u884C\u65F6',\n sourceBundled: '\u5185\u7F6E',\n sourceCustom: '\u81EA\u5B9A\u4E49',\n readOnly: '\u53EA\u8BFB',\n confirmDelete: '\u786E\u8BA4\u5220\u9664\u6280\u80FD\uFF1F',\n deleteWarn: '\u5C06\u5220\u9664 DSH_HOME/skills \u4E0B\u7684\u6574\u4E2A\u6280\u80FD\u76EE\u5F55\uFF0C\u6B64\u64CD\u4F5C\u4E0D\u53EF\u64A4\u9500\u3002',\n saved: '\u5DF2\u4FDD\u5B58\uFF0C\u6280\u80FD\u76EE\u5F55\u88AB\u76D1\u542C\uFF0C\u7A0D\u5019\u5373\u53EF\u5728\u4F1A\u8BDD\u4E2D\u4F7F\u7528\u3002',\n mcpTitle: 'MCP \u670D\u52A1\u5668',\n mcpIntro: '\u7BA1\u7406 profile \u4E2D\u7684 MCP \u670D\u52A1\u5668\u884C\uFF08@deepseek-ai/dsh-mcp-client\uFF09\u3002\u65B0\u589E\u3001\u4FEE\u6539\u6216\u5220\u9664\u540E\u9700\u8981\u91CD\u542F dsh \u624D\u751F\u6548\u3002',\n addServer: '\u6DFB\u52A0\u670D\u52A1\u5668',\n editServer: '\u7F16\u8F91\u670D\u52A1\u5668',\n serverName: '\u670D\u52A1\u5668\u540D\uFF08\u5DE5\u5177\u540D\u524D\u7F00\uFF09',\n transport: '\u4F20\u8F93\u65B9\u5F0F',\n transportStdio: 'stdio\uFF08\u672C\u5730\u547D\u4EE4\uFF09',\n transportHttp: 'streamable-http\uFF08URL\uFF09',\n command: '\u547D\u4EE4',\n args: '\u53C2\u6570\uFF08\u6BCF\u884C\u4E00\u4E2A\uFF09',\n envPairs: '\u73AF\u5883\u53D8\u91CF\uFF08\u6BCF\u884C KEY=VALUE\uFF09',\n url: 'URL',\n headersPairs: '\u8BF7\u6C42\u5934\uFF08\u6BCF\u884C KEY: VALUE\uFF09',\n disabled: '\u5DF2\u505C\u7528',\n enabled: '\u542F\u7528\u4E2D',\n toggle: '\u505C\u7528/\u542F\u7528',\n confirmRemove: '\u786E\u8BA4\u79FB\u9664\u8BE5\u670D\u52A1\u5668\uFF1F',\n removeWarn: '\u5C06\u4ECE profile \u914D\u7F6E\u4E2D\u79FB\u9664\u8FD9\u4E00\u884C\uFF0C\u91CD\u542F dsh \u540E\u5176\u5DE5\u5177\u4E0D\u518D\u51FA\u73B0\u3002',\n emptyMcp: '\u8FD8\u6CA1\u6709\u914D\u7F6E MCP \u670D\u52A1\u5668',\n importServers: '\u4ECE\u5176\u4ED6 Agent \u5BFC\u5165',\n importIntro: '\u626B\u63CF Claude Code\uFF08~/.claude.json\uFF09\u4E0E Codex\uFF08~/.codex/config.toml\uFF09\u7684 MCP \u670D\u52A1\u5668\u914D\u7F6E\uFF0C\u52FE\u9009\u540E\u5BFC\u5165\u4E3A\u672C profile \u7684\u670D\u52A1\u5668\u884C\u3002',\n importEmpty: '\u6CA1\u6709\u53D1\u73B0\u53EF\u5BFC\u5165\u7684 MCP \u670D\u52A1\u5668',\n importSelectAll: '\u5168\u9009',\n importExisting: '\u5DF2\u5B58\u5728',\n importSelected: '\u5BFC\u5165\u9009\u4E2D\u9879',\n restartNeeded: '\u914D\u7F6E\u5DF2\u5199\u5165\uFF0C\u91CD\u542F dsh \u540E\u751F\u6548\u3002',\n restartDesktopHint: '\u91CD\u542F\u7531\u684C\u9762\u5E94\u7528\u8D1F\u8D23\uFF1A\u6258\u76D8\u83DC\u5355\u300C\u91CD\u542F\u670D\u52A1\u300D\u3002',\n restartOtherHint: '\u91CD\u542F\u65B9\u5F0F\uFF1A\u5173\u95ED\u5F53\u524D dsh \u8FDB\u7A0B\u540E\u91CD\u65B0\u8FD0\u884C\u3002',\n restartNow: '\u91CD\u542F\u670D\u52A1',\n restart: '\u91CD\u542F',\n restartConfirmTitle: '\u91CD\u542F dsh\uFF1F',\n restartConfirmBody: '\u91CD\u542F\u4F1A\u4E2D\u65AD\u6B63\u5728\u8FDB\u884C\u7684\u56DE\u5408\uFF0C\u672A\u4FDD\u5B58\u7684\u8F93\u5165\u53EF\u80FD\u4E22\u5931\uFF1BMCP \u884C\u7684\u53D8\u66F4\u5C06\u5728\u91CD\u542F\u540E\u751F\u6548\u3002',\n restarting: '\u6B63\u5728\u91CD\u542F\uFF0C\u6062\u590D\u540E\u5C06\u81EA\u52A8\u5237\u65B0\u9875\u9762\u2026',\n restartPortHint: '\u82E5\u91CD\u542F\u540E\u9875\u9762\u957F\u65F6\u95F4\u672A\u6062\u590D\uFF0C\u53EF\u80FD\u662F\u7AEF\u53E3\u5DF2\u53D8\u5316\uFF1A\u5728\u7EC8\u7AEF\u67E5\u770B\u65B0\u5730\u5740\u540E\u6253\u5F00\u3002',\n failed: '\u64CD\u4F5C\u5931\u8D25',\n}\n\nexport const en = {\n sectionNav: 'Skills & MCP',\n skillsTab: 'Skills',\n mcpTab: 'MCP',\n skillsTitle: 'Skills',\n skillsIntro: 'View and edit the skills dsh discovers; user-level skills (DSH_HOME/skills) can be created, edited, and deleted here \u2014 the directory is watched, saves apply without a restart. ~/.claude/skills and ~/.codex/skills are scanned too when present (zero-copy, live-synced).',\n newSkill: 'New skill',\n editSkill: 'Edit skill',\n viewSkill: 'View skill',\n skillName: 'Name (kebab-case)',\n skillDescription: 'Description',\n skillWhenToUse: 'When to use (optional)',\n skillContent: 'Body (Markdown)',\n modelInvocable: 'Model-invocable',\n userInvocable: 'User-invocable (/)',\n save: 'Save',\n cancel: 'Cancel',\n delete: 'Delete',\n edit: 'Edit',\n view: 'View',\n close: 'Close',\n emptySkills: 'No skills discovered yet',\n loading: 'Loading\u2026',\n source: 'Source',\n sourceProjectDsh: 'project',\n sourceProjectAgents: 'project',\n sourceUserDsh: 'user',\n sourceUserAgents: 'user',\n sourceRuntime: 'runtime',\n sourceBundled: 'bundled',\n sourceCustom: 'custom',\n readOnly: 'read-only',\n confirmDelete: 'Delete this skill?',\n deleteWarn: 'Removes the whole skill directory under DSH_HOME/skills. This cannot be undone.',\n saved: 'Saved. The skills directory is watched; the skill is usable in sessions shortly.',\n mcpTitle: 'MCP servers',\n mcpIntro: 'Manage the MCP server rows (@deepseek-ai/dsh-mcp-client) in this profile. Additions, edits, and removals take effect after a dsh restart.',\n addServer: 'Add server',\n editServer: 'Edit server',\n serverName: 'Server name (tool name prefix)',\n transport: 'Transport',\n transportStdio: 'stdio (local command)',\n transportHttp: 'streamable-http (URL)',\n command: 'Command',\n args: 'Arguments (one per line)',\n envPairs: 'Environment (KEY=VALUE per line)',\n url: 'URL',\n headersPairs: 'Headers (KEY: VALUE per line)',\n disabled: 'Disabled',\n enabled: 'Enabled',\n toggle: 'Enable/disable',\n confirmRemove: 'Remove this server?',\n removeWarn: 'Removes the row from the profile configuration; its tools disappear after the next dsh restart.',\n emptyMcp: 'No MCP servers configured yet',\n importServers: 'Import from other agents',\n importIntro: 'Scans Claude Code (~/.claude.json) and Codex (~/.codex/config.toml) MCP server configs; selected entries become server rows in this profile.',\n importEmpty: 'No importable MCP servers found',\n importSelectAll: 'Select all',\n importExisting: 'already here',\n importSelected: 'Import selected',\n restartNeeded: 'Configuration written \u2014 restart dsh to apply.',\n restartDesktopHint: 'The desktop app owns restarts: use the tray \u201CRestart service\u201D.',\n restartOtherHint: 'Restart by closing this dsh process and running it again.',\n restartNow: 'Restart service',\n restart: 'Restart',\n restartConfirmTitle: 'Restart dsh?',\n restartConfirmBody: 'Restarting interrupts any running turn and may lose unsaved input; pending MCP changes apply after the restart.',\n restarting: 'Restarting \u2014 the page will reload once the host is back\u2026',\n restartPortHint: 'If the page does not recover, the port may have changed: check the terminal for the new URL.',\n failed: 'Operation failed',\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,IAAAA,gBAAmC;;;ACEnC,IAAAC,gBAAmD;;;ACF5C,IAAM,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAnB,mBAAoC;AAEpC,sCAAgF;AAkP1E;AArMC,SAAS,aAAa,OAAiH;AAC5I,QAAM,QAAQ,CAAC,UAAkB,UAAU,gBAAgB,gBAAgB,UAAU,UAAU,UAAU;AACzG,QAAM,QAAQ,CAAC,eAAe,OAAO;AACrC,QAAM,SAAS,CAAC,GAAG,IAAI,IAAI,MAAM,IAAI,UAAQ,KAAK,OAAO,KAAK,CAAC,CAAC,EAC7D,KAAK,CAAC,GAAG,MAAM;AACd,UAAM,OAAO,CAAC,UAAkB;AAAE,YAAM,KAAK,MAAM,QAAQ,KAAK;AAAG,aAAO,OAAO,KAAK,MAAM,SAAS;AAAA,IAAG;AACxG,WAAO,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,EAAE,cAAc,CAAC;AAAA,EAC/C,CAAC;AACH,SAAO,OAAO,IAAI,YAAU;AAAA,IAC1B;AAAA,IACA,OAAO,MAAM,KAAK;AAAA,IAClB,OAAO,MAAM,IAAI,CAAC,MAAM,WAAW,EAAE,MAAM,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,KAAK,MAAM,KAAK,OAAO,UAAU,KAAK;AAAA,EACvG,EAAE;AACJ;AAGA,SAAS,WAAW,MAAc,WAA8C;AAC9E,QAAM,MAA8B,CAAC;AACrC,aAAW,QAAQ,KAAK,MAAM,OAAO,GAAG;AACtC,UAAM,UAAU,KAAK,KAAK;AAC1B,QAAI,YAAY,MAAM,QAAQ,WAAW,GAAG,EAAG;AAC/C,UAAM,KAAK,QAAQ,QAAQ,SAAS;AACpC,QAAI,MAAM,EAAG;AACb,QAAI,QAAQ,MAAM,GAAG,EAAE,EAAE,KAAK,CAAC,IAAI,QAAQ,MAAM,KAAK,CAAC,EAAE,KAAK;AAAA,EAChE;AACA,SAAO;AACT;AAEA,SAAS,WAAW,KAAyC,WAA2B;AACtF,MAAI,QAAQ,OAAW,QAAO;AAC9B,SAAO,OAAO,QAAQ,GAAG,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,GAAG,GAAG,GAAG,SAAS,GAAG,MAAM,SAAS,IAAI,IAAI,KAAK,UAAU,KAAK,IAAI,KAAK,EAAE,EAAE,KAAK,IAAI;AACzI;AAEO,SAAS,OAAO,OAA8D;AACnF,QAAM,EAAE,GAAG,SAAS,IAAI;AACxB,QAAM,CAAC,SAAS,UAAU,QAAI,uBAA0B,IAAI;AAC5D,QAAM,CAAC,QAAQ,SAAS,QAAI,uBAA6B,IAAI;AAC7D,QAAM,CAAC,WAAW,YAAY,QAAI,uBAAwB,IAAI;AAC9D,QAAM,CAAC,YAAY,aAAa,QAAI,uBAAS,KAAK;AAClD,QAAM,CAAC,aAAa,cAAc,QAAI,uBAA4F,IAAI;AACtI,QAAM,CAAC,MAAM,OAAO,QAAI,uBAAS,KAAK;AACtC,QAAM,CAAC,SAAS,UAAU,QAAI,uBAAS,KAAK;AAC5C,QAAM,CAAC,gBAAgB,iBAAiB,QAAI,uBAAS,KAAK;AAC1D,QAAM,CAAC,YAAY,aAAa,QAAI,uBAAS,KAAK;AAClD,QAAM,CAAC,SAAS,UAAU,QAAI,uBAA+C,IAAI;AACjF,QAAM,CAAC,WAAW,YAAY,QAAI,uBAAwB,IAAI;AAC9D,QAAM,CAAC,QAAQ,SAAS,QAAI,uBAAS,CAAC;AAEtC,QAAM,aAAa,YAA2B;AAC5C,kBAAc,IAAI;AAClB,mBAAe,IAAI;AACnB,QAAI;AACF,YAAM,OAAO,MAAM,SAAS,WAAW;AACvC,YAAM,WAAW,IAAI,IAAI,KAAK,QAAQ;AACtC,qBAAe,KAAK,QAAQ,IAAI,aAAW;AAAA,QACzC;AAAA,QACA,UAAU,SAAS,IAAI,OAAO,IAAI;AAAA,QAClC,SAAS,CAAC,SAAS,IAAI,OAAO,IAAI;AAAA,MACpC,EAAE,CAAC;AAAA,IACL,SAAS,OAAO;AACd,qBAAe,CAAC,CAAC;AACjB,iBAAW,EAAE,IAAI,OAAO,MAAM,GAAG,EAAE,QAAQ,CAAC,KAAK,OAAO,iBAAiB,QAAQ,MAAM,UAAU,KAAK,CAAC,GAAG,CAAC;AAAA,IAC7G;AAAA,EACF;AAEA,QAAM,WAAW,YAA2B;AAC1C,QAAI,gBAAgB,KAAM;AAC1B,UAAM,QAAQ,YAAY,OAAO,UAAQ,KAAK,WAAW,CAAC,KAAK,QAAQ,EAAE,IAAI,WAAS,EAAE,OAAO,KAAK,OAAO,OAAO,MAAM,KAAK,OAAO,KAAK,EAAE;AAC3I,YAAQ,IAAI;AACZ,QAAI;AACF,YAAM,OAAO,MAAM,SAAS,YAAY,KAAK;AAC7C,YAAM,SAAS,KAAK,QAAQ,OAAO,UAAQ,CAAC,KAAK,EAAE;AACnD,iBAAW,OAAO,WAAW,IACzB,EAAE,IAAI,MAAM,MAAM,EAAE,eAAe,EAAE,IACrC,EAAE,IAAI,OAAO,MAAM,GAAG,EAAE,QAAQ,CAAC,KAAK,OAAO,IAAI,UAAQ,GAAG,KAAK,IAAI,KAAK,KAAK,KAAK,GAAG,EAAE,KAAK,IAAI,CAAC,GAAG,CAAC;AAC3G,oBAAc,KAAK;AACnB,iBAAW,IAAI;AACf,gBAAU,CAAC,UAAU,QAAQ,CAAC;AAAA,IAChC,SAAS,OAAO;AACd,iBAAW,EAAE,IAAI,OAAO,MAAM,GAAG,EAAE,QAAQ,CAAC,KAAK,OAAO,iBAAiB,QAAQ,MAAM,UAAU,KAAK,CAAC,GAAG,CAAC;AAAA,IAC7G,UAAE;AACA,cAAQ,KAAK;AAAA,IACf;AAAA,EACF;AAEA,QAAM,aAAa,CAAC,gBAA+B;AACjD,cAAU,CAAC,UAAU,QAAQ,CAAC;AAC9B,QAAI,YAAa,YAAW,IAAI;AAAA,EAClC;AAEA,8BAAU,MAAM;AACd,QAAI,UAAU;AACd,SAAK,SAAS,KAAK,EAAE;AAAA,MACnB,CAAC,SAAS;AAAE,YAAI,QAAS,YAAW,KAAK,OAAO;AAAA,MAAE;AAAA,MAClD,CAAC,UAAiB;AAAE,YAAI,SAAS;AAAE,qBAAW,CAAC,CAAC;AAAG,qBAAW,EAAE,IAAI,OAAO,MAAM,GAAG,EAAE,QAAQ,CAAC,KAAK,OAAO,MAAM,WAAW,KAAK,CAAC,GAAG,CAAC;AAAA,QAAE;AAAA,MAAE;AAAA,IAC5I;AACA,WAAO,MAAM;AAAE,gBAAU;AAAA,IAAM;AAAA,EACjC,GAAG,CAAC,UAAU,QAAQ,CAAC,CAAC;AAExB,QAAM,aAAa,MAAY;AAC7B,iBAAa,IAAI;AACjB,cAAU,EAAE,IAAI,IAAI,YAAY,IAAI,WAAW,SAAS,SAAS,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,IAAI,SAAS,GAAG,CAAC;AAAA,EAChH;AAEA,QAAM,WAAW,CAAC,QAAsB;AACtC,iBAAa,IAAI;AACjB,cAAU;AAAA,MACR,IAAI,IAAI;AAAA,MACR,YAAY,IAAI;AAAA,MAChB,WAAW,IAAI;AAAA,MACf,SAAS,IAAI,WAAW;AAAA,MACxB,OAAO,IAAI,QAAQ,CAAC,GAAG,KAAK,IAAI;AAAA,MAChC,KAAK,WAAW,IAAI,KAAK,GAAG;AAAA,MAC5B,KAAK,IAAI,OAAO;AAAA,MAChB,SAAS,WAAW,IAAI,SAAS,GAAG;AAAA,IACtC,CAAC;AAAA,EACH;AAEA,QAAM,SAAS,YAA2B;AACxC,QAAI,WAAW,KAAM;AACrB,YAAQ,IAAI;AACZ,iBAAa,IAAI;AACjB,QAAI;AACF,YAAM,SAAS,KAAK;AAAA,QAClB,IAAI,OAAO;AAAA,QACX,YAAY,OAAO,WAAW,KAAK;AAAA,QACnC,WAAW,OAAO;AAAA,QAClB,GAAI,OAAO,cAAc,UACrB;AAAA,UACE,SAAS,OAAO,QAAQ,KAAK;AAAA,UAC7B,MAAM,OAAO,KAAK,MAAM,OAAO,EAAE,IAAI,UAAQ,KAAK,KAAK,CAAC,EAAE,OAAO,UAAQ,SAAS,EAAE;AAAA,UACpF,KAAK,WAAW,OAAO,KAAK,GAAG;AAAA,QACjC,IACA;AAAA,UACE,KAAK,OAAO,IAAI,KAAK;AAAA,UACrB,SAAS,WAAW,OAAO,SAAS,GAAG;AAAA,QACzC;AAAA,MACN,CAAC;AACD,gBAAU,IAAI;AACd,iBAAW,EAAE,IAAI,MAAM,MAAM,EAAE,eAAe,EAAE,CAAC;AACjD,iBAAW,IAAI;AAAA,IACjB,SAAS,OAAO;AACd,mBAAa,OAAO,iBAAiB,QAAQ,MAAM,UAAU,KAAK,CAAC;AAAA,IACrE,UAAE;AACA,cAAQ,KAAK;AAAA,IACf;AAAA,EACF;AAEA,QAAM,WAAW,OAAO,QAA+B;AACrD,YAAQ,IAAI;AACZ,QAAI;AACF,YAAM,SAAS,OAAO,IAAI,IAAI,CAAC,IAAI,QAAQ;AAC3C,iBAAW,EAAE,IAAI,MAAM,MAAM,EAAE,eAAe,EAAE,CAAC;AACjD,iBAAW,IAAI;AAAA,IACjB,SAAS,OAAO;AACd,iBAAW,EAAE,IAAI,OAAO,MAAM,GAAG,EAAE,QAAQ,CAAC,KAAK,OAAO,iBAAiB,QAAQ,MAAM,UAAU,KAAK,CAAC,GAAG,CAAC;AAAA,IAC7G,UAAE;AACA,cAAQ,KAAK;AAAA,IACf;AAAA,EACF;AAEA,QAAM,WAAW,YAA2B;AAC1C,QAAI,cAAc,KAAM;AACxB,YAAQ,IAAI;AACZ,QAAI;AACF,YAAM,SAAS,OAAO,SAAS;AAC/B,iBAAW,EAAE,IAAI,MAAM,MAAM,EAAE,eAAe,EAAE,CAAC;AACjD,iBAAW,IAAI;AAAA,IACjB,SAAS,OAAO;AACd,iBAAW,EAAE,IAAI,OAAO,MAAM,GAAG,EAAE,QAAQ,CAAC,KAAK,OAAO,iBAAiB,QAAQ,MAAM,UAAU,KAAK,CAAC,GAAG,CAAC;AAAA,IAC7G,UAAE;AACA,cAAQ,KAAK;AACb,mBAAa,IAAI;AAAA,IACnB;AAAA,EACF;AAEA,QAAM,YAAY,MAAY;AAC5B,sBAAkB,KAAK;AACvB,kBAAc,IAAI;AAClB,SAAK,SAAS,QAAQ;AAEtB,QAAI,SAAS,QAAS;AACtB,UAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,UAAM,OAAO,MAAY;AACvB,UAAI,KAAK,IAAI,IAAI,SAAU;AAC3B,aAAO,WAAW,MAAM;AACtB,aAAK,SAAS,KAAK,EAAE;AAAA,UACnB,MAAM;AAAE,mBAAO,SAAS,OAAO;AAAA,UAAE;AAAA,UACjC,MAAM;AAAE,iBAAK;AAAA,UAAE;AAAA,QACjB;AAAA,MACF,GAAG,IAAI;AAAA,IACT;AACA,WAAO,WAAW,MAAM,GAAI;AAAA,EAC9B;AAEA,QAAM,gBACJ,6CAAC,SAAI,WAAU,cAAa,aAAU,QAAO,MAAK,UAChD;AAAA,gDAAC,4CAAS,OAAM,WAAU,MAAM,IAAI;AAAA,IACpC,6CAAC,SAAI,WAAU,kBACb;AAAA,kDAAC,UAAM,uBAAa,EAAE,YAAY,IAAI,EAAE,eAAe,GAAE;AAAA,MACzD,4CAAC,UAAK,WAAU,kBACb,uBACI,CAAC,SAAS,WAAW,EAAE,iBAAiB,IACzC,SAAS,UACP,4EAAG;AAAA,UAAE,oBAAoB;AAAA,QAAG;AAAA,QAAI,4CAAC,0CAAO,SAAQ,WAAU,MAAK,MAAK,SAAS,MAAM,kBAAkB,IAAI,GAAI,YAAE,YAAY,GAAE;AAAA,SAAS,IACtI,EAAE,kBAAkB,GAC5B;AAAA,OACF;AAAA,KACF;AAGF,SACE,6CAAC,SAAI,WAAU,eACb;AAAA,gDAAC,WAAO,eAAI;AAAA,IAEZ,6CAAC,SAAI,WAAU,YACb;AAAA,kDAAC,oDAAiB,eAAY,QAAO;AAAA,MACrC,4CAAC,QAAI,YAAE,UAAU,GAAE;AAAA,MACnB,4CAAC,UAAK,WAAU,cAAa;AAAA,MAC7B,4CAAC,0CAAO,SAAQ,SAAQ,MAAK,MAAK,UAAU,YAAY,SAAS,MAAM,kBAAkB,IAAI,GAAI,YAAE,SAAS,GAAE;AAAA,MAC9G,4CAAC,0CAAO,SAAQ,SAAQ,MAAK,MAAK,SAAS,MAAM,KAAK,WAAW,GAAI,YAAE,eAAe,GAAE;AAAA,MACxF,4CAAC,0CAAO,SAAQ,WAAU,MAAK,MAAK,SAAS,YAAa,YAAE,WAAW,GAAE;AAAA,OAC3E;AAAA,IACA,4CAAC,OAAE,WAAU,aAAa,YAAE,UAAU,GAAE;AAAA,IAEvC,YAAY,QACX,6CAAC,SAAI,WAAU,cAAa,aAAW,QAAQ,KAAK,OAAO,SAAS,MAAK,UACvE;AAAA,kDAAC,4CAAS,OAAO,QAAQ,KAAK,SAAS,SAAS,MAAM,IAAI;AAAA,MAC1D,4CAAC,SAAI,WAAU,kBAAiB,sDAAC,UAAM,kBAAQ,MAAK,GAAO;AAAA,OAC7D;AAAA,KAEA,WAAW,eAAe;AAAA,IAE5B,6CAAC,SAAI,WAAU,gBACb;AAAA,kDAAC,QAAI,YAAE,QAAQ,GAAE;AAAA,MAChB,YAAY,QAAQ,4CAAC,UAAK,WAAU,aAAa,kBAAQ,QAAO;AAAA,MACjE,4CAAC,UAAK,WAAU,cAAa;AAAA,MAC7B,4CAAC,YAAO,MAAK,UAAS,WAAU,eAAc,cAAY,EAAE,MAAM,GAAG,OAAO,EAAE,MAAM,GAAG,UAAU,MAAM,SAAS,MAAM,UAAU,CAAC,UAAU,QAAQ,CAAC,GAClJ,sDAAC,wDAAqB,MAAM,IAAI,eAAY,QAAO,GACrD;AAAA,OACF;AAAA,IAEC,YAAY,QAAQ,4CAAC,OAAE,WAAU,aAAa,YAAE,SAAS,GAAE;AAAA,IAC3D,YAAY,QAAQ,QAAQ,WAAW,KAAK,4CAAC,OAAE,WAAU,aAAa,YAAE,UAAU,GAAE;AAAA,IACpF,YAAY,QAAQ,QAAQ,SAAS,KACpC,4CAAC,QAAG,WAAU,aACX,kBAAQ,IAAI,CAAC,QACZ,6CAAC,QAAG,WAAU,YACZ;AAAA,mDAAC,SAAI,WAAU,eACb;AAAA,oDAAC,YAAO,WAAU,iBAAgB,OAAO,IAAI,IAAK,cAAI,YAAW;AAAA,QACjE,4CAAC,UAAK,WAAU,WAAW,cAAI,WAAU;AAAA,QACzC,4CAAC,UAAK,WAAU,WAAU,aAAW,IAAI,WAAW,QAAQ,QAAY,cAAI,WAAW,EAAE,UAAU,IAAI,EAAE,SAAS,GAAE;AAAA,SACtH;AAAA,MACA,4CAAC,OAAE,WAAU,gBACV,cAAI,cAAc,UAAU,GAAG,IAAI,WAAW,EAAE,KAAK,IAAI,QAAQ,CAAC,GAAG,KAAK,GAAG,CAAC,KAAK,IAAI,OAAO,IACjG;AAAA,MACA,6CAAC,SAAI,WAAU,eACb;AAAA,oDAAC,UAAK,WAAU,cAAa;AAAA,QAC7B,4CAAC,0CAAO,SAAQ,SAAQ,MAAK,MAAK,UAAU,MAAM,SAAS,MAAM,KAAK,SAAS,GAAG,GAAI,YAAE,QAAQ,GAAE;AAAA,QAClG,4CAAC,0CAAO,SAAQ,SAAQ,MAAK,MAAK,UAAU,MAAM,SAAS,MAAM,SAAS,GAAG,GAAI,YAAE,MAAM,GAAE;AAAA,QAC3F,4CAAC,0CAAO,SAAQ,SAAQ,MAAK,MAAK,UAAU,MAAM,SAAS,MAAM,aAAa,IAAI,EAAE,GAAI,YAAE,QAAQ,GAAE;AAAA,SACtG;AAAA,SAd4B,IAAI,EAelC,CACD,GACH;AAAA,IAGF;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,WAAW;AAAA,QACjB,SAAS,MAAM,UAAU,IAAI;AAAA,QAC7B,OAAO,WAAW,QAAQ,OAAO,OAAO,KAAK,EAAE,YAAY,IAAI,EAAE,WAAW;AAAA,QAC5E,WAAU;AAAA,QACV,kBAAiB;AAAA,QAEhB,qBAAW,QACV,6CAAC,SAAI,WAAU,YACb;AAAA,uDAAC,WAAM,WAAU,aACf;AAAA,wDAAC,UAAM,YAAE,YAAY,GAAE;AAAA,YACvB;AAAA,cAAC;AAAA;AAAA,gBACC,WAAU;AAAA,gBACV,OAAO,OAAO;AAAA,gBACd,UAAU,OAAO,OAAO;AAAA,gBACxB,UAAU,CAAC,UAAU,UAAU,EAAE,GAAG,QAAQ,YAAY,MAAM,OAAO,MAAM,CAAC;AAAA;AAAA,YAC9E;AAAA,aACF;AAAA,UACA,6CAAC,WAAM,WAAU,aACf;AAAA,wDAAC,UAAM,YAAE,WAAW,GAAE;AAAA,YACtB;AAAA,cAAC;AAAA;AAAA,gBACC,WAAU;AAAA,gBACV,OAAO,OAAO;AAAA,gBACd,UAAU,OAAO,OAAO;AAAA,gBACxB,UAAU,CAAC,UAAU,UAAU,EAAE,GAAG,QAAQ,WAAW,MAAM,OAAO,MAAkC,CAAC;AAAA,gBAEvG;AAAA,8DAAC,YAAO,OAAM,SAAS,YAAE,gBAAgB,GAAE;AAAA,kBAC3C,4CAAC,YAAO,OAAM,mBAAmB,YAAE,eAAe,GAAE;AAAA;AAAA;AAAA,YACtD;AAAA,aACF;AAAA,UACC,OAAO,cAAc,UACpB,4EACE;AAAA,yDAAC,WAAM,WAAU,aACf;AAAA,0DAAC,UAAM,YAAE,SAAS,GAAE;AAAA,cACpB,4CAAC,WAAM,WAAU,aAAY,OAAO,OAAO,SAAS,UAAU,CAAC,UAAU,UAAU,EAAE,GAAG,QAAQ,SAAS,MAAM,OAAO,MAAM,CAAC,GAAG;AAAA,eAClI;AAAA,YACA,6CAAC,WAAM,WAAU,aACf;AAAA,0DAAC,UAAM,YAAE,MAAM,GAAE;AAAA,cACjB,4CAAC,cAAS,WAAU,gBAAe,cAAW,QAAO,OAAO,OAAO,MAAM,UAAU,CAAC,UAAU,UAAU,EAAE,GAAG,QAAQ,MAAM,MAAM,OAAO,MAAM,CAAC,GAAG;AAAA,eACpJ;AAAA,YACA,6CAAC,WAAM,WAAU,aACf;AAAA,0DAAC,UAAM,YAAE,UAAU,GAAE;AAAA,cACrB,4CAAC,cAAS,WAAU,gBAAe,cAAW,QAAO,OAAO,OAAO,KAAK,UAAU,CAAC,UAAU,UAAU,EAAE,GAAG,QAAQ,KAAK,MAAM,OAAO,MAAM,CAAC,GAAG;AAAA,eAClJ;AAAA,aACF,IAEA,4EACE;AAAA,yDAAC,WAAM,WAAU,aACf;AAAA,0DAAC,UAAM,YAAE,KAAK,GAAE;AAAA,cAChB,4CAAC,WAAM,WAAU,aAAY,OAAO,OAAO,KAAK,UAAU,CAAC,UAAU,UAAU,EAAE,GAAG,QAAQ,KAAK,MAAM,OAAO,MAAM,CAAC,GAAG;AAAA,eAC1H;AAAA,YACA,6CAAC,WAAM,WAAU,aACf;AAAA,0DAAC,UAAM,YAAE,cAAc,GAAE;AAAA,cACzB,4CAAC,cAAS,WAAU,gBAAe,cAAW,QAAO,OAAO,OAAO,SAAS,UAAU,CAAC,UAAU,UAAU,EAAE,GAAG,QAAQ,SAAS,MAAM,OAAO,MAAM,CAAC,GAAG;AAAA,eAC1J;AAAA,aACF;AAAA,UAED,cAAc,QAAQ,4CAAC,OAAE,WAAU,iBAAiB,qBAAU;AAAA,UAC/D,6CAAC,SAAI,WAAU,eACb;AAAA,wDAAC,UAAK,WAAU,cAAa;AAAA,YAC7B,4CAAC,0CAAO,SAAQ,SAAQ,SAAS,MAAM,UAAU,IAAI,GAAI,YAAE,QAAQ,GAAE;AAAA,YACrE,4CAAC,0CAAO,SAAQ,WAAU,UAAU,MAAM,SAAS,MAAM,KAAK,OAAO,GAAI,YAAE,MAAM,GAAE;AAAA,aACrF;AAAA,WACF;AAAA;AAAA,IAEJ;AAAA,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,cAAc;AAAA,QACpB,SAAS,MAAM,aAAa,IAAI;AAAA,QAChC,OAAO,EAAE,eAAe;AAAA,QACxB,aAAa,aAAa;AAAA,QAC1B,QACE,4EACE;AAAA,sDAAC,0CAAO,SAAQ,SAAQ,SAAS,MAAM,aAAa,IAAI,GAAI,YAAE,QAAQ,GAAE;AAAA,UACxE,4CAAC,0CAAO,SAAQ,WAAU,UAAU,MAAM,SAAS,MAAM,KAAK,SAAS,GAAI,YAAE,QAAQ,GAAE;AAAA,WACzF;AAAA,QAGF,sDAAC,OAAG,YAAE,YAAY,GAAE;AAAA;AAAA,IACtB;AAAA,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,MAAM;AAAA,QACN,SAAS,MAAM,kBAAkB,KAAK;AAAA,QACtC,OAAO,EAAE,qBAAqB;AAAA,QAC9B,QACE,4EACE;AAAA,sDAAC,0CAAO,SAAQ,SAAQ,SAAS,MAAM,kBAAkB,KAAK,GAAI,YAAE,QAAQ,GAAE;AAAA,UAC9E,4CAAC,0CAAO,SAAQ,WAAU,SAAS,WAAY,YAAE,YAAY,GAAE;AAAA,WACjE;AAAA,QAGF,sDAAC,OAAG,YAAE,oBAAoB,GAAE;AAAA;AAAA,IAC9B;AAAA,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,MAAM;AAAA,QACN,SAAS,MAAM,cAAc,KAAK;AAAA,QAClC,OAAO,EAAE,eAAe;AAAA,QACxB,WAAU;AAAA,QAEV,uDAAC,SAAI,WAAU,YACb;AAAA,sDAAC,OAAE,WAAU,aAAY,OAAO,EAAE,QAAQ,EAAE,GAAI,YAAE,aAAa,GAAE;AAAA,UAChE,gBAAgB,QAAQ,4CAAC,OAAE,WAAU,aAAa,YAAE,SAAS,GAAE;AAAA,UAC/D,gBAAgB,QAAQ,YAAY,WAAW,KAAK,4CAAC,OAAE,WAAU,aAAa,YAAE,aAAa,GAAE;AAAA,UAC/F,gBAAgB,QAAQ,YAAY,SAAS,KAC5C,4CAAC,SAAI,WAAU,oBACZ,uBAAa,WAAW,EAAE,IAAI,WAAS;AACtC,kBAAM,aAAa,MAAM,MACtB,OAAO,CAAC,EAAE,KAAK,MAAM,CAAC,KAAK,QAAQ,EACnC,IAAI,CAAC,EAAE,MAAM,MAAM,KAAK;AAC3B,kBAAM,aAAa,WAAW,SAAS,KAClC,WAAW,MAAM,WAAS,YAAY,KAAK,EAAE,OAAO;AACzD,mBACE,6CAAC,aAAQ,WAAU,mBACjB;AAAA,2DAAC,SAAI,WAAU,kBACb;AAAA,4DAAC,UAAK,WAAU,WAAU,aAAU,UAAU,gBAAM,OAAM;AAAA,gBAC1D,4CAAC,UAAK,WAAU,mBAAmB,gBAAM,MAAM,QAAO;AAAA,gBACrD,WAAW,SAAS,KACnB,6CAAC,WAAM,WAAU,iBACf;AAAA;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAK;AAAA,sBACL,SAAS;AAAA,sBACT,UAAU,CAAC,UAAU;AACnB,8BAAM,OAAO,YAAY,MAAM;AAC/B,mCAAW,SAAS,WAAY,MAAK,KAAK,IAAI,EAAE,GAAG,KAAK,KAAK,GAAG,SAAS,MAAM,OAAO,QAAQ;AAC9F,uCAAe,IAAI;AAAA,sBACrB;AAAA;AAAA,kBACF;AAAA,kBACC,EAAE,iBAAiB;AAAA,mBACtB;AAAA,iBAEJ;AAAA,cACA,4CAAC,QAAG,WAAU,aAAY,OAAO,EAAE,qBAAqB,iBAAiB,GACtE,gBAAM,MAAM,IAAI,CAAC,EAAE,MAAM,MAAM,MAAM;AACpC,sBAAM,UAAU,KAAK,OAAO,cAAc,UACtC,GAAG,KAAK,OAAO,WAAW,EAAE,KAAK,KAAK,OAAO,QAAQ,CAAC,GAAG,KAAK,GAAG,CAAC,GAAG,KAAK,IAC1E,KAAK,OAAO,OAAO;AACvB,uBACE,6CAAC,QAAG,WAAU,YAA4D,OAAO,EAAE,SAAS,KAAK,WAAW,OAAO,EAAE,GACnH;AAAA,+DAAC,SAAI,WAAU,eACb;AAAA,iEAAC,WAAM,OAAO,EAAE,SAAS,eAAe,YAAY,UAAU,KAAK,GAAG,UAAU,GAAG,QAAQ,KAAK,WAAW,YAAY,UAAU,GAC/H;AAAA;AAAA,wBAAC;AAAA;AAAA,0BACC,MAAK;AAAA,0BACL,SAAS,KAAK;AAAA,0BACd,UAAU,KAAK;AAAA,0BACf,UAAU,CAAC,UAAU;AACnB,kCAAM,OAAO,YAAY,MAAM;AAC/B,iCAAK,KAAK,IAAI,EAAE,GAAG,MAAM,SAAS,MAAM,OAAO,QAAQ;AACvD,2CAAe,IAAI;AAAA,0BACrB;AAAA;AAAA,sBACF;AAAA,sBACA,4CAAC,YAAO,WAAU,iBAAgB,OAAO,KAAK,OAAO,MAAO,eAAK,OAAO,MAAK;AAAA,uBAC/E;AAAA,oBACA,4CAAC,UAAK,WAAU,WAAW,eAAK,OAAO,WAAU;AAAA,oBAChD,KAAK,YAAY,4CAAC,UAAK,WAAU,WAAW,YAAE,gBAAgB,GAAE;AAAA,qBACnE;AAAA,kBACA,4CAAC,OAAE,WAAU,gBAAe,OAAO,SAAU,mBAAQ;AAAA,qBAlBzB,GAAG,KAAK,OAAO,KAAK,IAAI,KAAK,OAAO,IAAI,EAmBtE;AAAA,cAEJ,CAAC,GACH;AAAA,iBA/CwC,MAAM,KAgDhD;AAAA,UAEJ,CAAC,GACH;AAAA,UAED,cAAc,QAAQ,4CAAC,OAAE,WAAU,iBAAiB,qBAAU;AAAA,UAC/D,6CAAC,SAAI,WAAU,eACb;AAAA,wDAAC,UAAK,WAAU,cAAa;AAAA,YAC7B,4CAAC,0CAAO,SAAQ,SAAQ,SAAS,MAAM,cAAc,KAAK,GAAI,YAAE,QAAQ,GAAE;AAAA,YAC1E;AAAA,cAAC;AAAA;AAAA,gBACC,SAAQ;AAAA,gBACR,UAAU,QAAQ,gBAAgB,QAAQ,CAAC,YAAY,KAAK,UAAQ,KAAK,WAAW,CAAC,KAAK,QAAQ;AAAA,gBAClG,SAAS,MAAM,KAAK,SAAS;AAAA,gBAE5B,YAAE,gBAAgB;AAAA;AAAA,YACrB;AAAA,aACF;AAAA,WACF;AAAA;AAAA,IACF;AAAA,KACF;AAEJ;;;ACnfA,IAAAC,gBAAoC;AAEpC,IAAAC,mCAAkF;AAyJ5E,IAAAC,sBAAA;AAxHN,IAAM,cAAsC;AAAA,EAC1C,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB,YAAY;AAAA,EACZ,eAAe;AAAA,EACf,SAAS;AAAA,EACT,SAAS;AAAA,EACT,QAAQ;AACV;AAEO,SAAS,UAAU,OAAiE;AACzF,QAAM,EAAE,GAAG,SAAS,IAAI;AACxB,QAAM,CAAC,QAAQ,SAAS,QAAI,wBAAgC,IAAI;AAChE,QAAM,CAAC,QAAQ,SAAS,QAAI,wBAA6B,IAAI;AAC7D,QAAM,CAAC,aAAa,cAAc,QAAI,wBAAwB,IAAI;AAClE,QAAM,CAAC,MAAM,OAAO,QAAI,wBAAS,KAAK;AACtC,QAAM,CAAC,SAAS,UAAU,QAAI,wBAA+C,IAAI;AACjF,QAAM,CAAC,WAAW,YAAY,QAAI,wBAAwB,IAAI;AAC9D,QAAM,CAAC,QAAQ,SAAS,QAAI,wBAAS,CAAC;AAEtC,+BAAU,MAAM;AACd,QAAI,UAAU;AACd,SAAK,SAAS,KAAK,EAAE;AAAA,MACnB,CAAC,SAAS;AAAE,YAAI,QAAS,WAAU,KAAK,MAAM;AAAA,MAAE;AAAA,MAChD,CAAC,UAAiB;AAAE,YAAI,SAAS;AAAE,oBAAU,CAAC,CAAC;AAAG,qBAAW,EAAE,IAAI,OAAO,MAAM,GAAG,EAAE,QAAQ,CAAC,KAAK,OAAO,MAAM,WAAW,KAAK,CAAC,GAAG,CAAC;AAAA,QAAE;AAAA,MAAE;AAAA,IAC3I;AACA,WAAO,MAAM;AAAE,gBAAU;AAAA,IAAM;AAAA,EACjC,GAAG,CAAC,UAAU,QAAQ,CAAC,CAAC;AAExB,QAAM,aAAa,MAAY;AAC7B,iBAAa,IAAI;AACjB,cAAU,EAAE,MAAM,UAAU,MAAM,IAAI,aAAa,IAAI,WAAW,IAAI,gBAAgB,MAAM,eAAe,MAAM,SAAS,GAAG,CAAC;AAAA,EAChI;AAEA,QAAM,eAAe,OAAO,UAAuC;AACjE,YAAQ,IAAI;AACZ,iBAAa,IAAI;AACjB,QAAI;AACF,YAAM,OAAO,MAAM,SAAS,IAAI,MAAM,IAAI;AAC1C,gBAAU;AAAA,QACR,MAAM,MAAM,WAAW,SAAS;AAAA,QAChC,MAAM,MAAM;AAAA,QACZ,aAAa,MAAM;AAAA,QACnB,WAAW,MAAM,aAAa;AAAA,QAC9B,gBAAgB,MAAM,WAAW;AAAA,QACjC,eAAe,MAAM,WAAW;AAAA,QAChC,SAAS,KAAK;AAAA,MAChB,CAAC;AAAA,IACH,SAAS,OAAO;AACd,iBAAW,EAAE,IAAI,OAAO,MAAM,GAAG,EAAE,QAAQ,CAAC,KAAK,OAAO,iBAAiB,QAAQ,MAAM,UAAU,KAAK,CAAC,GAAG,CAAC;AAAA,IAC7G,UAAE;AACA,cAAQ,KAAK;AAAA,IACf;AAAA,EACF;AAMA,QAAM,eAAe,CAAC,WAAgD,YAAY,QAAiB;AACjG,UAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,UAAM,OAAO,MAAY;AACvB,UAAI,KAAK,IAAI,IAAI,SAAU;AAC3B,iBAAW,MAAM;AACf,aAAK,SAAS,KAAK,EAAE;AAAA,UACnB,CAAC,SAAS;AACR,sBAAU,KAAK,MAAM;AACrB,gBAAI,CAAC,UAAU,KAAK,MAAM,EAAG,MAAK;AAAA,UACpC;AAAA,UACA,MAAM,KAAK;AAAA,QACb;AAAA,MACF,GAAG,GAAI;AAAA,IACT;AACA,SAAK;AAAA,EACP;AAEA,QAAM,SAAS,YAA2B;AACxC,QAAI,WAAW,KAAM;AACrB,YAAQ,IAAI;AACZ,iBAAa,IAAI;AACjB,QAAI;AACF,YAAMC,QAAO,OAAO,KAAK,KAAK;AAC9B,YAAM,SAAS,KAAK;AAAA,QAClB,MAAAA;AAAA,QACA,aAAa,OAAO;AAAA,QACpB,WAAW,OAAO,UAAU,KAAK,MAAM,KAAK,SAAY,OAAO;AAAA,QAC/D,gBAAgB,OAAO;AAAA,QACvB,eAAe,OAAO;AAAA,QACtB,SAAS,OAAO;AAAA,MAClB,CAAC;AACD,iBAAW,EAAE,IAAI,MAAM,MAAM,EAAE,OAAO,EAAE,CAAC;AACzC,gBAAU,IAAI;AACd,mBAAa,CAAAC,YAAUA,QAAO,KAAK,WAAS,MAAM,SAASD,KAAI,CAAC;AAAA,IAClE,SAAS,OAAO;AACd,mBAAa,OAAO,iBAAiB,QAAQ,MAAM,UAAU,KAAK,CAAC;AAAA,IACrE,UAAE;AACA,cAAQ,KAAK;AAAA,IACf;AAAA,EACF;AAEA,QAAM,WAAW,YAA2B;AAC1C,QAAI,gBAAgB,KAAM;AAC1B,UAAMA,QAAO;AACb,YAAQ,IAAI;AACZ,QAAI;AACF,YAAM,SAAS,OAAOA,KAAI;AAC1B,iBAAW,EAAE,IAAI,MAAM,MAAM,EAAE,OAAO,EAAE,CAAC;AACzC,mBAAa,CAAAC,YAAU,CAACA,QAAO,KAAK,WAAS,MAAM,SAASD,KAAI,CAAC;AAAA,IACnE,SAAS,OAAO;AACd,iBAAW,EAAE,IAAI,OAAO,MAAM,GAAG,EAAE,QAAQ,CAAC,KAAK,OAAO,iBAAiB,QAAQ,MAAM,UAAU,KAAK,CAAC,GAAG,CAAC;AAAA,IAC7G,UAAE;AACA,cAAQ,KAAK;AACb,qBAAe,IAAI;AAAA,IACrB;AAAA,EACF;AAEA,QAAM,WAAW,QAAQ,SAAS;AAElC,SACE,8CAAC,SAAI,WAAU,eACb;AAAA,iDAAC,WAAO,eAAI;AAAA,IAEZ,8CAAC,SAAI,WAAU,YACb;AAAA,mDAAC,uDAAmB,eAAY,QAAO;AAAA,MACvC,6CAAC,QAAI,YAAE,aAAa,GAAE;AAAA,MACtB,6CAAC,UAAK,WAAU,cAAa;AAAA,MAC7B,6CAAC,2CAAO,SAAQ,WAAU,MAAK,MAAK,SAAS,YAAa,YAAE,UAAU,GAAE;AAAA,OAC1E;AAAA,IACA,6CAAC,OAAE,WAAU,aAAa,YAAE,aAAa,GAAE;AAAA,IAE1C,YAAY,QACX,8CAAC,SAAI,WAAU,cAAa,aAAW,QAAQ,KAAK,OAAO,SAAS,MAAK,UACvE;AAAA,mDAAC,6CAAS,OAAO,QAAQ,KAAK,SAAS,SAAS,MAAM,IAAI;AAAA,MAC1D,6CAAC,SAAI,WAAU,kBAAiB,uDAAC,UAAM,kBAAQ,MAAK,GAAO;AAAA,OAC7D;AAAA,IAGF,8CAAC,SAAI,WAAU,gBACb;AAAA,mDAAC,QAAI,YAAE,WAAW,GAAE;AAAA,MACnB,WAAW,QAAQ,6CAAC,UAAK,WAAU,aAAa,iBAAO,QAAO;AAAA,MAC/D,6CAAC,UAAK,WAAU,cAAa;AAAA,MAC7B,6CAAC,YAAO,MAAK,UAAS,WAAU,eAAc,cAAY,EAAE,MAAM,GAAG,OAAO,EAAE,MAAM,GAAG,UAAU,MAAM,SAAS,MAAM,UAAU,CAAC,UAAU,QAAQ,CAAC,GAClJ,uDAAC,yDAAqB,MAAM,IAAI,eAAY,QAAO,GACrD;AAAA,OACF;AAAA,IAEC,WAAW,QAAQ,6CAAC,OAAE,WAAU,aAAa,YAAE,SAAS,GAAE;AAAA,IAC1D,WAAW,QAAQ,OAAO,WAAW,KAAK,6CAAC,OAAE,WAAU,aAAa,YAAE,aAAa,GAAE;AAAA,IACrF,WAAW,QAAQ,OAAO,SAAS,KAClC,6CAAC,QAAG,WAAU,aACX,iBAAO,IAAI,CAAC,UACX,8CAAC,QAAG,WAAU,YACZ;AAAA,oDAAC,SAAI,WAAU,eACb;AAAA,qDAAC,YAAO,WAAU,iBAAgB,OAAO,MAAM,MAAO,gBAAM,MAAK;AAAA,QACjE,6CAAC,UAAK,WAAU,WAAU,aAAU,UAAU,YAAE,YAAY,MAAM,MAAM,KAAK,cAAc,GAAE;AAAA,SAC/F;AAAA,MACA,6CAAC,OAAE,WAAU,gBAAe,OAAO,MAAM,aAAc,gBAAM,aAAY;AAAA,MACzE,8CAAC,SAAI,WAAU,eACZ;AAAA,SAAC,MAAM,WAAW,kBAAkB,6CAAC,UAAK,WAAU,WAAU,oBAAC;AAAA,QAC/D,CAAC,MAAM,WAAW,iBAAiB,6CAAC,UAAK,WAAU,WAAU,eAAC;AAAA,QAC/D,6CAAC,UAAK,WAAU,cAAa;AAAA,QAC7B,6CAAC,2CAAO,SAAQ,SAAQ,MAAK,MAAK,UAAU,MAAM,SAAS,MAAM,KAAK,aAAa,KAAK,GACrF,gBAAM,WAAW,EAAE,MAAM,IAAI,EAAE,MAAM,GACxC;AAAA,QACC,MAAM,YACL,6CAAC,2CAAO,SAAQ,SAAQ,MAAK,MAAK,UAAU,MAAM,SAAS,MAAM,eAAe,MAAM,IAAI,GAAI,YAAE,QAAQ,GAAE;AAAA,SAE9G;AAAA,SAhB4B,GAAG,MAAM,MAAM,IAAI,MAAM,IAAI,EAiB3D,CACD,GACH;AAAA,IAGF;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,WAAW;AAAA,QACjB,SAAS,MAAM,UAAU,IAAI;AAAA,QAC7B,OAAO,WAAW,OAAO,KAAK,OAAO,SAAS,WAAW,EAAE,UAAU,IAAI,OAAO,SAAS,SAAS,EAAE,WAAW,IAAI,EAAE,WAAW;AAAA,QAChI,WAAU;AAAA,QACV,kBAAiB;AAAA,QAEhB,qBAAW,QACV,8CAAC,SAAI,WAAU,YACb;AAAA,wDAAC,WAAM,WAAU,aACf;AAAA,yDAAC,UAAM,YAAE,WAAW,GAAE;AAAA,YACtB;AAAA,cAAC;AAAA;AAAA,gBACC,WAAU;AAAA,gBACV,OAAO,OAAO;AAAA,gBACd,UAAU,YAAY,OAAO,SAAS;AAAA,gBACtC,UAAU,CAAC,UAAU,UAAU,EAAE,GAAG,QAAQ,MAAM,MAAM,OAAO,MAAM,CAAC;AAAA;AAAA,YACxE;AAAA,aACF;AAAA,UACA,8CAAC,WAAM,WAAU,aACf;AAAA,yDAAC,UAAM,YAAE,kBAAkB,GAAE;AAAA,YAC7B;AAAA,cAAC;AAAA;AAAA,gBACC,WAAU;AAAA,gBACV,OAAO,OAAO;AAAA,gBACd,UAAU;AAAA,gBACV,UAAU,CAAC,UAAU,UAAU,EAAE,GAAG,QAAQ,aAAa,MAAM,OAAO,MAAM,CAAC;AAAA;AAAA,YAC/E;AAAA,aACF;AAAA,UACA,8CAAC,WAAM,WAAU,aACf;AAAA,yDAAC,UAAM,YAAE,gBAAgB,GAAE;AAAA,YAC3B;AAAA,cAAC;AAAA;AAAA,gBACC,WAAU;AAAA,gBACV,OAAO,OAAO;AAAA,gBACd,UAAU;AAAA,gBACV,UAAU,CAAC,UAAU,UAAU,EAAE,GAAG,QAAQ,WAAW,MAAM,OAAO,MAAM,CAAC;AAAA;AAAA,YAC7E;AAAA,aACF;AAAA,UACA,8CAAC,SAAI,WAAU,cACb;AAAA,0DAAC,WACC;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,SAAS,OAAO;AAAA,kBAChB,UAAU;AAAA,kBACV,UAAU,CAAC,UAAU,UAAU,EAAE,GAAG,QAAQ,gBAAgB,MAAM,OAAO,QAAQ,CAAC;AAAA;AAAA,cACpF;AAAA,cACC,EAAE,gBAAgB;AAAA,eACrB;AAAA,YACA,8CAAC,WACC;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,SAAS,OAAO;AAAA,kBAChB,UAAU;AAAA,kBACV,UAAU,CAAC,UAAU,UAAU,EAAE,GAAG,QAAQ,eAAe,MAAM,OAAO,QAAQ,CAAC;AAAA;AAAA,cACnF;AAAA,cACC,EAAE,eAAe;AAAA,eACpB;AAAA,aACF;AAAA,UACA,8CAAC,WAAM,WAAU,aACf;AAAA,yDAAC,UAAM,YAAE,cAAc,GAAE;AAAA,YACzB;AAAA,cAAC;AAAA;AAAA,gBACC,WAAU;AAAA,gBACV,OAAO,OAAO;AAAA,gBACd;AAAA,gBACA,UAAU,CAAC,UAAU,UAAU,EAAE,GAAG,QAAQ,SAAS,MAAM,OAAO,MAAM,CAAC;AAAA;AAAA,YAC3E;AAAA,aACF;AAAA,UACC,cAAc,QAAQ,6CAAC,OAAE,WAAU,iBAAiB,qBAAU;AAAA,UAC/D,8CAAC,SAAI,WAAU,eACb;AAAA,yDAAC,UAAK,WAAU,cAAa;AAAA,YAC7B,6CAAC,2CAAO,SAAQ,SAAQ,SAAS,MAAM,UAAU,IAAI,GAAI,qBAAW,EAAE,OAAO,IAAI,EAAE,QAAQ,GAAE;AAAA,YAC5F,CAAC,YAAY,6CAAC,2CAAO,SAAQ,WAAU,UAAU,MAAM,SAAS,MAAM,KAAK,OAAO,GAAI,YAAE,MAAM,GAAE;AAAA,aACnG;AAAA,WACF;AAAA;AAAA,IAEJ;AAAA,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,gBAAgB;AAAA,QACtB,SAAS,MAAM,eAAe,IAAI;AAAA,QAClC,OAAO,EAAE,eAAe;AAAA,QACxB,aAAa,eAAe;AAAA,QAC5B,QACE,8EACE;AAAA,uDAAC,2CAAO,SAAQ,SAAQ,SAAS,MAAM,eAAe,IAAI,GAAI,YAAE,QAAQ,GAAE;AAAA,UAC1E,6CAAC,2CAAO,SAAQ,WAAU,UAAU,MAAM,SAAS,MAAM,KAAK,SAAS,GAAI,YAAE,QAAQ,GAAE;AAAA,WACzF;AAAA,QAGF,uDAAC,OAAG,YAAE,YAAY,GAAE;AAAA;AAAA,IACtB;AAAA,KACF;AAEJ;;;AHrQI,IAAAE,sBAAA;AA1BG,SAAS,oBAAoB,OAInB;AACf,QAAM,EAAE,GAAG,QAAQ,IAAI,IAAI;AAC3B,QAAM,aAAS,qBAAM;AACrB,QAAM,cAAU,sBAAwC,CAAC,CAAC;AAC1D,QAAM,OAAO;AAAA,IACX,EAAE,IAAI,UAAU,OAAO,EAAE,WAAW,EAAE;AAAA,IACtC,EAAE,IAAI,OAAO,OAAO,EAAE,QAAQ,EAAE;AAAA,EAClC;AACA,QAAM,CAAC,UAAU,WAAW,QAAI,wBAAS,QAAQ;AAIjD,QAAM,CAAC,YAAY,aAAa,QAAI,wBAA8B,MAAM,oBAAI,IAAI,CAAC,QAAQ,CAAC,CAAC;AAE3F,+BAAU,MAAM;AACd,kBAAc,CAAC,aAAa;AAC1B,UAAI,SAAS,IAAI,QAAQ,EAAG,QAAO;AACnC,aAAO,oBAAI,IAAI,CAAC,GAAG,UAAU,QAAQ,CAAC;AAAA,IACxC,CAAC;AAAA,EACH,GAAG,CAAC,QAAQ,CAAC;AAEb,SACE,8CAAC,SAAI,WAAU,eACb;AAAA,iDAAC,WAAO,eAAI;AAAA,IAEZ,6CAAC,QAAG,WAAU,eAAe,YAAE,YAAY,GAAE;AAAA,IAC7C,6CAAC,SAAI,WAAU,YAAW,MAAK,WAAU,cAAY,EAAE,YAAY,GAChE,eAAK,IAAI,CAAC,KAAK,UAAU;AACxB,YAAM,WAAW,IAAI,OAAO;AAC5B,aACE;AAAA,QAAC;AAAA;AAAA,UAEC,KAAK,CAAC,YAAY;AAAE,oBAAQ,QAAQ,KAAK,IAAI;AAAA,UAAQ;AAAA,UACrD,IAAI,GAAG,MAAM,QAAQ,IAAI,EAAE;AAAA,UAC3B,MAAK;AAAA,UACL,MAAK;AAAA,UACL,WAAU;AAAA,UACV,iBAAe;AAAA,UACf,iBAAe,GAAG,MAAM,UAAU,IAAI,EAAE;AAAA,UACxC,eAAa,WAAW,SAAS;AAAA,UACjC,UAAU,WAAW,IAAI;AAAA,UACzB,SAAS,MAAM;AAAE,wBAAY,IAAI,EAAE;AAAA,UAAE;AAAA,UACrC,WAAW,CAAC,UAAU;AACpB,gBAAI;AACJ,oBAAQ,MAAM,KAAK;AAAA,cACjB,KAAK;AAAc,6BAAa,QAAQ,KAAK,KAAK;AAAQ;AAAA,cAC1D,KAAK;AAAa,6BAAa,QAAQ,IAAI,KAAK,UAAU,KAAK;AAAQ;AAAA,cACvE,KAAK;AAAQ,4BAAY;AAAG;AAAA,cAC5B,KAAK;AAAO,4BAAY,KAAK,SAAS;AAAG;AAAA,cACzC;AAAS;AAAA,YACX;AACA,kBAAM,eAAe;AACrB,wBAAY,KAAK,SAAS,GAAG,MAAM,QAAQ;AAC3C,oBAAQ,QAAQ,SAAS,GAAG,MAAM;AAAA,UACpC;AAAA,UAEC,cAAI;AAAA;AAAA,QAzBA,IAAI;AAAA,MA0BX;AAAA,IAEJ,CAAC,GACH;AAAA,IACC,KACE,OAAO,SAAO,IAAI,OAAO,YAAY,WAAW,IAAI,IAAI,EAAE,CAAC,EAC3D,IAAI,CAAC,QAAQ;AACZ,YAAM,WAAW,IAAI,OAAO;AAC5B,aACE;AAAA,QAAC;AAAA;AAAA,UAEC,IAAI,GAAG,MAAM,UAAU,IAAI,EAAE;AAAA,UAC7B,WAAU;AAAA,UACV,MAAK;AAAA,UACL,mBAAiB,GAAG,MAAM,QAAQ,IAAI,EAAE;AAAA,UACxC,QAAQ,CAAC;AAAA,UAER,cAAI,OAAO,WACR,6CAAC,aAAU,GAAM,UAAU,QAAQ,IACnC,6CAAC,UAAO,GAAM,UAAU,KAAK;AAAA;AAAA,QAT5B,IAAI;AAAA,MAUX;AAAA,IAEJ,CAAC;AAAA,KACL;AAEJ;;;AInGO,IAAM,KAAK;AAAA,EAChB,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,aAAa;AAAA,EACb,UAAU;AAAA,EACV,WAAW;AAAA,EACX,WAAW;AAAA,EACX,WAAW;AAAA,EACX,kBAAkB;AAAA,EAClB,gBAAgB;AAAA,EAChB,cAAc;AAAA,EACd,gBAAgB;AAAA,EAChB,eAAe;AAAA,EACf,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AAAA,EACP,aAAa;AAAA,EACb,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,kBAAkB;AAAA,EAClB,qBAAqB;AAAA,EACrB,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf,eAAe;AAAA,EACf,cAAc;AAAA,EACd,UAAU;AAAA,EACV,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,UAAU;AAAA,EACV,UAAU;AAAA,EACV,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,gBAAgB;AAAA,EAChB,eAAe;AAAA,EACf,SAAS;AAAA,EACT,MAAM;AAAA,EACN,UAAU;AAAA,EACV,KAAK;AAAA,EACL,cAAc;AAAA,EACd,UAAU;AAAA,EACV,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,eAAe;AAAA,EACf,aAAa;AAAA,EACb,aAAa;AAAA,EACb,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,eAAe;AAAA,EACf,oBAAoB;AAAA,EACpB,kBAAkB;AAAA,EAClB,YAAY;AAAA,EACZ,SAAS;AAAA,EACT,qBAAqB;AAAA,EACrB,oBAAoB;AAAA,EACpB,YAAY;AAAA,EACZ,iBAAiB;AAAA,EACjB,QAAQ;AACV;AAEO,IAAM,KAAK;AAAA,EAChB,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,aAAa;AAAA,EACb,UAAU;AAAA,EACV,WAAW;AAAA,EACX,WAAW;AAAA,EACX,WAAW;AAAA,EACX,kBAAkB;AAAA,EAClB,gBAAgB;AAAA,EAChB,cAAc;AAAA,EACd,gBAAgB;AAAA,EAChB,eAAe;AAAA,EACf,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AAAA,EACP,aAAa;AAAA,EACb,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,kBAAkB;AAAA,EAClB,qBAAqB;AAAA,EACrB,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf,eAAe;AAAA,EACf,cAAc;AAAA,EACd,UAAU;AAAA,EACV,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,UAAU;AAAA,EACV,UAAU;AAAA,EACV,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,gBAAgB;AAAA,EAChB,eAAe;AAAA,EACf,SAAS;AAAA,EACT,MAAM;AAAA,EACN,UAAU;AAAA,EACV,KAAK;AAAA,EACL,cAAc;AAAA,EACd,UAAU;AAAA,EACV,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,eAAe;AAAA,EACf,aAAa;AAAA,EACb,aAAa;AAAA,EACb,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,eAAe;AAAA,EACf,oBAAoB;AAAA,EACpB,kBAAkB;AAAA,EAClB,YAAY;AAAA,EACZ,SAAS;AAAA,EACT,qBAAqB;AAAA,EACrB,oBAAoB;AAAA,EACpB,YAAY;AAAA,EACZ,iBAAiB;AAAA,EACjB,QAAQ;AACV;;;ALrIO,IAAM,KAAK;AA2BlB,eAAe,UAAa,MAAc,MAAgC;AACxE,QAAM,WAAW,MAAM,MAAM,MAAM,IAAI;AACvC,QAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,MAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,KAAK,SAAS,QAAQ,SAAS,MAAM,EAAE;AACzE,SAAO;AACT;AAEA,IAAM,OAAO,CAAC,MAAc,SAC1B,UAAU,MAAM,EAAE,QAAQ,QAAQ,SAAS,EAAE,gBAAgB,mBAAmB,GAAG,MAAM,KAAK,UAAU,IAAI,EAAE,CAAC;AAE1G,IAAM,OAAO;AACb,IAAM,SAAS,CAAC,SAAS,QAAQ;AAgBjC,SAAS,MAAM,KAAsC;AAC1D,MAAI,OAAO,MAAM,IAAI,OAAO,SAAS,IAAI,EAAE,IAAI,GAAG,CAAC,GAAG,uCAAuC;AAC7F,QAAM,IAAI,IAAI,OAAO,KAAK,EAAE;AAE5B,QAAM,iBAAiC;AAAA,IACrC,MAAM,MAAM,UAAsC,iCAAiC;AAAA,IACnF,KAAK,CAACC,UAAiB,UAAqB,uCAAuC,mBAAmBA,KAAI,CAAC,EAAE;AAAA,IAC7G,MAAM,CAAC,UAAmB,KAAK,uCAAuC,KAAK;AAAA,IAC3E,QAAQ,CAACA,UAAiB,KAAK,yCAAyC,EAAE,MAAAA,MAAK,CAAC;AAAA,EAClF;AAEA,QAAM,cAA2B;AAAA,IAC/B,MAAM,MAAM,UAAiC,8BAA8B;AAAA,IAC3E,MAAM,CAAC,UAAmB,KAAK,qCAAqC,KAAK;AAAA,IACzE,QAAQ,CAAC,IAAY,aAAsB,KAAK,uCAAuC,EAAE,IAAI,SAAS,CAAC;AAAA,IACvG,QAAQ,CAAC,OAAe,KAAK,uCAAuC,EAAE,GAAG,CAAC;AAAA,IAC1E,YAAY,MAAM,UAAiE,sCAAsC;AAAA,IACzH,aAAa,CAAC,UACZ,KAAK,yCAAyC,EAAE,MAAM,CAAC;AAAA,IACzD,SAAS,YAA2B;AAClC,UAAI,OAAO,eAAe,QAAW;AACnC,eAAO,WAAW,iBAAiB;AACnC;AAAA,MACF;AAEA,UAAI;AAAE,cAAM,KAAK,oCAAoC,CAAC,CAAC;AAAA,MAAE,QAAQ;AAAA,MAAsC;AAAA,IACzG;AAAA,IACA,SAAS,OAAO,eAAe;AAAA,EACjC;AAKA,MAAI,MAAM,OAAO,oBAAoB,MAAM;AACzC,WAAO,IAAI,MAAM,SAAS;AAAA,MACxB,MAAM;AAAA,MACN,IAAI;AAAA,MACJ,OAAO;AAAA,MACP,OAAO,MAAM,EAAE,YAAY;AAAA,MAC3B,QAAQ;AAAA,IACV,GAAG,UAAM,cAAAC,eAAE,qBAAqB,EAAE,GAAG,QAAQ,gBAAgB,KAAK,YAAY,CAAC,CAAC;AAAA,EAClF,CAAC;AACH;",
6
+ "names": ["import_react", "import_react", "import_react", "import_dsh_client_ui_primitives", "import_jsx_runtime", "name", "skills", "import_jsx_runtime", "name", "h"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-capabilities",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Manage skills and MCP servers from the Web UI Settings. 在设置页管理 dsh 的技能与 MCP 服务器。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -0,0 +1,102 @@
1
+ /** Settings top-level “技能与 MCP” section: one nav entry beside 通用设置 and
2
+ * 模型, with the Skills and MCP pages as internal tabs. The two pages are the
3
+ * same components the Plugins section used to host — promoted a level instead
4
+ * of squeezed under 插件. Pure composition; data still arrives through the
5
+ * injected faces. */
6
+
7
+ import { useEffect, useId, useRef, useState } from 'react'
8
+ import type { ReactElement } from 'react'
9
+ import { CSS } from './css.ts'
10
+ import { McpTab } from './McpTab.tsx'
11
+ import type { McpInjected } from './McpTab.tsx'
12
+ import { SkillsTab } from './SkillsTab.tsx'
13
+ import type { SkillsInjected } from './SkillsTab.tsx'
14
+ import type { Translate } from './index.ts'
15
+
16
+ export function CapabilitiesSection(props: {
17
+ t: Translate
18
+ skills: SkillsInjected
19
+ mcp: McpInjected
20
+ }): ReactElement {
21
+ const { t, skills, mcp } = props
22
+ const tabsId = useId()
23
+ const tabRefs = useRef<Array<HTMLButtonElement | null>>([])
24
+ const rows = [
25
+ { id: 'skills', label: t('skillsTab') },
26
+ { id: 'mcp', label: t('mcpTab') },
27
+ ]
28
+ const [activeId, setActiveId] = useState('skills')
29
+ // The skills page mounts immediately; a tab mounts only when first
30
+ // selected, then stays mounted while hidden so editor drafts and outcome
31
+ // banners survive switching between the two views.
32
+ const [visitedIds, setVisitedIds] = useState<ReadonlySet<string>>(() => new Set(['skills']))
33
+
34
+ useEffect(() => {
35
+ setVisitedIds((previous) => {
36
+ if (previous.has(activeId)) return previous
37
+ return new Set([...previous, activeId])
38
+ })
39
+ }, [activeId])
40
+
41
+ return (
42
+ <div className="dpc-section">
43
+ <style>{CSS}</style>
44
+
45
+ <h2 className="dpc-heading">{t('sectionNav')}</h2>
46
+ <div className="dpc-tabs" role="tablist" aria-label={t('sectionNav')}>
47
+ {rows.map((row, index) => {
48
+ const selected = row.id === activeId
49
+ return (
50
+ <button
51
+ key={row.id}
52
+ ref={(element) => { tabRefs.current[index] = element }}
53
+ id={`${tabsId}-tab-${row.id}`}
54
+ type="button"
55
+ role="tab"
56
+ className="dpc-tab"
57
+ aria-selected={selected}
58
+ aria-controls={`${tabsId}-panel-${row.id}`}
59
+ data-active={selected ? 'true' : undefined}
60
+ tabIndex={selected ? 0 : -1}
61
+ onClick={() => { setActiveId(row.id) }}
62
+ onKeyDown={(event) => {
63
+ let nextIndex: number
64
+ switch (event.key) {
65
+ case 'ArrowRight': nextIndex = (index + 1) % rows.length; break
66
+ case 'ArrowLeft': nextIndex = (index - 1 + rows.length) % rows.length; break
67
+ case 'Home': nextIndex = 0; break
68
+ case 'End': nextIndex = rows.length - 1; break
69
+ default: return
70
+ }
71
+ event.preventDefault()
72
+ setActiveId(rows[nextIndex]?.id ?? 'skills')
73
+ tabRefs.current[nextIndex]?.focus()
74
+ }}
75
+ >
76
+ {row.label}
77
+ </button>
78
+ )
79
+ })}
80
+ </div>
81
+ {rows
82
+ .filter(row => row.id === activeId || visitedIds.has(row.id))
83
+ .map((row) => {
84
+ const selected = row.id === activeId
85
+ return (
86
+ <div
87
+ key={row.id}
88
+ id={`${tabsId}-panel-${row.id}`}
89
+ className="dpc-tabPanel"
90
+ role="tabpanel"
91
+ aria-labelledby={`${tabsId}-tab-${row.id}`}
92
+ hidden={!selected}
93
+ >
94
+ {row.id === 'skills'
95
+ ? <SkillsTab t={t} injected={skills} />
96
+ : <McpTab t={t} injected={mcp} />}
97
+ </div>
98
+ )
99
+ })}
100
+ </div>
101
+ )
102
+ }
@@ -44,6 +44,26 @@ interface EditorState {
44
44
  headers: string
45
45
  }
46
46
 
47
+ /** One import entry together with its index in the flat state array —
48
+ * checkbox handlers rewrite the flat array, so indices must survive grouping. */
49
+ type ImportItem = { server: ImportedServerView; existing: boolean; checked: boolean }
50
+
51
+ /** Group import candidates by source agent, known agents first. */
52
+ export function importGroups(items: ImportItem[]): Array<{ agent: string; label: string; items: Array<{ item: ImportItem; index: number }> }> {
53
+ const label = (agent: string) => agent === 'claude-code' ? 'Claude Code' : agent === 'codex' ? 'Codex' : agent
54
+ const order = ['claude-code', 'codex']
55
+ const agents = [...new Set(items.map(item => item.server.agent))]
56
+ .sort((a, b) => {
57
+ const rank = (agent: string) => { const at = order.indexOf(agent); return at === -1 ? order.length : at }
58
+ return rank(a) - rank(b) || a.localeCompare(b)
59
+ })
60
+ return agents.map(agent => ({
61
+ agent,
62
+ label: label(agent),
63
+ items: items.map((item, index) => ({ item, index })).filter(({ item }) => item.server.agent === agent),
64
+ }))
65
+ }
66
+
47
67
  /** KEY=VALUE / KEY: VALUE lines to a map. */
48
68
  function parsePairs(text: string, separator: ':' | '='): Record<string, string> {
49
69
  const map: Record<string, string> = {}
@@ -300,6 +320,8 @@ export function McpTab(props: { t: Translate; injected: McpInjected }): ReactEle
300
320
  open={editor !== null}
301
321
  onClose={() => setEditor(null)}
302
322
  title={editor !== null && editor.id !== '' ? t('editServer') : t('addServer')}
323
+ className="dpc-modalForm"
324
+ contentClassName="dpc-modalScroll"
303
325
  >
304
326
  {editor !== null && (
305
327
  <div className="dpc-form">
@@ -394,41 +416,73 @@ export function McpTab(props: { t: Translate; injected: McpInjected }): ReactEle
394
416
  open={importOpen}
395
417
  onClose={() => setImportOpen(false)}
396
418
  title={t('importServers')}
419
+ className="dpc-modalWide"
397
420
  >
398
421
  <div className="dpc-form">
399
422
  <p className="dpc-intro" style={{ margin: 0 }}>{t('importIntro')}</p>
400
423
  {importItems === null && <p className="dpc-empty">{t('loading')}</p>}
401
424
  {importItems !== null && importItems.length === 0 && <p className="dpc-empty">{t('importEmpty')}</p>}
402
425
  {importItems !== null && importItems.length > 0 && (
403
- <ul className="dpc-cards" style={{ gridTemplateColumns: 'minmax(0, 1fr)' }}>
404
- {importItems.map((item, index) => (
405
- <li className="dpc-card" key={`${item.server.agent}/${item.server.name}`} style={{ opacity: item.existing ? 0.55 : 1 }}>
406
- <div className="dpc-cardTop">
407
- <label style={{ display: 'inline-flex', alignItems: 'center', gap: 8, minWidth: 0, cursor: item.existing ? 'default' : 'pointer' }}>
408
- <input
409
- type="checkbox"
410
- checked={item.checked}
411
- disabled={item.existing}
412
- onChange={(event) => {
413
- const next = importItems.slice()
414
- next[index] = { ...item, checked: event.target.checked }
415
- setImportItems(next)
416
- }}
417
- />
418
- <strong className="dpc-cardTitle" title={item.server.name}>{item.server.name}</strong>
419
- </label>
420
- <span className="dpc-tag" data-kind="source">{item.server.agent === 'claude-code' ? 'Claude Code' : 'Codex'}</span>
421
- <span className="dpc-tag">{item.server.transport}</span>
422
- {item.existing && <span className="dpc-tag">{t('importExisting')}</span>}
423
- </div>
424
- <p className="dpc-cardDesc">
425
- {item.server.transport === 'stdio'
426
- ? `${item.server.command ?? ''} ${(item.server.args ?? []).join(' ')}`
427
- : item.server.url ?? ''}
428
- </p>
429
- </li>
430
- ))}
431
- </ul>
426
+ <div className="dpc-importScroll">
427
+ {importGroups(importItems).map(group => {
428
+ const selectable = group.items
429
+ .filter(({ item }) => !item.existing)
430
+ .map(({ index }) => index)
431
+ const allChecked = selectable.length > 0
432
+ && selectable.every(index => importItems[index].checked)
433
+ return (
434
+ <section className="dpc-importGroup" key={group.agent}>
435
+ <div className="dpc-importHead">
436
+ <span className="dpc-tag" data-kind="source">{group.label}</span>
437
+ <span className="dpc-importCount">{group.items.length}</span>
438
+ {selectable.length > 0 && (
439
+ <label className="dpc-importAll">
440
+ <input
441
+ type="checkbox"
442
+ checked={allChecked}
443
+ onChange={(event) => {
444
+ const next = importItems.slice()
445
+ for (const index of selectable) next[index] = { ...next[index], checked: event.target.checked }
446
+ setImportItems(next)
447
+ }}
448
+ />
449
+ {t('importSelectAll')}
450
+ </label>
451
+ )}
452
+ </div>
453
+ <ul className="dpc-cards" style={{ gridTemplateColumns: 'minmax(0, 1fr)' }}>
454
+ {group.items.map(({ item, index }) => {
455
+ const command = item.server.transport === 'stdio'
456
+ ? `${item.server.command ?? ''} ${(item.server.args ?? []).join(' ')}`.trim()
457
+ : item.server.url ?? ''
458
+ return (
459
+ <li className="dpc-card" key={`${item.server.agent}/${item.server.name}`} style={{ opacity: item.existing ? 0.55 : 1 }}>
460
+ <div className="dpc-cardTop">
461
+ <label style={{ display: 'inline-flex', alignItems: 'center', gap: 8, minWidth: 0, cursor: item.existing ? 'default' : 'pointer' }}>
462
+ <input
463
+ type="checkbox"
464
+ checked={item.checked}
465
+ disabled={item.existing}
466
+ onChange={(event) => {
467
+ const next = importItems.slice()
468
+ next[index] = { ...item, checked: event.target.checked }
469
+ setImportItems(next)
470
+ }}
471
+ />
472
+ <strong className="dpc-cardTitle" title={item.server.name}>{item.server.name}</strong>
473
+ </label>
474
+ <span className="dpc-tag">{item.server.transport}</span>
475
+ {item.existing && <span className="dpc-tag">{t('importExisting')}</span>}
476
+ </div>
477
+ <p className="dpc-cardDesc" title={command}>{command}</p>
478
+ </li>
479
+ )
480
+ })}
481
+ </ul>
482
+ </section>
483
+ )
484
+ })}
485
+ </div>
432
486
  )}
433
487
  {formError !== null && <p className="dpc-formError">{formError}</p>}
434
488
  <div className="dpc-cardRow">
@@ -213,6 +213,8 @@ export function SkillsTab(props: { t: Translate; injected: SkillsInjected }): Re
213
213
  open={editor !== null}
214
214
  onClose={() => setEditor(null)}
215
215
  title={editor === null ? '' : editor.mode === 'create' ? t('newSkill') : editor.mode === 'edit' ? t('editSkill') : t('viewSkill')}
216
+ className="dpc-modalForm"
217
+ contentClassName="dpc-modalScroll"
216
218
  >
217
219
  {editor !== null && (
218
220
  <div className="dpc-form">
package/src/client/css.ts CHANGED
@@ -4,6 +4,15 @@
4
4
 
5
5
  export const CSS = `
6
6
  .dpc-section{display:flex;flex-direction:column;gap:14px;width:100%;max-width:760px;color:var(--dsw-alias-label-primary)}
7
+ .dpc-heading{margin:0;font-size:18px;line-height:26px;font-weight:600}
8
+ /* Top-level section's internal tabs — same underline-tab look as the host's
9
+ Plugins section so the promoted placement still reads as one family. */
10
+ .dpc-tabs{display:flex;align-items:flex-end;gap:22px;border-bottom:1px solid var(--dsw-alias-border-l2);margin-top:2px}
11
+ .dpc-tab{position:relative;border:0;padding:7px 1px 9px;background:transparent;color:var(--dsw-alias-label-tertiary);font:inherit;font-size:13px;line-height:20px;cursor:pointer}
12
+ .dpc-tab:hover,.dpc-tab[data-active='true']{color:var(--dsw-alias-label-primary)}
13
+ .dpc-tab[data-active='true']::after,.dpc-tab:focus-visible::after{position:absolute;right:0;bottom:-1px;left:0;height:2px;border-radius:2px 2px 0 0;background:var(--dsw-alias-label-primary);content:''}
14
+ .dpc-tab:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px;border-radius:2px;color:var(--dsw-alias-label-primary)}
15
+ .dpc-tabPanel{min-width:0;padding-top:2px}
7
16
  .dpc-head{display:flex;align-items:center;gap:8px}
8
17
  .dpc-head h3{margin:0;font-size:13px;line-height:20px;font-weight:600}
9
18
  .dpc-head>svg{flex:none;color:var(--dsw-alias-label-tertiary)}
@@ -37,11 +46,25 @@ export const CSS = `
37
46
  .dpc-label{display:flex;flex-direction:column;gap:4px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary)}
38
47
  .dpc-label>span:first-child{color:var(--dsw-alias-label-tertiary)}
39
48
  .dpc-input,.dpc-textarea,.dpc-select{width:100%;box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:7px 10px;outline:none;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px}
40
- .dpc-textarea{min-height:120px;resize:vertical;font-family:var(--ds-font-family-code);line-height:1.5}
41
- .dpc-textarea[data-short='true']{min-height:64px}
49
+ .dpc-textarea{min-height:240px;resize:vertical;font-family:var(--ds-font-family-code);line-height:1.5}
50
+ .dpc-textarea[data-short='true']{min-height:96px}
42
51
  .dpc-input:focus-visible,.dpc-textarea:focus-visible,.dpc-select:focus-visible{border-color:var(--dsw-alias-state-business-primary);box-shadow:0 0 0 2px color-mix(in srgb,var(--dsw-alias-state-business-primary) 18%,transparent)}
43
52
  .dpc-checks{display:flex;gap:16px;font-size:13px;line-height:20px}
44
53
  .dpc-checks label{display:inline-flex;align-items:center;gap:6px;cursor:pointer}
45
54
  .dpc-formError{margin:0;color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px}
55
+ /* Import dialog: wider than the host's 380px default; the server list gets
56
+ its own scroll so title, intro, and footer stay pinned. The doubled class
57
+ beats the host dialog's module CSS regardless of injection order. */
58
+ .dpc-modalWide.dpc-modalWide{width:min(680px,100%)}
59
+ /* Editor dialogs (new skill / server): 640px wide so markdown bodies and
60
+ command/arg/env lines stop wrapping mid-token; the content column scrolls
61
+ on short viewports instead of clipping past the dialog edge. */
62
+ .dpc-modalForm.dpc-modalForm{width:min(640px,100%)}
63
+ .dpc-modalScroll.dpc-modalScroll{max-height:calc(100vh - 160px);overflow-y:auto}
64
+ .dpc-importScroll{display:flex;flex-direction:column;gap:14px;max-height:min(400px,52vh);overflow-y:auto;padding:2px 4px 2px 2px}
65
+ .dpc-importGroup{display:flex;flex-direction:column;gap:8px}
66
+ .dpc-importHead{display:flex;align-items:center;gap:8px;padding:0 2px}
67
+ .dpc-importCount{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums}
68
+ .dpc-importAll{margin-left:auto;display:inline-flex;align-items:center;gap:6px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary);cursor:pointer}
46
69
  @media(max-width:680px){.dpc-cards{grid-template-columns:minmax(0,1fr)}}
47
70
  `
@@ -1,9 +1,11 @@
1
- /** dsh-plugin-capabilities client entry: contributes the “技能/Skills” and
2
- * MCP” tabs into Settings Plugins. Calls the host routes with fetch. */
1
+ /** dsh-plugin-capabilities client entry: contributes the top-level
2
+ * “技能与 MCP” section into Settings (beside 通用设置/模型, with Skills and
3
+ * MCP as internal tabs). Calls the host routes with fetch. */
3
4
 
4
5
  import { createElement as h } from 'react'
5
- import { McpTab, type McpInjected, type McpRow } from './McpTab.tsx'
6
- import { SkillsTab, type SkillsInjected, type SkillRowView } from './SkillsTab.tsx'
6
+ import { CapabilitiesSection } from './CapabilitiesSection.tsx'
7
+ import type { McpInjected, McpRow } from './McpTab.tsx'
8
+ import type { SkillsInjected, SkillRowView } from './SkillsTab.tsx'
7
9
  import { zh, en } from './locales.ts'
8
10
 
9
11
  /** Locale dictionary namespace owned by this plugin. */
@@ -91,25 +93,16 @@ export function apply(ctx: CapabilitiesClientContext): void {
91
93
  desktop: window.dshDesktop !== undefined,
92
94
  }
93
95
 
94
- ctx.slots.inject('settings.plugins.tab', () => {
96
+ // One top-level nav entry (between 模型 and 插件), not a tab under the
97
+ // Plugins section — the section component owns its internal Skills/MCP
98
+ // tabs directly, so nothing registers into settings.plugins.tab anymore.
99
+ ctx.slots.inject('settings.section', () => {
95
100
  return ctx.slots.register({
96
- name: 'settings.plugins.tab',
97
- id: 'capabilities-skills',
98
- order: 30,
99
- label: () => t('skillsTab'),
101
+ name: 'settings.section',
102
+ id: 'capabilities',
103
+ order: 12,
104
+ label: () => t('sectionNav'),
100
105
  locale: NS,
101
- inject: () => skillsInjected,
102
- }, () => h(SkillsTab, { t, injected: skillsInjected }))
103
- })
104
-
105
- ctx.slots.inject('settings.plugins.tab', () => {
106
- return ctx.slots.register({
107
- name: 'settings.plugins.tab',
108
- id: 'capabilities-mcp',
109
- order: 40,
110
- label: () => t('mcpTab'),
111
- locale: NS,
112
- inject: () => mcpInjected,
113
- }, () => h(McpTab, { t, injected: mcpInjected }))
106
+ }, () => h(CapabilitiesSection, { t, skills: skillsInjected, mcp: mcpInjected }))
114
107
  })
115
108
  }
@@ -1,6 +1,7 @@
1
1
  /** zh/en dictionaries for the Settings capabilities tabs. */
2
2
 
3
3
  export const zh = {
4
+ sectionNav: '技能与 MCP',
4
5
  skillsTab: '技能',
5
6
  mcpTab: 'MCP',
6
7
  skillsTitle: '技能管理',
@@ -56,6 +57,7 @@ export const zh = {
56
57
  importServers: '从其他 Agent 导入',
57
58
  importIntro: '扫描 Claude Code(~/.claude.json)与 Codex(~/.codex/config.toml)的 MCP 服务器配置,勾选后导入为本 profile 的服务器行。',
58
59
  importEmpty: '没有发现可导入的 MCP 服务器',
60
+ importSelectAll: '全选',
59
61
  importExisting: '已存在',
60
62
  importSelected: '导入选中项',
61
63
  restartNeeded: '配置已写入,重启 dsh 后生效。',
@@ -71,6 +73,7 @@ export const zh = {
71
73
  }
72
74
 
73
75
  export const en = {
76
+ sectionNav: 'Skills & MCP',
74
77
  skillsTab: 'Skills',
75
78
  mcpTab: 'MCP',
76
79
  skillsTitle: 'Skills',
@@ -126,6 +129,7 @@ export const en = {
126
129
  importServers: 'Import from other agents',
127
130
  importIntro: 'Scans Claude Code (~/.claude.json) and Codex (~/.codex/config.toml) MCP server configs; selected entries become server rows in this profile.',
128
131
  importEmpty: 'No importable MCP servers found',
132
+ importSelectAll: 'Select all',
129
133
  importExisting: 'already here',
130
134
  importSelected: 'Import selected',
131
135
  restartNeeded: 'Configuration written — restart dsh to apply.',