dsh-plugin-capabilities 0.1.6 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -28,10 +28,10 @@ __export(index_exports, {
28
28
  name: () => name
29
29
  });
30
30
  module.exports = __toCommonJS(index_exports);
31
- var import_react4 = require("react");
31
+ var import_react5 = require("react");
32
32
 
33
33
  // src/client/CapabilitiesSection.tsx
34
- var import_react3 = require("react");
34
+ var import_react4 = require("react");
35
35
 
36
36
  // src/client/css.ts
37
37
  var CSS = `
@@ -98,13 +98,237 @@ var CSS = `
98
98
  .dpc-importHead{display:flex;align-items:center;gap:8px;padding:0 2px}
99
99
  .dpc-importCount{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums}
100
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}
101
+ /* Skills search + source filter chips. */
102
+ .dpc-search{width:200px;box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:4px 10px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;font-size:12px;line-height:18px}
103
+ .dpc-search:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-2px}
104
+ .dpc-chips{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
105
+ .dpc-chip{border:1px solid var(--dsw-alias-border-l2);border-radius:999px;padding:2px 10px;background:transparent;color:var(--dsw-alias-label-secondary);font:inherit;font-size:12px;line-height:16px;cursor:pointer}
106
+ .dpc-chip:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}
107
+ .dpc-chip[data-active='true']{border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}
108
+ .dpc-chip:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:1px}
109
+ /* Load-policy switch on skill cards (no form semantics \u2014 instant apply). */
110
+ .dpc-switch{position:relative;flex:none;width:30px;height:18px;border:0;border-radius:999px;background:var(--dsw-alias-bg-layer-1);box-shadow:inset 0 0 0 1px var(--dsw-alias-border-l2);cursor:pointer;transition:background .15s}
111
+ .dpc-switch[aria-checked='true']{background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 55%,transparent);box-shadow:none}
112
+ .dpc-switchKnob{position:absolute;top:2px;left:2px;width:14px;height:14px;border-radius:50%;background:var(--dsw-alias-label-primary);transition:left .15s}
113
+ .dpc-switch[aria-checked='true'] .dpc-switchKnob{left:14px;background:#fff}
114
+ .dpc-switch:disabled{opacity:.55;cursor:default}
115
+ .dpc-switch:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}
116
+ /* Quiet inline link-button (open folder, homepage). */
117
+ .dpc-link{border:0;padding:0;background:transparent;color:var(--dsw-alias-state-business-primary);font:inherit;font-size:12px;line-height:18px;cursor:pointer;text-decoration:none}
118
+ .dpc-link:hover{text-decoration:underline}
119
+ .dpc-link:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px;border-radius:2px}
120
+ /* Skill repositories list + add form. */
121
+ .dpc-rootsHead{margin-top:10px}
122
+ .dpc-roots{display:flex;flex-direction:column;gap:6px;margin:0;padding:0;list-style:none}
123
+ .dpc-root{display:flex;align-items:center;gap:8px;min-width:0;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:8px 12px;background:var(--dsw-alias-bg-layer-3)}
124
+ .dpc-rootLabel{flex:none;font-size:13px;line-height:18px;font-weight:600}
125
+ .dpc-rootPath{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary);font-family:var(--ds-font-family-code)}
126
+ .dpc-addRoot{border:1px dashed var(--dsw-alias-border-l2);border-radius:8px;padding:10px 12px}
127
+ .dpc-addRootRow{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
128
+ .dpc-addRootRow .dpc-select{width:auto;flex:none}
129
+ .dpc-addRootRow .dpc-input{flex:1;min-width:200px}
130
+ /* Market segments + env hint + MCP format preview. */
131
+ .dpc-segments{display:inline-flex;gap:4px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:3px;background:var(--dsw-alias-bg-layer-1)}
132
+ .dpc-segment{border:0;border-radius:6px;padding:4px 14px;background:transparent;color:var(--dsw-alias-label-secondary);font:inherit;font-size:12px;line-height:18px;cursor:pointer}
133
+ .dpc-segment:hover{color:var(--dsw-alias-label-primary)}
134
+ .dpc-segment[data-active='true']{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);font-weight:600}
135
+ .dpc-segment:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-2px}
136
+ .dpc-envHint{margin:0;font-size:12px;line-height:18px;color:var(--dsw-alias-state-warning-primary,var(--dsw-alias-label-secondary))}
137
+ .dpc-format{border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:8px 12px;background:var(--dsw-alias-bg-layer-1)}
138
+ .dpc-format summary{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary);cursor:pointer;user-select:none}
139
+ .dpc-format summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px;border-radius:2px}
140
+ .dpc-format .dpc-form{margin-top:8px}
141
+ .dpc-formatHint{margin:2px 0 0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary)}
142
+ .dpc-code{margin:4px 0 8px;border:1px solid var(--dsw-alias-border-l2);border-radius:6px;padding:8px 10px;overflow-x:auto;background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);font-family:var(--ds-font-family-code);font-size:11px;line-height:17px;white-space:pre}
101
143
  @media(max-width:680px){.dpc-cards{grid-template-columns:minmax(0,1fr)}}
102
144
  `;
103
145
 
104
- // src/client/McpTab.tsx
146
+ // src/client/MarketTab.tsx
105
147
  var import_react = require("react");
106
148
  var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
107
149
  var import_jsx_runtime = require("react/jsx-runtime");
150
+ var zhUi = () => navigator.language.toLowerCase().startsWith("zh");
151
+ function MarketTab(props) {
152
+ const { t, market, mcp } = props;
153
+ const [segment, setSegment] = (0, import_react.useState)("skills");
154
+ const [repos, setRepos] = (0, import_react.useState)(null);
155
+ const [servers, setServers] = (0, import_react.useState)(null);
156
+ const [source, setSource] = (0, import_react.useState)("remote");
157
+ const [busyId, setBusyId] = (0, import_react.useState)(null);
158
+ const [outcome, setOutcome] = (0, import_react.useState)(null);
159
+ const [confirmUninstall, setConfirmUninstall] = (0, import_react.useState)(null);
160
+ const [reload, setReload] = (0, import_react.useState)(0);
161
+ const refreshSkills = (0, import_react.useCallback)(() => {
162
+ setRepos(null);
163
+ void market.skillsIndex().then(
164
+ (body) => {
165
+ setRepos(body.repos);
166
+ setSource(body.source);
167
+ },
168
+ (error) => {
169
+ setRepos([]);
170
+ setOutcome({ ok: false, text: `${t("failed")}: ${String(error.message ?? error)}` });
171
+ }
172
+ );
173
+ }, [market, t]);
174
+ const refreshMcp = (0, import_react.useCallback)(() => {
175
+ setServers(null);
176
+ void market.mcpIndex().then(
177
+ (body) => {
178
+ setServers(body.servers);
179
+ setSource(body.source);
180
+ },
181
+ (error) => {
182
+ setServers([]);
183
+ setOutcome({ ok: false, text: `${t("failed")}: ${String(error.message ?? error)}` });
184
+ }
185
+ );
186
+ }, [market, t]);
187
+ (0, import_react.useEffect)(() => {
188
+ if (segment === "skills") refreshSkills();
189
+ else refreshMcp();
190
+ }, [segment, reload, refreshSkills, refreshMcp]);
191
+ const doInstallRepo = async (repo) => {
192
+ setBusyId(repo.id);
193
+ setOutcome(null);
194
+ try {
195
+ await market.installSkillRepo(repo.url);
196
+ setOutcome({ ok: true, text: t("marketRepoInstalled") });
197
+ refreshSkills();
198
+ } catch (error) {
199
+ setOutcome({ ok: false, text: `${t("failed")}: ${String(error instanceof Error ? error.message : error)}` });
200
+ refreshSkills();
201
+ } finally {
202
+ setBusyId(null);
203
+ }
204
+ };
205
+ const doInstallMcp = async (server) => {
206
+ setBusyId(server.id);
207
+ setOutcome(null);
208
+ try {
209
+ await market.installMcp(server.id);
210
+ setOutcome({ ok: true, text: t("restartNeeded") });
211
+ refreshMcp();
212
+ } catch (error) {
213
+ setOutcome({ ok: false, text: `${t("failed")}: ${String(error instanceof Error ? error.message : error)}` });
214
+ refreshMcp();
215
+ } finally {
216
+ setBusyId(null);
217
+ }
218
+ };
219
+ const doUninstall = async () => {
220
+ if (confirmUninstall === null) return;
221
+ const target = confirmUninstall;
222
+ setBusyId(target.id);
223
+ try {
224
+ if (target.kind === "root") {
225
+ await market.removeRoot(target.id);
226
+ setOutcome({ ok: true, text: t("rootRemoved") });
227
+ refreshSkills();
228
+ } else {
229
+ await mcp.remove(target.id);
230
+ setOutcome({ ok: true, text: t("restartNeeded") });
231
+ refreshMcp();
232
+ }
233
+ } catch (error) {
234
+ setOutcome({ ok: false, text: `${t("failed")}: ${String(error instanceof Error ? error.message : error)}` });
235
+ } finally {
236
+ setBusyId(null);
237
+ setConfirmUninstall(null);
238
+ }
239
+ };
240
+ const pick = (base, zh2) => zhUi() && zh2 !== void 0 ? zh2 : base;
241
+ const title = (entry) => pick(entry.name, entry.nameZh);
242
+ const desc = (entry) => pick(entry.description, entry.descriptionZh);
243
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-section", children: [
244
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: CSS }),
245
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-head", children: [
246
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: t("marketTitle") }),
247
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-spacer" }),
248
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dpc-refresh", "aria-label": t("refresh"), title: t("refresh"), onClick: () => setReload((value) => value + 1), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconRefreshOutline14, { size: 14, "aria-hidden": "true" }) })
249
+ ] }),
250
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-intro", children: t("marketIntro") }),
251
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-segments", role: "tablist", "aria-label": t("marketTitle"), children: [
252
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", role: "tab", "aria-selected": segment === "skills", className: "dpc-segment", "data-active": segment === "skills" ? "true" : void 0, onClick: () => setSegment("skills"), children: t("marketSkills") }),
253
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", role: "tab", "aria-selected": segment === "mcp", className: "dpc-segment", "data-active": segment === "mcp" ? "true" : void 0, onClick: () => setSegment("mcp"), children: t("marketMcp") })
254
+ ] }),
255
+ outcome !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-banner", "data-kind": outcome.ok ? "ok" : "error", role: "status", children: [
256
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.StateDot, { state: outcome.ok ? "done" : "error", size: 10 }),
257
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dpc-bannerBody", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: outcome.text }) })
258
+ ] }),
259
+ source === "bundled" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-banner", "data-kind": "info", role: "status", children: [
260
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.StateDot, { state: "ongoing", size: 10 }),
261
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dpc-bannerBody", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: t("marketOffline") }) })
262
+ ] }),
263
+ segment === "skills" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
264
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-intro", children: t("marketSkillsIntro") }),
265
+ repos === null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-empty", children: t("loading") }),
266
+ repos !== null && repos.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-empty", children: t("marketEmpty") }),
267
+ repos !== null && repos.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: "dpc-cards", children: repos.map((repo) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: "dpc-card", children: [
268
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-cardTop", children: [
269
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { className: "dpc-cardTitle", title: repo.url, children: title(repo) }),
270
+ repo.skillCount !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-tag", children: t("marketSkillCount").replace("{n}", String(repo.skillCount)) }),
271
+ repo.installedId !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-tag", "data-kind": "source", children: t("marketInstalled") })
272
+ ] }),
273
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-cardDesc", children: desc(repo) }),
274
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-cardRow", children: [
275
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { className: "dpc-link", href: repo.homepage ?? repo.url, target: "_blank", rel: "noreferrer", children: t("marketHome") }),
276
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-spacer" }),
277
+ repo.installedId !== null ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "ghost", size: "sm", disabled: busyId !== null, onClick: () => setConfirmUninstall({ kind: "root", id: repo.installedId, name: title(repo) }), children: t("marketUninstall") }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", size: "sm", disabled: busyId !== null, onClick: () => void doInstallRepo(repo), children: busyId === repo.id ? t("marketInstalling") : t("marketInstall") })
278
+ ] })
279
+ ] }, repo.id)) })
280
+ ] }),
281
+ segment === "mcp" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
282
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-intro", children: t("marketMcpIntro") }),
283
+ servers === null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-empty", children: t("loading") }),
284
+ servers !== null && servers.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-empty", children: t("marketEmpty") }),
285
+ servers !== null && servers.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: "dpc-cards", children: servers.map((server) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: "dpc-card", children: [
286
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-cardTop", children: [
287
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { className: "dpc-cardTitle", title: server.id, children: title(server) }),
288
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-tag", children: server.transport }),
289
+ server.runtime !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-tag", children: server.runtime }),
290
+ server.installed && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-tag", "data-kind": "source", children: t("marketInstalled") })
291
+ ] }),
292
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-cardDesc", children: desc(server) }),
293
+ server.envKeys !== void 0 && server.envKeys.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "dpc-envHint", children: [
294
+ t("marketNeedsEnv"),
295
+ ": ",
296
+ server.envKeys.join(", ")
297
+ ] }),
298
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-cardRow", children: [
299
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { className: "dpc-link", href: server.homepage, target: "_blank", rel: "noreferrer", children: t("marketHome") }),
300
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-spacer" }),
301
+ server.installed ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "ghost", size: "sm", disabled: busyId !== null, onClick: () => void mcp.list().then(
302
+ (body) => {
303
+ const row = body.servers.find((item) => item.serverName === server.id);
304
+ if (row !== void 0) setConfirmUninstall({ kind: "mcp", id: row.id, name: server.id });
305
+ },
306
+ (error) => setOutcome({ ok: false, text: `${t("failed")}: ${String(error.message ?? error)}` })
307
+ ), children: t("marketUninstall") }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", size: "sm", disabled: busyId !== null, onClick: () => void doInstallMcp(server), children: busyId === server.id ? t("marketInstalling") : t("marketAdd") })
308
+ ] })
309
+ ] }, server.id)) })
310
+ ] }),
311
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
312
+ import_dsh_client_ui_primitives.Modal,
313
+ {
314
+ open: confirmUninstall !== null,
315
+ onClose: () => setConfirmUninstall(null),
316
+ title: t("marketUninstallConfirm"),
317
+ description: confirmUninstall?.name ?? void 0,
318
+ footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
319
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "ghost", onClick: () => setConfirmUninstall(null), children: t("cancel") }),
320
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", disabled: busyId !== null, onClick: () => void doUninstall(), children: t("marketUninstall") })
321
+ ] }),
322
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: confirmUninstall?.kind === "root" ? t("marketUninstallRootWarn") : t("removeWarn") })
323
+ }
324
+ )
325
+ ] });
326
+ }
327
+
328
+ // src/client/McpTab.tsx
329
+ var import_react2 = require("react");
330
+ var import_dsh_client_ui_primitives2 = require("@deepseek-ai/dsh-client-ui-primitives");
331
+ var import_jsx_runtime2 = require("react/jsx-runtime");
108
332
  function importGroups(items) {
109
333
  const label = (agent) => agent === "claude-code" ? "Claude Code" : agent === "codex" ? "Codex" : agent;
110
334
  const order = ["claude-code", "codex"];
@@ -136,20 +360,116 @@ function mapToPairs(map, separator) {
136
360
  if (map === void 0) return "";
137
361
  return Object.entries(map).map(([key, value]) => `${key}${separator}${value.includes("\n") ? JSON.stringify(value) : value}`).join("\n");
138
362
  }
363
+ function yamlScalar(value) {
364
+ return /^[A-Za-z0-9_./@-]+$/.test(value) ? value : JSON.stringify(value);
365
+ }
366
+ function mcpRowYaml(input) {
367
+ const name2 = input.serverName.trim() === "" ? "server-name" : input.serverName.trim();
368
+ const lines = [
369
+ "- insert:",
370
+ ` - id: mcp-${name2}`,
371
+ " name: '@deepseek-ai/dsh-mcp-client'",
372
+ " config:",
373
+ ` serverName: ${yamlScalar(name2)}`,
374
+ ` transport: ${input.transport}`
375
+ ];
376
+ if (input.transport === "stdio") {
377
+ lines.push(` command: ${yamlScalar(input.command ?? "")}`);
378
+ const args = input.args ?? [];
379
+ if (args.length > 0) {
380
+ lines.push(" args:");
381
+ for (const arg of args) lines.push(` - ${yamlScalar(arg)}`);
382
+ }
383
+ const env = input.env ?? {};
384
+ if (Object.keys(env).length > 0) {
385
+ lines.push(" env:");
386
+ for (const [key, value] of Object.entries(env)) lines.push(` ${yamlScalar(key)}: ${yamlScalar(value)}`);
387
+ }
388
+ } else {
389
+ lines.push(` url: ${yamlScalar(input.url ?? "")}`);
390
+ const headers = input.headers ?? {};
391
+ if (Object.keys(headers).length > 0) {
392
+ lines.push(" headers:");
393
+ for (const [key, value] of Object.entries(headers)) lines.push(` ${yamlScalar(key)}: ${yamlScalar(value)}`);
394
+ }
395
+ }
396
+ return lines.join("\n");
397
+ }
398
+ function mcpJsonExample(transport) {
399
+ const entry = transport === "stdio" ? {
400
+ command: "npx",
401
+ args: ["-y", "@example/mcp-server"],
402
+ env: { API_KEY: "value" }
403
+ } : {
404
+ type: "http",
405
+ url: "https://example.com/mcp",
406
+ headers: { Authorization: "Bearer <token>" }
407
+ };
408
+ return JSON.stringify({ mcpServers: { "server-name": entry } }, null, 2);
409
+ }
410
+ function parseMcpJson(text) {
411
+ let parsed;
412
+ try {
413
+ parsed = JSON.parse(text);
414
+ } catch {
415
+ return { error: "not valid JSON" };
416
+ }
417
+ if (typeof parsed !== "object" || parsed === null) return { error: "expected a JSON object" };
418
+ let record = parsed;
419
+ let nameFromWrapper;
420
+ const wrapped = record.mcpServers ?? record.mcp_servers ?? record.servers;
421
+ if (typeof wrapped === "object" && wrapped !== null && !Array.isArray(wrapped)) {
422
+ const first = Object.entries(wrapped)[0];
423
+ if (first === void 0) return { error: "mcpServers object is empty" };
424
+ nameFromWrapper = first[0];
425
+ if (typeof first[1] !== "object" || first[1] === null) return { error: "server entry is not an object" };
426
+ record = first[1];
427
+ }
428
+ const stringMap = (value) => {
429
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
430
+ const out = {};
431
+ for (const [key, entry] of Object.entries(value)) {
432
+ if (typeof entry === "string") out[key] = entry;
433
+ }
434
+ return Object.keys(out).length > 0 ? out : void 0;
435
+ };
436
+ const args = Array.isArray(record.args) && record.args.every((entry) => typeof entry === "string") ? record.args : void 0;
437
+ const command = typeof record.command === "string" ? record.command : void 0;
438
+ const url = typeof record.url === "string" ? record.url : void 0;
439
+ const declared = typeof record.type === "string" ? record.type : typeof record.transport === "string" ? record.transport : void 0;
440
+ const httpDeclared = declared === "http" || declared === "streamable-http" || declared === "sse";
441
+ const transport = command !== void 0 && !httpDeclared ? "stdio" : url !== void 0 ? "streamable-http" : httpDeclared ? "streamable-http" : "stdio";
442
+ if (transport === "stdio" && command === void 0) return { error: 'stdio config needs a "command" field' };
443
+ if (transport === "streamable-http" && url === void 0) return { error: 'http config needs a "url" field' };
444
+ const serverName = nameFromWrapper ?? (typeof record.serverName === "string" ? record.serverName : void 0) ?? (typeof record.name === "string" && record.name !== "@deepseek-ai/dsh-mcp-client" ? record.name : void 0);
445
+ const env = stringMap(record.env);
446
+ const headers = stringMap(record.headers);
447
+ return {
448
+ ...serverName !== void 0 ? { serverName } : {},
449
+ transport,
450
+ ...command !== void 0 ? { command } : {},
451
+ ...args !== void 0 ? { args } : {},
452
+ ...env !== void 0 ? { env } : {},
453
+ ...url !== void 0 ? { url } : {},
454
+ ...headers !== void 0 ? { headers } : {}
455
+ };
456
+ }
139
457
  function McpTab(props) {
140
458
  const { t, injected } = props;
141
- const [servers, setServers] = (0, import_react.useState)(null);
142
- const [editor, setEditor] = (0, import_react.useState)(null);
143
- const [confirmId, setConfirmId] = (0, import_react.useState)(null);
144
- const [importOpen, setImportOpen] = (0, import_react.useState)(false);
145
- const [importItems, setImportItems] = (0, import_react.useState)(null);
146
- const [busy, setBusy] = (0, import_react.useState)(false);
147
- const [pending, setPending] = (0, import_react.useState)(false);
148
- const [restartConfirm, setRestartConfirm] = (0, import_react.useState)(false);
149
- const [restarting, setRestarting] = (0, import_react.useState)(false);
150
- const [outcome, setOutcome] = (0, import_react.useState)(null);
151
- const [formError, setFormError] = (0, import_react.useState)(null);
152
- const [reload, setReload] = (0, import_react.useState)(0);
459
+ const [servers, setServers] = (0, import_react2.useState)(null);
460
+ const [editor, setEditor] = (0, import_react2.useState)(null);
461
+ const [confirmId, setConfirmId] = (0, import_react2.useState)(null);
462
+ const [importOpen, setImportOpen] = (0, import_react2.useState)(false);
463
+ const [importItems, setImportItems] = (0, import_react2.useState)(null);
464
+ const [busy, setBusy] = (0, import_react2.useState)(false);
465
+ const [pending, setPending] = (0, import_react2.useState)(false);
466
+ const [restartConfirm, setRestartConfirm] = (0, import_react2.useState)(false);
467
+ const [restarting, setRestarting] = (0, import_react2.useState)(false);
468
+ const [outcome, setOutcome] = (0, import_react2.useState)(null);
469
+ const [formError, setFormError] = (0, import_react2.useState)(null);
470
+ const [reload, setReload] = (0, import_react2.useState)(0);
471
+ const [pasteJson, setPasteJson] = (0, import_react2.useState)("");
472
+ const [pasteError, setPasteError] = (0, import_react2.useState)(null);
153
473
  const openImport = async () => {
154
474
  setImportOpen(true);
155
475
  setImportItems(null);
@@ -187,7 +507,7 @@ function McpTab(props) {
187
507
  setReload((value) => value + 1);
188
508
  if (showPending) setPending(true);
189
509
  };
190
- (0, import_react.useEffect)(() => {
510
+ (0, import_react2.useEffect)(() => {
191
511
  let current = true;
192
512
  void injected.list().then(
193
513
  (body) => {
@@ -206,10 +526,14 @@ function McpTab(props) {
206
526
  }, [injected, reload, t]);
207
527
  const openCreate = () => {
208
528
  setFormError(null);
529
+ setPasteError(null);
530
+ setPasteJson("");
209
531
  setEditor({ id: "", serverName: "", transport: "stdio", command: "", args: "", env: "", url: "", headers: "" });
210
532
  };
211
533
  const openEdit = (row) => {
212
534
  setFormError(null);
535
+ setPasteError(null);
536
+ setPasteJson("");
213
537
  setEditor({
214
538
  id: row.id,
215
539
  serverName: row.serverName,
@@ -221,6 +545,34 @@ function McpTab(props) {
221
545
  headers: mapToPairs(row.headers, ":")
222
546
  });
223
547
  };
548
+ const doPasteFill = () => {
549
+ if (editor === null || pasteJson.trim() === "") return;
550
+ const parsed = parseMcpJson(pasteJson);
551
+ if ("error" in parsed) {
552
+ setPasteError(parsed.error);
553
+ return;
554
+ }
555
+ const lockIdentity = editor.id !== "";
556
+ if (lockIdentity && parsed.transport !== editor.transport) {
557
+ setPasteError(t("pasteTransportMismatch"));
558
+ return;
559
+ }
560
+ setPasteError(null);
561
+ setFormError(null);
562
+ setEditor({
563
+ ...editor,
564
+ ...parsed.serverName !== void 0 && !lockIdentity ? { serverName: parsed.serverName } : {},
565
+ ...!lockIdentity ? { transport: parsed.transport } : {},
566
+ ...parsed.transport === "stdio" ? {
567
+ command: parsed.command ?? editor.command,
568
+ args: (parsed.args ?? []).join("\n"),
569
+ env: mapToPairs(parsed.env, "=")
570
+ } : {
571
+ url: parsed.url ?? editor.url,
572
+ headers: mapToPairs(parsed.headers, ":")
573
+ }
574
+ });
575
+ };
224
576
  const doSave = async () => {
225
577
  if (editor === null) return;
226
578
  setBusy(true);
@@ -295,67 +647,67 @@ function McpTab(props) {
295
647
  };
296
648
  window.setTimeout(poll, 3e3);
297
649
  };
298
- const restartBanner = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-banner", "data-kind": "info", role: "status", children: [
299
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.StateDot, { state: "ongoing", size: 10 }),
300
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-bannerBody", children: [
301
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: restarting ? t("restarting") : t("restartNeeded") }),
302
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-bannerHint", children: restarting ? !injected.desktop && t("restartPortHint") : injected.desktop ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
650
+ const restartBanner = /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-banner", "data-kind": "info", role: "status", children: [
651
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.StateDot, { state: "ongoing", size: 10 }),
652
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-bannerBody", children: [
653
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: restarting ? t("restarting") : t("restartNeeded") }),
654
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dpc-bannerHint", children: restarting ? !injected.desktop && t("restartPortHint") : injected.desktop ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
303
655
  t("restartDesktopHint"),
304
656
  " ",
305
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", size: "sm", onClick: () => setRestartConfirm(true), children: t("restartNow") })
657
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "outline", size: "sm", onClick: () => setRestartConfirm(true), children: t("restartNow") })
306
658
  ] }) : t("restartOtherHint") })
307
659
  ] })
308
660
  ] });
309
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-section", children: [
310
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: CSS }),
311
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-head", children: [
312
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconApiOutline14, { "aria-hidden": "true" }),
313
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: t("mcpTitle") }),
314
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-spacer" }),
315
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "ghost", size: "sm", disabled: restarting, onClick: () => setRestartConfirm(true), children: t("restart") }),
316
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "ghost", size: "sm", onClick: () => void openImport(), children: t("importServers") }),
317
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", size: "sm", onClick: openCreate, children: t("addServer") })
661
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-section", children: [
662
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: CSS }),
663
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-head", children: [
664
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.IconApiOutline14, { "aria-hidden": "true" }),
665
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { children: t("mcpTitle") }),
666
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dpc-spacer" }),
667
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "ghost", size: "sm", disabled: restarting, onClick: () => setRestartConfirm(true), children: t("restart") }),
668
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "ghost", size: "sm", onClick: () => void openImport(), children: t("importServers") }),
669
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "primary", size: "sm", onClick: openCreate, children: t("addServer") })
318
670
  ] }),
319
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-intro", children: t("mcpIntro") }),
320
- outcome !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-banner", "data-kind": outcome.ok ? "ok" : "error", role: "status", children: [
321
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.StateDot, { state: outcome.ok ? "done" : "error", size: 10 }),
322
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dpc-bannerBody", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: outcome.text }) })
671
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dpc-intro", children: t("mcpIntro") }),
672
+ outcome !== null && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-banner", "data-kind": outcome.ok ? "ok" : "error", role: "status", children: [
673
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.StateDot, { state: outcome.ok ? "done" : "error", size: 10 }),
674
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dpc-bannerBody", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: outcome.text }) })
323
675
  ] }),
324
676
  (pending || restarting) && restartBanner,
325
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-listHead", children: [
326
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: t("mcpTab") }),
327
- servers !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-count", children: servers.length }),
328
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-spacer" }),
329
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dpc-refresh", "aria-label": t("view"), title: t("view"), disabled: busy, onClick: () => setReload((value) => value + 1), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconRefreshOutline14, { size: 14, "aria-hidden": "true" }) })
677
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-listHead", children: [
678
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { children: t("mcpTab") }),
679
+ servers !== null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dpc-count", children: servers.length }),
680
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dpc-spacer" }),
681
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dpc-refresh", "aria-label": t("view"), title: t("view"), disabled: busy, onClick: () => setReload((value) => value + 1), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.IconRefreshOutline14, { size: 14, "aria-hidden": "true" }) })
330
682
  ] }),
331
- servers === null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-empty", children: t("loading") }),
332
- servers !== null && servers.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-empty", children: t("emptyMcp") }),
333
- servers !== null && servers.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: "dpc-cards", children: servers.map((row) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: "dpc-card", children: [
334
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-cardTop", children: [
335
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { className: "dpc-cardTitle", title: row.id, children: row.serverName }),
336
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-tag", children: row.transport }),
337
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-tag", "data-kind": row.disabled ? "off" : void 0, children: row.disabled ? t("disabled") : t("enabled") })
683
+ servers === null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dpc-empty", children: t("loading") }),
684
+ servers !== null && servers.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dpc-empty", children: t("emptyMcp") }),
685
+ servers !== null && servers.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ul", { className: "dpc-cards", children: servers.map((row) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { className: "dpc-card", children: [
686
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-cardTop", children: [
687
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { className: "dpc-cardTitle", title: row.id, children: row.serverName }),
688
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dpc-tag", children: row.transport }),
689
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dpc-tag", "data-kind": row.disabled ? "off" : void 0, children: row.disabled ? t("disabled") : t("enabled") })
338
690
  ] }),
339
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-cardDesc", children: row.transport === "stdio" ? `${row.command ?? ""} ${(row.args ?? []).join(" ")}` : row.url ?? "" }),
340
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-cardRow", children: [
341
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-spacer" }),
342
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "ghost", size: "sm", disabled: busy, onClick: () => void doToggle(row), children: t("toggle") }),
343
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "ghost", size: "sm", disabled: busy, onClick: () => openEdit(row), children: t("edit") }),
344
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "ghost", size: "sm", disabled: busy, onClick: () => setConfirmId(row.id), children: t("delete") })
691
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dpc-cardDesc", children: row.transport === "stdio" ? `${row.command ?? ""} ${(row.args ?? []).join(" ")}` : row.url ?? "" }),
692
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-cardRow", children: [
693
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dpc-spacer" }),
694
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "ghost", size: "sm", disabled: busy, onClick: () => void doToggle(row), children: t("toggle") }),
695
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "ghost", size: "sm", disabled: busy, onClick: () => openEdit(row), children: t("edit") }),
696
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "ghost", size: "sm", disabled: busy, onClick: () => setConfirmId(row.id), children: t("delete") })
345
697
  ] })
346
698
  ] }, row.id)) }),
347
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
348
- import_dsh_client_ui_primitives.Modal,
699
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
700
+ import_dsh_client_ui_primitives2.Modal,
349
701
  {
350
702
  open: editor !== null,
351
703
  onClose: () => setEditor(null),
352
704
  title: editor !== null && editor.id !== "" ? t("editServer") : t("addServer"),
353
705
  className: "dpc-modalForm",
354
706
  contentClassName: "dpc-modalScroll",
355
- children: editor !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-form", children: [
356
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "dpc-label", children: [
357
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: t("serverName") }),
358
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
707
+ children: editor !== null && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-form", children: [
708
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "dpc-label", children: [
709
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: t("serverName") }),
710
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
359
711
  "input",
360
712
  {
361
713
  className: "dpc-input",
@@ -365,9 +717,9 @@ function McpTab(props) {
365
717
  }
366
718
  )
367
719
  ] }),
368
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "dpc-label", children: [
369
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: t("transport") }),
370
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
720
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "dpc-label", children: [
721
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: t("transport") }),
722
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
371
723
  "select",
372
724
  {
373
725
  className: "dpc-select",
@@ -375,91 +727,126 @@ function McpTab(props) {
375
727
  disabled: editor.id !== "",
376
728
  onChange: (event) => setEditor({ ...editor, transport: event.target.value }),
377
729
  children: [
378
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "stdio", children: t("transportStdio") }),
379
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "streamable-http", children: t("transportHttp") })
730
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "stdio", children: t("transportStdio") }),
731
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "streamable-http", children: t("transportHttp") })
380
732
  ]
381
733
  }
382
734
  )
383
735
  ] }),
384
- editor.transport === "stdio" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
385
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "dpc-label", children: [
386
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: t("command") }),
387
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { className: "dpc-input", value: editor.command, onChange: (event) => setEditor({ ...editor, command: event.target.value }) })
736
+ editor.transport === "stdio" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
737
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "dpc-label", children: [
738
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: t("command") }),
739
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { className: "dpc-input", value: editor.command, onChange: (event) => setEditor({ ...editor, command: event.target.value }) })
388
740
  ] }),
389
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "dpc-label", children: [
390
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: t("args") }),
391
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("textarea", { className: "dpc-textarea", "data-short": "true", value: editor.args, onChange: (event) => setEditor({ ...editor, args: event.target.value }) })
741
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "dpc-label", children: [
742
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: t("args") }),
743
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("textarea", { className: "dpc-textarea", "data-short": "true", value: editor.args, onChange: (event) => setEditor({ ...editor, args: event.target.value }) })
392
744
  ] }),
393
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "dpc-label", children: [
394
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: t("envPairs") }),
395
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("textarea", { className: "dpc-textarea", "data-short": "true", value: editor.env, onChange: (event) => setEditor({ ...editor, env: event.target.value }) })
745
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "dpc-label", children: [
746
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: t("envPairs") }),
747
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("textarea", { className: "dpc-textarea", "data-short": "true", value: editor.env, onChange: (event) => setEditor({ ...editor, env: event.target.value }) })
396
748
  ] })
397
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
398
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "dpc-label", children: [
399
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: t("url") }),
400
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { className: "dpc-input", value: editor.url, onChange: (event) => setEditor({ ...editor, url: event.target.value }) })
749
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
750
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "dpc-label", children: [
751
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: t("url") }),
752
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { className: "dpc-input", value: editor.url, onChange: (event) => setEditor({ ...editor, url: event.target.value }) })
401
753
  ] }),
402
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "dpc-label", children: [
403
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: t("headersPairs") }),
404
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("textarea", { className: "dpc-textarea", "data-short": "true", value: editor.headers, onChange: (event) => setEditor({ ...editor, headers: event.target.value }) })
754
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "dpc-label", children: [
755
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: t("headersPairs") }),
756
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("textarea", { className: "dpc-textarea", "data-short": "true", value: editor.headers, onChange: (event) => setEditor({ ...editor, headers: event.target.value }) })
757
+ ] })
758
+ ] }),
759
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("details", { className: "dpc-format", children: [
760
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("summary", { children: t("formatTitle") }),
761
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-form", children: [
762
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "dpc-label", children: [
763
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: t("formatPaste") }),
764
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
765
+ "textarea",
766
+ {
767
+ className: "dpc-textarea",
768
+ "data-short": "true",
769
+ placeholder: '{\n "mcpServers": { "name": { "command": "npx", "args": ["\u2026"] } } }\n',
770
+ value: pasteJson,
771
+ onChange: (event) => setPasteJson(event.target.value)
772
+ }
773
+ )
774
+ ] }),
775
+ pasteError !== null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dpc-formError", children: pasteError }),
776
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dpc-cardRow", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "outline", size: "sm", disabled: pasteJson.trim() === "", onClick: doPasteFill, children: t("formatFill") }) }),
777
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dpc-formatHint", children: t("formatYamlHint") }),
778
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("pre", { className: "dpc-code", children: mcpRowYaml({
779
+ serverName: editor.serverName,
780
+ transport: editor.transport,
781
+ ...editor.transport === "stdio" ? {
782
+ command: editor.command.trim(),
783
+ args: editor.args.split(/\r?\n/).map((line) => line.trim()).filter((line) => line !== ""),
784
+ env: parsePairs(editor.env, "=")
785
+ } : {
786
+ url: editor.url.trim(),
787
+ headers: parsePairs(editor.headers, ":")
788
+ }
789
+ }) }),
790
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dpc-formatHint", children: t("formatJsonHint") }),
791
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("pre", { className: "dpc-code", children: mcpJsonExample(editor.transport) })
405
792
  ] })
406
793
  ] }),
407
- formError !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-formError", children: formError }),
408
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-cardRow", children: [
409
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-spacer" }),
410
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "ghost", onClick: () => setEditor(null), children: t("cancel") }),
411
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", disabled: busy, onClick: () => void doSave(), children: t("save") })
794
+ formError !== null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dpc-formError", children: formError }),
795
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-cardRow", children: [
796
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dpc-spacer" }),
797
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "ghost", onClick: () => setEditor(null), children: t("cancel") }),
798
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "primary", disabled: busy, onClick: () => void doSave(), children: t("save") })
412
799
  ] })
413
800
  ] })
414
801
  }
415
802
  ),
416
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
417
- import_dsh_client_ui_primitives.Modal,
803
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
804
+ import_dsh_client_ui_primitives2.Modal,
418
805
  {
419
806
  open: confirmId !== null,
420
807
  onClose: () => setConfirmId(null),
421
808
  title: t("confirmRemove"),
422
809
  description: confirmId ?? void 0,
423
- footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
424
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "ghost", onClick: () => setConfirmId(null), children: t("cancel") }),
425
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", disabled: busy, onClick: () => void doRemove(), children: t("delete") })
810
+ footer: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
811
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "ghost", onClick: () => setConfirmId(null), children: t("cancel") }),
812
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "primary", disabled: busy, onClick: () => void doRemove(), children: t("delete") })
426
813
  ] }),
427
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: t("removeWarn") })
814
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: t("removeWarn") })
428
815
  }
429
816
  ),
430
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
431
- import_dsh_client_ui_primitives.Modal,
817
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
818
+ import_dsh_client_ui_primitives2.Modal,
432
819
  {
433
820
  open: restartConfirm,
434
821
  onClose: () => setRestartConfirm(false),
435
822
  title: t("restartConfirmTitle"),
436
- footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
437
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "ghost", onClick: () => setRestartConfirm(false), children: t("cancel") }),
438
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", onClick: doRestart, children: t("restartNow") })
823
+ footer: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
824
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "ghost", onClick: () => setRestartConfirm(false), children: t("cancel") }),
825
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "primary", onClick: doRestart, children: t("restartNow") })
439
826
  ] }),
440
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: t("restartConfirmBody") })
827
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: t("restartConfirmBody") })
441
828
  }
442
829
  ),
443
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
444
- import_dsh_client_ui_primitives.Modal,
830
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
831
+ import_dsh_client_ui_primitives2.Modal,
445
832
  {
446
833
  open: importOpen,
447
834
  onClose: () => setImportOpen(false),
448
835
  title: t("importServers"),
449
836
  className: "dpc-modalWide",
450
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-form", children: [
451
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-intro", style: { margin: 0 }, children: t("importIntro") }),
452
- importItems === null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-empty", children: t("loading") }),
453
- importItems !== null && importItems.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-empty", children: t("importEmpty") }),
454
- importItems !== null && importItems.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dpc-importScroll", children: importGroups(importItems).map((group) => {
837
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-form", children: [
838
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dpc-intro", style: { margin: 0 }, children: t("importIntro") }),
839
+ importItems === null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dpc-empty", children: t("loading") }),
840
+ importItems !== null && importItems.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dpc-empty", children: t("importEmpty") }),
841
+ importItems !== null && importItems.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dpc-importScroll", children: importGroups(importItems).map((group) => {
455
842
  const selectable = group.items.filter(({ item }) => !item.existing).map(({ index }) => index);
456
843
  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)(
844
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("section", { className: "dpc-importGroup", children: [
845
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-importHead", children: [
846
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dpc-tag", "data-kind": "source", children: group.label }),
847
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dpc-importCount", children: group.items.length }),
848
+ selectable.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "dpc-importAll", children: [
849
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
463
850
  "input",
464
851
  {
465
852
  type: "checkbox",
@@ -474,12 +861,12 @@ function McpTab(props) {
474
861
  t("importSelectAll")
475
862
  ] })
476
863
  ] }),
477
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: "dpc-cards", style: { gridTemplateColumns: "minmax(0, 1fr)" }, children: group.items.map(({ item, index }) => {
864
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ul", { className: "dpc-cards", style: { gridTemplateColumns: "minmax(0, 1fr)" }, children: group.items.map(({ item, index }) => {
478
865
  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)(
866
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { className: "dpc-card", style: { opacity: item.existing ? 0.55 : 1 }, children: [
867
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-cardTop", children: [
868
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { style: { display: "inline-flex", alignItems: "center", gap: 8, minWidth: 0, cursor: item.existing ? "default" : "pointer" }, children: [
869
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
483
870
  "input",
484
871
  {
485
872
  type: "checkbox",
@@ -492,22 +879,22 @@ function McpTab(props) {
492
879
  }
493
880
  }
494
881
  ),
495
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { className: "dpc-cardTitle", title: item.server.name, children: item.server.name })
882
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { className: "dpc-cardTitle", title: item.server.name, children: item.server.name })
496
883
  ] }),
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") })
884
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dpc-tag", children: item.server.transport }),
885
+ item.existing && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dpc-tag", children: t("importExisting") })
499
886
  ] }),
500
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-cardDesc", title: command, children: command })
887
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dpc-cardDesc", title: command, children: command })
501
888
  ] }, `${item.server.agent}/${item.server.name}`);
502
889
  }) })
503
890
  ] }, group.agent);
504
891
  }) }),
505
- formError !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-formError", children: formError }),
506
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-cardRow", children: [
507
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-spacer" }),
508
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "ghost", onClick: () => setImportOpen(false), children: t("cancel") }),
509
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
510
- import_dsh_client_ui_primitives.Button,
892
+ formError !== null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dpc-formError", children: formError }),
893
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-cardRow", children: [
894
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dpc-spacer" }),
895
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "ghost", onClick: () => setImportOpen(false), children: t("cancel") }),
896
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
897
+ import_dsh_client_ui_primitives2.Button,
511
898
  {
512
899
  variant: "primary",
513
900
  disabled: busy || importItems === null || !importItems.some((item) => item.checked && !item.existing),
@@ -523,9 +910,9 @@ function McpTab(props) {
523
910
  }
524
911
 
525
912
  // src/client/SkillsTab.tsx
526
- var import_react2 = require("react");
527
- var import_dsh_client_ui_primitives2 = require("@deepseek-ai/dsh-client-ui-primitives");
528
- var import_jsx_runtime2 = require("react/jsx-runtime");
913
+ var import_react3 = require("react");
914
+ var import_dsh_client_ui_primitives3 = require("@deepseek-ai/dsh-client-ui-primitives");
915
+ var import_jsx_runtime3 = require("react/jsx-runtime");
529
916
  var SOURCE_KEYS = {
530
917
  "project-dsh": "sourceProjectDsh",
531
918
  "project-agents": "sourceProjectAgents",
@@ -535,16 +922,29 @@ var SOURCE_KEYS = {
535
922
  bundled: "sourceBundled",
536
923
  custom: "sourceCustom"
537
924
  };
925
+ function policyTag(skill) {
926
+ const { modelInvocable, userInvocable } = skill.invocation;
927
+ if (!modelInvocable && !userInvocable) return { key: "skillDisabled", off: true };
928
+ if (!modelInvocable) return { key: "skillUserOnly", off: false };
929
+ if (!userInvocable) return { key: "skillModelOnly", off: false };
930
+ return { off: false };
931
+ }
538
932
  function SkillsTab(props) {
539
933
  const { t, injected } = props;
540
- const [skills, setSkills] = (0, import_react2.useState)(null);
541
- const [editor, setEditor] = (0, import_react2.useState)(null);
542
- const [confirmName, setConfirmName] = (0, import_react2.useState)(null);
543
- const [busy, setBusy] = (0, import_react2.useState)(false);
544
- const [outcome, setOutcome] = (0, import_react2.useState)(null);
545
- const [formError, setFormError] = (0, import_react2.useState)(null);
546
- const [reload, setReload] = (0, import_react2.useState)(0);
547
- (0, import_react2.useEffect)(() => {
934
+ const [skills, setSkills] = (0, import_react3.useState)(null);
935
+ const [editor, setEditor] = (0, import_react3.useState)(null);
936
+ const [confirmName, setConfirmName] = (0, import_react3.useState)(null);
937
+ const [busy, setBusy] = (0, import_react3.useState)(false);
938
+ const [outcome, setOutcome] = (0, import_react3.useState)(null);
939
+ const [formError, setFormError] = (0, import_react3.useState)(null);
940
+ const [reload, setReload] = (0, import_react3.useState)(0);
941
+ const [query, setQuery] = (0, import_react3.useState)("");
942
+ const [sourceFilter, setSourceFilter] = (0, import_react3.useState)("all");
943
+ const [roots, setRoots] = (0, import_react3.useState)(null);
944
+ const [addRoot, setAddRoot] = (0, import_react3.useState)(null);
945
+ const [rootBusy, setRootBusy] = (0, import_react3.useState)(false);
946
+ const [confirmRootId, setConfirmRootId] = (0, import_react3.useState)(null);
947
+ (0, import_react3.useEffect)(() => {
548
948
  let current = true;
549
949
  void injected.list().then(
550
950
  (body) => {
@@ -557,6 +957,14 @@ function SkillsTab(props) {
557
957
  }
558
958
  }
559
959
  );
960
+ void injected.roots().then(
961
+ (body) => {
962
+ if (current) setRoots(body.roots);
963
+ },
964
+ () => {
965
+ if (current) setRoots([]);
966
+ }
967
+ );
560
968
  return () => {
561
969
  current = false;
562
970
  };
@@ -639,54 +1047,225 @@ function SkillsTab(props) {
639
1047
  setConfirmName(null);
640
1048
  }
641
1049
  };
1050
+ const doToggle = async (skill) => {
1051
+ const enabled = skill.invocation.modelInvocable || skill.invocation.userInvocable;
1052
+ setBusy(true);
1053
+ try {
1054
+ await injected.policy(skill.name, !enabled);
1055
+ setOutcome({ ok: true, text: !enabled ? t("skillEnabled") : t("skillDisabledMsg") });
1056
+ refreshUntil((list) => {
1057
+ const row = list.find((item) => item.name === skill.name);
1058
+ return row !== void 0 && row.invocation.modelInvocable === !enabled && row.invocation.userInvocable === !enabled;
1059
+ });
1060
+ } catch (error) {
1061
+ setOutcome({ ok: false, text: `${t("failed")}: ${String(error instanceof Error ? error.message : error)}` });
1062
+ } finally {
1063
+ setBusy(false);
1064
+ }
1065
+ };
1066
+ const doOpen = async (target) => {
1067
+ try {
1068
+ await injected.open(target);
1069
+ } catch (error) {
1070
+ setOutcome({ ok: false, text: `${t("failed")}: ${String(error instanceof Error ? error.message : error)}` });
1071
+ }
1072
+ };
1073
+ const doAddRoot = async () => {
1074
+ if (addRoot === null || addRoot.value.trim() === "") return;
1075
+ setRootBusy(true);
1076
+ setFormError(null);
1077
+ try {
1078
+ await injected.addRoot(addRoot.kind === "local" ? { kind: "local", path: addRoot.value.trim() } : { kind: "git", url: addRoot.value.trim() });
1079
+ setOutcome({ ok: true, text: t("rootAdded") });
1080
+ setAddRoot(null);
1081
+ void injected.roots().then(
1082
+ (body) => setRoots(body.roots),
1083
+ () => setRoots([])
1084
+ );
1085
+ for (let tick = 0; tick < 4; tick++) {
1086
+ await new Promise((resolve) => setTimeout(resolve, 1500));
1087
+ void injected.list().then(
1088
+ (body) => setSkills(body.skills),
1089
+ () => void 0
1090
+ );
1091
+ }
1092
+ } catch (error) {
1093
+ setFormError(String(error instanceof Error ? error.message : error));
1094
+ } finally {
1095
+ setRootBusy(false);
1096
+ }
1097
+ };
1098
+ const doRemoveRoot = async () => {
1099
+ if (confirmRootId === null) return;
1100
+ setRootBusy(true);
1101
+ try {
1102
+ await injected.removeRoot(confirmRootId);
1103
+ setOutcome({ ok: true, text: t("rootRemoved") });
1104
+ setRoots(null);
1105
+ void injected.roots().then(
1106
+ (body) => setRoots(body.roots),
1107
+ () => setRoots([])
1108
+ );
1109
+ setReload((value) => value + 1);
1110
+ } catch (error) {
1111
+ setOutcome({ ok: false, text: `${t("failed")}: ${String(error instanceof Error ? error.message : error)}` });
1112
+ } finally {
1113
+ setRootBusy(false);
1114
+ setConfirmRootId(null);
1115
+ }
1116
+ };
642
1117
  const readOnly = editor?.mode === "view";
643
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-section", children: [
644
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: CSS }),
645
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-head", children: [
646
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.IconSkillOutline16, { "aria-hidden": "true" }),
647
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { children: t("skillsTitle") }),
648
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dpc-spacer" }),
649
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "primary", size: "sm", onClick: openCreate, children: t("newSkill") })
1118
+ const needle = query.trim().toLowerCase();
1119
+ const filtered = skills === null ? [] : skills.filter((skill) => (sourceFilter === "all" || skill.source === sourceFilter) && (needle === "" || skill.name.toLowerCase().includes(needle) || skill.description.toLowerCase().includes(needle)));
1120
+ const sources = skills === null ? [] : [...new Set(skills.map((skill) => skill.source))];
1121
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dpc-section", children: [
1122
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("style", { children: CSS }),
1123
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dpc-head", children: [
1124
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives3.IconSkillOutline16, { "aria-hidden": "true" }),
1125
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { children: t("skillsTitle") }),
1126
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dpc-spacer" }),
1127
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives3.Button, { variant: "ghost", size: "sm", onClick: () => void doOpen({ target: "user-skills" }), children: t("openUserSkills") }),
1128
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives3.Button, { variant: "primary", size: "sm", onClick: openCreate, children: t("newSkill") })
650
1129
  ] }),
651
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dpc-intro", children: t("skillsIntro") }),
652
- outcome !== null && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-banner", "data-kind": outcome.ok ? "ok" : "error", role: "status", children: [
653
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.StateDot, { state: outcome.ok ? "done" : "error", size: 10 }),
654
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dpc-bannerBody", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: outcome.text }) })
1130
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "dpc-intro", children: t("skillsIntro") }),
1131
+ outcome !== null && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dpc-banner", "data-kind": outcome.ok ? "ok" : "error", role: "status", children: [
1132
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives3.StateDot, { state: outcome.ok ? "done" : "error", size: 10 }),
1133
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dpc-bannerBody", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: outcome.text }) })
655
1134
  ] }),
656
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-listHead", children: [
657
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { children: t("skillsTab") }),
658
- skills !== null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dpc-count", children: skills.length }),
659
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dpc-spacer" }),
660
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dpc-refresh", "aria-label": t("view"), title: t("view"), disabled: busy, onClick: () => setReload((value) => value + 1), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.IconRefreshOutline14, { size: 14, "aria-hidden": "true" }) })
1135
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dpc-listHead", children: [
1136
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { children: t("skillsTab") }),
1137
+ skills !== null && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dpc-count", children: [
1138
+ filtered.length,
1139
+ "/",
1140
+ skills.length
1141
+ ] }),
1142
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dpc-spacer" }),
1143
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1144
+ "input",
1145
+ {
1146
+ className: "dpc-search",
1147
+ type: "search",
1148
+ placeholder: t("searchSkills"),
1149
+ "aria-label": t("searchSkills"),
1150
+ value: query,
1151
+ onChange: (event) => setQuery(event.target.value)
1152
+ }
1153
+ ),
1154
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "dpc-refresh", "aria-label": t("refresh"), title: t("refresh"), disabled: busy, onClick: () => setReload((value) => value + 1), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives3.IconRefreshOutline14, { size: 14, "aria-hidden": "true" }) })
661
1155
  ] }),
662
- skills === null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dpc-empty", children: t("loading") }),
663
- skills !== null && skills.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dpc-empty", children: t("emptySkills") }),
664
- skills !== null && skills.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ul", { className: "dpc-cards", children: skills.map((skill) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { className: "dpc-card", children: [
665
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-cardTop", children: [
666
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { className: "dpc-cardTitle", title: skill.name, children: skill.name }),
667
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dpc-tag", "data-kind": "source", children: t(SOURCE_KEYS[skill.source] ?? "sourceCustom") })
1156
+ sources.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dpc-chips", role: "group", "aria-label": t("source"), children: [{ id: "all", label: t("filterAll") }, ...sources.map((source) => ({ id: source, label: t(SOURCE_KEYS[source] ?? "sourceCustom") }))].map((chip) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1157
+ "button",
1158
+ {
1159
+ type: "button",
1160
+ className: "dpc-chip",
1161
+ "data-active": sourceFilter === chip.id ? "true" : void 0,
1162
+ onClick: () => setSourceFilter(chip.id),
1163
+ children: chip.label
1164
+ },
1165
+ chip.id
1166
+ )) }),
1167
+ skills === null && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "dpc-empty", children: t("loading") }),
1168
+ skills !== null && filtered.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "dpc-empty", children: skills.length === 0 ? t("emptySkills") : t("noMatch") }),
1169
+ skills !== null && filtered.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { className: "dpc-cards", children: filtered.map((skill) => {
1170
+ const tag = policyTag(skill);
1171
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("li", { className: "dpc-card", children: [
1172
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dpc-cardTop", children: [
1173
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { className: "dpc-cardTitle", title: skill.name, children: skill.name }),
1174
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dpc-tag", "data-kind": "source", children: t(SOURCE_KEYS[skill.source] ?? "sourceCustom") }),
1175
+ tag.key !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dpc-tag", "data-kind": tag.off ? "off" : void 0, children: t(tag.key) })
1176
+ ] }),
1177
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "dpc-cardDesc", title: skill.description, children: skill.description }),
1178
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dpc-cardRow", children: [
1179
+ skill.policyEditable && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1180
+ "button",
1181
+ {
1182
+ type: "button",
1183
+ className: "dpc-switch",
1184
+ role: "switch",
1185
+ "aria-checked": skill.invocation.modelInvocable || skill.invocation.userInvocable,
1186
+ "aria-label": t("toggleSkill"),
1187
+ title: t("toggleSkillHint"),
1188
+ disabled: busy,
1189
+ onClick: () => void doToggle(skill),
1190
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dpc-switchKnob" })
1191
+ }
1192
+ ),
1193
+ skill.dir !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "dpc-link", onClick: () => void doOpen({ target: "skill", name: skill.name }), children: t("openFolder") }),
1194
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dpc-spacer" }),
1195
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives3.Button, { variant: "ghost", size: "sm", disabled: busy, onClick: () => void openExisting(skill), children: skill.editable ? t("edit") : t("view") }),
1196
+ skill.editable && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives3.Button, { variant: "ghost", size: "sm", disabled: busy, onClick: () => setConfirmName(skill.name), children: t("delete") })
1197
+ ] })
1198
+ ] }, `${skill.source}/${skill.name}`);
1199
+ }) }),
1200
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dpc-listHead dpc-rootsHead", children: [
1201
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { children: t("rootsTitle") }),
1202
+ roots !== null && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dpc-count", children: roots.length }),
1203
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dpc-spacer" }),
1204
+ addRoot === null && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives3.Button, { variant: "ghost", size: "sm", onClick: () => setAddRoot({ kind: "git", value: "" }), children: t("addRoot") })
1205
+ ] }),
1206
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "dpc-intro", children: t("rootsIntro") }),
1207
+ roots === null && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "dpc-empty", children: t("loading") }),
1208
+ roots !== null && roots.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "dpc-empty", children: t("emptyRoots") }),
1209
+ roots !== null && roots.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { className: "dpc-roots", children: roots.map((root) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("li", { className: "dpc-root", children: [
1210
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dpc-tag", "data-kind": "source", children: root.kind === "git" ? "GitHub" : t("rootLocal") }),
1211
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { className: "dpc-rootLabel", title: root.kind === "git" ? root.url : root.path, children: root.label }),
1212
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dpc-rootPath", children: root.kind === "git" ? root.url : root.path }),
1213
+ !root.live && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dpc-tag", "data-kind": "off", children: t("rootStale") }),
1214
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dpc-spacer" }),
1215
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "dpc-link", onClick: () => void doOpen({ target: "root", id: root.id }), children: t("openFolder") }),
1216
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives3.Button, { variant: "ghost", size: "sm", disabled: rootBusy, onClick: () => setConfirmRootId(root.id), children: t("delete") })
1217
+ ] }, root.id)) }),
1218
+ addRoot !== null && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dpc-form dpc-addRoot", children: [
1219
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dpc-addRootRow", children: [
1220
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1221
+ "select",
1222
+ {
1223
+ className: "dpc-select",
1224
+ "aria-label": t("rootKind"),
1225
+ value: addRoot.kind,
1226
+ onChange: (event) => setAddRoot({ ...addRoot, kind: event.target.value }),
1227
+ children: [
1228
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "git", children: "GitHub" }),
1229
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "local", children: t("rootLocal") })
1230
+ ]
1231
+ }
1232
+ ),
1233
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1234
+ "input",
1235
+ {
1236
+ className: "dpc-input",
1237
+ type: "text",
1238
+ placeholder: addRoot.kind === "git" ? "https://github.com/anthropics/skills" : t("rootLocalPlaceholder"),
1239
+ "aria-label": t("rootPlaceholder"),
1240
+ value: addRoot.value,
1241
+ autoFocus: true,
1242
+ onChange: (event) => setAddRoot({ ...addRoot, value: event.target.value }),
1243
+ onKeyDown: (event) => {
1244
+ if (event.key === "Enter") void doAddRoot();
1245
+ }
1246
+ }
1247
+ ),
1248
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives3.Button, { variant: "primary", size: "sm", disabled: rootBusy || addRoot.value.trim() === "", onClick: () => void doAddRoot(), children: t("add") }),
1249
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives3.Button, { variant: "ghost", size: "sm", disabled: rootBusy, onClick: () => {
1250
+ setAddRoot(null);
1251
+ setFormError(null);
1252
+ }, children: t("cancel") })
668
1253
  ] }),
669
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dpc-cardDesc", title: skill.description, children: skill.description }),
670
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-cardRow", children: [
671
- !skill.invocation.modelInvocable && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dpc-tag", children: "\u2699" }),
672
- !skill.invocation.userInvocable && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dpc-tag", children: "/" }),
673
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dpc-spacer" }),
674
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "ghost", size: "sm", disabled: busy, onClick: () => void openExisting(skill), children: skill.editable ? t("edit") : t("view") }),
675
- skill.editable && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "ghost", size: "sm", disabled: busy, onClick: () => setConfirmName(skill.name), children: t("delete") })
676
- ] })
677
- ] }, `${skill.source}/${skill.name}`)) }),
678
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
679
- import_dsh_client_ui_primitives2.Modal,
1254
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "dpc-intro", children: addRoot.kind === "git" ? t("rootGitHint") : t("rootLocalHint") }),
1255
+ formError !== null && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "dpc-formError", children: formError })
1256
+ ] }),
1257
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1258
+ import_dsh_client_ui_primitives3.Modal,
680
1259
  {
681
1260
  open: editor !== null,
682
1261
  onClose: () => setEditor(null),
683
1262
  title: editor === null ? "" : editor.mode === "create" ? t("newSkill") : editor.mode === "edit" ? t("editSkill") : t("viewSkill"),
684
1263
  className: "dpc-modalForm",
685
1264
  contentClassName: "dpc-modalScroll",
686
- children: editor !== null && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-form", children: [
687
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "dpc-label", children: [
688
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: t("skillName") }),
689
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1265
+ children: editor !== null && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dpc-form", children: [
1266
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "dpc-label", children: [
1267
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: t("skillName") }),
1268
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
690
1269
  "input",
691
1270
  {
692
1271
  className: "dpc-input",
@@ -696,9 +1275,9 @@ function SkillsTab(props) {
696
1275
  }
697
1276
  )
698
1277
  ] }),
699
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "dpc-label", children: [
700
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: t("skillDescription") }),
701
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1278
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "dpc-label", children: [
1279
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: t("skillDescription") }),
1280
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
702
1281
  "input",
703
1282
  {
704
1283
  className: "dpc-input",
@@ -708,9 +1287,9 @@ function SkillsTab(props) {
708
1287
  }
709
1288
  )
710
1289
  ] }),
711
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "dpc-label", children: [
712
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: t("skillWhenToUse") }),
713
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1290
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "dpc-label", children: [
1291
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: t("skillWhenToUse") }),
1292
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
714
1293
  "input",
715
1294
  {
716
1295
  className: "dpc-input",
@@ -720,9 +1299,9 @@ function SkillsTab(props) {
720
1299
  }
721
1300
  )
722
1301
  ] }),
723
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-checks", children: [
724
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { children: [
725
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1302
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dpc-checks", children: [
1303
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { children: [
1304
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
726
1305
  "input",
727
1306
  {
728
1307
  type: "checkbox",
@@ -733,8 +1312,8 @@ function SkillsTab(props) {
733
1312
  ),
734
1313
  t("modelInvocable")
735
1314
  ] }),
736
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { children: [
737
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1315
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { children: [
1316
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
738
1317
  "input",
739
1318
  {
740
1319
  type: "checkbox",
@@ -746,9 +1325,9 @@ function SkillsTab(props) {
746
1325
  t("userInvocable")
747
1326
  ] })
748
1327
  ] }),
749
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "dpc-label", children: [
750
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: t("skillContent") }),
751
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1328
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "dpc-label", children: [
1329
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: t("skillContent") }),
1330
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
752
1331
  "textarea",
753
1332
  {
754
1333
  className: "dpc-textarea",
@@ -758,56 +1337,71 @@ function SkillsTab(props) {
758
1337
  }
759
1338
  )
760
1339
  ] }),
761
- formError !== null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dpc-formError", children: formError }),
762
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-cardRow", children: [
763
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dpc-spacer" }),
764
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "ghost", onClick: () => setEditor(null), children: readOnly ? t("close") : t("cancel") }),
765
- !readOnly && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "primary", disabled: busy, onClick: () => void doSave(), children: t("save") })
1340
+ formError !== null && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "dpc-formError", children: formError }),
1341
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dpc-cardRow", children: [
1342
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dpc-spacer" }),
1343
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives3.Button, { variant: "ghost", onClick: () => setEditor(null), children: readOnly ? t("close") : t("cancel") }),
1344
+ !readOnly && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives3.Button, { variant: "primary", disabled: busy, onClick: () => void doSave(), children: t("save") })
766
1345
  ] })
767
1346
  ] })
768
1347
  }
769
1348
  ),
770
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
771
- import_dsh_client_ui_primitives2.Modal,
1349
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1350
+ import_dsh_client_ui_primitives3.Modal,
772
1351
  {
773
1352
  open: confirmName !== null,
774
1353
  onClose: () => setConfirmName(null),
775
1354
  title: t("confirmDelete"),
776
1355
  description: confirmName ?? void 0,
777
- footer: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
778
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "ghost", onClick: () => setConfirmName(null), children: t("cancel") }),
779
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "primary", disabled: busy, onClick: () => void doDelete(), children: t("delete") })
1356
+ footer: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1357
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives3.Button, { variant: "ghost", onClick: () => setConfirmName(null), children: t("cancel") }),
1358
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives3.Button, { variant: "primary", disabled: busy, onClick: () => void doDelete(), children: t("delete") })
1359
+ ] }),
1360
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: t("deleteWarn") })
1361
+ }
1362
+ ),
1363
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1364
+ import_dsh_client_ui_primitives3.Modal,
1365
+ {
1366
+ open: confirmRootId !== null,
1367
+ onClose: () => setConfirmRootId(null),
1368
+ title: t("confirmRemoveRoot"),
1369
+ description: roots?.find((root) => root.id === confirmRootId)?.label ?? void 0,
1370
+ footer: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1371
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives3.Button, { variant: "ghost", onClick: () => setConfirmRootId(null), children: t("cancel") }),
1372
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives3.Button, { variant: "primary", disabled: rootBusy, onClick: () => void doRemoveRoot(), children: t("delete") })
780
1373
  ] }),
781
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: t("deleteWarn") })
1374
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: t("removeRootWarn") })
782
1375
  }
783
1376
  )
784
1377
  ] });
785
1378
  }
786
1379
 
787
1380
  // src/client/CapabilitiesSection.tsx
788
- var import_jsx_runtime3 = require("react/jsx-runtime");
1381
+ var import_jsx_runtime4 = require("react/jsx-runtime");
789
1382
  function CapabilitiesSection(props) {
790
- const { t, skills, mcp } = props;
791
- const tabsId = (0, import_react3.useId)();
792
- const tabRefs = (0, import_react3.useRef)([]);
1383
+ const { t, skills, mcp, market } = props;
1384
+ const tabsId = (0, import_react4.useId)();
1385
+ const tabRefs = (0, import_react4.useRef)([]);
793
1386
  const rows = [
794
1387
  { id: "skills", label: t("skillsTab") },
795
- { id: "mcp", label: t("mcpTab") }
1388
+ { id: "mcp", label: t("mcpTab") },
1389
+ { id: "market", label: t("marketTab") }
796
1390
  ];
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)(() => {
1391
+ const [activeId, setActiveId] = (0, import_react4.useState)("skills");
1392
+ const [visitedIds, setVisitedIds] = (0, import_react4.useState)(() => /* @__PURE__ */ new Set(["skills"]));
1393
+ (0, import_react4.useEffect)(() => {
800
1394
  setVisitedIds((previous) => {
801
1395
  if (previous.has(activeId)) return previous;
802
1396
  return /* @__PURE__ */ new Set([...previous, activeId]);
803
1397
  });
804
1398
  }, [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) => {
1399
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dpc-section", children: [
1400
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: CSS }),
1401
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { className: "dpc-heading", children: t("sectionNav") }),
1402
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dpc-tabs", role: "tablist", "aria-label": t("sectionNav"), children: rows.map((row, index) => {
809
1403
  const selected = row.id === activeId;
810
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1404
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
811
1405
  "button",
812
1406
  {
813
1407
  ref: (element) => {
@@ -853,7 +1447,7 @@ function CapabilitiesSection(props) {
853
1447
  }) }),
854
1448
  rows.filter((row) => row.id === activeId || visitedIds.has(row.id)).map((row) => {
855
1449
  const selected = row.id === activeId;
856
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1450
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
857
1451
  "div",
858
1452
  {
859
1453
  id: `${tabsId}-panel-${row.id}`,
@@ -861,7 +1455,7 @@ function CapabilitiesSection(props) {
861
1455
  role: "tabpanel",
862
1456
  "aria-labelledby": `${tabsId}-tab-${row.id}`,
863
1457
  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 })
1458
+ children: row.id === "skills" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SkillsTab, { t, injected: skills }) : row.id === "mcp" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(McpTab, { t, injected: mcp }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MarketTab, { t, market, mcp })
865
1459
  },
866
1460
  row.id
867
1461
  );
@@ -874,8 +1468,9 @@ var zh = {
874
1468
  sectionNav: "\u6280\u80FD\u4E0E MCP",
875
1469
  skillsTab: "\u6280\u80FD",
876
1470
  mcpTab: "MCP",
1471
+ marketTab: "\u5E02\u573A",
877
1472
  skillsTitle: "\u6280\u80FD\u7BA1\u7406",
878
- 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",
1473
+ 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\u5361\u7247\u4E0A\u7684\u5F00\u5173\u63A7\u5236\u662F\u5426\u52A0\u8F7D\uFF1A\u505C\u7528\u5373\u4ECE\u6A21\u578B\u4E0E\u7528\u6237\u76EE\u5F55\u540C\u65F6\u6458\u9664\uFF0C\u5F00\u542F\u6062\u590D\u9ED8\u8BA4\u3002",
879
1474
  newSkill: "\u65B0\u5EFA\u6280\u80FD",
880
1475
  editSkill: "\u7F16\u8F91\u6280\u80FD",
881
1476
  viewSkill: "\u67E5\u770B\u6280\u80FD",
@@ -905,6 +1500,35 @@ var zh = {
905
1500
  confirmDelete: "\u786E\u8BA4\u5220\u9664\u6280\u80FD\uFF1F",
906
1501
  deleteWarn: "\u5C06\u5220\u9664 DSH_HOME/skills \u4E0B\u7684\u6574\u4E2A\u6280\u80FD\u76EE\u5F55\uFF0C\u6B64\u64CD\u4F5C\u4E0D\u53EF\u64A4\u9500\u3002",
907
1502
  saved: "\u5DF2\u4FDD\u5B58\uFF0C\u6280\u80FD\u76EE\u5F55\u88AB\u76D1\u542C\uFF0C\u7A0D\u5019\u5373\u53EF\u5728\u4F1A\u8BDD\u4E2D\u4F7F\u7528\u3002",
1503
+ searchSkills: "\u641C\u7D22\u6280\u80FD\u540D\u79F0\u6216\u63CF\u8FF0",
1504
+ filterAll: "\u5168\u90E8",
1505
+ noMatch: "\u6CA1\u6709\u5339\u914D\u7684\u6280\u80FD",
1506
+ refresh: "\u5237\u65B0",
1507
+ skillDisabled: "\u5DF2\u505C\u7528",
1508
+ skillUserOnly: "\u4EC5\u7528\u6237\u53EF\u7528",
1509
+ skillModelOnly: "\u4EC5\u6A21\u578B\u53EF\u7528",
1510
+ skillEnabled: "\u5DF2\u542F\u7528\uFF0C\u7A0D\u5019\u5728\u5217\u8868\u4E2D\u751F\u6548\u3002",
1511
+ skillDisabledMsg: "\u5DF2\u505C\u7528\uFF1A\u6A21\u578B\u4E0E / \u547D\u4EE4\u90FD\u4E0D\u4F1A\u518D\u52A0\u8F7D\u5B83\u3002",
1512
+ toggleSkill: "\u5207\u6362\u6280\u80FD\u52A0\u8F7D",
1513
+ toggleSkillHint: "\u505C\u7528\u4F1A\u5728\u8BE5\u6280\u80FD\u7684 SKILL.md \u91CC\u5199\u5165 disable-model-invocation \u4E0E user-invocable\uFF0C\u540C\u65F6\u4ECE\u6A21\u578B\u4E0E\u7528\u6237\u76EE\u5F55\u6458\u9664\uFF1B\u5F00\u542F\u5219\u6062\u590D\u9ED8\u8BA4\u7B56\u7565\u3002\u6587\u4EF6\u5176\u4F59\u5185\u5BB9\u4E0D\u53D8\u3002",
1514
+ openFolder: "\u6253\u5F00\u76EE\u5F55",
1515
+ openUserSkills: "\u6253\u5F00\u6280\u80FD\u76EE\u5F55",
1516
+ rootsTitle: "\u6280\u80FD\u4ED3\u5E93",
1517
+ rootsIntro: "\u624B\u52A8\u6DFB\u52A0\u6280\u80FD\u4ED3\u5E93\uFF1A\u672C\u5730\u76EE\u5F55\u76F4\u63A5\u626B\u63CF\uFF08\u96F6\u62F7\u8D1D\uFF09\uFF0CGitHub \u4ED3\u5E93\u4E0B\u8F7D\u538B\u7F29\u5305\u540E\u89E3\u5305\u5230 DSH_HOME \u5185\uFF08\u65E0\u9700\u5B89\u88C5 git\uFF09\u3002\u4E5F\u53EF\u4EE5\u5728\u300C\u5E02\u573A\u300D\u9875\u4E00\u952E\u5B89\u88C5\u7CBE\u9009\u4ED3\u5E93\u3002",
1518
+ emptyRoots: "\u8FD8\u6CA1\u6709\u6DFB\u52A0\u81EA\u5B9A\u4E49\u6280\u80FD\u4ED3\u5E93",
1519
+ addRoot: "\u6DFB\u52A0\u4ED3\u5E93",
1520
+ rootLocal: "\u672C\u5730",
1521
+ rootLocalPlaceholder: "D:\\path\\to\\skills",
1522
+ rootKind: "\u4ED3\u5E93\u7C7B\u578B",
1523
+ rootPlaceholder: "\u4ED3\u5E93\u5730\u5740",
1524
+ add: "\u6DFB\u52A0",
1525
+ rootAdded: "\u4ED3\u5E93\u5DF2\u6DFB\u52A0\uFF0C\u76EE\u5F55\u5B9E\u65F6\u751F\u6548\u3002",
1526
+ rootRemoved: "\u4ED3\u5E93\u5DF2\u79FB\u9664\u3002",
1527
+ rootStale: "\u76EE\u5F55\u7F3A\u5931",
1528
+ rootGitHint: "\u652F\u6301 https://github.com/owner/repo\u3001owner/repo\uFF0C\u53EF\u7528 #\u5206\u652F \u6216 /tree/\u5206\u652F \u6307\u5B9A\u7248\u672C\u3002",
1529
+ rootLocalHint: "\u6307\u5411\u4E00\u4E2A\u6280\u80FD\u76EE\u5F55\uFF08\u5185\u542B SKILL.md\uFF09\u6216\u88C5\u7740\u591A\u4E2A\u6280\u80FD\u76EE\u5F55\u7684\u6587\u4EF6\u5939\u3002",
1530
+ confirmRemoveRoot: "\u786E\u8BA4\u79FB\u9664\u8BE5\u4ED3\u5E93\uFF1F",
1531
+ removeRootWarn: "\u5C06\u53D6\u6D88\u626B\u63CF\u8BE5\u4ED3\u5E93\u5E76\u5220\u9664\u4E0B\u8F7D\u5230 DSH_HOME \u7684\u526F\u672C\uFF08\u672C\u5730\u76EE\u5F55\u672C\u8EAB\u4E0D\u53D7\u5F71\u54CD\uFF09\u3002",
908
1532
  mcpTitle: "MCP \u670D\u52A1\u5668",
909
1533
  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",
910
1534
  addServer: "\u6DFB\u52A0\u670D\u52A1\u5668",
@@ -939,14 +1563,40 @@ var zh = {
939
1563
  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",
940
1564
  restarting: "\u6B63\u5728\u91CD\u542F\uFF0C\u6062\u590D\u540E\u5C06\u81EA\u52A8\u5237\u65B0\u9875\u9762\u2026",
941
1565
  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",
942
- failed: "\u64CD\u4F5C\u5931\u8D25"
1566
+ failed: "\u64CD\u4F5C\u5931\u8D25",
1567
+ formatTitle: "\u5B8C\u6574\u683C\u5F0F\u4E0E\u5FEB\u901F\u586B\u5145",
1568
+ formatPaste: "\u7C98\u8D34 JSON \u914D\u7F6E\uFF08mcpServers \u5305\u88C5\u6216\u5355\u6761\u76EE\u5747\u53EF\uFF09",
1569
+ formatFill: "\u89E3\u6790\u5E76\u586B\u5145\u8868\u5355",
1570
+ formatYamlHint: "\u4FDD\u5B58\u540E\u5C06\u5199\u5165 profile patch \u7684\u5B8C\u6574\u884C\uFF08\u5B9E\u65F6\u9884\u89C8\uFF09\uFF1A",
1571
+ formatJsonHint: "\u7B49\u4EF7\u7684 mcpServers JSON \u5199\u6CD5\uFF08\u53EF\u7C98\u8D34\u5230\u5176\u4ED6\u5DE5\u5177\uFF09\uFF1A",
1572
+ pasteTransportMismatch: "\u8BE5\u884C\u7684\u4F20\u8F93\u65B9\u5F0F\u5DF2\u9501\u5B9A\uFF0C\u4E0E\u7C98\u8D34\u914D\u7F6E\u7684\u7C7B\u578B\u4E0D\u4E00\u81F4\u3002",
1573
+ marketTitle: "\u5E02\u573A",
1574
+ marketIntro: "\u7CBE\u9009\u7684\u6280\u80FD\u4ED3\u5E93\u4E0E MCP \u670D\u52A1\u5668\uFF0C\u4E00\u952E\u5B89\u88C5\u3002\u5217\u8868\u6765\u81EA\u63D2\u4EF6\u4ED3\u5E93\u7684\u5728\u7EBF\u7D22\u5F15\uFF0C\u79BB\u7EBF\u65F6\u56DE\u9000\u5230\u5305\u5185\u5FEB\u7167\u3002",
1575
+ marketSkills: "\u6280\u80FD\u5E02\u573A",
1576
+ marketMcp: "MCP \u5E02\u573A",
1577
+ marketSkillsIntro: "\u5B89\u88C5\u6280\u80FD\u4ED3\u5E93\uFF08GitHub \u4E0B\u8F7D\u89E3\u5305\uFF0C\u5B9E\u65F6\u8FDB\u5165\u626B\u63CF\u76EE\u5F55\uFF09\uFF1B\u5DF2\u5B89\u88C5\u7684\u4ED3\u5E93\u53EF\u5728\u300C\u6280\u80FD\u300D\u9875\u7BA1\u7406\u3002",
1578
+ marketMcpIntro: "\u6DFB\u52A0 MCP \u670D\u52A1\u5668\u884C\uFF08\u4E0E\u300CMCP\u300D\u9875\u540C\u4E00\u5B58\u50A8\uFF0C\u6DFB\u52A0\u540E\u9700\u91CD\u542F\u751F\u6548\uFF09\uFF1B\u9700\u8981\u5BC6\u94A5\u7684\u670D\u52A1\u5728\u5B89\u88C5\u540E\u4E8E\u5217\u8868\u4E2D\u8865\u586B\u73AF\u5883\u53D8\u91CF\u3002",
1579
+ marketEmpty: "\u5E02\u573A\u5217\u8868\u4E3A\u7A7A\u6216\u4E0D\u53EF\u7528",
1580
+ marketOffline: "\u5728\u7EBF\u7D22\u5F15\u4E0D\u53EF\u8FBE\uFF0C\u5F53\u524D\u5C55\u793A\u5305\u5185\u5FEB\u7167\uFF08\u53EF\u80FD\u4E0E\u6700\u65B0\u5217\u8868\u6709\u5DEE\u5F02\uFF09\u3002",
1581
+ marketSkillCount: "{n} \u4E2A\u6280\u80FD",
1582
+ marketInstall: "\u5B89\u88C5",
1583
+ marketAdd: "\u6DFB\u52A0",
1584
+ marketInstalling: "\u8FDB\u884C\u4E2D\u2026",
1585
+ marketInstalled: "\u5DF2\u5B89\u88C5",
1586
+ marketUninstall: "\u79FB\u9664",
1587
+ marketHome: "\u4E3B\u9875",
1588
+ marketNeedsEnv: "\u9700\u8981\u73AF\u5883\u53D8\u91CF",
1589
+ marketRepoInstalled: "\u4ED3\u5E93\u5DF2\u5B89\u88C5\uFF0C\u6280\u80FD\u7A0D\u5019\u51FA\u73B0\u5728\u300C\u6280\u80FD\u300D\u9875\u5217\u8868\u3002",
1590
+ marketUninstallConfirm: "\u786E\u8BA4\u79FB\u9664\uFF1F",
1591
+ marketUninstallRootWarn: "\u5C06\u53D6\u6D88\u626B\u63CF\u8BE5\u4ED3\u5E93\u5E76\u5220\u9664\u4E0B\u8F7D\u7684\u526F\u672C\uFF08\u79FB\u9664\u540E\u53EF\u91CD\u65B0\u5B89\u88C5\uFF09\u3002"
943
1592
  };
944
1593
  var en = {
945
1594
  sectionNav: "Skills & MCP",
946
1595
  skillsTab: "Skills",
947
1596
  mcpTab: "MCP",
1597
+ marketTab: "Market",
948
1598
  skillsTitle: "Skills",
949
- 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).",
1599
+ 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). The switch on each card toggles loading: disabling removes the skill from both the model and user catalogs; enabling restores the default policy.",
950
1600
  newSkill: "New skill",
951
1601
  editSkill: "Edit skill",
952
1602
  viewSkill: "View skill",
@@ -976,6 +1626,35 @@ var en = {
976
1626
  confirmDelete: "Delete this skill?",
977
1627
  deleteWarn: "Removes the whole skill directory under DSH_HOME/skills. This cannot be undone.",
978
1628
  saved: "Saved. The skills directory is watched; the skill is usable in sessions shortly.",
1629
+ searchSkills: "Search name or description",
1630
+ filterAll: "All",
1631
+ noMatch: "No skills match",
1632
+ refresh: "Refresh",
1633
+ skillDisabled: "disabled",
1634
+ skillUserOnly: "user-only",
1635
+ skillModelOnly: "model-only",
1636
+ skillEnabled: "Enabled \u2014 the list reflects it shortly.",
1637
+ skillDisabledMsg: "Disabled: neither the model nor / commands will load it.",
1638
+ toggleSkill: "Toggle skill loading",
1639
+ toggleSkillHint: "Disabling writes disable-model-invocation and user-invocable into the skill\u2019s SKILL.md, removing it from both catalogs; enabling restores the default policy. The rest of the file is untouched.",
1640
+ openFolder: "Open folder",
1641
+ openUserSkills: "Open skills folder",
1642
+ rootsTitle: "Skill repositories",
1643
+ rootsIntro: "Add skill repositories by hand: local folders are scanned in place (zero-copy), GitHub repos arrive as a downloaded copy inside DSH_HOME (no git needed). The Market tab offers curated repos with one click.",
1644
+ emptyRoots: "No custom skill repositories yet",
1645
+ addRoot: "Add repository",
1646
+ rootLocal: "Local",
1647
+ rootLocalPlaceholder: "D:\\path\\to\\skills",
1648
+ rootKind: "Repository type",
1649
+ rootPlaceholder: "Repository address",
1650
+ add: "Add",
1651
+ rootAdded: "Repository added \u2014 the catalog follows live.",
1652
+ rootRemoved: "Repository removed.",
1653
+ rootStale: "missing",
1654
+ rootGitHint: "Accepts https://github.com/owner/repo and owner/repo; pick a version with #branch or /tree/branch.",
1655
+ rootLocalHint: "Point at one skill folder (with SKILL.md) or a folder of skill folders.",
1656
+ confirmRemoveRoot: "Remove this repository?",
1657
+ removeRootWarn: "Stops scanning the repository and deletes the copy downloaded into DSH_HOME (a local folder itself is untouched).",
979
1658
  mcpTitle: "MCP servers",
980
1659
  mcpIntro: "Manage the MCP server rows (@deepseek-ai/dsh-mcp-client) in this profile. Additions, edits, and removals take effect after a dsh restart.",
981
1660
  addServer: "Add server",
@@ -1010,7 +1689,32 @@ var en = {
1010
1689
  restartConfirmBody: "Restarting interrupts any running turn and may lose unsaved input; pending MCP changes apply after the restart.",
1011
1690
  restarting: "Restarting \u2014 the page will reload once the host is back\u2026",
1012
1691
  restartPortHint: "If the page does not recover, the port may have changed: check the terminal for the new URL.",
1013
- failed: "Operation failed"
1692
+ failed: "Operation failed",
1693
+ formatTitle: "Full format & quick fill",
1694
+ formatPaste: "Paste a JSON config (mcpServers wrapper or a bare entry)",
1695
+ formatFill: "Parse and fill the form",
1696
+ formatYamlHint: "The complete row as it will land in the profile patch (live preview):",
1697
+ formatJsonHint: "Equivalent mcpServers JSON (paste-ready for other tools):",
1698
+ pasteTransportMismatch: "This row\u2019s transport is locked and differs from the pasted config.",
1699
+ marketTitle: "Market",
1700
+ marketIntro: "Curated skill repositories and MCP servers, installable with one click. The list comes from the plugin repository\u2019s online index, falling back to the bundled snapshot offline.",
1701
+ marketSkills: "Skills market",
1702
+ marketMcp: "MCP market",
1703
+ marketSkillsIntro: "Installs skill repositories (GitHub download, live catalog); manage installed ones on the Skills tab.",
1704
+ marketMcpIntro: "Adds MCP server rows (same storage as the MCP tab; restart to apply). Servers needing keys get their environment filled in on the row after install.",
1705
+ marketEmpty: "Market list is empty or unavailable",
1706
+ marketOffline: "Online index unreachable \u2014 showing the bundled snapshot, which may lag behind.",
1707
+ marketSkillCount: "{n} skills",
1708
+ marketInstall: "Install",
1709
+ marketAdd: "Add",
1710
+ marketInstalling: "Working\u2026",
1711
+ marketInstalled: "installed",
1712
+ marketUninstall: "Uninstall",
1713
+ marketHome: "Homepage",
1714
+ marketNeedsEnv: "Needs environment variables",
1715
+ marketRepoInstalled: "Repository installed \u2014 its skills appear on the Skills tab shortly.",
1716
+ marketUninstallConfirm: "Uninstall?",
1717
+ marketUninstallRootWarn: "Stops scanning the repository and deletes the downloaded copy (you can reinstall later)."
1014
1718
  };
1015
1719
 
1016
1720
  // src/client/index.ts
@@ -1031,7 +1735,12 @@ function apply(ctx) {
1031
1735
  list: () => fetchJson("/dsh-plugin-capabilities/skills"),
1032
1736
  get: (name2) => fetchJson(`/dsh-plugin-capabilities/skill?name=${encodeURIComponent(name2)}`),
1033
1737
  save: (input) => post("/dsh-plugin-capabilities/skill/save", input),
1034
- remove: (name2) => post("/dsh-plugin-capabilities/skill/delete", { name: name2 })
1738
+ remove: (name2) => post("/dsh-plugin-capabilities/skill/delete", { name: name2 }),
1739
+ policy: (name2, enabled) => post("/dsh-plugin-capabilities/skill/policy", { name: name2, enabled }),
1740
+ open: (target) => post("/dsh-plugin-capabilities/open", target),
1741
+ roots: () => fetchJson("/dsh-plugin-capabilities/roots"),
1742
+ addRoot: (input) => post("/dsh-plugin-capabilities/roots/add", input),
1743
+ removeRoot: (id) => post("/dsh-plugin-capabilities/roots/remove", { id })
1035
1744
  };
1036
1745
  const mcpInjected = {
1037
1746
  list: () => fetchJson("/dsh-plugin-capabilities/mcp"),
@@ -1052,6 +1761,13 @@ function apply(ctx) {
1052
1761
  },
1053
1762
  desktop: window.dshDesktop !== void 0
1054
1763
  };
1764
+ const marketInjected = {
1765
+ skillsIndex: () => fetchJson("/dsh-plugin-capabilities/market/skills"),
1766
+ mcpIndex: () => fetchJson("/dsh-plugin-capabilities/market/mcp"),
1767
+ installSkillRepo: (url) => post("/dsh-plugin-capabilities/market/skills/install", { url }),
1768
+ installMcp: (id) => post("/dsh-plugin-capabilities/market/mcp/install", { id }),
1769
+ removeRoot: skillsInjected.removeRoot
1770
+ };
1055
1771
  ctx.slots.inject("settings.section", () => {
1056
1772
  return ctx.slots.register({
1057
1773
  name: "settings.section",
@@ -1059,7 +1775,7 @@ function apply(ctx) {
1059
1775
  order: 12,
1060
1776
  label: () => t("sectionNav"),
1061
1777
  locale: NS
1062
- }, () => (0, import_react4.createElement)(CapabilitiesSection, { t, skills: skillsInjected, mcp: mcpInjected }));
1778
+ }, () => (0, import_react5.createElement)(CapabilitiesSection, { t, skills: skillsInjected, mcp: mcpInjected, market: marketInjected }));
1063
1779
  });
1064
1780
  }
1065
1781