@nanmicoder/dsh-skills-hub 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +9 -0
- package/CONTRIBUTING.md +11 -0
- package/LICENSE +21 -0
- package/README.md +120 -0
- package/README_EN.md +120 -0
- package/README_ZH.md +5 -0
- package/assets/readme/hero-en.png +0 -0
- package/assets/readme/hero-zh.png +0 -0
- package/assets/readme/marketplace.png +0 -0
- package/compatibility.json +10 -0
- package/cordis.patch.yml +5 -0
- package/docs/COMPATIBILITY.md +31 -0
- package/docs/CONTRACT.md +125 -0
- package/docs/RELEASE.md +24 -0
- package/lib/client.js +25115 -0
- package/lib/client.js.map +1 -0
- package/lib/config.js +51 -0
- package/lib/index.js +176 -0
- package/lib/market/cache.js +113 -0
- package/lib/market/clawhub-provider.js +598 -0
- package/lib/market/install-service.js +394 -0
- package/lib/market/market-service.js +312 -0
- package/lib/market/provider-fetch.js +216 -0
- package/lib/market/skillhub-provider.js +277 -0
- package/lib/market/types.js +82 -0
- package/lib/skills/installed.js +426 -0
- package/lib/skills/management.js +36 -0
- package/lib/skills/read-text.js +23 -0
- package/lib/skills/root.js +112 -0
- package/lib/types/client/api.d.ts +106 -0
- package/lib/types/client/components/FilterBar.d.ts +20 -0
- package/lib/types/client/components/FrontmatterPanel.d.ts +15 -0
- package/lib/types/client/components/InstallConfirmDialog.d.ts +31 -0
- package/lib/types/client/components/InstallStateBadge.d.ts +11 -0
- package/lib/types/client/components/InstalledSkills.d.ts +3 -0
- package/lib/types/client/components/MarkdownView.d.ts +4 -0
- package/lib/types/client/components/MarketDisclaimer.d.ts +10 -0
- package/lib/types/client/components/MarketHome.d.ts +12 -0
- package/lib/types/client/components/SecurityBadge.d.ts +16 -0
- package/lib/types/client/components/SkillAvatar.d.ts +14 -0
- package/lib/types/client/components/SkillCard.d.ts +17 -0
- package/lib/types/client/components/SkillDetailView.d.ts +21 -0
- package/lib/types/client/components/SourceStatusBar.d.ts +13 -0
- package/lib/types/client/icons.d.ts +71 -0
- package/lib/types/client/index.d.ts +56 -0
- package/lib/types/client/locale-context.d.ts +31 -0
- package/lib/types/client/locales.d.ts +117 -0
- package/lib/types/client/page/MarketPage.d.ts +21 -0
- package/lib/types/client/skill-markdown.d.ts +2 -0
- package/lib/types/client/state.d.ts +119 -0
- package/lib/types/config.d.ts +51 -0
- package/lib/types/index.d.ts +42 -0
- package/lib/types/market/cache.d.ts +49 -0
- package/lib/types/market/clawhub-provider.d.ts +28 -0
- package/lib/types/market/install-service.d.ts +75 -0
- package/lib/types/market/market-service.d.ts +74 -0
- package/lib/types/market/provider-fetch.d.ts +61 -0
- package/lib/types/market/skillhub-provider.d.ts +13 -0
- package/lib/types/market/types.d.ts +163 -0
- package/lib/types/skills/installed.d.ts +117 -0
- package/lib/types/skills/management.d.ts +7 -0
- package/lib/types/skills/read-text.d.ts +5 -0
- package/lib/types/skills/root.d.ts +48 -0
- package/lib/types/web-routes.d.ts +74 -0
- package/lib/web-routes.js +427 -0
- package/package.json +160 -0
- package/release-notes/v0.0.1.md +11 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skills Hub HTTP client (browser half).
|
|
3
|
+
*
|
|
4
|
+
* Every request goes to the Host surface documented in docs/CONTRACT.md §5.2:
|
|
5
|
+
* one prefix route under `/api/skills-hub`, JSON in and JSON out, errors always
|
|
6
|
+
* shaped `{ error: { code, message } }`.
|
|
7
|
+
*
|
|
8
|
+
* Client purity: this module imports *types only* from the shared wire layer —
|
|
9
|
+
* never a value from the Host's `src/market/*`, which would drag Node code into
|
|
10
|
+
* the browser bundle (contract §2).
|
|
11
|
+
*/
|
|
12
|
+
import type { MarketFileContent, MarketListResult, MarketSource, NormalizedSkill, NormalizedSkillDetail, SecurityStatus, SourceStatusInfo } from '../market/types.ts';
|
|
13
|
+
/**
|
|
14
|
+
* Structural mirror of the Host's `InstalledSkillRecord`
|
|
15
|
+
* (`src/skills/installed.ts`, contract §5.2).
|
|
16
|
+
*
|
|
17
|
+
* Declared here instead of `import type`-ing the Host module on purpose: that
|
|
18
|
+
* module reads the filesystem through `node:fs`, so pulling it into the browser
|
|
19
|
+
* program would drag Node-only code (and its missing `node:*` declarations)
|
|
20
|
+
* across the client purity boundary. The client only ever *reads* this shape out
|
|
21
|
+
* of JSON, so a structural mirror is sufficient — the wire contract stays the
|
|
22
|
+
* single source of truth.
|
|
23
|
+
*/
|
|
24
|
+
export interface InstalledSkillRecord {
|
|
25
|
+
key: string;
|
|
26
|
+
linked: boolean;
|
|
27
|
+
removable: boolean;
|
|
28
|
+
/** `source:slug` when the provenance sidecar exists, else `local:<dir>`. */
|
|
29
|
+
id: string;
|
|
30
|
+
source: MarketSource | 'local';
|
|
31
|
+
slug: string;
|
|
32
|
+
name: string;
|
|
33
|
+
dirName: string;
|
|
34
|
+
dirPath: string;
|
|
35
|
+
version?: string;
|
|
36
|
+
summary?: string;
|
|
37
|
+
installedAt?: string;
|
|
38
|
+
/** Installed by this plugin (the `.skills-hub.json` sidecar is present). */
|
|
39
|
+
managed: boolean;
|
|
40
|
+
bytes: number;
|
|
41
|
+
fileCount: number;
|
|
42
|
+
}
|
|
43
|
+
/** List query accepted by `GET /api/skills-hub/skills`. */
|
|
44
|
+
export interface MarketQuery {
|
|
45
|
+
q?: string;
|
|
46
|
+
source: 'all' | MarketSource;
|
|
47
|
+
security: 'all' | SecurityStatus;
|
|
48
|
+
installed: 'all' | 'installed' | 'installable';
|
|
49
|
+
cursor?: string;
|
|
50
|
+
limit?: number;
|
|
51
|
+
}
|
|
52
|
+
/** One failure from the Skills Hub surface (transport, HTTP, or malformed body). */
|
|
53
|
+
export declare class SkillsHubApiError extends Error {
|
|
54
|
+
/** Host error code (`MARKET_ERROR_CODES` value, `BAD_REQUEST`, …) or a client-side code. */
|
|
55
|
+
readonly code: string;
|
|
56
|
+
/** HTTP status; `0` when the request never reached the Host. */
|
|
57
|
+
readonly status: number;
|
|
58
|
+
constructor(code: string, status: number, message: string);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Whether a rejection is an aborted request.
|
|
62
|
+
*
|
|
63
|
+
* `AbortError` is control flow, not a failure: the controller aborts superseded
|
|
64
|
+
* requests on purpose, and surfacing one as a user-facing error would put a
|
|
65
|
+
* spurious banner in front of a perfectly healthy panel. Checked by `name`
|
|
66
|
+
* (not `instanceof`) because an abort may cross realms (iframes, workers).
|
|
67
|
+
* Exported so the state layer applies the exact same rule.
|
|
68
|
+
*/
|
|
69
|
+
export declare function isAbortError(error: unknown): boolean;
|
|
70
|
+
/** Search the catalogue (market sources) — the remote list page. */
|
|
71
|
+
export declare function fetchMarketList(query: MarketQuery, signal?: AbortSignal): Promise<MarketListResult>;
|
|
72
|
+
/** One skill's full detail plus the health of the source that served it. */
|
|
73
|
+
export declare function fetchSkillDetail(id: string, signal?: AbortSignal): Promise<{
|
|
74
|
+
skill: NormalizedSkillDetail;
|
|
75
|
+
sourceStatus: SourceStatusInfo;
|
|
76
|
+
}>;
|
|
77
|
+
/** One file of a skill, for the Files tab. */
|
|
78
|
+
export declare function fetchSkillFile(id: string, path: string, signal?: AbortSignal): Promise<MarketFileContent>;
|
|
79
|
+
/** Health of every market source, refreshed independently of the list. */
|
|
80
|
+
export declare function fetchSourceStatus(signal?: AbortSignal): Promise<Record<MarketSource, SourceStatusInfo>>;
|
|
81
|
+
/**
|
|
82
|
+
* Every skill DSH can currently see, including directories this plugin did not
|
|
83
|
+
* install (`managed: false`). This is the local index behind the "installed"
|
|
84
|
+
* filter — no upstream request is involved.
|
|
85
|
+
*/
|
|
86
|
+
export declare function fetchInstalled(signal?: AbortSignal): Promise<InstalledSkillRecord[]>;
|
|
87
|
+
/** Install a market skill into the local skills directory. */
|
|
88
|
+
export declare function installSkill(id: string): Promise<{
|
|
89
|
+
installedPath: string;
|
|
90
|
+
skill: NormalizedSkill;
|
|
91
|
+
}>;
|
|
92
|
+
/** Remove a skill this plugin installed (the Host refuses unmanaged directories). */
|
|
93
|
+
export declare function uninstallSkill(id: string): Promise<{
|
|
94
|
+
removedPath: string;
|
|
95
|
+
skill: NormalizedSkill;
|
|
96
|
+
}>;
|
|
97
|
+
/** Preview the exact local copy without consulting a market provider. */
|
|
98
|
+
export declare function fetchInstalledDetail(key: string, signal?: AbortSignal): Promise<{
|
|
99
|
+
item: InstalledSkillRecord;
|
|
100
|
+
markdown: string;
|
|
101
|
+
}>;
|
|
102
|
+
/** Remove a specifically selected local entry after the management UI confirmation. */
|
|
103
|
+
export declare function removeInstalled(key: string): Promise<{
|
|
104
|
+
item: InstalledSkillRecord;
|
|
105
|
+
removedPath: string;
|
|
106
|
+
}>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { MarketFilters } from '../state.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Search field plus the three catalogue filters.
|
|
4
|
+
*
|
|
5
|
+
* Native `<select>`s rather than a menu primitive: the DSH `Menu` is an
|
|
6
|
+
* anchored popover that needs its own open state and focus return, and three
|
|
7
|
+
* of them side by side would be three focus traps in a row. A `<select>` gets
|
|
8
|
+
* platform keyboard behaviour, a real label association, and renders inside a
|
|
9
|
+
* 720px panel without a portal.
|
|
10
|
+
*
|
|
11
|
+
* The result count is announced through a visually hidden live region instead
|
|
12
|
+
* of being printed twice: the visible count lives in the home header, and a
|
|
13
|
+
* screen reader still hears it change when a filter narrows the list.
|
|
14
|
+
*/
|
|
15
|
+
export declare function FilterBar(props: {
|
|
16
|
+
filters: MarketFilters;
|
|
17
|
+
total: number;
|
|
18
|
+
disabled: boolean;
|
|
19
|
+
onChange: (patch: Partial<MarketFilters>) => void;
|
|
20
|
+
}): JSX.Element;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured view of a SKILL.md frontmatter block.
|
|
3
|
+
*
|
|
4
|
+
* SKILL.md metadata is data, not prose: pushing it through the markdown
|
|
5
|
+
* renderer turns a dozen short fields into a wall of setext headings. It is
|
|
6
|
+
* also reference material rather than the document itself, so it renders as a
|
|
7
|
+
* label/value list under its own heading, with the list itself scrolling once a
|
|
8
|
+
* skill declares more than a screenful of keys.
|
|
9
|
+
*
|
|
10
|
+
* `data` is typed `unknown`-valued on purpose — it is upstream YAML, and every
|
|
11
|
+
* cell is derived defensively rather than trusted.
|
|
12
|
+
*/
|
|
13
|
+
export declare function FrontmatterPanel(props: {
|
|
14
|
+
data: Record<string, unknown>;
|
|
15
|
+
}): JSX.Element;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { MarketSource, SecurityStatus } from '../../market/types.ts';
|
|
2
|
+
/** The subset of a skill the confirmation needs; the caller owns the source object. */
|
|
3
|
+
export interface InstallConfirmSkill {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
source: MarketSource;
|
|
7
|
+
version?: string;
|
|
8
|
+
securityStatus: SecurityStatus;
|
|
9
|
+
authorName: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Install confirmation.
|
|
13
|
+
*
|
|
14
|
+
* Mounted only while a confirmation is pending — the frozen props carry no
|
|
15
|
+
* `skill | null` and no `open`, so the caller's conditional render *is* the
|
|
16
|
+
* open state (the optional `open` prop exists only for a caller that prefers
|
|
17
|
+
* to keep the element mounted).
|
|
18
|
+
*
|
|
19
|
+
* Escape and mask-click closing come from `Modal` itself; while `busy` both
|
|
20
|
+
* are neutralised and the buttons are disabled, because an install already in
|
|
21
|
+
* flight cannot be recalled and a dialog that vanishes mid-request reads as a
|
|
22
|
+
* cancel that never happened.
|
|
23
|
+
*/
|
|
24
|
+
export declare function InstallConfirmDialog(props: {
|
|
25
|
+
skill: InstallConfirmSkill;
|
|
26
|
+
busy: boolean;
|
|
27
|
+
onCancel: () => void;
|
|
28
|
+
onConfirm: () => void;
|
|
29
|
+
/** Optional: defaults to open, since conditional mounting is the usual call site. */
|
|
30
|
+
open?: boolean;
|
|
31
|
+
}): JSX.Element;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { InstallState } from '../../market/types.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Local install state chip.
|
|
4
|
+
*
|
|
5
|
+
* The three labels come from dictionary keys the contract already fixes —
|
|
6
|
+
* `installed`, `installed.installable` and `notInstallable` — so this badge
|
|
7
|
+
* adds no vocabulary of its own.
|
|
8
|
+
*/
|
|
9
|
+
export declare function InstallStateBadge(props: {
|
|
10
|
+
state: InstallState;
|
|
11
|
+
}): JSX.Element;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Third-party risk notice shown above the catalogue until it is acknowledged.
|
|
3
|
+
*
|
|
4
|
+
* Purely presentational: persistence (`localStorage`, `disclaimerDismissed`)
|
|
5
|
+
* belongs to the controller, so this component stays reusable in a bare test
|
|
6
|
+
* render and cannot drift from the stored flag.
|
|
7
|
+
*/
|
|
8
|
+
export declare function MarketDisclaimer(props: {
|
|
9
|
+
onDismiss: () => void;
|
|
10
|
+
}): JSX.Element;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { MarketState, MarketController } from '../state.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Market catalogue page.
|
|
4
|
+
*
|
|
5
|
+
* Presentational by contract: every interaction below is a call on the
|
|
6
|
+
* controller, and every rendered value comes from `state`. That keeps the
|
|
7
|
+
* whole page renderable in a bare test render with no services and no fetch.
|
|
8
|
+
*/
|
|
9
|
+
export declare function MarketHome(props: {
|
|
10
|
+
state: MarketState;
|
|
11
|
+
controller: MarketController;
|
|
12
|
+
}): JSX.Element;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { SecurityReport, SecurityStatus } from '../../market/types.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Audit verdict chip.
|
|
4
|
+
*
|
|
5
|
+
* Shows a shield for every verdict except `flagged`, which uses the warning
|
|
6
|
+
* triangle: a scan that found something must not look like a scan that merely
|
|
7
|
+
* ran, and shape reads faster than color alone.
|
|
8
|
+
*
|
|
9
|
+
* `reports` supplies the upstream status line for the hover title, so the chip
|
|
10
|
+
* can stay a two-word summary without hiding what the vendor actually said.
|
|
11
|
+
*/
|
|
12
|
+
export declare function SecurityBadge(props: {
|
|
13
|
+
status: SecurityStatus;
|
|
14
|
+
reports?: SecurityReport[];
|
|
15
|
+
compact?: boolean;
|
|
16
|
+
}): JSX.Element;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { MarketSource } from '../../market/types.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Skill icon tile.
|
|
4
|
+
*
|
|
5
|
+
* An `<img>` is rendered only for an `https:` URL: the string arrives from a
|
|
6
|
+
* third-party catalogue, and allowing `javascript:` or `data:` here would turn
|
|
7
|
+
* an upstream payload into script execution inside the DSH page.
|
|
8
|
+
*/
|
|
9
|
+
export declare function SkillAvatar(props: {
|
|
10
|
+
name: string;
|
|
11
|
+
source: MarketSource;
|
|
12
|
+
iconUrl?: string;
|
|
13
|
+
size: number;
|
|
14
|
+
}): JSX.Element;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { NormalizedSkill } from '../../market/types.ts';
|
|
2
|
+
/**
|
|
3
|
+
* One catalogue card.
|
|
4
|
+
*
|
|
5
|
+
* The open affordance is a single stretched `<button>` behind the content
|
|
6
|
+
* instead of a click handler on the `<article>`: it is focusable, Enter/Space
|
|
7
|
+
* work for free, and the card never pretends a div is a link. That button sits
|
|
8
|
+
* under the content, so the content is `pointer-events: none` and the install
|
|
9
|
+
* control opts back in — without that pair, either the card stops opening or
|
|
10
|
+
* the install button becomes unclickable.
|
|
11
|
+
*/
|
|
12
|
+
export declare function SkillCard(props: {
|
|
13
|
+
skill: NormalizedSkill;
|
|
14
|
+
installing: boolean;
|
|
15
|
+
onOpen: (id: string) => void;
|
|
16
|
+
onInstall?: (id: string) => void;
|
|
17
|
+
}): JSX.Element;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { NormalizedSkillDetail } from '../../market/types.ts';
|
|
2
|
+
import type { MarketController, MarketState } from '../state.ts';
|
|
3
|
+
/**
|
|
4
|
+
* Skill detail page.
|
|
5
|
+
*
|
|
6
|
+
* Like the home page this component owns no data: the tab, the selected file
|
|
7
|
+
* and the file contents all live in `MarketState`, so switching tabs or
|
|
8
|
+
* re-opening a skill does not lose its place.
|
|
9
|
+
*
|
|
10
|
+
* The bundled GFM renderer handles catalogue Markdown without relying on a
|
|
11
|
+
* host renderer delegate. Raw HTML is disabled and URLs use its safe default.
|
|
12
|
+
*/
|
|
13
|
+
export declare function SkillDetailView(props: {
|
|
14
|
+
detail: NormalizedSkillDetail;
|
|
15
|
+
state: MarketState;
|
|
16
|
+
controller: MarketController;
|
|
17
|
+
}): JSX.Element;
|
|
18
|
+
/** Keeps the detail layout stable while its network request is in flight. */
|
|
19
|
+
export declare function SkillDetailSkeleton({ onBack }: {
|
|
20
|
+
onBack: () => void;
|
|
21
|
+
}): JSX.Element;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { MarketSource, SourceStatusInfo } from '../../market/types.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Per-source health strip.
|
|
4
|
+
*
|
|
5
|
+
* The upstream error string rides the pill's `title` instead of being printed:
|
|
6
|
+
* a 720px panel has no room for a stack trace next to two source names, and an
|
|
7
|
+
* operator who needs it can hover.
|
|
8
|
+
*/
|
|
9
|
+
export declare function SourceStatusBar(props: {
|
|
10
|
+
sources: Record<MarketSource, SourceStatusInfo>;
|
|
11
|
+
onRefresh: () => void;
|
|
12
|
+
refreshing: boolean;
|
|
13
|
+
}): JSX.Element;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inline SVG icon set for the Skills Hub panel.
|
|
3
|
+
*
|
|
4
|
+
* The reference implementation used `lucide-react`; this plugin may not add a
|
|
5
|
+
* dependency for fifteen glyphs, so every icon is drawn here as a stroked
|
|
6
|
+
* `currentColor` SVG. Drawing rules kept uniform on purpose — a mixed set of
|
|
7
|
+
* stroke widths and caps is the fastest way for a ported UI to look alien:
|
|
8
|
+
* - 16x16 viewBox (20x20 for the sidebar glyph, which the shell renders at 16-20px),
|
|
9
|
+
* - `fill: none` + `stroke: currentColor` so an icon inherits its parent's
|
|
10
|
+
* token-driven color and stays correct in both themes,
|
|
11
|
+
* - 1.5 default stroke width (`1.75` where a heavier weight is needed),
|
|
12
|
+
* - `aria-hidden` always: every icon here is decorative and either sits next
|
|
13
|
+
* to a text label or inside a control that carries its own accessible name.
|
|
14
|
+
*/
|
|
15
|
+
/** Props shared by every icon: pixel box and an optional extra class. */
|
|
16
|
+
export interface SkillsHubIconProps {
|
|
17
|
+
/** Rendered width and height in px. Defaults to 16. */
|
|
18
|
+
size?: number;
|
|
19
|
+
/** Extra class for placement; the icon owns only its own geometry. */
|
|
20
|
+
className?: string;
|
|
21
|
+
}
|
|
22
|
+
/** Props of the sidebar entry glyph, which the shell marks as selected. */
|
|
23
|
+
export interface SkillsHubPanelIconProps extends SkillsHubIconProps {
|
|
24
|
+
/**
|
|
25
|
+
* Selected-panel state, injected by `sidebar.panellist` for the active row.
|
|
26
|
+
* Only the glyph's weight and awning fill change — color stays `currentColor`
|
|
27
|
+
* so the sidebar keeps ownership of the row's token colors.
|
|
28
|
+
*/
|
|
29
|
+
active?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Sidebar entry glyph: a shop awning over a storefront.
|
|
33
|
+
*
|
|
34
|
+
* Drawn on a 20px grid rather than 16 so the awning scallops stay legible at
|
|
35
|
+
* the 20px the shell may use for a wide sidebar; at 16px it scales down
|
|
36
|
+
* cleanly because every edge is axis-aligned or a single arc.
|
|
37
|
+
*
|
|
38
|
+
* Renders a bare `<svg>` and never a button: `sidebar.panellist` entries are
|
|
39
|
+
* rendered inside the sidebar's own `<button>` (see `PanelRow` in
|
|
40
|
+
* `dsh-client-ui-sidebar/lib/client.js`), so an interactive wrapper here would
|
|
41
|
+
* nest a control inside a control.
|
|
42
|
+
*/
|
|
43
|
+
export declare function SkillsHubIcon({ size, active, className }: SkillsHubPanelIconProps): JSX.Element;
|
|
44
|
+
/** Magnifier: search input affordance. */
|
|
45
|
+
export declare function SearchIcon({ size, className }: SkillsHubIconProps): JSX.Element;
|
|
46
|
+
/** Arrow into a tray: install action and the download count. */
|
|
47
|
+
export declare function DownloadIcon({ size, className }: SkillsHubIconProps): JSX.Element;
|
|
48
|
+
/** Five-point star: star count. */
|
|
49
|
+
export declare function StarIcon({ size, className }: SkillsHubIconProps): JSX.Element;
|
|
50
|
+
/** Waste bin: uninstall action. */
|
|
51
|
+
export declare function TrashIcon({ size, className }: SkillsHubIconProps): JSX.Element;
|
|
52
|
+
/** Shield outline: audit / security status. */
|
|
53
|
+
export declare function ShieldIcon({ size, className }: SkillsHubIconProps): JSX.Element;
|
|
54
|
+
/** Left arrow: back to the catalogue. */
|
|
55
|
+
export declare function ArrowLeftIcon({ size, className }: SkillsHubIconProps): JSX.Element;
|
|
56
|
+
/** Circular arrow pair: re-fetch list or source health. */
|
|
57
|
+
export declare function RefreshIcon({ size, className }: SkillsHubIconProps): JSX.Element;
|
|
58
|
+
/** Two stacked sheets: copy file content. */
|
|
59
|
+
export declare function CopyIcon({ size, className }: SkillsHubIconProps): JSX.Element;
|
|
60
|
+
/** Tick: copied confirmation and the installed state. */
|
|
61
|
+
export declare function CheckIcon({ size, className }: SkillsHubIconProps): JSX.Element;
|
|
62
|
+
/** Document with a folded corner: a skill file. */
|
|
63
|
+
export declare function FileIcon({ size, className }: SkillsHubIconProps): JSX.Element;
|
|
64
|
+
/** Warning triangle: errors and flagged skills. */
|
|
65
|
+
export declare function AlertIcon({ size, className }: SkillsHubIconProps): JSX.Element;
|
|
66
|
+
/** Chevron: disclosure affordance on a selectable row. */
|
|
67
|
+
export declare function ChevronRightIcon({ size, className }: SkillsHubIconProps): JSX.Element;
|
|
68
|
+
/** Diagonal cross: dismiss a banner, clear the search field. */
|
|
69
|
+
export declare function CloseIcon({ size, className }: SkillsHubIconProps): JSX.Element;
|
|
70
|
+
/** Price tag: leading glyph of a card's tag row. */
|
|
71
|
+
export declare function TagIcon({ size, className }: SkillsHubIconProps): JSX.Element;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skills Hub, browser half.
|
|
3
|
+
*
|
|
4
|
+
* Two registrations, exactly the shape `@deepseek-ai/dsh-client-ui-plugin-manager`
|
|
5
|
+
* uses for its own panel (read from
|
|
6
|
+
* `.../dsh-client-ui-plugin-manager/lib/client.js`, same DSH version):
|
|
7
|
+
*
|
|
8
|
+
* ctx.slots.inject('main', function* () { yield ctx.slots.register({ name: 'main', key: PANEL_ID, … }, MarketPage) })
|
|
9
|
+
* ctx.slots.inject('sidebar.panellist', () => ctx.slots.register({ name: 'sidebar.panellist', id: PANEL_ID, order, label, … }, SkillsHubIcon))
|
|
10
|
+
*
|
|
11
|
+
* `slots.inject` waits for the declaring entry (the frame declares `main`, the
|
|
12
|
+
* sidebar declares `sidebar.panellist`) and keeps the registration on this
|
|
13
|
+
* plugin's fiber, so unloading the plugin removes both contributions.
|
|
14
|
+
*
|
|
15
|
+
* Selecting the panel is **not** done here: the sidebar renders each
|
|
16
|
+
* `sidebar.panellist` entry inside its own `<button>` whose click calls
|
|
17
|
+
* `selectPanel(id)` → `ctx.layout.selectPanel(id)`
|
|
18
|
+
* (`.../dsh-client-ui-sidebar/lib/client.js`, `PanelRow` ≈ line 173 and the
|
|
19
|
+
* injected `selectPanel` ≈ line 483). Because our entry `id` equals the `main`
|
|
20
|
+
* registration key, that call opens this panel; adding our own button or a
|
|
21
|
+
* second `selectPanel` call would nest interactive elements and fight the shell.
|
|
22
|
+
*/
|
|
23
|
+
import type { Context as ClientContext } from '@deepseek-ai/cordis';
|
|
24
|
+
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
25
|
+
interface SlotMap {
|
|
26
|
+
/**
|
|
27
|
+
* Global panel icon rows of the sidebar.
|
|
28
|
+
*
|
|
29
|
+
* Declared here because `@deepseek-ai/dsh-client-ui-sidebar` (the package that
|
|
30
|
+
* owns this slot) is not a dependency of this plugin: the registration below
|
|
31
|
+
* needs the slot key to exist in the contract table, and `slots.inject` waits
|
|
32
|
+
* for the sidebar to declare it at runtime. The owner share is the sidebar's
|
|
33
|
+
* own (`SidebarPanelIconOwnerProps`: requested edge and selection state).
|
|
34
|
+
*/
|
|
35
|
+
'sidebar.panellist': {
|
|
36
|
+
kind: 'list';
|
|
37
|
+
scope: 'root';
|
|
38
|
+
owner: {
|
|
39
|
+
size: number;
|
|
40
|
+
active: boolean;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/** Stable plugin name of the browser half. */
|
|
46
|
+
export declare const name = "skills-hub-client";
|
|
47
|
+
/**
|
|
48
|
+
* Services required before the registrations can be made. `slots` is the
|
|
49
|
+
* registry itself; `locale` owns the dictionary this panel translates through.
|
|
50
|
+
* `layout` is deliberately absent — the sidebar selects the panel for us.
|
|
51
|
+
*/
|
|
52
|
+
export declare const inject: string[];
|
|
53
|
+
/** Sidebar entry id and `main` slot key — the same value is what links the two. */
|
|
54
|
+
export declare const PANEL_ID = "skills-hub";
|
|
55
|
+
/** Register the sidebar entry and the panel it opens. */
|
|
56
|
+
export declare function apply(ctx: ClientContext): void;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Panel-local locale context.
|
|
3
|
+
*
|
|
4
|
+
* The framework hands the translate seat (`t: TranslateNS<NS>`) to the *slot
|
|
5
|
+
* component* only — `PropsLocale<N>` in
|
|
6
|
+
* `@deepseek-ai/dsh-client-ui-slots/lib/types/index.d.ts` adds it exactly when a
|
|
7
|
+
* registration declares `locale: NS`, and nothing propagates it further down the
|
|
8
|
+
* tree. The presentational components are deliberately prop-shaped (contract
|
|
9
|
+
* §5.4 gives them no `t`), so `MarketPage` — the one component the slot
|
|
10
|
+
* machinery calls — publishes its seat here and every descendant reads it.
|
|
11
|
+
*
|
|
12
|
+
* A context (rather than prop drilling or a module singleton) is what keeps the
|
|
13
|
+
* components pure and testable: a test can render any of them under its own
|
|
14
|
+
* provider without touching the DSH runtime.
|
|
15
|
+
*/
|
|
16
|
+
import { type ReactNode } from 'react';
|
|
17
|
+
/** Translate one dictionary key with optional `{name}` template params. */
|
|
18
|
+
export type Translate = (key: string, params?: Record<string, unknown>) => string;
|
|
19
|
+
/** Publish the slot-injected translate seat to the panel's descendants. */
|
|
20
|
+
export declare function LocaleProvider(props: {
|
|
21
|
+
t: Translate;
|
|
22
|
+
children: ReactNode;
|
|
23
|
+
}): JSX.Element;
|
|
24
|
+
/**
|
|
25
|
+
* Read the panel's translate seat.
|
|
26
|
+
*
|
|
27
|
+
* Throws instead of falling back to the raw key: a component rendered outside
|
|
28
|
+
* the panel would otherwise silently print dictionary keys, which is far harder
|
|
29
|
+
* to notice than a loud assembly error.
|
|
30
|
+
*/
|
|
31
|
+
export declare function useT(): Translate;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `skillsHub` dictionaries for the Skills Hub panel.
|
|
3
|
+
*
|
|
4
|
+
* Registration mechanics are copied from `dsh-agent-teams/src/client/locales.ts`
|
|
5
|
+
* (same DSH version): the plugin calls `ctx.locale.register(NS, { zh, en })` and
|
|
6
|
+
* every registration site that declares `locale: NS` receives the framework's
|
|
7
|
+
* typed `t` seat. The namespace name is frozen by docs/CONTRACT.md §5.3.
|
|
8
|
+
*/
|
|
9
|
+
/** Dictionary namespace owned by the Skills Hub client plugin. */
|
|
10
|
+
export declare const NS = "skillsHub";
|
|
11
|
+
/**
|
|
12
|
+
* Simplified Chinese dictionary — the key-set source of truth.
|
|
13
|
+
*
|
|
14
|
+
* Kept as an unexported literal so `en` below can be *checked* against its exact
|
|
15
|
+
* key union while `zh` itself is exported as the plain `Record<string, string>`
|
|
16
|
+
* the contract §5.3 declares (the literal type would otherwise leak into every
|
|
17
|
+
* consumer, including the components workstream).
|
|
18
|
+
*/
|
|
19
|
+
declare const zhDictionary: {
|
|
20
|
+
installedTitle: string;
|
|
21
|
+
marketTab: string;
|
|
22
|
+
installedScope: string;
|
|
23
|
+
installedSearch: string;
|
|
24
|
+
refreshInstalled: string;
|
|
25
|
+
installedEmpty: string;
|
|
26
|
+
localSkill: string;
|
|
27
|
+
linkedSkill: string;
|
|
28
|
+
viewInstalled: string;
|
|
29
|
+
localUninstallDescription: string;
|
|
30
|
+
unlinkDescription: string;
|
|
31
|
+
uninstalling: string;
|
|
32
|
+
panel: string;
|
|
33
|
+
title: string;
|
|
34
|
+
subtitle: string;
|
|
35
|
+
searchPlaceholder: string;
|
|
36
|
+
clearSearch: string;
|
|
37
|
+
'source.all': string;
|
|
38
|
+
'source.clawhub': string;
|
|
39
|
+
'source.skillhub': string;
|
|
40
|
+
'security.all': string;
|
|
41
|
+
'security.verified': string;
|
|
42
|
+
'security.benign': string;
|
|
43
|
+
'security.unknown': string;
|
|
44
|
+
'security.flagged': string;
|
|
45
|
+
'installed.all': string;
|
|
46
|
+
'installed.installed': string;
|
|
47
|
+
'installed.installable': string;
|
|
48
|
+
'filter.source': string;
|
|
49
|
+
'filter.security': string;
|
|
50
|
+
'filter.installed': string;
|
|
51
|
+
count: string;
|
|
52
|
+
loading: string;
|
|
53
|
+
loadingMore: string;
|
|
54
|
+
loadMore: string;
|
|
55
|
+
loadMoreError: string;
|
|
56
|
+
empty: string;
|
|
57
|
+
emptyHint: string;
|
|
58
|
+
emptySearch: string;
|
|
59
|
+
emptySearchHint: string;
|
|
60
|
+
error: string;
|
|
61
|
+
retry: string;
|
|
62
|
+
install: string;
|
|
63
|
+
installing: string;
|
|
64
|
+
installed: string;
|
|
65
|
+
notInstallable: string;
|
|
66
|
+
uninstall: string;
|
|
67
|
+
uninstallConfirm: string;
|
|
68
|
+
uninstallTitle: string;
|
|
69
|
+
uninstallDescription: string;
|
|
70
|
+
installConfirmMessage: string;
|
|
71
|
+
installLocation: string;
|
|
72
|
+
cancel: string;
|
|
73
|
+
confirm: string;
|
|
74
|
+
close: string;
|
|
75
|
+
back: string;
|
|
76
|
+
overview: string;
|
|
77
|
+
files: string;
|
|
78
|
+
security: string;
|
|
79
|
+
viewReport: string;
|
|
80
|
+
frontmatter: string;
|
|
81
|
+
footnotes: string;
|
|
82
|
+
author: string;
|
|
83
|
+
downloads: string;
|
|
84
|
+
installs: string;
|
|
85
|
+
stars: string;
|
|
86
|
+
updated: string;
|
|
87
|
+
license: string;
|
|
88
|
+
version: string;
|
|
89
|
+
disclaimer: string;
|
|
90
|
+
dismiss: string;
|
|
91
|
+
'sourceStatus.ok': string;
|
|
92
|
+
'sourceStatus.degraded': string;
|
|
93
|
+
'sourceStatus.failed': string;
|
|
94
|
+
'sourceStatus.cached': string;
|
|
95
|
+
installDone: string;
|
|
96
|
+
uninstallDone: string;
|
|
97
|
+
noFiles: string;
|
|
98
|
+
fileTooLarge: string;
|
|
99
|
+
copy: string;
|
|
100
|
+
copied: string;
|
|
101
|
+
unoaudited: string;
|
|
102
|
+
};
|
|
103
|
+
/** Simplified Chinese dictionary. */
|
|
104
|
+
export declare const zh: Record<string, string>;
|
|
105
|
+
/**
|
|
106
|
+
* English dictionary. Typed against the Chinese key union so a missing or extra
|
|
107
|
+
* key is a compile error — the locale runtime requires bilingual balance and
|
|
108
|
+
* would otherwise throw at registration time.
|
|
109
|
+
*/
|
|
110
|
+
export declare const en: Record<keyof typeof zhDictionary, string>;
|
|
111
|
+
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
112
|
+
interface LocaleNamespaceMap {
|
|
113
|
+
/** Skills Hub panel copy. */
|
|
114
|
+
skillsHub: string;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skills Hub panel root.
|
|
3
|
+
*
|
|
4
|
+
* Responsibilities, and nothing else:
|
|
5
|
+
* - own exactly one controller per mounted panel instance (`props.useMarketController`);
|
|
6
|
+
* - route `state.view` to `MarketHome` or `SkillDetailView`;
|
|
7
|
+
* - render the install confirmation the controller asked for;
|
|
8
|
+
* - render one notice toast for `state.notice`.
|
|
9
|
+
*
|
|
10
|
+
* This is also the only component the slot machinery calls, so it is the only
|
|
11
|
+
* place that receives the framework's `t` seat (`PropsLocale<NS>`); it publishes
|
|
12
|
+
* that seat through `LocaleProvider` for every descendant, which keeps the
|
|
13
|
+
* presentational components free of slot props (contract §5.4).
|
|
14
|
+
*/
|
|
15
|
+
import type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots';
|
|
16
|
+
import { NS } from '../locales.ts';
|
|
17
|
+
import { type MarketPageInjected } from '../state.ts';
|
|
18
|
+
/** Composed props of the `main` registration: the injected business face plus the locale seat. */
|
|
19
|
+
export type MarketPageProps = MarketPageInjected & PropsLocale<typeof NS>;
|
|
20
|
+
/** Panel entry: bind the controller and publish the locale seat. */
|
|
21
|
+
export declare function MarketPage(props: MarketPageProps): JSX.Element;
|