@sparkelf/dsh-client-ui-skill-center 0.2.0-rc.9

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 ADDED
@@ -0,0 +1,613 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@sparkelf/dsh-client-ui-skill-center",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react = require("react");
8
+ let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
9
+ let react_jsx_runtime = require("react/jsx-runtime");
10
+ //#region \0dsh-css:/root/projects/deepseek-harness-plus/packages/plus/ui-skill-center/src/client/SkillCenterPage.module.css.mjs
11
+ const css = ".DGiSta_page{box-sizing:border-box;height:100%;color:var(--dsw-alias-label-primary);flex-direction:column;align-items:center;gap:32px;padding:28px clamp(24px,4vw,48px) 48px;display:flex;overflow:auto}.DGiSta_page>*{width:100%;max-width:900px}.DGiSta_pageHead{justify-content:space-between;align-items:flex-start;gap:16px;display:flex}.DGiSta_pageTitle{margin:0;font-size:20px;font-weight:500;line-height:28px}.DGiSta_pageIntro{color:var(--dsw-alias-label-secondary);margin:4px 0 0;font-size:13px;line-height:20px}.DGiSta_toolbar{justify-content:flex-end;align-items:center;gap:8px;display:flex}.DGiSta_button{border:.5px solid var(--dsw-alias-border-l3);height:32px;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:8px;padding:0 12px;font-size:13px}.DGiSta_button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.DGiSta_button:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.DGiSta_primaryButton{background:var(--dsw-alias-brand-primary);height:32px;color:var(--dsw-alias-label-primary);cursor:pointer;border:none;border-radius:8px;padding:0 14px;font-size:13px}.DGiSta_search{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l3);width:100%;height:36px;color:var(--dsw-alias-label-primary);background:0 0;border-radius:10px;padding:0 12px;font-size:13px}.DGiSta_search::placeholder{color:var(--dsw-alias-label-tertiary)}.DGiSta_group{margin-top:32px}.DGiSta_groupTitle{align-items:baseline;gap:8px;margin:0 0 4px;font-size:15px;font-weight:500;line-height:22px;display:flex}.DGiSta_groupCount{color:var(--dsw-alias-label-tertiary);font-size:13px;font-weight:400}.DGiSta_groupHint{color:var(--dsw-alias-label-caption);font-size:12px;font-weight:400}.DGiSta_cards{margin:0;padding:0;list-style:none}.DGiSta_card{border-radius:12px;min-width:0;margin:0 -8px}.DGiSta_card:hover{background:var(--dsw-alias-interactive-bg-hover)}.DGiSta_cardHead{align-items:center;gap:14px;padding:8px;display:flex}.DGiSta_cardIcon{border:.5px solid var(--dsw-alias-border-l3);width:48px;height:48px;color:var(--dsw-alias-label-secondary);border-radius:10px;flex:none;justify-content:center;align-items:center;display:inline-flex}.DGiSta_cardMain{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.DGiSta_cardTitle{text-overflow:ellipsis;white-space:nowrap;font-size:14px;font-weight:500;line-height:20px;overflow:hidden}.DGiSta_cardDesc{color:var(--dsw-alias-label-secondary);-webkit-line-clamp:2;-webkit-box-orient:vertical;font-size:13px;line-height:18px;display:-webkit-box;overflow:hidden}.DGiSta_cardWhenToUse{color:var(--dsw-alias-label-caption);font-size:12px;line-height:18px}.DGiSta_cardMarks{flex-wrap:wrap;gap:6px;margin-top:2px;display:flex}.DGiSta_mark{border:.5px solid var(--dsw-alias-border-l3);color:var(--dsw-alias-label-tertiary);border-radius:4px;padding:1px 6px;font-size:11px;line-height:16px}.DGiSta_markOn{border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.DGiSta_markOff{border-color:var(--dsw-alias-border-l4);color:var(--dsw-alias-label-caption)}.DGiSta_markDanger{border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}.DGiSta_cardEnd{flex:none;align-items:center;gap:8px;display:flex}.DGiSta_switch{background:var(--dsw-alias-border-l3);cursor:pointer;border:none;border-radius:10px;width:36px;height:20px;transition:background .12s;position:relative}.DGiSta_switch:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.DGiSta_switchOn{background:var(--dsw-alias-state-success-primary)}.DGiSta_switch:after{content:\"\";background:var(--dsw-alias-bg-layer-1);border-radius:50%;width:16px;height:16px;transition:transform .12s;position:absolute;top:2px;left:2px}.DGiSta_switchOn:after{transform:translate(16px)}.DGiSta_form{border:.5px solid var(--dsw-alias-border-l3);border-radius:12px;flex-direction:column;gap:12px;padding:16px;display:flex}.DGiSta_field{flex-direction:column;gap:4px;display:flex}.DGiSta_fieldLabel{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}.DGiSta_input{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l3);height:32px;color:var(--dsw-alias-label-primary);background:0 0;border-radius:8px;padding:0 10px;font-size:13px}.DGiSta_textarea{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l3);min-height:80px;color:var(--dsw-alias-label-primary);font-family:var(--dsw-font-markdown-code-block-small);resize:vertical;background:0 0;border-radius:8px;padding:8px 10px;font-size:12px}.DGiSta_formActions{justify-content:flex-end;gap:8px;display:flex}.DGiSta_status{text-align:center;color:var(--dsw-alias-label-tertiary);padding:32px 0;font-size:13px}.DGiSta_statusError{color:var(--dsw-alias-state-error-primary)}";
12
+ const tagId = "@sparkelf/dsh-client-ui-skill-center/SkillCenterPage.module.css";
13
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
14
+ const tag = document.createElement("style");
15
+ tag.dataset.plugin = "@sparkelf/dsh-client-ui-skill-center";
16
+ tag.dataset.pluginCss = tagId;
17
+ tag.textContent = css;
18
+ document.head.appendChild(tag);
19
+ }
20
+ var SkillCenterPage_module_css_default = {
21
+ "button": "DGiSta_button",
22
+ "card": "DGiSta_card",
23
+ "cardDesc": "DGiSta_cardDesc",
24
+ "cardEnd": "DGiSta_cardEnd",
25
+ "cardHead": "DGiSta_cardHead",
26
+ "cardIcon": "DGiSta_cardIcon",
27
+ "cardMain": "DGiSta_cardMain",
28
+ "cardMarks": "DGiSta_cardMarks",
29
+ "cardTitle": "DGiSta_cardTitle",
30
+ "cardWhenToUse": "DGiSta_cardWhenToUse",
31
+ "cards": "DGiSta_cards",
32
+ "field": "DGiSta_field",
33
+ "fieldLabel": "DGiSta_fieldLabel",
34
+ "form": "DGiSta_form",
35
+ "formActions": "DGiSta_formActions",
36
+ "group": "DGiSta_group",
37
+ "groupCount": "DGiSta_groupCount",
38
+ "groupHint": "DGiSta_groupHint",
39
+ "groupTitle": "DGiSta_groupTitle",
40
+ "input": "DGiSta_input",
41
+ "mark": "DGiSta_mark",
42
+ "markDanger": "DGiSta_markDanger",
43
+ "markOff": "DGiSta_markOff",
44
+ "markOn": "DGiSta_markOn",
45
+ "page": "DGiSta_page",
46
+ "pageHead": "DGiSta_pageHead",
47
+ "pageIntro": "DGiSta_pageIntro",
48
+ "pageTitle": "DGiSta_pageTitle",
49
+ "primaryButton": "DGiSta_primaryButton",
50
+ "search": "DGiSta_search",
51
+ "status": "DGiSta_status",
52
+ "statusError": "DGiSta_statusError",
53
+ "switch": "DGiSta_switch",
54
+ "switchOn": "DGiSta_switchOn",
55
+ "textarea": "DGiSta_textarea",
56
+ "toolbar": "DGiSta_toolbar"
57
+ };
58
+ //#endregion
59
+ //#region src/client/api.ts
60
+ /**
61
+ * Skill center API client. Talks to the host route family over same-origin
62
+ * fetch; the host owns the trust fence.
63
+ *
64
+ * @module @sparkelf/dsh-client-ui-skill-center/client/api
65
+ */
66
+ /** Route paths, mirrored from the host (`src/index.ts` ROUTES). */
67
+ const API = {
68
+ list: "/api/dsh-skill-center/list",
69
+ setEnabled: "/api/dsh-skill-center/set-enabled",
70
+ create: "/api/dsh-skill-center/create",
71
+ remove: "/api/dsh-skill-center/delete"
72
+ };
73
+ /** One thrown API error carrying the host's message. */
74
+ var ApiError = class extends Error {};
75
+ /** Skill center API client. */
76
+ var SkillApi = class {
77
+ /** Fetch the grouped skill list. */
78
+ async list(cwd) {
79
+ const url = typeof cwd === "string" && cwd.trim() !== "" ? `${API.list}?cwd=${encodeURIComponent(cwd)}` : API.list;
80
+ return this.request(url);
81
+ }
82
+ /** Enable or disable a skill by rewriting its frontmatter flag. */
83
+ async setEnabled(name, path, enabled) {
84
+ return this.request(API.setEnabled, {
85
+ method: "POST",
86
+ body: {
87
+ name,
88
+ path,
89
+ enabled
90
+ }
91
+ });
92
+ }
93
+ /** Create a skill file under the user or project root. */
94
+ async create(payload) {
95
+ return this.request(API.create, {
96
+ method: "POST",
97
+ body: payload
98
+ });
99
+ }
100
+ /** Delete a skill by moving it into the recoverable trash. */
101
+ async remove(name, path) {
102
+ return this.request(API.remove, {
103
+ method: "POST",
104
+ body: {
105
+ name,
106
+ path
107
+ }
108
+ });
109
+ }
110
+ async request(path, options = {}) {
111
+ const init = { method: options.method ?? "GET" };
112
+ if (options.body !== void 0) {
113
+ init.headers = { "content-type": "application/json" };
114
+ init.body = JSON.stringify(options.body);
115
+ }
116
+ const response = await fetch(path, init);
117
+ let body;
118
+ try {
119
+ body = await response.json();
120
+ } catch {
121
+ body = void 0;
122
+ }
123
+ if (!response.ok) {
124
+ const reported = typeof body === "object" && body !== null && typeof body.error === "string" ? body.error : "";
125
+ throw new ApiError(reported === "" ? "HTTP " + String(response.status) : reported);
126
+ }
127
+ return body;
128
+ }
129
+ };
130
+ //#endregion
131
+ //#region src/client/SkillCenterPage.tsx
132
+ /**
133
+ * Skill center page. Browses the loaded skills grouped by source, toggles model
134
+ * invocation, creates a skill, and deletes one into a recoverable trash.
135
+ *
136
+ * The header, rows, and glyph frame follow the official plugin panel's layout so
137
+ * the page reads as one of the shell's own.
138
+ *
139
+ * @module @sparkelf/dsh-client-ui-skill-center/client/SkillCenterPage
140
+ */
141
+ /**
142
+ * One labelled create-form field.
143
+ * @param props - the field's label and the control it wraps.
144
+ * @returns the labelled field.
145
+ */
146
+ function DraftField({ label, children }) {
147
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
148
+ className: SkillCenterPage_module_css_default.field,
149
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
150
+ className: SkillCenterPage_module_css_default.fieldLabel,
151
+ children: label
152
+ }), children]
153
+ });
154
+ }
155
+ /**
156
+ * One skill row.
157
+ * @param props - the entry, its toggle, and its delete action.
158
+ * @returns the row.
159
+ */
160
+ function SkillCard({ skill, t, onToggle, onDelete }) {
161
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
162
+ className: SkillCenterPage_module_css_default.card,
163
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
164
+ className: SkillCenterPage_module_css_default.cardHead,
165
+ children: [
166
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
167
+ className: SkillCenterPage_module_css_default.cardIcon,
168
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSkillOutline16, { size: 22 })
169
+ }),
170
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
171
+ className: SkillCenterPage_module_css_default.cardMain,
172
+ children: [
173
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
174
+ className: SkillCenterPage_module_css_default.cardTitle,
175
+ children: skill.name
176
+ }),
177
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
178
+ className: SkillCenterPage_module_css_default.cardDesc,
179
+ children: skill.description
180
+ }),
181
+ skill.whenToUse === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
182
+ className: SkillCenterPage_module_css_default.cardWhenToUse,
183
+ children: skill.whenToUse
184
+ }),
185
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
186
+ className: SkillCenterPage_module_css_default.cardMarks,
187
+ children: [
188
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
189
+ className: skill.modelInvocable ? SkillCenterPage_module_css_default.mark + " " + SkillCenterPage_module_css_default.markOn : SkillCenterPage_module_css_default.mark + " " + SkillCenterPage_module_css_default.markOff,
190
+ children: t("skill.modelInvocable")
191
+ }),
192
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
193
+ className: skill.userInvocable ? SkillCenterPage_module_css_default.mark + " " + SkillCenterPage_module_css_default.markOn : SkillCenterPage_module_css_default.mark + " " + SkillCenterPage_module_css_default.markOff,
194
+ children: t("skill.userInvocable")
195
+ }),
196
+ skill.linked === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
197
+ className: SkillCenterPage_module_css_default.mark + " " + SkillCenterPage_module_css_default.markDanger,
198
+ children: t("skill.linked")
199
+ }) : null
200
+ ]
201
+ })
202
+ ]
203
+ }),
204
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
205
+ className: SkillCenterPage_module_css_default.cardEnd,
206
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
207
+ type: "button",
208
+ className: skill.modelInvocable ? SkillCenterPage_module_css_default.switch + " " + SkillCenterPage_module_css_default.switchOn : SkillCenterPage_module_css_default.switch,
209
+ "aria-label": skill.modelInvocable ? t("skill.disabled") : t("skill.enabled"),
210
+ "aria-pressed": skill.modelInvocable,
211
+ onClick: () => {
212
+ onToggle(skill, !skill.modelInvocable);
213
+ }
214
+ }), skill.path === void 0 || skill.linked === true ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
215
+ type: "button",
216
+ className: SkillCenterPage_module_css_default.button,
217
+ onClick: () => {
218
+ onDelete(skill);
219
+ },
220
+ children: t("action.delete")
221
+ })]
222
+ })
223
+ ]
224
+ })
225
+ });
226
+ }
227
+ /**
228
+ * The skill center panel.
229
+ * @param props - the slot's runtime share, providing the dictionary resolver.
230
+ * @returns the rendered panel.
231
+ */
232
+ function SkillCenterPage({ t }) {
233
+ const api = (0, react.useMemo)(() => new SkillApi(), []);
234
+ const [state, setState] = (0, react.useState)({ kind: "loading" });
235
+ const [query, setQuery] = (0, react.useState)("");
236
+ const [creating, setCreating] = (0, react.useState)(false);
237
+ const [draft, setDraft] = (0, react.useState)({
238
+ name: "",
239
+ description: "",
240
+ whenToUse: "",
241
+ content: "",
242
+ root: "user"
243
+ });
244
+ const load = (0, react.useCallback)(async () => {
245
+ try {
246
+ setState({
247
+ kind: "ready",
248
+ payload: await api.list()
249
+ });
250
+ } catch (error) {
251
+ setState({
252
+ kind: "failed",
253
+ message: error instanceof Error ? error.message : String(error)
254
+ });
255
+ }
256
+ }, [api]);
257
+ (0, react.useEffect)(() => {
258
+ load();
259
+ }, [load]);
260
+ const groups = (0, react.useMemo)(() => {
261
+ if (state.kind !== "ready") return [];
262
+ const q = query.trim().toLowerCase();
263
+ if (q === "") return state.payload.groups;
264
+ return state.payload.groups.map((g) => ({
265
+ ...g,
266
+ skills: g.skills.filter((s) => s.name.toLowerCase().includes(q) || s.description.toLowerCase().includes(q) || (s.whenToUse ?? "").toLowerCase().includes(q))
267
+ })).filter((g) => g.skills.length > 0);
268
+ }, [state, query]);
269
+ const toggle = (0, react.useCallback)(async (skill, enabled) => {
270
+ if (skill.path === void 0) return;
271
+ try {
272
+ await api.setEnabled(skill.name, skill.path, enabled);
273
+ await load();
274
+ } catch (error) {
275
+ setState({
276
+ kind: "failed",
277
+ message: error instanceof Error ? error.message : String(error)
278
+ });
279
+ }
280
+ }, [api, load]);
281
+ const remove = (0, react.useCallback)(async (skill) => {
282
+ if (skill.path === void 0) return;
283
+ if (!window.confirm(t("delete.confirm"))) return;
284
+ try {
285
+ await api.remove(skill.name, skill.path);
286
+ await load();
287
+ } catch (error) {
288
+ setState({
289
+ kind: "failed",
290
+ message: error instanceof Error ? error.message : String(error)
291
+ });
292
+ }
293
+ }, [
294
+ api,
295
+ load,
296
+ t
297
+ ]);
298
+ /** Bind one draft key to a change handler. */
299
+ const set = (0, react.useCallback)((key) => (event) => {
300
+ const value = event.target.value;
301
+ setDraft((d) => ({
302
+ ...d,
303
+ [key]: value
304
+ }));
305
+ }, []);
306
+ const submit = (0, react.useCallback)(async () => {
307
+ try {
308
+ await api.create({
309
+ root: draft.root,
310
+ name: draft.name,
311
+ description: draft.description,
312
+ ...draft.whenToUse.trim() === "" ? {} : { whenToUse: draft.whenToUse },
313
+ ...draft.content.trim() === "" ? {} : { content: draft.content }
314
+ });
315
+ setDraft({
316
+ name: "",
317
+ description: "",
318
+ whenToUse: "",
319
+ content: "",
320
+ root: "user"
321
+ });
322
+ setCreating(false);
323
+ await load();
324
+ } catch (error) {
325
+ setState({
326
+ kind: "failed",
327
+ message: error instanceof Error ? error.message : String(error)
328
+ });
329
+ }
330
+ }, [
331
+ api,
332
+ draft,
333
+ load
334
+ ]);
335
+ const total = state.kind === "ready" ? state.payload.groups.reduce((n, g) => n + g.skills.length, 0) : 0;
336
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
337
+ className: SkillCenterPage_module_css_default.page,
338
+ children: [
339
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
340
+ className: SkillCenterPage_module_css_default.pageHead,
341
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h1", {
342
+ className: SkillCenterPage_module_css_default.pageTitle,
343
+ children: t("panel")
344
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
345
+ className: SkillCenterPage_module_css_default.pageIntro,
346
+ children: t("panel.intro")
347
+ })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
348
+ className: SkillCenterPage_module_css_default.toolbar,
349
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
350
+ type: "button",
351
+ className: SkillCenterPage_module_css_default.button,
352
+ onClick: () => {
353
+ load();
354
+ },
355
+ children: t("action.refresh")
356
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
357
+ type: "button",
358
+ className: SkillCenterPage_module_css_default.primaryButton,
359
+ onClick: () => {
360
+ setCreating((v) => !v);
361
+ },
362
+ children: t("action.create")
363
+ })]
364
+ })]
365
+ }),
366
+ creating ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
367
+ className: SkillCenterPage_module_css_default.form,
368
+ children: [
369
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DraftField, {
370
+ label: t("create.name"),
371
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
372
+ className: SkillCenterPage_module_css_default.input,
373
+ value: draft.name,
374
+ onChange: set("name")
375
+ })
376
+ }),
377
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DraftField, {
378
+ label: t("create.description"),
379
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
380
+ className: SkillCenterPage_module_css_default.input,
381
+ value: draft.description,
382
+ onChange: set("description")
383
+ })
384
+ }),
385
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DraftField, {
386
+ label: t("create.whenToUse"),
387
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
388
+ className: SkillCenterPage_module_css_default.input,
389
+ value: draft.whenToUse,
390
+ onChange: set("whenToUse")
391
+ })
392
+ }),
393
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DraftField, {
394
+ label: t("create.content"),
395
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
396
+ className: SkillCenterPage_module_css_default.textarea,
397
+ value: draft.content,
398
+ onChange: set("content")
399
+ })
400
+ }),
401
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DraftField, {
402
+ label: t("create.root"),
403
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
404
+ className: SkillCenterPage_module_css_default.input,
405
+ value: draft.root,
406
+ onChange: (e) => {
407
+ setDraft((d) => ({
408
+ ...d,
409
+ root: e.target.value === "project" ? "project" : "user"
410
+ }));
411
+ },
412
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
413
+ value: "user",
414
+ children: t("create.rootUser")
415
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
416
+ value: "project",
417
+ children: t("create.rootProject")
418
+ })]
419
+ })
420
+ }),
421
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
422
+ className: SkillCenterPage_module_css_default.formActions,
423
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
424
+ type: "button",
425
+ className: SkillCenterPage_module_css_default.button,
426
+ onClick: () => {
427
+ setCreating(false);
428
+ },
429
+ children: t("action.cancel")
430
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
431
+ type: "button",
432
+ className: SkillCenterPage_module_css_default.primaryButton,
433
+ onClick: () => {
434
+ submit();
435
+ },
436
+ children: t("action.confirm")
437
+ })]
438
+ })
439
+ ]
440
+ }) : null,
441
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
442
+ className: SkillCenterPage_module_css_default.search,
443
+ placeholder: t("search.placeholder"),
444
+ value: query,
445
+ onChange: (e) => {
446
+ setQuery(e.target.value);
447
+ }
448
+ }),
449
+ state.kind === "loading" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
450
+ className: SkillCenterPage_module_css_default.status,
451
+ children: t("state.loading")
452
+ }) : null,
453
+ state.kind === "failed" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
454
+ className: SkillCenterPage_module_css_default.status + " " + SkillCenterPage_module_css_default.statusError,
455
+ children: [
456
+ t("state.failed"),
457
+ ": ",
458
+ state.message
459
+ ]
460
+ }) : null,
461
+ state.kind === "ready" && groups.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
462
+ className: SkillCenterPage_module_css_default.status,
463
+ children: query.trim() === "" ? t("state.empty") : t("search.noMatches")
464
+ }) : null,
465
+ state.kind === "ready" && groups.length > 0 && total === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
466
+ className: SkillCenterPage_module_css_default.status,
467
+ children: t("state.empty")
468
+ }) : null,
469
+ groups.map((group) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
470
+ className: SkillCenterPage_module_css_default.group,
471
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("h2", {
472
+ className: SkillCenterPage_module_css_default.groupTitle,
473
+ children: [
474
+ group.title,
475
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
476
+ className: SkillCenterPage_module_css_default.groupCount,
477
+ children: group.skills.length
478
+ }),
479
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
480
+ className: SkillCenterPage_module_css_default.groupHint,
481
+ children: group.hint
482
+ })
483
+ ]
484
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
485
+ className: SkillCenterPage_module_css_default.cards,
486
+ children: group.skills.map((skill) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillCard, {
487
+ skill,
488
+ t,
489
+ onToggle: (s, enabled) => {
490
+ toggle(s, enabled);
491
+ },
492
+ onDelete: (s) => {
493
+ remove(s);
494
+ }
495
+ }, group.key + ":" + skill.name))
496
+ })]
497
+ }, group.key))
498
+ ]
499
+ });
500
+ }
501
+ //#endregion
502
+ //#region src/client/SkillCenterPanelIcon.tsx
503
+ /**
504
+ * Render the glyph at the size the sidebar asks for.
505
+ * @param props - the sidebar's icon share: requested edge and selected state.
506
+ * @returns the icon element.
507
+ */
508
+ function SkillCenterPanelIcon({ size }) {
509
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSkillOutline16, { size });
510
+ }
511
+ //#endregion
512
+ //#region src/client/locales.ts
513
+ /** Simplified Chinese dictionary. */
514
+ const zh = {
515
+ "panel": "技能中心",
516
+ "panel.intro": "浏览已加载的技能,启用或停用模型调用",
517
+ "search.placeholder": "搜索技能…",
518
+ "search.clear": "清除搜索",
519
+ "search.noMatches": "无匹配技能",
520
+ "action.refresh": "刷新",
521
+ "action.create": "新建技能",
522
+ "action.delete": "删除",
523
+ "action.cancel": "取消",
524
+ "action.confirm": "确定",
525
+ "state.loading": "正在加载技能…",
526
+ "state.empty": "该分组暂无技能",
527
+ "state.failed": "技能列表加载失败",
528
+ "skill.enabled": "已启用",
529
+ "skill.disabled": "已停用",
530
+ "skill.modelInvocable": "模型可调用",
531
+ "skill.userInvocable": "用户可调用",
532
+ "skill.linked": "符号链接(不可删除)",
533
+ "create.title": "新建技能",
534
+ "create.name": "名称(小写连字符)",
535
+ "create.description": "描述",
536
+ "create.whenToUse": "何时使用(可选)",
537
+ "create.content": "正文(可选)",
538
+ "create.root": "位置",
539
+ "create.rootUser": "用户级",
540
+ "create.rootProject": "项目级",
541
+ "delete.confirm": "删除后可从 .trash 恢复。确定删除?"
542
+ };
543
+ /** English dictionary. */
544
+ const en = {
545
+ "panel": "Skill Center",
546
+ "panel.intro": "Browse loaded skills and control model invocation",
547
+ "search.placeholder": "Search skills…",
548
+ "search.clear": "Clear search",
549
+ "search.noMatches": "No matching skills",
550
+ "action.refresh": "Refresh",
551
+ "action.create": "New skill",
552
+ "action.delete": "Delete",
553
+ "action.cancel": "Cancel",
554
+ "action.confirm": "Confirm",
555
+ "state.loading": "Loading skills…",
556
+ "state.empty": "No skills in this group",
557
+ "state.failed": "Could not load the skill list",
558
+ "skill.enabled": "Enabled",
559
+ "skill.disabled": "Disabled",
560
+ "skill.modelInvocable": "Model invocable",
561
+ "skill.userInvocable": "User invocable",
562
+ "skill.linked": "Symlink (cannot delete)",
563
+ "create.title": "New skill",
564
+ "create.name": "Name (lowercase kebab-case)",
565
+ "create.description": "Description",
566
+ "create.whenToUse": "When to use (optional)",
567
+ "create.content": "Body (optional)",
568
+ "create.root": "Location",
569
+ "create.rootUser": "User",
570
+ "create.rootProject": "Project",
571
+ "delete.confirm": "Deleting moves it into .trash, where it can be restored. Delete?"
572
+ };
573
+ //#endregion
574
+ //#region src/client/index.ts
575
+ /** Dictionary namespace owned by this plugin. */
576
+ const NS = "skillCenter";
577
+ /** The id shared by the sidebar entry and the main panel it opens. */
578
+ const PANEL_ID = "skill-center";
579
+ /** Services required by the sidebar and page registrations. */
580
+ const inject = ["slots", "locale"];
581
+ /**
582
+ * Contribute the Skill Center entry and the page it opens.
583
+ * @param ctx - the browser plugin context.
584
+ */
585
+ function apply(ctx) {
586
+ ctx.effect(() => ctx.locale.register(NS, {
587
+ zh,
588
+ en
589
+ }), "ui-skill-center: dictionaries");
590
+ const t = ctx.locale.bind(NS);
591
+ ctx.slots.inject("main", () => ctx.slots.register({
592
+ name: "main",
593
+ key: PANEL_ID,
594
+ locale: NS
595
+ }, SkillCenterPage));
596
+ ctx.slots.inject("sidebar.panellist", () => ctx.slots.register({
597
+ name: "sidebar.panellist",
598
+ id: PANEL_ID,
599
+ order: 10,
600
+ label: () => t("panel"),
601
+ locale: NS
602
+ }, SkillCenterPanelIcon));
603
+ }
604
+ //#endregion
605
+ exports.NS = NS;
606
+ exports.PANEL_ID = PANEL_ID;
607
+ exports.apply = apply;
608
+ exports.inject = inject;
609
+ return module.exports;
610
+ }
611
+ });
612
+
613
+ //# sourceMappingURL=client.js.map