@dawn0608/dsh-skill-manager 0.0.0-stage → 0.1.1

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,667 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@dawn0608/dsh-skill-manager",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ var __defProp = Object.defineProperty;
7
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
8
+ var __getOwnPropNames = Object.getOwnPropertyNames;
9
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
10
+ var __export = (target, all) => {
11
+ for (var name in all)
12
+ __defProp(target, name, { get: all[name], enumerable: true });
13
+ };
14
+ var __copyProps = (to, from, except, desc) => {
15
+ if (from && typeof from === "object" || typeof from === "function") {
16
+ for (let key of __getOwnPropNames(from))
17
+ if (!__hasOwnProp.call(to, key) && key !== except)
18
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
19
+ }
20
+ return to;
21
+ };
22
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
23
+
24
+ // src/client/index.js
25
+ var index_exports = {};
26
+ __export(index_exports, {
27
+ apply: () => apply,
28
+ inject: () => inject
29
+ });
30
+ module.exports = __toCommonJS(index_exports);
31
+ var import_react3 = require("react");
32
+ var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
33
+
34
+ // icon.svg
35
+ var icon_default = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36" width="36" height="36">%0A <path d="M18 9 27 14v8l-9 5-9-5v-8Z"%0A fill="none" stroke="%23527ee8" stroke-width="2" stroke-linejoin="round"/>%0A <path d="m18 13.5 1.5 3 3 1.5-3 1.5-1.5 3-1.5-3-3-1.5 3-1.5Z" fill="%23527ee8"/>%0A</svg>%0A';
36
+
37
+ // src/client/i18n.js
38
+ var NS = "skillManager";
39
+ var DICTS = {
40
+ en: {
41
+ title: "Skills",
42
+ search: "Search skills",
43
+ imported: "Imported",
44
+ builtin: "Built-in skills",
45
+ self: "Self-authored skills",
46
+ "third-party": "Third-party skills",
47
+ loading: "Loading skills\u2026",
48
+ empty: "No skills to display.",
49
+ noMatch: "No skills match your search.",
50
+ shadowed: "Shadowed",
51
+ openDetail: "View {name}",
52
+ switchLabel: "Enable {name}",
53
+ back: "Back to Skills",
54
+ "origin-builtin": "Built-in",
55
+ "origin-self": "Self-authored",
56
+ "origin-third-party": "Third-party",
57
+ unknownOrigin: "Origin unconfirmed",
58
+ openRepository: "Open original repository {repository}",
59
+ preview: "SKILL.md preview",
60
+ loadingPreview: "Loading SKILL.md\u2026",
61
+ retry: "Retry",
62
+ copy: "Copy",
63
+ copied: "Copied",
64
+ codeBlock: "Code block",
65
+ wrap: "Wrap lines",
66
+ unwrap: "Stop wrapping lines",
67
+ footnotes: "Footnotes",
68
+ uninstall: "Uninstall",
69
+ uninstallLabel: "Uninstall {name}",
70
+ uninstallTitle: "Uninstall skill?",
71
+ uninstallHint: "Remove the imported copy of \u201C{name}\u201D from Harness?",
72
+ cancel: "Cancel",
73
+ saving: "Saving\u2026",
74
+ invalid: "The request is invalid.",
75
+ conflict: "The skill catalog changed. Wait for the list to refresh and try again.",
76
+ sharedConflict: "A shared skill has conflicting settings or a matching name in another location.",
77
+ sharedUnavailable: "Could not sync a shared skill. The background service will retry.",
78
+ "not-found": "This skill could not be found.",
79
+ unavailable: "Could not read from or connect to Harness.",
80
+ forbidden: "This page does not have permission to perform the action.",
81
+ internal: "The operation failed. Check the Harness logs.",
82
+ incomplete: "The skill catalog is incomplete. This page will retry automatically."
83
+ },
84
+ zh: {
85
+ title: "\u6280\u80FD",
86
+ search: "\u641C\u7D22\u6280\u80FD",
87
+ imported: "\u5DF2\u5BFC\u5165",
88
+ builtin: "\u5185\u7F6E\u6280\u80FD",
89
+ self: "\u81EA\u5EFA\u6280\u80FD",
90
+ "third-party": "\u7B2C\u4E09\u65B9\u6280\u80FD",
91
+ loading: "\u6B63\u5728\u52A0\u8F7D\u6280\u80FD\u2026",
92
+ empty: "\u6682\u65E0\u53EF\u663E\u793A\u7684\u6280\u80FD\u3002",
93
+ noMatch: "\u6CA1\u6709\u7B26\u5408\u641C\u7D22\u6761\u4EF6\u7684\u6280\u80FD\u3002",
94
+ shadowed: "\u5DF2\u88AB\u8986\u76D6",
95
+ openDetail: "\u67E5\u770B {name}",
96
+ switchLabel: "\u542F\u7528 {name}",
97
+ back: "\u8FD4\u56DE\u6280\u80FD\u5217\u8868",
98
+ "origin-builtin": "\u5185\u7F6E",
99
+ "origin-self": "\u81EA\u5EFA",
100
+ "origin-third-party": "\u7B2C\u4E09\u65B9",
101
+ unknownOrigin: "\u5F52\u5C5E\u5F85\u786E\u8BA4",
102
+ openRepository: "\u67E5\u770B\u539F\u4ED3\u5E93 {repository}",
103
+ preview: "SKILL.md \u9884\u89C8",
104
+ loadingPreview: "\u6B63\u5728\u52A0\u8F7D SKILL.md\u2026",
105
+ retry: "\u91CD\u8BD5",
106
+ copy: "\u590D\u5236",
107
+ copied: "\u5DF2\u590D\u5236",
108
+ codeBlock: "\u4EE3\u7801\u5757",
109
+ wrap: "\u81EA\u52A8\u6362\u884C",
110
+ unwrap: "\u53D6\u6D88\u81EA\u52A8\u6362\u884C",
111
+ footnotes: "\u811A\u6CE8",
112
+ uninstall: "\u5378\u8F7D",
113
+ uninstallLabel: "\u5378\u8F7D {name}",
114
+ uninstallTitle: "\u5378\u8F7D\u6280\u80FD\uFF1F",
115
+ uninstallHint: "\u4ECE Harness \u4E2D\u79FB\u9664\u6280\u80FD\u300C{name}\u300D\u7684\u5BFC\u5165\u526F\u672C\uFF1F",
116
+ cancel: "\u53D6\u6D88",
117
+ saving: "\u6B63\u5728\u4FDD\u5B58\u2026",
118
+ invalid: "\u8BF7\u6C42\u65E0\u6548\u3002",
119
+ conflict: "\u6280\u80FD\u5217\u8868\u5DF2\u53D8\u5316\u3002\u8BF7\u7B49\u5F85\u5217\u8868\u5237\u65B0\u540E\u91CD\u8BD5\u3002",
120
+ sharedConflict: "\u5171\u4EAB\u6280\u80FD\u5B58\u5728\u8BBE\u7F6E\u51B2\u7A81\u6216\u540C\u540D\u6761\u76EE\u3002",
121
+ sharedUnavailable: "\u5171\u4EAB\u6280\u80FD\u540C\u6B65\u5931\u8D25\u3002\u540E\u53F0\u4F1A\u81EA\u52A8\u91CD\u8BD5\u3002",
122
+ "not-found": "\u627E\u4E0D\u5230\u6B64\u6280\u80FD\u3002",
123
+ unavailable: "\u65E0\u6CD5\u8FDE\u63A5 Harness \u6216\u8BFB\u53D6\u6570\u636E\u3002",
124
+ forbidden: "\u5F53\u524D\u9875\u9762\u6CA1\u6709\u6267\u884C\u6B64\u64CD\u4F5C\u7684\u6743\u9650\u3002",
125
+ internal: "\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u67E5\u770B Harness \u65E5\u5FD7\u3002",
126
+ incomplete: "\u6280\u80FD\u5217\u8868\u4E0D\u5B8C\u6574\u3002\u9875\u9762\u4F1A\u81EA\u52A8\u91CD\u8BD5\u3002"
127
+ }
128
+ };
129
+ function translateFrom(dict, key, params) {
130
+ const template = Object.hasOwn(dict, key) ? dict[key] : DICTS.en.internal;
131
+ return template.replace(/\{(\w+)\}/g, (match, name) => params?.[name] ?? match);
132
+ }
133
+ function createTranslator(locale) {
134
+ const translate = locale.bind(NS);
135
+ return (key, params) => {
136
+ const value = translate(key, params);
137
+ if (typeof value === "string" && value !== key && value !== NS + "." + key) return value;
138
+ const language = locale.getSnapshot().active?.toLowerCase().startsWith("zh") ? "zh" : "en";
139
+ return translateFrom(DICTS[language], key, params);
140
+ };
141
+ }
142
+
143
+ // src/client/page.js
144
+ var import_react2 = require("react");
145
+
146
+ // src/client/api.js
147
+ var SkillError = class extends Error {
148
+ constructor(code) {
149
+ super(code);
150
+ this.code = code;
151
+ }
152
+ };
153
+ var HOST_ERRORS = /* @__PURE__ */ new Set([
154
+ "invalid",
155
+ "conflict",
156
+ "not-found",
157
+ "unavailable",
158
+ "forbidden",
159
+ "internal",
160
+ "incomplete"
161
+ ]);
162
+ async function request(body, signal, fetchImpl = globalThis.fetch) {
163
+ let response;
164
+ try {
165
+ response = await fetchImpl("api/skill-manager", {
166
+ method: "POST",
167
+ headers: {
168
+ accept: "application/json",
169
+ "content-type": "application/json",
170
+ "x-dsh-skill-manager": "1"
171
+ },
172
+ cache: "no-store",
173
+ credentials: "same-origin",
174
+ body: JSON.stringify(body),
175
+ signal
176
+ });
177
+ } catch {
178
+ throw new SkillError("unavailable");
179
+ }
180
+ const result = await response.json().catch(() => null);
181
+ if (result?.ok === false && HOST_ERRORS.has(result.code)) throw new SkillError(result.code);
182
+ if (response.ok && result?.ok === true) return result.value;
183
+ throw new SkillError([401, 403].includes(response.status) ? "forbidden" : "unavailable");
184
+ }
185
+
186
+ // src/client/catalog.js
187
+ function createCatalog(send = request) {
188
+ const listeners = /* @__PURE__ */ new Set();
189
+ let snapshot = {
190
+ skills: null,
191
+ listError: null,
192
+ mutationError: null,
193
+ busy: false,
194
+ savingId: null
195
+ };
196
+ let reading;
197
+ let timer;
198
+ let stopped = false;
199
+ let hasCompleteCatalog = false;
200
+ function publish(patch) {
201
+ snapshot = { ...snapshot, ...patch };
202
+ for (const listener of listeners) listener();
203
+ }
204
+ function cancelRefresh() {
205
+ reading?.abort();
206
+ reading = void 0;
207
+ }
208
+ async function refresh() {
209
+ if (snapshot.busy || stopped) return;
210
+ cancelRefresh();
211
+ const pending = new AbortController();
212
+ reading = pending;
213
+ try {
214
+ const result = await send(
215
+ { action: "list", workspaceId: null },
216
+ AbortSignal.any([pending.signal, AbortSignal.timeout(8e3)])
217
+ );
218
+ if (reading !== pending) return;
219
+ if (!Array.isArray(result?.skills) || typeof result.complete !== "boolean") {
220
+ throw new SkillError("unavailable");
221
+ }
222
+ if (result.complete) {
223
+ hasCompleteCatalog = true;
224
+ publish({ skills: result.skills, listError: result.syncError ?? null });
225
+ } else if (!hasCompleteCatalog) {
226
+ publish({ skills: result.skills, listError: "incomplete" });
227
+ } else {
228
+ publish({ listError: "incomplete" });
229
+ }
230
+ } catch (error) {
231
+ if (reading === pending) publish({ listError: error.code ?? "unavailable" });
232
+ } finally {
233
+ if (reading === pending) reading = void 0;
234
+ }
235
+ }
236
+ async function mutate(action) {
237
+ if (snapshot.busy || stopped) return false;
238
+ cancelRefresh();
239
+ publish({ busy: true, savingId: action.action === "setEnabled" ? action.id : null, mutationError: null });
240
+ try {
241
+ await send({ ...action, workspaceId: null }, AbortSignal.timeout(3e4));
242
+ let { skills } = snapshot;
243
+ if (skills && action.action === "setEnabled") {
244
+ skills = skills.map((skill) => skill.id === action.id ? { ...skill, enabled: action.enabled } : skill);
245
+ } else if (skills && action.action === "remove") {
246
+ skills = skills.filter((skill) => skill.id !== action.id);
247
+ }
248
+ publish({ skills, busy: false, savingId: null });
249
+ void refresh();
250
+ return true;
251
+ } catch (error) {
252
+ publish({ mutationError: error.code ?? "unavailable", busy: false, savingId: null });
253
+ if (error.code === "conflict" || error.code === "not-found") void refresh();
254
+ return false;
255
+ }
256
+ }
257
+ return {
258
+ getSnapshot: () => snapshot,
259
+ subscribe(listener) {
260
+ listeners.add(listener);
261
+ return () => {
262
+ listeners.delete(listener);
263
+ };
264
+ },
265
+ refresh,
266
+ mutate,
267
+ clearMutationError() {
268
+ if (snapshot.mutationError) publish({ mutationError: null });
269
+ },
270
+ start() {
271
+ if (timer !== void 0) return;
272
+ stopped = false;
273
+ void refresh();
274
+ timer = setInterval(() => {
275
+ if (!reading) void refresh();
276
+ }, 4e3);
277
+ },
278
+ stop() {
279
+ stopped = true;
280
+ clearInterval(timer);
281
+ timer = void 0;
282
+ cancelRefresh();
283
+ }
284
+ };
285
+ }
286
+
287
+ // src/client/dialog.js
288
+ var import_react = require("react");
289
+ function Modal({ title, busy, close, children }) {
290
+ const ref = (0, import_react.useRef)(null);
291
+ const titleId = (0, import_react.useId)();
292
+ (0, import_react.useLayoutEffect)(() => {
293
+ const dialog = ref.current;
294
+ dialog.showModal();
295
+ dialog.querySelector("[data-initial-focus]")?.focus();
296
+ return () => dialog.close();
297
+ }, []);
298
+ return (0, import_react.createElement)("dialog", {
299
+ className: "dshskill_dialog",
300
+ ref,
301
+ "aria-labelledby": titleId,
302
+ onCancel(event) {
303
+ event.preventDefault();
304
+ if (!busy) close();
305
+ }
306
+ }, (0, import_react.createElement)("h2", { id: titleId }, title), children);
307
+ }
308
+ function Feedback({ error, busy, translate }) {
309
+ return (0, import_react.createElement)(
310
+ "div",
311
+ { className: "dshskill_feedback" },
312
+ busy && (0, import_react.createElement)("p", { role: "status" }, translate("saving")),
313
+ error && (0, import_react.createElement)("p", { className: "dshskill_error", role: "alert" }, translate(error))
314
+ );
315
+ }
316
+ function RemoveDialog({ skill, translate: t, store, busy, error, close, removed }) {
317
+ async function remove() {
318
+ if (busy) return;
319
+ const ok = await store.mutate({ action: "remove", id: skill.id });
320
+ if (ok) removed();
321
+ }
322
+ return (0, import_react.createElement)(
323
+ Modal,
324
+ { title: t("uninstallTitle"), busy, close },
325
+ (0, import_react.createElement)("p", { className: "dshskill_muted" }, t("uninstallHint", { name: skill.name })),
326
+ (0, import_react.createElement)(Feedback, { error, busy, translate: t }),
327
+ (0, import_react.createElement)(
328
+ "div",
329
+ { className: "dshskill_dialogActions" },
330
+ (0, import_react.createElement)("button", {
331
+ type: "button",
332
+ className: "dshskill_control",
333
+ "data-initial-focus": "",
334
+ "aria-disabled": busy || void 0,
335
+ onClick: () => {
336
+ if (!busy) close();
337
+ }
338
+ }, t("cancel")),
339
+ (0, import_react.createElement)("button", { type: "button", className: "dshskill_control dshskill_danger", "aria-disabled": busy || void 0, onClick: remove }, t("uninstall"))
340
+ )
341
+ );
342
+ }
343
+
344
+ // src/client/page.js
345
+ var GROUPS = ["builtin", "self", "third-party"];
346
+ function SkillToggle({ skill, busy, saving, store, translate: t, controls }) {
347
+ const { StateDot: StateDot2, Switch: Switch2 } = controls;
348
+ return (0, import_react2.createElement)(
349
+ "span",
350
+ { className: "dshskill_toggle" },
351
+ (0, import_react2.createElement)(
352
+ "span",
353
+ { className: "dshskill_toggleProgress", role: "status" },
354
+ (0, import_react2.createElement)("span", { className: "dshskill_progressAnnouncement" }, saving ? t("saving") : ""),
355
+ saving && (0, import_react2.createElement)(StateDot2, { state: "ongoing" })
356
+ ),
357
+ (0, import_react2.createElement)(Switch2, {
358
+ checked: skill.enabled,
359
+ label: t("switchLabel", { name: skill.name }),
360
+ disabled: busy,
361
+ onChange: (enabled) => {
362
+ void store.mutate({ action: "setEnabled", id: skill.id, enabled });
363
+ }
364
+ })
365
+ );
366
+ }
367
+ function CatalogRows({ skills, query, translate: t, busy, savingId, store, openDetail, controls }) {
368
+ const { SkillEmblem: SkillEmblem2, Tag: Tag2 } = controls;
369
+ const headingId = (0, import_react2.useId)();
370
+ const term = query.trim().toLocaleLowerCase();
371
+ const visible = skills.filter((skill) => !term || [skill.name, skill.description].some((text) => text?.toLocaleLowerCase().includes(term)));
372
+ if (!visible.length) return (0, import_react2.createElement)("p", { className: "dshskill_muted", role: "status" }, t(term ? "noMatch" : "empty"));
373
+ return (0, import_react2.createElement)("div", { className: "dshskill_groups" }, GROUPS.map((group) => {
374
+ const entries = visible.filter((skill) => (skill.origin ?? "third-party") === group);
375
+ if (!entries.length) return null;
376
+ const id = headingId + group;
377
+ return (0, import_react2.createElement)(
378
+ "section",
379
+ { key: group, className: "dshskill_group", "aria-labelledby": id },
380
+ (0, import_react2.createElement)("h2", { id }, t(group)),
381
+ (0, import_react2.createElement)("ul", { className: "dshskill_rows" }, entries.map((skill) => {
382
+ const descriptionId = skill.description ? headingId + "-" + skill.id : void 0;
383
+ const saving = savingId === skill.id;
384
+ return (0, import_react2.createElement)(
385
+ "li",
386
+ { key: skill.id, className: "dshskill_row" },
387
+ (0, import_react2.createElement)("span", { className: "dshskill_icon" }, (0, import_react2.createElement)(SkillEmblem2, { size: 24 })),
388
+ (0, import_react2.createElement)(
389
+ "div",
390
+ { className: "dshskill_rowText" },
391
+ (0, import_react2.createElement)(
392
+ "div",
393
+ { className: "dshskill_titleRow" },
394
+ (0, import_react2.createElement)("button", {
395
+ type: "button",
396
+ className: "dshskill_rowButton",
397
+ "data-skill-id": skill.id,
398
+ "aria-label": t("openDetail", { name: skill.name }),
399
+ "aria-describedby": descriptionId,
400
+ onClick: () => openDetail(skill)
401
+ }, skill.name),
402
+ skill.shadowed && (0, import_react2.createElement)(Tag2, { className: "dshskill_badge" }, t("shadowed"))
403
+ ),
404
+ descriptionId && (0, import_react2.createElement)("span", { className: "dshskill_description", id: descriptionId }, skill.description)
405
+ ),
406
+ typeof skill.enabled === "boolean" && (0, import_react2.createElement)(SkillToggle, { skill, busy, saving, store, translate: t, controls })
407
+ );
408
+ }))
409
+ );
410
+ }));
411
+ }
412
+ function SkillDetail({ skill, titleRef, translate: t, busy, saving, back, openRemove, controls, store }) {
413
+ const { BackIcon, Button: Button2, SkillDocument: SkillDocument2, SkillEmblem: SkillEmblem2, Tag: Tag2, TrashIcon } = controls;
414
+ const [preview, setPreview] = (0, import_react2.useState)(null);
415
+ const [attempt, setAttempt] = (0, import_react2.useState)(0);
416
+ (0, import_react2.useEffect)(() => {
417
+ const pending = new AbortController();
418
+ request(
419
+ { action: "detail", workspaceId: null, id: skill.id },
420
+ AbortSignal.any([pending.signal, AbortSignal.timeout(8e3)])
421
+ ).then((result) => {
422
+ if (!pending.signal.aborted) setPreview(result);
423
+ }).catch((error) => {
424
+ if (!pending.signal.aborted) setPreview({ error: error.code ?? "unavailable" });
425
+ });
426
+ return () => pending.abort();
427
+ }, [skill.id, attempt]);
428
+ const imported = skill.group === "imported";
429
+ const canToggle = typeof skill.enabled === "boolean";
430
+ const origin = preview?.origin ?? skill.origin;
431
+ const repository = origin === "third-party" ? preview?.repository : void 0;
432
+ const repositoryLabel = repository?.replace(/^https:\/\/(?:github\.com\/)?/, "").replace(/\/$/, "");
433
+ const originLabel = t(origin ? "origin-" + origin : imported ? "imported" : "unknownOrigin");
434
+ return (0, import_react2.createElement)(
435
+ "div",
436
+ { className: "dshskill_detail" },
437
+ (0, import_react2.createElement)(
438
+ "header",
439
+ { className: "dshskill_detailTop" },
440
+ (0, import_react2.createElement)(
441
+ "button",
442
+ { type: "button", className: "dshskill_back", "aria-label": t("back"), onClick: back },
443
+ (0, import_react2.createElement)(BackIcon, { className: "dshskill_backIcon", "aria-hidden": true }),
444
+ (0, import_react2.createElement)("span", null, t("title"))
445
+ ),
446
+ (0, import_react2.createElement)(
447
+ "div",
448
+ { className: "dshskill_detailHead" },
449
+ (0, import_react2.createElement)("span", { className: "dshskill_icon", "aria-hidden": true }, (0, import_react2.createElement)(SkillEmblem2, { branded: true, size: 36 })),
450
+ (imported || canToggle) && (0, import_react2.createElement)(
451
+ "div",
452
+ { className: "dshskill_detailActions" },
453
+ imported && (0, import_react2.createElement)(Button2, {
454
+ type: "button",
455
+ variant: "outline",
456
+ size: "sm",
457
+ className: "dshskill_detailUninstall",
458
+ icon: (0, import_react2.createElement)(TrashIcon, { size: 13 }),
459
+ disabled: busy,
460
+ "aria-label": t("uninstallLabel", { name: skill.name }),
461
+ onClick: openRemove
462
+ }, t("uninstall")),
463
+ canToggle && (0, import_react2.createElement)(SkillToggle, { skill, busy, saving, store, translate: t, controls })
464
+ )
465
+ )
466
+ ),
467
+ (0, import_react2.createElement)(
468
+ "div",
469
+ { className: "dshskill_detailMain" },
470
+ (0, import_react2.createElement)(
471
+ "div",
472
+ { className: "dshskill_detailTitleRow" },
473
+ (0, import_react2.createElement)("h1", { ref: titleRef, tabIndex: -1, title: skill.name }, skill.name),
474
+ repository ? (0, import_react2.createElement)(
475
+ "a",
476
+ {
477
+ className: "dshskill_detailAttribution dshskill_repository",
478
+ href: repository,
479
+ target: "_blank",
480
+ rel: "noopener noreferrer",
481
+ title: repositoryLabel,
482
+ "aria-label": t("openRepository", { repository: repositoryLabel })
483
+ },
484
+ (0, import_react2.createElement)("span", { className: "dshskill_attributionText" }, repositoryLabel),
485
+ (0, import_react2.createElement)("span", { "aria-hidden": true }, "\u2197")
486
+ ) : (0, import_react2.createElement)(
487
+ Tag2,
488
+ { className: "dshskill_detailAttribution", tone: "neutral" },
489
+ (0, import_react2.createElement)("span", { className: "dshskill_attributionText", title: originLabel }, originLabel)
490
+ )
491
+ ),
492
+ skill.description && (0, import_react2.createElement)("p", { className: "dshskill_detailDescription" }, skill.description)
493
+ ),
494
+ (0, import_react2.createElement)(
495
+ "div",
496
+ { className: "dshskill_detailSections" },
497
+ (0, import_react2.createElement)(SkillDocument2, { preview, translate: t, onRetry: () => {
498
+ setPreview(null);
499
+ setAttempt((value) => value + 1);
500
+ } })
501
+ )
502
+ );
503
+ }
504
+ function createPage(locale, controls, createStore = createCatalog) {
505
+ const t = createTranslator(locale);
506
+ const subscribeLocale = (listener) => locale.subscribe(listener);
507
+ const localeVersion = () => locale.getSnapshot().revision;
508
+ return function SkillsPage() {
509
+ (0, import_react2.useSyncExternalStore)(subscribeLocale, localeVersion, localeVersion);
510
+ const [store] = (0, import_react2.useState)(createStore);
511
+ const state = (0, import_react2.useSyncExternalStore)(store.subscribe, store.getSnapshot, store.getSnapshot);
512
+ const [query, setQuery] = (0, import_react2.useState)("");
513
+ const [detail, setDetail] = (0, import_react2.useState)(null);
514
+ const [confirmRemoval, setConfirmRemoval] = (0, import_react2.useState)(false);
515
+ const pageRef = (0, import_react2.useRef)(null);
516
+ const titleRef = (0, import_react2.useRef)(null);
517
+ const returnTo = (0, import_react2.useRef)(null);
518
+ (0, import_react2.useEffect)(() => {
519
+ store.start();
520
+ return () => store.stop();
521
+ }, [store]);
522
+ (0, import_react2.useLayoutEffect)(() => {
523
+ if (detail) {
524
+ pageRef.current.scrollTop = 0;
525
+ titleRef.current.focus({ preventScroll: true });
526
+ } else if (returnTo.current !== null) {
527
+ const { id, scrollTop } = returnTo.current;
528
+ const row = Array.from(pageRef.current.querySelectorAll("[data-skill-id]")).find((button) => button.dataset.skillId === id);
529
+ if (row) {
530
+ pageRef.current.scrollTop = scrollTop;
531
+ row.focus({ preventScroll: true });
532
+ } else {
533
+ titleRef.current.focus();
534
+ }
535
+ returnTo.current = null;
536
+ }
537
+ }, [detail]);
538
+ function openDetail(skill) {
539
+ returnTo.current = { id: skill.id, scrollTop: pageRef.current.scrollTop };
540
+ setDetail(skill);
541
+ store.clearMutationError();
542
+ }
543
+ function closeDialog() {
544
+ setConfirmRemoval(false);
545
+ store.clearMutationError();
546
+ }
547
+ function removed() {
548
+ closeDialog();
549
+ setDetail(null);
550
+ }
551
+ const { skills, busy, savingId, listError, mutationError } = state;
552
+ const currentSkill = detail && (skills?.find((skill) => skill.id === detail.id) ?? detail);
553
+ return (0, import_react2.createElement)(
554
+ "div",
555
+ { className: "dshskill_page", "data-detail": currentSkill ? "" : void 0, ref: pageRef },
556
+ (0, import_react2.createElement)(
557
+ "div",
558
+ { className: "dshskill_content" },
559
+ currentSkill ? (0, import_react2.createElement)(SkillDetail, {
560
+ skill: currentSkill,
561
+ titleRef,
562
+ translate: t,
563
+ busy,
564
+ saving: savingId === currentSkill.id,
565
+ controls,
566
+ store,
567
+ back: () => setDetail(null),
568
+ openRemove: () => {
569
+ store.clearMutationError();
570
+ setConfirmRemoval(true);
571
+ }
572
+ }) : (0, import_react2.createElement)(
573
+ "div",
574
+ { className: "dshskill_catalog" },
575
+ (0, import_react2.createElement)(
576
+ "header",
577
+ { className: "dshskill_header" },
578
+ (0, import_react2.createElement)("h1", { ref: titleRef, tabIndex: -1 }, t("title")),
579
+ (0, import_react2.createElement)(controls.Input, {
580
+ className: "dshskill_search",
581
+ type: "search",
582
+ icon: (0, import_react2.createElement)(controls.SearchIcon, { size: 14 }),
583
+ "aria-label": t("search"),
584
+ placeholder: t("search"),
585
+ value: query,
586
+ onChange: (event) => setQuery(event.currentTarget.value)
587
+ })
588
+ ),
589
+ listError && (0, import_react2.createElement)("p", { role: "alert", className: "dshskill_error" }, t(listError)),
590
+ skills === null ? !listError && (0, import_react2.createElement)("p", { role: "status", className: "dshskill_muted" }, t("loading")) : (0, import_react2.createElement)(CatalogRows, { skills, query, translate: t, busy, savingId, store, openDetail, controls })
591
+ ),
592
+ !confirmRemoval && busy && !savingId && (0, import_react2.createElement)("p", { role: "status", className: "dshskill_muted" }, t("saving")),
593
+ !confirmRemoval && mutationError && (0, import_react2.createElement)("p", { role: "alert", className: "dshskill_error" }, t(mutationError)),
594
+ confirmRemoval && (0, import_react2.createElement)(RemoveDialog, { skill: currentSkill, translate: t, store, busy, error: mutationError, close: closeDialog, removed })
595
+ )
596
+ );
597
+ };
598
+ }
599
+
600
+ // src/client/style.css
601
+ var style_default = ".dshskill_emblem {\n display: inline-block;\n flex-shrink: 0;\n background-color: currentColor;\n mask-position: center;\n mask-repeat: no-repeat;\n mask-size: 150%;\n}\n\n.dshskill_page {\n --skill-hover: var(--dsw-alias-interactive-bg-hover);\n height: 100%;\n min-width: 0;\n min-height: 0;\n overflow: auto;\n padding: 28px clamp(24px, 4vw, 48px) 48px;\n box-sizing: border-box;\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-bg-base);\n font-size: 13px;\n line-height: 20px;\n}\n\nhtml[data-platform='darwin'] .dshskill_page {\n padding-top: calc(28px + var(--dsh-frame-top-clearance, 0px));\n}\n\n.dshskill_page[data-detail] {\n scrollbar-gutter: stable;\n}\n\n.dshskill_content {\n max-width: 960px;\n margin-inline: auto;\n}\n\n.dshskill_page :where(h1, h2, p, ul):not(:where(.dshskill_detailSections *)) {\n margin: 0;\n}\n\n.dshskill_header h1,\n.dshskill_detailMain h1 {\n font-size: 20px;\n line-height: 28px;\n font-weight: 500;\n overflow-wrap: anywhere;\n}\n\n.dshskill_group h2,\n.dshskill_dialog h2 {\n font-size: 14px;\n line-height: 22px;\n font-weight: 500;\n}\n\n.dshskill_catalog {\n display: flex;\n flex-direction: column;\n gap: 32px;\n}\n\n.dshskill_header {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n}\n\n.dshskill_header > div {\n min-width: 0;\n max-width: 100%;\n}\n\n.dshskill_control {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n min-height: 32px;\n min-width: 0;\n box-sizing: border-box;\n padding: 4px 10px;\n border: 0.5px solid var(--dsw-alias-border-l3);\n border-radius: var(--dsw-radius-lg, 8px);\n background: var(--dsw-alias-bg-base);\n color: inherit;\n font: inherit;\n}\n\n.dshskill_control {\n cursor: pointer;\n}\n\n.dshskill_control:not([aria-disabled='true']):hover,\n.dshskill_row:hover {\n background: var(--skill-hover);\n}\n\n.dshskill_control[aria-disabled='true'] {\n opacity: 0.55;\n cursor: default;\n}\n\n.dshskill_search {\n box-sizing: border-box;\n width: 200px;\n max-width: 100%;\n min-width: 0;\n}\n\n.dshskill_search input::placeholder {\n color: var(--dsw-alias-label-secondary);\n}\n\n.dshskill_page :where(.dshskill_control, .dshskill_back, .dshskill_header h1, .dshskill_detailMain h1, .dshskill_repository):focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary));\n outline-offset: 2px;\n}\n\n.dshskill_groups,\n.dshskill_group,\n.dshskill_rows {\n display: flex;\n flex-direction: column;\n}\n\n.dshskill_groups {\n gap: 32px;\n}\n\n.dshskill_group {\n gap: 8px;\n}\n\n.dshskill_rows {\n gap: 2px;\n padding: 0;\n list-style: none;\n}\n\n.dshskill_row {\n position: relative;\n display: flex;\n align-items: center;\n gap: 14px;\n min-width: 0;\n margin-inline: -8px;\n padding: 8px;\n border-radius: var(--dsw-radius-xl, 12px);\n line-height: normal;\n}\n\n.dshskill_row:active {\n background: var(--dsw-alias-interactive-bg-active);\n}\n\n.dshskill_rowButton {\n max-width: 100%;\n border: 0;\n padding: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n font-size: 14px;\n line-height: 20px;\n font-weight: 500;\n text-align: start;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n cursor: pointer;\n}\n\n.dshskill_rowButton::after {\n position: absolute;\n inset: 0;\n border-radius: var(--dsw-radius-xl, 12px);\n content: '';\n}\n\n.dshskill_rowButton:focus-visible {\n outline: none;\n}\n\n.dshskill_rowButton:focus-visible::after {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary));\n outline-offset: 2px;\n}\n\n.dshskill_icon {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n border: 0.5px solid var(--dsw-alias-border-l3);\n border-radius: var(--dsw-radius-lg, 8px);\n color: var(--dsw-alias-label-secondary);\n}\n\n.dshskill_rowText {\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: 4px;\n min-width: 0;\n}\n\n.dshskill_titleRow {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n min-width: 0;\n}\n\n.dshskill_muted {\n color: var(--dsw-alias-label-secondary);\n}\n\n.dshskill_description {\n display: -webkit-box;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 1;\n overflow: hidden;\n color: var(--dsw-alias-label-tertiary);\n font-size: 13px;\n line-height: 18px;\n}\n\n.dshskill_badge {\n height: 18px;\n padding: 0 7px;\n font-size: 10px;\n line-height: 1;\n}\n\n.dshskill_toggle {\n display: inline-flex;\n flex: none;\n align-items: center;\n gap: 8px;\n}\n\n.dshskill_row .dshskill_toggle {\n position: relative;\n z-index: 1;\n}\n\n.dshskill_toggleProgress {\n display: inline-flex;\n width: 14px;\n height: 14px;\n}\n\n.dshskill_toggleProgress > svg {\n width: 100%;\n height: 100%;\n}\n\n.dshskill_progressAnnouncement {\n position: absolute;\n clip-path: inset(50%);\n width: 1px;\n height: 1px;\n overflow: hidden;\n white-space: nowrap;\n}\n\n.dshskill_error,\n.dshskill_danger {\n color: var(--dsw-alias-state-error-primary);\n}\n\n.dshskill_detailTop,\n.dshskill_detailMain,\n.dshskill_detailSections {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n.dshskill_back {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 0;\n border: 0;\n background: transparent;\n color: var(--dsw-alias-label-tertiary);\n font: inherit;\n font-size: 12.5px;\n line-height: normal;\n cursor: pointer;\n}\n\n.dshskill_back:hover {\n color: var(--dsw-alias-label-primary);\n}\n\n.dshskill_backIcon {\n transform: rotate(90deg);\n}\n\n.dshskill_detailHead {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n margin-top: 32px;\n}\n\n.dshskill_detailActions {\n display: flex;\n align-items: center;\n gap: 16px;\n}\n\n.dshskill_detailActions .dshskill_detailUninstall {\n color: var(--dsw-alias-state-error-primary);\n --dsh-button-outline-color: color-mix(in srgb, var(--dsw-alias-state-error-primary) 30%, transparent);\n --dsw-alias-interactive-bg-hover: color-mix(in srgb, var(--dsw-alias-state-error-primary) 8%, transparent);\n}\n\n.dshskill_detailMain {\n gap: 8px;\n margin-top: 20px;\n}\n\n.dshskill_detailTitleRow {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n white-space: nowrap;\n}\n\n.dshskill_detailTitleRow h1,\n.dshskill_attributionText {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.dshskill_detailTitleRow .dshskill_detailAttribution {\n box-sizing: border-box;\n flex: none;\n min-width: 0;\n max-width: 45%;\n /* Balance the smaller attribution text against the title's visible glyphs. */\n translate: 0 1px;\n}\n\n.dshskill_repository {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n line-height: 18px;\n text-decoration: underline;\n text-underline-offset: 0.15em;\n}\n\n.dshskill_detailDescription {\n color: var(--dsw-alias-label-secondary);\n font-size: 14px;\n line-height: 22px;\n overflow-wrap: anywhere;\n}\n\n.dshskill_detailSections {\n gap: 32px;\n margin-top: 32px;\n}\n\n.dshskill_dialog {\n position: fixed;\n width: min(440px, calc(100vw - 32px));\n max-width: none;\n max-height: calc(100vh - 32px);\n margin: auto;\n padding: 20px;\n box-sizing: border-box;\n border: 0.5px solid var(--dsw-alias-border-l3);\n border-radius: var(--dsw-radius-xl, 12px);\n background: var(--dsw-alias-bg-base);\n color: var(--dsw-alias-label-primary);\n box-shadow: 0 12px 40px rgb(0 0 0 / 16%);\n}\n\n.dshskill_dialog[open] {\n display: grid;\n gap: 12px;\n}\n\n.dshskill_dialog::backdrop {\n background: rgb(0 0 0 / 26%);\n}\n\n.dshskill_dialogActions {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n justify-content: flex-end;\n}\n\n.dshskill_feedback:empty {\n display: none;\n}\n\n@media (max-width: 767px) {\n .dshskill_page:not([data-detail]) {\n padding-inline: 20px;\n }\n}\n";
602
+
603
+ // src/client/index.js
604
+ var inject = ["slots", "locale"];
605
+ function SkillEmblem({ size = 16, branded = false } = {}) {
606
+ if (branded) return (0, import_react3.createElement)("img", { src: icon_default, width: size, height: size, alt: "" });
607
+ return (0, import_react3.createElement)("span", {
608
+ className: "dshskill_emblem",
609
+ "aria-hidden": true,
610
+ style: { width: size, height: size, maskImage: `url(${JSON.stringify(icon_default)})` }
611
+ });
612
+ }
613
+ var skillDocumentImages = { resolve: () => void 0 };
614
+ function SkillDocument({ preview, translate: t, onRetry }) {
615
+ const labels = (0, import_dsh_client_ui_primitives.useDetailDocumentLabels)({
616
+ title: t("preview"),
617
+ loading: t("loadingPreview"),
618
+ retry: t("retry"),
619
+ copy: t("copy"),
620
+ copied: t("copied"),
621
+ codeBlock: t("codeBlock"),
622
+ wrap: t("wrap"),
623
+ unwrap: t("unwrap"),
624
+ footnotes: t("footnotes")
625
+ });
626
+ const state = preview === null ? void 0 : preview.error ? { status: "error", message: t(preview.error) } : { status: "ready", markdown: preview.body };
627
+ return (0, import_react3.createElement)(import_dsh_client_ui_primitives.DetailDocument, { state: (0, import_dsh_client_ui_primitives.detailDocumentState)(state), labels, images: skillDocumentImages, onRetry });
628
+ }
629
+ function apply(ctx) {
630
+ ctx.effect(() => ctx.locale.register(NS, DICTS), "skill-manager: dictionaries");
631
+ ctx.effect(() => {
632
+ const style = document.createElement("style");
633
+ style.dataset.plugin = "@dawn0608/dsh-skill-manager";
634
+ style.textContent = style_default;
635
+ document.head.appendChild(style);
636
+ return () => style.remove();
637
+ }, "skill-manager: styles");
638
+ const translate = createTranslator(ctx.locale);
639
+ ctx.slots.inject("sidebar.panellist", () => ctx.slots.register({
640
+ name: "sidebar.panellist",
641
+ id: "skill-manager",
642
+ order: 5,
643
+ label: () => translate("title"),
644
+ locale: NS
645
+ }, SkillEmblem));
646
+ ctx.slots.inject("main", () => ctx.slots.register({
647
+ name: "main",
648
+ key: "skill-manager",
649
+ locale: NS
650
+ }, createPage(ctx.locale, {
651
+ BackIcon: import_dsh_client_ui_primitives.IconChevronDownOutlineRegular,
652
+ Button: import_dsh_client_ui_primitives.Button,
653
+ Input: import_dsh_client_ui_primitives.Input,
654
+ SearchIcon: import_dsh_client_ui_primitives.IconSearchOutlineRegular,
655
+ SkillDocument,
656
+ SkillEmblem,
657
+ StateDot: import_dsh_client_ui_primitives.StateDot,
658
+ Switch: import_dsh_client_ui_primitives.Switch,
659
+ Tag: import_dsh_client_ui_primitives.Tag,
660
+ TrashIcon: import_dsh_client_ui_primitives.IconTrashOutlineRegular
661
+ })));
662
+ }
663
+ Object.defineProperty(module.exports, Symbol.toStringTag, { value: "Module" });
664
+ return module.exports;
665
+ },
666
+ });
667
+ //# sourceMappingURL=client.js.map