@nanmicoder/dsh-skills-hub 0.0.1 → 0.0.2
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 +18 -0
- package/README.md +31 -82
- package/README_EN.md +31 -82
- package/README_ZH.md +2 -0
- package/compatibility.json +1 -1
- package/docs/COMPATIBILITY.md +34 -0
- package/docs/CONFIGURATION.md +38 -0
- package/docs/CONTRACT.md +25 -7
- package/docs/RELEASE.md +5 -5
- package/lib/client.js +1916 -1025
- package/lib/client.js.map +1 -1
- package/lib/config.js +2 -0
- package/lib/index.js +16 -6
- package/lib/market/cache.js +104 -30
- package/lib/market/disk-cache.js +113 -0
- package/lib/market/install-service.js +8 -6
- package/lib/market/market-service.js +79 -29
- package/lib/market/provider-fetch.js +9 -2
- package/lib/market/types.js +2 -1
- package/lib/skills/management.js +182 -4
- package/lib/skills/root.js +1 -1
- package/lib/types/client/api.d.ts +33 -5
- package/lib/types/client/components/InstallConfirmDialog.d.ts +6 -0
- package/lib/types/client/components/InstalledSkillDetail.d.ts +24 -0
- package/lib/types/client/components/InstalledSkills.d.ts +18 -1
- package/lib/types/client/components/MarkdownView.d.ts +7 -1
- package/lib/types/client/components/SkillAvatar.d.ts +1 -1
- package/lib/types/client/components/SkillDetailShell.d.ts +63 -0
- package/lib/types/client/components/SkillDetailView.d.ts +4 -6
- package/lib/types/client/components/SkillFiles.d.ts +32 -0
- package/lib/types/client/components/SourceStatusBar.d.ts +4 -0
- package/lib/types/client/icons.d.ts +4 -0
- package/lib/types/client/locales.d.ts +30 -0
- package/lib/types/client/relative-time.d.ts +13 -0
- package/lib/types/client/skill-markdown.d.ts +39 -0
- package/lib/types/client/state.d.ts +13 -4
- package/lib/types/config.d.ts +2 -0
- package/lib/types/market/cache.d.ts +62 -14
- package/lib/types/market/disk-cache.d.ts +16 -0
- package/lib/types/market/market-service.d.ts +15 -3
- package/lib/types/market/provider-fetch.d.ts +2 -0
- package/lib/types/skills/management.d.ts +63 -2
- package/lib/types/skills/root.d.ts +8 -0
- package/lib/types/web-routes.d.ts +2 -0
- package/lib/web-routes.js +42 -4
- package/package.json +1 -1
- package/release-notes/v0.0.1.md +1 -1
- package/release-notes/v0.0.2.md +12 -0
package/lib/client.js
CHANGED
|
@@ -85,6 +85,30 @@ window.__ModuleLoader__.load({
|
|
|
85
85
|
return t;
|
|
86
86
|
}
|
|
87
87
|
//#endregion
|
|
88
|
+
//#region lib/client/relative-time.js
|
|
89
|
+
/**
|
|
90
|
+
* How old a snapshot is, in the panel's own vocabulary.
|
|
91
|
+
*
|
|
92
|
+
* The panel shows cached catalogue data, so "when was this actually fetched" is
|
|
93
|
+
* part of the answer rather than a debug detail: a reader who is about to
|
|
94
|
+
* install third-party code is entitled to know whether the security report in
|
|
95
|
+
* front of them was read a minute ago or yesterday.
|
|
96
|
+
*/
|
|
97
|
+
/** Coarse buckets: the first match wins, and precision beyond them is noise. */
|
|
98
|
+
function formatAge(t, from, now = Date.now()) {
|
|
99
|
+
const minutes = Math.floor(Math.max(0, now - from) / 6e4);
|
|
100
|
+
if (minutes < 1) return t("ageJustNow");
|
|
101
|
+
if (minutes < 60) return t("ageMinutes", { count: minutes });
|
|
102
|
+
const hours = Math.floor(minutes / 60);
|
|
103
|
+
if (hours < 24) return t("ageHours", { count: hours });
|
|
104
|
+
return t("ageDays", { count: Math.floor(hours / 24) });
|
|
105
|
+
}
|
|
106
|
+
/** Absolute timestamp for the hover text, so the bucket never hides the fact. */
|
|
107
|
+
function formatStamp(from) {
|
|
108
|
+
const date = new Date(from);
|
|
109
|
+
return Number.isNaN(date.getTime()) ? "" : date.toLocaleString();
|
|
110
|
+
}
|
|
111
|
+
//#endregion
|
|
88
112
|
//#region lib/client/icons.js
|
|
89
113
|
/** Attributes every icon shares; spread first so a caller's props win. */
|
|
90
114
|
const stroke = {
|
|
@@ -347,15 +371,47 @@ window.__ModuleLoader__.load({
|
|
|
347
371
|
})]
|
|
348
372
|
});
|
|
349
373
|
}
|
|
374
|
+
/** Folder: leading glyph of the installed skill's location line. */
|
|
375
|
+
function FolderIcon({ size = 16, className }) {
|
|
376
|
+
return (0, react_jsx_runtime.jsx)("svg", {
|
|
377
|
+
width: size,
|
|
378
|
+
height: size,
|
|
379
|
+
viewBox: "0 0 16 16",
|
|
380
|
+
className,
|
|
381
|
+
strokeWidth: 1.5,
|
|
382
|
+
...stroke,
|
|
383
|
+
"aria-hidden": "true",
|
|
384
|
+
focusable: "false",
|
|
385
|
+
children: (0, react_jsx_runtime.jsx)("path", { d: "M1.9 4.6a1.3 1.3 0 0 1 1.3-1.3h2.5l1.4 1.6h5.7a1.3 1.3 0 0 1 1.3 1.3v5.6a1.3 1.3 0 0 1-1.3 1.3H3.2a1.3 1.3 0 0 1-1.3-1.3z" })
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
/** Arrow leaving a frame: open the same skill in the marketplace. */
|
|
389
|
+
function ExternalLinkIcon({ size = 16, className }) {
|
|
390
|
+
return (0, react_jsx_runtime.jsxs)("svg", {
|
|
391
|
+
width: size,
|
|
392
|
+
height: size,
|
|
393
|
+
viewBox: "0 0 16 16",
|
|
394
|
+
className,
|
|
395
|
+
strokeWidth: 1.5,
|
|
396
|
+
...stroke,
|
|
397
|
+
"aria-hidden": "true",
|
|
398
|
+
focusable: "false",
|
|
399
|
+
children: [
|
|
400
|
+
(0, react_jsx_runtime.jsx)("path", { d: "M9.4 2.6h4v4" }),
|
|
401
|
+
(0, react_jsx_runtime.jsx)("path", { d: "M13.4 2.6 7.9 8.1" }),
|
|
402
|
+
(0, react_jsx_runtime.jsx)("path", { d: "M12.2 9.6v3.1a1.3 1.3 0 0 1-1.3 1.3H3.6a1.3 1.3 0 0 1-1.3-1.3V5.4a1.3 1.3 0 0 1 1.3-1.3h3.1" })
|
|
403
|
+
]
|
|
404
|
+
});
|
|
405
|
+
}
|
|
350
406
|
//#endregion
|
|
351
407
|
//#region \0dsh-css:src/client/components/SecurityBadge.module.css.mjs
|
|
352
|
-
const css$
|
|
353
|
-
const tagId$
|
|
354
|
-
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$
|
|
408
|
+
const css$14 = ".DlDA7a_badge{box-sizing:border-box;white-space:nowrap;border-radius:999px;flex:none;align-items:center;gap:4px;height:20px;padding:0 7px;font-size:11px;font-weight:600;line-height:18px;display:inline-flex}.DlDA7a_compact{height:18px;padding:0 6px;font-size:10.5px}.DlDA7a_success{color:var(--dsw-alias-state-success-primary,#22c55e);background:color-mix(in srgb, var(--dsw-alias-state-success-primary,#22c55e) 12%, transparent)}.DlDA7a_neutral{color:var(--dsw-alias-label-secondary,#61666b);background:var(--dsw-alias-bg-module-platform,#f5f6f7)}.DlDA7a_danger{color:var(--dsw-alias-state-error-primary,#ec1313);background:color-mix(in srgb, var(--dsw-alias-state-error-primary,#ec1313) 12%, transparent)}";
|
|
409
|
+
const tagId$14 = "@nanmicoder/dsh-skills-hub/SecurityBadge.module.css";
|
|
410
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$14) + "]") === null) {
|
|
355
411
|
const tag = document.createElement("style");
|
|
356
412
|
tag.dataset.plugin = "@nanmicoder/dsh-skills-hub";
|
|
357
|
-
tag.dataset.pluginCss = tagId$
|
|
358
|
-
tag.textContent = css$
|
|
413
|
+
tag.dataset.pluginCss = tagId$14;
|
|
414
|
+
tag.textContent = css$14;
|
|
359
415
|
document.head.appendChild(tag);
|
|
360
416
|
}
|
|
361
417
|
var SecurityBadge_module_css_default = {
|
|
@@ -398,13 +454,13 @@ window.__ModuleLoader__.load({
|
|
|
398
454
|
}
|
|
399
455
|
//#endregion
|
|
400
456
|
//#region \0dsh-css:src/client/components/InstallConfirmDialog.module.css.mjs
|
|
401
|
-
const css$
|
|
402
|
-
const tagId$
|
|
403
|
-
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$
|
|
457
|
+
const css$13 = ".UTdxCa_facts{margin:0}.UTdxCa_row{border-bottom:1px solid var(--dsw-alias-border-l1,#0000000a);justify-content:space-between;align-items:baseline;gap:16px;min-width:0;padding:8px 0;display:flex}.UTdxCa_row:last-child{border-bottom:none}.UTdxCa_label{color:var(--dsw-alias-label-tertiary,#81858c);flex:none;font-size:12.5px;line-height:19px}.UTdxCa_value{min-width:0;color:var(--dsw-alias-label-primary,#0f1115);text-align:right;overflow-wrap:anywhere;margin:0;font-size:13px;font-weight:600;line-height:19px}.UTdxCa_path{color:var(--dsw-alias-label-secondary,#61666b);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11.5px;font-weight:500}.UTdxCa_risk{border-radius:var(--dsw-radius-sm,8px);background:color-mix(in srgb, var(--dsw-alias-state-warn-primary,#f59e0b) 10%, transparent);color:var(--dsw-alias-label-secondary,#61666b);align-items:flex-start;gap:8px;margin:12px 0 0;padding:9px 11px;font-size:12px;line-height:19px;display:flex}.UTdxCa_riskIcon{color:var(--dsw-alias-state-warn-primary,#f59e0b);flex:none;margin-top:1px}.UTdxCa_actions{flex-wrap:wrap;justify-content:flex-end;align-items:center;gap:8px;width:100%;display:flex}";
|
|
458
|
+
const tagId$13 = "@nanmicoder/dsh-skills-hub/InstallConfirmDialog.module.css";
|
|
459
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$13) + "]") === null) {
|
|
404
460
|
const tag = document.createElement("style");
|
|
405
461
|
tag.dataset.plugin = "@nanmicoder/dsh-skills-hub";
|
|
406
|
-
tag.dataset.pluginCss = tagId$
|
|
407
|
-
tag.textContent = css$
|
|
462
|
+
tag.dataset.pluginCss = tagId$13;
|
|
463
|
+
tag.textContent = css$13;
|
|
408
464
|
document.head.appendChild(tag);
|
|
409
465
|
}
|
|
410
466
|
var InstallConfirmDialog_module_css_default = {
|
|
@@ -472,72 +528,84 @@ window.__ModuleLoader__.load({
|
|
|
472
528
|
children: busy ? t("installing") : t("confirm")
|
|
473
529
|
})]
|
|
474
530
|
}),
|
|
475
|
-
children: [
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
531
|
+
children: [
|
|
532
|
+
(0, react_jsx_runtime.jsxs)("dl", {
|
|
533
|
+
className: InstallConfirmDialog_module_css_default.facts,
|
|
534
|
+
children: [
|
|
535
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
536
|
+
className: InstallConfirmDialog_module_css_default.row,
|
|
537
|
+
children: [(0, react_jsx_runtime.jsx)("dt", {
|
|
538
|
+
className: InstallConfirmDialog_module_css_default.label,
|
|
539
|
+
children: t("filter.source")
|
|
540
|
+
}), (0, react_jsx_runtime.jsx)("dd", {
|
|
541
|
+
className: InstallConfirmDialog_module_css_default.value,
|
|
542
|
+
children: sourceLabel
|
|
543
|
+
})]
|
|
544
|
+
}),
|
|
545
|
+
skill.version !== void 0 && skill.version !== "" && (0, react_jsx_runtime.jsxs)("div", {
|
|
546
|
+
className: InstallConfirmDialog_module_css_default.row,
|
|
547
|
+
children: [(0, react_jsx_runtime.jsx)("dt", {
|
|
548
|
+
className: InstallConfirmDialog_module_css_default.label,
|
|
549
|
+
children: t("version")
|
|
550
|
+
}), (0, react_jsx_runtime.jsxs)("dd", {
|
|
551
|
+
className: InstallConfirmDialog_module_css_default.value,
|
|
552
|
+
children: ["v", skill.version]
|
|
553
|
+
})]
|
|
554
|
+
}),
|
|
555
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
556
|
+
className: InstallConfirmDialog_module_css_default.row,
|
|
557
|
+
children: [(0, react_jsx_runtime.jsx)("dt", {
|
|
558
|
+
className: InstallConfirmDialog_module_css_default.label,
|
|
559
|
+
children: t("author")
|
|
560
|
+
}), (0, react_jsx_runtime.jsx)("dd", {
|
|
561
|
+
className: InstallConfirmDialog_module_css_default.value,
|
|
562
|
+
children: skill.authorName
|
|
563
|
+
})]
|
|
564
|
+
}),
|
|
565
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
566
|
+
className: InstallConfirmDialog_module_css_default.row,
|
|
567
|
+
children: [(0, react_jsx_runtime.jsx)("dt", {
|
|
568
|
+
className: InstallConfirmDialog_module_css_default.label,
|
|
569
|
+
children: t("security")
|
|
570
|
+
}), (0, react_jsx_runtime.jsx)("dd", {
|
|
571
|
+
className: InstallConfirmDialog_module_css_default.value,
|
|
572
|
+
children: (0, react_jsx_runtime.jsx)(SecurityBadge, { status: skill.securityStatus })
|
|
573
|
+
})]
|
|
574
|
+
}),
|
|
575
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
576
|
+
className: InstallConfirmDialog_module_css_default.row,
|
|
577
|
+
children: [(0, react_jsx_runtime.jsx)("dt", {
|
|
578
|
+
className: InstallConfirmDialog_module_css_default.label,
|
|
579
|
+
children: t("installLocation")
|
|
580
|
+
}), (0, react_jsx_runtime.jsxs)("dd", {
|
|
581
|
+
className: `${InstallConfirmDialog_module_css_default.value} ${InstallConfirmDialog_module_css_default.path}`,
|
|
582
|
+
children: [
|
|
583
|
+
"…/skills/",
|
|
584
|
+
slugOf(skill.id).toLowerCase(),
|
|
585
|
+
"/"
|
|
586
|
+
]
|
|
587
|
+
})]
|
|
588
|
+
})
|
|
589
|
+
]
|
|
590
|
+
}),
|
|
591
|
+
skill.snapshotAt !== void 0 && (0, react_jsx_runtime.jsxs)("p", {
|
|
592
|
+
className: InstallConfirmDialog_module_css_default.risk,
|
|
593
|
+
role: "note",
|
|
594
|
+
title: formatStamp(skill.snapshotAt),
|
|
595
|
+
children: [(0, react_jsx_runtime.jsx)(AlertIcon, {
|
|
596
|
+
size: 16,
|
|
597
|
+
className: InstallConfirmDialog_module_css_default.riskIcon
|
|
598
|
+
}), (0, react_jsx_runtime.jsx)("span", { children: t("installSnapshot", { age: formatAge(t, skill.snapshotAt) }) })]
|
|
599
|
+
}),
|
|
600
|
+
risky && (0, react_jsx_runtime.jsxs)("p", {
|
|
601
|
+
className: InstallConfirmDialog_module_css_default.risk,
|
|
602
|
+
role: "note",
|
|
603
|
+
children: [(0, react_jsx_runtime.jsx)(AlertIcon, {
|
|
604
|
+
size: 16,
|
|
605
|
+
className: InstallConfirmDialog_module_css_default.riskIcon
|
|
606
|
+
}), (0, react_jsx_runtime.jsx)("span", { children: t("unoaudited") })]
|
|
607
|
+
})
|
|
608
|
+
]
|
|
541
609
|
});
|
|
542
610
|
}
|
|
543
611
|
//#endregion
|
|
@@ -653,7 +721,7 @@ window.__ModuleLoader__.load({
|
|
|
653
721
|
throw new SkillsHubApiError("BAD_REQUEST", 400, `"${id}" is not a market skill id (expected "<source>:<slug>")`);
|
|
654
722
|
}
|
|
655
723
|
/** Search the catalogue (market sources) — the remote list page. */
|
|
656
|
-
async function fetchMarketList(query, signal) {
|
|
724
|
+
async function fetchMarketList(query, signal, options = {}) {
|
|
657
725
|
const search = new URLSearchParams();
|
|
658
726
|
const q = query.q?.trim();
|
|
659
727
|
if (q) search.set("q", q);
|
|
@@ -662,13 +730,15 @@ window.__ModuleLoader__.load({
|
|
|
662
730
|
if (query.installed !== "all") search.set("installed", query.installed);
|
|
663
731
|
if (query.cursor) search.set("cursor", query.cursor);
|
|
664
732
|
if (typeof query.limit === "number" && Number.isFinite(query.limit)) search.set("limit", String(query.limit));
|
|
733
|
+
if (options.refresh === true) search.set("refresh", "1");
|
|
665
734
|
const suffix = search.toString();
|
|
666
735
|
return getJson(`/skills${suffix === "" ? "" : `?${suffix}`}`, signal);
|
|
667
736
|
}
|
|
668
737
|
/** One skill's full detail plus the health of the source that served it. */
|
|
669
|
-
async function fetchSkillDetail(id, signal) {
|
|
738
|
+
async function fetchSkillDetail(id, signal, options = {}) {
|
|
670
739
|
const { source, slug } = parseMarketId(id);
|
|
671
|
-
|
|
740
|
+
const suffix = options.refresh === true ? "?refresh=1" : "";
|
|
741
|
+
return getJson(`/skills/${source}/${encodeURIComponent(slug)}${suffix}`, signal);
|
|
672
742
|
}
|
|
673
743
|
/** One file of a skill, for the Files tab. */
|
|
674
744
|
async function fetchSkillFile(id, path, signal) {
|
|
@@ -703,130 +773,479 @@ window.__ModuleLoader__.load({
|
|
|
703
773
|
function fetchInstalledDetail(key, signal) {
|
|
704
774
|
return getJson(`/installed/detail?key=${encodeURIComponent(key)}`, signal);
|
|
705
775
|
}
|
|
776
|
+
/** One file of an installed skill; the Host validates the key and contains the path. */
|
|
777
|
+
async function fetchInstalledFile(key, path, signal) {
|
|
778
|
+
return (await getJson(`/installed/file?key=${encodeURIComponent(key)}&path=${encodeURIComponent(path)}`, signal)).file;
|
|
779
|
+
}
|
|
706
780
|
/** Remove a specifically selected local entry after the management UI confirmation. */
|
|
707
781
|
function removeInstalled(key) {
|
|
708
782
|
return postJson("/installed/uninstall", { key });
|
|
709
783
|
}
|
|
710
784
|
//#endregion
|
|
711
785
|
//#region lib/client/skill-markdown.js
|
|
712
|
-
/**
|
|
713
|
-
|
|
714
|
-
|
|
786
|
+
/**
|
|
787
|
+
* Frontmatter handling for skill documents.
|
|
788
|
+
*
|
|
789
|
+
* A `SKILL.md` opens with a YAML block, and feeding that block to a markdown
|
|
790
|
+
* renderer is not merely ugly — it is wrong: a lone `---` is a setext underline,
|
|
791
|
+
* so the closing fence promotes the whole metadata block into one giant heading.
|
|
792
|
+
* The block is split off here instead, so the UI can render it as structured
|
|
793
|
+
* metadata (`FrontmatterPanel`) while the rest of the document goes to the
|
|
794
|
+
* renderer. The approach mirrors the desktop project's `skillFrontmatter.ts`.
|
|
795
|
+
*
|
|
796
|
+
* The parser is a deliberately small YAML subset — scalars, inline and block
|
|
797
|
+
* sequences, `|`/`>` block scalars, and one level of nested mapping that is kept
|
|
798
|
+
* as the raw YAML it is — because skill frontmatter is flat by convention and a
|
|
799
|
+
* full YAML engine is a dependency this browser bundle does not need. It never
|
|
800
|
+
* throws: a document without a fence, or with an unterminated one, comes back as
|
|
801
|
+
* body only.
|
|
802
|
+
*/
|
|
803
|
+
/** A fence closing the block; it must sit at column zero. */
|
|
804
|
+
const CLOSING_FENCE = /^(?:---|\.\.\.)[ \t]*$/;
|
|
805
|
+
/** Top-level `key:` or `key: value`. Indented lines belong to the value above. */
|
|
806
|
+
const KEY_LINE = /^([A-Za-z0-9_$][\w.$-]*)[ \t]*:(?:[ \t]+(.*))?$/;
|
|
807
|
+
/** `|`, `>`, optionally with chomping and indentation indicators. */
|
|
808
|
+
const BLOCK_SCALAR = /^([|>])([-+]?)(\d*)$/;
|
|
809
|
+
function isBlank(line) {
|
|
810
|
+
return line.trim() === "";
|
|
811
|
+
}
|
|
812
|
+
/** Visual indentation width; a tab counts as two columns, as YAML's own rules do. */
|
|
813
|
+
function indentWidth(line) {
|
|
814
|
+
let width = 0;
|
|
815
|
+
for (const char of line) if (char === " ") width += 1;
|
|
816
|
+
else if (char === " ") width += 2;
|
|
817
|
+
else break;
|
|
818
|
+
return width;
|
|
819
|
+
}
|
|
820
|
+
/** One pair of surrounding quotes, with the escapes YAML defines for it. */
|
|
821
|
+
function stripQuotes(raw) {
|
|
822
|
+
if (raw.length >= 2) {
|
|
823
|
+
const first = raw[0];
|
|
824
|
+
const last = raw[raw.length - 1];
|
|
825
|
+
if (first === "\"" && last === "\"") return {
|
|
826
|
+
value: raw.slice(1, -1).replace(/\\"/g, "\"").replace(/\\\\/g, "\\"),
|
|
827
|
+
quoted: true
|
|
828
|
+
};
|
|
829
|
+
if (first === "'" && last === "'") return {
|
|
830
|
+
value: raw.slice(1, -1).replace(/''/g, "'"),
|
|
831
|
+
quoted: true
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
return {
|
|
835
|
+
value: raw,
|
|
836
|
+
quoted: false
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
/** Drop a trailing ` # comment`, but only when it sits outside quotes. */
|
|
840
|
+
function stripComment(raw) {
|
|
841
|
+
let quote = null;
|
|
842
|
+
for (let index = 0; index < raw.length; index++) {
|
|
843
|
+
const char = raw[index];
|
|
844
|
+
if (quote !== null) {
|
|
845
|
+
if (char === quote) quote = null;
|
|
846
|
+
continue;
|
|
847
|
+
}
|
|
848
|
+
if (char === "\"" || char === "'") {
|
|
849
|
+
quote = char;
|
|
850
|
+
continue;
|
|
851
|
+
}
|
|
852
|
+
if (char === "#" && (index === 0 || raw[index - 1] === " " || raw[index - 1] === " ")) return raw.slice(0, index);
|
|
853
|
+
}
|
|
854
|
+
return raw;
|
|
855
|
+
}
|
|
856
|
+
/** Scalars keep their YAML meaning where it is unambiguous, and stay text otherwise. */
|
|
857
|
+
function coerceScalar(raw) {
|
|
858
|
+
const trimmed = raw.trim();
|
|
859
|
+
if (trimmed === "") return "";
|
|
860
|
+
const { value, quoted } = stripQuotes(trimmed);
|
|
861
|
+
if (quoted) return value;
|
|
862
|
+
const lower = value.toLowerCase();
|
|
863
|
+
if (lower === "true") return true;
|
|
864
|
+
if (lower === "false") return false;
|
|
865
|
+
if (lower === "null" || lower === "~") return null;
|
|
866
|
+
if (/^-?(?:0|[1-9]\d*)(?:\.\d+)?$/.test(value)) return Number(value);
|
|
867
|
+
return value;
|
|
868
|
+
}
|
|
869
|
+
/** Split `[a, "b, c", d]` on top-level commas only. */
|
|
870
|
+
function splitInlineSequence(inner) {
|
|
871
|
+
const parts = [];
|
|
872
|
+
let current = "";
|
|
873
|
+
let quote = null;
|
|
874
|
+
let depth = 0;
|
|
875
|
+
for (const char of inner) {
|
|
876
|
+
if (quote !== null) {
|
|
877
|
+
current += char;
|
|
878
|
+
if (char === quote) quote = null;
|
|
879
|
+
continue;
|
|
880
|
+
}
|
|
881
|
+
if (char === "\"" || char === "'") {
|
|
882
|
+
quote = char;
|
|
883
|
+
current += char;
|
|
884
|
+
continue;
|
|
885
|
+
}
|
|
886
|
+
if (char === "[" || char === "{") depth += 1;
|
|
887
|
+
if (char === "]" || char === "}") depth -= 1;
|
|
888
|
+
if (char === "," && depth === 0) {
|
|
889
|
+
parts.push(current);
|
|
890
|
+
current = "";
|
|
891
|
+
continue;
|
|
892
|
+
}
|
|
893
|
+
current += char;
|
|
894
|
+
}
|
|
895
|
+
parts.push(current);
|
|
896
|
+
return parts.map((part) => part.trim()).filter((part) => part !== "");
|
|
715
897
|
}
|
|
716
|
-
//#endregion
|
|
717
|
-
//#region node_modules/comma-separated-tokens/index.js
|
|
718
898
|
/**
|
|
719
|
-
*
|
|
899
|
+
* Remove a block's common indentation and its blank edges.
|
|
720
900
|
*
|
|
721
|
-
* @param
|
|
722
|
-
*
|
|
723
|
-
* @param {Options} [options]
|
|
724
|
-
* Configuration for `stringify` (optional).
|
|
725
|
-
* @returns {string}
|
|
726
|
-
* Comma-separated tokens.
|
|
901
|
+
* @param lines - the block's lines, verbatim.
|
|
902
|
+
* @param folded - `>` joins the lines into one paragraph; `|` keeps them.
|
|
727
903
|
*/
|
|
728
|
-
function
|
|
729
|
-
const
|
|
730
|
-
|
|
904
|
+
function dedent(lines, folded) {
|
|
905
|
+
const margin = lines.reduce((min, line) => isBlank(line) ? min : Math.min(min, indentWidth(line)), Number.MAX_SAFE_INTEGER);
|
|
906
|
+
const dedented = lines.map((line) => isBlank(line) ? "" : line.slice(margin === Number.MAX_SAFE_INTEGER ? 0 : margin));
|
|
907
|
+
while (dedented.length > 0 && isBlank(dedented[0] ?? "")) dedented.shift();
|
|
908
|
+
while (dedented.length > 0 && isBlank(dedented[dedented.length - 1] ?? "")) dedented.pop();
|
|
909
|
+
return folded ? dedented.join(" ").replace(/\s+/g, " ").trim() : dedented.join("\n");
|
|
910
|
+
}
|
|
911
|
+
/** Parse the top-level keys of one YAML block. Shapes this subset does not model stay verbatim. */
|
|
912
|
+
function parseBlock(yaml) {
|
|
913
|
+
const lines = yaml.split("\n");
|
|
914
|
+
const result = {};
|
|
915
|
+
let index = 0;
|
|
916
|
+
while (index < lines.length) {
|
|
917
|
+
const line = lines[index] ?? "";
|
|
918
|
+
index += 1;
|
|
919
|
+
if (isBlank(line) || line.trimStart().startsWith("#") || indentWidth(line) > 0) continue;
|
|
920
|
+
const match = KEY_LINE.exec(line);
|
|
921
|
+
if (match === null) continue;
|
|
922
|
+
const key = match[1] ?? "";
|
|
923
|
+
const inline = match[2] ?? "";
|
|
924
|
+
const blockScalar = BLOCK_SCALAR.exec(inline.trim());
|
|
925
|
+
if (blockScalar !== null) {
|
|
926
|
+
const collected = [];
|
|
927
|
+
while (index < lines.length) {
|
|
928
|
+
const next = lines[index] ?? "";
|
|
929
|
+
if (!isBlank(next) && indentWidth(next) === 0) break;
|
|
930
|
+
collected.push(next);
|
|
931
|
+
index += 1;
|
|
932
|
+
}
|
|
933
|
+
result[key] = dedent(collected, blockScalar[1] === ">");
|
|
934
|
+
continue;
|
|
935
|
+
}
|
|
936
|
+
const value = stripComment(inline).trim();
|
|
937
|
+
if (value !== "") {
|
|
938
|
+
result[key] = value.startsWith("[") && value.endsWith("]") ? splitInlineSequence(value.slice(1, -1)).map(coerceScalar) : coerceScalar(value);
|
|
939
|
+
continue;
|
|
940
|
+
}
|
|
941
|
+
const children = [];
|
|
942
|
+
while (index < lines.length) {
|
|
943
|
+
const next = lines[index] ?? "";
|
|
944
|
+
if (!isBlank(next) && indentWidth(next) === 0) break;
|
|
945
|
+
children.push(next);
|
|
946
|
+
index += 1;
|
|
947
|
+
}
|
|
948
|
+
const meaningful = children.filter((entry) => !isBlank(entry) && !entry.trimStart().startsWith("#"));
|
|
949
|
+
if (meaningful.length === 0) {
|
|
950
|
+
result[key] = null;
|
|
951
|
+
continue;
|
|
952
|
+
}
|
|
953
|
+
if (meaningful.every((entry) => entry.trimStart().startsWith("- "))) {
|
|
954
|
+
result[key] = meaningful.map((entry) => coerceScalar(stripComment(entry.trimStart().slice(2))));
|
|
955
|
+
continue;
|
|
956
|
+
}
|
|
957
|
+
result[key] = dedent(children, false);
|
|
958
|
+
}
|
|
959
|
+
return result;
|
|
731
960
|
}
|
|
732
|
-
//#endregion
|
|
733
|
-
//#region node_modules/estree-util-is-identifier-name/lib/index.js
|
|
734
|
-
const nameRe = /^[$_\p{ID_Start}][$_\u{200C}\u{200D}\p{ID_Continue}]*$/u;
|
|
735
|
-
const nameReJsx = /^[$_\p{ID_Start}][-$_\u{200C}\u{200D}\p{ID_Continue}]*$/u;
|
|
736
|
-
/** @type {Options} */
|
|
737
|
-
const emptyOptions$3 = {};
|
|
738
961
|
/**
|
|
739
|
-
*
|
|
962
|
+
* Parse a metadata block that has already been extracted — the Host bounds its
|
|
963
|
+
* size before it reaches the browser, so this entry point works on that text.
|
|
740
964
|
*
|
|
741
|
-
* @param
|
|
742
|
-
*
|
|
743
|
-
* @param {Options | null | undefined} [options]
|
|
744
|
-
* Configuration (optional).
|
|
745
|
-
* @returns {boolean}
|
|
746
|
-
* Whether `name` can be an identifier.
|
|
965
|
+
* @param yaml - the block's text, without its fences.
|
|
966
|
+
* @returns the parsed keys, or `null` when the block carries nothing to show.
|
|
747
967
|
*/
|
|
748
|
-
function
|
|
749
|
-
|
|
968
|
+
function parseSkillFrontmatter(yaml) {
|
|
969
|
+
if (yaml.trim() === "") return null;
|
|
970
|
+
try {
|
|
971
|
+
const parsed = parseBlock(yaml.replace(/\r\n/g, "\n"));
|
|
972
|
+
return Object.keys(parsed).length === 0 ? null : parsed;
|
|
973
|
+
} catch {
|
|
974
|
+
return null;
|
|
975
|
+
}
|
|
750
976
|
}
|
|
751
|
-
//#endregion
|
|
752
|
-
//#region node_modules/hast-util-whitespace/lib/index.js
|
|
753
977
|
/**
|
|
754
|
-
*
|
|
755
|
-
*/
|
|
756
|
-
const re = /[ \t\n\f\r]/g;
|
|
757
|
-
/**
|
|
758
|
-
* Check if the given value is *inter-element whitespace*.
|
|
978
|
+
* Separate a document's frontmatter from its body.
|
|
759
979
|
*
|
|
760
|
-
* @param
|
|
761
|
-
*
|
|
762
|
-
* @returns {boolean}
|
|
763
|
-
* Whether the `value` is inter-element whitespace (`boolean`): consisting of
|
|
764
|
-
* zero or more of space, tab (`\t`), line feed (`\n`), carriage return
|
|
765
|
-
* (`\r`), or form feed (`\f`); if a node is passed it must be a `Text` node,
|
|
766
|
-
* whose `value` field is checked.
|
|
980
|
+
* @param markdown - the whole document.
|
|
981
|
+
* @returns the parsed metadata (or `null`) plus the body to render.
|
|
767
982
|
*/
|
|
768
|
-
function
|
|
769
|
-
|
|
983
|
+
function splitSkillFrontmatter(markdown) {
|
|
984
|
+
const lines = (markdown.startsWith("") ? markdown.slice(1) : markdown).replace(/\r\n/g, "\n").split("\n");
|
|
985
|
+
if (!/^---[ \t]*$/.test(lines[0] ?? "")) return {
|
|
986
|
+
frontmatter: null,
|
|
987
|
+
body: markdown
|
|
988
|
+
};
|
|
989
|
+
for (let index = 1; index < lines.length; index++) {
|
|
990
|
+
if (!CLOSING_FENCE.test(lines[index] ?? "")) continue;
|
|
991
|
+
const body = lines.slice(index + 1).join("\n").replace(/^\n+/, "");
|
|
992
|
+
return {
|
|
993
|
+
frontmatter: parseSkillFrontmatter(lines.slice(1, index).join("\n")),
|
|
994
|
+
body
|
|
995
|
+
};
|
|
996
|
+
}
|
|
997
|
+
return {
|
|
998
|
+
frontmatter: null,
|
|
999
|
+
body: markdown
|
|
1000
|
+
};
|
|
770
1001
|
}
|
|
771
|
-
/**
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
*/
|
|
775
|
-
function empty$1(value) {
|
|
776
|
-
return value.replace(re, "") === "";
|
|
1002
|
+
/** Frontmatter is metadata, not part of the skill's rendered instructions. */
|
|
1003
|
+
function stripSkillFrontmatter(markdown) {
|
|
1004
|
+
return splitSkillFrontmatter(markdown).body;
|
|
777
1005
|
}
|
|
778
1006
|
//#endregion
|
|
779
|
-
//#region
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
1007
|
+
//#region \0dsh-css:src/client/components/FrontmatterPanel.module.css.mjs
|
|
1008
|
+
const css$12 = ".hR0bda_panel{border:1px solid var(--dsw-alias-border-l2,#0000001a);border-radius:var(--dsw-radius-sm,8px);background:var(--dsw-alias-bg-layer-1,#fff);flex-direction:column;min-width:0;display:flex;overflow:hidden}.hR0bda_head{border-bottom:1px solid var(--dsw-alias-border-l1,#0000000a);background:var(--dsw-alias-bg-module-platform,#f5f6f7);align-items:center;gap:8px;padding:8px 12px;display:flex}.hR0bda_title{min-width:0;color:var(--dsw-alias-label-tertiary,#81858c);letter-spacing:.08em;text-transform:uppercase;flex:1;margin:0;font-size:10.5px;font-weight:600}.hR0bda_count{color:var(--dsw-alias-label-tertiary,#81858c);font-variant-numeric:tabular-nums;font-size:11px}.hR0bda_list{max-height:320px;margin:0;padding:0;overflow-y:auto}.hR0bda_row{border-bottom:1px solid var(--dsw-alias-border-l1,#0000000a);flex-direction:column;gap:3px;min-width:0;padding:8px 12px;display:flex}.hR0bda_row:last-child{border-bottom:none}.hR0bda_key{overflow-wrap:anywhere;min-width:0;color:var(--dsw-alias-label-tertiary,#81858c);letter-spacing:.02em;font-size:10.5px;font-weight:600}.hR0bda_value{min-width:0;color:var(--dsw-alias-label-primary,#0f1115);margin:0;font-size:12.5px;line-height:19px}.hR0bda_text{overflow-wrap:anywhere}.hR0bda_chips{flex-wrap:wrap;gap:4px;display:flex}.hR0bda_chip{background:var(--dsw-alias-bg-module-platform,#f5f6f7);max-width:100%;color:var(--dsw-alias-label-secondary,#61666b);overflow-wrap:anywhere;border-radius:999px;padding:1px 7px;font-size:11px;line-height:17px;display:inline-flex}.hR0bda_chipOn{background:color-mix(in srgb, var(--dsw-alias-state-success-primary,#22c55e) 12%, transparent);color:var(--dsw-alias-state-success-primary,#22c55e)}.hR0bda_block{border-radius:var(--dsw-radius-xs,4px);background:var(--dsw-alias-bg-module-platform,#f5f6f7);max-height:200px;color:var(--dsw-alias-label-secondary,#61666b);white-space:pre-wrap;overflow-wrap:anywhere;margin:0;padding:8px 10px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11.5px;line-height:18px;overflow:auto}";
|
|
1009
|
+
const tagId$12 = "@nanmicoder/dsh-skills-hub/FrontmatterPanel.module.css";
|
|
1010
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$12) + "]") === null) {
|
|
1011
|
+
const tag = document.createElement("style");
|
|
1012
|
+
tag.dataset.plugin = "@nanmicoder/dsh-skills-hub";
|
|
1013
|
+
tag.dataset.pluginCss = tagId$12;
|
|
1014
|
+
tag.textContent = css$12;
|
|
1015
|
+
document.head.appendChild(tag);
|
|
1016
|
+
}
|
|
1017
|
+
var FrontmatterPanel_module_css_default = {
|
|
1018
|
+
"block": "hR0bda_block",
|
|
1019
|
+
"chip": "hR0bda_chip",
|
|
1020
|
+
"chipOn": "hR0bda_chipOn",
|
|
1021
|
+
"chips": "hR0bda_chips",
|
|
1022
|
+
"count": "hR0bda_count",
|
|
1023
|
+
"head": "hR0bda_head",
|
|
1024
|
+
"key": "hR0bda_key",
|
|
1025
|
+
"list": "hR0bda_list",
|
|
1026
|
+
"panel": "hR0bda_panel",
|
|
1027
|
+
"row": "hR0bda_row",
|
|
1028
|
+
"text": "hR0bda_text",
|
|
1029
|
+
"title": "hR0bda_title",
|
|
1030
|
+
"value": "hR0bda_value"
|
|
800
1031
|
};
|
|
801
|
-
Schema.prototype.normal = {};
|
|
802
|
-
Schema.prototype.property = {};
|
|
803
|
-
Schema.prototype.space = void 0;
|
|
804
1032
|
//#endregion
|
|
805
|
-
//#region
|
|
806
|
-
/**
|
|
807
|
-
|
|
808
|
-
*/
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
* Definitions.
|
|
812
|
-
* @param {Space | undefined} [space]
|
|
813
|
-
* Space.
|
|
814
|
-
* @returns {Schema}
|
|
815
|
-
* Schema.
|
|
816
|
-
*/
|
|
817
|
-
function merge(definitions, space) {
|
|
818
|
-
/** @type {Record<string, Info>} */
|
|
819
|
-
const property = {};
|
|
820
|
-
/** @type {Record<string, string>} */
|
|
821
|
-
const normal = {};
|
|
822
|
-
for (const definition of definitions) {
|
|
823
|
-
Object.assign(property, definition.property);
|
|
824
|
-
Object.assign(normal, definition.normal);
|
|
825
|
-
}
|
|
826
|
-
return new Schema(property, normal, space);
|
|
1033
|
+
//#region lib/client/components/FrontmatterPanel.js
|
|
1034
|
+
/** Longest JSON we are willing to render inline, in characters. */
|
|
1035
|
+
const MAX_JSON_LENGTH = 600;
|
|
1036
|
+
/** Narrow an `unknown` frontmatter value to a plain object (arrays excluded). */
|
|
1037
|
+
function isRecord(value) {
|
|
1038
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
827
1039
|
}
|
|
828
|
-
|
|
829
|
-
|
|
1040
|
+
/** Scalar rendering for anything that is not an array, object or block string. */
|
|
1041
|
+
function scalarText(value) {
|
|
1042
|
+
if (value === null || value === void 0) return "—";
|
|
1043
|
+
if (typeof value === "string") return value;
|
|
1044
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return String(value);
|
|
1045
|
+
if (typeof value === "symbol") return value.description ?? "symbol";
|
|
1046
|
+
if (typeof value === "function") return "fn";
|
|
1047
|
+
return "—";
|
|
1048
|
+
}
|
|
1049
|
+
/** Objects go out as JSON; a pathological value must not fill the whole rail. */
|
|
1050
|
+
function jsonText(value) {
|
|
1051
|
+
try {
|
|
1052
|
+
const text = JSON.stringify(value, null, 2);
|
|
1053
|
+
if (text === void 0) return "—";
|
|
1054
|
+
return text.length > MAX_JSON_LENGTH ? `${text.slice(0, MAX_JSON_LENGTH)}…` : text;
|
|
1055
|
+
} catch {
|
|
1056
|
+
return "—";
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
/** One value cell; the shape decides between a chip row, a code block and text. */
|
|
1060
|
+
function ValueView({ value }) {
|
|
1061
|
+
if (Array.isArray(value)) {
|
|
1062
|
+
if (value.length === 0) return (0, react_jsx_runtime.jsx)("span", {
|
|
1063
|
+
className: FrontmatterPanel_module_css_default.text,
|
|
1064
|
+
children: "—"
|
|
1065
|
+
});
|
|
1066
|
+
return (0, react_jsx_runtime.jsx)("span", {
|
|
1067
|
+
className: FrontmatterPanel_module_css_default.chips,
|
|
1068
|
+
children: value.map((item, index) => (0, react_jsx_runtime.jsx)("span", {
|
|
1069
|
+
className: FrontmatterPanel_module_css_default.chip,
|
|
1070
|
+
children: scalarText(item)
|
|
1071
|
+
}, `${String(index)}`))
|
|
1072
|
+
});
|
|
1073
|
+
}
|
|
1074
|
+
if (typeof value === "boolean") return (0, react_jsx_runtime.jsx)("span", {
|
|
1075
|
+
className: `${FrontmatterPanel_module_css_default.chip} ${value ? FrontmatterPanel_module_css_default.chipOn : ""}`,
|
|
1076
|
+
children: value ? "true" : "false"
|
|
1077
|
+
});
|
|
1078
|
+
if (typeof value === "string" && value.includes("\n")) return (0, react_jsx_runtime.jsx)("pre", {
|
|
1079
|
+
className: FrontmatterPanel_module_css_default.block,
|
|
1080
|
+
children: value
|
|
1081
|
+
});
|
|
1082
|
+
if (isRecord(value)) return (0, react_jsx_runtime.jsx)("pre", {
|
|
1083
|
+
className: FrontmatterPanel_module_css_default.block,
|
|
1084
|
+
children: jsonText(value)
|
|
1085
|
+
});
|
|
1086
|
+
return (0, react_jsx_runtime.jsx)("span", {
|
|
1087
|
+
className: FrontmatterPanel_module_css_default.text,
|
|
1088
|
+
children: scalarText(value)
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1091
|
+
/**
|
|
1092
|
+
* Structured view of a SKILL.md frontmatter block.
|
|
1093
|
+
*
|
|
1094
|
+
* SKILL.md metadata is data, not prose: pushing it through the markdown
|
|
1095
|
+
* renderer turns a dozen short fields into a wall of setext headings. It is
|
|
1096
|
+
* also reference material rather than the document itself, so it renders as a
|
|
1097
|
+
* label/value list under its own heading, with the list itself scrolling once a
|
|
1098
|
+
* skill declares more than a screenful of keys.
|
|
1099
|
+
*
|
|
1100
|
+
* `data` is typed `unknown`-valued on purpose — it is upstream YAML, and every
|
|
1101
|
+
* cell is derived defensively rather than trusted.
|
|
1102
|
+
*/
|
|
1103
|
+
function FrontmatterPanel(props) {
|
|
1104
|
+
const { data } = props;
|
|
1105
|
+
const t = useT();
|
|
1106
|
+
const entries = Object.entries(data);
|
|
1107
|
+
if (entries.length === 0) return (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, {});
|
|
1108
|
+
return (0, react_jsx_runtime.jsxs)("section", {
|
|
1109
|
+
className: FrontmatterPanel_module_css_default.panel,
|
|
1110
|
+
children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
1111
|
+
className: FrontmatterPanel_module_css_default.head,
|
|
1112
|
+
children: [(0, react_jsx_runtime.jsx)("h3", {
|
|
1113
|
+
className: FrontmatterPanel_module_css_default.title,
|
|
1114
|
+
children: t("frontmatter")
|
|
1115
|
+
}), (0, react_jsx_runtime.jsx)("span", {
|
|
1116
|
+
className: FrontmatterPanel_module_css_default.count,
|
|
1117
|
+
children: entries.length
|
|
1118
|
+
})]
|
|
1119
|
+
}), (0, react_jsx_runtime.jsx)("dl", {
|
|
1120
|
+
className: FrontmatterPanel_module_css_default.list,
|
|
1121
|
+
children: entries.map(([key, value]) => (0, react_jsx_runtime.jsxs)("div", {
|
|
1122
|
+
className: FrontmatterPanel_module_css_default.row,
|
|
1123
|
+
children: [(0, react_jsx_runtime.jsx)("dt", {
|
|
1124
|
+
className: FrontmatterPanel_module_css_default.key,
|
|
1125
|
+
title: key,
|
|
1126
|
+
children: key
|
|
1127
|
+
}), (0, react_jsx_runtime.jsx)("dd", {
|
|
1128
|
+
className: FrontmatterPanel_module_css_default.value,
|
|
1129
|
+
children: (0, react_jsx_runtime.jsx)(ValueView, { value })
|
|
1130
|
+
})]
|
|
1131
|
+
}, key))
|
|
1132
|
+
})]
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
//#endregion
|
|
1136
|
+
//#region node_modules/comma-separated-tokens/index.js
|
|
1137
|
+
/**
|
|
1138
|
+
* Serialize an array of strings or numbers to comma-separated tokens.
|
|
1139
|
+
*
|
|
1140
|
+
* @param {Array<string|number>} values
|
|
1141
|
+
* List of tokens.
|
|
1142
|
+
* @param {Options} [options]
|
|
1143
|
+
* Configuration for `stringify` (optional).
|
|
1144
|
+
* @returns {string}
|
|
1145
|
+
* Comma-separated tokens.
|
|
1146
|
+
*/
|
|
1147
|
+
function stringify$1(values, options) {
|
|
1148
|
+
const settings = options || {};
|
|
1149
|
+
return (values[values.length - 1] === "" ? [...values, ""] : values).join((settings.padRight ? " " : "") + "," + (settings.padLeft === false ? "" : " ")).trim();
|
|
1150
|
+
}
|
|
1151
|
+
//#endregion
|
|
1152
|
+
//#region node_modules/estree-util-is-identifier-name/lib/index.js
|
|
1153
|
+
const nameRe = /^[$_\p{ID_Start}][$_\u{200C}\u{200D}\p{ID_Continue}]*$/u;
|
|
1154
|
+
const nameReJsx = /^[$_\p{ID_Start}][-$_\u{200C}\u{200D}\p{ID_Continue}]*$/u;
|
|
1155
|
+
/** @type {Options} */
|
|
1156
|
+
const emptyOptions$3 = {};
|
|
1157
|
+
/**
|
|
1158
|
+
* Checks if the given value is a valid identifier name.
|
|
1159
|
+
*
|
|
1160
|
+
* @param {string} name
|
|
1161
|
+
* Identifier to check.
|
|
1162
|
+
* @param {Options | null | undefined} [options]
|
|
1163
|
+
* Configuration (optional).
|
|
1164
|
+
* @returns {boolean}
|
|
1165
|
+
* Whether `name` can be an identifier.
|
|
1166
|
+
*/
|
|
1167
|
+
function name$1(name, options) {
|
|
1168
|
+
return ((options || emptyOptions$3).jsx ? nameReJsx : nameRe).test(name);
|
|
1169
|
+
}
|
|
1170
|
+
//#endregion
|
|
1171
|
+
//#region node_modules/hast-util-whitespace/lib/index.js
|
|
1172
|
+
/**
|
|
1173
|
+
* @typedef {import('hast').Nodes} Nodes
|
|
1174
|
+
*/
|
|
1175
|
+
const re = /[ \t\n\f\r]/g;
|
|
1176
|
+
/**
|
|
1177
|
+
* Check if the given value is *inter-element whitespace*.
|
|
1178
|
+
*
|
|
1179
|
+
* @param {Nodes | string} thing
|
|
1180
|
+
* Thing to check (`Node` or `string`).
|
|
1181
|
+
* @returns {boolean}
|
|
1182
|
+
* Whether the `value` is inter-element whitespace (`boolean`): consisting of
|
|
1183
|
+
* zero or more of space, tab (`\t`), line feed (`\n`), carriage return
|
|
1184
|
+
* (`\r`), or form feed (`\f`); if a node is passed it must be a `Text` node,
|
|
1185
|
+
* whose `value` field is checked.
|
|
1186
|
+
*/
|
|
1187
|
+
function whitespace(thing) {
|
|
1188
|
+
return typeof thing === "object" ? thing.type === "text" ? empty$1(thing.value) : false : empty$1(thing);
|
|
1189
|
+
}
|
|
1190
|
+
/**
|
|
1191
|
+
* @param {string} value
|
|
1192
|
+
* @returns {boolean}
|
|
1193
|
+
*/
|
|
1194
|
+
function empty$1(value) {
|
|
1195
|
+
return value.replace(re, "") === "";
|
|
1196
|
+
}
|
|
1197
|
+
//#endregion
|
|
1198
|
+
//#region node_modules/property-information/lib/util/schema.js
|
|
1199
|
+
/**
|
|
1200
|
+
* @import {Schema as SchemaType, Space} from 'property-information'
|
|
1201
|
+
*/
|
|
1202
|
+
/** @type {SchemaType} */
|
|
1203
|
+
var Schema = class {
|
|
1204
|
+
/**
|
|
1205
|
+
* @param {SchemaType['property']} property
|
|
1206
|
+
* Property.
|
|
1207
|
+
* @param {SchemaType['normal']} normal
|
|
1208
|
+
* Normal.
|
|
1209
|
+
* @param {Space | undefined} [space]
|
|
1210
|
+
* Space.
|
|
1211
|
+
* @returns
|
|
1212
|
+
* Schema.
|
|
1213
|
+
*/
|
|
1214
|
+
constructor(property, normal, space) {
|
|
1215
|
+
this.normal = normal;
|
|
1216
|
+
this.property = property;
|
|
1217
|
+
if (space) this.space = space;
|
|
1218
|
+
}
|
|
1219
|
+
};
|
|
1220
|
+
Schema.prototype.normal = {};
|
|
1221
|
+
Schema.prototype.property = {};
|
|
1222
|
+
Schema.prototype.space = void 0;
|
|
1223
|
+
//#endregion
|
|
1224
|
+
//#region node_modules/property-information/lib/util/merge.js
|
|
1225
|
+
/**
|
|
1226
|
+
* @import {Info, Space} from 'property-information'
|
|
1227
|
+
*/
|
|
1228
|
+
/**
|
|
1229
|
+
* @param {ReadonlyArray<Schema>} definitions
|
|
1230
|
+
* Definitions.
|
|
1231
|
+
* @param {Space | undefined} [space]
|
|
1232
|
+
* Space.
|
|
1233
|
+
* @returns {Schema}
|
|
1234
|
+
* Schema.
|
|
1235
|
+
*/
|
|
1236
|
+
function merge(definitions, space) {
|
|
1237
|
+
/** @type {Record<string, Info>} */
|
|
1238
|
+
const property = {};
|
|
1239
|
+
/** @type {Record<string, string>} */
|
|
1240
|
+
const normal = {};
|
|
1241
|
+
for (const definition of definitions) {
|
|
1242
|
+
Object.assign(property, definition.property);
|
|
1243
|
+
Object.assign(normal, definition.normal);
|
|
1244
|
+
}
|
|
1245
|
+
return new Schema(property, normal, space);
|
|
1246
|
+
}
|
|
1247
|
+
//#endregion
|
|
1248
|
+
//#region node_modules/property-information/lib/normalize.js
|
|
830
1249
|
/**
|
|
831
1250
|
* Get the cleaned case insensitive form of an attribute or property.
|
|
832
1251
|
*
|
|
@@ -22485,72 +22904,71 @@ function(property) {
|
|
|
22485
22904
|
toMarkdownExtensions.push(gfmToMarkdown(settings));
|
|
22486
22905
|
}
|
|
22487
22906
|
//#endregion
|
|
22488
|
-
//#region \0dsh-css:src/client/components/
|
|
22489
|
-
const css$
|
|
22490
|
-
const tagId$
|
|
22491
|
-
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$
|
|
22907
|
+
//#region \0dsh-css:src/client/components/SkillDetailShell.module.css.mjs
|
|
22908
|
+
const css$11 = "._58q8UW_detail{box-sizing:border-box;min-width:0;color:var(--dsw-alias-label-primary,#0f1115);flex-direction:column;gap:12px;padding:16px 20px 28px;display:flex}._58q8UW_back{display:flex}._58q8UW_header{align-items:flex-start;gap:18px;min-width:0;display:flex}._58q8UW_headerText{flex:1;min-width:0}._58q8UW_eyebrow{align-items:center;gap:7px;margin:0;font-size:11px;line-height:16px;display:flex}._58q8UW_source{color:var(--dsw-alias-label-tertiary,#81858c);letter-spacing:.1em;text-transform:uppercase;font-weight:700}._58q8UW_version{color:var(--dsw-alias-label-tertiary,#81858c);font-variant-numeric:tabular-nums}._58q8UW_name{letter-spacing:-.014em;overflow-wrap:anywhere;margin:4px 0 0;font-size:26px;font-weight:700;line-height:34px}._58q8UW_name:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#4176e6);outline-offset:3px;border-radius:var(--dsw-radius-xs,4px)}._58q8UW_badges{flex-wrap:wrap;align-items:center;gap:6px;margin-top:10px;display:flex}._58q8UW_summary{max-width:68ch;color:var(--dsw-alias-label-secondary,#61666b);overflow-wrap:anywhere;margin:10px 0 0;font-size:14px;line-height:22px}._58q8UW_columns{flex-wrap:wrap;align-items:flex-start;gap:20px;min-width:0;display:flex}._58q8UW_main{flex-direction:column;flex:340px;gap:14px;min-width:0;display:flex}._58q8UW_panel{border:1px solid var(--dsw-alias-border-l2,#0000001a);border-radius:var(--dsw-radius-md,12px);background:var(--dsw-alias-bg-layer-1,#fff);min-width:0;padding:16px 18px}._58q8UW_markdown{overflow-wrap:anywhere;max-width:74ch;font-size:13.5px;line-height:1.7}._58q8UW_frontmatter{margin-top:18px}._58q8UW_muted{color:var(--dsw-alias-label-tertiary,#81858c);align-items:center;gap:6px;margin:0;padding:6px 0;font-size:12.5px;line-height:19px;display:flex}._58q8UW_files{flex-wrap:wrap;align-items:flex-start;gap:14px;min-width:0;display:flex}._58q8UW_fileList{border:1px solid var(--dsw-alias-border-l2,#0000001a);border-radius:var(--dsw-radius-sm,8px);flex:220px;min-width:0;max-height:420px;margin:0;padding:4px;list-style:none;overflow-y:auto}._58q8UW_fileItem{box-sizing:border-box;border-radius:var(--dsw-radius-xs,4px);width:100%;color:var(--dsw-alias-label-secondary,#61666b);font:inherit;text-align:left;cursor:pointer;background:0 0;border:none;align-items:center;gap:7px;margin:0;padding:6px 8px;font-size:12px;line-height:18px;display:flex}._58q8UW_fileItem:hover{background:var(--dsw-alias-interactive-bg-hover,#2631480f)}._58q8UW_fileItem:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#4176e6);outline-offset:-1px}._58q8UW_fileItemActive{background:var(--dsw-alias-button-ghost-active-fill,#ebeef2);color:var(--dsw-alias-label-primary,#0f1115);font-weight:600}._58q8UW_fileIcon{color:var(--dsw-alias-label-tertiary,#81858c);flex:none}._58q8UW_filePath{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}._58q8UW_fileFlag{background:color-mix(in srgb, var(--dsw-alias-state-warn-primary,#f59e0b) 14%, transparent);color:var(--dsw-alias-state-warn-primary,#f59e0b);border-radius:999px;flex:none;padding:0 6px;font-size:10.5px;font-weight:600;line-height:17px}._58q8UW_fileMeta{color:var(--dsw-alias-label-tertiary,#81858c);font-variant-numeric:tabular-nums;white-space:nowrap;flex:none;font-size:11px}._58q8UW_fileView{flex:2 320px;min-width:0}._58q8UW_fileBar{align-items:center;gap:8px;min-width:0;margin-bottom:8px;display:flex}._58q8UW_fileBarPath{min-width:0;color:var(--dsw-alias-label-secondary,#61666b);text-overflow:ellipsis;white-space:nowrap;flex:1;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11.5px;overflow:hidden}._58q8UW_copy{flex:none}._58q8UW_viewToggle{border:1px solid var(--dsw-alias-border-l2,#0000001a);background:var(--dsw-alias-bg-layer-2,#f0f2f5);border-radius:999px;flex:none;gap:2px;padding:2px;display:inline-flex}._58q8UW_viewToggle button{color:var(--dsw-alias-label-secondary,#61666b);font:inherit;cursor:pointer;background:0 0;border:0;border-radius:999px;padding:2px 10px;font-size:11.5px;line-height:18px}._58q8UW_viewToggle button[aria-pressed=true]{background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-primary,#0f1115);font-weight:600}._58q8UW_viewToggle button:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#4176e6);outline-offset:1px}._58q8UW_rail{flex:260px;min-width:0}._58q8UW_railCard{border:1px solid var(--dsw-alias-border-l2,#0000001a);border-radius:var(--dsw-radius-md,12px);background:var(--dsw-alias-bg-layer-1,#fff);flex-direction:column;min-width:0;display:flex;overflow:hidden}._58q8UW_railAction{flex-direction:column;gap:8px;padding:14px 14px 10px;display:flex}._58q8UW_railAction>button{width:100%}._58q8UW_facts{margin:0;padding:0 14px 6px}._58q8UW_fact{border-bottom:1px solid var(--dsw-alias-border-l1,#0000000a);justify-content:space-between;align-items:baseline;gap:14px;min-width:0;padding:9px 0;display:flex}._58q8UW_fact:last-child{border-bottom:none}._58q8UW_factLabel{color:var(--dsw-alias-label-tertiary,#81858c);flex:none;font-size:12.5px;line-height:19px}._58q8UW_factValue{min-width:0;color:var(--dsw-alias-label-primary,#0f1115);text-align:right;overflow-wrap:anywhere;margin:0;font-size:13px;font-weight:600;line-height:19px}._58q8UW_srOnly{clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}._58q8UW_markdown>:first-child{margin-top:0}._58q8UW_markdown>:last-child{margin-bottom:0}._58q8UW_markdown h1,._58q8UW_markdown h2,._58q8UW_markdown h3{margin:1.4em 0 .6em;line-height:1.35}._58q8UW_markdown h1{font-size:1.7em}._58q8UW_markdown h2{font-size:1.4em}._58q8UW_markdown h3{font-size:1.15em}._58q8UW_markdown p,._58q8UW_markdown ul,._58q8UW_markdown ol{margin:.75em 0}._58q8UW_markdown ul,._58q8UW_markdown ol{padding-left:1.7em}._58q8UW_markdown a{color:var(--dsw-alias-state-business-primary,#4176e6);text-decoration:underline}._58q8UW_markdown pre{background:var(--dsw-alias-bg-layer-2,#f2f3f5);border-radius:8px;padding:14px;overflow-x:auto}._58q8UW_markdown code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.9em}._58q8UW_markdown :not(pre)>code{background:var(--dsw-alias-bg-layer-2,#f2f3f5);border-radius:4px;padding:2px 5px}._58q8UW_markdown blockquote{border-left:3px solid var(--dsw-alias-border-l2,#0000001a);color:var(--dsw-alias-label-secondary,#61666b);margin:1em 0;padding-left:14px}._58q8UW_markdown table{border-collapse:collapse;max-width:100%;display:block;overflow-x:auto}._58q8UW_markdown th,._58q8UW_markdown td{border:1px solid var(--dsw-alias-border-l2,#0000001a);text-align:left;padding:7px 10px}._58q8UW_markdown img{max-width:100%;height:auto}._58q8UW_markdown hr{border:0;border-top:1px solid var(--dsw-alias-border-l2,#0000001a);margin:20px 0}._58q8UW_skeletonAvatar,._58q8UW_skeletonTitle,._58q8UW_skeletonLine{background:var(--dsw-alias-border-l2,#0000001a);border-radius:5px;animation:1.6s ease-in-out infinite alternate _58q8UW_skeletonPulse;display:block}._58q8UW_skeletonAvatar{border-radius:18px;flex:none;width:84px;height:84px}._58q8UW_skeletonTitle{width:55%;height:28px;margin:8px 0 20px}._58q8UW_skeletonLine{width:100%;height:13px;margin:16px 0}._58q8UW_skeletonLine:nth-child(3n){width:72%}@keyframes _58q8UW_skeletonPulse{to{opacity:.4}}@media (prefers-reduced-motion:reduce){._58q8UW_skeletonAvatar,._58q8UW_skeletonTitle,._58q8UW_skeletonLine{animation:none}}";
|
|
22909
|
+
const tagId$11 = "@nanmicoder/dsh-skills-hub/SkillDetailShell.module.css";
|
|
22910
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$11) + "]") === null) {
|
|
22492
22911
|
const tag = document.createElement("style");
|
|
22493
22912
|
tag.dataset.plugin = "@nanmicoder/dsh-skills-hub";
|
|
22494
|
-
tag.dataset.pluginCss = tagId$
|
|
22495
|
-
tag.textContent = css$
|
|
22913
|
+
tag.dataset.pluginCss = tagId$11;
|
|
22914
|
+
tag.textContent = css$11;
|
|
22496
22915
|
document.head.appendChild(tag);
|
|
22497
22916
|
}
|
|
22498
|
-
var
|
|
22499
|
-
"back": "
|
|
22500
|
-
"badges": "
|
|
22501
|
-
"columns": "
|
|
22502
|
-
"copy": "
|
|
22503
|
-
"detail": "
|
|
22504
|
-
"eyebrow": "
|
|
22505
|
-
"fact": "
|
|
22506
|
-
"factLabel": "
|
|
22507
|
-
"factValue": "
|
|
22508
|
-
"facts": "
|
|
22509
|
-
"fileBar": "
|
|
22510
|
-
"fileBarPath": "
|
|
22511
|
-
"fileFlag": "
|
|
22512
|
-
"fileIcon": "
|
|
22513
|
-
"fileItem": "
|
|
22514
|
-
"fileItemActive": "
|
|
22515
|
-
"fileList": "
|
|
22516
|
-
"fileMeta": "
|
|
22517
|
-
"filePath": "
|
|
22518
|
-
"fileView": "
|
|
22519
|
-
"files": "
|
|
22520
|
-
"frontmatter": "
|
|
22521
|
-
"header": "
|
|
22522
|
-
"headerText": "
|
|
22523
|
-
"main": "
|
|
22524
|
-
"markdown": "
|
|
22525
|
-
"muted": "
|
|
22526
|
-
"name": "
|
|
22527
|
-
"panel": "
|
|
22528
|
-
"rail": "
|
|
22529
|
-
"railAction": "
|
|
22530
|
-
"railCard": "
|
|
22531
|
-
"
|
|
22532
|
-
"
|
|
22533
|
-
"
|
|
22534
|
-
"
|
|
22535
|
-
"
|
|
22536
|
-
"
|
|
22537
|
-
"
|
|
22538
|
-
"
|
|
22539
|
-
"
|
|
22540
|
-
"skeletonTitle": "xO9T5G_skeletonTitle",
|
|
22541
|
-
"source": "xO9T5G_source",
|
|
22542
|
-
"srOnly": "xO9T5G_srOnly",
|
|
22543
|
-
"summary": "xO9T5G_summary",
|
|
22544
|
-
"version": "xO9T5G_version",
|
|
22545
|
-
"warning": "xO9T5G_warning",
|
|
22546
|
-
"warningIcon": "xO9T5G_warningIcon"
|
|
22917
|
+
var SkillDetailShell_module_css_default = {
|
|
22918
|
+
"back": "_58q8UW_back",
|
|
22919
|
+
"badges": "_58q8UW_badges",
|
|
22920
|
+
"columns": "_58q8UW_columns",
|
|
22921
|
+
"copy": "_58q8UW_copy",
|
|
22922
|
+
"detail": "_58q8UW_detail",
|
|
22923
|
+
"eyebrow": "_58q8UW_eyebrow",
|
|
22924
|
+
"fact": "_58q8UW_fact",
|
|
22925
|
+
"factLabel": "_58q8UW_factLabel",
|
|
22926
|
+
"factValue": "_58q8UW_factValue",
|
|
22927
|
+
"facts": "_58q8UW_facts",
|
|
22928
|
+
"fileBar": "_58q8UW_fileBar",
|
|
22929
|
+
"fileBarPath": "_58q8UW_fileBarPath",
|
|
22930
|
+
"fileFlag": "_58q8UW_fileFlag",
|
|
22931
|
+
"fileIcon": "_58q8UW_fileIcon",
|
|
22932
|
+
"fileItem": "_58q8UW_fileItem",
|
|
22933
|
+
"fileItemActive": "_58q8UW_fileItemActive",
|
|
22934
|
+
"fileList": "_58q8UW_fileList",
|
|
22935
|
+
"fileMeta": "_58q8UW_fileMeta",
|
|
22936
|
+
"filePath": "_58q8UW_filePath",
|
|
22937
|
+
"fileView": "_58q8UW_fileView",
|
|
22938
|
+
"files": "_58q8UW_files",
|
|
22939
|
+
"frontmatter": "_58q8UW_frontmatter",
|
|
22940
|
+
"header": "_58q8UW_header",
|
|
22941
|
+
"headerText": "_58q8UW_headerText",
|
|
22942
|
+
"main": "_58q8UW_main",
|
|
22943
|
+
"markdown": "_58q8UW_markdown",
|
|
22944
|
+
"muted": "_58q8UW_muted",
|
|
22945
|
+
"name": "_58q8UW_name",
|
|
22946
|
+
"panel": "_58q8UW_panel",
|
|
22947
|
+
"rail": "_58q8UW_rail",
|
|
22948
|
+
"railAction": "_58q8UW_railAction",
|
|
22949
|
+
"railCard": "_58q8UW_railCard",
|
|
22950
|
+
"skeletonAvatar": "_58q8UW_skeletonAvatar",
|
|
22951
|
+
"skeletonLine": "_58q8UW_skeletonLine",
|
|
22952
|
+
"skeletonPulse": "_58q8UW_skeletonPulse",
|
|
22953
|
+
"skeletonTitle": "_58q8UW_skeletonTitle",
|
|
22954
|
+
"source": "_58q8UW_source",
|
|
22955
|
+
"srOnly": "_58q8UW_srOnly",
|
|
22956
|
+
"summary": "_58q8UW_summary",
|
|
22957
|
+
"version": "_58q8UW_version",
|
|
22958
|
+
"viewToggle": "_58q8UW_viewToggle"
|
|
22547
22959
|
};
|
|
22548
22960
|
//#endregion
|
|
22549
22961
|
//#region lib/client/components/MarkdownView.js
|
|
22550
|
-
/**
|
|
22962
|
+
/**
|
|
22963
|
+
* Self-contained GFM rendering for untrusted marketplace and local skill docs.
|
|
22964
|
+
*
|
|
22965
|
+
* Raw HTML is skipped and external links are opened with `noopener noreferrer`;
|
|
22966
|
+
* the typography lives in the detail shell so a rendered document looks the same
|
|
22967
|
+
* in the overview tab and in the file viewer.
|
|
22968
|
+
*/
|
|
22551
22969
|
function MarkdownView({ content }) {
|
|
22552
22970
|
return (0, react_jsx_runtime.jsx)("div", {
|
|
22553
|
-
className:
|
|
22971
|
+
className: SkillDetailShell_module_css_default.markdown,
|
|
22554
22972
|
children: (0, react_jsx_runtime.jsx)(Markdown, {
|
|
22555
22973
|
remarkPlugins: [remarkGfm],
|
|
22556
22974
|
skipHtml: true,
|
|
@@ -22565,51 +22983,840 @@ function(property) {
|
|
|
22565
22983
|
});
|
|
22566
22984
|
}
|
|
22567
22985
|
//#endregion
|
|
22568
|
-
//#region \0dsh-css:src/client/components/
|
|
22569
|
-
const css$
|
|
22570
|
-
const tagId$
|
|
22571
|
-
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$
|
|
22986
|
+
//#region \0dsh-css:src/client/components/SkillAvatar.module.css.mjs
|
|
22987
|
+
const css$10 = ".C6tuwq_image{object-fit:cover;border:1px solid var(--dsw-alias-border-l2,#0000001a);background:var(--dsw-alias-bg-module-platform,#f5f6f7);flex:none}.C6tuwq_letter{box-sizing:border-box;letter-spacing:-.02em;color:var(--dsw-alias-label-primary-inverted,#fff);user-select:none;box-shadow:inset 0 1px 0 color-mix(in srgb, var(--dsw-alias-label-primary-inverted,#fff) 12%, transparent);flex:none;justify-content:center;align-items:center;font-weight:700;line-height:1;display:inline-flex;overflow:hidden}.C6tuwq_tone1{background:linear-gradient(145deg, color-mix(in srgb, var(--dsw-alias-state-success-primary,#22c55e) 74%, var(--dsw-alias-label-primary,#0f1115)), color-mix(in srgb, var(--dsw-alias-state-success-primary,#22c55e) 48%, var(--dsw-alias-label-primary,#0f1115)))}.C6tuwq_tone2{background:linear-gradient(145deg, color-mix(in srgb, var(--dsw-alias-state-warn-primary,#f59e0b) 80%, var(--dsw-alias-label-primary,#0f1115)), color-mix(in srgb, var(--dsw-alias-state-warn-primary,#f59e0b) 54%, var(--dsw-alias-label-primary,#0f1115)))}.C6tuwq_tone5{background:linear-gradient(145deg, color-mix(in srgb, color-mix(in srgb, var(--dsw-alias-state-warn-primary,#f59e0b) 55%, var(--dsw-alias-state-success-primary,#22c55e)) 78%, var(--dsw-alias-label-primary,#0f1115)), color-mix(in srgb, color-mix(in srgb, var(--dsw-alias-state-warn-primary,#f59e0b) 55%, var(--dsw-alias-state-success-primary,#22c55e)) 50%, var(--dsw-alias-label-primary,#0f1115)))}.C6tuwq_tone0{background:linear-gradient(145deg, color-mix(in srgb, var(--dsw-alias-state-business-primary,#4176e6) 82%, var(--dsw-alias-label-primary,#0f1115)), color-mix(in srgb, var(--dsw-alias-state-business-primary,#4176e6) 56%, var(--dsw-alias-label-primary,#0f1115)))}.C6tuwq_tone4{background:linear-gradient(145deg, color-mix(in srgb, color-mix(in srgb, var(--dsw-alias-state-business-primary,#4176e6) 58%, var(--dsw-alias-state-success-primary,#22c55e)) 80%, var(--dsw-alias-label-primary,#0f1115)), color-mix(in srgb, color-mix(in srgb, var(--dsw-alias-state-business-primary,#4176e6) 58%, var(--dsw-alias-state-success-primary,#22c55e)) 52%, var(--dsw-alias-label-primary,#0f1115)))}.C6tuwq_tone3{background:linear-gradient(145deg, color-mix(in srgb, var(--dsw-alias-state-idle-primary,#d4d4d4) 62%, var(--dsw-alias-label-primary,#0f1115)), color-mix(in srgb, var(--dsw-alias-state-idle-primary,#d4d4d4) 38%, var(--dsw-alias-label-primary,#0f1115)))}";
|
|
22988
|
+
const tagId$10 = "@nanmicoder/dsh-skills-hub/SkillAvatar.module.css";
|
|
22989
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$10) + "]") === null) {
|
|
22572
22990
|
const tag = document.createElement("style");
|
|
22573
22991
|
tag.dataset.plugin = "@nanmicoder/dsh-skills-hub";
|
|
22574
|
-
tag.dataset.pluginCss = tagId$
|
|
22575
|
-
tag.textContent = css$
|
|
22992
|
+
tag.dataset.pluginCss = tagId$10;
|
|
22993
|
+
tag.textContent = css$10;
|
|
22576
22994
|
document.head.appendChild(tag);
|
|
22577
22995
|
}
|
|
22578
|
-
var
|
|
22579
|
-
"
|
|
22580
|
-
"
|
|
22581
|
-
"
|
|
22582
|
-
"
|
|
22583
|
-
"
|
|
22584
|
-
"
|
|
22585
|
-
"
|
|
22586
|
-
"
|
|
22587
|
-
|
|
22588
|
-
|
|
22589
|
-
|
|
22590
|
-
|
|
22591
|
-
|
|
22592
|
-
|
|
22593
|
-
|
|
22594
|
-
|
|
22595
|
-
|
|
22596
|
-
|
|
22597
|
-
|
|
22598
|
-
|
|
22599
|
-
|
|
22996
|
+
var SkillAvatar_module_css_default = {
|
|
22997
|
+
"image": "C6tuwq_image",
|
|
22998
|
+
"letter": "C6tuwq_letter",
|
|
22999
|
+
"tone0": "C6tuwq_tone0",
|
|
23000
|
+
"tone1": "C6tuwq_tone1",
|
|
23001
|
+
"tone2": "C6tuwq_tone2",
|
|
23002
|
+
"tone3": "C6tuwq_tone3",
|
|
23003
|
+
"tone4": "C6tuwq_tone4",
|
|
23004
|
+
"tone5": "C6tuwq_tone5"
|
|
23005
|
+
};
|
|
23006
|
+
//#endregion
|
|
23007
|
+
//#region lib/client/components/SkillAvatar.js
|
|
23008
|
+
/** Palette slots; each maps to one `.toneN` class in the module stylesheet. */
|
|
23009
|
+
const TONE_COUNT = 6;
|
|
23010
|
+
/**
|
|
23011
|
+
* Per-source tone family. The `source` prop is otherwise unused by a letter
|
|
23012
|
+
* avatar, so it earns its place as the family selector: ClawHub entries read
|
|
23013
|
+
* warm, SkillHub entries read cool, and a mixed grid still shows at a glance
|
|
23014
|
+
* where a card came from. A locally authored skill has no marketplace family,
|
|
23015
|
+
* so its tile stays neutral rather than borrowing one.
|
|
23016
|
+
*/
|
|
23017
|
+
const SOURCE_TONES = {
|
|
23018
|
+
clawhub: [
|
|
23019
|
+
2,
|
|
23020
|
+
5,
|
|
23021
|
+
1
|
|
23022
|
+
],
|
|
23023
|
+
skillhub: [
|
|
23024
|
+
0,
|
|
23025
|
+
4,
|
|
23026
|
+
3
|
|
23027
|
+
],
|
|
23028
|
+
local: [3]
|
|
23029
|
+
};
|
|
23030
|
+
/** Class name per tone slot, resolved once so the render path stays trivial. */
|
|
23031
|
+
const TONE_CLASSES = [
|
|
23032
|
+
SkillAvatar_module_css_default["tone0"] ?? "",
|
|
23033
|
+
SkillAvatar_module_css_default["tone1"] ?? "",
|
|
23034
|
+
SkillAvatar_module_css_default["tone2"] ?? "",
|
|
23035
|
+
SkillAvatar_module_css_default["tone3"] ?? "",
|
|
23036
|
+
SkillAvatar_module_css_default["tone4"] ?? "",
|
|
23037
|
+
SkillAvatar_module_css_default["tone5"] ?? ""
|
|
23038
|
+
];
|
|
23039
|
+
/**
|
|
23040
|
+
* Deterministic palette index: the same name always keeps the same identity
|
|
23041
|
+
* color across sessions, pages and re-renders.
|
|
23042
|
+
*/
|
|
23043
|
+
function hashIndex(input) {
|
|
23044
|
+
let hash = 0;
|
|
23045
|
+
for (let i = 0; i < input.length; i++) hash = hash * 31 + input.charCodeAt(i) | 0;
|
|
23046
|
+
return Math.abs(hash) % TONE_COUNT;
|
|
23047
|
+
}
|
|
23048
|
+
/**
|
|
23049
|
+
* First visible character, uppercased. `Array.from` rather than `name[0]` so a
|
|
23050
|
+
* CJK or astral-plane initial is not cut in half by UTF-16 indexing.
|
|
23051
|
+
*/
|
|
23052
|
+
function initialOf(name) {
|
|
23053
|
+
const first = Array.from(name.trim())[0];
|
|
23054
|
+
return first ? first.toUpperCase() : "?";
|
|
23055
|
+
}
|
|
23056
|
+
/**
|
|
23057
|
+
* Skill icon tile.
|
|
23058
|
+
*
|
|
23059
|
+
* An `<img>` is rendered only for an `https:` URL: the string arrives from a
|
|
23060
|
+
* third-party catalogue, and allowing `javascript:` or `data:` here would turn
|
|
23061
|
+
* an upstream payload into script execution inside the DSH page.
|
|
23062
|
+
*/
|
|
23063
|
+
function SkillAvatar(props) {
|
|
23064
|
+
const { name, source, iconUrl, size } = props;
|
|
23065
|
+
const radius = Math.round(size * .24);
|
|
23066
|
+
if (iconUrl !== void 0 && iconUrl.startsWith("https://")) return (0, react_jsx_runtime.jsx)("img", {
|
|
23067
|
+
src: iconUrl,
|
|
23068
|
+
alt: "",
|
|
23069
|
+
width: size,
|
|
23070
|
+
height: size,
|
|
23071
|
+
loading: "lazy",
|
|
23072
|
+
decoding: "async",
|
|
23073
|
+
className: SkillAvatar_module_css_default.image,
|
|
23074
|
+
style: {
|
|
23075
|
+
width: size,
|
|
23076
|
+
height: size,
|
|
23077
|
+
borderRadius: radius
|
|
23078
|
+
}
|
|
23079
|
+
});
|
|
23080
|
+
const family = SOURCE_TONES[source];
|
|
23081
|
+
const tone = family[hashIndex(name) % family.length] ?? 0;
|
|
23082
|
+
return (0, react_jsx_runtime.jsx)("span", {
|
|
23083
|
+
"aria-hidden": "true",
|
|
23084
|
+
className: `${SkillAvatar_module_css_default.letter} ${TONE_CLASSES[tone] ?? ""}`,
|
|
23085
|
+
style: {
|
|
23086
|
+
width: size,
|
|
23087
|
+
height: size,
|
|
23088
|
+
borderRadius: radius,
|
|
23089
|
+
fontSize: Math.round(size * .4)
|
|
23090
|
+
},
|
|
23091
|
+
children: initialOf(name)
|
|
23092
|
+
});
|
|
23093
|
+
}
|
|
23094
|
+
//#endregion
|
|
23095
|
+
//#region lib/client/components/SkillDetailShell.js
|
|
23096
|
+
/**
|
|
23097
|
+
* The detail surface shared by the market catalogue and the installed inventory.
|
|
23098
|
+
*
|
|
23099
|
+
* Both pages answer the same question — "what is inside this skill?" — and the
|
|
23100
|
+
* only real difference is where the facts come from: an upstream provider or the
|
|
23101
|
+
* filesystem. Everything that is *reading* rather than *fetching* therefore
|
|
23102
|
+
* lives here: the back affordance, the identity header, the two tabs, the
|
|
23103
|
+
* document column, the facts rail and the focus move that keeps a keyboard user
|
|
23104
|
+
* from being left at the top of the document when the list is replaced.
|
|
23105
|
+
*
|
|
23106
|
+
* The two pages are not identical, so the shell takes slots rather than a data
|
|
23107
|
+
* model: `badges`, `notice`, `overview`, `files` and `rail` are composed by the
|
|
23108
|
+
* caller, while each caller owns its own loading state and its own choice of tab
|
|
23109
|
+
* storage (the market page keeps it in `MarketState`, the local page in its own
|
|
23110
|
+
* component state).
|
|
23111
|
+
*/
|
|
23112
|
+
/** Stable DOM ids for the tab list and its panels; one detail page is mounted at a time. */
|
|
23113
|
+
const TAB_ID = "skills-hub-detail-tab";
|
|
23114
|
+
const PANEL_ID$1 = "skills-hub-detail-panel";
|
|
23115
|
+
/**
|
|
23116
|
+
* The shell's stylesheet, re-exported for its callers.
|
|
23117
|
+
*
|
|
23118
|
+
* The pages built on this shell also render shell-shaped fragments (a report
|
|
23119
|
+
* row, a path line, a facts list) and reaching them through an import is more
|
|
23120
|
+
* honest than re-declaring near-copies of the same rules in a second module.
|
|
23121
|
+
*/
|
|
23122
|
+
const detailStyles = SkillDetailShell_module_css_default;
|
|
23123
|
+
/** One label/value row of the facts rail. */
|
|
23124
|
+
function Fact(props) {
|
|
23125
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
23126
|
+
className: SkillDetailShell_module_css_default.fact,
|
|
23127
|
+
children: [(0, react_jsx_runtime.jsx)("dt", {
|
|
23128
|
+
className: SkillDetailShell_module_css_default.factLabel,
|
|
23129
|
+
children: props.label
|
|
23130
|
+
}), (0, react_jsx_runtime.jsx)("dd", {
|
|
23131
|
+
className: SkillDetailShell_module_css_default.factValue,
|
|
23132
|
+
children: props.children
|
|
23133
|
+
})]
|
|
23134
|
+
});
|
|
23135
|
+
}
|
|
23136
|
+
function SkillDetailShell(props) {
|
|
23137
|
+
const { name, activeTab, onTabChange } = props;
|
|
23138
|
+
const headingRef = (0, react.useRef)(null);
|
|
23139
|
+
(0, react.useEffect)(() => {
|
|
23140
|
+
headingRef.current?.focus();
|
|
23141
|
+
}, [props.focusKey]);
|
|
23142
|
+
const tabs = [{
|
|
23143
|
+
value: "overview",
|
|
23144
|
+
label: props.overviewLabel,
|
|
23145
|
+
id: `${TAB_ID}-overview`,
|
|
23146
|
+
panelId: `${PANEL_ID$1}-overview`
|
|
23147
|
+
}, {
|
|
23148
|
+
value: "files",
|
|
23149
|
+
label: props.filesLabel,
|
|
23150
|
+
id: `${TAB_ID}-files`,
|
|
23151
|
+
panelId: `${PANEL_ID$1}-files`
|
|
23152
|
+
}];
|
|
23153
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
23154
|
+
className: SkillDetailShell_module_css_default.detail,
|
|
23155
|
+
children: [
|
|
23156
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
23157
|
+
className: SkillDetailShell_module_css_default.back,
|
|
23158
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
23159
|
+
variant: "ghost",
|
|
23160
|
+
size: "sm",
|
|
23161
|
+
icon: (0, react_jsx_runtime.jsx)(ArrowLeftIcon, { size: 16 }),
|
|
23162
|
+
onClick: props.onBack,
|
|
23163
|
+
children: props.backLabel
|
|
23164
|
+
})
|
|
23165
|
+
}),
|
|
23166
|
+
(0, react_jsx_runtime.jsxs)("header", {
|
|
23167
|
+
className: SkillDetailShell_module_css_default.header,
|
|
23168
|
+
children: [props.avatar, (0, react_jsx_runtime.jsxs)("div", {
|
|
23169
|
+
className: SkillDetailShell_module_css_default.headerText,
|
|
23170
|
+
children: [
|
|
23171
|
+
(0, react_jsx_runtime.jsx)("p", {
|
|
23172
|
+
className: SkillDetailShell_module_css_default.eyebrow,
|
|
23173
|
+
children: props.eyebrow
|
|
23174
|
+
}),
|
|
23175
|
+
(0, react_jsx_runtime.jsx)("h1", {
|
|
23176
|
+
ref: headingRef,
|
|
23177
|
+
tabIndex: -1,
|
|
23178
|
+
className: SkillDetailShell_module_css_default.name,
|
|
23179
|
+
children: name
|
|
23180
|
+
}),
|
|
23181
|
+
props.badges === void 0 ? null : (0, react_jsx_runtime.jsx)("div", {
|
|
23182
|
+
className: SkillDetailShell_module_css_default.badges,
|
|
23183
|
+
children: props.badges
|
|
23184
|
+
}),
|
|
23185
|
+
props.summary === void 0 || props.summary === "" ? null : (0, react_jsx_runtime.jsx)("p", {
|
|
23186
|
+
className: SkillDetailShell_module_css_default.summary,
|
|
23187
|
+
children: props.summary
|
|
23188
|
+
})
|
|
23189
|
+
]
|
|
23190
|
+
})]
|
|
23191
|
+
}),
|
|
23192
|
+
props.notice,
|
|
23193
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
23194
|
+
className: SkillDetailShell_module_css_default.columns,
|
|
23195
|
+
children: [(0, react_jsx_runtime.jsxs)("main", {
|
|
23196
|
+
className: SkillDetailShell_module_css_default.main,
|
|
23197
|
+
children: [
|
|
23198
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SegmentedTabs, {
|
|
23199
|
+
items: tabs,
|
|
23200
|
+
value: activeTab,
|
|
23201
|
+
onChange: onTabChange,
|
|
23202
|
+
label: name
|
|
23203
|
+
}),
|
|
23204
|
+
activeTab === "overview" && (0, react_jsx_runtime.jsx)("section", {
|
|
23205
|
+
className: SkillDetailShell_module_css_default.panel,
|
|
23206
|
+
id: `${PANEL_ID$1}-overview`,
|
|
23207
|
+
role: "tabpanel",
|
|
23208
|
+
"aria-labelledby": `${TAB_ID}-overview`,
|
|
23209
|
+
children: props.overview
|
|
23210
|
+
}),
|
|
23211
|
+
activeTab === "files" && (0, react_jsx_runtime.jsx)("section", {
|
|
23212
|
+
className: SkillDetailShell_module_css_default.panel,
|
|
23213
|
+
id: `${PANEL_ID$1}-files`,
|
|
23214
|
+
role: "tabpanel",
|
|
23215
|
+
"aria-labelledby": `${TAB_ID}-files`,
|
|
23216
|
+
children: props.files
|
|
23217
|
+
})
|
|
23218
|
+
]
|
|
23219
|
+
}), (0, react_jsx_runtime.jsx)("aside", {
|
|
23220
|
+
className: SkillDetailShell_module_css_default.rail,
|
|
23221
|
+
children: (0, react_jsx_runtime.jsx)("div", {
|
|
23222
|
+
className: SkillDetailShell_module_css_default.railCard,
|
|
23223
|
+
children: props.rail
|
|
23224
|
+
})
|
|
23225
|
+
})]
|
|
23226
|
+
})
|
|
23227
|
+
]
|
|
23228
|
+
});
|
|
23229
|
+
}
|
|
23230
|
+
/**
|
|
23231
|
+
* Keeps the detail layout stable while its request is in flight.
|
|
23232
|
+
*
|
|
23233
|
+
* It renders the same boxes as the real page rather than a spinner, because the
|
|
23234
|
+
* click that opens a detail is the moment the panel changes shape; a centered
|
|
23235
|
+
* "loading" line would make that change happen twice.
|
|
23236
|
+
*/
|
|
23237
|
+
function SkillDetailSkeleton({ onBack }) {
|
|
23238
|
+
const t = useT();
|
|
23239
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
23240
|
+
className: SkillDetailShell_module_css_default.detail,
|
|
23241
|
+
"aria-busy": "true",
|
|
23242
|
+
children: [(0, react_jsx_runtime.jsx)("div", {
|
|
23243
|
+
className: SkillDetailShell_module_css_default.back,
|
|
23244
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
23245
|
+
variant: "ghost",
|
|
23246
|
+
size: "sm",
|
|
23247
|
+
icon: (0, react_jsx_runtime.jsx)(ArrowLeftIcon, { size: 16 }),
|
|
23248
|
+
onClick: onBack,
|
|
23249
|
+
children: t("back")
|
|
23250
|
+
})
|
|
23251
|
+
}), (0, react_jsx_runtime.jsx)("div", {
|
|
23252
|
+
role: "status",
|
|
23253
|
+
"aria-label": t("loading"),
|
|
23254
|
+
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
23255
|
+
"aria-hidden": "true",
|
|
23256
|
+
children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
23257
|
+
className: SkillDetailShell_module_css_default.header,
|
|
23258
|
+
children: [(0, react_jsx_runtime.jsx)("span", { className: SkillDetailShell_module_css_default.skeletonAvatar }), (0, react_jsx_runtime.jsxs)("div", {
|
|
23259
|
+
className: SkillDetailShell_module_css_default.headerText,
|
|
23260
|
+
children: [
|
|
23261
|
+
(0, react_jsx_runtime.jsx)("span", { className: SkillDetailShell_module_css_default.skeletonTitle }),
|
|
23262
|
+
(0, react_jsx_runtime.jsx)("span", { className: SkillDetailShell_module_css_default.skeletonLine }),
|
|
23263
|
+
(0, react_jsx_runtime.jsx)("span", { className: SkillDetailShell_module_css_default.skeletonLine })
|
|
23264
|
+
]
|
|
23265
|
+
})]
|
|
23266
|
+
}), (0, react_jsx_runtime.jsxs)("div", {
|
|
23267
|
+
className: SkillDetailShell_module_css_default.columns,
|
|
23268
|
+
style: { marginTop: 24 },
|
|
23269
|
+
children: [(0, react_jsx_runtime.jsx)("div", {
|
|
23270
|
+
className: SkillDetailShell_module_css_default.main,
|
|
23271
|
+
children: (0, react_jsx_runtime.jsx)("div", {
|
|
23272
|
+
className: SkillDetailShell_module_css_default.panel,
|
|
23273
|
+
children: Array.from({ length: 10 }, (_, index) => (0, react_jsx_runtime.jsx)("span", { className: SkillDetailShell_module_css_default.skeletonLine }, index))
|
|
23274
|
+
})
|
|
23275
|
+
}), (0, react_jsx_runtime.jsx)("div", {
|
|
23276
|
+
className: SkillDetailShell_module_css_default.rail,
|
|
23277
|
+
children: (0, react_jsx_runtime.jsx)("div", {
|
|
23278
|
+
className: SkillDetailShell_module_css_default.panel,
|
|
23279
|
+
children: Array.from({ length: 5 }, (_, index) => (0, react_jsx_runtime.jsx)("span", { className: SkillDetailShell_module_css_default.skeletonLine }, index))
|
|
23280
|
+
})
|
|
23281
|
+
})]
|
|
23282
|
+
})]
|
|
23283
|
+
})
|
|
23284
|
+
})]
|
|
23285
|
+
});
|
|
23286
|
+
}
|
|
23287
|
+
//#endregion
|
|
23288
|
+
//#region lib/client/components/SkillFiles.js
|
|
23289
|
+
/**
|
|
23290
|
+
* Files tab body, shared by the two detail pages.
|
|
23291
|
+
*
|
|
23292
|
+
* The market page lists provider file metadata and the local page lists the
|
|
23293
|
+
* bytes on disk, but both are the same control: a picker on the left, one
|
|
23294
|
+
* document on the right, and a copy button that confirms itself. The only
|
|
23295
|
+
* difference the caller keeps is how a selection is fetched.
|
|
23296
|
+
*
|
|
23297
|
+
* A markdown document opens *rendered*: SKILL.md is prose meant to be read, and
|
|
23298
|
+
* showing its source first answers a question nobody asked. Installing still has
|
|
23299
|
+
* to be a trust decision, though, so the raw bytes are one toggle away — with
|
|
23300
|
+
* line numbers and the copy button pointed at exactly what is on disk. Other
|
|
23301
|
+
* files have no rendered form and stay source-only.
|
|
23302
|
+
*/
|
|
23303
|
+
/** How long the copy button stays in its confirmed state. */
|
|
23304
|
+
const COPIED_RESET_MS$1 = 2e3;
|
|
23305
|
+
/**
|
|
23306
|
+
* A rendered markdown document: its metadata as structure, its body as prose.
|
|
23307
|
+
*
|
|
23308
|
+
* The split matters more than it looks: handed to the renderer, the closing
|
|
23309
|
+
* `---` of the frontmatter turns the whole block into one setext heading.
|
|
23310
|
+
*/
|
|
23311
|
+
function MarkdownFilePreview({ content }) {
|
|
23312
|
+
const split = (0, react.useMemo)(() => splitSkillFrontmatter(content), [content]);
|
|
23313
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
23314
|
+
className: detailStyles.markdown,
|
|
23315
|
+
children: [split.frontmatter !== null && (0, react_jsx_runtime.jsx)("div", {
|
|
23316
|
+
className: detailStyles.frontmatter,
|
|
23317
|
+
children: (0, react_jsx_runtime.jsx)(FrontmatterPanel, { data: split.frontmatter })
|
|
23318
|
+
}), (0, react_jsx_runtime.jsx)(MarkdownView, { content: split.body })]
|
|
23319
|
+
});
|
|
23320
|
+
}
|
|
23321
|
+
function SkillFiles(props) {
|
|
23322
|
+
const { files, selected, loading, error = null, onSelect } = props;
|
|
23323
|
+
const t = useT();
|
|
23324
|
+
const [copied, setCopied] = (0, react.useState)(false);
|
|
23325
|
+
const [mode, setMode] = (0, react.useState)("preview");
|
|
23326
|
+
const resetTimer = (0, react.useRef)(void 0);
|
|
23327
|
+
(0, react.useEffect)(() => () => {
|
|
23328
|
+
if (resetTimer.current !== void 0) clearTimeout(resetTimer.current);
|
|
23329
|
+
}, []);
|
|
23330
|
+
const selectedPath = selected?.path ?? null;
|
|
23331
|
+
/** Only markdown has a rendered form; a toggle on a script or a JSON file would be a lie. */
|
|
23332
|
+
const renderable = selected !== null && selected.language === "markdown";
|
|
23333
|
+
(0, react.useEffect)(() => {
|
|
23334
|
+
setMode("preview");
|
|
23335
|
+
}, [selectedPath]);
|
|
23336
|
+
const copyFile = (0, react.useCallback)(() => {
|
|
23337
|
+
const content = selected?.content;
|
|
23338
|
+
if (content === void 0) return;
|
|
23339
|
+
(0, _deepseek_ai_dsh_client_ui_primitives.writeClipboard)(content).then((ok) => {
|
|
23340
|
+
if (!ok) return;
|
|
23341
|
+
setCopied(true);
|
|
23342
|
+
if (resetTimer.current !== void 0) clearTimeout(resetTimer.current);
|
|
23343
|
+
resetTimer.current = setTimeout(() => setCopied(false), COPIED_RESET_MS$1);
|
|
23344
|
+
});
|
|
23345
|
+
}, [selected]);
|
|
23346
|
+
if (files.length === 0) return loading ? (0, react_jsx_runtime.jsxs)("p", {
|
|
23347
|
+
className: detailStyles.muted,
|
|
23348
|
+
role: "status",
|
|
23349
|
+
"aria-live": "polite",
|
|
23350
|
+
children: [
|
|
23351
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, {
|
|
23352
|
+
state: "ongoing",
|
|
23353
|
+
size: 14
|
|
23354
|
+
}),
|
|
23355
|
+
" ",
|
|
23356
|
+
t("loading")
|
|
23357
|
+
]
|
|
23358
|
+
}) : (0, react_jsx_runtime.jsx)("p", {
|
|
23359
|
+
className: detailStyles.muted,
|
|
23360
|
+
children: t("noFiles")
|
|
23361
|
+
});
|
|
23362
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
23363
|
+
className: detailStyles.files,
|
|
23364
|
+
children: [(0, react_jsx_runtime.jsx)("ul", {
|
|
23365
|
+
className: detailStyles.fileList,
|
|
23366
|
+
children: files.map((meta) => {
|
|
23367
|
+
const active = meta.path === selectedPath;
|
|
23368
|
+
return (0, react_jsx_runtime.jsx)("li", { children: (0, react_jsx_runtime.jsxs)("button", {
|
|
23369
|
+
type: "button",
|
|
23370
|
+
className: `${detailStyles.fileItem} ${active ? detailStyles.fileItemActive : ""}`,
|
|
23371
|
+
"aria-current": active ? "true" : void 0,
|
|
23372
|
+
onClick: () => onSelect(meta.path),
|
|
23373
|
+
children: [
|
|
23374
|
+
(0, react_jsx_runtime.jsx)(FileIcon, {
|
|
23375
|
+
size: 14,
|
|
23376
|
+
className: detailStyles.fileIcon
|
|
23377
|
+
}),
|
|
23378
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
23379
|
+
className: detailStyles.filePath,
|
|
23380
|
+
title: meta.path,
|
|
23381
|
+
children: meta.path
|
|
23382
|
+
}),
|
|
23383
|
+
meta.tooBig === true && (0, react_jsx_runtime.jsx)("span", {
|
|
23384
|
+
className: detailStyles.fileFlag,
|
|
23385
|
+
children: t("fileTooLarge")
|
|
23386
|
+
}),
|
|
23387
|
+
(0, react_jsx_runtime.jsxs)("span", {
|
|
23388
|
+
className: detailStyles.fileMeta,
|
|
23389
|
+
children: [
|
|
23390
|
+
(0, _deepseek_ai_dsh_client_ui_primitives.fileSizeText)(meta.size),
|
|
23391
|
+
" · ",
|
|
23392
|
+
meta.language
|
|
23393
|
+
]
|
|
23394
|
+
})
|
|
23395
|
+
]
|
|
23396
|
+
}) }, meta.path);
|
|
23397
|
+
})
|
|
23398
|
+
}), (0, react_jsx_runtime.jsx)("div", {
|
|
23399
|
+
className: detailStyles.fileView,
|
|
23400
|
+
children: error !== null ? (0, react_jsx_runtime.jsxs)("p", {
|
|
23401
|
+
className: detailStyles.muted,
|
|
23402
|
+
role: "alert",
|
|
23403
|
+
children: [
|
|
23404
|
+
(0, react_jsx_runtime.jsx)(AlertIcon, { size: 14 }),
|
|
23405
|
+
" ",
|
|
23406
|
+
error
|
|
23407
|
+
]
|
|
23408
|
+
}) : selected === null ? loading ? (0, react_jsx_runtime.jsxs)("p", {
|
|
23409
|
+
className: detailStyles.muted,
|
|
23410
|
+
role: "status",
|
|
23411
|
+
"aria-live": "polite",
|
|
23412
|
+
children: [
|
|
23413
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, {
|
|
23414
|
+
state: "ongoing",
|
|
23415
|
+
size: 14
|
|
23416
|
+
}),
|
|
23417
|
+
" ",
|
|
23418
|
+
t("loading")
|
|
23419
|
+
]
|
|
23420
|
+
}) : null : (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
23421
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
23422
|
+
className: detailStyles.fileBar,
|
|
23423
|
+
children: [
|
|
23424
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
23425
|
+
className: detailStyles.fileBarPath,
|
|
23426
|
+
title: selected.path,
|
|
23427
|
+
children: selected.path
|
|
23428
|
+
}),
|
|
23429
|
+
renderable && (0, react_jsx_runtime.jsxs)("div", {
|
|
23430
|
+
className: detailStyles.viewToggle,
|
|
23431
|
+
role: "group",
|
|
23432
|
+
"aria-label": t("fileView"),
|
|
23433
|
+
children: [(0, react_jsx_runtime.jsx)("button", {
|
|
23434
|
+
type: "button",
|
|
23435
|
+
"aria-pressed": mode === "preview",
|
|
23436
|
+
onClick: () => setMode("preview"),
|
|
23437
|
+
children: t("filePreview")
|
|
23438
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
23439
|
+
type: "button",
|
|
23440
|
+
"aria-pressed": mode === "source",
|
|
23441
|
+
onClick: () => setMode("source"),
|
|
23442
|
+
children: t("fileSource")
|
|
23443
|
+
})]
|
|
23444
|
+
}),
|
|
23445
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
23446
|
+
variant: "outline",
|
|
23447
|
+
size: "sm",
|
|
23448
|
+
className: detailStyles.copy,
|
|
23449
|
+
"aria-label": copied ? t("copied") : t("copy"),
|
|
23450
|
+
onClick: copyFile,
|
|
23451
|
+
icon: copied ? (0, react_jsx_runtime.jsx)(CheckIcon, { size: 14 }) : (0, react_jsx_runtime.jsx)(CopyIcon, { size: 14 }),
|
|
23452
|
+
children: copied ? t("copied") : t("copy")
|
|
23453
|
+
})
|
|
23454
|
+
]
|
|
23455
|
+
}),
|
|
23456
|
+
selected.truncated && (0, react_jsx_runtime.jsx)("p", {
|
|
23457
|
+
className: detailStyles.muted,
|
|
23458
|
+
children: t("fileTruncated")
|
|
23459
|
+
}),
|
|
23460
|
+
renderable && mode === "preview" ? (0, react_jsx_runtime.jsx)(MarkdownFilePreview, { content: selected.content }) : (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.CodeBlock, {
|
|
23461
|
+
code: selected.content,
|
|
23462
|
+
lang: selected.language,
|
|
23463
|
+
showHeader: false,
|
|
23464
|
+
lineNumbers: true,
|
|
23465
|
+
copyLabel: t("copy"),
|
|
23466
|
+
copiedLabel: t("copied")
|
|
23467
|
+
}),
|
|
23468
|
+
(0, react_jsx_runtime.jsx)("p", {
|
|
23469
|
+
className: detailStyles.srOnly,
|
|
23470
|
+
role: "status",
|
|
23471
|
+
"aria-live": "polite",
|
|
23472
|
+
children: copied ? t("copied") : ""
|
|
23473
|
+
})
|
|
23474
|
+
] })
|
|
23475
|
+
})]
|
|
23476
|
+
});
|
|
23477
|
+
}
|
|
23478
|
+
//#endregion
|
|
23479
|
+
//#region \0dsh-css:src/client/components/InstalledSkillDetail.module.css.mjs
|
|
23480
|
+
const css$9 = "._4PqW5a_chip{box-sizing:border-box;background:var(--dsw-alias-bg-layer-2,#f0f2f5);height:20px;color:var(--dsw-alias-label-secondary,#61666b);white-space:nowrap;border-radius:999px;flex:none;align-items:center;padding:0 7px;font-size:11px;font-weight:600;line-height:18px;display:inline-flex}._4PqW5a_chipOn{background:color-mix(in srgb, var(--dsw-alias-state-business-primary,#4176e6) 12%, transparent);color:var(--dsw-alias-state-business-primary,#4176e6)}._4PqW5a_pathRow{border:1px solid var(--dsw-alias-border-l2,#0000001a);border-radius:var(--dsw-radius-sm,8px);background:var(--dsw-alias-bg-layer-1,#fff);align-items:center;gap:8px;min-width:0;padding:7px 10px;display:flex}._4PqW5a_pathIcon{color:var(--dsw-alias-label-tertiary,#81858c);flex:none}._4PqW5a_pathText{min-width:0;color:var(--dsw-alias-label-secondary,#61666b);overflow-wrap:anywhere;flex:1;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11.5px;line-height:18px}._4PqW5a_pathCopy{flex:none}._4PqW5a_provenance{border-top:1px solid var(--dsw-alias-border-l1,#0000000a);color:var(--dsw-alias-label-tertiary,#81858c);margin:0;padding:12px 14px 14px;font-size:12px;line-height:18px}._4PqW5a_failure{flex-direction:column;align-items:flex-start;gap:8px;display:flex}._4PqW5a_failureTitle{margin:0;font-size:13.5px;font-weight:600}._4PqW5a_failureText{color:var(--dsw-alias-label-secondary,#61666b);overflow-wrap:anywhere;margin:0;font-size:12.5px;line-height:19px}";
|
|
23481
|
+
const tagId$9 = "@nanmicoder/dsh-skills-hub/InstalledSkillDetail.module.css";
|
|
23482
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$9) + "]") === null) {
|
|
23483
|
+
const tag = document.createElement("style");
|
|
23484
|
+
tag.dataset.plugin = "@nanmicoder/dsh-skills-hub";
|
|
23485
|
+
tag.dataset.pluginCss = tagId$9;
|
|
23486
|
+
tag.textContent = css$9;
|
|
23487
|
+
document.head.appendChild(tag);
|
|
23488
|
+
}
|
|
23489
|
+
var InstalledSkillDetail_module_css_default = {
|
|
23490
|
+
"chip": "_4PqW5a_chip",
|
|
23491
|
+
"chipOn": "_4PqW5a_chipOn",
|
|
23492
|
+
"failure": "_4PqW5a_failure",
|
|
23493
|
+
"failureText": "_4PqW5a_failureText",
|
|
23494
|
+
"failureTitle": "_4PqW5a_failureTitle",
|
|
23495
|
+
"pathCopy": "_4PqW5a_pathCopy",
|
|
23496
|
+
"pathIcon": "_4PqW5a_pathIcon",
|
|
23497
|
+
"pathRow": "_4PqW5a_pathRow",
|
|
23498
|
+
"pathText": "_4PqW5a_pathText",
|
|
23499
|
+
"provenance": "_4PqW5a_provenance"
|
|
23500
|
+
};
|
|
23501
|
+
//#endregion
|
|
23502
|
+
//#region lib/client/components/InstalledSkillDetail.js
|
|
23503
|
+
/**
|
|
23504
|
+
* Installed skill detail page.
|
|
23505
|
+
*
|
|
23506
|
+
* The list's "view" affordance used to open a 380px confirmation-style modal,
|
|
23507
|
+
* which is the wrong container for a document: SKILL.md is a page of prose with
|
|
23508
|
+
* tables and code, and the marketplace already answers the same question with a
|
|
23509
|
+
* full-panel page. This page is that same reading surface (`SkillDetailShell`),
|
|
23510
|
+
* fed by the filesystem instead of a provider.
|
|
23511
|
+
*
|
|
23512
|
+
* What it deliberately does *not* do is pretend a local skill is a market
|
|
23513
|
+
* entry. There is no author, download count or security report to show, so
|
|
23514
|
+
* those rows are absent rather than zero. Where the two identities do meet —
|
|
23515
|
+
* a skill installed from a market, proven by its `.skills-hub.json` sidecar —
|
|
23516
|
+
* the page links to the market detail instead of duplicating it, which keeps
|
|
23517
|
+
* upstream data upstream and this page readable with the network down.
|
|
23518
|
+
*/
|
|
23519
|
+
/** How long the path copy button stays in its confirmed state. */
|
|
23520
|
+
const COPIED_RESET_MS = 2e3;
|
|
23521
|
+
/** Reader-facing text for anything thrown by the client. */
|
|
23522
|
+
function messageOf$1(reason) {
|
|
23523
|
+
return reason instanceof Error ? reason.message : String(reason);
|
|
23524
|
+
}
|
|
23525
|
+
/** Local install timestamps are ISO strings; an unparseable one renders as nothing. */
|
|
23526
|
+
function formatInstalledAt(value) {
|
|
23527
|
+
const date = new Date(value);
|
|
23528
|
+
return Number.isNaN(date.getTime()) ? "" : date.toLocaleString();
|
|
23529
|
+
}
|
|
23530
|
+
function InstalledSkillDetail(props) {
|
|
23531
|
+
const { item } = props;
|
|
23532
|
+
const t = useT();
|
|
23533
|
+
const [detail, setDetail] = (0, react.useState)(null);
|
|
23534
|
+
const [error, setError] = (0, react.useState)(null);
|
|
23535
|
+
const [revision, setRevision] = (0, react.useState)(0);
|
|
23536
|
+
const [tab, setTab] = (0, react.useState)("overview");
|
|
23537
|
+
const [selectedPath, setSelectedPath] = (0, react.useState)(null);
|
|
23538
|
+
const [file, setFile] = (0, react.useState)(null);
|
|
23539
|
+
const [fileLoading, setFileLoading] = (0, react.useState)(false);
|
|
23540
|
+
const [fileError, setFileError] = (0, react.useState)(null);
|
|
23541
|
+
const [copied, setCopied] = (0, react.useState)(false);
|
|
23542
|
+
const resetTimer = (0, react.useRef)(void 0);
|
|
23543
|
+
(0, react.useEffect)(() => {
|
|
23544
|
+
const abort = new AbortController();
|
|
23545
|
+
setError(null);
|
|
23546
|
+
fetchInstalledDetail(item.key, abort.signal).then((result) => {
|
|
23547
|
+
if (!abort.signal.aborted) setDetail(result);
|
|
23548
|
+
}).catch((reason) => {
|
|
23549
|
+
if (!abort.signal.aborted && !isAbortError(reason)) setError(messageOf$1(reason));
|
|
23550
|
+
});
|
|
23551
|
+
return () => abort.abort();
|
|
23552
|
+
}, [item.key, revision]);
|
|
23553
|
+
(0, react.useEffect)(() => {
|
|
23554
|
+
if (tab !== "files" || detail === null || selectedPath !== null) return;
|
|
23555
|
+
const preferred = detail.files.find((entry) => entry.path.toLowerCase() === "skill.md") ?? detail.files[0];
|
|
23556
|
+
if (preferred !== void 0) setSelectedPath(preferred.path);
|
|
23557
|
+
}, [
|
|
23558
|
+
tab,
|
|
23559
|
+
detail,
|
|
23560
|
+
selectedPath
|
|
23561
|
+
]);
|
|
23562
|
+
(0, react.useEffect)(() => {
|
|
23563
|
+
if (selectedPath === null) return;
|
|
23564
|
+
const abort = new AbortController();
|
|
23565
|
+
setFileLoading(true);
|
|
23566
|
+
setFileError(null);
|
|
23567
|
+
fetchInstalledFile(item.key, selectedPath, abort.signal).then((result) => {
|
|
23568
|
+
if (!abort.signal.aborted) setFile(result);
|
|
23569
|
+
}).catch((reason) => {
|
|
23570
|
+
if (abort.signal.aborted || isAbortError(reason)) return;
|
|
23571
|
+
setFile(null);
|
|
23572
|
+
setFileError(messageOf$1(reason));
|
|
23573
|
+
}).finally(() => {
|
|
23574
|
+
if (!abort.signal.aborted) setFileLoading(false);
|
|
23575
|
+
});
|
|
23576
|
+
return () => abort.abort();
|
|
23577
|
+
}, [item.key, selectedPath]);
|
|
23578
|
+
(0, react.useEffect)(() => () => {
|
|
23579
|
+
if (resetTimer.current !== void 0) clearTimeout(resetTimer.current);
|
|
23580
|
+
}, []);
|
|
23581
|
+
const copyPath = () => {
|
|
23582
|
+
(0, _deepseek_ai_dsh_client_ui_primitives.writeClipboard)(item.dirPath).then((ok) => {
|
|
23583
|
+
if (!ok) return;
|
|
23584
|
+
setCopied(true);
|
|
23585
|
+
if (resetTimer.current !== void 0) clearTimeout(resetTimer.current);
|
|
23586
|
+
resetTimer.current = setTimeout(() => setCopied(false), COPIED_RESET_MS);
|
|
23587
|
+
});
|
|
23588
|
+
};
|
|
23589
|
+
const marketId = item.managed && item.source !== "local" ? item.id : null;
|
|
23590
|
+
const body = detail === null ? "" : stripSkillFrontmatter(detail.markdown);
|
|
23591
|
+
const metadata = (0, react.useMemo)(() => detail?.frontmatter == null ? null : parseSkillFrontmatter(detail.frontmatter), [detail]);
|
|
23592
|
+
const baseName = item.dirPath.split(/[\\/]/).pop() ?? item.dirName;
|
|
23593
|
+
const flatSkill = detail !== null && detail.files.length === 1 && detail.files[0]?.path === baseName;
|
|
23594
|
+
const installedAt = item.installedAt === void 0 ? "" : formatInstalledAt(item.installedAt);
|
|
23595
|
+
return (0, react_jsx_runtime.jsx)(SkillDetailShell, {
|
|
23596
|
+
focusKey: item.key,
|
|
23597
|
+
onBack: props.onBack,
|
|
23598
|
+
backLabel: t("back"),
|
|
23599
|
+
avatar: (0, react_jsx_runtime.jsx)(SkillAvatar, {
|
|
23600
|
+
name: item.name,
|
|
23601
|
+
source: item.source,
|
|
23602
|
+
size: 84
|
|
23603
|
+
}),
|
|
23604
|
+
eyebrow: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("span", {
|
|
23605
|
+
className: detailStyles.source,
|
|
23606
|
+
children: t(`source.${item.source}`)
|
|
23607
|
+
}), item.version !== void 0 && item.version !== "" && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("span", {
|
|
23608
|
+
"aria-hidden": "true",
|
|
23609
|
+
children: "·"
|
|
23610
|
+
}), (0, react_jsx_runtime.jsxs)("span", {
|
|
23611
|
+
className: detailStyles.version,
|
|
23612
|
+
children: ["v", item.version]
|
|
23613
|
+
})] })] }),
|
|
23614
|
+
name: item.name,
|
|
23615
|
+
badges: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("span", {
|
|
23616
|
+
className: `${InstalledSkillDetail_module_css_default.chip} ${item.managed ? InstalledSkillDetail_module_css_default.chipOn : ""}`,
|
|
23617
|
+
children: item.managed ? t("managedByHub") : t("unmanagedSkill")
|
|
23618
|
+
}), item.linked && (0, react_jsx_runtime.jsx)("span", {
|
|
23619
|
+
className: InstalledSkillDetail_module_css_default.chip,
|
|
23620
|
+
children: t("symlinkSkill")
|
|
23621
|
+
})] }),
|
|
23622
|
+
summary: item.summary,
|
|
23623
|
+
notice: (0, react_jsx_runtime.jsxs)("div", {
|
|
23624
|
+
className: InstalledSkillDetail_module_css_default.pathRow,
|
|
23625
|
+
children: [
|
|
23626
|
+
(0, react_jsx_runtime.jsx)(FolderIcon, {
|
|
23627
|
+
size: 14,
|
|
23628
|
+
className: InstalledSkillDetail_module_css_default.pathIcon
|
|
23629
|
+
}),
|
|
23630
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
23631
|
+
className: InstalledSkillDetail_module_css_default.pathText,
|
|
23632
|
+
title: item.dirPath,
|
|
23633
|
+
children: item.dirPath
|
|
23634
|
+
}),
|
|
23635
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
23636
|
+
variant: "ghost",
|
|
23637
|
+
size: "sm",
|
|
23638
|
+
className: InstalledSkillDetail_module_css_default.pathCopy,
|
|
23639
|
+
"aria-label": copied ? t("copiedPath") : t("copyPath"),
|
|
23640
|
+
onClick: copyPath,
|
|
23641
|
+
icon: copied ? (0, react_jsx_runtime.jsx)(CheckIcon, { size: 14 }) : (0, react_jsx_runtime.jsx)(CopyIcon, { size: 14 }),
|
|
23642
|
+
children: copied ? t("copiedPath") : t("copyPath")
|
|
23643
|
+
})
|
|
23644
|
+
]
|
|
23645
|
+
}),
|
|
23646
|
+
overviewLabel: t("overview"),
|
|
23647
|
+
filesLabel: `${t("files")} (${detail?.files.length ?? item.fileCount})`,
|
|
23648
|
+
activeTab: tab,
|
|
23649
|
+
onTabChange: setTab,
|
|
23650
|
+
overview: error !== null ? (0, react_jsx_runtime.jsxs)("div", {
|
|
23651
|
+
className: InstalledSkillDetail_module_css_default.failure,
|
|
23652
|
+
role: "alert",
|
|
23653
|
+
children: [
|
|
23654
|
+
(0, react_jsx_runtime.jsx)("p", {
|
|
23655
|
+
className: InstalledSkillDetail_module_css_default.failureTitle,
|
|
23656
|
+
children: t("installedReadFailed")
|
|
23657
|
+
}),
|
|
23658
|
+
(0, react_jsx_runtime.jsx)("p", {
|
|
23659
|
+
className: InstalledSkillDetail_module_css_default.failureText,
|
|
23660
|
+
children: error
|
|
23661
|
+
}),
|
|
23662
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
23663
|
+
variant: "outline",
|
|
23664
|
+
size: "sm",
|
|
23665
|
+
onClick: () => setRevision((value) => value + 1),
|
|
23666
|
+
children: t("retry")
|
|
23667
|
+
})
|
|
23668
|
+
]
|
|
23669
|
+
}) : detail === null ? (0, react_jsx_runtime.jsxs)("p", {
|
|
23670
|
+
className: detailStyles.muted,
|
|
23671
|
+
role: "status",
|
|
23672
|
+
"aria-live": "polite",
|
|
23673
|
+
children: [
|
|
23674
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, {
|
|
23675
|
+
state: "ongoing",
|
|
23676
|
+
size: 14
|
|
23677
|
+
}),
|
|
23678
|
+
" ",
|
|
23679
|
+
t("loading")
|
|
23680
|
+
]
|
|
23681
|
+
}) : (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [metadata !== null && (0, react_jsx_runtime.jsx)("div", {
|
|
23682
|
+
className: detailStyles.frontmatter,
|
|
23683
|
+
children: (0, react_jsx_runtime.jsx)(FrontmatterPanel, { data: metadata })
|
|
23684
|
+
}), body.trim() === "" ? (0, react_jsx_runtime.jsx)("p", {
|
|
23685
|
+
className: detailStyles.muted,
|
|
23686
|
+
children: t("emptyDocument")
|
|
23687
|
+
}) : (0, react_jsx_runtime.jsx)(MarkdownView, { content: body })] }),
|
|
23688
|
+
files: error !== null ? (0, react_jsx_runtime.jsx)("p", {
|
|
23689
|
+
className: detailStyles.muted,
|
|
23690
|
+
role: "alert",
|
|
23691
|
+
children: t("installedReadFailed")
|
|
23692
|
+
}) : (0, react_jsx_runtime.jsx)(SkillFiles, {
|
|
23693
|
+
files: detail?.files ?? [],
|
|
23694
|
+
selected: file,
|
|
23695
|
+
loading: fileLoading || detail === null,
|
|
23696
|
+
error: fileError,
|
|
23697
|
+
onSelect: (path) => {
|
|
23698
|
+
setFile(null);
|
|
23699
|
+
setSelectedPath(path);
|
|
23700
|
+
}
|
|
23701
|
+
}),
|
|
23702
|
+
rail: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
23703
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
23704
|
+
className: detailStyles.railAction,
|
|
23705
|
+
children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
23706
|
+
variant: "outline",
|
|
23707
|
+
size: "md",
|
|
23708
|
+
disabled: !item.removable,
|
|
23709
|
+
title: item.removable ? t("uninstallConfirm") : t("uninstallDisabled"),
|
|
23710
|
+
icon: (0, react_jsx_runtime.jsx)(TrashIcon, { size: 16 }),
|
|
23711
|
+
onClick: () => props.onUninstall(item),
|
|
23712
|
+
children: t("uninstall")
|
|
23713
|
+
}), marketId !== null && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
23714
|
+
variant: "ghost",
|
|
23715
|
+
size: "md",
|
|
23716
|
+
icon: (0, react_jsx_runtime.jsx)(ExternalLinkIcon, { size: 16 }),
|
|
23717
|
+
onClick: () => props.onOpenMarket(marketId),
|
|
23718
|
+
children: t("openInMarket")
|
|
23719
|
+
})]
|
|
23720
|
+
}),
|
|
23721
|
+
(0, react_jsx_runtime.jsxs)("dl", {
|
|
23722
|
+
className: detailStyles.facts,
|
|
23723
|
+
children: [
|
|
23724
|
+
(0, react_jsx_runtime.jsx)(Fact, {
|
|
23725
|
+
label: t("sourceLabel"),
|
|
23726
|
+
children: t(`source.${item.source}`)
|
|
23727
|
+
}),
|
|
23728
|
+
item.version !== void 0 && item.version !== "" && (0, react_jsx_runtime.jsxs)(Fact, {
|
|
23729
|
+
label: t("version"),
|
|
23730
|
+
children: ["v", item.version]
|
|
23731
|
+
}),
|
|
23732
|
+
installedAt !== "" && (0, react_jsx_runtime.jsx)(Fact, {
|
|
23733
|
+
label: t("installedAtLabel"),
|
|
23734
|
+
children: installedAt
|
|
23735
|
+
}),
|
|
23736
|
+
detail !== null && (0, react_jsx_runtime.jsx)(Fact, {
|
|
23737
|
+
label: t("skillKind"),
|
|
23738
|
+
children: flatSkill ? t("flatSkill") : t("bundleSkill")
|
|
23739
|
+
}),
|
|
23740
|
+
(0, react_jsx_runtime.jsxs)(Fact, {
|
|
23741
|
+
label: t("files"),
|
|
23742
|
+
children: [
|
|
23743
|
+
t("filesCount", { count: item.fileCount }),
|
|
23744
|
+
" · ",
|
|
23745
|
+
(0, _deepseek_ai_dsh_client_ui_primitives.fileSizeText)(item.bytes)
|
|
23746
|
+
]
|
|
23747
|
+
})
|
|
23748
|
+
]
|
|
23749
|
+
}),
|
|
23750
|
+
marketId !== null && (0, react_jsx_runtime.jsx)("p", {
|
|
23751
|
+
className: InstalledSkillDetail_module_css_default.provenance,
|
|
23752
|
+
children: t("marketProvenance")
|
|
23753
|
+
})
|
|
23754
|
+
] })
|
|
23755
|
+
});
|
|
23756
|
+
}
|
|
23757
|
+
//#endregion
|
|
23758
|
+
//#region \0dsh-css:src/client/components/InstalledSkills.module.css.mjs
|
|
23759
|
+
const css$8 = ".eYwU2a_root{box-sizing:border-box;width:100%;max-width:1280px;margin:0 auto;padding:28px}.eYwU2a_header{justify-content:space-between;align-items:flex-start;gap:20px;display:flex}.eYwU2a_header h1{margin:0;font-size:24px}.eYwU2a_header p{max-width:760px;color:var(--dsw-alias-label-secondary,#61666b);margin:8px 0 0;font-size:13px;line-height:1.6}.eYwU2a_search{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2,#0000001a);border-radius:var(--dsw-radius-sm,8px);background:var(--dsw-alias-bg-base,#fff);width:100%;color:inherit;font:inherit;margin-top:20px;padding:10px 12px}.eYwU2a_search:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#4176e6);outline-offset:1px}.eYwU2a_list{gap:12px;display:grid}.eYwU2a_card{border:1px solid var(--dsw-alias-border-l2,#0000001a);border-radius:var(--dsw-radius-md,12px);justify-content:space-between;align-items:center;gap:20px;padding:18px;display:flex}.eYwU2a_info{min-width:0}.eYwU2a_name{color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;margin:0 10px 0 0;padding:0;font-weight:600}.eYwU2a_name:hover{text-decoration:underline}.eYwU2a_name:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#4176e6);outline-offset:2px;border-radius:var(--dsw-radius-xs,4px)}.eYwU2a_badge{border-radius:var(--dsw-radius-xs,4px);background:var(--dsw-alias-bg-layer-2,#f0f2f5);margin:2px 6px 2px 0;padding:2px 6px;font-size:11px;display:inline-block}.eYwU2a_version,.eYwU2a_count{color:var(--dsw-alias-label-secondary,#61666b);font-size:12px}.eYwU2a_count{margin:16px 0 12px}.eYwU2a_summary{margin:8px 0;font-size:13px;line-height:1.6}.eYwU2a_path{color:var(--dsw-alias-label-secondary,#61666b);overflow-wrap:anywhere;margin:0;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;line-height:1.7}.eYwU2a_actions{flex-shrink:0;gap:8px;display:flex}.eYwU2a_empty{color:var(--dsw-alias-label-secondary,#61666b);text-align:center;padding:50px 10px}.eYwU2a_error{color:var(--dsw-alias-state-error-primary,#ec1313)}.eYwU2a_skeleton{border-radius:var(--dsw-radius-md,12px);background:var(--dsw-alias-bg-layer-2,#f0f2f5);height:108px;margin-top:10px;animation:1.5s ease-in-out infinite eYwU2a_pulse}@keyframes eYwU2a_pulse{50%{opacity:.45}}@media (prefers-reduced-motion:reduce){.eYwU2a_skeleton{animation:none}}@media (width<=640px){.eYwU2a_root{padding:18px}.eYwU2a_card{flex-direction:column;align-items:stretch}.eYwU2a_actions{justify-content:flex-end}}";
|
|
23760
|
+
const tagId$8 = "@nanmicoder/dsh-skills-hub/InstalledSkills.module.css";
|
|
23761
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$8) + "]") === null) {
|
|
23762
|
+
const tag = document.createElement("style");
|
|
23763
|
+
tag.dataset.plugin = "@nanmicoder/dsh-skills-hub";
|
|
23764
|
+
tag.dataset.pluginCss = tagId$8;
|
|
23765
|
+
tag.textContent = css$8;
|
|
23766
|
+
document.head.appendChild(tag);
|
|
23767
|
+
}
|
|
23768
|
+
var InstalledSkills_module_css_default = {
|
|
23769
|
+
"actions": "eYwU2a_actions",
|
|
23770
|
+
"badge": "eYwU2a_badge",
|
|
23771
|
+
"card": "eYwU2a_card",
|
|
23772
|
+
"count": "eYwU2a_count",
|
|
23773
|
+
"empty": "eYwU2a_empty",
|
|
23774
|
+
"error": "eYwU2a_error",
|
|
23775
|
+
"header": "eYwU2a_header",
|
|
23776
|
+
"info": "eYwU2a_info",
|
|
23777
|
+
"list": "eYwU2a_list",
|
|
23778
|
+
"name": "eYwU2a_name",
|
|
23779
|
+
"path": "eYwU2a_path",
|
|
23780
|
+
"pulse": "eYwU2a_pulse",
|
|
23781
|
+
"root": "eYwU2a_root",
|
|
23782
|
+
"search": "eYwU2a_search",
|
|
23783
|
+
"skeleton": "eYwU2a_skeleton",
|
|
23784
|
+
"summary": "eYwU2a_summary",
|
|
23785
|
+
"version": "eYwU2a_version"
|
|
23786
|
+
};
|
|
23787
|
+
//#endregion
|
|
22600
23788
|
//#region lib/client/components/InstalledSkills.js
|
|
22601
|
-
|
|
23789
|
+
/**
|
|
23790
|
+
* Installed skills: the local inventory and its reading page.
|
|
23791
|
+
*
|
|
23792
|
+
* Two views, one component. The detail page replaces the list *inside* this
|
|
23793
|
+
* component rather than in `MarketPage`, so the list is never unmounted: the
|
|
23794
|
+
* search query, the loaded rows and the scroll position are all still there when
|
|
23795
|
+
* the reader comes back. That is also why the selected row is stored as a key
|
|
23796
|
+
* and resolved against the current rows — a refresh that drops the entry closes
|
|
23797
|
+
* the page instead of leaving it pointing at something that no longer exists.
|
|
23798
|
+
*
|
|
23799
|
+
* Removal stays a confirmation modal on purpose: it is a short yes/no about a
|
|
23800
|
+
* destructive action, which is exactly what the primitive's dialog is for. The
|
|
23801
|
+
* skill's *content* is not, which is why it moved to a page.
|
|
23802
|
+
*/
|
|
23803
|
+
/** Reader-facing text for anything thrown by the client. */
|
|
23804
|
+
function messageOf(reason) {
|
|
23805
|
+
return reason instanceof Error ? reason.message : String(reason);
|
|
23806
|
+
}
|
|
23807
|
+
function InstalledSkills(props) {
|
|
22602
23808
|
const t = useT();
|
|
22603
23809
|
const [items, setItems] = (0, react.useState)([]);
|
|
22604
23810
|
const [loading, setLoading] = (0, react.useState)(true);
|
|
22605
23811
|
const [error, setError] = (0, react.useState)(null);
|
|
22606
23812
|
const [query, setQuery] = (0, react.useState)("");
|
|
22607
23813
|
const [revision, setRevision] = (0, react.useState)(0);
|
|
22608
|
-
const [
|
|
22609
|
-
const [markdown, setMarkdown] = (0, react.useState)(null);
|
|
22610
|
-
const [detailError, setDetailError] = (0, react.useState)(null);
|
|
23814
|
+
const [selectedKey, setSelectedKey] = (0, react.useState)(null);
|
|
22611
23815
|
const [pending, setPending] = (0, react.useState)(null);
|
|
22612
23816
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
23817
|
+
const [returnFocusKey, setReturnFocusKey] = (0, react.useState)(null);
|
|
23818
|
+
/** Row buttons by skill key, so the detail page can hand focus back. */
|
|
23819
|
+
const rowButtons = (0, react.useRef)(/* @__PURE__ */ new Map());
|
|
22613
23820
|
(0, react.useEffect)(() => {
|
|
22614
23821
|
const abort = new AbortController();
|
|
22615
23822
|
setLoading(true);
|
|
@@ -22617,44 +23824,46 @@ function(property) {
|
|
|
22617
23824
|
fetchInstalled(abort.signal).then((result) => {
|
|
22618
23825
|
if (!abort.signal.aborted) setItems(result);
|
|
22619
23826
|
}).catch((reason) => {
|
|
22620
|
-
if (!abort.signal.aborted && !isAbortError(reason)) setError(
|
|
23827
|
+
if (!abort.signal.aborted && !isAbortError(reason)) setError(messageOf(reason));
|
|
22621
23828
|
}).finally(() => {
|
|
22622
23829
|
if (!abort.signal.aborted) setLoading(false);
|
|
22623
23830
|
});
|
|
22624
23831
|
return () => abort.abort();
|
|
22625
23832
|
}, [revision]);
|
|
22626
|
-
|
|
22627
|
-
|
|
22628
|
-
setDetailError(null);
|
|
22629
|
-
if (!selected) return;
|
|
22630
|
-
const abort = new AbortController();
|
|
22631
|
-
fetchInstalledDetail(selected.key, abort.signal).then((result) => {
|
|
22632
|
-
if (!abort.signal.aborted) setMarkdown(stripSkillFrontmatter(result.markdown));
|
|
22633
|
-
}).catch((reason) => {
|
|
22634
|
-
if (!abort.signal.aborted && !isAbortError(reason)) setDetailError(String(reason.message ?? reason));
|
|
22635
|
-
});
|
|
22636
|
-
return () => abort.abort();
|
|
22637
|
-
}, [selected]);
|
|
22638
|
-
async function uninstall() {
|
|
22639
|
-
if (!pending || busy) return;
|
|
23833
|
+
const uninstall = async (target) => {
|
|
23834
|
+
if (busy) return;
|
|
22640
23835
|
setBusy(true);
|
|
22641
23836
|
setError(null);
|
|
22642
23837
|
try {
|
|
22643
|
-
await removeInstalled(
|
|
22644
|
-
setItems((current) => current.filter((item) => item.key !==
|
|
22645
|
-
|
|
23838
|
+
await removeInstalled(target.key);
|
|
23839
|
+
setItems((current) => current.filter((item) => item.key !== target.key));
|
|
23840
|
+
setSelectedKey((current) => current === target.key ? null : current);
|
|
22646
23841
|
setPending(null);
|
|
22647
|
-
onChanged();
|
|
23842
|
+
props.onChanged();
|
|
22648
23843
|
} catch (reason) {
|
|
22649
|
-
setError(
|
|
23844
|
+
setError(messageOf(reason));
|
|
22650
23845
|
setPending(null);
|
|
22651
23846
|
} finally {
|
|
22652
23847
|
setBusy(false);
|
|
22653
23848
|
}
|
|
22654
|
-
}
|
|
23849
|
+
};
|
|
23850
|
+
const selected = selectedKey === null ? null : items.find((item) => item.key === selectedKey) ?? null;
|
|
23851
|
+
(0, react.useEffect)(() => {
|
|
23852
|
+
if (returnFocusKey === null) return;
|
|
23853
|
+
rowButtons.current.get(returnFocusKey)?.focus();
|
|
23854
|
+
setReturnFocusKey(null);
|
|
23855
|
+
}, [returnFocusKey]);
|
|
22655
23856
|
const needle = query.trim().toLocaleLowerCase();
|
|
22656
23857
|
const visible = items.filter((item) => `${item.name} ${item.summary ?? ""} ${item.dirPath}`.toLocaleLowerCase().includes(needle));
|
|
22657
|
-
return (0, react_jsx_runtime.jsxs)(
|
|
23858
|
+
return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [selected !== null ? (0, react_jsx_runtime.jsx)(InstalledSkillDetail, {
|
|
23859
|
+
item: selected,
|
|
23860
|
+
onBack: () => {
|
|
23861
|
+
setReturnFocusKey(selected.key);
|
|
23862
|
+
setSelectedKey(null);
|
|
23863
|
+
},
|
|
23864
|
+
onUninstall: setPending,
|
|
23865
|
+
onOpenMarket: props.onOpenMarket
|
|
23866
|
+
}, selected.key) : (0, react_jsx_runtime.jsxs)("section", {
|
|
22658
23867
|
className: InstalledSkills_module_css_default.root,
|
|
22659
23868
|
children: [
|
|
22660
23869
|
(0, react_jsx_runtime.jsxs)("header", {
|
|
@@ -22675,7 +23884,7 @@ function(property) {
|
|
|
22675
23884
|
value: query,
|
|
22676
23885
|
onChange: (event) => setQuery(event.target.value)
|
|
22677
23886
|
}),
|
|
22678
|
-
error && (0, react_jsx_runtime.jsx)("p", {
|
|
23887
|
+
error !== null && (0, react_jsx_runtime.jsx)("p", {
|
|
22679
23888
|
role: "alert",
|
|
22680
23889
|
className: InstalledSkills_module_css_default.error,
|
|
22681
23890
|
children: error
|
|
@@ -22700,7 +23909,11 @@ function(property) {
|
|
|
22700
23909
|
children: [
|
|
22701
23910
|
(0, react_jsx_runtime.jsx)("button", {
|
|
22702
23911
|
className: InstalledSkills_module_css_default.name,
|
|
22703
|
-
|
|
23912
|
+
ref: (node) => {
|
|
23913
|
+
if (node === null) rowButtons.current.delete(item.key);
|
|
23914
|
+
else rowButtons.current.set(item.key, node);
|
|
23915
|
+
},
|
|
23916
|
+
onClick: () => setSelectedKey(item.key),
|
|
22704
23917
|
children: item.name
|
|
22705
23918
|
}),
|
|
22706
23919
|
(0, react_jsx_runtime.jsx)("span", {
|
|
@@ -22711,11 +23924,11 @@ function(property) {
|
|
|
22711
23924
|
className: InstalledSkills_module_css_default.badge,
|
|
22712
23925
|
children: t("linkedSkill")
|
|
22713
23926
|
}),
|
|
22714
|
-
item.version && (0, react_jsx_runtime.jsxs)("span", {
|
|
23927
|
+
item.version !== void 0 && item.version !== "" && (0, react_jsx_runtime.jsxs)("span", {
|
|
22715
23928
|
className: InstalledSkills_module_css_default.version,
|
|
22716
23929
|
children: ["v", item.version]
|
|
22717
23930
|
}),
|
|
22718
|
-
item.summary && (0, react_jsx_runtime.jsx)("p", {
|
|
23931
|
+
item.summary !== void 0 && item.summary !== "" && (0, react_jsx_runtime.jsx)("p", {
|
|
22719
23932
|
className: InstalledSkills_module_css_default.summary,
|
|
22720
23933
|
children: item.summary
|
|
22721
23934
|
}),
|
|
@@ -22729,79 +23942,59 @@ function(property) {
|
|
|
22729
23942
|
children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
22730
23943
|
variant: "outline",
|
|
22731
23944
|
size: "sm",
|
|
22732
|
-
onClick: () =>
|
|
23945
|
+
onClick: () => setSelectedKey(item.key),
|
|
22733
23946
|
children: t("viewInstalled")
|
|
22734
23947
|
}), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
22735
23948
|
variant: "outline",
|
|
22736
23949
|
size: "sm",
|
|
22737
23950
|
disabled: !item.removable,
|
|
23951
|
+
title: item.removable ? void 0 : t("uninstallDisabled"),
|
|
22738
23952
|
onClick: () => setPending(item),
|
|
22739
23953
|
children: t("uninstall")
|
|
22740
23954
|
})]
|
|
22741
23955
|
})]
|
|
22742
23956
|
}, item.key))
|
|
22743
|
-
})] })
|
|
22744
|
-
selected && (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
22745
|
-
open: true,
|
|
22746
|
-
title: selected.name,
|
|
22747
|
-
closeLabel: t("close"),
|
|
22748
|
-
onClose: () => setSelected(null),
|
|
22749
|
-
children: [(0, react_jsx_runtime.jsx)("p", {
|
|
22750
|
-
className: InstalledSkills_module_css_default.path,
|
|
22751
|
-
children: selected.dirPath
|
|
22752
|
-
}), (0, react_jsx_runtime.jsx)("div", {
|
|
22753
|
-
className: InstalledSkills_module_css_default.preview,
|
|
22754
|
-
children: detailError ? (0, react_jsx_runtime.jsx)("p", {
|
|
22755
|
-
role: "alert",
|
|
22756
|
-
children: detailError
|
|
22757
|
-
}) : markdown === null ? (0, react_jsx_runtime.jsxs)("div", {
|
|
22758
|
-
role: "status",
|
|
22759
|
-
"aria-label": t("loading"),
|
|
22760
|
-
children: [(0, react_jsx_runtime.jsx)("div", { className: InstalledSkills_module_css_default.skeleton }), (0, react_jsx_runtime.jsx)("div", { className: InstalledSkills_module_css_default.skeleton })]
|
|
22761
|
-
}) : (0, react_jsx_runtime.jsx)(MarkdownView, { content: markdown })
|
|
22762
|
-
})]
|
|
22763
|
-
}),
|
|
22764
|
-
pending && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
22765
|
-
open: true,
|
|
22766
|
-
title: t("uninstallTitle", { name: pending.name }),
|
|
22767
|
-
closeLabel: t("close"),
|
|
22768
|
-
description: t(pending.linked ? "unlinkDescription" : "localUninstallDescription"),
|
|
22769
|
-
onClose: () => {
|
|
22770
|
-
if (!busy) setPending(null);
|
|
22771
|
-
},
|
|
22772
|
-
footer: (0, react_jsx_runtime.jsxs)("div", {
|
|
22773
|
-
className: InstalledSkills_module_css_default.actions,
|
|
22774
|
-
children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
22775
|
-
variant: "outline",
|
|
22776
|
-
size: "md",
|
|
22777
|
-
disabled: busy,
|
|
22778
|
-
onClick: () => setPending(null),
|
|
22779
|
-
children: t("cancel")
|
|
22780
|
-
}), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
22781
|
-
variant: "primary",
|
|
22782
|
-
size: "md",
|
|
22783
|
-
disabled: busy,
|
|
22784
|
-
onClick: () => void uninstall(),
|
|
22785
|
-
children: t(busy ? "uninstalling" : "uninstallConfirm")
|
|
22786
|
-
})]
|
|
22787
|
-
}),
|
|
22788
|
-
children: (0, react_jsx_runtime.jsx)("p", {
|
|
22789
|
-
className: InstalledSkills_module_css_default.path,
|
|
22790
|
-
children: pending.dirPath
|
|
22791
|
-
})
|
|
22792
|
-
})
|
|
23957
|
+
})] })
|
|
22793
23958
|
]
|
|
22794
|
-
})
|
|
23959
|
+
}), pending !== null && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
23960
|
+
open: true,
|
|
23961
|
+
title: t("uninstallTitle", { name: pending.name }),
|
|
23962
|
+
closeLabel: t("close"),
|
|
23963
|
+
description: t(pending.linked ? "unlinkDescription" : "localUninstallDescription"),
|
|
23964
|
+
onClose: () => {
|
|
23965
|
+
if (!busy) setPending(null);
|
|
23966
|
+
},
|
|
23967
|
+
footer: (0, react_jsx_runtime.jsxs)("div", {
|
|
23968
|
+
className: InstalledSkills_module_css_default.actions,
|
|
23969
|
+
children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
23970
|
+
variant: "outline",
|
|
23971
|
+
size: "md",
|
|
23972
|
+
disabled: busy,
|
|
23973
|
+
onClick: () => setPending(null),
|
|
23974
|
+
children: t("cancel")
|
|
23975
|
+
}), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
23976
|
+
variant: "primary",
|
|
23977
|
+
size: "md",
|
|
23978
|
+
disabled: busy,
|
|
23979
|
+
onClick: () => void uninstall(pending),
|
|
23980
|
+
children: t(busy ? "uninstalling" : "uninstallConfirm")
|
|
23981
|
+
})]
|
|
23982
|
+
}),
|
|
23983
|
+
children: (0, react_jsx_runtime.jsx)("p", {
|
|
23984
|
+
className: InstalledSkills_module_css_default.path,
|
|
23985
|
+
children: pending.dirPath
|
|
23986
|
+
})
|
|
23987
|
+
})] });
|
|
22795
23988
|
}
|
|
22796
23989
|
//#endregion
|
|
22797
23990
|
//#region \0dsh-css:src/client/components/FilterBar.module.css.mjs
|
|
22798
|
-
const css$
|
|
22799
|
-
const tagId$
|
|
22800
|
-
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$
|
|
23991
|
+
const css$7 = ".iX7c8W_bar{flex-wrap:wrap;align-items:center;gap:8px;min-width:0;display:flex}.iX7c8W_search{flex:220px;align-items:center;gap:6px;min-width:0;display:flex}.iX7c8W_searchInput{flex:auto;min-width:0}.iX7c8W_clear{flex:none}.iX7c8W_field{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2,#0000001a);border-radius:var(--dsw-radius-sm,8px);background:var(--dsw-alias-bg-layer-1,#fff);cursor:pointer;flex:none;align-items:center;gap:6px;height:32px;padding:0 6px 0 10px;display:inline-flex}.iX7c8W_field:focus-within{border-color:var(--dsw-alias-state-business-primary,#4176e6)}.iX7c8W_fieldLabel{color:var(--dsw-alias-label-tertiary,#81858c);white-space:nowrap;font-size:12px;line-height:18px}.iX7c8W_select{max-width:148px;color:var(--dsw-alias-label-primary,#0f1115);font:inherit;cursor:pointer;text-overflow:ellipsis;background:0 0;border:none;outline:none;font-size:12.5px;font-weight:600;line-height:18px}.iX7c8W_select:disabled{color:var(--dsw-alias-label-tertiary,#81858c);cursor:not-allowed}.iX7c8W_select option{background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-primary,#0f1115)}.iX7c8W_srOnly{clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}";
|
|
23992
|
+
const tagId$7 = "@nanmicoder/dsh-skills-hub/FilterBar.module.css";
|
|
23993
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$7) + "]") === null) {
|
|
22801
23994
|
const tag = document.createElement("style");
|
|
22802
23995
|
tag.dataset.plugin = "@nanmicoder/dsh-skills-hub";
|
|
22803
|
-
tag.dataset.pluginCss = tagId$
|
|
22804
|
-
tag.textContent = css$
|
|
23996
|
+
tag.dataset.pluginCss = tagId$7;
|
|
23997
|
+
tag.textContent = css$7;
|
|
22805
23998
|
document.head.appendChild(tag);
|
|
22806
23999
|
}
|
|
22807
24000
|
var FilterBar_module_css_default = {
|
|
@@ -22928,13 +24121,13 @@ function(property) {
|
|
|
22928
24121
|
}
|
|
22929
24122
|
//#endregion
|
|
22930
24123
|
//#region \0dsh-css:src/client/components/MarketDisclaimer.module.css.mjs
|
|
22931
|
-
const css$
|
|
22932
|
-
const tagId$
|
|
22933
|
-
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$
|
|
24124
|
+
const css$6 = ".e2Evdq_banner{box-sizing:border-box;border:1px solid color-mix(in srgb, var(--dsw-alias-state-warn-primary,#f59e0b) 38%, transparent);border-radius:var(--dsw-radius-sm,8px);background:color-mix(in srgb, var(--dsw-alias-state-warn-primary,#f59e0b) 10%, transparent);align-items:flex-start;gap:10px;padding:10px 12px;display:flex}.e2Evdq_icon{color:var(--dsw-alias-state-warn-primary,#f59e0b);flex:none;margin-top:1px}.e2Evdq_text{overflow-wrap:anywhere;min-width:0;color:var(--dsw-alias-label-secondary,#61666b);flex:1;margin:0;font-size:12.5px;line-height:1.65}.e2Evdq_dismiss{flex:none;margin:-4px -6px 0 0}";
|
|
24125
|
+
const tagId$6 = "@nanmicoder/dsh-skills-hub/MarketDisclaimer.module.css";
|
|
24126
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$6) + "]") === null) {
|
|
22934
24127
|
const tag = document.createElement("style");
|
|
22935
24128
|
tag.dataset.plugin = "@nanmicoder/dsh-skills-hub";
|
|
22936
|
-
tag.dataset.pluginCss = tagId$
|
|
22937
|
-
tag.textContent = css$
|
|
24129
|
+
tag.dataset.pluginCss = tagId$6;
|
|
24130
|
+
tag.textContent = css$6;
|
|
22938
24131
|
document.head.appendChild(tag);
|
|
22939
24132
|
}
|
|
22940
24133
|
var MarketDisclaimer_module_css_default = {
|
|
@@ -22980,13 +24173,13 @@ function(property) {
|
|
|
22980
24173
|
}
|
|
22981
24174
|
//#endregion
|
|
22982
24175
|
//#region \0dsh-css:src/client/components/InstallStateBadge.module.css.mjs
|
|
22983
|
-
const css$
|
|
22984
|
-
const tagId$
|
|
22985
|
-
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$
|
|
24176
|
+
const css$5 = ".WSc5Pa_badge{box-sizing:border-box;white-space:nowrap;border-radius:999px;flex:none;align-items:center;gap:4px;height:20px;padding:0 7px;font-size:11px;font-weight:600;line-height:18px;display:inline-flex}.WSc5Pa_success{color:var(--dsw-alias-state-success-primary,#22c55e);background:color-mix(in srgb, var(--dsw-alias-state-success-primary,#22c55e) 12%, transparent)}.WSc5Pa_brand{color:var(--dsw-alias-state-business-primary,#4176e6);background:color-mix(in srgb, var(--dsw-alias-state-business-primary,#4176e6) 12%, transparent)}.WSc5Pa_danger{color:var(--dsw-alias-state-error-primary,#ec1313);background:color-mix(in srgb, var(--dsw-alias-state-error-primary,#ec1313) 12%, transparent)}";
|
|
24177
|
+
const tagId$5 = "@nanmicoder/dsh-skills-hub/InstallStateBadge.module.css";
|
|
24178
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$5) + "]") === null) {
|
|
22986
24179
|
const tag = document.createElement("style");
|
|
22987
24180
|
tag.dataset.plugin = "@nanmicoder/dsh-skills-hub";
|
|
22988
|
-
tag.dataset.pluginCss = tagId$
|
|
22989
|
-
tag.textContent = css$
|
|
24181
|
+
tag.dataset.pluginCss = tagId$5;
|
|
24182
|
+
tag.textContent = css$5;
|
|
22990
24183
|
document.head.appendChild(tag);
|
|
22991
24184
|
}
|
|
22992
24185
|
var InstallStateBadge_module_css_default = {
|
|
@@ -23021,113 +24214,6 @@ function(property) {
|
|
|
23021
24214
|
});
|
|
23022
24215
|
}
|
|
23023
24216
|
//#endregion
|
|
23024
|
-
//#region \0dsh-css:src/client/components/SkillAvatar.module.css.mjs
|
|
23025
|
-
const css$5 = ".C6tuwq_image{object-fit:cover;border:1px solid var(--dsw-alias-border-l2,#0000001a);background:var(--dsw-alias-bg-module-platform,#f5f6f7);flex:none}.C6tuwq_letter{box-sizing:border-box;letter-spacing:-.02em;color:var(--dsw-alias-label-primary-inverted,#fff);user-select:none;box-shadow:inset 0 1px 0 color-mix(in srgb, var(--dsw-alias-label-primary-inverted,#fff) 12%, transparent);flex:none;justify-content:center;align-items:center;font-weight:700;line-height:1;display:inline-flex;overflow:hidden}.C6tuwq_tone1{background:linear-gradient(145deg, color-mix(in srgb, var(--dsw-alias-state-success-primary,#22c55e) 74%, var(--dsw-alias-label-primary,#0f1115)), color-mix(in srgb, var(--dsw-alias-state-success-primary,#22c55e) 48%, var(--dsw-alias-label-primary,#0f1115)))}.C6tuwq_tone2{background:linear-gradient(145deg, color-mix(in srgb, var(--dsw-alias-state-warn-primary,#f59e0b) 80%, var(--dsw-alias-label-primary,#0f1115)), color-mix(in srgb, var(--dsw-alias-state-warn-primary,#f59e0b) 54%, var(--dsw-alias-label-primary,#0f1115)))}.C6tuwq_tone5{background:linear-gradient(145deg, color-mix(in srgb, color-mix(in srgb, var(--dsw-alias-state-warn-primary,#f59e0b) 55%, var(--dsw-alias-state-success-primary,#22c55e)) 78%, var(--dsw-alias-label-primary,#0f1115)), color-mix(in srgb, color-mix(in srgb, var(--dsw-alias-state-warn-primary,#f59e0b) 55%, var(--dsw-alias-state-success-primary,#22c55e)) 50%, var(--dsw-alias-label-primary,#0f1115)))}.C6tuwq_tone0{background:linear-gradient(145deg, color-mix(in srgb, var(--dsw-alias-state-business-primary,#4176e6) 82%, var(--dsw-alias-label-primary,#0f1115)), color-mix(in srgb, var(--dsw-alias-state-business-primary,#4176e6) 56%, var(--dsw-alias-label-primary,#0f1115)))}.C6tuwq_tone4{background:linear-gradient(145deg, color-mix(in srgb, color-mix(in srgb, var(--dsw-alias-state-business-primary,#4176e6) 58%, var(--dsw-alias-state-success-primary,#22c55e)) 80%, var(--dsw-alias-label-primary,#0f1115)), color-mix(in srgb, color-mix(in srgb, var(--dsw-alias-state-business-primary,#4176e6) 58%, var(--dsw-alias-state-success-primary,#22c55e)) 52%, var(--dsw-alias-label-primary,#0f1115)))}.C6tuwq_tone3{background:linear-gradient(145deg, color-mix(in srgb, var(--dsw-alias-state-idle-primary,#d4d4d4) 62%, var(--dsw-alias-label-primary,#0f1115)), color-mix(in srgb, var(--dsw-alias-state-idle-primary,#d4d4d4) 38%, var(--dsw-alias-label-primary,#0f1115)))}";
|
|
23026
|
-
const tagId$5 = "@nanmicoder/dsh-skills-hub/SkillAvatar.module.css";
|
|
23027
|
-
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$5) + "]") === null) {
|
|
23028
|
-
const tag = document.createElement("style");
|
|
23029
|
-
tag.dataset.plugin = "@nanmicoder/dsh-skills-hub";
|
|
23030
|
-
tag.dataset.pluginCss = tagId$5;
|
|
23031
|
-
tag.textContent = css$5;
|
|
23032
|
-
document.head.appendChild(tag);
|
|
23033
|
-
}
|
|
23034
|
-
var SkillAvatar_module_css_default = {
|
|
23035
|
-
"image": "C6tuwq_image",
|
|
23036
|
-
"letter": "C6tuwq_letter",
|
|
23037
|
-
"tone0": "C6tuwq_tone0",
|
|
23038
|
-
"tone1": "C6tuwq_tone1",
|
|
23039
|
-
"tone2": "C6tuwq_tone2",
|
|
23040
|
-
"tone3": "C6tuwq_tone3",
|
|
23041
|
-
"tone4": "C6tuwq_tone4",
|
|
23042
|
-
"tone5": "C6tuwq_tone5"
|
|
23043
|
-
};
|
|
23044
|
-
//#endregion
|
|
23045
|
-
//#region lib/client/components/SkillAvatar.js
|
|
23046
|
-
/** Palette slots; each maps to one `.toneN` class in the module stylesheet. */
|
|
23047
|
-
const TONE_COUNT = 6;
|
|
23048
|
-
/**
|
|
23049
|
-
* Per-source tone family. The `source` prop is otherwise unused by a letter
|
|
23050
|
-
* avatar, so it earns its place as the family selector: ClawHub entries read
|
|
23051
|
-
* warm, SkillHub entries read cool, and a mixed grid still shows at a glance
|
|
23052
|
-
* where a card came from.
|
|
23053
|
-
*/
|
|
23054
|
-
const SOURCE_TONES = {
|
|
23055
|
-
clawhub: [
|
|
23056
|
-
2,
|
|
23057
|
-
5,
|
|
23058
|
-
1
|
|
23059
|
-
],
|
|
23060
|
-
skillhub: [
|
|
23061
|
-
0,
|
|
23062
|
-
4,
|
|
23063
|
-
3
|
|
23064
|
-
]
|
|
23065
|
-
};
|
|
23066
|
-
/** Class name per tone slot, resolved once so the render path stays trivial. */
|
|
23067
|
-
const TONE_CLASSES = [
|
|
23068
|
-
SkillAvatar_module_css_default["tone0"] ?? "",
|
|
23069
|
-
SkillAvatar_module_css_default["tone1"] ?? "",
|
|
23070
|
-
SkillAvatar_module_css_default["tone2"] ?? "",
|
|
23071
|
-
SkillAvatar_module_css_default["tone3"] ?? "",
|
|
23072
|
-
SkillAvatar_module_css_default["tone4"] ?? "",
|
|
23073
|
-
SkillAvatar_module_css_default["tone5"] ?? ""
|
|
23074
|
-
];
|
|
23075
|
-
/**
|
|
23076
|
-
* Deterministic palette index: the same name always keeps the same identity
|
|
23077
|
-
* color across sessions, pages and re-renders.
|
|
23078
|
-
*/
|
|
23079
|
-
function hashIndex(input) {
|
|
23080
|
-
let hash = 0;
|
|
23081
|
-
for (let i = 0; i < input.length; i++) hash = hash * 31 + input.charCodeAt(i) | 0;
|
|
23082
|
-
return Math.abs(hash) % TONE_COUNT;
|
|
23083
|
-
}
|
|
23084
|
-
/**
|
|
23085
|
-
* First visible character, uppercased. `Array.from` rather than `name[0]` so a
|
|
23086
|
-
* CJK or astral-plane initial is not cut in half by UTF-16 indexing.
|
|
23087
|
-
*/
|
|
23088
|
-
function initialOf(name) {
|
|
23089
|
-
const first = Array.from(name.trim())[0];
|
|
23090
|
-
return first ? first.toUpperCase() : "?";
|
|
23091
|
-
}
|
|
23092
|
-
/**
|
|
23093
|
-
* Skill icon tile.
|
|
23094
|
-
*
|
|
23095
|
-
* An `<img>` is rendered only for an `https:` URL: the string arrives from a
|
|
23096
|
-
* third-party catalogue, and allowing `javascript:` or `data:` here would turn
|
|
23097
|
-
* an upstream payload into script execution inside the DSH page.
|
|
23098
|
-
*/
|
|
23099
|
-
function SkillAvatar(props) {
|
|
23100
|
-
const { name, source, iconUrl, size } = props;
|
|
23101
|
-
const radius = Math.round(size * .24);
|
|
23102
|
-
if (iconUrl !== void 0 && iconUrl.startsWith("https://")) return (0, react_jsx_runtime.jsx)("img", {
|
|
23103
|
-
src: iconUrl,
|
|
23104
|
-
alt: "",
|
|
23105
|
-
width: size,
|
|
23106
|
-
height: size,
|
|
23107
|
-
loading: "lazy",
|
|
23108
|
-
decoding: "async",
|
|
23109
|
-
className: SkillAvatar_module_css_default.image,
|
|
23110
|
-
style: {
|
|
23111
|
-
width: size,
|
|
23112
|
-
height: size,
|
|
23113
|
-
borderRadius: radius
|
|
23114
|
-
}
|
|
23115
|
-
});
|
|
23116
|
-
const family = SOURCE_TONES[source];
|
|
23117
|
-
const tone = family[hashIndex(name) % family.length] ?? 0;
|
|
23118
|
-
return (0, react_jsx_runtime.jsx)("span", {
|
|
23119
|
-
"aria-hidden": "true",
|
|
23120
|
-
className: `${SkillAvatar_module_css_default.letter} ${TONE_CLASSES[tone] ?? ""}`,
|
|
23121
|
-
style: {
|
|
23122
|
-
width: size,
|
|
23123
|
-
height: size,
|
|
23124
|
-
borderRadius: radius,
|
|
23125
|
-
fontSize: Math.round(size * .4)
|
|
23126
|
-
},
|
|
23127
|
-
children: initialOf(name)
|
|
23128
|
-
});
|
|
23129
|
-
}
|
|
23130
|
-
//#endregion
|
|
23131
24217
|
//#region \0dsh-css:src/client/components/SkillCard.module.css.mjs
|
|
23132
24218
|
const css$4 = ".cii49a_card{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2,#0000001a);border-radius:var(--dsw-radius-md,12px);background:var(--dsw-alias-bg-layer-1,#fff);flex-direction:column;gap:10px;min-width:0;min-height:188px;padding:14px;transition:border-color .14s,background-color .14s;display:flex;position:relative}.cii49a_card:hover{border-color:var(--dsw-alias-border-l3,#00000029);background:var(--dsw-alias-interactive-bg-hover,#2631480f)}.cii49a_card:focus-within{border-color:var(--dsw-alias-state-business-primary,#4176e6)}.cii49a_open{z-index:0;border-radius:inherit;cursor:pointer;background:0 0;border:none;margin:0;padding:0;position:absolute;inset:0}.cii49a_open:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#4176e6);outline-offset:2px}.cii49a_head,.cii49a_summary,.cii49a_tags,.cii49a_footer{z-index:1;pointer-events:none;position:relative}.cii49a_head{align-items:center;gap:12px;min-width:0;display:flex}.cii49a_headText{flex:1;min-width:0}.cii49a_titleRow{align-items:baseline;gap:8px;min-width:0;display:flex}.cii49a_title{min-width:0;color:var(--dsw-alias-label-primary,#0f1115);letter-spacing:-.01em;text-overflow:ellipsis;white-space:nowrap;flex:1;margin:0;font-size:15px;font-weight:700;line-height:22px;overflow:hidden}.cii49a_version{color:var(--dsw-alias-label-tertiary,#81858c);font-variant-numeric:tabular-nums;flex:none;font-size:11.5px;font-weight:600}.cii49a_origin{align-items:center;gap:6px;min-width:0;margin:2px 0 0;font-size:11px;line-height:16px;display:flex}.cii49a_source{color:var(--dsw-alias-label-tertiary,#81858c);letter-spacing:.06em;text-transform:uppercase;flex:none;font-weight:700}.cii49a_author{min-width:0;color:var(--dsw-alias-label-secondary,#61666b);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.cii49a_summary{min-height:42px;color:var(--dsw-alias-label-secondary,#61666b);overflow-wrap:anywhere;-webkit-line-clamp:3;-webkit-box-orient:vertical;margin:0;font-size:13px;line-height:21px;display:-webkit-box;overflow:hidden}.cii49a_tags{min-height:18px;color:var(--dsw-alias-label-tertiary,#81858c);flex-wrap:wrap;align-items:center;gap:4px 8px;margin:0;font-size:12px;line-height:18px;display:flex}.cii49a_tagIcon{color:var(--dsw-alias-label-tertiary,#81858c);flex:none}.cii49a_tag{text-overflow:ellipsis;white-space:nowrap;max-width:100%;overflow:hidden}.cii49a_footer{border-top:1px solid var(--dsw-alias-border-l1,#0000000a);flex-wrap:wrap;align-items:center;gap:8px;margin-top:auto;padding-top:10px;display:flex}.cii49a_badges{flex-wrap:wrap;align-items:center;gap:6px;min-width:0;display:flex}.cii49a_stats{color:var(--dsw-alias-label-secondary,#61666b);font-variant-numeric:tabular-nums;flex-wrap:wrap;align-items:center;gap:10px;margin-left:auto;font-size:12px;line-height:18px;display:flex}.cii49a_stat{white-space:nowrap;align-items:center;gap:4px;display:inline-flex}.cii49a_install{z-index:2;pointer-events:auto;position:relative}";
|
|
23133
24219
|
const tagId$4 = "@nanmicoder/dsh-skills-hub/SkillCard.module.css";
|
|
@@ -23291,7 +24377,7 @@ function(property) {
|
|
|
23291
24377
|
}
|
|
23292
24378
|
//#endregion
|
|
23293
24379
|
//#region \0dsh-css:src/client/components/SourceStatusBar.module.css.mjs
|
|
23294
|
-
const css$3 = ".QI7v1q_bar{flex-wrap:wrap;align-items:center;gap:6px;display:flex}.QI7v1q_list{flex-wrap:wrap;align-items:center;gap:6px;min-width:0;margin:0;padding:0;list-style:none;display:flex}.QI7v1q_item{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2,#0000001a);background:var(--dsw-alias-bg-layer-1,#fff);white-space:nowrap;border-radius:999px;align-items:center;gap:6px;height:24px;padding:0 9px;font-size:12px;line-height:18px;display:inline-flex}.QI7v1q_dot{font-size:10px;line-height:10px}.QI7v1q_name{color:var(--dsw-alias-label-primary,#0f1115);font-weight:600}.QI7v1q_status{color:var(--dsw-alias-label-tertiary,#81858c)}.QI7v1q_refresh{flex:none}.QI7v1q_spinning{transform-origin:50%;animation:.9s linear infinite QI7v1q_skills-hub-spin}@keyframes QI7v1q_skills-hub-spin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion:reduce){.QI7v1q_spinning{animation:none}}";
|
|
24380
|
+
const css$3 = ".QI7v1q_bar{flex-wrap:wrap;align-items:center;gap:6px;display:flex}.QI7v1q_list{flex-wrap:wrap;align-items:center;gap:6px;min-width:0;margin:0;padding:0;list-style:none;display:flex}.QI7v1q_item{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2,#0000001a);background:var(--dsw-alias-bg-layer-1,#fff);white-space:nowrap;border-radius:999px;align-items:center;gap:6px;height:24px;padding:0 9px;font-size:12px;line-height:18px;display:inline-flex}.QI7v1q_dot{font-size:10px;line-height:10px}.QI7v1q_name{color:var(--dsw-alias-label-primary,#0f1115);font-weight:600}.QI7v1q_status{color:var(--dsw-alias-label-tertiary,#81858c)}.QI7v1q_snapshot{border-left:1px solid var(--dsw-alias-border-l2,#0000001a);color:var(--dsw-alias-label-secondary,#61666b);font-variant-numeric:tabular-nums;padding-left:6px}.QI7v1q_refresh{flex:none}.QI7v1q_spinning{transform-origin:50%;animation:.9s linear infinite QI7v1q_skills-hub-spin}@keyframes QI7v1q_skills-hub-spin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion:reduce){.QI7v1q_spinning{animation:none}}";
|
|
23295
24381
|
const tagId$3 = "@nanmicoder/dsh-skills-hub/SourceStatusBar.module.css";
|
|
23296
24382
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
|
|
23297
24383
|
const tag = document.createElement("style");
|
|
@@ -23308,6 +24394,7 @@ function(property) {
|
|
|
23308
24394
|
"name": "QI7v1q_name",
|
|
23309
24395
|
"refresh": "QI7v1q_refresh",
|
|
23310
24396
|
"skills-hub-spin": "QI7v1q_skills-hub-spin",
|
|
24397
|
+
"snapshot": "QI7v1q_snapshot",
|
|
23311
24398
|
"spinning": "QI7v1q_spinning",
|
|
23312
24399
|
"status": "QI7v1q_status"
|
|
23313
24400
|
};
|
|
@@ -23332,6 +24419,10 @@ function(property) {
|
|
|
23332
24419
|
* The upstream error string rides the pill's `title` instead of being printed:
|
|
23333
24420
|
* a 720px panel has no room for a stack trace next to two source names, and an
|
|
23334
24421
|
* operator who needs it can hover.
|
|
24422
|
+
*
|
|
24423
|
+
* The snapshot age *is* printed: a healthy source can still be answering from a
|
|
24424
|
+
* cache, and a reader deciding whether to install third-party code is entitled
|
|
24425
|
+
* to see that the page in front of them was read minutes ago rather than now.
|
|
23335
24426
|
*/
|
|
23336
24427
|
function SourceStatusBar(props) {
|
|
23337
24428
|
const { sources, onRefresh, refreshing } = props;
|
|
@@ -23342,6 +24433,7 @@ function(property) {
|
|
|
23342
24433
|
className: SourceStatusBar_module_css_default.list,
|
|
23343
24434
|
children: SOURCES.map((source) => {
|
|
23344
24435
|
const info = sources[source];
|
|
24436
|
+
const snapshotAt = info.fromCache === true ? info.fetchedAt : void 0;
|
|
23345
24437
|
return (0, react_jsx_runtime.jsxs)("li", {
|
|
23346
24438
|
className: SourceStatusBar_module_css_default.item,
|
|
23347
24439
|
title: info.error,
|
|
@@ -23357,18 +24449,23 @@ function(property) {
|
|
|
23357
24449
|
(0, react_jsx_runtime.jsx)("span", {
|
|
23358
24450
|
className: SourceStatusBar_module_css_default.status,
|
|
23359
24451
|
children: t(`sourceStatus.${info.status}`)
|
|
24452
|
+
}),
|
|
24453
|
+
snapshotAt !== void 0 && (0, react_jsx_runtime.jsx)("span", {
|
|
24454
|
+
className: SourceStatusBar_module_css_default.snapshot,
|
|
24455
|
+
title: formatStamp(snapshotAt),
|
|
24456
|
+
children: t("snapshotAge", { age: formatAge(t, snapshotAt) })
|
|
23360
24457
|
})
|
|
23361
24458
|
]
|
|
23362
24459
|
}, source);
|
|
23363
24460
|
})
|
|
23364
24461
|
}), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
23365
|
-
label: t("
|
|
24462
|
+
label: t("refreshNow"),
|
|
23366
24463
|
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
23367
24464
|
variant: "ghost",
|
|
23368
24465
|
size: "sm",
|
|
23369
24466
|
className: SourceStatusBar_module_css_default.refresh,
|
|
23370
24467
|
disabled: refreshing,
|
|
23371
|
-
"aria-label": t("
|
|
24468
|
+
"aria-label": t("refreshNow"),
|
|
23372
24469
|
onClick: onRefresh,
|
|
23373
24470
|
icon: (0, react_jsx_runtime.jsx)(RefreshIcon, {
|
|
23374
24471
|
size: 16,
|
|
@@ -23420,6 +24517,27 @@ function(property) {
|
|
|
23420
24517
|
//#endregion
|
|
23421
24518
|
//#region lib/client/components/MarketHome.js
|
|
23422
24519
|
/**
|
|
24520
|
+
* How far ahead of the end of the list the next page is fetched.
|
|
24521
|
+
*
|
|
24522
|
+
* The catalogue scrolls inside the host's dock rather than the document, and an
|
|
24523
|
+
* observer whose root is the viewport is clipped by that dock: `rootMargin` on
|
|
24524
|
+
* the viewport is dead weight there (measured: the same sentinel reports
|
|
24525
|
+
* "not intersecting" 412px below the fold with a viewport root, and
|
|
24526
|
+
* "intersecting" with the dock as root). The margin therefore only means
|
|
24527
|
+
* anything once the observer is given the real scroll container.
|
|
24528
|
+
*/
|
|
24529
|
+
const PREFETCH_MARGIN = "900px 0px";
|
|
24530
|
+
/** Nearest scrollable ancestor, or `null` when the list scrolls with the document. */
|
|
24531
|
+
function scrollContainerOf(element) {
|
|
24532
|
+
let node = element.parentElement;
|
|
24533
|
+
while (node !== null && node !== document.body) {
|
|
24534
|
+
const overflowY = getComputedStyle(node).overflowY;
|
|
24535
|
+
if ((overflowY === "auto" || overflowY === "scroll") && node.scrollHeight > node.clientHeight) return node;
|
|
24536
|
+
node = node.parentElement;
|
|
24537
|
+
}
|
|
24538
|
+
return null;
|
|
24539
|
+
}
|
|
24540
|
+
/**
|
|
23423
24541
|
* Market catalogue page.
|
|
23424
24542
|
*
|
|
23425
24543
|
* Presentational by contract: every interaction below is a call on the
|
|
@@ -23430,16 +24548,31 @@ function(property) {
|
|
|
23430
24548
|
const { state, controller } = props;
|
|
23431
24549
|
const t = useT();
|
|
23432
24550
|
const sentinelRef = (0, react.useRef)(null);
|
|
24551
|
+
const [scrolled, setScrolled] = (0, react.useState)(false);
|
|
24552
|
+
(0, react.useEffect)(() => {
|
|
24553
|
+
if (scrolled) return;
|
|
24554
|
+
const mark = () => setScrolled(true);
|
|
24555
|
+
document.addEventListener("scroll", mark, {
|
|
24556
|
+
capture: true,
|
|
24557
|
+
passive: true,
|
|
24558
|
+
once: true
|
|
24559
|
+
});
|
|
24560
|
+
return () => document.removeEventListener("scroll", mark, { capture: true });
|
|
24561
|
+
}, [scrolled]);
|
|
23433
24562
|
(0, react.useEffect)(() => {
|
|
23434
24563
|
const sentinel = sentinelRef.current;
|
|
23435
|
-
if (sentinel === null || state.nextCursor === null || state.loading || state.loadingMore || state.error !== null) return;
|
|
24564
|
+
if (sentinel === null || !scrolled || state.nextCursor === null || state.loading || state.loadingMore || state.error !== null) return;
|
|
23436
24565
|
const observer = new IntersectionObserver((entries) => {
|
|
23437
24566
|
if (entries.some((entry) => entry.isIntersecting)) controller.loadMore();
|
|
23438
|
-
}, {
|
|
24567
|
+
}, {
|
|
24568
|
+
root: scrollContainerOf(sentinel),
|
|
24569
|
+
rootMargin: PREFETCH_MARGIN
|
|
24570
|
+
});
|
|
23439
24571
|
observer.observe(sentinel);
|
|
23440
24572
|
return () => observer.disconnect();
|
|
23441
24573
|
}, [
|
|
23442
24574
|
controller,
|
|
24575
|
+
scrolled,
|
|
23443
24576
|
state.nextCursor,
|
|
23444
24577
|
state.loading,
|
|
23445
24578
|
state.loadingMore,
|
|
@@ -23488,7 +24621,7 @@ function(property) {
|
|
|
23488
24621
|
children: t("count", { count: state.items.length })
|
|
23489
24622
|
}), (0, react_jsx_runtime.jsx)(SourceStatusBar, {
|
|
23490
24623
|
sources: state.sources,
|
|
23491
|
-
onRefresh: () => void controller.refresh(),
|
|
24624
|
+
onRefresh: () => void controller.refresh({ force: true }),
|
|
23492
24625
|
refreshing: state.loading
|
|
23493
24626
|
})]
|
|
23494
24627
|
}),
|
|
@@ -23559,171 +24692,60 @@ function(property) {
|
|
|
23559
24692
|
className: MarketHome_module_css_default.emptyText,
|
|
23560
24693
|
children: narrowed ? t("emptySearchHint") : t("emptyHint")
|
|
23561
24694
|
})
|
|
23562
|
-
]
|
|
23563
|
-
}),
|
|
23564
|
-
hasItems && (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: (0, react_jsx_runtime.jsx)("div", {
|
|
23565
|
-
className: MarketHome_module_css_default.grid,
|
|
23566
|
-
children: state.items.map((skill) => (0, react_jsx_runtime.jsx)(SkillCard, {
|
|
23567
|
-
skill,
|
|
23568
|
-
installing: state.installingIds.has(skill.id),
|
|
23569
|
-
onOpen: (id) => void controller.openDetail(id),
|
|
23570
|
-
onInstall: (id) => controller.requestInstall(id)
|
|
23571
|
-
}, skill.id))
|
|
23572
|
-
}) }),
|
|
23573
|
-
state.nextCursor !== null && (0, react_jsx_runtime.jsx)("div", {
|
|
23574
|
-
ref: sentinelRef,
|
|
23575
|
-
className: MarketHome_module_css_default.more,
|
|
23576
|
-
children: state.loadingMore && (0, react_jsx_runtime.jsxs)("p", {
|
|
23577
|
-
className: MarketHome_module_css_default.loading,
|
|
23578
|
-
role: "status",
|
|
23579
|
-
"aria-live": "polite",
|
|
23580
|
-
children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, {
|
|
23581
|
-
state: "ongoing",
|
|
23582
|
-
size: 16
|
|
23583
|
-
}), t("loadingMore")]
|
|
23584
|
-
})
|
|
23585
|
-
})
|
|
23586
|
-
]
|
|
23587
|
-
})
|
|
23588
|
-
]
|
|
23589
|
-
});
|
|
23590
|
-
}
|
|
23591
|
-
//#endregion
|
|
23592
|
-
//#region \0dsh-css:src/client/components/FrontmatterPanel.module.css.mjs
|
|
23593
|
-
const css$1 = ".hR0bda_panel{border:1px solid var(--dsw-alias-border-l2,#0000001a);border-radius:var(--dsw-radius-sm,8px);background:var(--dsw-alias-bg-layer-1,#fff);flex-direction:column;min-width:0;display:flex;overflow:hidden}.hR0bda_head{border-bottom:1px solid var(--dsw-alias-border-l1,#0000000a);background:var(--dsw-alias-bg-module-platform,#f5f6f7);align-items:center;gap:8px;padding:8px 12px;display:flex}.hR0bda_title{min-width:0;color:var(--dsw-alias-label-tertiary,#81858c);letter-spacing:.08em;text-transform:uppercase;flex:1;margin:0;font-size:10.5px;font-weight:600}.hR0bda_count{color:var(--dsw-alias-label-tertiary,#81858c);font-variant-numeric:tabular-nums;font-size:11px}.hR0bda_list{max-height:320px;margin:0;padding:0;overflow-y:auto}.hR0bda_row{border-bottom:1px solid var(--dsw-alias-border-l1,#0000000a);flex-direction:column;gap:3px;min-width:0;padding:8px 12px;display:flex}.hR0bda_row:last-child{border-bottom:none}.hR0bda_key{overflow-wrap:anywhere;min-width:0;color:var(--dsw-alias-label-tertiary,#81858c);letter-spacing:.02em;font-size:10.5px;font-weight:600}.hR0bda_value{min-width:0;color:var(--dsw-alias-label-primary,#0f1115);margin:0;font-size:12.5px;line-height:19px}.hR0bda_text{overflow-wrap:anywhere}.hR0bda_chips{flex-wrap:wrap;gap:4px;display:flex}.hR0bda_chip{background:var(--dsw-alias-bg-module-platform,#f5f6f7);max-width:100%;color:var(--dsw-alias-label-secondary,#61666b);overflow-wrap:anywhere;border-radius:999px;padding:1px 7px;font-size:11px;line-height:17px;display:inline-flex}.hR0bda_chipOn{background:color-mix(in srgb, var(--dsw-alias-state-success-primary,#22c55e) 12%, transparent);color:var(--dsw-alias-state-success-primary,#22c55e)}.hR0bda_block{border-radius:var(--dsw-radius-xs,4px);background:var(--dsw-alias-bg-module-platform,#f5f6f7);max-height:200px;color:var(--dsw-alias-label-secondary,#61666b);white-space:pre-wrap;overflow-wrap:anywhere;margin:0;padding:8px 10px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11.5px;line-height:18px;overflow:auto}";
|
|
23594
|
-
const tagId$1 = "@nanmicoder/dsh-skills-hub/FrontmatterPanel.module.css";
|
|
23595
|
-
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
|
|
23596
|
-
const tag = document.createElement("style");
|
|
23597
|
-
tag.dataset.plugin = "@nanmicoder/dsh-skills-hub";
|
|
23598
|
-
tag.dataset.pluginCss = tagId$1;
|
|
23599
|
-
tag.textContent = css$1;
|
|
23600
|
-
document.head.appendChild(tag);
|
|
23601
|
-
}
|
|
23602
|
-
var FrontmatterPanel_module_css_default = {
|
|
23603
|
-
"block": "hR0bda_block",
|
|
23604
|
-
"chip": "hR0bda_chip",
|
|
23605
|
-
"chipOn": "hR0bda_chipOn",
|
|
23606
|
-
"chips": "hR0bda_chips",
|
|
23607
|
-
"count": "hR0bda_count",
|
|
23608
|
-
"head": "hR0bda_head",
|
|
23609
|
-
"key": "hR0bda_key",
|
|
23610
|
-
"list": "hR0bda_list",
|
|
23611
|
-
"panel": "hR0bda_panel",
|
|
23612
|
-
"row": "hR0bda_row",
|
|
23613
|
-
"text": "hR0bda_text",
|
|
23614
|
-
"title": "hR0bda_title",
|
|
23615
|
-
"value": "hR0bda_value"
|
|
23616
|
-
};
|
|
23617
|
-
//#endregion
|
|
23618
|
-
//#region lib/client/components/FrontmatterPanel.js
|
|
23619
|
-
/** Longest JSON we are willing to render inline, in characters. */
|
|
23620
|
-
const MAX_JSON_LENGTH = 600;
|
|
23621
|
-
/** Narrow an `unknown` frontmatter value to a plain object (arrays excluded). */
|
|
23622
|
-
function isRecord(value) {
|
|
23623
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
23624
|
-
}
|
|
23625
|
-
/** Scalar rendering for anything that is not an array, object or block string. */
|
|
23626
|
-
function scalarText(value) {
|
|
23627
|
-
if (value === null || value === void 0) return "—";
|
|
23628
|
-
if (typeof value === "string") return value;
|
|
23629
|
-
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return String(value);
|
|
23630
|
-
if (typeof value === "symbol") return value.description ?? "symbol";
|
|
23631
|
-
if (typeof value === "function") return "fn";
|
|
23632
|
-
return "—";
|
|
23633
|
-
}
|
|
23634
|
-
/** Objects go out as JSON; a pathological value must not fill the whole rail. */
|
|
23635
|
-
function jsonText(value) {
|
|
23636
|
-
try {
|
|
23637
|
-
const text = JSON.stringify(value, null, 2);
|
|
23638
|
-
if (text === void 0) return "—";
|
|
23639
|
-
return text.length > MAX_JSON_LENGTH ? `${text.slice(0, MAX_JSON_LENGTH)}…` : text;
|
|
23640
|
-
} catch {
|
|
23641
|
-
return "—";
|
|
23642
|
-
}
|
|
23643
|
-
}
|
|
23644
|
-
/** One value cell; the shape decides between a chip row, a code block and text. */
|
|
23645
|
-
function ValueView({ value }) {
|
|
23646
|
-
if (Array.isArray(value)) {
|
|
23647
|
-
if (value.length === 0) return (0, react_jsx_runtime.jsx)("span", {
|
|
23648
|
-
className: FrontmatterPanel_module_css_default.text,
|
|
23649
|
-
children: "—"
|
|
23650
|
-
});
|
|
23651
|
-
return (0, react_jsx_runtime.jsx)("span", {
|
|
23652
|
-
className: FrontmatterPanel_module_css_default.chips,
|
|
23653
|
-
children: value.map((item, index) => (0, react_jsx_runtime.jsx)("span", {
|
|
23654
|
-
className: FrontmatterPanel_module_css_default.chip,
|
|
23655
|
-
children: scalarText(item)
|
|
23656
|
-
}, `${String(index)}`))
|
|
23657
|
-
});
|
|
23658
|
-
}
|
|
23659
|
-
if (typeof value === "boolean") return (0, react_jsx_runtime.jsx)("span", {
|
|
23660
|
-
className: `${FrontmatterPanel_module_css_default.chip} ${value ? FrontmatterPanel_module_css_default.chipOn : ""}`,
|
|
23661
|
-
children: value ? "true" : "false"
|
|
23662
|
-
});
|
|
23663
|
-
if (typeof value === "string" && value.includes("\n")) return (0, react_jsx_runtime.jsx)("pre", {
|
|
23664
|
-
className: FrontmatterPanel_module_css_default.block,
|
|
23665
|
-
children: value
|
|
23666
|
-
});
|
|
23667
|
-
if (isRecord(value)) return (0, react_jsx_runtime.jsx)("pre", {
|
|
23668
|
-
className: FrontmatterPanel_module_css_default.block,
|
|
23669
|
-
children: jsonText(value)
|
|
23670
|
-
});
|
|
23671
|
-
return (0, react_jsx_runtime.jsx)("span", {
|
|
23672
|
-
className: FrontmatterPanel_module_css_default.text,
|
|
23673
|
-
children: scalarText(value)
|
|
24695
|
+
]
|
|
24696
|
+
}),
|
|
24697
|
+
hasItems && (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: (0, react_jsx_runtime.jsx)("div", {
|
|
24698
|
+
className: MarketHome_module_css_default.grid,
|
|
24699
|
+
children: state.items.map((skill) => (0, react_jsx_runtime.jsx)(SkillCard, {
|
|
24700
|
+
skill,
|
|
24701
|
+
installing: state.installingIds.has(skill.id),
|
|
24702
|
+
onOpen: (id) => void controller.openDetail(id),
|
|
24703
|
+
onInstall: (id) => controller.requestInstall(id)
|
|
24704
|
+
}, skill.id))
|
|
24705
|
+
}) }),
|
|
24706
|
+
state.nextCursor !== null && (0, react_jsx_runtime.jsx)("div", {
|
|
24707
|
+
ref: sentinelRef,
|
|
24708
|
+
className: MarketHome_module_css_default.more,
|
|
24709
|
+
children: state.loadingMore && (0, react_jsx_runtime.jsxs)("p", {
|
|
24710
|
+
className: MarketHome_module_css_default.loading,
|
|
24711
|
+
role: "status",
|
|
24712
|
+
"aria-live": "polite",
|
|
24713
|
+
children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, {
|
|
24714
|
+
state: "ongoing",
|
|
24715
|
+
size: 16
|
|
24716
|
+
}), t("loadingMore")]
|
|
24717
|
+
})
|
|
24718
|
+
})
|
|
24719
|
+
]
|
|
24720
|
+
})
|
|
24721
|
+
]
|
|
23674
24722
|
});
|
|
23675
24723
|
}
|
|
23676
|
-
|
|
23677
|
-
|
|
23678
|
-
|
|
23679
|
-
|
|
23680
|
-
|
|
23681
|
-
|
|
23682
|
-
|
|
23683
|
-
|
|
23684
|
-
|
|
23685
|
-
|
|
23686
|
-
* cell is derived defensively rather than trusted.
|
|
23687
|
-
*/
|
|
23688
|
-
function FrontmatterPanel(props) {
|
|
23689
|
-
const { data } = props;
|
|
23690
|
-
const t = useT();
|
|
23691
|
-
const entries = Object.entries(data);
|
|
23692
|
-
if (entries.length === 0) return (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, {});
|
|
23693
|
-
return (0, react_jsx_runtime.jsxs)("section", {
|
|
23694
|
-
className: FrontmatterPanel_module_css_default.panel,
|
|
23695
|
-
children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
23696
|
-
className: FrontmatterPanel_module_css_default.head,
|
|
23697
|
-
children: [(0, react_jsx_runtime.jsx)("h3", {
|
|
23698
|
-
className: FrontmatterPanel_module_css_default.title,
|
|
23699
|
-
children: t("frontmatter")
|
|
23700
|
-
}), (0, react_jsx_runtime.jsx)("span", {
|
|
23701
|
-
className: FrontmatterPanel_module_css_default.count,
|
|
23702
|
-
children: entries.length
|
|
23703
|
-
})]
|
|
23704
|
-
}), (0, react_jsx_runtime.jsx)("dl", {
|
|
23705
|
-
className: FrontmatterPanel_module_css_default.list,
|
|
23706
|
-
children: entries.map(([key, value]) => (0, react_jsx_runtime.jsxs)("div", {
|
|
23707
|
-
className: FrontmatterPanel_module_css_default.row,
|
|
23708
|
-
children: [(0, react_jsx_runtime.jsx)("dt", {
|
|
23709
|
-
className: FrontmatterPanel_module_css_default.key,
|
|
23710
|
-
title: key,
|
|
23711
|
-
children: key
|
|
23712
|
-
}), (0, react_jsx_runtime.jsx)("dd", {
|
|
23713
|
-
className: FrontmatterPanel_module_css_default.value,
|
|
23714
|
-
children: (0, react_jsx_runtime.jsx)(ValueView, { value })
|
|
23715
|
-
})]
|
|
23716
|
-
}, key))
|
|
23717
|
-
})]
|
|
23718
|
-
});
|
|
24724
|
+
//#endregion
|
|
24725
|
+
//#region \0dsh-css:src/client/components/SkillDetailView.module.css.mjs
|
|
24726
|
+
const css$1 = ".xO9T5G_warning{border-radius:var(--dsw-radius-sm,8px);background:color-mix(in srgb, var(--dsw-alias-state-error-primary,#ec1313) 10%, transparent);color:var(--dsw-alias-label-primary,#0f1115);align-items:center;gap:8px;margin:0;padding:8px 12px;font-size:12.5px;line-height:19px;display:flex}.xO9T5G_warningIcon{color:var(--dsw-alias-state-error-primary,#ec1313);flex:none}.xO9T5G_reports{border-top:1px solid var(--dsw-alias-border-l1,#0000000a);border-bottom:1px solid var(--dsw-alias-border-l1,#0000000a);flex-wrap:wrap;align-items:center;gap:8px 14px;padding:10px 2px;font-size:12.5px;line-height:19px;display:flex}.xO9T5G_reportsLabel{color:var(--dsw-alias-label-tertiary,#81858c)}.xO9T5G_report{flex-wrap:wrap;align-items:center;gap:8px;min-width:0;display:inline-flex}.xO9T5G_reportVendor{color:var(--dsw-alias-label-primary,#0f1115);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12.5px;font-weight:600}.xO9T5G_reportStatus{color:var(--dsw-alias-label-secondary,#61666b)}.xO9T5G_reportLink{background:color-mix(in srgb, var(--dsw-alias-state-business-primary,#4176e6) 12%, transparent);color:var(--dsw-alias-state-business-primary,#4176e6);border-radius:999px;align-items:center;padding:2px 9px;font-size:12px;font-weight:600;text-decoration:none;display:inline-flex}.xO9T5G_reportLink:hover{background:color-mix(in srgb, var(--dsw-alias-state-business-primary,#4176e6) 20%, transparent)}.xO9T5G_reportLink:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#4176e6);outline-offset:2px}.xO9T5G_snapshot{border-radius:var(--dsw-radius-sm,8px);background:color-mix(in srgb, var(--dsw-alias-state-warn-primary,#f59e0b) 12%, transparent);color:var(--dsw-alias-label-primary,#0f1115);flex-wrap:wrap;align-items:center;gap:10px;margin:0;padding:8px 12px;font-size:12.5px;line-height:19px;display:flex}.xO9T5G_snapshotAction{border:1px solid var(--dsw-alias-border-l2,#0000001a);background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-primary,#0f1115);font:inherit;cursor:pointer;border-radius:999px;padding:2px 9px;font-size:12px;font-weight:600}.xO9T5G_snapshotAction:disabled{opacity:.6;cursor:default}.xO9T5G_snapshotAction:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#4176e6);outline-offset:2px}";
|
|
24727
|
+
const tagId$1 = "@nanmicoder/dsh-skills-hub/SkillDetailView.module.css";
|
|
24728
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
|
|
24729
|
+
const tag = document.createElement("style");
|
|
24730
|
+
tag.dataset.plugin = "@nanmicoder/dsh-skills-hub";
|
|
24731
|
+
tag.dataset.pluginCss = tagId$1;
|
|
24732
|
+
tag.textContent = css$1;
|
|
24733
|
+
document.head.appendChild(tag);
|
|
23719
24734
|
}
|
|
24735
|
+
var SkillDetailView_module_css_default = {
|
|
24736
|
+
"report": "xO9T5G_report",
|
|
24737
|
+
"reportLink": "xO9T5G_reportLink",
|
|
24738
|
+
"reportStatus": "xO9T5G_reportStatus",
|
|
24739
|
+
"reportVendor": "xO9T5G_reportVendor",
|
|
24740
|
+
"reports": "xO9T5G_reports",
|
|
24741
|
+
"reportsLabel": "xO9T5G_reportsLabel",
|
|
24742
|
+
"snapshot": "xO9T5G_snapshot",
|
|
24743
|
+
"snapshotAction": "xO9T5G_snapshotAction",
|
|
24744
|
+
"warning": "xO9T5G_warning",
|
|
24745
|
+
"warningIcon": "xO9T5G_warningIcon"
|
|
24746
|
+
};
|
|
23720
24747
|
//#endregion
|
|
23721
24748
|
//#region lib/client/components/SkillDetailView.js
|
|
23722
|
-
/** How long the copy button stays in its confirmed state. */
|
|
23723
|
-
const COPIED_RESET_MS = 2e3;
|
|
23724
|
-
/** Stable DOM ids for the tab list and its panels; one detail view is mounted at a time. */
|
|
23725
|
-
const TAB_ID = "skills-hub-detail-tab";
|
|
23726
|
-
const PANEL_ID$1 = "skills-hub-detail-panel";
|
|
23727
24749
|
/** Compact counts, matching the catalogue cards. */
|
|
23728
24750
|
function formatCount(value) {
|
|
23729
24751
|
if (value >= 1e6) return `${(value / 1e6).toFixed(1)}M`;
|
|
@@ -23743,25 +24765,14 @@ function(property) {
|
|
|
23743
24765
|
function safeReportUrl(url) {
|
|
23744
24766
|
return url !== void 0 && /^https?:\/\//i.test(url) ? url : void 0;
|
|
23745
24767
|
}
|
|
23746
|
-
/** One label/value row of the right-hand facts rail. */
|
|
23747
|
-
function Fact(props) {
|
|
23748
|
-
return (0, react_jsx_runtime.jsxs)("div", {
|
|
23749
|
-
className: SkillDetailView_module_css_default.fact,
|
|
23750
|
-
children: [(0, react_jsx_runtime.jsx)("dt", {
|
|
23751
|
-
className: SkillDetailView_module_css_default.factLabel,
|
|
23752
|
-
children: props.label
|
|
23753
|
-
}), (0, react_jsx_runtime.jsx)("dd", {
|
|
23754
|
-
className: SkillDetailView_module_css_default.factValue,
|
|
23755
|
-
children: props.children
|
|
23756
|
-
})]
|
|
23757
|
-
});
|
|
23758
|
-
}
|
|
23759
24768
|
/**
|
|
23760
|
-
*
|
|
24769
|
+
* Market skill detail page.
|
|
23761
24770
|
*
|
|
23762
24771
|
* Like the home page this component owns no data: the tab, the selected file
|
|
23763
24772
|
* and the file contents all live in `MarketState`, so switching tabs or
|
|
23764
|
-
* re-opening a skill does not lose its place.
|
|
24773
|
+
* re-opening a skill does not lose its place. The reading surface itself — back
|
|
24774
|
+
* affordance, header, tabs, facts rail — is `SkillDetailShell`, which the local
|
|
24775
|
+
* installed page uses too.
|
|
23765
24776
|
*
|
|
23766
24777
|
* The bundled GFM renderer handles catalogue Markdown without relying on a
|
|
23767
24778
|
* host renderer delegate. Raw HTML is disabled and URLs use its safe default.
|
|
@@ -23769,101 +24780,39 @@ function(property) {
|
|
|
23769
24780
|
function SkillDetailView(props) {
|
|
23770
24781
|
const { detail, state, controller } = props;
|
|
23771
24782
|
const t = useT();
|
|
23772
|
-
const headingRef = (0, react.useRef)(null);
|
|
23773
|
-
const [copied, setCopied] = (0, react.useState)(false);
|
|
23774
|
-
const resetTimer = (0, react.useRef)(void 0);
|
|
23775
|
-
(0, react.useEffect)(() => {
|
|
23776
|
-
headingRef.current?.focus();
|
|
23777
|
-
}, [detail.id]);
|
|
23778
|
-
(0, react.useEffect)(() => () => {
|
|
23779
|
-
if (resetTimer.current !== void 0) clearTimeout(resetTimer.current);
|
|
23780
|
-
}, []);
|
|
23781
|
-
const copyFile = (0, react.useCallback)(() => {
|
|
23782
|
-
const content = state.file?.content;
|
|
23783
|
-
if (content === void 0) return;
|
|
23784
|
-
(0, _deepseek_ai_dsh_client_ui_primitives.writeClipboard)(content).then((ok) => {
|
|
23785
|
-
if (!ok) return;
|
|
23786
|
-
setCopied(true);
|
|
23787
|
-
if (resetTimer.current !== void 0) clearTimeout(resetTimer.current);
|
|
23788
|
-
resetTimer.current = setTimeout(() => setCopied(false), COPIED_RESET_MS);
|
|
23789
|
-
});
|
|
23790
|
-
}, [state.file]);
|
|
23791
|
-
const tabs = [{
|
|
23792
|
-
value: "overview",
|
|
23793
|
-
label: t("overview"),
|
|
23794
|
-
id: `${TAB_ID}-overview`,
|
|
23795
|
-
panelId: `${PANEL_ID$1}-overview`
|
|
23796
|
-
}, {
|
|
23797
|
-
value: "files",
|
|
23798
|
-
label: `${t("files")} (${detail.files.length})`,
|
|
23799
|
-
id: `${TAB_ID}-files`,
|
|
23800
|
-
panelId: `${PANEL_ID$1}-files`
|
|
23801
|
-
}];
|
|
23802
24783
|
const installationInFlight = state.installingIds.has(detail.id);
|
|
23803
24784
|
const author = detail.author.displayName ?? detail.author.handle;
|
|
23804
|
-
const
|
|
23805
|
-
const
|
|
23806
|
-
const
|
|
23807
|
-
|
|
23808
|
-
|
|
23809
|
-
|
|
23810
|
-
|
|
23811
|
-
|
|
23812
|
-
|
|
23813
|
-
|
|
23814
|
-
|
|
23815
|
-
|
|
23816
|
-
|
|
23817
|
-
|
|
23818
|
-
|
|
23819
|
-
|
|
23820
|
-
|
|
23821
|
-
|
|
23822
|
-
|
|
23823
|
-
|
|
23824
|
-
|
|
23825
|
-
|
|
23826
|
-
|
|
23827
|
-
|
|
23828
|
-
|
|
23829
|
-
|
|
23830
|
-
|
|
23831
|
-
|
|
23832
|
-
|
|
23833
|
-
|
|
23834
|
-
|
|
23835
|
-
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
23836
|
-
className: SkillDetailView_module_css_default.source,
|
|
23837
|
-
children: t(`source.${detail.source}`)
|
|
23838
|
-
}), detail.version !== void 0 && detail.version !== "" && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("span", {
|
|
23839
|
-
"aria-hidden": "true",
|
|
23840
|
-
children: "·"
|
|
23841
|
-
}), (0, react_jsx_runtime.jsxs)("span", {
|
|
23842
|
-
className: SkillDetailView_module_css_default.version,
|
|
23843
|
-
children: ["v", detail.version]
|
|
23844
|
-
})] })]
|
|
23845
|
-
}),
|
|
23846
|
-
(0, react_jsx_runtime.jsx)("h1", {
|
|
23847
|
-
ref: headingRef,
|
|
23848
|
-
tabIndex: -1,
|
|
23849
|
-
className: SkillDetailView_module_css_default.name,
|
|
23850
|
-
children: detail.name
|
|
23851
|
-
}),
|
|
23852
|
-
(0, react_jsx_runtime.jsxs)("div", {
|
|
23853
|
-
className: SkillDetailView_module_css_default.badges,
|
|
23854
|
-
children: [(0, react_jsx_runtime.jsx)(SecurityBadge, {
|
|
23855
|
-
status: detail.securityStatus,
|
|
23856
|
-
reports: detail.securityReports
|
|
23857
|
-
}), (0, react_jsx_runtime.jsx)(InstallStateBadge, { state: detail.installState })]
|
|
23858
|
-
}),
|
|
23859
|
-
detail.summary !== "" && (0, react_jsx_runtime.jsx)("p", {
|
|
23860
|
-
className: SkillDetailView_module_css_default.summary,
|
|
23861
|
-
children: detail.summary
|
|
23862
|
-
})
|
|
23863
|
-
]
|
|
23864
|
-
})]
|
|
23865
|
-
}),
|
|
23866
|
-
detail.installState === "not-installable" && detail.notInstallableReason !== void 0 && (0, react_jsx_runtime.jsxs)("p", {
|
|
24785
|
+
const reports = detail.securityReports ?? [];
|
|
24786
|
+
const snapshotAt = state.detailStatus?.fromCache === true ? state.detailStatus.fetchedAt : void 0;
|
|
24787
|
+
const notInstallable = detail.installState === "not-installable" && detail.notInstallableReason !== void 0;
|
|
24788
|
+
return (0, react_jsx_runtime.jsx)(SkillDetailShell, {
|
|
24789
|
+
focusKey: detail.id,
|
|
24790
|
+
onBack: () => controller.closeDetail(),
|
|
24791
|
+
backLabel: t("back"),
|
|
24792
|
+
avatar: (0, react_jsx_runtime.jsx)(SkillAvatar, {
|
|
24793
|
+
name: detail.name,
|
|
24794
|
+
source: detail.source,
|
|
24795
|
+
iconUrl: detail.iconUrl,
|
|
24796
|
+
size: 84
|
|
24797
|
+
}),
|
|
24798
|
+
eyebrow: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("span", {
|
|
24799
|
+
className: detailStyles.source,
|
|
24800
|
+
children: t(`source.${detail.source}`)
|
|
24801
|
+
}), detail.version !== void 0 && detail.version !== "" && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("span", {
|
|
24802
|
+
"aria-hidden": "true",
|
|
24803
|
+
children: "·"
|
|
24804
|
+
}), (0, react_jsx_runtime.jsxs)("span", {
|
|
24805
|
+
className: detailStyles.version,
|
|
24806
|
+
children: ["v", detail.version]
|
|
24807
|
+
})] })] }),
|
|
24808
|
+
name: detail.name,
|
|
24809
|
+
badges: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(SecurityBadge, {
|
|
24810
|
+
status: detail.securityStatus,
|
|
24811
|
+
reports: detail.securityReports
|
|
24812
|
+
}), (0, react_jsx_runtime.jsx)(InstallStateBadge, { state: detail.installState })] }),
|
|
24813
|
+
summary: detail.summary,
|
|
24814
|
+
notice: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
24815
|
+
notInstallable && (0, react_jsx_runtime.jsxs)("p", {
|
|
23867
24816
|
className: SkillDetailView_module_css_default.warning,
|
|
23868
24817
|
role: "note",
|
|
23869
24818
|
title: detail.notInstallableReason,
|
|
@@ -23872,12 +24821,12 @@ function(property) {
|
|
|
23872
24821
|
className: SkillDetailView_module_css_default.warningIcon
|
|
23873
24822
|
}), (0, react_jsx_runtime.jsx)("span", { children: t("notInstallable") })]
|
|
23874
24823
|
}),
|
|
23875
|
-
|
|
24824
|
+
reports.length > 0 && (0, react_jsx_runtime.jsxs)("div", {
|
|
23876
24825
|
className: SkillDetailView_module_css_default.reports,
|
|
23877
24826
|
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
23878
24827
|
className: SkillDetailView_module_css_default.reportsLabel,
|
|
23879
24828
|
children: t("security")
|
|
23880
|
-
}),
|
|
24829
|
+
}), reports.map((report) => {
|
|
23881
24830
|
const url = safeReportUrl(report.reportUrl);
|
|
23882
24831
|
return (0, react_jsx_runtime.jsxs)("span", {
|
|
23883
24832
|
className: SkillDetailView_module_css_default.report,
|
|
@@ -23901,245 +24850,96 @@ function(property) {
|
|
|
23901
24850
|
}, `${report.vendor}:${report.status}`);
|
|
23902
24851
|
})]
|
|
23903
24852
|
}),
|
|
23904
|
-
(0, react_jsx_runtime.jsxs)("
|
|
23905
|
-
className: SkillDetailView_module_css_default.
|
|
23906
|
-
|
|
23907
|
-
|
|
23908
|
-
|
|
23909
|
-
|
|
23910
|
-
|
|
23911
|
-
|
|
23912
|
-
|
|
23913
|
-
|
|
23914
|
-
|
|
23915
|
-
|
|
23916
|
-
className: SkillDetailView_module_css_default.panel,
|
|
23917
|
-
id: `${PANEL_ID$1}-overview`,
|
|
23918
|
-
role: "tabpanel",
|
|
23919
|
-
"aria-labelledby": `${TAB_ID}-overview`,
|
|
23920
|
-
children: [detail.description.trim() !== "" ? (0, react_jsx_runtime.jsx)(MarkdownView, { content: detail.description }) : detail.summary !== "" && (0, react_jsx_runtime.jsx)("p", {
|
|
23921
|
-
className: SkillDetailView_module_css_default.summary,
|
|
23922
|
-
children: detail.summary
|
|
23923
|
-
}), detail.descriptionFrontmatter !== void 0 && Object.keys(detail.descriptionFrontmatter).length > 0 && (0, react_jsx_runtime.jsx)("div", {
|
|
23924
|
-
className: SkillDetailView_module_css_default.frontmatter,
|
|
23925
|
-
children: (0, react_jsx_runtime.jsx)(FrontmatterPanel, { data: detail.descriptionFrontmatter })
|
|
23926
|
-
})]
|
|
23927
|
-
}),
|
|
23928
|
-
state.activeTab === "files" && (0, react_jsx_runtime.jsx)("section", {
|
|
23929
|
-
className: SkillDetailView_module_css_default.panel,
|
|
23930
|
-
id: `${PANEL_ID$1}-files`,
|
|
23931
|
-
role: "tabpanel",
|
|
23932
|
-
"aria-labelledby": `${TAB_ID}-files`,
|
|
23933
|
-
children: detail.files.length === 0 ? (0, react_jsx_runtime.jsx)("p", {
|
|
23934
|
-
className: SkillDetailView_module_css_default.muted,
|
|
23935
|
-
children: t("noFiles")
|
|
23936
|
-
}) : (0, react_jsx_runtime.jsxs)("div", {
|
|
23937
|
-
className: SkillDetailView_module_css_default.files,
|
|
23938
|
-
children: [(0, react_jsx_runtime.jsx)("ul", {
|
|
23939
|
-
className: SkillDetailView_module_css_default.fileList,
|
|
23940
|
-
children: detail.files.map((meta) => {
|
|
23941
|
-
const active = meta.path === selectedPath;
|
|
23942
|
-
return (0, react_jsx_runtime.jsx)("li", { children: (0, react_jsx_runtime.jsxs)("button", {
|
|
23943
|
-
type: "button",
|
|
23944
|
-
className: `${SkillDetailView_module_css_default.fileItem} ${active ? SkillDetailView_module_css_default.fileItemActive : ""}`,
|
|
23945
|
-
"aria-current": active ? "true" : void 0,
|
|
23946
|
-
onClick: () => selectFile(meta),
|
|
23947
|
-
children: [
|
|
23948
|
-
(0, react_jsx_runtime.jsx)(FileIcon, {
|
|
23949
|
-
size: 14,
|
|
23950
|
-
className: SkillDetailView_module_css_default.fileIcon
|
|
23951
|
-
}),
|
|
23952
|
-
(0, react_jsx_runtime.jsx)("span", {
|
|
23953
|
-
className: SkillDetailView_module_css_default.filePath,
|
|
23954
|
-
title: meta.path,
|
|
23955
|
-
children: meta.path
|
|
23956
|
-
}),
|
|
23957
|
-
meta.tooBig && (0, react_jsx_runtime.jsx)("span", {
|
|
23958
|
-
className: SkillDetailView_module_css_default.fileFlag,
|
|
23959
|
-
children: t("fileTooLarge")
|
|
23960
|
-
}),
|
|
23961
|
-
(0, react_jsx_runtime.jsxs)("span", {
|
|
23962
|
-
className: SkillDetailView_module_css_default.fileMeta,
|
|
23963
|
-
children: [
|
|
23964
|
-
(0, _deepseek_ai_dsh_client_ui_primitives.fileSizeText)(meta.size),
|
|
23965
|
-
" · ",
|
|
23966
|
-
meta.language
|
|
23967
|
-
]
|
|
23968
|
-
})
|
|
23969
|
-
]
|
|
23970
|
-
}) }, meta.path);
|
|
23971
|
-
})
|
|
23972
|
-
}), (0, react_jsx_runtime.jsx)("div", {
|
|
23973
|
-
className: SkillDetailView_module_css_default.fileView,
|
|
23974
|
-
children: file === null ? state.fileLoading ? (0, react_jsx_runtime.jsxs)("p", {
|
|
23975
|
-
className: SkillDetailView_module_css_default.muted,
|
|
23976
|
-
role: "status",
|
|
23977
|
-
"aria-live": "polite",
|
|
23978
|
-
children: [
|
|
23979
|
-
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, {
|
|
23980
|
-
state: "ongoing",
|
|
23981
|
-
size: 14
|
|
23982
|
-
}),
|
|
23983
|
-
" ",
|
|
23984
|
-
t("loading")
|
|
23985
|
-
]
|
|
23986
|
-
}) : null : (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
23987
|
-
(0, react_jsx_runtime.jsxs)("div", {
|
|
23988
|
-
className: SkillDetailView_module_css_default.fileBar,
|
|
23989
|
-
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
23990
|
-
className: SkillDetailView_module_css_default.fileBarPath,
|
|
23991
|
-
title: file.path,
|
|
23992
|
-
children: file.path
|
|
23993
|
-
}), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
23994
|
-
variant: "outline",
|
|
23995
|
-
size: "sm",
|
|
23996
|
-
className: SkillDetailView_module_css_default.copy,
|
|
23997
|
-
"aria-label": copied ? t("copied") : t("copy"),
|
|
23998
|
-
onClick: copyFile,
|
|
23999
|
-
icon: copied ? (0, react_jsx_runtime.jsx)(CheckIcon, { size: 14 }) : (0, react_jsx_runtime.jsx)(CopyIcon, { size: 14 }),
|
|
24000
|
-
children: copied ? t("copied") : t("copy")
|
|
24001
|
-
})]
|
|
24002
|
-
}),
|
|
24003
|
-
file.truncated && (0, react_jsx_runtime.jsx)("p", {
|
|
24004
|
-
className: SkillDetailView_module_css_default.muted,
|
|
24005
|
-
children: t("fileTooLarge")
|
|
24006
|
-
}),
|
|
24007
|
-
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.CodeBlock, {
|
|
24008
|
-
code: file.content,
|
|
24009
|
-
lang: file.language,
|
|
24010
|
-
showHeader: false,
|
|
24011
|
-
lineNumbers: true,
|
|
24012
|
-
copyLabel: t("copy"),
|
|
24013
|
-
copiedLabel: t("copied")
|
|
24014
|
-
}),
|
|
24015
|
-
(0, react_jsx_runtime.jsx)("p", {
|
|
24016
|
-
className: SkillDetailView_module_css_default.srOnly,
|
|
24017
|
-
role: "status",
|
|
24018
|
-
"aria-live": "polite",
|
|
24019
|
-
children: copied ? t("copied") : ""
|
|
24020
|
-
})
|
|
24021
|
-
] })
|
|
24022
|
-
})]
|
|
24023
|
-
})
|
|
24024
|
-
})
|
|
24025
|
-
]
|
|
24026
|
-
}), (0, react_jsx_runtime.jsx)("aside", {
|
|
24027
|
-
className: SkillDetailView_module_css_default.rail,
|
|
24028
|
-
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
24029
|
-
className: SkillDetailView_module_css_default.railCard,
|
|
24030
|
-
children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
24031
|
-
className: SkillDetailView_module_css_default.railAction,
|
|
24032
|
-
children: [
|
|
24033
|
-
detail.installState === "installable" && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
24034
|
-
variant: "primary",
|
|
24035
|
-
size: "md",
|
|
24036
|
-
disabled: installationInFlight,
|
|
24037
|
-
icon: (0, react_jsx_runtime.jsx)(DownloadIcon, { size: 16 }),
|
|
24038
|
-
onClick: () => controller.requestInstall(detail.id),
|
|
24039
|
-
children: installationInFlight ? t("installing") : t("install")
|
|
24040
|
-
}),
|
|
24041
|
-
detail.installState === "installed" && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
24042
|
-
variant: "outline",
|
|
24043
|
-
size: "md",
|
|
24044
|
-
disabled: installationInFlight,
|
|
24045
|
-
title: t("uninstallConfirm"),
|
|
24046
|
-
icon: (0, react_jsx_runtime.jsx)(TrashIcon, { size: 16 }),
|
|
24047
|
-
onClick: () => void controller.uninstall(detail.id),
|
|
24048
|
-
children: t("uninstall")
|
|
24049
|
-
}),
|
|
24050
|
-
detail.installState === "not-installable" && (0, react_jsx_runtime.jsx)("p", {
|
|
24051
|
-
className: SkillDetailView_module_css_default.muted,
|
|
24052
|
-
children: t("notInstallable")
|
|
24053
|
-
})
|
|
24054
|
-
]
|
|
24055
|
-
}), (0, react_jsx_runtime.jsxs)("dl", {
|
|
24056
|
-
className: SkillDetailView_module_css_default.facts,
|
|
24057
|
-
children: [
|
|
24058
|
-
(0, react_jsx_runtime.jsx)(Fact, {
|
|
24059
|
-
label: t("author"),
|
|
24060
|
-
children: author
|
|
24061
|
-
}),
|
|
24062
|
-
(0, react_jsx_runtime.jsx)(Fact, {
|
|
24063
|
-
label: t("downloads"),
|
|
24064
|
-
children: formatCount(detail.stats.downloads)
|
|
24065
|
-
}),
|
|
24066
|
-
detail.stats.installs !== void 0 && (0, react_jsx_runtime.jsx)(Fact, {
|
|
24067
|
-
label: t("installs"),
|
|
24068
|
-
children: formatCount(detail.stats.installs)
|
|
24069
|
-
}),
|
|
24070
|
-
detail.stats.stars !== void 0 && (0, react_jsx_runtime.jsx)(Fact, {
|
|
24071
|
-
label: t("stars"),
|
|
24072
|
-
children: formatCount(detail.stats.stars)
|
|
24073
|
-
}),
|
|
24074
|
-
detail.updatedAt !== void 0 && (0, react_jsx_runtime.jsx)(Fact, {
|
|
24075
|
-
label: t("updated"),
|
|
24076
|
-
children: formatDate(detail.updatedAt)
|
|
24077
|
-
}),
|
|
24078
|
-
detail.license !== void 0 && detail.license !== "" && (0, react_jsx_runtime.jsx)(Fact, {
|
|
24079
|
-
label: t("license"),
|
|
24080
|
-
children: detail.license
|
|
24081
|
-
}),
|
|
24082
|
-
detail.version !== void 0 && detail.version !== "" && (0, react_jsx_runtime.jsxs)(Fact, {
|
|
24083
|
-
label: t("version"),
|
|
24084
|
-
children: ["v", detail.version]
|
|
24085
|
-
})
|
|
24086
|
-
]
|
|
24087
|
-
})]
|
|
24088
|
-
})
|
|
24089
|
-
})]
|
|
24090
|
-
})
|
|
24091
|
-
]
|
|
24092
|
-
});
|
|
24093
|
-
}
|
|
24094
|
-
/** Keeps the detail layout stable while its network request is in flight. */
|
|
24095
|
-
function SkillDetailSkeleton({ onBack }) {
|
|
24096
|
-
const t = useT();
|
|
24097
|
-
return (0, react_jsx_runtime.jsxs)("div", {
|
|
24098
|
-
className: SkillDetailView_module_css_default.detail,
|
|
24099
|
-
"aria-busy": "true",
|
|
24100
|
-
children: [(0, react_jsx_runtime.jsx)("div", {
|
|
24101
|
-
className: SkillDetailView_module_css_default.back,
|
|
24102
|
-
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
24103
|
-
variant: "ghost",
|
|
24104
|
-
size: "sm",
|
|
24105
|
-
icon: (0, react_jsx_runtime.jsx)(ArrowLeftIcon, { size: 16 }),
|
|
24106
|
-
onClick: onBack,
|
|
24107
|
-
children: t("back")
|
|
24108
|
-
})
|
|
24109
|
-
}), (0, react_jsx_runtime.jsx)("div", {
|
|
24110
|
-
role: "status",
|
|
24111
|
-
"aria-label": t("loading"),
|
|
24112
|
-
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
24113
|
-
"aria-hidden": "true",
|
|
24114
|
-
children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
24115
|
-
className: SkillDetailView_module_css_default.header,
|
|
24116
|
-
children: [(0, react_jsx_runtime.jsx)("span", { className: SkillDetailView_module_css_default.skeletonAvatar }), (0, react_jsx_runtime.jsxs)("div", {
|
|
24117
|
-
className: SkillDetailView_module_css_default.headerText,
|
|
24118
|
-
children: [
|
|
24119
|
-
(0, react_jsx_runtime.jsx)("span", { className: SkillDetailView_module_css_default.skeletonTitle }),
|
|
24120
|
-
(0, react_jsx_runtime.jsx)("span", { className: SkillDetailView_module_css_default.skeletonLine }),
|
|
24121
|
-
(0, react_jsx_runtime.jsx)("span", { className: SkillDetailView_module_css_default.skeletonLine })
|
|
24122
|
-
]
|
|
24123
|
-
})]
|
|
24124
|
-
}), (0, react_jsx_runtime.jsxs)("div", {
|
|
24125
|
-
className: SkillDetailView_module_css_default.columns,
|
|
24126
|
-
style: { marginTop: 24 },
|
|
24127
|
-
children: [(0, react_jsx_runtime.jsx)("div", {
|
|
24128
|
-
className: SkillDetailView_module_css_default.main,
|
|
24129
|
-
children: (0, react_jsx_runtime.jsx)("div", {
|
|
24130
|
-
className: SkillDetailView_module_css_default.panel,
|
|
24131
|
-
children: Array.from({ length: 10 }, (_, index) => (0, react_jsx_runtime.jsx)("span", { className: SkillDetailView_module_css_default.skeletonLine }, index))
|
|
24132
|
-
})
|
|
24133
|
-
}), (0, react_jsx_runtime.jsx)("div", {
|
|
24134
|
-
className: SkillDetailView_module_css_default.rail,
|
|
24135
|
-
children: (0, react_jsx_runtime.jsx)("div", {
|
|
24136
|
-
className: SkillDetailView_module_css_default.panel,
|
|
24137
|
-
children: Array.from({ length: 5 }, (_, index) => (0, react_jsx_runtime.jsx)("span", { className: SkillDetailView_module_css_default.skeletonLine }, index))
|
|
24138
|
-
})
|
|
24139
|
-
})]
|
|
24853
|
+
snapshotAt !== void 0 && (0, react_jsx_runtime.jsxs)("p", {
|
|
24854
|
+
className: SkillDetailView_module_css_default.snapshot,
|
|
24855
|
+
role: "note",
|
|
24856
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
24857
|
+
title: formatStamp(snapshotAt),
|
|
24858
|
+
children: t("detailSnapshot", { age: formatAge(t, snapshotAt) })
|
|
24859
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
24860
|
+
type: "button",
|
|
24861
|
+
className: SkillDetailView_module_css_default.snapshotAction,
|
|
24862
|
+
disabled: state.detailLoading,
|
|
24863
|
+
onClick: () => void controller.openDetail(detail.id, { refresh: true }),
|
|
24864
|
+
children: t("refreshSnapshot")
|
|
24140
24865
|
})]
|
|
24141
24866
|
})
|
|
24142
|
-
})
|
|
24867
|
+
] }),
|
|
24868
|
+
overviewLabel: t("overview"),
|
|
24869
|
+
filesLabel: `${t("files")} (${detail.files.length})`,
|
|
24870
|
+
activeTab: state.activeTab,
|
|
24871
|
+
onTabChange: (tab) => controller.setTab(tab),
|
|
24872
|
+
overview: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [detail.description.trim() !== "" ? (0, react_jsx_runtime.jsx)(MarkdownView, { content: detail.description }) : detail.summary !== "" && (0, react_jsx_runtime.jsx)("p", {
|
|
24873
|
+
className: detailStyles.summary,
|
|
24874
|
+
children: detail.summary
|
|
24875
|
+
}), detail.descriptionFrontmatter !== void 0 && Object.keys(detail.descriptionFrontmatter).length > 0 && (0, react_jsx_runtime.jsx)("div", {
|
|
24876
|
+
className: detailStyles.frontmatter,
|
|
24877
|
+
children: (0, react_jsx_runtime.jsx)(FrontmatterPanel, { data: detail.descriptionFrontmatter })
|
|
24878
|
+
})] }),
|
|
24879
|
+
files: (0, react_jsx_runtime.jsx)(SkillFiles, {
|
|
24880
|
+
files: detail.files,
|
|
24881
|
+
selected: state.file,
|
|
24882
|
+
loading: state.fileLoading,
|
|
24883
|
+
onSelect: (path) => void controller.selectFile(path)
|
|
24884
|
+
}),
|
|
24885
|
+
rail: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
24886
|
+
className: detailStyles.railAction,
|
|
24887
|
+
children: [
|
|
24888
|
+
detail.installState === "installable" && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
24889
|
+
variant: "primary",
|
|
24890
|
+
size: "md",
|
|
24891
|
+
disabled: installationInFlight,
|
|
24892
|
+
icon: (0, react_jsx_runtime.jsx)(DownloadIcon, { size: 16 }),
|
|
24893
|
+
onClick: () => controller.requestInstall(detail.id),
|
|
24894
|
+
children: installationInFlight ? t("installing") : t("install")
|
|
24895
|
+
}),
|
|
24896
|
+
detail.installState === "installed" && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
24897
|
+
variant: "outline",
|
|
24898
|
+
size: "md",
|
|
24899
|
+
disabled: installationInFlight,
|
|
24900
|
+
title: t("uninstallConfirm"),
|
|
24901
|
+
icon: (0, react_jsx_runtime.jsx)(TrashIcon, { size: 16 }),
|
|
24902
|
+
onClick: () => void controller.uninstall(detail.id),
|
|
24903
|
+
children: t("uninstall")
|
|
24904
|
+
}),
|
|
24905
|
+
detail.installState === "not-installable" && (0, react_jsx_runtime.jsx)("p", {
|
|
24906
|
+
className: detailStyles.muted,
|
|
24907
|
+
children: t("notInstallable")
|
|
24908
|
+
})
|
|
24909
|
+
]
|
|
24910
|
+
}), (0, react_jsx_runtime.jsxs)("dl", {
|
|
24911
|
+
className: detailStyles.facts,
|
|
24912
|
+
children: [
|
|
24913
|
+
(0, react_jsx_runtime.jsx)(Fact, {
|
|
24914
|
+
label: t("author"),
|
|
24915
|
+
children: author
|
|
24916
|
+
}),
|
|
24917
|
+
(0, react_jsx_runtime.jsx)(Fact, {
|
|
24918
|
+
label: t("downloads"),
|
|
24919
|
+
children: formatCount(detail.stats.downloads)
|
|
24920
|
+
}),
|
|
24921
|
+
detail.stats.installs !== void 0 && (0, react_jsx_runtime.jsx)(Fact, {
|
|
24922
|
+
label: t("installs"),
|
|
24923
|
+
children: formatCount(detail.stats.installs)
|
|
24924
|
+
}),
|
|
24925
|
+
detail.stats.stars !== void 0 && (0, react_jsx_runtime.jsx)(Fact, {
|
|
24926
|
+
label: t("stars"),
|
|
24927
|
+
children: formatCount(detail.stats.stars)
|
|
24928
|
+
}),
|
|
24929
|
+
detail.updatedAt !== void 0 && (0, react_jsx_runtime.jsx)(Fact, {
|
|
24930
|
+
label: t("updated"),
|
|
24931
|
+
children: formatDate(detail.updatedAt)
|
|
24932
|
+
}),
|
|
24933
|
+
detail.license !== void 0 && detail.license !== "" && (0, react_jsx_runtime.jsx)(Fact, {
|
|
24934
|
+
label: t("license"),
|
|
24935
|
+
children: detail.license
|
|
24936
|
+
}),
|
|
24937
|
+
detail.version !== void 0 && detail.version !== "" && (0, react_jsx_runtime.jsxs)(Fact, {
|
|
24938
|
+
label: t("version"),
|
|
24939
|
+
children: ["v", detail.version]
|
|
24940
|
+
})
|
|
24941
|
+
]
|
|
24942
|
+
})] })
|
|
24143
24943
|
});
|
|
24144
24944
|
}
|
|
24145
24945
|
//#endregion
|
|
@@ -24159,8 +24959,8 @@ function(property) {
|
|
|
24159
24959
|
* already holds.
|
|
24160
24960
|
* - **in-flight guards**: one install/uninstall per id, one page per cursor, one
|
|
24161
24961
|
* detail/file request per target.
|
|
24162
|
-
* - **detail
|
|
24163
|
-
*
|
|
24962
|
+
* - **detail snapshots**: reopening can show the previous copy immediately,
|
|
24963
|
+
* while the Host rechecks its TTL and current install annotations.
|
|
24164
24964
|
* - **local management**: the InstalledSkills component independently lists what is already
|
|
24165
24965
|
* on disk (`fetchInstalled`) instead of asking the market, matching the
|
|
24166
24966
|
* contract's §5.3 behaviour.
|
|
@@ -24205,11 +25005,13 @@ function(property) {
|
|
|
24205
25005
|
items: [],
|
|
24206
25006
|
nextCursor: null,
|
|
24207
25007
|
sources: emptySources(),
|
|
25008
|
+
itemStatuses: {},
|
|
24208
25009
|
loading: false,
|
|
24209
25010
|
loadingMore: false,
|
|
24210
25011
|
error: null,
|
|
24211
25012
|
view: { kind: "home" },
|
|
24212
25013
|
detail: null,
|
|
25014
|
+
detailStatus: null,
|
|
24213
25015
|
detailLoading: false,
|
|
24214
25016
|
detailError: null,
|
|
24215
25017
|
activeTab: "overview",
|
|
@@ -24258,6 +25060,9 @@ function(property) {
|
|
|
24258
25060
|
[source]: status
|
|
24259
25061
|
};
|
|
24260
25062
|
}
|
|
25063
|
+
function itemStatuses(items, sources) {
|
|
25064
|
+
return Object.fromEntries(items.map((item) => [item.id, sources[item.source]]));
|
|
25065
|
+
}
|
|
24261
25066
|
/** Copy the install-related fields of a fresh skill onto a cached detail. */
|
|
24262
25067
|
function patchDetail(detail, updated) {
|
|
24263
25068
|
return {
|
|
@@ -24271,7 +25076,6 @@ function(property) {
|
|
|
24271
25076
|
let state = initialState();
|
|
24272
25077
|
const listeners = /* @__PURE__ */ new Set();
|
|
24273
25078
|
const detailCache = /* @__PURE__ */ new Map();
|
|
24274
|
-
const fileCache = /* @__PURE__ */ new Map();
|
|
24275
25079
|
let listSequence = 0;
|
|
24276
25080
|
let detailSequence = 0;
|
|
24277
25081
|
let fileSequence = 0;
|
|
@@ -24337,11 +25141,17 @@ function(property) {
|
|
|
24337
25141
|
} : item).filter(keepUnderInstalledFilter) };
|
|
24338
25142
|
if (state.detail !== null && state.detail.id === updated.id) {
|
|
24339
25143
|
const patched = patchDetail(state.detail, updated);
|
|
24340
|
-
detailCache.set(updated.id,
|
|
25144
|
+
detailCache.set(updated.id, {
|
|
25145
|
+
skill: patched,
|
|
25146
|
+
status: state.detailStatus ?? { status: "ok" }
|
|
25147
|
+
});
|
|
24341
25148
|
patch.detail = patched;
|
|
24342
25149
|
} else {
|
|
24343
25150
|
const cached = detailCache.get(updated.id);
|
|
24344
|
-
if (cached !== void 0) detailCache.set(updated.id,
|
|
25151
|
+
if (cached !== void 0) detailCache.set(updated.id, {
|
|
25152
|
+
skill: patchDetail(cached.skill, updated),
|
|
25153
|
+
status: cached.status
|
|
25154
|
+
});
|
|
24345
25155
|
}
|
|
24346
25156
|
commit(patch);
|
|
24347
25157
|
}
|
|
@@ -24353,7 +25163,7 @@ function(property) {
|
|
|
24353
25163
|
* development double-invocation repeats it. A different filter set always
|
|
24354
25164
|
* issues a fresh request.
|
|
24355
25165
|
*/
|
|
24356
|
-
async function refresh() {
|
|
25166
|
+
async function refresh(options = {}) {
|
|
24357
25167
|
cancelDebounce();
|
|
24358
25168
|
detailCache.clear();
|
|
24359
25169
|
const key = currentListKey();
|
|
@@ -24370,6 +25180,7 @@ function(property) {
|
|
|
24370
25180
|
loadingMore: false,
|
|
24371
25181
|
error: null,
|
|
24372
25182
|
items: [],
|
|
25183
|
+
itemStatuses: {},
|
|
24373
25184
|
nextCursor: null
|
|
24374
25185
|
});
|
|
24375
25186
|
const promise = (async () => {
|
|
@@ -24380,12 +25191,13 @@ function(property) {
|
|
|
24380
25191
|
security: filters.security,
|
|
24381
25192
|
installed: filters.installed,
|
|
24382
25193
|
limit: PAGE_SIZE
|
|
24383
|
-
}, controller.signal);
|
|
25194
|
+
}, controller.signal, { refresh: options.force === true });
|
|
24384
25195
|
if (sequence !== listSequence) return;
|
|
24385
25196
|
commit({
|
|
24386
25197
|
items: result.items,
|
|
24387
25198
|
nextCursor: result.nextCursor,
|
|
24388
25199
|
sources: result.sources,
|
|
25200
|
+
itemStatuses: itemStatuses(result.items, result.sources),
|
|
24389
25201
|
loading: false
|
|
24390
25202
|
});
|
|
24391
25203
|
} catch (error) {
|
|
@@ -24433,6 +25245,10 @@ function(property) {
|
|
|
24433
25245
|
items: [...state.items, ...appended],
|
|
24434
25246
|
nextCursor: result.nextCursor,
|
|
24435
25247
|
sources: result.sources,
|
|
25248
|
+
itemStatuses: {
|
|
25249
|
+
...state.itemStatuses,
|
|
25250
|
+
...itemStatuses(appended, result.sources)
|
|
25251
|
+
},
|
|
24436
25252
|
loadingMore: false
|
|
24437
25253
|
});
|
|
24438
25254
|
} catch (error) {
|
|
@@ -24471,32 +25287,42 @@ function(property) {
|
|
|
24471
25287
|
function isCurrentDetail(id) {
|
|
24472
25288
|
return state.view.kind === "detail" && state.view.id === id;
|
|
24473
25289
|
}
|
|
24474
|
-
async function loadDetail(id) {
|
|
25290
|
+
async function loadDetail(id, options = {}) {
|
|
24475
25291
|
const sequence = ++detailSequence;
|
|
24476
25292
|
detailAbort?.abort();
|
|
24477
25293
|
const controller = new AbortController();
|
|
24478
25294
|
detailAbort = controller;
|
|
24479
25295
|
try {
|
|
24480
|
-
const { skill, sourceStatus } = await fetchSkillDetail(id, controller.signal);
|
|
25296
|
+
const { skill, sourceStatus } = await fetchSkillDetail(id, controller.signal, { refresh: options.refresh === true });
|
|
24481
25297
|
if (sequence !== detailSequence || !isCurrentDetail(id)) return;
|
|
24482
|
-
detailCache.set(id,
|
|
25298
|
+
detailCache.set(id, {
|
|
25299
|
+
skill,
|
|
25300
|
+
status: sourceStatus
|
|
25301
|
+
});
|
|
24483
25302
|
commit({
|
|
24484
25303
|
detail: skill,
|
|
25304
|
+
detailStatus: sourceStatus,
|
|
24485
25305
|
detailLoading: false,
|
|
24486
25306
|
detailError: null,
|
|
24487
25307
|
sources: mergeSourceStatus(state.sources, skill.source, sourceStatus)
|
|
24488
25308
|
});
|
|
24489
25309
|
} catch (error) {
|
|
24490
25310
|
if (sequence !== detailSequence || isAbortError(error)) return;
|
|
25311
|
+
detailCache.delete(id);
|
|
24491
25312
|
commit({
|
|
25313
|
+
detail: null,
|
|
25314
|
+
detailStatus: null,
|
|
24492
25315
|
detailLoading: false,
|
|
24493
25316
|
detailError: errorMessage(error)
|
|
24494
25317
|
});
|
|
24495
25318
|
}
|
|
24496
25319
|
}
|
|
24497
|
-
/**
|
|
24498
|
-
async function openDetail(id) {
|
|
24499
|
-
const cached = detailCache.get(id);
|
|
25320
|
+
/** Show a previous copy immediately, then check the Host's authoritative cache. */
|
|
25321
|
+
async function openDetail(id, options = {}) {
|
|
25322
|
+
const cached = options.refresh === true ? void 0 : detailCache.get(id);
|
|
25323
|
+
fileSequence += 1;
|
|
25324
|
+
fileAbort?.abort();
|
|
25325
|
+
inFlightFileKey = null;
|
|
24500
25326
|
commit({
|
|
24501
25327
|
view: {
|
|
24502
25328
|
kind: "detail",
|
|
@@ -24507,18 +25333,20 @@ function(property) {
|
|
|
24507
25333
|
fileLoading: false,
|
|
24508
25334
|
detailError: null
|
|
24509
25335
|
});
|
|
24510
|
-
if (cached !== void 0) {
|
|
24511
|
-
|
|
24512
|
-
|
|
24513
|
-
|
|
24514
|
-
|
|
24515
|
-
|
|
24516
|
-
|
|
24517
|
-
|
|
25336
|
+
if (cached !== void 0) commit({
|
|
25337
|
+
detail: cached.skill,
|
|
25338
|
+
detailStatus: {
|
|
25339
|
+
...cached.status,
|
|
25340
|
+
fromCache: true
|
|
25341
|
+
},
|
|
25342
|
+
detailLoading: false
|
|
25343
|
+
});
|
|
25344
|
+
else commit({
|
|
24518
25345
|
detail: null,
|
|
25346
|
+
detailStatus: null,
|
|
24519
25347
|
detailLoading: true
|
|
24520
25348
|
});
|
|
24521
|
-
await loadDetail(id);
|
|
25349
|
+
await loadDetail(id, options);
|
|
24522
25350
|
}
|
|
24523
25351
|
function closeDetail() {
|
|
24524
25352
|
detailSequence += 1;
|
|
@@ -24528,6 +25356,7 @@ function(property) {
|
|
|
24528
25356
|
commit({
|
|
24529
25357
|
view: { kind: "home" },
|
|
24530
25358
|
detail: null,
|
|
25359
|
+
detailStatus: null,
|
|
24531
25360
|
detailLoading: false,
|
|
24532
25361
|
detailError: null,
|
|
24533
25362
|
file: null,
|
|
@@ -24545,19 +25374,11 @@ function(property) {
|
|
|
24545
25374
|
if (first === void 0) return;
|
|
24546
25375
|
selectFile(first.path);
|
|
24547
25376
|
}
|
|
24548
|
-
/**
|
|
25377
|
+
/** Read through the Host's TTL cache, with one in-flight request per path. */
|
|
24549
25378
|
async function selectFile(path) {
|
|
24550
25379
|
if (state.view.kind !== "detail") return;
|
|
24551
25380
|
const id = state.view.id;
|
|
24552
25381
|
const key = fileCacheKey(id, path);
|
|
24553
|
-
const cached = fileCache.get(key);
|
|
24554
|
-
if (cached !== void 0) {
|
|
24555
|
-
commit({
|
|
24556
|
-
file: cached,
|
|
24557
|
-
fileLoading: false
|
|
24558
|
-
});
|
|
24559
|
-
return;
|
|
24560
|
-
}
|
|
24561
25382
|
if (inFlightFileKey === key) return;
|
|
24562
25383
|
const sequence = ++fileSequence;
|
|
24563
25384
|
fileAbort?.abort();
|
|
@@ -24571,7 +25392,6 @@ function(property) {
|
|
|
24571
25392
|
try {
|
|
24572
25393
|
const file = await fetchSkillFile(id, path, controller.signal);
|
|
24573
25394
|
if (sequence !== fileSequence) return;
|
|
24574
|
-
fileCache.set(key, file);
|
|
24575
25395
|
commit({
|
|
24576
25396
|
file,
|
|
24577
25397
|
fileLoading: false
|
|
@@ -24768,10 +25588,16 @@ function(property) {
|
|
|
24768
25588
|
}),
|
|
24769
25589
|
(0, react_jsx_runtime.jsx)("div", {
|
|
24770
25590
|
className: MarketPage_module_css_default.body,
|
|
24771
|
-
children: section === "installed" ? (0, react_jsx_runtime.jsx)(InstalledSkills, {
|
|
24772
|
-
|
|
24773
|
-
|
|
24774
|
-
|
|
25591
|
+
children: section === "installed" ? (0, react_jsx_runtime.jsx)(InstalledSkills, {
|
|
25592
|
+
onChanged: () => {
|
|
25593
|
+
controller.closeDetail();
|
|
25594
|
+
controller.refresh();
|
|
25595
|
+
},
|
|
25596
|
+
onOpenMarket: (id) => {
|
|
25597
|
+
setSection("market");
|
|
25598
|
+
controller.openDetail(id);
|
|
25599
|
+
}
|
|
25600
|
+
}) : state.view.kind === "home" ? (0, react_jsx_runtime.jsx)(MarketHome, {
|
|
24775
25601
|
state,
|
|
24776
25602
|
controller
|
|
24777
25603
|
}) : state.detail !== null ? (0, react_jsx_runtime.jsx)(SkillDetailView, {
|
|
@@ -24839,14 +25665,19 @@ function(property) {
|
|
|
24839
25665
|
*/
|
|
24840
25666
|
function confirmSkillOf(state, id) {
|
|
24841
25667
|
const skill = (state.detail !== null && state.detail.id === id ? state.detail : null) ?? state.items.find((item) => item.id === id) ?? null;
|
|
24842
|
-
if (skill !== null)
|
|
24843
|
-
id: skill.id
|
|
24844
|
-
|
|
24845
|
-
|
|
24846
|
-
|
|
24847
|
-
|
|
24848
|
-
|
|
24849
|
-
|
|
25668
|
+
if (skill !== null) {
|
|
25669
|
+
const status = state.detail !== null && state.detail.id === id ? state.detailStatus : state.itemStatuses[skill.id];
|
|
25670
|
+
const snapshotAt = status?.fromCache === true ? status.fetchedAt : void 0;
|
|
25671
|
+
return {
|
|
25672
|
+
id: skill.id,
|
|
25673
|
+
name: skill.name,
|
|
25674
|
+
source: skill.source,
|
|
25675
|
+
version: skill.version,
|
|
25676
|
+
securityStatus: skill.securityStatus,
|
|
25677
|
+
authorName: skill.author.displayName ?? skill.author.handle,
|
|
25678
|
+
...snapshotAt === void 0 ? {} : { snapshotAt }
|
|
25679
|
+
};
|
|
25680
|
+
}
|
|
24850
25681
|
return {
|
|
24851
25682
|
id,
|
|
24852
25683
|
name: id,
|
|
@@ -24885,6 +25716,34 @@ function(property) {
|
|
|
24885
25716
|
localUninstallDescription: "将删除下方路径的技能及其全部文件。共享 agents 目录中的技能也会影响其他使用它的工具。",
|
|
24886
25717
|
unlinkDescription: "仅移除此技能的符号链接,保留链接目标及其文件。",
|
|
24887
25718
|
uninstalling: "正在卸载…",
|
|
25719
|
+
uninstallDisabled: "卸载已在插件配置中禁用。",
|
|
25720
|
+
copyPath: "复制路径",
|
|
25721
|
+
copiedPath: "已复制路径",
|
|
25722
|
+
sourceLabel: "来源",
|
|
25723
|
+
managedByHub: "由 Skills Hub 安装",
|
|
25724
|
+
unmanagedSkill: "手工放置",
|
|
25725
|
+
symlinkSkill: "符号链接",
|
|
25726
|
+
installedAtLabel: "安装时间",
|
|
25727
|
+
skillKind: "形态",
|
|
25728
|
+
bundleSkill: "目录包",
|
|
25729
|
+
flatSkill: "单文件",
|
|
25730
|
+
filesCount: "{count} 个文件",
|
|
25731
|
+
emptyDocument: "这个技能没有正文内容。",
|
|
25732
|
+
installedReadFailed: "读取本地技能失败。",
|
|
25733
|
+
openInMarket: "在市场查看",
|
|
25734
|
+
fileView: "文件视图",
|
|
25735
|
+
filePreview: "预览",
|
|
25736
|
+
fileSource: "源码",
|
|
25737
|
+
snapshotAge: "快照 {age}",
|
|
25738
|
+
ageJustNow: "刚刚",
|
|
25739
|
+
ageMinutes: "{count} 分钟前",
|
|
25740
|
+
ageHours: "{count} 小时前",
|
|
25741
|
+
ageDays: "{count} 天前",
|
|
25742
|
+
refreshNow: "重新获取(跳过缓存)",
|
|
25743
|
+
refreshSnapshot: "重新获取",
|
|
25744
|
+
detailSnapshot: "以下内容来自 {age} 的缓存快照,作者可能已经更新。",
|
|
25745
|
+
installSnapshot: "这里显示的市场数据是 {age} 的快照;点确认后会重新拉取最新清单再安装。",
|
|
25746
|
+
marketProvenance: "这个技能来自技能市场:作者说明、统计与安全报告在市场的详情页里;本地这一份可能已经改动过。",
|
|
24888
25747
|
panel: "技能市场",
|
|
24889
25748
|
title: "技能市场",
|
|
24890
25749
|
subtitle: "浏览、预览并安装来自 ClawHub 与 SkillHub 的第三方技能。",
|
|
@@ -24893,6 +25752,7 @@ function(property) {
|
|
|
24893
25752
|
"source.all": "全部来源",
|
|
24894
25753
|
"source.clawhub": "ClawHub",
|
|
24895
25754
|
"source.skillhub": "SkillHub",
|
|
25755
|
+
"source.local": "本机技能",
|
|
24896
25756
|
"security.all": "全部安全状态",
|
|
24897
25757
|
"security.verified": "已验证",
|
|
24898
25758
|
"security.benign": "无风险",
|
|
@@ -24952,6 +25812,7 @@ function(property) {
|
|
|
24952
25812
|
uninstallDone: "已卸载",
|
|
24953
25813
|
noFiles: "这个技能没有可预览的文件。",
|
|
24954
25814
|
fileTooLarge: "文件过大,无法预览。",
|
|
25815
|
+
fileTruncated: "文件过大,仅显示开头部分。",
|
|
24955
25816
|
copy: "复制",
|
|
24956
25817
|
copied: "已复制",
|
|
24957
25818
|
unoaudited: "未审计"
|
|
@@ -24974,6 +25835,34 @@ function(property) {
|
|
|
24974
25835
|
localUninstallDescription: "Delete the skill and all files at the path below. Removing shared agents skills also affects other tools using them.",
|
|
24975
25836
|
unlinkDescription: "Remove only this skill’s symbolic link. The target and its files will be kept.",
|
|
24976
25837
|
uninstalling: "Uninstalling…",
|
|
25838
|
+
uninstallDisabled: "Uninstalling is disabled by the plugin configuration.",
|
|
25839
|
+
copyPath: "Copy path",
|
|
25840
|
+
copiedPath: "Path copied",
|
|
25841
|
+
sourceLabel: "Source",
|
|
25842
|
+
managedByHub: "Installed by Skills Hub",
|
|
25843
|
+
unmanagedSkill: "Placed by hand",
|
|
25844
|
+
symlinkSkill: "Symlink",
|
|
25845
|
+
installedAtLabel: "Installed",
|
|
25846
|
+
skillKind: "Layout",
|
|
25847
|
+
bundleSkill: "Directory bundle",
|
|
25848
|
+
flatSkill: "Single file",
|
|
25849
|
+
filesCount: "{count} files",
|
|
25850
|
+
emptyDocument: "This skill has no document body.",
|
|
25851
|
+
installedReadFailed: "Could not read the local skill.",
|
|
25852
|
+
openInMarket: "View in marketplace",
|
|
25853
|
+
fileView: "File view",
|
|
25854
|
+
filePreview: "Preview",
|
|
25855
|
+
fileSource: "Source",
|
|
25856
|
+
snapshotAge: "Snapshot {age}",
|
|
25857
|
+
ageJustNow: "just now",
|
|
25858
|
+
ageMinutes: "{count} min ago",
|
|
25859
|
+
ageHours: "{count} h ago",
|
|
25860
|
+
ageDays: "{count} d ago",
|
|
25861
|
+
refreshNow: "Refetch (skip cache)",
|
|
25862
|
+
refreshSnapshot: "Refetch",
|
|
25863
|
+
detailSnapshot: "This content is a cached snapshot from {age}; the author may have published since.",
|
|
25864
|
+
installSnapshot: "The marketplace data shown here is a snapshot from {age}; confirming refetches the current manifest before installing.",
|
|
25865
|
+
marketProvenance: "This skill came from a marketplace: the author description, statistics and security reports live on its marketplace page, while this local copy may have changed since.",
|
|
24977
25866
|
panel: "Skills Hub",
|
|
24978
25867
|
title: "Skills Hub",
|
|
24979
25868
|
subtitle: "Browse, preview and install third-party skills from ClawHub and SkillHub.",
|
|
@@ -24982,6 +25871,7 @@ function(property) {
|
|
|
24982
25871
|
"source.all": "All sources",
|
|
24983
25872
|
"source.clawhub": "ClawHub",
|
|
24984
25873
|
"source.skillhub": "SkillHub",
|
|
25874
|
+
"source.local": "Local skill",
|
|
24985
25875
|
"security.all": "Any security status",
|
|
24986
25876
|
"security.verified": "Verified",
|
|
24987
25877
|
"security.benign": "Benign",
|
|
@@ -25041,6 +25931,7 @@ function(property) {
|
|
|
25041
25931
|
uninstallDone: "Uninstalled",
|
|
25042
25932
|
noFiles: "This skill has no previewable files.",
|
|
25043
25933
|
fileTooLarge: "The file is too large to preview.",
|
|
25934
|
+
fileTruncated: "The file is too large; only the beginning is shown.",
|
|
25044
25935
|
copy: "Copy",
|
|
25045
25936
|
copied: "Copied",
|
|
25046
25937
|
unoaudited: "Not audited"
|