dsh-skill-hub 0.2.0 → 0.2.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/CONTRIBUTING.md +1 -1
- package/README.md +181 -249
- package/README.zh.md +275 -0
- package/lib/client.js +381 -257
- package/lib/client.js.map +1 -1
- package/lib/index.js +126 -26
- package/lib/types/client/SkillHubSettingsCard.d.ts +23 -17
- package/lib/types/client/api.d.ts +9 -3
- package/lib/types/client/grouping.d.ts +3 -0
- package/lib/types/client/index.d.ts +12 -6
- package/lib/types/client/locales.d.ts +7 -4
- package/lib/types/client/market-catalog.d.ts +1 -1
- package/lib/types/client/panel/MarketView.d.ts +5 -5
- package/lib/types/client/panel/SkillHubPanel.d.ts +1 -1
- package/lib/types/client/panel/SourcesView.d.ts +5 -3
- package/lib/types/client/panel/useSkillHub.d.ts +4 -0
- package/lib/types/client/settings-card.d.ts +5 -4
- package/lib/types/index.d.ts +12 -1
- package/lib/types/protocol.d.ts +23 -0
- package/lib/types/skillfs.d.ts +1 -1
- package/lib/types/update.d.ts +1 -1
- package/package.json +28 -27
- package/src/client/SkillHubSettingsCard.tsx +25 -14
- package/src/client/api.ts +9 -6
- package/src/client/grouping.test.ts +12 -0
- package/src/client/grouping.ts +10 -1
- package/src/client/index.tsx +25 -19
- package/src/client/locales.ts +14 -8
- package/src/client/market-catalog.ts +1 -1
- package/src/client/panel/MarketView.tsx +77 -64
- package/src/client/panel/SkillHubPanel.tsx +18 -1
- package/src/client/panel/SourcesView.tsx +92 -5
- package/src/client/panel/panel.module.css +6 -1
- package/src/client/panel/useSkillHub.ts +25 -11
- package/src/client/settings-card.tsx +5 -4
- package/src/index.ts +78 -31
- package/src/protocol.ts +24 -0
- package/src/routes.test.ts +55 -0
- package/src/routes.ts +90 -10
- package/src/skillfs.ts +1 -1
- package/src/update.ts +1 -1
- package/lib/types/client/api-config-scope.d.ts +0 -48
- package/src/client/api-config-scope.test.ts +0 -79
- package/src/client/api-config-scope.ts +0 -119
package/lib/client.js
CHANGED
|
@@ -88,11 +88,14 @@ window.__ModuleLoader__.load({
|
|
|
88
88
|
}
|
|
89
89
|
/** The browser half's only data entry point. */
|
|
90
90
|
var SkillHubApi = class {
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
/** Catalog lookup options (cwd selects a workspace's project skills). */
|
|
92
|
+
catalog(options) {
|
|
93
|
+
return fetchWithTimeout(options?.cwd !== void 0 && options.cwd !== "" ? SKILL_HUB_API.catalog + "?cwd=" + encodeURIComponent(options.cwd) : SKILL_HUB_API.catalog).then((response) => readJson(response));
|
|
93
94
|
}
|
|
94
|
-
|
|
95
|
-
|
|
95
|
+
/** One skill's detail (cwd selects a workspace's project skills). */
|
|
96
|
+
async skill(name, options) {
|
|
97
|
+
const cwd = options?.cwd !== void 0 && options.cwd !== "" ? "&cwd=" + encodeURIComponent(options.cwd) : "";
|
|
98
|
+
return (await readJson(await fetchWithTimeout(SKILL_HUB_API.skill + "?name=" + encodeURIComponent(name) + cwd))).skill;
|
|
96
99
|
}
|
|
97
100
|
/** Move one writable skill into the restorable trash. */
|
|
98
101
|
async deleteSkill(name) {
|
|
@@ -135,7 +138,7 @@ window.__ModuleLoader__.load({
|
|
|
135
138
|
body: JSON.stringify(payload)
|
|
136
139
|
}));
|
|
137
140
|
}
|
|
138
|
-
/** The user's added market sources
|
|
141
|
+
/** The user's added market sources. */
|
|
139
142
|
async market() {
|
|
140
143
|
return readJson(await fetchWithTimeout(SKILL_HUB_API.market));
|
|
141
144
|
}
|
|
@@ -294,102 +297,6 @@ window.__ModuleLoader__.load({
|
|
|
294
297
|
}
|
|
295
298
|
};
|
|
296
299
|
//#endregion
|
|
297
|
-
//#region src/client/api-config-scope.ts
|
|
298
|
-
/** Model-invocable dot color default. Single source for the TS side; the
|
|
299
|
-
* panel's CSS mirrors it via --hub-model (panel.module.css). */
|
|
300
|
-
const DEFAULT_DOT_MODEL_COLOR = "#2f81f7";
|
|
301
|
-
/** User-invocable dot color default. Single source for the TS side; the
|
|
302
|
-
* panel's CSS mirrors it via --hub-user (panel.module.css). */
|
|
303
|
-
const DEFAULT_DOT_USER_COLOR = "#3fb950";
|
|
304
|
-
/** Built-in defaults every field inherits until the user overrides it. */
|
|
305
|
-
const DEFAULTS = {
|
|
306
|
-
enabled: true,
|
|
307
|
-
announceToAgent: true,
|
|
308
|
-
showUseCount: true,
|
|
309
|
-
showUseTime: true,
|
|
310
|
-
showGroupSummary: true
|
|
311
|
-
};
|
|
312
|
-
/** Boolean config fields (color fields are strings). */
|
|
313
|
-
const BOOLEAN_FIELDS = /* @__PURE__ */ new Set([
|
|
314
|
-
"enabled",
|
|
315
|
-
"announceToAgent",
|
|
316
|
-
"showUseCount",
|
|
317
|
-
"showUseTime",
|
|
318
|
-
"showGroupSummary"
|
|
319
|
-
]);
|
|
320
|
-
/** Config fields the card edits (used to guard set/unset field names). */
|
|
321
|
-
const FIELDS = /* @__PURE__ */ new Set([
|
|
322
|
-
"enabled",
|
|
323
|
-
"announceToAgent",
|
|
324
|
-
"dotModelColor",
|
|
325
|
-
"dotUserColor",
|
|
326
|
-
"showUseCount",
|
|
327
|
-
"showUseTime",
|
|
328
|
-
"showGroupSummary"
|
|
329
|
-
]);
|
|
330
|
-
/** FormScope-compatible reactive config handle (see module doc). */
|
|
331
|
-
var ApiConfigScope = class {
|
|
332
|
-
api;
|
|
333
|
-
status = "loading";
|
|
334
|
-
value = { ...DEFAULTS };
|
|
335
|
-
saved = {};
|
|
336
|
-
listeners = /* @__PURE__ */ new Set();
|
|
337
|
-
/** @param api - the browser-half API client (shared with the skill panel). */
|
|
338
|
-
constructor(api) {
|
|
339
|
-
this.api = api;
|
|
340
|
-
this.load();
|
|
341
|
-
}
|
|
342
|
-
async load() {
|
|
343
|
-
try {
|
|
344
|
-
const response = await this.api.config();
|
|
345
|
-
this.value = response.config;
|
|
346
|
-
this.saved = { ...response.saved };
|
|
347
|
-
this.status = "ready";
|
|
348
|
-
} catch {
|
|
349
|
-
this.status = "unavailable";
|
|
350
|
-
}
|
|
351
|
-
this.publish();
|
|
352
|
-
}
|
|
353
|
-
subscribe(listener) {
|
|
354
|
-
this.listeners.add(listener);
|
|
355
|
-
return () => {
|
|
356
|
-
this.listeners.delete(listener);
|
|
357
|
-
};
|
|
358
|
-
}
|
|
359
|
-
getSnapshot() {
|
|
360
|
-
return {
|
|
361
|
-
status: this.status,
|
|
362
|
-
writable: true,
|
|
363
|
-
value: { ...this.value },
|
|
364
|
-
base: { ...DEFAULTS },
|
|
365
|
-
user: { ...this.saved }
|
|
366
|
-
};
|
|
367
|
-
}
|
|
368
|
-
/** Write one field through the config route and adopt the persisted state. */
|
|
369
|
-
async set(field, value) {
|
|
370
|
-
if (!FIELDS.has(field)) return;
|
|
371
|
-
const patch = BOOLEAN_FIELDS.has(field) ? { [field]: Boolean(value) } : { [field]: typeof value === "string" ? value : void 0 };
|
|
372
|
-
await this.apply(patch);
|
|
373
|
-
}
|
|
374
|
-
/** Clear one field's override so it re-inherits the default. */
|
|
375
|
-
async unset(field) {
|
|
376
|
-
if (!FIELDS.has(field)) return;
|
|
377
|
-
await this.apply({ [field]: void 0 });
|
|
378
|
-
}
|
|
379
|
-
/** Persist a patch, then re-seed from the route's fresh response. */
|
|
380
|
-
async apply(patch) {
|
|
381
|
-
const request = {};
|
|
382
|
-
for (const [key, value] of Object.entries(patch)) request[key] = value === void 0 ? null : value;
|
|
383
|
-
const response = await this.api.saveConfig(request);
|
|
384
|
-
this.value = response.config;
|
|
385
|
-
this.saved = { ...response.saved };
|
|
386
|
-
this.publish();
|
|
387
|
-
}
|
|
388
|
-
publish() {
|
|
389
|
-
for (const listener of this.listeners) listener();
|
|
390
|
-
}
|
|
391
|
-
};
|
|
392
|
-
//#endregion
|
|
393
300
|
//#region src/client/locales.ts
|
|
394
301
|
/**
|
|
395
302
|
* dsh-skill-hub surface copy (zh/en). The dictionary type is re-exported
|
|
@@ -419,19 +326,19 @@ window.__ModuleLoader__.load({
|
|
|
419
326
|
"panel.emptyAll": "本地还没有技能。点击「新建技能」创建第一个。",
|
|
420
327
|
"panel.loading": "读取中…",
|
|
421
328
|
"panel.incomplete": "(目录可能不完整)",
|
|
422
|
-
"
|
|
329
|
+
"panel.workspacePlaceholder": "工作区路径(回车应用)",
|
|
330
|
+
"panel.workspaceHint": "填写项目路径后,面板同时显示该项目 .dsh/skills 与 .agents/skills 下的技能(只读);留空回车恢复用户级视图。",
|
|
331
|
+
"panel.workspaceClear": "清除工作区",
|
|
423
332
|
"market.addPlaceholder": "owner/repo 或 https://github.com/…",
|
|
424
333
|
"market.addSource": "添加源",
|
|
425
334
|
"market.noSources": "还没有市场源。从下方内置市场添加,或输入一个 GitHub 仓库。",
|
|
426
|
-
"market.
|
|
335
|
+
"market.title": "市场",
|
|
427
336
|
"market.catalogHint": "精选技能仓库,添加后即可扫描安装,并持续检查更新。",
|
|
428
337
|
"market.catalog.anthropics": "Anthropic 官方技能合集(Claude Skills)",
|
|
429
338
|
"market.catalog.superpowers": "Superpowers 社区技能合集",
|
|
430
339
|
"market.catalog.mattpocock": "Matt Pocock 技能集",
|
|
431
340
|
"market.catalog.openDesign": "设计技能合集(design-templates)",
|
|
432
|
-
"market.added": "已添加",
|
|
433
341
|
"market.add": "添加",
|
|
434
|
-
"market.mySources": "我的市场源",
|
|
435
342
|
"market.installed": "已装 {count}",
|
|
436
343
|
"market.updatable": "可更新 {count}",
|
|
437
344
|
"market.deletedUpstream": "上游已删 {count}",
|
|
@@ -496,6 +403,9 @@ window.__ModuleLoader__.load({
|
|
|
496
403
|
"groups.empty": "还没有场景。先新建一个,再把技能归类。",
|
|
497
404
|
"groups.noCollections": "暂无来源组。从市场导入技能后自动按来源聚合。",
|
|
498
405
|
"groups.personal": "个人",
|
|
406
|
+
"groups.project": "项目级",
|
|
407
|
+
"groups.subdivide": "细分",
|
|
408
|
+
"groups.merge": "合并",
|
|
499
409
|
"groups.noWritable": "该组没有可写技能(只读技能无法由中枢开关)",
|
|
500
410
|
"groups.closeAll": "全部关闭",
|
|
501
411
|
"groups.keepOn": "保留开启",
|
|
@@ -623,19 +533,19 @@ window.__ModuleLoader__.load({
|
|
|
623
533
|
"panel.emptyAll": "No skills installed yet. Create the first one.",
|
|
624
534
|
"panel.loading": "Loading…",
|
|
625
535
|
"panel.incomplete": "(catalog may be incomplete)",
|
|
626
|
-
"
|
|
536
|
+
"panel.workspacePlaceholder": "Workspace path (Enter to apply)",
|
|
537
|
+
"panel.workspaceHint": "Enter a project path to also show that workspace's .dsh/skills and .agents/skills skills (read-only); clear + Enter to return to the user-level view.",
|
|
538
|
+
"panel.workspaceClear": "Clear workspace",
|
|
627
539
|
"market.addPlaceholder": "owner/repo or https://github.com/…",
|
|
628
540
|
"market.addSource": "Add source",
|
|
629
541
|
"market.noSources": "No market sources yet. Add one from the built-in catalog below, or enter a GitHub repo.",
|
|
630
|
-
"market.
|
|
542
|
+
"market.title": "Market",
|
|
631
543
|
"market.catalogHint": "Curated skill repos — add one to scan and install, with ongoing update checks.",
|
|
632
544
|
"market.catalog.anthropics": "Official Anthropic skill collection (Claude Skills)",
|
|
633
545
|
"market.catalog.superpowers": "Superpowers community skill collection",
|
|
634
546
|
"market.catalog.mattpocock": "Matt Pocock skill collection",
|
|
635
547
|
"market.catalog.openDesign": "Design skill collection (design-templates)",
|
|
636
|
-
"market.added": "Added",
|
|
637
548
|
"market.add": "Add",
|
|
638
|
-
"market.mySources": "My sources",
|
|
639
549
|
"market.installed": "{count} installed",
|
|
640
550
|
"market.updatable": "{count} updatable",
|
|
641
551
|
"market.deletedUpstream": "{count} deleted upstream",
|
|
@@ -700,6 +610,9 @@ window.__ModuleLoader__.load({
|
|
|
700
610
|
"groups.empty": "No scenes yet. Create one and start categorizing skills.",
|
|
701
611
|
"groups.noCollections": "No source groups yet. Import skills from the market to group them by source.",
|
|
702
612
|
"groups.personal": "Personal",
|
|
613
|
+
"groups.project": "Project",
|
|
614
|
+
"groups.subdivide": "Split",
|
|
615
|
+
"groups.merge": "Merge",
|
|
703
616
|
"groups.noWritable": "No writable skills in this group (read-only skills cannot be switched from the hub)",
|
|
704
617
|
"groups.closeAll": "Close all",
|
|
705
618
|
"groups.keepOn": "Keep on",
|
|
@@ -1301,11 +1214,17 @@ window.__ModuleLoader__.load({
|
|
|
1301
1214
|
};
|
|
1302
1215
|
//#endregion
|
|
1303
1216
|
//#region src/client/SkillHubSettingsCard.tsx
|
|
1217
|
+
/** Model-invocable dot color default. Single source for the TS side; the
|
|
1218
|
+
* panel's CSS mirrors it via --hub-model (panel.module.css). */
|
|
1219
|
+
const DEFAULT_DOT_MODEL_COLOR = "#2f81f7";
|
|
1220
|
+
/** User-invocable dot color default. Single source for the TS side; the
|
|
1221
|
+
* panel's CSS mirrors it via --hub-user (panel.module.css). */
|
|
1222
|
+
const DEFAULT_DOT_USER_COLOR = "#3fb950";
|
|
1304
1223
|
/** Bridges the hub's config scope onto the card's staged form. */
|
|
1305
1224
|
var SkillHubSettingsCardController = class {
|
|
1306
1225
|
form;
|
|
1307
1226
|
store;
|
|
1308
|
-
/** @param scope - the hub
|
|
1227
|
+
/** @param scope - the hub settings scope the card edits (FormScope-compatible). */
|
|
1309
1228
|
constructor(scope) {
|
|
1310
1229
|
this.form = new CardForm(scope, [
|
|
1311
1230
|
booleanField("enabled"),
|
|
@@ -1512,15 +1431,23 @@ window.__ModuleLoader__.load({
|
|
|
1512
1431
|
}
|
|
1513
1432
|
/** Origin-repo filter value: skills with no source record (private skills). */
|
|
1514
1433
|
const PRIVATE_SOURCE = "private";
|
|
1434
|
+
/** 项目级技能来源(它们有 workspace 归属,不属于「个人」组)。 */
|
|
1435
|
+
function isProjectSource(source) {
|
|
1436
|
+
return source === "project-dsh" || source === "project-agents";
|
|
1437
|
+
}
|
|
1515
1438
|
/**
|
|
1516
1439
|
* Apply the origin filter ('all' or a specific origin repo; skills without a
|
|
1517
1440
|
* source record count as PRIVATE_SOURCE). The origins map is the store's
|
|
1518
1441
|
* skillName → repo derivation, so filtering follows the tracked source
|
|
1519
1442
|
* records instead of the filesystem root a skill happens to live under.
|
|
1443
|
+
* 项目级技能(有 workspace 归属)永远不算「个人」。
|
|
1520
1444
|
*/
|
|
1521
1445
|
function filterBySource(skills, source, origins) {
|
|
1522
1446
|
if (source === "all") return [...skills];
|
|
1523
|
-
return skills.filter((skill) =>
|
|
1447
|
+
return skills.filter((skill) => {
|
|
1448
|
+
if (isProjectSource(skill.source)) return false;
|
|
1449
|
+
return (origins[skill.name] ?? "private") === source;
|
|
1450
|
+
});
|
|
1524
1451
|
}
|
|
1525
1452
|
/**
|
|
1526
1453
|
* Sort a skill list in place-safe copy order: name ascending, added
|
|
@@ -1583,7 +1510,7 @@ window.__ModuleLoader__.load({
|
|
|
1583
1510
|
}
|
|
1584
1511
|
//#endregion
|
|
1585
1512
|
//#region \0dsh-css:/Users/huanyi/Documents/personal/tools/dsh-skill-hub/src/client/panel/panel.module.css.mjs
|
|
1586
|
-
const css = "._6VtqdG_panel{-webkit-font-smoothing:antialiased;color-scheme:light dark;--hub-model:#2f81f7;--hub-user:#3fb950;flex-direction:column;gap:14px;max-width:720px;margin:0 auto;padding:28px 20px 44px;font-family:-apple-system,BlinkMacSystemFont,SF Pro Text,Helvetica Neue,sans-serif;display:flex}._6VtqdG_header{flex-wrap:wrap;align-items:baseline;gap:10px;display:flex}._6VtqdG_title{letter-spacing:-.02em;margin:0;font-size:24px;font-weight:700}._6VtqdG_hint{opacity:.5;font-size:12px}._6VtqdG_actions{gap:8px;margin-left:auto;display:flex}._6VtqdG_segmented{background:#80808024;border-radius:9px;gap:2px;padding:2px;display:flex}._6VtqdG_segBtn{color:inherit;opacity:.62;cursor:pointer;background:0 0;border:none;border-radius:7px;padding:5px 13px;font-size:12px;transition:background .15s,opacity .15s}._6VtqdG_segBtn:hover{opacity:.9}._6VtqdG_segBtnActive{opacity:1;background:#80808047;font-weight:600}._6VtqdG_search{width:100%;color:inherit;background:#8080801f;border:none;border-radius:10px;outline:none;padding:10px 14px;font-size:13px;transition:background .15s}._6VtqdG_search::placeholder{opacity:.45}._6VtqdG_search:focus{background:#8080802e}._6VtqdG_section{background:#8080800f;border-radius:12px;flex-direction:column;margin-top:4px;display:flex;overflow:hidden;box-shadow:0 1px 2px #00000008,0 4px 12px #0000000a}._6VtqdG_sectionTitle{margin:14px 16px 2px;font-size:13px;font-weight:700}._6VtqdG_groupHead{align-items:center;gap:8px;padding:12px 14px 4px;display:flex}._6VtqdG_groupTitle{opacity:.92;flex:1;min-width:0;font-size:13px;font-weight:600}._6VtqdG_groupOps{flex:none;gap:6px;display:inline-flex}._6VtqdG_opBtn{color:inherit;cursor:pointer;background:#80808021;border:none;border-radius:999px;padding:3px 11px;font-size:11px;transition:background .15s}._6VtqdG_opBtn:hover{background:#80808038}._6VtqdG_opBtn:disabled{opacity:.4;cursor:default}._6VtqdG_row{cursor:pointer;background:0 0;align-items:center;gap:12px;padding:10px 14px;transition:background .12s;display:flex}._6VtqdG_row:hover{background:#80808014}._6VtqdG_rowStatic{cursor:default}._6VtqdG_rowStatic:hover{background:0 0}._6VtqdG_row+._6VtqdG_row{border-top:.5px solid #80808021}._6VtqdG_rowMain{flex:1;min-width:0}._6VtqdG_rowName{align-items:center;gap:5px;font-size:13.5px;display:flex}._6VtqdG_rowNameText{font-weight:600}._6VtqdG_rowDesc{opacity:.52;white-space:nowrap;text-overflow:ellipsis;margin-top:1px;font-size:12.5px;overflow:hidden}._6VtqdG_rowMeta{opacity:.45;white-space:nowrap;flex:none;font-size:11.5px}._6VtqdG_badges{flex:none;align-items:center;gap:6px;display:flex}._6VtqdG_badge{opacity:.8;white-space:nowrap;border:.5px solid #8080804d;border-radius:999px;padding:1px 7px;font-size:10.5px}._6VtqdG_badgeModel{color:var(--hub-model);border-color:currentColor}._6VtqdG_badgeUser{color:var(--hub-user);border-color:currentColor}._6VtqdG_badgeUses{color:#d29922;border-color:currentColor}._6VtqdG_badgeReadonly{opacity:.5}._6VtqdG_switch{cursor:pointer;background:#8080804d;border:none;border-radius:999px;flex:none;width:36px;height:21px;padding:2px;transition:background .18s}._6VtqdG_switch:disabled{opacity:.5;cursor:default}._6VtqdG_switchOn{background:#34c759}._6VtqdG_switchThumb{background:#fff;border-radius:50%;width:17px;height:17px;transition:transform .18s;display:block;transform:translate(0);box-shadow:0 1px 2px #00000040}._6VtqdG_switchOn ._6VtqdG_switchThumb{transform:translate(15px)}._6VtqdG_empty{opacity:.5;text-align:center;padding:20px 0;font-size:13px}._6VtqdG_diagRow{background:#d299220f;border-left:3px solid #d29922;padding:9px 14px;font-size:12px}._6VtqdG_diagPath{opacity:.75;word-break:break-all;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px}._6VtqdG_diagReason{opacity:.65;margin-top:1px}._6VtqdG_updateLink{color:inherit;font-weight:600;text-decoration:underline}._6VtqdG_errorBanner{color:#d1242f;white-space:pre-wrap;word-break:break-all;background:#d1242f12;border-radius:10px;align-items:flex-start;gap:10px;padding:10px 14px;font-size:12.5px;display:flex}._6VtqdG_successBanner{color:#3fb950;white-space:pre-wrap;word-break:break-all;background:#3fb95014;border-radius:10px;align-items:flex-start;gap:10px;padding:10px 14px;font-size:12.5px;display:flex}._6VtqdG_detailHead{flex-wrap:wrap;align-items:center;gap:10px;display:flex}._6VtqdG_back{color:inherit;cursor:pointer;background:#8080801f;border:none;border-radius:8px;padding:6px 12px;font-size:12.5px}._6VtqdG_back:hover{background:#80808033}._6VtqdG_detailName{letter-spacing:-.01em;font-size:19px;font-weight:700}._6VtqdG_detailMeta{opacity:.6;flex-direction:column;gap:4px;font-size:12px;display:flex}._6VtqdG_detailMetaLine{word-break:break-all}._6VtqdG_detailContent{white-space:pre-wrap;word-break:break-word;background:#8080800d;border-radius:12px;max-height:62vh;padding:14px 16px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.6;overflow:auto}._6VtqdG_form{background:#8080800d;border-radius:12px;flex-direction:column;gap:10px;padding:14px 16px;display:flex;box-shadow:0 1px 2px #00000008,0 4px 12px #0000000a}._6VtqdG_formRow{flex-direction:column;gap:5px;display:flex}._6VtqdG_formLabel{opacity:.6;font-size:12px}._6VtqdG_input,._6VtqdG_select{color:inherit;background:#8080801f;border:none;border-radius:9px;outline:none;padding:9px 12px;font-size:13px}._6VtqdG_input:focus,._6VtqdG_select:focus{background:#8080802e}._6VtqdG_buttons{align-items:center;gap:8px;display:flex}._6VtqdG_button{cursor:pointer;color:inherit;background:#8080801f;border:none;border-radius:8px;padding:6px 13px;font-size:12.5px;transition:background .15s}._6VtqdG_button:hover{background:#80808033}._6VtqdG_primary{background:var(--hub-model);color:#fff}._6VtqdG_primary:hover{opacity:.92;background:#2f81f7}._6VtqdG_primary:disabled{opacity:.45;cursor:default}._6VtqdG_danger{color:#fff;background:#d1242f}._6VtqdG_danger:hover{opacity:.92;background:#d1242f}._6VtqdG_danger:disabled{opacity:.45;cursor:default}._6VtqdG_formError{color:#d1242f}._6VtqdG_formSuccess{color:#3fb950}._6VtqdG_muted{opacity:.55}._6VtqdG_dot{vertical-align:middle;border-radius:50%;width:7px;height:7px;margin-left:5px;display:inline-block}._6VtqdG_dotModel{background:var(--hub-model)}._6VtqdG_dotUser{background:var(--hub-user)}._6VtqdG_legend{opacity:.55;flex-wrap:wrap;align-items:center;gap:12px;padding:2px 2px 0;font-size:11.5px;display:flex}._6VtqdG_legendItem{align-items:center;gap:4px;display:inline-flex}._6VtqdG_legendItem ._6VtqdG_dot{margin-left:0}._6VtqdG_legendHint{opacity:.8}._6VtqdG_badgeSource,._6VtqdG_badgeCount{opacity:.72;background:#8080801a;border:none}._6VtqdG_badgeDisabled{opacity:.58;background:#8080801a;border:none}._6VtqdG_disclosure{min-width:0;color:inherit;cursor:pointer;text-align:left;background:0 0;border:none;flex:1;align-items:center;gap:7px;padding:2px 0;display:flex}._6VtqdG_disclosure:hover ._6VtqdG_groupTitle{opacity:1}._6VtqdG_chevron{opacity:.55;border-bottom:1.5px solid;border-right:1.5px solid;flex:none;width:8px;height:8px;margin-right:2px;transition:transform .15s;transform:rotate(45deg)}._6VtqdG_chevronCollapsed{transform:rotate(-45deg)}._6VtqdG_headerCount{opacity:.5;white-space:nowrap;margin-left:2px;font-size:12px}._6VtqdG_subbar{flex-wrap:wrap;align-items:center;gap:8px;display:flex}._6VtqdG_legendToggle{width:22px;height:22px;color:inherit;cursor:pointer;opacity:.55;background:#80808024;border:none;border-radius:50%;flex:none;font-size:12px;line-height:1;transition:opacity .15s,background .15s}._6VtqdG_legendToggle:hover{opacity:1}._6VtqdG_legendToggleActive{opacity:1;background:#80808047}._6VtqdG_filterBar{flex-wrap:wrap;align-items:center;gap:8px;display:flex}._6VtqdG_filterBar ._6VtqdG_formLabel{margin:0}._6VtqdG_useCount{color:#d29922;vertical-align:1px;background:#d299221f;border-radius:999px;margin-left:6px;padding:0 6px;font-size:11px;font-weight:700;line-height:1.6;display:inline-block}._6VtqdG_useTime{opacity:.45;white-space:nowrap;margin-left:auto;font-size:11px}._6VtqdG_switchMixed{background:#2f81f773}._6VtqdG_switchMixed ._6VtqdG_switchThumb{transform:translate(7.5px);box-shadow:0 1px 2px #00000040}._6VtqdG_groupTitleInner{align-items:baseline;display:inline-flex}._6VtqdG_groupOps{flex-wrap:wrap;flex:none;align-items:center;gap:6px;display:inline-flex}._6VtqdG_sourceLink{color:inherit;opacity:.9;border-bottom:1px dotted;font-weight:600;text-decoration:none}._6VtqdG_sourceLink:hover{opacity:1}._6VtqdG_statusBadges{flex:none;align-items:center;gap:4px;display:inline-flex}._6VtqdG_statusBadge{opacity:.8;white-space:nowrap;background:#8080801a;border:.5px solid #8080804d;border-radius:999px;padding:1px 7px;font-size:10.5px}._6VtqdG_statusOk{color:#3fb950;background:0 0;border-color:currentColor}._6VtqdG_statusUpdated{color:#d29922;background:#d2992214;border-color:currentColor}._6VtqdG_statusError{color:#d1242f;background:#d1242f14;border-color:currentColor}._6VtqdG_statusWrap{cursor:pointer;background:0 0;border:none;align-items:center;gap:4px;padding:0;font-family:inherit;display:inline-flex}._6VtqdG_statusWrap:hover ._6VtqdG_statusBadge{opacity:1}._6VtqdG_statusButton{cursor:pointer;font-family:inherit}._6VtqdG_statusButton:hover{opacity:1}._6VtqdG_opDanger{color:#d1242f;background:#d1242f1a}._6VtqdG_opDanger:hover{background:#d1242f2e}._6VtqdG_iconBtn{border-radius:999px;justify-content:center;align-items:center;width:24px;height:24px;padding:0;display:inline-flex}._6VtqdG_sourceCard{background:#8080800d;border-radius:12px;flex-direction:column;gap:4px;padding:12px 14px;font-size:12px;display:flex}._6VtqdG_sourceCardTitle{flex-wrap:wrap;align-items:center;gap:8px;display:flex}._6VtqdG_dialogOverlay{z-index:50;-webkit-backdrop-filter:blur(2px);background:#00000059;justify-content:center;align-items:center;padding:24px;display:flex;position:fixed;inset:0}._6VtqdG_dialog{background:var(--dsw-surface,#f5f5f7);min-width:min(320px,100%);max-width:380px;color:var(--dsw-text,#1d1d1f);border-radius:14px;padding:18px 18px 14px;box-shadow:0 12px 40px #00000047,0 2px 8px #00000029}@media (prefers-color-scheme:dark){._6VtqdG_dialog{color:#f5f5f7;background:#2c2c2e}}._6VtqdG_dialogTitle{letter-spacing:-.01em;margin:0 0 8px;font-size:15px;font-weight:700}._6VtqdG_dialogText{opacity:.65;margin:0 0 10px;font-size:12.5px;line-height:1.5}._6VtqdG_dialogList{flex-direction:column;gap:5px;max-height:200px;margin:0 0 14px;padding:0;list-style:none;display:flex;overflow:auto}._6VtqdG_dialogList li{opacity:.85;word-break:break-all;font-size:12.5px}._6VtqdG_dialogActions{justify-content:flex-end;gap:8px;display:flex}._6VtqdG_filterBar ._6VtqdG_search{flex:1;min-width:160px}._6VtqdG_filterBar ._6VtqdG_formLabel{flex:none}._6VtqdG_filterBar ._6VtqdG_select{flex:none;max-width:150px}._6VtqdG_filterBar ._6VtqdG_segmented{flex:none}._6VtqdG_titleIcon{vertical-align:-2px;opacity:.8;margin-right:2px;display:inline-block}._6VtqdG_grow{flex:1}._6VtqdG_hintLine{opacity:.55;margin:0;font-size:11.5px}._6VtqdG_hintPadded{margin:6px 12px 2px}._6VtqdG_hintInline{margin:4px 2px}._6VtqdG_rowMuted{cursor:default;opacity:.55}._6VtqdG_actionsPadded{padding:8px 12px}._6VtqdG_actionsTop{margin-top:8px}._6VtqdG_actionsBottom{margin-bottom:8px}._6VtqdG_groupTime{margin-left:6px}._6VtqdG_sectionHeadRow{align-items:center;gap:8px;display:flex}._6VtqdG_sectionTitleFill{flex:1}._6VtqdG_dialogSelect{width:100%;margin-bottom:12px}._6VtqdG_dialogRow{align-items:center;gap:8px;display:flex}";
|
|
1513
|
+
const css = "._6VtqdG_panel{-webkit-font-smoothing:antialiased;color-scheme:light dark;--hub-model:#2f81f7;--hub-user:#3fb950;flex-direction:column;gap:14px;max-width:720px;margin:0 auto;padding:28px 20px 44px;font-family:-apple-system,BlinkMacSystemFont,SF Pro Text,Helvetica Neue,sans-serif;display:flex}._6VtqdG_header{flex-wrap:wrap;align-items:baseline;gap:10px;display:flex}._6VtqdG_title{letter-spacing:-.02em;margin:0;font-size:24px;font-weight:700}._6VtqdG_hint{opacity:.5;font-size:12px}._6VtqdG_actions{gap:8px;margin-left:auto;display:flex}._6VtqdG_segmented{background:#80808024;border-radius:9px;gap:2px;padding:2px;display:flex}._6VtqdG_segBtn{color:inherit;opacity:.62;cursor:pointer;background:0 0;border:none;border-radius:7px;padding:5px 13px;font-size:12px;transition:background .15s,opacity .15s}._6VtqdG_segBtn:hover{opacity:.9}._6VtqdG_segBtnActive{opacity:1;background:#80808047;font-weight:600}._6VtqdG_search{width:100%;color:inherit;background:#8080801f;border:none;border-radius:10px;outline:none;padding:10px 14px;font-size:13px;transition:background .15s}._6VtqdG_search::placeholder{opacity:.45}._6VtqdG_search:focus{background:#8080802e}._6VtqdG_section{background:#8080800f;border-radius:12px;flex-direction:column;margin-top:4px;display:flex;overflow:hidden;box-shadow:0 1px 2px #00000008,0 4px 12px #0000000a}._6VtqdG_sectionTitle{margin:14px 16px 2px;font-size:13px;font-weight:700}._6VtqdG_groupHead{align-items:center;gap:8px;padding:12px 14px 4px;display:flex}._6VtqdG_groupTitle{opacity:.92;flex:1;min-width:0;font-size:13px;font-weight:600}._6VtqdG_groupOps{flex:none;gap:6px;display:inline-flex}._6VtqdG_opBtn{color:inherit;cursor:pointer;background:#80808021;border:none;border-radius:999px;padding:3px 11px;font-size:11px;transition:background .15s}._6VtqdG_opBtn:hover{background:#80808038}._6VtqdG_opBtn:disabled{opacity:.4;cursor:default}._6VtqdG_row{cursor:pointer;background:0 0;align-items:center;gap:12px;padding:10px 14px;transition:background .12s;display:flex}._6VtqdG_row:hover{background:#80808014}._6VtqdG_rowStatic{cursor:default}._6VtqdG_rowStatic:hover{background:0 0}._6VtqdG_row+._6VtqdG_row{border-top:.5px solid #80808021}._6VtqdG_rowMain{flex:1;min-width:0}._6VtqdG_rowName{align-items:center;gap:5px;font-size:13.5px;display:flex}._6VtqdG_rowNameText{font-weight:600}._6VtqdG_rowDesc{opacity:.52;white-space:nowrap;text-overflow:ellipsis;margin-top:1px;font-size:12.5px;overflow:hidden}._6VtqdG_rowMeta{opacity:.45;white-space:nowrap;flex:none;font-size:11.5px}._6VtqdG_badges{flex:none;align-items:center;gap:6px;display:flex}._6VtqdG_badge{opacity:.8;white-space:nowrap;border:.5px solid #8080804d;border-radius:999px;padding:1px 7px;font-size:10.5px}._6VtqdG_badgeModel{color:var(--hub-model);border-color:currentColor}._6VtqdG_badgeUser{color:var(--hub-user);border-color:currentColor}._6VtqdG_badgeUses{color:#d29922;border-color:currentColor}._6VtqdG_badgeReadonly{opacity:.5}._6VtqdG_switch{cursor:pointer;background:#8080804d;border:none;border-radius:999px;flex:none;width:36px;height:21px;padding:2px;transition:background .18s}._6VtqdG_switch:disabled{opacity:.5;cursor:default}._6VtqdG_switchOn{background:#34c759}._6VtqdG_switchThumb{background:#fff;border-radius:50%;width:17px;height:17px;transition:transform .18s;display:block;transform:translate(0);box-shadow:0 1px 2px #00000040}._6VtqdG_switchOn ._6VtqdG_switchThumb{transform:translate(15px)}._6VtqdG_empty{opacity:.5;text-align:center;padding:20px 0;font-size:13px}._6VtqdG_diagRow{background:#d299220f;border-left:3px solid #d29922;padding:9px 14px;font-size:12px}._6VtqdG_diagPath{opacity:.75;word-break:break-all;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px}._6VtqdG_diagReason{opacity:.65;margin-top:1px}._6VtqdG_updateLink{color:inherit;font-weight:600;text-decoration:underline}._6VtqdG_errorBanner{color:#d1242f;white-space:pre-wrap;word-break:break-all;background:#d1242f12;border-radius:10px;align-items:flex-start;gap:10px;padding:10px 14px;font-size:12.5px;display:flex}._6VtqdG_successBanner{color:#3fb950;white-space:pre-wrap;word-break:break-all;background:#3fb95014;border-radius:10px;align-items:flex-start;gap:10px;padding:10px 14px;font-size:12.5px;display:flex}._6VtqdG_detailHead{flex-wrap:wrap;align-items:center;gap:10px;display:flex}._6VtqdG_back{color:inherit;cursor:pointer;background:#8080801f;border:none;border-radius:8px;padding:6px 12px;font-size:12.5px}._6VtqdG_back:hover{background:#80808033}._6VtqdG_detailName{letter-spacing:-.01em;font-size:19px;font-weight:700}._6VtqdG_detailMeta{opacity:.6;flex-direction:column;gap:4px;font-size:12px;display:flex}._6VtqdG_detailMetaLine{word-break:break-all}._6VtqdG_detailContent{white-space:pre-wrap;word-break:break-word;background:#8080800d;border-radius:12px;max-height:62vh;padding:14px 16px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.6;overflow:auto}._6VtqdG_form{background:#8080800d;border-radius:12px;flex-direction:column;gap:10px;padding:14px 16px;display:flex;box-shadow:0 1px 2px #00000008,0 4px 12px #0000000a}._6VtqdG_formRow{flex-direction:column;gap:5px;display:flex}._6VtqdG_formLabel{opacity:.6;font-size:12px}._6VtqdG_input,._6VtqdG_select{color:inherit;background:#8080801f;border:none;border-radius:9px;outline:none;padding:9px 12px;font-size:13px}._6VtqdG_input:focus,._6VtqdG_select:focus{background:#8080802e}._6VtqdG_buttons{align-items:center;gap:8px;display:flex}._6VtqdG_button{cursor:pointer;color:inherit;background:#8080801f;border:none;border-radius:8px;padding:6px 13px;font-size:12.5px;transition:background .15s}._6VtqdG_button:hover{background:#80808033}._6VtqdG_primary{background:var(--hub-model);color:#fff}._6VtqdG_primary:hover{opacity:.92;background:#2f81f7}._6VtqdG_primary:disabled{opacity:.45;cursor:default}._6VtqdG_danger{color:#fff;background:#d1242f}._6VtqdG_danger:hover{opacity:.92;background:#d1242f}._6VtqdG_danger:disabled{opacity:.45;cursor:default}._6VtqdG_formError{color:#d1242f}._6VtqdG_formSuccess{color:#3fb950}._6VtqdG_muted{opacity:.55}._6VtqdG_dot{vertical-align:middle;border-radius:50%;width:7px;height:7px;margin-left:5px;display:inline-block}._6VtqdG_dotModel{background:var(--hub-model)}._6VtqdG_dotUser{background:var(--hub-user)}._6VtqdG_legend{opacity:.55;flex-wrap:wrap;align-items:center;gap:12px;padding:2px 2px 0;font-size:11.5px;display:flex}._6VtqdG_legendItem{align-items:center;gap:4px;display:inline-flex}._6VtqdG_legendItem ._6VtqdG_dot{margin-left:0}._6VtqdG_legendHint{opacity:.8}._6VtqdG_badgeSource,._6VtqdG_badgeCount{opacity:.72;background:#8080801a;border:none}._6VtqdG_badgeDisabled{opacity:.58;background:#8080801a;border:none}._6VtqdG_disclosure{min-width:0;color:inherit;cursor:pointer;text-align:left;background:0 0;border:none;flex:1;align-items:center;gap:7px;padding:2px 0;display:flex}._6VtqdG_disclosure:hover ._6VtqdG_groupTitle{opacity:1}._6VtqdG_chevron{opacity:.55;border-bottom:1.5px solid;border-right:1.5px solid;flex:none;width:8px;height:8px;margin-right:2px;transition:transform .15s;transform:rotate(45deg)}._6VtqdG_chevronCollapsed{transform:rotate(-45deg)}._6VtqdG_headerCount{opacity:.5;white-space:nowrap;margin-left:2px;font-size:12px}._6VtqdG_subbar{flex-wrap:wrap;align-items:center;gap:8px;display:flex}._6VtqdG_legendToggle{width:22px;height:22px;color:inherit;cursor:pointer;opacity:.55;background:#80808024;border:none;border-radius:50%;flex:none;font-size:12px;line-height:1;transition:opacity .15s,background .15s}._6VtqdG_legendToggle:hover{opacity:1}._6VtqdG_legendToggleActive{opacity:1;background:#80808047}._6VtqdG_filterBar{flex-wrap:wrap;align-items:center;gap:8px;display:flex}._6VtqdG_filterBar ._6VtqdG_formLabel{margin:0}._6VtqdG_useCount{color:#d29922;vertical-align:1px;background:#d299221f;border-radius:999px;margin-left:6px;padding:0 6px;font-size:11px;font-weight:700;line-height:1.6;display:inline-block}._6VtqdG_useTime{opacity:.45;white-space:nowrap;margin-left:auto;font-size:11px}._6VtqdG_switchMixed{background:#2f81f773}._6VtqdG_switchMixed ._6VtqdG_switchThumb{transform:translate(7.5px);box-shadow:0 1px 2px #00000040}._6VtqdG_groupTitleInner{align-items:baseline;display:inline-flex}._6VtqdG_groupOps{flex-wrap:wrap;flex:none;align-items:center;gap:6px;display:inline-flex}._6VtqdG_sourceLink{color:inherit;opacity:.9;border-bottom:1px dotted;font-weight:600;text-decoration:none}._6VtqdG_sourceLink:hover{opacity:1}._6VtqdG_statusBadges{flex:none;align-items:center;gap:4px;display:inline-flex}._6VtqdG_statusBadge{opacity:.8;white-space:nowrap;background:#8080801a;border:.5px solid #8080804d;border-radius:999px;padding:1px 7px;font-size:10.5px}._6VtqdG_statusOk{color:#3fb950;background:0 0;border-color:currentColor}._6VtqdG_statusUpdated{color:#d29922;background:#d2992214;border-color:currentColor}._6VtqdG_statusError{color:#d1242f;background:#d1242f14;border-color:currentColor}._6VtqdG_statusWrap{cursor:pointer;background:0 0;border:none;align-items:center;gap:4px;padding:0;font-family:inherit;display:inline-flex}._6VtqdG_statusWrap:hover ._6VtqdG_statusBadge{opacity:1}._6VtqdG_statusButton{cursor:pointer;font-family:inherit}._6VtqdG_statusButton:hover{opacity:1}._6VtqdG_opDanger{color:#d1242f;background:#d1242f1a}._6VtqdG_opDanger:hover{background:#d1242f2e}._6VtqdG_iconBtn{border-radius:999px;justify-content:center;align-items:center;width:24px;height:24px;padding:0;display:inline-flex}._6VtqdG_sourceCard{background:#8080800d;border-radius:12px;flex-direction:column;gap:4px;padding:12px 14px;font-size:12px;display:flex}._6VtqdG_sourceCardTitle{flex-wrap:wrap;align-items:center;gap:8px;display:flex}._6VtqdG_dialogOverlay{z-index:50;-webkit-backdrop-filter:blur(2px);background:#00000059;justify-content:center;align-items:center;padding:24px;display:flex;position:fixed;inset:0}._6VtqdG_dialog{background:var(--dsw-surface,#f5f5f7);min-width:min(320px,100%);max-width:380px;color:var(--dsw-text,#1d1d1f);border-radius:14px;padding:18px 18px 14px;box-shadow:0 12px 40px #00000047,0 2px 8px #00000029}@media (prefers-color-scheme:dark){._6VtqdG_dialog{color:#f5f5f7;background:#2c2c2e}}._6VtqdG_dialogTitle{letter-spacing:-.01em;margin:0 0 8px;font-size:15px;font-weight:700}._6VtqdG_dialogText{opacity:.65;margin:0 0 10px;font-size:12.5px;line-height:1.5}._6VtqdG_dialogList{flex-direction:column;gap:5px;max-height:200px;margin:0 0 14px;padding:0;list-style:none;display:flex;overflow:auto}._6VtqdG_dialogList li{opacity:.85;word-break:break-all;font-size:12.5px}._6VtqdG_dialogActions{justify-content:flex-end;gap:8px;display:flex}._6VtqdG_filterBar ._6VtqdG_search{flex:1;min-width:160px}._6VtqdG_filterBar ._6VtqdG_formLabel{flex:none}._6VtqdG_filterBar ._6VtqdG_select{flex:none;max-width:150px}._6VtqdG_filterBar ._6VtqdG_segmented{flex:none}._6VtqdG_titleIcon{vertical-align:-2px;opacity:.8;margin-right:2px;display:inline-block}._6VtqdG_grow{flex:1}._6VtqdG_hintLine{opacity:.55;margin:0;font-size:11.5px}._6VtqdG_hintPadded{margin:6px 12px 2px}._6VtqdG_hintInline{margin:4px 2px}._6VtqdG_rowMuted{cursor:default;opacity:.55}._6VtqdG_actionsPadded{padding:8px 12px}._6VtqdG_actionsTop{margin-top:8px}._6VtqdG_actionsBottom{margin-bottom:8px}._6VtqdG_groupTime{margin-left:6px}._6VtqdG_sectionHeadRow{align-items:center;gap:8px;display:flex}._6VtqdG_sectionTitleFill{flex:1}._6VtqdG_dialogSelect{width:100%;margin-bottom:12px}._6VtqdG_dialogRow{align-items:center;gap:8px;display:flex}._6VtqdG_workspaceBox{align-items:center;gap:6px;display:inline-flex}._6VtqdG_workspaceInput{width:200px;padding:6px 10px;font-size:12px}._6VtqdG_projectNest{margin-left:14px}";
|
|
1587
1514
|
const tagId = "dsh-skill-hub/panel.module.css";
|
|
1588
1515
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
1589
1516
|
const tag = document.createElement("style");
|
|
@@ -1664,6 +1591,7 @@ window.__ModuleLoader__.load({
|
|
|
1664
1591
|
"opDanger": "_6VtqdG_opDanger",
|
|
1665
1592
|
"panel": "_6VtqdG_panel",
|
|
1666
1593
|
"primary": "_6VtqdG_primary",
|
|
1594
|
+
"projectNest": "_6VtqdG_projectNest",
|
|
1667
1595
|
"row": "_6VtqdG_row",
|
|
1668
1596
|
"rowDesc": "_6VtqdG_rowDesc",
|
|
1669
1597
|
"rowMain": "_6VtqdG_rowMain",
|
|
@@ -1701,7 +1629,9 @@ window.__ModuleLoader__.load({
|
|
|
1701
1629
|
"titleIcon": "_6VtqdG_titleIcon",
|
|
1702
1630
|
"updateLink": "_6VtqdG_updateLink",
|
|
1703
1631
|
"useCount": "_6VtqdG_useCount",
|
|
1704
|
-
"useTime": "_6VtqdG_useTime"
|
|
1632
|
+
"useTime": "_6VtqdG_useTime",
|
|
1633
|
+
"workspaceBox": "_6VtqdG_workspaceBox",
|
|
1634
|
+
"workspaceInput": "_6VtqdG_workspaceInput"
|
|
1705
1635
|
};
|
|
1706
1636
|
//#endregion
|
|
1707
1637
|
//#region src/client/panel/SourceStatusBadge.tsx
|
|
@@ -2187,6 +2117,123 @@ window.__ModuleLoader__.load({
|
|
|
2187
2117
|
}
|
|
2188
2118
|
//#endregion
|
|
2189
2119
|
//#region src/client/panel/SourcesView.tsx
|
|
2120
|
+
/** 项目级三级树:项目级 → 具体工作区(可折叠、可细分)→ .dsh/.agents。 */
|
|
2121
|
+
function ProjectTree(props) {
|
|
2122
|
+
const { hub } = props;
|
|
2123
|
+
const { sorted, sourceFilter, origins, collapsedGroups, subdividedProjects, toggleGroupCollapse, toggleSubdivide } = hub;
|
|
2124
|
+
const projectSkills = filterBySource(sorted, sourceFilter, origins).filter((skill) => isProjectSource(skill.source));
|
|
2125
|
+
if (projectSkills.length === 0) return null;
|
|
2126
|
+
const byProject = /* @__PURE__ */ new Map();
|
|
2127
|
+
for (const skill of projectSkills) {
|
|
2128
|
+
const key = skill.workspace ?? skill.source;
|
|
2129
|
+
const entry = byProject.get(key);
|
|
2130
|
+
if (entry === void 0) byProject.set(key, {
|
|
2131
|
+
title: skill.workspaceTitle ?? skill.workspace ?? tt("groups.project"),
|
|
2132
|
+
skills: [skill]
|
|
2133
|
+
});
|
|
2134
|
+
else entry.skills.push(skill);
|
|
2135
|
+
}
|
|
2136
|
+
const topCollapsed = collapsedGroups.has("project");
|
|
2137
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2138
|
+
className: panel_module_css_default.section,
|
|
2139
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2140
|
+
className: panel_module_css_default.groupHead,
|
|
2141
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2142
|
+
type: "button",
|
|
2143
|
+
className: panel_module_css_default.disclosure,
|
|
2144
|
+
"aria-expanded": !topCollapsed,
|
|
2145
|
+
onClick: () => {
|
|
2146
|
+
toggleGroupCollapse("project");
|
|
2147
|
+
},
|
|
2148
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: panel_module_css_default.chevron + (topCollapsed ? " " + panel_module_css_default.chevronCollapsed : "") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2149
|
+
className: panel_module_css_default.groupTitle,
|
|
2150
|
+
children: [
|
|
2151
|
+
tt("groups.project"),
|
|
2152
|
+
" · ",
|
|
2153
|
+
byProject.size
|
|
2154
|
+
]
|
|
2155
|
+
})]
|
|
2156
|
+
})
|
|
2157
|
+
}), !topCollapsed ? [...byProject.entries()].map(([key, proj]) => {
|
|
2158
|
+
const projKey = "project:" + key;
|
|
2159
|
+
const projCollapsed = collapsedGroups.has(projKey);
|
|
2160
|
+
const subdivided = subdividedProjects.has(key);
|
|
2161
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2162
|
+
className: panel_module_css_default.section + " " + panel_module_css_default.projectNest,
|
|
2163
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2164
|
+
className: panel_module_css_default.groupHead,
|
|
2165
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2166
|
+
type: "button",
|
|
2167
|
+
className: panel_module_css_default.disclosure,
|
|
2168
|
+
"aria-expanded": !projCollapsed,
|
|
2169
|
+
onClick: () => {
|
|
2170
|
+
toggleGroupCollapse(projKey);
|
|
2171
|
+
},
|
|
2172
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: panel_module_css_default.chevron + (projCollapsed ? " " + panel_module_css_default.chevronCollapsed : "") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2173
|
+
className: panel_module_css_default.groupTitle,
|
|
2174
|
+
children: [
|
|
2175
|
+
proj.title,
|
|
2176
|
+
" · ",
|
|
2177
|
+
proj.skills.length,
|
|
2178
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(GroupSummary, {
|
|
2179
|
+
members: proj.skills.map((skill) => skill.name),
|
|
2180
|
+
hub
|
|
2181
|
+
})
|
|
2182
|
+
]
|
|
2183
|
+
})]
|
|
2184
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2185
|
+
className: panel_module_css_default.groupOps,
|
|
2186
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2187
|
+
type: "button",
|
|
2188
|
+
className: panel_module_css_default.opBtn,
|
|
2189
|
+
onClick: (event) => {
|
|
2190
|
+
event.stopPropagation();
|
|
2191
|
+
toggleSubdivide(key);
|
|
2192
|
+
},
|
|
2193
|
+
children: subdivided ? tt("groups.merge") : tt("groups.subdivide")
|
|
2194
|
+
})
|
|
2195
|
+
})]
|
|
2196
|
+
}), !projCollapsed ? subdivided ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2197
|
+
className: panel_module_css_default.projectNest,
|
|
2198
|
+
children: ["project-dsh", "project-agents"].map((source) => {
|
|
2199
|
+
const list = proj.skills.filter((skill) => skill.source === source);
|
|
2200
|
+
if (list.length === 0) return null;
|
|
2201
|
+
const srcKey = projKey + ":" + source;
|
|
2202
|
+
const srcCollapsed = collapsedGroups.has(srcKey);
|
|
2203
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2204
|
+
className: panel_module_css_default.section + " " + panel_module_css_default.projectNest,
|
|
2205
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2206
|
+
className: panel_module_css_default.groupHead,
|
|
2207
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2208
|
+
type: "button",
|
|
2209
|
+
className: panel_module_css_default.disclosure,
|
|
2210
|
+
"aria-expanded": !srcCollapsed,
|
|
2211
|
+
onClick: () => {
|
|
2212
|
+
toggleGroupCollapse(srcKey);
|
|
2213
|
+
},
|
|
2214
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: panel_module_css_default.chevron + (srcCollapsed ? " " + panel_module_css_default.chevronCollapsed : "") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2215
|
+
className: panel_module_css_default.groupTitle,
|
|
2216
|
+
children: [
|
|
2217
|
+
tt("badge.source." + source),
|
|
2218
|
+
" · ",
|
|
2219
|
+
list.length
|
|
2220
|
+
]
|
|
2221
|
+
})]
|
|
2222
|
+
})
|
|
2223
|
+
}), !srcCollapsed ? list.map((skill) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillRow, {
|
|
2224
|
+
skill,
|
|
2225
|
+
hub
|
|
2226
|
+
}, skill.name)) : null]
|
|
2227
|
+
}, srcKey);
|
|
2228
|
+
})
|
|
2229
|
+
}) : proj.skills.map((skill) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillRow, {
|
|
2230
|
+
skill,
|
|
2231
|
+
hub
|
|
2232
|
+
}, skill.name)) : null]
|
|
2233
|
+
}, projKey);
|
|
2234
|
+
}) : null]
|
|
2235
|
+
});
|
|
2236
|
+
}
|
|
2190
2237
|
function SourcesView(props) {
|
|
2191
2238
|
const { hub } = props;
|
|
2192
2239
|
const { catalog, groupsState, skillView, sourceFilter, origins, sorted, normalized, collapsedGroups, viewNames, sourceCheck, actionNames, checkingSource, syncingSource, batchBusy, busyNames, toggleGroupCollapse, checkSources, requestSync, requestDelete, toggleGroup, enableDisabled } = hub;
|
|
@@ -2195,6 +2242,7 @@ window.__ModuleLoader__.load({
|
|
|
2195
2242
|
hub
|
|
2196
2243
|
}, skill.name)) });
|
|
2197
2244
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
2245
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProjectTree, { hub }),
|
|
2198
2246
|
groupsState !== null && groupsState.collections.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2199
2247
|
className: panel_module_css_default.empty,
|
|
2200
2248
|
children: tt("groups.noCollections")
|
|
@@ -2295,7 +2343,7 @@ window.__ModuleLoader__.load({
|
|
|
2295
2343
|
}, "col:" + collection.name);
|
|
2296
2344
|
}),
|
|
2297
2345
|
(() => {
|
|
2298
|
-
const uncategorized = filterBySource(sorted, sourceFilter, origins).filter((skill) => origins[skill.name] === void 0);
|
|
2346
|
+
const uncategorized = filterBySource(sorted, sourceFilter, origins).filter((skill) => origins[skill.name] === void 0 && !isProjectSource(skill.source));
|
|
2299
2347
|
if (uncategorized.length === 0) return null;
|
|
2300
2348
|
const collapsed = collapsedGroups.has("uncategorized-source");
|
|
2301
2349
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
@@ -2658,6 +2706,12 @@ window.__ModuleLoader__.load({
|
|
|
2658
2706
|
}
|
|
2659
2707
|
//#endregion
|
|
2660
2708
|
//#region src/client/panel/MarketView.tsx
|
|
2709
|
+
/**
|
|
2710
|
+
* Market tab: one unified market list — built-in catalog entries (add
|
|
2711
|
+
* button while not yet added) and the user's market sources (full state
|
|
2712
|
+
* badges + actions once added, plus custom sources), with check-all and
|
|
2713
|
+
* update-all actions and the repo scan result with the importable checklist.
|
|
2714
|
+
*/
|
|
2661
2715
|
/** Compact byte size for repo preview rows. */
|
|
2662
2716
|
function formatBytes(bytes) {
|
|
2663
2717
|
if (bytes < 1024) return bytes + " B";
|
|
@@ -2669,54 +2723,140 @@ window.__ModuleLoader__.load({
|
|
|
2669
2723
|
const { marketState, marketCheck, sourceCheck, sourcesState, repoDiscoverState, scanningRepo, repoSelected, setRepoSelected, repoImporting, repoResult, newSourceName, setNewSourceName, syncingMarket, tagBusy, addSource, addMarketSource, removeMarketSource, scanRepo, checkMarket, checkSources, syncMarketSource, toggleRepoSelected, importRepo, updateAllDialog, setUpdateAllDialog, updateAll } = hub;
|
|
2670
2724
|
/** 有可更新技能的来源(全部更新按钮与确认列表共用)。 */
|
|
2671
2725
|
const updatableRepos = Object.entries(sourceCheck).filter(([, check]) => check.changed && check.updated.length > 0);
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2726
|
+
/** 一个已添加来源的完整行:状态徽章 + 操作按钮。 */
|
|
2727
|
+
const sourceRow = (record) => {
|
|
2728
|
+
const releaseCheck = marketCheck[record.repo];
|
|
2729
|
+
const skillCheck = sourceCheck[record.repo];
|
|
2730
|
+
const installedCount = sourcesState?.sources.find((source) => source.repo === record.repo)?.skills.length ?? 0;
|
|
2731
|
+
const scanning = scanningRepo === record.repo;
|
|
2732
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2733
|
+
className: panel_module_css_default.row + " " + panel_module_css_default.rowStatic,
|
|
2675
2734
|
children: [
|
|
2676
2735
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2677
|
-
className: panel_module_css_default.
|
|
2678
|
-
children:
|
|
2736
|
+
className: panel_module_css_default.rowMain,
|
|
2737
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2738
|
+
className: panel_module_css_default.rowName,
|
|
2739
|
+
children: [
|
|
2740
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
2741
|
+
className: panel_module_css_default.sourceLink,
|
|
2742
|
+
href: "https://github.com/" + record.repo,
|
|
2743
|
+
target: "_blank",
|
|
2744
|
+
rel: "noreferrer",
|
|
2745
|
+
children: record.repo
|
|
2746
|
+
}),
|
|
2747
|
+
record.ref !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2748
|
+
className: panel_module_css_default.badge + " " + panel_module_css_default.badgeSource,
|
|
2749
|
+
children: record.ref
|
|
2750
|
+
}) : null,
|
|
2751
|
+
installedCount > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2752
|
+
className: panel_module_css_default.badge + " " + panel_module_css_default.badgeCount,
|
|
2753
|
+
children: tt("market.installed", { count: installedCount })
|
|
2754
|
+
}) : null,
|
|
2755
|
+
skillCheck?.changed === true && skillCheck.updated.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2756
|
+
className: panel_module_css_default.badge + " " + panel_module_css_default.statusUpdated,
|
|
2757
|
+
children: tt("market.updatable", { count: skillCheck.updated.length })
|
|
2758
|
+
}) : null,
|
|
2759
|
+
skillCheck !== void 0 && skillCheck.deleted.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2760
|
+
className: panel_module_css_default.badge + " " + panel_module_css_default.statusError,
|
|
2761
|
+
children: tt("market.deletedUpstream", { count: skillCheck.deleted.length })
|
|
2762
|
+
}) : null,
|
|
2763
|
+
releaseCheck?.updateAvailable === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2764
|
+
className: panel_module_css_default.badge + " " + panel_module_css_default.statusUpdated,
|
|
2765
|
+
children: releaseCheck.latestTag !== void 0 ? tt("market.newRelease", { version: releaseCheck.latestTag }) : tt("market.updated")
|
|
2766
|
+
}) : null
|
|
2767
|
+
]
|
|
2768
|
+
})
|
|
2679
2769
|
}),
|
|
2680
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
2681
|
-
|
|
2682
|
-
|
|
2770
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2771
|
+
type: "button",
|
|
2772
|
+
className: panel_module_css_default.opBtn,
|
|
2773
|
+
disabled: scanning,
|
|
2774
|
+
onClick: () => {
|
|
2775
|
+
checkMarket();
|
|
2776
|
+
},
|
|
2777
|
+
children: tt("market.check")
|
|
2683
2778
|
}),
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
onClick: () => {
|
|
2712
|
-
addSource(entry.repo);
|
|
2713
|
-
},
|
|
2714
|
-
children: tt("market.add")
|
|
2715
|
-
})]
|
|
2716
|
-
}, entry.repo);
|
|
2779
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2780
|
+
type: "button",
|
|
2781
|
+
className: panel_module_css_default.opBtn + (releaseCheck?.updateAvailable === true ? " " + panel_module_css_default.opDanger : ""),
|
|
2782
|
+
disabled: syncingMarket === record.repo,
|
|
2783
|
+
onClick: () => {
|
|
2784
|
+
syncMarketSource(record.repo);
|
|
2785
|
+
},
|
|
2786
|
+
children: syncingMarket === record.repo ? tt("market.syncing") : tt("market.sync")
|
|
2787
|
+
}),
|
|
2788
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2789
|
+
type: "button",
|
|
2790
|
+
className: panel_module_css_default.opBtn,
|
|
2791
|
+
disabled: scanning,
|
|
2792
|
+
onClick: () => {
|
|
2793
|
+
scanRepo(record.repo);
|
|
2794
|
+
},
|
|
2795
|
+
children: scanning ? tt("market.scanning") : tt("market.scan")
|
|
2796
|
+
}),
|
|
2797
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2798
|
+
type: "button",
|
|
2799
|
+
className: panel_module_css_default.opBtn,
|
|
2800
|
+
disabled: tagBusy,
|
|
2801
|
+
title: tt("market.removeHint"),
|
|
2802
|
+
onClick: () => {
|
|
2803
|
+
removeMarketSource(record.repo);
|
|
2804
|
+
},
|
|
2805
|
+
children: tt("market.deleteSource")
|
|
2717
2806
|
})
|
|
2718
2807
|
]
|
|
2719
|
-
})
|
|
2808
|
+
});
|
|
2809
|
+
};
|
|
2810
|
+
const rows = [];
|
|
2811
|
+
for (const entry of MARKET_CATALOG) {
|
|
2812
|
+
const record = marketState.repos.find((item) => item.repo === entry.repo);
|
|
2813
|
+
if (record !== void 0) rows.push({
|
|
2814
|
+
key: entry.repo,
|
|
2815
|
+
element: sourceRow(record)
|
|
2816
|
+
});
|
|
2817
|
+
else {
|
|
2818
|
+
const busy = scanningRepo !== null || tagBusy;
|
|
2819
|
+
rows.push({
|
|
2820
|
+
key: entry.repo,
|
|
2821
|
+
element: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2822
|
+
className: panel_module_css_default.row + " " + panel_module_css_default.rowStatic,
|
|
2823
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2824
|
+
className: panel_module_css_default.rowMain,
|
|
2825
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2826
|
+
className: panel_module_css_default.rowName,
|
|
2827
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
2828
|
+
className: panel_module_css_default.sourceLink,
|
|
2829
|
+
href: "https://github.com/" + entry.repo,
|
|
2830
|
+
target: "_blank",
|
|
2831
|
+
rel: "noreferrer",
|
|
2832
|
+
children: entry.repo
|
|
2833
|
+
})
|
|
2834
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2835
|
+
className: panel_module_css_default.rowDesc,
|
|
2836
|
+
children: tt(entry.descriptionKey)
|
|
2837
|
+
})]
|
|
2838
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2839
|
+
type: "button",
|
|
2840
|
+
className: panel_module_css_default.opBtn,
|
|
2841
|
+
disabled: busy,
|
|
2842
|
+
onClick: () => {
|
|
2843
|
+
addSource(entry.repo);
|
|
2844
|
+
},
|
|
2845
|
+
children: tt("market.add")
|
|
2846
|
+
})]
|
|
2847
|
+
})
|
|
2848
|
+
});
|
|
2849
|
+
}
|
|
2850
|
+
}
|
|
2851
|
+
for (const record of marketState.repos) {
|
|
2852
|
+
if (MARKET_CATALOG.some((entry) => entry.repo === record.repo)) continue;
|
|
2853
|
+
rows.push({
|
|
2854
|
+
key: record.repo,
|
|
2855
|
+
element: sourceRow(record)
|
|
2856
|
+
});
|
|
2857
|
+
}
|
|
2858
|
+
const unaddedCatalog = MARKET_CATALOG.filter((entry) => !marketState.repos.some((record) => record.repo === entry.repo));
|
|
2859
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
2720
2860
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2721
2861
|
className: panel_module_css_default.section,
|
|
2722
2862
|
children: [
|
|
@@ -2725,7 +2865,7 @@ window.__ModuleLoader__.load({
|
|
|
2725
2865
|
children: [
|
|
2726
2866
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2727
2867
|
className: panel_module_css_default.sectionTitleFill,
|
|
2728
|
-
children: tt("market.
|
|
2868
|
+
children: tt("market.title")
|
|
2729
2869
|
}),
|
|
2730
2870
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2731
2871
|
type: "button",
|
|
@@ -2747,10 +2887,6 @@ window.__ModuleLoader__.load({
|
|
|
2747
2887
|
})
|
|
2748
2888
|
]
|
|
2749
2889
|
}),
|
|
2750
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2751
|
-
className: panel_module_css_default.hintLine + " " + panel_module_css_default.hintPadded,
|
|
2752
|
-
children: tt("market.addHint")
|
|
2753
|
-
}),
|
|
2754
2890
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2755
2891
|
className: panel_module_css_default.buttons + " " + panel_module_css_default.actionsPadded,
|
|
2756
2892
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
@@ -2776,97 +2912,19 @@ window.__ModuleLoader__.load({
|
|
|
2776
2912
|
children: tt("market.addSource")
|
|
2777
2913
|
})]
|
|
2778
2914
|
}),
|
|
2915
|
+
unaddedCatalog.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2916
|
+
className: panel_module_css_default.hintLine + " " + panel_module_css_default.hintPadded,
|
|
2917
|
+
children: tt("market.catalogHint")
|
|
2918
|
+
}) : null,
|
|
2779
2919
|
marketState.status === "loading" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2780
2920
|
className: panel_module_css_default.empty,
|
|
2781
2921
|
children: tt("panel.loading")
|
|
2782
2922
|
}) : null,
|
|
2783
|
-
marketState.status
|
|
2923
|
+
marketState.status !== "loading" && rows.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2784
2924
|
className: panel_module_css_default.empty,
|
|
2785
2925
|
children: tt("market.noSources")
|
|
2786
2926
|
}) : null,
|
|
2787
|
-
marketState.
|
|
2788
|
-
const releaseCheck = marketCheck[record.repo];
|
|
2789
|
-
const skillCheck = sourceCheck[record.repo];
|
|
2790
|
-
const installedCount = sourcesState?.sources.find((source) => source.repo === record.repo)?.skills.length ?? 0;
|
|
2791
|
-
const scanning = scanningRepo === record.repo;
|
|
2792
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2793
|
-
className: panel_module_css_default.row + " " + panel_module_css_default.rowStatic,
|
|
2794
|
-
children: [
|
|
2795
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2796
|
-
className: panel_module_css_default.rowMain,
|
|
2797
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2798
|
-
className: panel_module_css_default.rowName,
|
|
2799
|
-
children: [
|
|
2800
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
2801
|
-
className: panel_module_css_default.sourceLink,
|
|
2802
|
-
href: "https://github.com/" + record.repo,
|
|
2803
|
-
target: "_blank",
|
|
2804
|
-
rel: "noreferrer",
|
|
2805
|
-
children: record.repo
|
|
2806
|
-
}),
|
|
2807
|
-
record.ref !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2808
|
-
className: panel_module_css_default.badge + " " + panel_module_css_default.badgeSource,
|
|
2809
|
-
children: record.ref
|
|
2810
|
-
}) : null,
|
|
2811
|
-
installedCount > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2812
|
-
className: panel_module_css_default.badge + " " + panel_module_css_default.badgeCount,
|
|
2813
|
-
children: tt("market.installed", { count: installedCount })
|
|
2814
|
-
}) : null,
|
|
2815
|
-
skillCheck?.changed === true && skillCheck.updated.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2816
|
-
className: panel_module_css_default.badge + " " + panel_module_css_default.statusUpdated,
|
|
2817
|
-
children: tt("market.updatable", { count: skillCheck.updated.length })
|
|
2818
|
-
}) : null,
|
|
2819
|
-
skillCheck !== void 0 && skillCheck.deleted.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2820
|
-
className: panel_module_css_default.badge + " " + panel_module_css_default.statusError,
|
|
2821
|
-
children: tt("market.deletedUpstream", { count: skillCheck.deleted.length })
|
|
2822
|
-
}) : null,
|
|
2823
|
-
releaseCheck?.updateAvailable === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2824
|
-
className: panel_module_css_default.badge + " " + panel_module_css_default.statusUpdated,
|
|
2825
|
-
children: releaseCheck.latestTag !== void 0 ? tt("market.newRelease", { version: releaseCheck.latestTag }) : tt("market.updated")
|
|
2826
|
-
}) : null
|
|
2827
|
-
]
|
|
2828
|
-
})
|
|
2829
|
-
}),
|
|
2830
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2831
|
-
type: "button",
|
|
2832
|
-
className: panel_module_css_default.opBtn,
|
|
2833
|
-
disabled: scanning,
|
|
2834
|
-
onClick: () => {
|
|
2835
|
-
checkMarket();
|
|
2836
|
-
},
|
|
2837
|
-
children: tt("market.check")
|
|
2838
|
-
}),
|
|
2839
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2840
|
-
type: "button",
|
|
2841
|
-
className: panel_module_css_default.opBtn + (releaseCheck?.updateAvailable === true ? " " + panel_module_css_default.opDanger : ""),
|
|
2842
|
-
disabled: syncingMarket === record.repo,
|
|
2843
|
-
onClick: () => {
|
|
2844
|
-
syncMarketSource(record.repo);
|
|
2845
|
-
},
|
|
2846
|
-
children: syncingMarket === record.repo ? tt("market.syncing") : tt("market.sync")
|
|
2847
|
-
}),
|
|
2848
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2849
|
-
type: "button",
|
|
2850
|
-
className: panel_module_css_default.opBtn,
|
|
2851
|
-
disabled: scanning,
|
|
2852
|
-
onClick: () => {
|
|
2853
|
-
scanRepo(record.repo);
|
|
2854
|
-
},
|
|
2855
|
-
children: scanning ? tt("market.scanning") : tt("market.scan")
|
|
2856
|
-
}),
|
|
2857
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2858
|
-
type: "button",
|
|
2859
|
-
className: panel_module_css_default.opBtn,
|
|
2860
|
-
disabled: tagBusy,
|
|
2861
|
-
title: tt("market.removeHint"),
|
|
2862
|
-
onClick: () => {
|
|
2863
|
-
removeMarketSource(record.repo);
|
|
2864
|
-
},
|
|
2865
|
-
children: tt("market.deleteSource")
|
|
2866
|
-
})
|
|
2867
|
-
]
|
|
2868
|
-
}, record.repo);
|
|
2869
|
-
})
|
|
2927
|
+
marketState.status !== "loading" ? rows.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Fragment, { children: row.element }, row.key)) : null
|
|
2870
2928
|
]
|
|
2871
2929
|
}),
|
|
2872
2930
|
repoDiscoverState.status === "scanning" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -3039,6 +3097,8 @@ window.__ModuleLoader__.load({
|
|
|
3039
3097
|
const [repoImporting, setRepoImporting] = (0, react.useState)(false);
|
|
3040
3098
|
const [repoResult, setRepoResult] = (0, react.useState)(null);
|
|
3041
3099
|
const [search, setSearch] = (0, react.useState)("");
|
|
3100
|
+
/** 工作区(项目)路径;空 = 只看用户级技能。 */
|
|
3101
|
+
const [workspace, setWorkspace] = (0, react.useState)("");
|
|
3042
3102
|
const [detail, setDetail] = (0, react.useState)(null);
|
|
3043
3103
|
const [detailLoading, setDetailLoading] = (0, react.useState)(false);
|
|
3044
3104
|
const [busyNames, setBusyNames] = (0, react.useState)(/* @__PURE__ */ new Set());
|
|
@@ -3083,8 +3143,10 @@ window.__ModuleLoader__.load({
|
|
|
3083
3143
|
const [newTagName, setNewTagName] = (0, react.useState)("");
|
|
3084
3144
|
const [tagBusy, setTagBusy] = (0, react.useState)(false);
|
|
3085
3145
|
const [editSearch, setEditSearch] = (0, react.useState)("");
|
|
3086
|
-
/** 分组视图里收起的分组(key 为 tag:<id> 或
|
|
3146
|
+
/** 分组视图里收起的分组(key 为 tag:<id>、col:<name> 或 project 树键)。 */
|
|
3087
3147
|
const [collapsedGroups, setCollapsedGroups] = (0, react.useState)(/* @__PURE__ */ new Set());
|
|
3148
|
+
/** 项目级三级树里已细分(按 .dsh/.agents)的项目键。 */
|
|
3149
|
+
const [subdividedProjects, setSubdividedProjects] = (0, react.useState)(/* @__PURE__ */ new Set());
|
|
3088
3150
|
const [showLegend, setShowLegend] = (0, react.useState)(false);
|
|
3089
3151
|
const toggleGroupCollapse = (0, react.useCallback)((key) => {
|
|
3090
3152
|
setCollapsedGroups((previous) => {
|
|
@@ -3094,9 +3156,17 @@ window.__ModuleLoader__.load({
|
|
|
3094
3156
|
return next;
|
|
3095
3157
|
});
|
|
3096
3158
|
}, []);
|
|
3159
|
+
const toggleSubdivide = (0, react.useCallback)((key) => {
|
|
3160
|
+
setSubdividedProjects((previous) => {
|
|
3161
|
+
const next = new Set(previous);
|
|
3162
|
+
if (next.has(key)) next.delete(key);
|
|
3163
|
+
else next.add(key);
|
|
3164
|
+
return next;
|
|
3165
|
+
});
|
|
3166
|
+
}, []);
|
|
3097
3167
|
const load = (0, react.useCallback)(async () => {
|
|
3098
3168
|
try {
|
|
3099
|
-
const next = await api.catalog();
|
|
3169
|
+
const next = await api.catalog(workspace !== "" ? { cwd: workspace } : void 0);
|
|
3100
3170
|
setCatalog(next);
|
|
3101
3171
|
setLoadError(null);
|
|
3102
3172
|
} catch (error) {
|
|
@@ -3104,7 +3174,7 @@ window.__ModuleLoader__.load({
|
|
|
3104
3174
|
} finally {
|
|
3105
3175
|
setLoading(false);
|
|
3106
3176
|
}
|
|
3107
|
-
}, [api]);
|
|
3177
|
+
}, [api, workspace]);
|
|
3108
3178
|
const loadUses = (0, react.useCallback)(async () => {
|
|
3109
3179
|
try {
|
|
3110
3180
|
const result = await api.stats();
|
|
@@ -3208,13 +3278,13 @@ window.__ModuleLoader__.load({
|
|
|
3208
3278
|
setDetailLoading(true);
|
|
3209
3279
|
setLoadError(null);
|
|
3210
3280
|
try {
|
|
3211
|
-
setDetail(await api.skill(name));
|
|
3281
|
+
setDetail(await api.skill(name, workspace !== "" ? { cwd: workspace } : void 0));
|
|
3212
3282
|
} catch (error) {
|
|
3213
3283
|
setLoadError(errorMessage(error));
|
|
3214
3284
|
} finally {
|
|
3215
3285
|
setDetailLoading(false);
|
|
3216
3286
|
}
|
|
3217
|
-
}, [api]);
|
|
3287
|
+
}, [api, workspace]);
|
|
3218
3288
|
const toggle = (0, react.useCallback)(async (skill, enabled) => {
|
|
3219
3289
|
setBusyNames((previous) => new Set(previous).add(skill.name));
|
|
3220
3290
|
setLoadError(null);
|
|
@@ -3802,7 +3872,7 @@ window.__ModuleLoader__.load({
|
|
|
3802
3872
|
const sourceOptions = (0, react.useMemo)(() => {
|
|
3803
3873
|
const skills = catalog?.skills ?? [];
|
|
3804
3874
|
const repos = [...new Set(skills.map((skill) => origins[skill.name]).filter((repo) => repo !== void 0))].sort();
|
|
3805
|
-
const hasPrivate = skills.some((skill) => origins[skill.name] === void 0);
|
|
3875
|
+
const hasPrivate = skills.some((skill) => origins[skill.name] === void 0 && !isProjectSource(skill.source));
|
|
3806
3876
|
return [...repos, ...hasPrivate ? [PRIVATE_SOURCE] : []];
|
|
3807
3877
|
}, [catalog, origins]);
|
|
3808
3878
|
const filtered = (0, react.useMemo)(() => (catalog?.skills ?? []).filter((skill) => normalized.length === 0 || skill.name.toLocaleLowerCase().includes(normalized) || skill.description.toLocaleLowerCase().includes(normalized)), [catalog, normalized]);
|
|
@@ -3818,6 +3888,7 @@ window.__ModuleLoader__.load({
|
|
|
3818
3888
|
repoImporting,
|
|
3819
3889
|
repoResult,
|
|
3820
3890
|
search,
|
|
3891
|
+
workspace,
|
|
3821
3892
|
detail,
|
|
3822
3893
|
detailLoading,
|
|
3823
3894
|
busyNames,
|
|
@@ -3859,6 +3930,7 @@ window.__ModuleLoader__.load({
|
|
|
3859
3930
|
tagBusy,
|
|
3860
3931
|
editSearch,
|
|
3861
3932
|
collapsedGroups,
|
|
3933
|
+
subdividedProjects,
|
|
3862
3934
|
showLegend,
|
|
3863
3935
|
actionNames,
|
|
3864
3936
|
viewNames,
|
|
@@ -3874,6 +3946,7 @@ window.__ModuleLoader__.load({
|
|
|
3874
3946
|
setLoadError,
|
|
3875
3947
|
setSuccessBanner,
|
|
3876
3948
|
setSearch,
|
|
3949
|
+
setWorkspace,
|
|
3877
3950
|
setDetail,
|
|
3878
3951
|
setShowForm,
|
|
3879
3952
|
setFormName,
|
|
@@ -3900,6 +3973,7 @@ window.__ModuleLoader__.load({
|
|
|
3900
3973
|
setEditSearch,
|
|
3901
3974
|
setShowLegend,
|
|
3902
3975
|
toggleGroupCollapse,
|
|
3976
|
+
toggleSubdivide,
|
|
3903
3977
|
checkUpdate,
|
|
3904
3978
|
loadMarket,
|
|
3905
3979
|
openDetail,
|
|
@@ -3935,9 +4009,24 @@ window.__ModuleLoader__.load({
|
|
|
3935
4009
|
}
|
|
3936
4010
|
//#endregion
|
|
3937
4011
|
//#region src/client/panel/SkillHubPanel.tsx
|
|
4012
|
+
/**
|
|
4013
|
+
* The skill hub panel: catalog grouped by tags + source collections, search
|
|
4014
|
+
* and filter in one row, per-group tri-state switches with conflict dialogs,
|
|
4015
|
+
* upstream source tracking (check / sync / follow upstream deletion into a
|
|
4016
|
+
* restorable trash), market sources, disabled re-enable,
|
|
4017
|
+
* detail inspection, and the new-skill scaffold form.
|
|
4018
|
+
*
|
|
4019
|
+
* Thin shell: state and flows live in useSkillHub, the tab contents live in
|
|
4020
|
+
* SourcesView / ScenesView / MarketView, and the dialog family lives in
|
|
4021
|
+
* dialogs.tsx. This component owns only the shared chrome (header, banners,
|
|
4022
|
+
* filter bar, shared sections) and the view routing — and holds no hooks,
|
|
4023
|
+
* so the detail / tag-editor early returns are safe.
|
|
4024
|
+
*/
|
|
3938
4025
|
function SkillHubPanel(props) {
|
|
3939
4026
|
const hub = useSkillHub(props.api);
|
|
3940
|
-
|
|
4027
|
+
/** 工作区输入草稿:回车才应用,避免每次按键都触发目录重拉。 */
|
|
4028
|
+
const [workspaceDraft, setWorkspaceDraft] = (0, react.useState)("");
|
|
4029
|
+
const { catalog, loading, loadError, successBanner, updateState, detail, detailLoading, showForm, formName, formDesc, formRoot, formBusy, formMessage, hubConfig, tab, skillView, sourceFilter, sortKey, search, workspace, setWorkspace, sourcesState, tagBusy, busyNames, normalized, origins, sourceOptions, filtered, conflictDialog, confirmDialog, deleteSkillDialog, confirmClearTrash, branchChoice, branchBusy, marketSyncDialog, syncBusy, editingTag, editName, membersDraft, editSearch, uses, groupsState, sourceCheck, checkingSource, syncingSource, showLegend, setLoadError, setSuccessBanner, setDetail, setShowForm, setFormName, setFormDesc, setFormRoot, setFormMessage, setTab, setSkillView, setSourceFilter, setSortKey, setSearch, setConflictDialog, setConfirmDialog, setDeleteSkillDialog, setConfirmClearTrash, setBranchChoice, setMarketSyncDialog, setEditingTag, setEditName, setMembersDraft, setEditSearch, setShowLegend, checkUpdate, loadMarket, checkSources, requestSync, requestDelete, restoreTrash, clearTrash, runDeleteSkill, runConfirmed, resolveConflict, confirmBranchChoice, confirmMarketSync, create, saveTag, deleteTag, enableDisabled } = hub;
|
|
3941
4030
|
if (detail !== null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillDetailView, {
|
|
3942
4031
|
detail,
|
|
3943
4032
|
hubConfig,
|
|
@@ -4081,6 +4170,30 @@ window.__ModuleLoader__.load({
|
|
|
4081
4170
|
})
|
|
4082
4171
|
]
|
|
4083
4172
|
}),
|
|
4173
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4174
|
+
className: panel_module_css_default.workspaceBox,
|
|
4175
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
4176
|
+
className: panel_module_css_default.search + " " + panel_module_css_default.workspaceInput,
|
|
4177
|
+
value: workspaceDraft,
|
|
4178
|
+
placeholder: workspace !== "" ? workspace : tt("panel.workspacePlaceholder"),
|
|
4179
|
+
title: tt("panel.workspaceHint"),
|
|
4180
|
+
onChange: (event) => {
|
|
4181
|
+
setWorkspaceDraft(event.target.value);
|
|
4182
|
+
},
|
|
4183
|
+
onKeyDown: (event) => {
|
|
4184
|
+
if (event.key === "Enter") setWorkspace(event.target.value.trim());
|
|
4185
|
+
}
|
|
4186
|
+
}), workspace !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4187
|
+
type: "button",
|
|
4188
|
+
className: panel_module_css_default.opBtn,
|
|
4189
|
+
title: tt("panel.workspaceClear"),
|
|
4190
|
+
onClick: () => {
|
|
4191
|
+
setWorkspace("");
|
|
4192
|
+
setWorkspaceDraft("");
|
|
4193
|
+
},
|
|
4194
|
+
children: "✕"
|
|
4195
|
+
}) : null]
|
|
4196
|
+
}),
|
|
4084
4197
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4085
4198
|
type: "button",
|
|
4086
4199
|
className: panel_module_css_default.legendToggle + (showLegend ? " " + panel_module_css_default.legendToggleActive : ""),
|
|
@@ -4473,8 +4586,20 @@ window.__ModuleLoader__.load({
|
|
|
4473
4586
|
//#region src/client/index.tsx
|
|
4474
4587
|
/** Locale namespace this plugin owns. */
|
|
4475
4588
|
const NS = "dsh-skill-hub";
|
|
4476
|
-
/**
|
|
4477
|
-
|
|
4589
|
+
/**
|
|
4590
|
+
* Required services (fiber inject waiting — the runtime must be up first).
|
|
4591
|
+
* `connection`/`remote` are the settings transport's own prerequisites
|
|
4592
|
+
* (`ctx.settingsScope.bind` resolves them on the caller's fiber), and
|
|
4593
|
+
* `settingsScope` is the namespace-scope binder itself; mirror the official
|
|
4594
|
+
* settings-plugins inject list.
|
|
4595
|
+
*/
|
|
4596
|
+
const inject = [
|
|
4597
|
+
"slots",
|
|
4598
|
+
"locale",
|
|
4599
|
+
"connection",
|
|
4600
|
+
"remote",
|
|
4601
|
+
"settingsScope"
|
|
4602
|
+
];
|
|
4478
4603
|
/**
|
|
4479
4604
|
* Mount the settings card and the skill hub section.
|
|
4480
4605
|
* @param ctx - client root context (slots, locale).
|
|
@@ -4486,11 +4611,10 @@ window.__ModuleLoader__.load({
|
|
|
4486
4611
|
}), "dsh-skill-hub: dictionaries");
|
|
4487
4612
|
const t = ctx.locale.bind(NS);
|
|
4488
4613
|
const api = new SkillHubApi();
|
|
4489
|
-
const settingsCard = new SkillHubSettingsCardController(
|
|
4614
|
+
const settingsCard = new SkillHubSettingsCardController(ctx.settingsScope.bind({ namespace: NS }));
|
|
4490
4615
|
ctx.effect(() => ctx.slots.inject("settings.plugin.item", () => ctx.slots.register({
|
|
4491
4616
|
name: "settings.plugin.item",
|
|
4492
|
-
|
|
4493
|
-
order: 115,
|
|
4617
|
+
key: NS,
|
|
4494
4618
|
locale: NS,
|
|
4495
4619
|
inject: () => settingsCard.inject()
|
|
4496
4620
|
}, SkillHubSettingsCard)), "dsh-skill-hub: settings card");
|