@sparkelf/dsh-client-ui-skill-center 0.2.0-rc.9 → 0.2.1-alpha.10
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 +166 -132
- package/lib/index.js +158 -24
- package/lib/types/client/SkillCenterPage.d.ts +3 -3
- package/lib/types/client/index.d.ts +1 -0
- package/lib/types/client/slot-contract.d.ts +18 -0
- package/lib/types/index.d.ts +60 -0
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -4,11 +4,11 @@ window.__ModuleLoader__.load({
|
|
|
4
4
|
var module = { exports: {} };
|
|
5
5
|
var exports = module.exports;
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
7
8
|
let react = require("react");
|
|
8
9
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
9
|
-
let react_jsx_runtime = require("react/jsx-runtime");
|
|
10
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:
|
|
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:960px}.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:16px;display:flex}.DGiSta_iconButton{width:28px;height:28px;color:var(--dsw-alias-label-caption);cursor:pointer;background:0 0;border:0;border-radius:28px;justify-content:center;align-items:center;display:inline-flex}.DGiSta_iconButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}.DGiSta_iconButton:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.DGiSta_iconWrap{display:inline-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_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_cardForeign{opacity:.72}.DGiSta_cardMain{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.DGiSta_workspaceBadge{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary);vertical-align:middle;border-radius:4px;margin-left:8px;padding:1px 6px;font-size:11px;font-weight:400;line-height:16px}.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_dialog{width:min(560px,100%)}.DGiSta_form{flex-direction:column;gap:12px;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_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
12
|
const tagId = "@sparkelf/dsh-client-ui-skill-center/SkillCenterPage.module.css";
|
|
13
13
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
14
14
|
const tag = document.createElement("style");
|
|
@@ -22,6 +22,7 @@ window.__ModuleLoader__.load({
|
|
|
22
22
|
"card": "DGiSta_card",
|
|
23
23
|
"cardDesc": "DGiSta_cardDesc",
|
|
24
24
|
"cardEnd": "DGiSta_cardEnd",
|
|
25
|
+
"cardForeign": "DGiSta_cardForeign",
|
|
25
26
|
"cardHead": "DGiSta_cardHead",
|
|
26
27
|
"cardIcon": "DGiSta_cardIcon",
|
|
27
28
|
"cardMain": "DGiSta_cardMain",
|
|
@@ -29,14 +30,16 @@ window.__ModuleLoader__.load({
|
|
|
29
30
|
"cardTitle": "DGiSta_cardTitle",
|
|
30
31
|
"cardWhenToUse": "DGiSta_cardWhenToUse",
|
|
31
32
|
"cards": "DGiSta_cards",
|
|
33
|
+
"dialog": "DGiSta_dialog",
|
|
32
34
|
"field": "DGiSta_field",
|
|
33
35
|
"fieldLabel": "DGiSta_fieldLabel",
|
|
34
36
|
"form": "DGiSta_form",
|
|
35
|
-
"formActions": "DGiSta_formActions",
|
|
36
37
|
"group": "DGiSta_group",
|
|
37
38
|
"groupCount": "DGiSta_groupCount",
|
|
38
39
|
"groupHint": "DGiSta_groupHint",
|
|
39
40
|
"groupTitle": "DGiSta_groupTitle",
|
|
41
|
+
"iconButton": "DGiSta_iconButton",
|
|
42
|
+
"iconWrap": "DGiSta_iconWrap",
|
|
40
43
|
"input": "DGiSta_input",
|
|
41
44
|
"mark": "DGiSta_mark",
|
|
42
45
|
"markDanger": "DGiSta_markDanger",
|
|
@@ -46,17 +49,17 @@ window.__ModuleLoader__.load({
|
|
|
46
49
|
"pageHead": "DGiSta_pageHead",
|
|
47
50
|
"pageIntro": "DGiSta_pageIntro",
|
|
48
51
|
"pageTitle": "DGiSta_pageTitle",
|
|
49
|
-
"primaryButton": "DGiSta_primaryButton",
|
|
50
52
|
"search": "DGiSta_search",
|
|
51
53
|
"status": "DGiSta_status",
|
|
52
54
|
"statusError": "DGiSta_statusError",
|
|
53
55
|
"switch": "DGiSta_switch",
|
|
54
56
|
"switchOn": "DGiSta_switchOn",
|
|
55
57
|
"textarea": "DGiSta_textarea",
|
|
56
|
-
"toolbar": "DGiSta_toolbar"
|
|
58
|
+
"toolbar": "DGiSta_toolbar",
|
|
59
|
+
"workspaceBadge": "DGiSta_workspaceBadge"
|
|
57
60
|
};
|
|
58
61
|
//#endregion
|
|
59
|
-
//#region
|
|
62
|
+
//#region lib/types/client/api.js
|
|
60
63
|
/**
|
|
61
64
|
* Skill center API client. Talks to the host route family over same-origin
|
|
62
65
|
* fetch; the host owns the trust fence.
|
|
@@ -128,7 +131,7 @@ window.__ModuleLoader__.load({
|
|
|
128
131
|
}
|
|
129
132
|
};
|
|
130
133
|
//#endregion
|
|
131
|
-
//#region
|
|
134
|
+
//#region lib/types/client/SkillCenterPage.js
|
|
132
135
|
/**
|
|
133
136
|
* Skill center page. Browses the loaded skills grouped by source, toggles model
|
|
134
137
|
* invocation, creates a skill, and deletes one into a recoverable trash.
|
|
@@ -144,9 +147,9 @@ window.__ModuleLoader__.load({
|
|
|
144
147
|
* @returns the labelled field.
|
|
145
148
|
*/
|
|
146
149
|
function DraftField({ label, children }) {
|
|
147
|
-
return
|
|
150
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
148
151
|
className: SkillCenterPage_module_css_default.field,
|
|
149
|
-
children: [
|
|
152
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
150
153
|
className: SkillCenterPage_module_css_default.fieldLabel,
|
|
151
154
|
children: label
|
|
152
155
|
}), children]
|
|
@@ -158,42 +161,45 @@ window.__ModuleLoader__.load({
|
|
|
158
161
|
* @returns the row.
|
|
159
162
|
*/
|
|
160
163
|
function SkillCard({ skill, t, onToggle, onDelete }) {
|
|
161
|
-
return
|
|
162
|
-
className: SkillCenterPage_module_css_default.card,
|
|
163
|
-
children:
|
|
164
|
+
return (0, react_jsx_runtime.jsx)("li", {
|
|
165
|
+
className: skill.isActiveWorkspace === false && skill.workspaceName !== void 0 ? SkillCenterPage_module_css_default.card + " " + SkillCenterPage_module_css_default.cardForeign : SkillCenterPage_module_css_default.card,
|
|
166
|
+
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
164
167
|
className: SkillCenterPage_module_css_default.cardHead,
|
|
165
168
|
children: [
|
|
166
|
-
|
|
169
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
167
170
|
className: SkillCenterPage_module_css_default.cardIcon,
|
|
168
|
-
children:
|
|
171
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSkillOutlineRegular, { size: 22 })
|
|
169
172
|
}),
|
|
170
|
-
|
|
173
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
171
174
|
className: SkillCenterPage_module_css_default.cardMain,
|
|
172
175
|
children: [
|
|
173
|
-
|
|
176
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
174
177
|
className: SkillCenterPage_module_css_default.cardTitle,
|
|
175
|
-
children: skill.name
|
|
178
|
+
children: [skill.name, skill.workspaceName === void 0 ? null : (0, react_jsx_runtime.jsx)("span", {
|
|
179
|
+
className: SkillCenterPage_module_css_default.workspaceBadge,
|
|
180
|
+
children: skill.workspaceName
|
|
181
|
+
})]
|
|
176
182
|
}),
|
|
177
|
-
|
|
183
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
178
184
|
className: SkillCenterPage_module_css_default.cardDesc,
|
|
179
185
|
children: skill.description
|
|
180
186
|
}),
|
|
181
|
-
skill.whenToUse === void 0 ? null :
|
|
187
|
+
skill.whenToUse === void 0 ? null : (0, react_jsx_runtime.jsx)("div", {
|
|
182
188
|
className: SkillCenterPage_module_css_default.cardWhenToUse,
|
|
183
189
|
children: skill.whenToUse
|
|
184
190
|
}),
|
|
185
|
-
|
|
191
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
186
192
|
className: SkillCenterPage_module_css_default.cardMarks,
|
|
187
193
|
children: [
|
|
188
|
-
|
|
194
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
189
195
|
className: skill.modelInvocable ? SkillCenterPage_module_css_default.mark + " " + SkillCenterPage_module_css_default.markOn : SkillCenterPage_module_css_default.mark + " " + SkillCenterPage_module_css_default.markOff,
|
|
190
196
|
children: t("skill.modelInvocable")
|
|
191
197
|
}),
|
|
192
|
-
|
|
198
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
193
199
|
className: skill.userInvocable ? SkillCenterPage_module_css_default.mark + " " + SkillCenterPage_module_css_default.markOn : SkillCenterPage_module_css_default.mark + " " + SkillCenterPage_module_css_default.markOff,
|
|
194
200
|
children: t("skill.userInvocable")
|
|
195
201
|
}),
|
|
196
|
-
skill.linked === true ?
|
|
202
|
+
skill.linked === true ? (0, react_jsx_runtime.jsx)("span", {
|
|
197
203
|
className: SkillCenterPage_module_css_default.mark + " " + SkillCenterPage_module_css_default.markDanger,
|
|
198
204
|
children: t("skill.linked")
|
|
199
205
|
}) : null
|
|
@@ -201,9 +207,9 @@ window.__ModuleLoader__.load({
|
|
|
201
207
|
})
|
|
202
208
|
]
|
|
203
209
|
}),
|
|
204
|
-
|
|
210
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
205
211
|
className: SkillCenterPage_module_css_default.cardEnd,
|
|
206
|
-
children: [
|
|
212
|
+
children: [(0, react_jsx_runtime.jsx)("button", {
|
|
207
213
|
type: "button",
|
|
208
214
|
className: skill.modelInvocable ? SkillCenterPage_module_css_default.switch + " " + SkillCenterPage_module_css_default.switchOn : SkillCenterPage_module_css_default.switch,
|
|
209
215
|
"aria-label": skill.modelInvocable ? t("skill.disabled") : t("skill.enabled"),
|
|
@@ -211,7 +217,7 @@ window.__ModuleLoader__.load({
|
|
|
211
217
|
onClick: () => {
|
|
212
218
|
onToggle(skill, !skill.modelInvocable);
|
|
213
219
|
}
|
|
214
|
-
}), skill.path === void 0 || skill.linked === true ? null :
|
|
220
|
+
}), skill.path === void 0 || skill.linked === true ? null : (0, react_jsx_runtime.jsx)("button", {
|
|
215
221
|
type: "button",
|
|
216
222
|
className: SkillCenterPage_module_css_default.button,
|
|
217
223
|
onClick: () => {
|
|
@@ -226,10 +232,10 @@ window.__ModuleLoader__.load({
|
|
|
226
232
|
}
|
|
227
233
|
/**
|
|
228
234
|
* The skill center panel.
|
|
229
|
-
* @param props - the slot's runtime share
|
|
235
|
+
* @param props - the slot's runtime share: the dictionary resolver and the workspace rows.
|
|
230
236
|
* @returns the rendered panel.
|
|
231
237
|
*/
|
|
232
|
-
function SkillCenterPage({ t }) {
|
|
238
|
+
function SkillCenterPage({ t, renderSlot, ...runtime }) {
|
|
233
239
|
const api = (0, react.useMemo)(() => new SkillApi(), []);
|
|
234
240
|
const [state, setState] = (0, react.useState)({ kind: "loading" });
|
|
235
241
|
const [query, setQuery] = (0, react.useState)("");
|
|
@@ -241,11 +247,16 @@ window.__ModuleLoader__.load({
|
|
|
241
247
|
content: "",
|
|
242
248
|
root: "user"
|
|
243
249
|
});
|
|
250
|
+
const useWorkspaces = runtime.useWorkspaces;
|
|
251
|
+
const workspaces = useWorkspaces === void 0 ? [] : useWorkspaces((snapshot) => snapshot.items);
|
|
252
|
+
const scope = (0, react.useMemo)(() => {
|
|
253
|
+
return (workspaces.find((item) => item.sessionIds.length > 0) ?? workspaces[0])?.path;
|
|
254
|
+
}, [workspaces]);
|
|
244
255
|
const load = (0, react.useCallback)(async () => {
|
|
245
256
|
try {
|
|
246
257
|
setState({
|
|
247
258
|
kind: "ready",
|
|
248
|
-
payload: await api.list()
|
|
259
|
+
payload: await api.list(scope)
|
|
249
260
|
});
|
|
250
261
|
} catch (error) {
|
|
251
262
|
setState({
|
|
@@ -253,7 +264,7 @@ window.__ModuleLoader__.load({
|
|
|
253
264
|
message: error instanceof Error ? error.message : String(error)
|
|
254
265
|
});
|
|
255
266
|
}
|
|
256
|
-
}, [api]);
|
|
267
|
+
}, [api, scope]);
|
|
257
268
|
(0, react.useEffect)(() => {
|
|
258
269
|
load();
|
|
259
270
|
}, [load]);
|
|
@@ -333,29 +344,36 @@ window.__ModuleLoader__.load({
|
|
|
333
344
|
load
|
|
334
345
|
]);
|
|
335
346
|
const total = state.kind === "ready" ? state.payload.groups.reduce((n, g) => n + g.skills.length, 0) : 0;
|
|
336
|
-
return
|
|
347
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
337
348
|
className: SkillCenterPage_module_css_default.page,
|
|
338
349
|
children: [
|
|
339
|
-
|
|
350
|
+
(0, react_jsx_runtime.jsxs)("header", {
|
|
340
351
|
className: SkillCenterPage_module_css_default.pageHead,
|
|
341
|
-
children: [
|
|
352
|
+
children: [(0, react_jsx_runtime.jsxs)("div", { children: [(0, react_jsx_runtime.jsx)("h1", {
|
|
342
353
|
className: SkillCenterPage_module_css_default.pageTitle,
|
|
343
354
|
children: t("panel")
|
|
344
|
-
}),
|
|
355
|
+
}), (0, react_jsx_runtime.jsx)("p", {
|
|
345
356
|
className: SkillCenterPage_module_css_default.pageIntro,
|
|
346
357
|
children: t("panel.intro")
|
|
347
|
-
})] }),
|
|
358
|
+
})] }), (0, react_jsx_runtime.jsxs)("div", {
|
|
348
359
|
className: SkillCenterPage_module_css_default.toolbar,
|
|
349
|
-
children: [
|
|
360
|
+
children: [(0, react_jsx_runtime.jsx)("button", {
|
|
350
361
|
type: "button",
|
|
351
|
-
className: SkillCenterPage_module_css_default.
|
|
362
|
+
className: SkillCenterPage_module_css_default.iconButton,
|
|
363
|
+
"aria-label": t("action.refresh"),
|
|
364
|
+
title: t("action.refresh"),
|
|
352
365
|
onClick: () => {
|
|
353
366
|
load();
|
|
354
367
|
},
|
|
355
|
-
children:
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
368
|
+
children: (0, react_jsx_runtime.jsx)("span", {
|
|
369
|
+
className: SkillCenterPage_module_css_default.iconWrap,
|
|
370
|
+
"aria-hidden": "true",
|
|
371
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRefreshOutlineRegular, {})
|
|
372
|
+
})
|
|
373
|
+
}), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
374
|
+
variant: "primary",
|
|
375
|
+
size: "sm",
|
|
376
|
+
icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutlineRegular, { size: 13 }),
|
|
359
377
|
onClick: () => {
|
|
360
378
|
setCreating((v) => !v);
|
|
361
379
|
},
|
|
@@ -363,82 +381,87 @@ window.__ModuleLoader__.load({
|
|
|
363
381
|
})]
|
|
364
382
|
})]
|
|
365
383
|
}),
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
384
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
385
|
+
open: creating,
|
|
386
|
+
onClose: () => {
|
|
387
|
+
setCreating(false);
|
|
388
|
+
},
|
|
389
|
+
title: t("create.title"),
|
|
390
|
+
closeLabel: t("action.cancel"),
|
|
391
|
+
className: SkillCenterPage_module_css_default.dialog,
|
|
392
|
+
footer: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
393
|
+
variant: "outline",
|
|
394
|
+
autoFocus: true,
|
|
395
|
+
onClick: () => {
|
|
396
|
+
setCreating(false);
|
|
397
|
+
},
|
|
398
|
+
children: t("action.cancel")
|
|
399
|
+
}), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
400
|
+
variant: "primary",
|
|
401
|
+
onClick: () => {
|
|
402
|
+
submit();
|
|
403
|
+
},
|
|
404
|
+
children: t("action.confirm")
|
|
405
|
+
})] }),
|
|
406
|
+
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
407
|
+
className: SkillCenterPage_module_css_default.form,
|
|
408
|
+
children: [
|
|
409
|
+
(0, react_jsx_runtime.jsx)(DraftField, {
|
|
410
|
+
label: t("create.name"),
|
|
411
|
+
children: (0, react_jsx_runtime.jsx)("input", {
|
|
412
|
+
className: SkillCenterPage_module_css_default.input,
|
|
413
|
+
value: draft.name,
|
|
414
|
+
onChange: set("name")
|
|
415
|
+
})
|
|
416
|
+
}),
|
|
417
|
+
(0, react_jsx_runtime.jsx)(DraftField, {
|
|
418
|
+
label: t("create.description"),
|
|
419
|
+
children: (0, react_jsx_runtime.jsx)("input", {
|
|
420
|
+
className: SkillCenterPage_module_css_default.input,
|
|
421
|
+
value: draft.description,
|
|
422
|
+
onChange: set("description")
|
|
423
|
+
})
|
|
424
|
+
}),
|
|
425
|
+
(0, react_jsx_runtime.jsx)(DraftField, {
|
|
426
|
+
label: t("create.whenToUse"),
|
|
427
|
+
children: (0, react_jsx_runtime.jsx)("input", {
|
|
428
|
+
className: SkillCenterPage_module_css_default.input,
|
|
429
|
+
value: draft.whenToUse,
|
|
430
|
+
onChange: set("whenToUse")
|
|
431
|
+
})
|
|
432
|
+
}),
|
|
433
|
+
(0, react_jsx_runtime.jsx)(DraftField, {
|
|
434
|
+
label: t("create.content"),
|
|
435
|
+
children: (0, react_jsx_runtime.jsx)("textarea", {
|
|
436
|
+
className: SkillCenterPage_module_css_default.textarea,
|
|
437
|
+
value: draft.content,
|
|
438
|
+
onChange: set("content")
|
|
439
|
+
})
|
|
440
|
+
}),
|
|
441
|
+
(0, react_jsx_runtime.jsx)(DraftField, {
|
|
442
|
+
label: t("create.root"),
|
|
443
|
+
children: (0, react_jsx_runtime.jsxs)("select", {
|
|
444
|
+
className: SkillCenterPage_module_css_default.input,
|
|
445
|
+
value: draft.root,
|
|
446
|
+
onChange: (e) => {
|
|
447
|
+
setDraft((d) => ({
|
|
448
|
+
...d,
|
|
449
|
+
root: e.target.value === "project" ? "project" : "user"
|
|
450
|
+
}));
|
|
451
|
+
},
|
|
452
|
+
children: [(0, react_jsx_runtime.jsx)("option", {
|
|
453
|
+
value: "user",
|
|
454
|
+
children: t("create.rootUser")
|
|
455
|
+
}), (0, react_jsx_runtime.jsx)("option", {
|
|
456
|
+
value: "project",
|
|
457
|
+
children: t("create.rootProject")
|
|
458
|
+
})]
|
|
459
|
+
})
|
|
419
460
|
})
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
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", {
|
|
461
|
+
]
|
|
462
|
+
})
|
|
463
|
+
}),
|
|
464
|
+
(0, react_jsx_runtime.jsx)("input", {
|
|
442
465
|
className: SkillCenterPage_module_css_default.search,
|
|
443
466
|
placeholder: t("search.placeholder"),
|
|
444
467
|
value: query,
|
|
@@ -446,11 +469,11 @@ window.__ModuleLoader__.load({
|
|
|
446
469
|
setQuery(e.target.value);
|
|
447
470
|
}
|
|
448
471
|
}),
|
|
449
|
-
state.kind === "loading" ?
|
|
472
|
+
state.kind === "loading" ? (0, react_jsx_runtime.jsx)("div", {
|
|
450
473
|
className: SkillCenterPage_module_css_default.status,
|
|
451
474
|
children: t("state.loading")
|
|
452
475
|
}) : null,
|
|
453
|
-
state.kind === "failed" ?
|
|
476
|
+
state.kind === "failed" ? (0, react_jsx_runtime.jsxs)("div", {
|
|
454
477
|
className: SkillCenterPage_module_css_default.status + " " + SkillCenterPage_module_css_default.statusError,
|
|
455
478
|
children: [
|
|
456
479
|
t("state.failed"),
|
|
@@ -458,32 +481,32 @@ window.__ModuleLoader__.load({
|
|
|
458
481
|
state.message
|
|
459
482
|
]
|
|
460
483
|
}) : null,
|
|
461
|
-
state.kind === "ready" && groups.length === 0 ?
|
|
484
|
+
state.kind === "ready" && groups.length === 0 ? (0, react_jsx_runtime.jsx)("div", {
|
|
462
485
|
className: SkillCenterPage_module_css_default.status,
|
|
463
486
|
children: query.trim() === "" ? t("state.empty") : t("search.noMatches")
|
|
464
487
|
}) : null,
|
|
465
|
-
state.kind === "ready" && groups.length > 0 && total === 0 ?
|
|
488
|
+
state.kind === "ready" && groups.length > 0 && total === 0 ? (0, react_jsx_runtime.jsx)("div", {
|
|
466
489
|
className: SkillCenterPage_module_css_default.status,
|
|
467
490
|
children: t("state.empty")
|
|
468
491
|
}) : null,
|
|
469
|
-
groups.map((group) =>
|
|
492
|
+
groups.map((group) => (0, react_jsx_runtime.jsxs)("div", {
|
|
470
493
|
className: SkillCenterPage_module_css_default.group,
|
|
471
|
-
children: [
|
|
494
|
+
children: [(0, react_jsx_runtime.jsxs)("h2", {
|
|
472
495
|
className: SkillCenterPage_module_css_default.groupTitle,
|
|
473
496
|
children: [
|
|
474
497
|
group.title,
|
|
475
|
-
|
|
498
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
476
499
|
className: SkillCenterPage_module_css_default.groupCount,
|
|
477
500
|
children: group.skills.length
|
|
478
501
|
}),
|
|
479
|
-
|
|
502
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
480
503
|
className: SkillCenterPage_module_css_default.groupHint,
|
|
481
504
|
children: group.hint
|
|
482
505
|
})
|
|
483
506
|
]
|
|
484
|
-
}),
|
|
507
|
+
}), (0, react_jsx_runtime.jsx)("ul", {
|
|
485
508
|
className: SkillCenterPage_module_css_default.cards,
|
|
486
|
-
children: group.skills.map((skill) =>
|
|
509
|
+
children: group.skills.map((skill) => (0, react_jsx_runtime.jsx)(SkillCard, {
|
|
487
510
|
skill,
|
|
488
511
|
t,
|
|
489
512
|
onToggle: (s, enabled) => {
|
|
@@ -494,22 +517,29 @@ window.__ModuleLoader__.load({
|
|
|
494
517
|
}
|
|
495
518
|
}, group.key + ":" + skill.name))
|
|
496
519
|
})]
|
|
497
|
-
}, group.key))
|
|
520
|
+
}, group.key)),
|
|
521
|
+
renderSlot("skill-center.section", {})
|
|
498
522
|
]
|
|
499
523
|
});
|
|
500
524
|
}
|
|
501
525
|
//#endregion
|
|
502
|
-
//#region
|
|
526
|
+
//#region lib/types/client/SkillCenterPanelIcon.js
|
|
503
527
|
/**
|
|
504
528
|
* Render the glyph at the size the sidebar asks for.
|
|
505
529
|
* @param props - the sidebar's icon share: requested edge and selected state.
|
|
506
530
|
* @returns the icon element.
|
|
507
531
|
*/
|
|
508
532
|
function SkillCenterPanelIcon({ size }) {
|
|
509
|
-
return
|
|
533
|
+
return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSkillOutlineRegular, { size });
|
|
510
534
|
}
|
|
511
535
|
//#endregion
|
|
512
|
-
//#region
|
|
536
|
+
//#region lib/types/client/locales.js
|
|
537
|
+
/**
|
|
538
|
+
* Skill center dictionaries. Keys are typed so the panel and the sidebar entry
|
|
539
|
+
* cannot reference a string that does not exist in both languages.
|
|
540
|
+
*
|
|
541
|
+
* @module @sparkelf/dsh-client-ui-skill-center/client/locales
|
|
542
|
+
*/
|
|
513
543
|
/** Simplified Chinese dictionary. */
|
|
514
544
|
const zh = {
|
|
515
545
|
"panel": "技能中心",
|
|
@@ -571,7 +601,7 @@ window.__ModuleLoader__.load({
|
|
|
571
601
|
"delete.confirm": "Deleting moves it into .trash, where it can be restored. Delete?"
|
|
572
602
|
};
|
|
573
603
|
//#endregion
|
|
574
|
-
//#region
|
|
604
|
+
//#region lib/types/client/index.js
|
|
575
605
|
/** Dictionary namespace owned by this plugin. */
|
|
576
606
|
const NS = "skillCenter";
|
|
577
607
|
/** The id shared by the sidebar entry and the main panel it opens. */
|
|
@@ -591,7 +621,11 @@ window.__ModuleLoader__.load({
|
|
|
591
621
|
ctx.slots.inject("main", () => ctx.slots.register({
|
|
592
622
|
name: "main",
|
|
593
623
|
key: PANEL_ID,
|
|
594
|
-
locale: NS
|
|
624
|
+
locale: NS,
|
|
625
|
+
children: { "skill-center.section": {
|
|
626
|
+
kind: "list",
|
|
627
|
+
scope: "root"
|
|
628
|
+
} }
|
|
595
629
|
}, SkillCenterPage));
|
|
596
630
|
ctx.slots.inject("sidebar.panellist", () => ctx.slots.register({
|
|
597
631
|
name: "sidebar.panellist",
|
package/lib/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
1
|
+
import { existsSync, lstatSync } from "node:fs";
|
|
2
|
+
import { mkdir, readFile, readdir, rename, writeFile } from "node:fs/promises";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
|
-
import { dirname, join, resolve } from "node:path";
|
|
4
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
5
5
|
//#region lib/types/index.js
|
|
6
6
|
/**
|
|
7
7
|
* Skill center — host half. Serves the skill-center data source over the
|
|
@@ -40,37 +40,37 @@ const SOURCE_GROUPS = [
|
|
|
40
40
|
{
|
|
41
41
|
key: "bundled",
|
|
42
42
|
title: "系统内置",
|
|
43
|
-
hint: "随 Harness
|
|
43
|
+
hint: "随 Harness 一同安装,对所有项目生效"
|
|
44
44
|
},
|
|
45
45
|
{
|
|
46
|
-
key: "
|
|
47
|
-
title: "
|
|
48
|
-
hint: "
|
|
46
|
+
key: "project-dsh",
|
|
47
|
+
title: "项目 .dsh/skills",
|
|
48
|
+
hint: "位于项目目录,仅对该工作区生效"
|
|
49
49
|
},
|
|
50
50
|
{
|
|
51
|
-
key: "
|
|
52
|
-
title: "
|
|
53
|
-
hint: "
|
|
51
|
+
key: "project-agents",
|
|
52
|
+
title: "项目 .agents/skills",
|
|
53
|
+
hint: "位于项目目录,随项目提交,对协作者共享"
|
|
54
54
|
},
|
|
55
55
|
{
|
|
56
|
-
key: "
|
|
57
|
-
title: "
|
|
58
|
-
hint: "
|
|
56
|
+
key: "custom",
|
|
57
|
+
title: "自定义目录",
|
|
58
|
+
hint: "由本插件的 customSkillDirs 配置"
|
|
59
59
|
},
|
|
60
60
|
{
|
|
61
|
-
key: "
|
|
62
|
-
title: "
|
|
63
|
-
hint: "
|
|
61
|
+
key: "user-dsh",
|
|
62
|
+
title: "用户 ~/.dsh/skills",
|
|
63
|
+
hint: "在用户 dsh 目录下,对本机所有项目生效"
|
|
64
64
|
},
|
|
65
65
|
{
|
|
66
|
-
key: "
|
|
67
|
-
title: "
|
|
68
|
-
hint: "
|
|
66
|
+
key: "user-agents",
|
|
67
|
+
title: "用户 ~/.agents/skills",
|
|
68
|
+
hint: "在用户 agents 目录下,对本机所有项目生效"
|
|
69
69
|
},
|
|
70
70
|
{
|
|
71
|
-
key: "
|
|
72
|
-
title: "
|
|
73
|
-
hint: "
|
|
71
|
+
key: "runtime",
|
|
72
|
+
title: "运行时注册",
|
|
73
|
+
hint: "由已安装的插件在运行时注册"
|
|
74
74
|
}
|
|
75
75
|
];
|
|
76
76
|
/**
|
|
@@ -124,6 +124,121 @@ function levelOfSource(source, provider) {
|
|
|
124
124
|
if (s.includes("runtime")) return "runtime";
|
|
125
125
|
return "custom";
|
|
126
126
|
}
|
|
127
|
+
/** The Markdown a skill directory may declare its bundle with. */
|
|
128
|
+
const SKILL_FILE = "SKILL.md";
|
|
129
|
+
/**
|
|
130
|
+
* Read one root directory for skills.
|
|
131
|
+
*
|
|
132
|
+
* The web-app bundle disables the host `skill-filesystem` row and lets each preset
|
|
133
|
+
* own local discovery, so the host registry carries only the bundled catalog: a
|
|
134
|
+
* panel reading the registry alone shows the shipped skills and none of the
|
|
135
|
+
* project or user ones the agent is actually running with. Reading the root
|
|
136
|
+
* conventions here closes that gap without a second catalog authority — the
|
|
137
|
+
* registry still owns discovery for anything it does serve, and this scan is
|
|
138
|
+
* merged under it by name.
|
|
139
|
+
*
|
|
140
|
+
* Both layouts the harness accepts are read: a directory holding SKILL.md, and a
|
|
141
|
+
* flat Markdown file.
|
|
142
|
+
*
|
|
143
|
+
* @param root - absolute root directory to scan.
|
|
144
|
+
* @param level - the group key every skill under this root belongs to.
|
|
145
|
+
* @param workspace - the project a project-scoped root belongs to, so a row can name
|
|
146
|
+
* its origin and mark itself when it is not the workspace being viewed.
|
|
147
|
+
* @returns the skills found, sorted by name; missing roots yield none.
|
|
148
|
+
*/
|
|
149
|
+
async function scanSkillRoot(root, level, workspace) {
|
|
150
|
+
let entries;
|
|
151
|
+
try {
|
|
152
|
+
entries = await readdir(root, { withFileTypes: true });
|
|
153
|
+
} catch {
|
|
154
|
+
return [];
|
|
155
|
+
}
|
|
156
|
+
const found = [];
|
|
157
|
+
for (const entry of entries) {
|
|
158
|
+
const path = entry.isDirectory() || entry.isSymbolicLink() ? join(root, entry.name, SKILL_FILE) : entry.name.endsWith(".md") ? join(root, entry.name) : void 0;
|
|
159
|
+
if (path === void 0 || !existsSync(path)) continue;
|
|
160
|
+
const name = entry.name.replace(/\.md$/u, "");
|
|
161
|
+
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/u.test(name)) continue;
|
|
162
|
+
let source;
|
|
163
|
+
try {
|
|
164
|
+
source = await readFile(path, "utf8");
|
|
165
|
+
} catch {
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
const front = /^---\r?\n([\s\S]*?)\r?\n---/.exec(source)?.[1] ?? "";
|
|
169
|
+
const field = (key) => {
|
|
170
|
+
return new RegExp("^\\s*" + key + "\\s*:\\s*(.+?)\\s*$", "mu").exec(front)?.[1]?.replace(/^['"]|['"]$/gu, "");
|
|
171
|
+
};
|
|
172
|
+
const disabled = readDisabledFlag(source);
|
|
173
|
+
const invocation = field("invocation");
|
|
174
|
+
found.push({
|
|
175
|
+
name,
|
|
176
|
+
description: field("description") ?? "",
|
|
177
|
+
level,
|
|
178
|
+
path,
|
|
179
|
+
linked: lstatSyncSafe(path),
|
|
180
|
+
modelInvocable: disabled !== true && invocation !== "user",
|
|
181
|
+
userInvocable: invocation !== "model",
|
|
182
|
+
...field("when-to-use") === void 0 ? {} : { whenToUse: field("when-to-use") },
|
|
183
|
+
...workspace === void 0 ? {} : {
|
|
184
|
+
workspaceRoot: workspace.root,
|
|
185
|
+
workspaceName: workspace.name,
|
|
186
|
+
isActiveWorkspace: workspace.active
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
return found.sort((a, b) => a.name.localeCompare(b.name));
|
|
191
|
+
}
|
|
192
|
+
/** Whether a skill file is reached through a symlink, which makes deletion unsafe. */
|
|
193
|
+
function lstatSyncSafe(path) {
|
|
194
|
+
try {
|
|
195
|
+
return lstatSync(path).isSymbolicLink() || lstatSync(dirname(path)).isSymbolicLink();
|
|
196
|
+
} catch {
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Every local skill root this deployment uses, with the group each belongs to.
|
|
202
|
+
*
|
|
203
|
+
* @param deps - the resolved configuration.
|
|
204
|
+
* @param projectRoots - project roots whose skill directories are in scope.
|
|
205
|
+
* @param activeRoot - the project root the view is about, marked active so the panel
|
|
206
|
+
* can distinguish a row from another workspace.
|
|
207
|
+
* @returns the roots to scan, in group order.
|
|
208
|
+
*/
|
|
209
|
+
function localSkillRoots(deps, projectRoots, activeRoot) {
|
|
210
|
+
const roots = [];
|
|
211
|
+
for (const projectRoot of projectRoots) {
|
|
212
|
+
const workspace = {
|
|
213
|
+
root: projectRoot,
|
|
214
|
+
name: basename(projectRoot) || projectRoot,
|
|
215
|
+
active: projectRoot === activeRoot
|
|
216
|
+
};
|
|
217
|
+
roots.push({
|
|
218
|
+
path: join(projectRoot, ".dsh", "skills"),
|
|
219
|
+
level: "project-dsh",
|
|
220
|
+
workspace
|
|
221
|
+
});
|
|
222
|
+
roots.push({
|
|
223
|
+
path: join(projectRoot, ".agents", "skills"),
|
|
224
|
+
level: "project-agents",
|
|
225
|
+
workspace
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
for (const dir of deps.customSkillDirs) roots.push({
|
|
229
|
+
path: dir,
|
|
230
|
+
level: "custom"
|
|
231
|
+
});
|
|
232
|
+
roots.push({
|
|
233
|
+
path: join(deps.dshHome, "skills"),
|
|
234
|
+
level: "user-dsh"
|
|
235
|
+
});
|
|
236
|
+
roots.push({
|
|
237
|
+
path: join(deps.agentsHome, "skills"),
|
|
238
|
+
level: "user-agents"
|
|
239
|
+
});
|
|
240
|
+
return roots;
|
|
241
|
+
}
|
|
127
242
|
/**
|
|
128
243
|
* Read `disable-model-invocation` from a skill file.
|
|
129
244
|
* @param source - the skill file's content.
|
|
@@ -196,11 +311,12 @@ async function readJson(req, maxBytes) {
|
|
|
196
311
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error("request body must be a JSON object");
|
|
197
312
|
return parsed;
|
|
198
313
|
}
|
|
199
|
-
/** The list handler: group the registry snapshot
|
|
314
|
+
/** The list handler: group the registry snapshot and the local roots together. */
|
|
200
315
|
async function handleList(deps, cwd) {
|
|
201
316
|
const projectRoots = [...new Set(deps.activeSessionCwds().map(findProjectRoot).concat(findProjectRoot(cwd)))];
|
|
202
317
|
const snapshot = await deps.snapshotProject(cwd);
|
|
203
318
|
const byGroup = new Map(SOURCE_GROUPS.map((g) => [g.key, []]));
|
|
319
|
+
const seen = /* @__PURE__ */ new Set();
|
|
204
320
|
for (const skill of snapshot.skills) {
|
|
205
321
|
const level = levelOfSource(skill.source, skill.provider);
|
|
206
322
|
const entry = {
|
|
@@ -212,8 +328,26 @@ async function handleList(deps, cwd) {
|
|
|
212
328
|
userInvocable: skill.invocation.userInvocable,
|
|
213
329
|
...skill.whenToUse === void 0 ? {} : { whenToUse: skill.whenToUse }
|
|
214
330
|
};
|
|
331
|
+
seen.add(level + ":" + skill.name);
|
|
215
332
|
(byGroup.get(level) ?? byGroup.get("custom"))?.push(entry);
|
|
216
333
|
}
|
|
334
|
+
for (const root of localSkillRoots(deps, projectRoots, findProjectRoot(cwd))) for (const skill of await scanSkillRoot(root.path, root.level, root.workspace)) {
|
|
335
|
+
if (seen.has(root.level + ":" + skill.name)) continue;
|
|
336
|
+
seen.add(root.level + ":" + skill.name);
|
|
337
|
+
byGroup.get(root.level)?.push({
|
|
338
|
+
name: skill.name,
|
|
339
|
+
description: skill.description,
|
|
340
|
+
level: skill.level,
|
|
341
|
+
path: skill.path,
|
|
342
|
+
linked: skill.linked,
|
|
343
|
+
modelInvocable: skill.modelInvocable,
|
|
344
|
+
userInvocable: skill.userInvocable,
|
|
345
|
+
...skill.whenToUse === void 0 ? {} : { whenToUse: skill.whenToUse },
|
|
346
|
+
...skill.workspaceRoot === void 0 ? {} : { workspaceRoot: skill.workspaceRoot },
|
|
347
|
+
...skill.workspaceName === void 0 ? {} : { workspaceName: skill.workspaceName },
|
|
348
|
+
...skill.isActiveWorkspace === void 0 ? {} : { isActiveWorkspace: skill.isActiveWorkspace }
|
|
349
|
+
});
|
|
350
|
+
}
|
|
217
351
|
const groups = [];
|
|
218
352
|
for (const g of SOURCE_GROUPS) {
|
|
219
353
|
const skills = (byGroup.get(g.key) ?? []).sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -393,4 +527,4 @@ function apply(ctx, config = {}) {
|
|
|
393
527
|
for (const route of makeRoutes(deps)) ctx.effect(() => ctx.webServer.register(route), "ui-skill-center: route " + route.path);
|
|
394
528
|
}
|
|
395
529
|
//#endregion
|
|
396
|
-
export { ROUTES, apply, buildSkillContent, findProjectRoot, inject, levelOfSource, makeRoutes, name, projectSkillRoot, readDisabledFlag, userSkillRoot, withDisabledFlag };
|
|
530
|
+
export { ROUTES, apply, buildSkillContent, findProjectRoot, inject, levelOfSource, localSkillRoots, makeRoutes, name, projectSkillRoot, readDisabledFlag, scanSkillRoot, userSkillRoot, withDisabledFlag };
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* @module @sparkelf/dsh-client-ui-skill-center/client/SkillCenterPage
|
|
9
9
|
*/
|
|
10
10
|
import { type ReactNode } from 'react';
|
|
11
|
-
import type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
11
|
+
import type { PropsRenderSlots, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
12
12
|
import type { SkillCenterLocaleKey } from './locales.ts';
|
|
13
13
|
/** What the panel needs from its slot: the dictionary resolver. */
|
|
14
14
|
export interface SkillCenterPageProps {
|
|
@@ -16,8 +16,8 @@ export interface SkillCenterPageProps {
|
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
18
|
* The skill center panel.
|
|
19
|
-
* @param props - the slot's runtime share
|
|
19
|
+
* @param props - the slot's runtime share: the dictionary resolver and the workspace rows.
|
|
20
20
|
* @returns the rendered panel.
|
|
21
21
|
*/
|
|
22
|
-
export declare function SkillCenterPage({ t }: PropsRuntime<'main'> & SkillCenterPageProps): ReactNode;
|
|
22
|
+
export declare function SkillCenterPage({ t, renderSlot, ...runtime }: PropsRuntime<'main'> & PropsRenderSlots<'skill-center.section'> & SkillCenterPageProps): ReactNode;
|
|
23
23
|
//# sourceMappingURL=SkillCenterPage.d.ts.map
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { Context } from '@deepseek-ai/cordis';
|
|
10
10
|
import type { MainPanelId } from '@deepseek-ai/dsh-client-ui-layout/client';
|
|
11
|
+
export type { SkillCenterSectionOwnerProps } from './slot-contract.ts';
|
|
11
12
|
import { type SkillCenterLocaleKey } from './locales.ts';
|
|
12
13
|
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
13
14
|
interface LocaleNamespaceMap {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Owner props a contributed section receives. The slot passes none today; the type exists so a
|
|
3
|
+
* consumer can name the slot's owner share without importing the page.
|
|
4
|
+
*/
|
|
5
|
+
export interface SkillCenterSectionOwnerProps {
|
|
6
|
+
/** Reserved for an owner share the panel may add; currently empty. */
|
|
7
|
+
readonly reserved?: never;
|
|
8
|
+
}
|
|
9
|
+
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
10
|
+
interface SlotMap {
|
|
11
|
+
/** A section contributed to the skill center page, below the skill list. */
|
|
12
|
+
'skill-center.section': {
|
|
13
|
+
kind: 'list';
|
|
14
|
+
scope: 'root';
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=slot-contract.d.ts.map
|
package/lib/types/index.d.ts
CHANGED
|
@@ -88,6 +88,66 @@ export declare function findProjectRoot(cwd: string): string;
|
|
|
88
88
|
* @returns the group key.
|
|
89
89
|
*/
|
|
90
90
|
export declare function levelOfSource(source: string, provider: string): string;
|
|
91
|
+
/** One skill found by reading a root directory. */
|
|
92
|
+
export interface ScannedSkill {
|
|
93
|
+
name: string;
|
|
94
|
+
description: string;
|
|
95
|
+
whenToUse?: string;
|
|
96
|
+
level: string;
|
|
97
|
+
path: string;
|
|
98
|
+
linked: boolean;
|
|
99
|
+
modelInvocable: boolean;
|
|
100
|
+
userInvocable: boolean;
|
|
101
|
+
/** The project a project-scoped skill came from; absent for user and bundled roots. */
|
|
102
|
+
workspaceRoot?: string;
|
|
103
|
+
workspaceName?: string;
|
|
104
|
+
isActiveWorkspace?: boolean;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Read one root directory for skills.
|
|
108
|
+
*
|
|
109
|
+
* The web-app bundle disables the host `skill-filesystem` row and lets each preset
|
|
110
|
+
* own local discovery, so the host registry carries only the bundled catalog: a
|
|
111
|
+
* panel reading the registry alone shows the shipped skills and none of the
|
|
112
|
+
* project or user ones the agent is actually running with. Reading the root
|
|
113
|
+
* conventions here closes that gap without a second catalog authority — the
|
|
114
|
+
* registry still owns discovery for anything it does serve, and this scan is
|
|
115
|
+
* merged under it by name.
|
|
116
|
+
*
|
|
117
|
+
* Both layouts the harness accepts are read: a directory holding SKILL.md, and a
|
|
118
|
+
* flat Markdown file.
|
|
119
|
+
*
|
|
120
|
+
* @param root - absolute root directory to scan.
|
|
121
|
+
* @param level - the group key every skill under this root belongs to.
|
|
122
|
+
* @param workspace - the project a project-scoped root belongs to, so a row can name
|
|
123
|
+
* its origin and mark itself when it is not the workspace being viewed.
|
|
124
|
+
* @returns the skills found, sorted by name; missing roots yield none.
|
|
125
|
+
*/
|
|
126
|
+
export declare function scanSkillRoot(root: string, level: string, workspace?: {
|
|
127
|
+
root: string;
|
|
128
|
+
name: string;
|
|
129
|
+
active: boolean;
|
|
130
|
+
}): Promise<ScannedSkill[]>;
|
|
131
|
+
/**
|
|
132
|
+
* Every local skill root this deployment uses, with the group each belongs to.
|
|
133
|
+
*
|
|
134
|
+
* @param deps - the resolved configuration.
|
|
135
|
+
* @param projectRoots - project roots whose skill directories are in scope.
|
|
136
|
+
* @param activeRoot - the project root the view is about, marked active so the panel
|
|
137
|
+
* can distinguish a row from another workspace.
|
|
138
|
+
* @returns the roots to scan, in group order.
|
|
139
|
+
*/
|
|
140
|
+
export declare function localSkillRoots(deps: Pick<SkillRoutesDeps, 'dshHome' | 'agentsHome'> & {
|
|
141
|
+
customSkillDirs: readonly string[];
|
|
142
|
+
}, projectRoots: readonly string[], activeRoot?: string): {
|
|
143
|
+
path: string;
|
|
144
|
+
level: string;
|
|
145
|
+
workspace?: {
|
|
146
|
+
root: string;
|
|
147
|
+
name: string;
|
|
148
|
+
active: boolean;
|
|
149
|
+
};
|
|
150
|
+
}[];
|
|
91
151
|
/**
|
|
92
152
|
* Read `disable-model-invocation` from a skill file.
|
|
93
153
|
* @param source - the skill file's content.
|
package/package.json
CHANGED