@khorsheed/dsh-capability-catalog 0.1.95
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/LICENSE +21 -0
- package/README.i18n.yaml +6 -0
- package/README.md +286 -0
- package/README.zh.md +138 -0
- package/cordis.patch.yml +8 -0
- package/lib/client.js +10100 -0
- package/lib/index.js +4022 -0
- package/lib/invariant.js +25 -0
- package/lib/mcpConnector-C2TbkPBY.js +69 -0
- package/lib/typert.host.d.ts +3 -0
- package/lib/typert.host.js +1417 -0
- package/lib/typert.remote-client.d.ts +68 -0
- package/lib/typert.remote-client.js +1136 -0
- package/lib/types/capabilities.d.ts +103 -0
- package/lib/types/capabilities.js +116 -0
- package/lib/types/channels.d.ts +41 -0
- package/lib/types/channels.js +75 -0
- package/lib/types/client/AddMcpDialog.d.ts +13 -0
- package/lib/types/client/AddMcpDialog.js +79 -0
- package/lib/types/client/AddSkillModal.d.ts +17 -0
- package/lib/types/client/AddSkillModal.js +138 -0
- package/lib/types/client/BundleFileTree.d.ts +7 -0
- package/lib/types/client/BundleFileTree.js +74 -0
- package/lib/types/client/CapabilityCatalogCard.d.ts +57 -0
- package/lib/types/client/CapabilityCatalogCard.js +479 -0
- package/lib/types/client/CredentialField.d.ts +14 -0
- package/lib/types/client/CredentialField.js +9 -0
- package/lib/types/client/McpCards.d.ts +13 -0
- package/lib/types/client/McpCards.js +12 -0
- package/lib/types/client/McpServerManageModal.d.ts +18 -0
- package/lib/types/client/McpServerManageModal.js +63 -0
- package/lib/types/client/MetadataRow.d.ts +10 -0
- package/lib/types/client/MetadataRow.js +6 -0
- package/lib/types/client/ModalShell.d.ts +16 -0
- package/lib/types/client/ModalShell.js +27 -0
- package/lib/types/client/ModeChips.d.ts +38 -0
- package/lib/types/client/ModeChips.js +42 -0
- package/lib/types/client/SchemaView.d.ts +11 -0
- package/lib/types/client/SchemaView.js +143 -0
- package/lib/types/client/SkillCards.d.ts +28 -0
- package/lib/types/client/SkillCards.js +43 -0
- package/lib/types/client/SkillDetailModal.d.ts +69 -0
- package/lib/types/client/SkillDetailModal.js +167 -0
- package/lib/types/client/ToolCards.d.ts +28 -0
- package/lib/types/client/ToolCards.js +52 -0
- package/lib/types/client/ToolDetailModal.d.ts +12 -0
- package/lib/types/client/ToolDetailModal.js +29 -0
- package/lib/types/client/icons.d.ts +26 -0
- package/lib/types/client/icons.js +10 -0
- package/lib/types/client/index.d.ts +21 -0
- package/lib/types/client/index.js +196 -0
- package/lib/types/client/locales.d.ts +392 -0
- package/lib/types/client/locales.js +395 -0
- package/lib/types/client/mcp-model.d.ts +24 -0
- package/lib/types/client/mcp-model.js +87 -0
- package/lib/types/client/mode-model.d.ts +108 -0
- package/lib/types/client/mode-model.js +140 -0
- package/lib/types/client/preset-display.d.ts +21 -0
- package/lib/types/client/preset-display.js +20 -0
- package/lib/types/client/settle.d.ts +40 -0
- package/lib/types/client/settle.js +57 -0
- package/lib/types/client/slots.d.ts +86 -0
- package/lib/types/client/slots.js +1 -0
- package/lib/types/envHint.d.ts +22 -0
- package/lib/types/envHint.js +76 -0
- package/lib/types/import.d.ts +106 -0
- package/lib/types/import.js +635 -0
- package/lib/types/index.d.ts +216 -0
- package/lib/types/index.js +739 -0
- package/lib/types/invariant.d.ts +16 -0
- package/lib/types/invariant.js +23 -0
- package/lib/types/mcpConnector.d.ts +24 -0
- package/lib/types/mcpConnector.js +73 -0
- package/lib/types/mcpStore.d.ts +68 -0
- package/lib/types/mcpStore.js +235 -0
- package/lib/types/mcpTools.d.ts +45 -0
- package/lib/types/mcpTools.js +125 -0
- package/lib/types/mcps.d.ts +64 -0
- package/lib/types/mcps.js +157 -0
- package/lib/types/modes.d.ts +65 -0
- package/lib/types/modes.js +97 -0
- package/lib/types/namespace.d.ts +9 -0
- package/lib/types/namespace.js +8 -0
- package/lib/types/official-tools.d.ts +3 -0
- package/lib/types/official-tools.js +44 -0
- package/lib/types/preset-scope.d.ts +105 -0
- package/lib/types/preset-scope.js +117 -0
- package/lib/types/remote.d.ts +48 -0
- package/lib/types/remote.js +120 -0
- package/lib/types/scoped-delivery.d.ts +243 -0
- package/lib/types/scoped-delivery.js +686 -0
- package/lib/types/scoped-edits.d.ts +90 -0
- package/lib/types/scoped-edits.js +234 -0
- package/lib/types/settings.d.ts +28 -0
- package/lib/types/settings.js +28 -0
- package/lib/types/shellEnv.d.ts +65 -0
- package/lib/types/shellEnv.js +232 -0
- package/lib/types/skills.d.ts +103 -0
- package/lib/types/skills.js +303 -0
- package/lib/types/tool-origin.d.ts +29 -0
- package/lib/types/tool-origin.js +30 -0
- package/lib/types/types.d.ts +315 -0
- package/lib/types/types.js +8 -0
- package/lib/types/zip.d.ts +24 -0
- package/lib/types/zip.js +80 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +119 -0
- package/scripts/gen-official-tools.mts +52 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CapabilityCatalogKey } from './locales.ts';
|
|
2
|
+
import { type McpGroup } from './mcp-model.ts';
|
|
3
|
+
/** One MCP server tile in the `mcp` segment: name + transport pill + tool count,
|
|
4
|
+
* with an external enable switch (managed only) and a click-through to the
|
|
5
|
+
* manage modal. Servers carry no description, so the body shows the tool count. */
|
|
6
|
+
export declare function McpCard({ group, onOpen, onSetEnabled, onRemove, t }: {
|
|
7
|
+
group: McpGroup;
|
|
8
|
+
onOpen: () => void;
|
|
9
|
+
onSetEnabled: (serverName: string, enabled: boolean) => Promise<void>;
|
|
10
|
+
onRemove: (serverName: string) => Promise<void>;
|
|
11
|
+
t: (key: CapabilityCatalogKey) => string;
|
|
12
|
+
}): import("react").JSX.Element;
|
|
13
|
+
//# sourceMappingURL=McpCards.d.ts.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { IconBrowseOutlineMedium, IconTrashOutlineMedium } from './icons.js';
|
|
3
|
+
import { transportLabel } from './mcp-model.js';
|
|
4
|
+
import css from './CapabilityCatalogCard.module.css';
|
|
5
|
+
/** One MCP server tile in the `mcp` segment: name + transport pill + tool count,
|
|
6
|
+
* with an external enable switch (managed only) and a click-through to the
|
|
7
|
+
* manage modal. Servers carry no description, so the body shows the tool count. */
|
|
8
|
+
export function McpCard({ group, onOpen, onSetEnabled, onRemove, t }) {
|
|
9
|
+
const transportText = transportLabel(t, group.transport ?? 'stdio');
|
|
10
|
+
const body = group.toolCount > 0 ? t('mcpToolCount').replace('{n}', String(group.toolCount)) : t('mcpNoTools');
|
|
11
|
+
return (_jsxs("div", { className: css.pvCard, children: [_jsxs("button", { type: "button", className: css.pvMain, onClick: onOpen, children: [_jsxs("span", { className: css.pvHead, children: [_jsx("span", { className: css.pvName, children: group.serverName }), _jsx("span", { className: `${css.pvTag} ${group.transport === 'stdio' ? css.tagStdio : group.transport === 'streamable-http' ? css.tagHttp : ''}`, children: transportText })] }), _jsx("span", { className: css.pvDesc, children: body }), _jsx("span", { className: css.pvSub, children: group.managed ? (group.enabled ? t('mcpEnabled') : t('mcpDisabled')) : 'MCP' })] }), _jsxs("div", { className: css.pvFoot, children: [group.managed ? (_jsxs("label", { className: `${css.switch} ${css.mcpFootToggle}`, onClick: (e) => e.stopPropagation(), title: t('mcpEnabled'), children: [_jsx("input", { type: "checkbox", checked: group.enabled, onChange: (e) => void onSetEnabled(group.serverName, e.target.checked), "aria-label": t('mcpEnabled') }), _jsx("span", { className: css.track }), _jsx("span", { className: css.thumb })] })) : null, _jsxs("span", { className: css.mcpCardActions, children: [_jsx("button", { type: "button", className: css.iconButton, onClick: onOpen, "aria-label": t('viewDetail'), title: t('viewDetail'), children: _jsx(IconBrowseOutlineMedium, { size: 16 }) }), group.managed ? (_jsx("button", { type: "button", className: `${css.iconButton} ${css.iconDanger}`, onClick: () => void onRemove(group.serverName), "aria-label": t('mcpRemove'), title: t('mcpRemove'), children: _jsx(IconTrashOutlineMedium, { size: 16 }) })) : null] })] })] }));
|
|
12
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { CatalogToolRow } from '@khorsheed/dsh-capability-catalog/types';
|
|
2
|
+
import type { CapabilityCatalogKey } from './locales.ts';
|
|
3
|
+
import { type McpGroup } from './mcp-model.ts';
|
|
4
|
+
/** Manage-modal for one MCP server (opened from the tile): config JSON,
|
|
5
|
+
* credential inputs, per-tool enable toggles, and discover. Clicking a tool row
|
|
6
|
+
* opens the SHARED tool detail modal (stacked on top) for its parameter schema —
|
|
7
|
+
* the manage modal stays server-level. */
|
|
8
|
+
export declare function McpServerManageModal({ group, discovering, onClose, onSetCredential, onSetToolEnabled, onDiscover, onOpenTool, t }: {
|
|
9
|
+
group: McpGroup;
|
|
10
|
+
discovering: boolean;
|
|
11
|
+
onClose: () => void;
|
|
12
|
+
onSetCredential: (ref: string, value: string) => Promise<boolean>;
|
|
13
|
+
onSetToolEnabled: (serverName: string, tool: string, enabled: boolean) => Promise<void>;
|
|
14
|
+
onDiscover: (serverName: string) => Promise<void>;
|
|
15
|
+
onOpenTool: (tool: CatalogToolRow) => void;
|
|
16
|
+
t: (key: CapabilityCatalogKey) => string;
|
|
17
|
+
}): import("react").JSX.Element;
|
|
18
|
+
//# sourceMappingURL=McpServerManageModal.d.ts.map
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { IconChevronRightOutlineMedium, IconSearchOutlineMedium } from './icons.js';
|
|
4
|
+
import { CredentialField } from './CredentialField.js';
|
|
5
|
+
import { ModalShell } from './ModalShell.js';
|
|
6
|
+
import { configDisplay } from './mcp-model.js';
|
|
7
|
+
import css from './CapabilityCatalogCard.module.css';
|
|
8
|
+
/** The CatalogToolRow the shared tool detail modal needs for one MCP tool row —
|
|
9
|
+
* live rows pass through; managed rows are synthesized with the server attribution. */
|
|
10
|
+
function mcpToolRowFor(tool, serverName) {
|
|
11
|
+
if ('channel' in tool)
|
|
12
|
+
return tool;
|
|
13
|
+
return {
|
|
14
|
+
name: tool.name,
|
|
15
|
+
description: tool.description,
|
|
16
|
+
channel: 'mcp',
|
|
17
|
+
confidence: 'exact',
|
|
18
|
+
serverName,
|
|
19
|
+
...(tool.parameters !== undefined ? { parameters: tool.parameters } : {}),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/** Manage-modal for one MCP server (opened from the tile): config JSON,
|
|
23
|
+
* credential inputs, per-tool enable toggles, and discover. Clicking a tool row
|
|
24
|
+
* opens the SHARED tool detail modal (stacked on top) for its parameter schema —
|
|
25
|
+
* the manage modal stays server-level. */
|
|
26
|
+
export function McpServerManageModal({ group, discovering, onClose, onSetCredential, onSetToolEnabled, onDiscover, onOpenTool, t }) {
|
|
27
|
+
const [credValues, setCredValues] = useState({});
|
|
28
|
+
const [credState, setCredState] = useState({});
|
|
29
|
+
const [toolQuery, setToolQuery] = useState('');
|
|
30
|
+
const saveCred = async (ref) => {
|
|
31
|
+
const value = credValues[ref] ?? '';
|
|
32
|
+
if (value === '')
|
|
33
|
+
return;
|
|
34
|
+
setCredState((s) => ({ ...s, [ref]: 'saving' }));
|
|
35
|
+
const ok = await onSetCredential(ref, value);
|
|
36
|
+
setCredState((s) => ({ ...s, [ref]: ok ? 'ok' : 'fail' }));
|
|
37
|
+
if (ok)
|
|
38
|
+
setCredValues((s) => ({ ...s, [ref]: '' }));
|
|
39
|
+
};
|
|
40
|
+
const runDiscover = async () => {
|
|
41
|
+
await onDiscover(group.serverName);
|
|
42
|
+
};
|
|
43
|
+
const live = group.managed ? group.tools : group.liveTools;
|
|
44
|
+
const toolQ = toolQuery.trim().toLowerCase();
|
|
45
|
+
const visibleLive = toolQ === ''
|
|
46
|
+
? live
|
|
47
|
+
: live.filter((tool) => tool.name.toLowerCase().includes(toolQ) || tool.description.toLowerCase().includes(toolQ));
|
|
48
|
+
// How many of this server's tools are enabled (live-only groups have no
|
|
49
|
+
// per-tool toggle, so all are treated as enabled).
|
|
50
|
+
const enabledCount = group.managed ? group.tools.filter((tool) => tool.enabled).length : live.length;
|
|
51
|
+
return (_jsx(ModalShell, { title: group.serverName, onClose: onClose, t: t, children: _jsxs("div", { className: css.mcpBody, children: [group.managed && group.config !== undefined ? (_jsxs(_Fragment, { children: [_jsxs("details", { className: css.mcpConfigDetails, children: [_jsxs("summary", { className: css.mcpConfigSummary, children: [_jsx("span", { className: css.mcpConfigChevron, children: _jsx(IconChevronRightOutlineMedium, { size: 14 }) }), t('mcpConfig')] }), _jsx("pre", { className: css.mcpConfig, children: configDisplay(group.config) })] }), group.credentials.length > 0 ? (_jsxs("div", { children: [_jsx("div", { className: css.mcpBlockLabel, children: t('credentials') }), _jsx("div", { className: css.confHint, children: t('mcpNeedsCred') }), group.credentials.map((decl) => {
|
|
52
|
+
const ref = decl.ref;
|
|
53
|
+
const state = credState[ref] ?? 'idle';
|
|
54
|
+
return (_jsx(CredentialField, { label: decl.label, configured: decl.configured, value: credValues[ref] ?? '', state: state, onChange: (value) => setCredValues((s) => ({ ...s, [ref]: value })), onSave: () => void saveCred(ref), t: t }, ref));
|
|
55
|
+
})] })) : null] })) : null, _jsxs("div", { children: [_jsxs("div", { className: css.mcpToolBar, children: [_jsxs("div", { className: css.mcpToolLabelRow, children: [_jsxs("div", { className: css.mcpBlockLabel, children: [t('mcpTools'), _jsx("span", { className: css.tabCnt, children: live.length })] }), live.length > 0 ? _jsxs("span", { className: css.mcpEnabledCount, children: [enabledCount, "/", live.length, " ", t('mcpEnabledOf')] }) : null] }), live.length > 0 ? (_jsxs("div", { className: css.mcpToolSearchBox, children: [_jsx("span", { className: css.searchIcon, children: _jsx(IconSearchOutlineMedium, { size: 14 }) }), _jsx("input", { className: css.mcpToolSearch, type: "search", value: toolQuery, placeholder: t('mcpToolSearchPlaceholder'), "aria-label": t('mcpToolSearchPlaceholder'), onChange: (e) => setToolQuery(e.target.value) })] })) : null] }), live.length === 0 ? (_jsx("div", { className: css.empty, children: t('mcpEmptyTools') })) : visibleLive.length === 0 ? (_jsx("div", { className: css.empty, children: t('toolNoMatch') })) : (_jsx("div", { className: css.mcpTools, children: visibleLive.map((tool) => (_jsx(McpToolRow, { tool: tool, managed: group.managed, serverName: group.serverName, onOpen: (row) => onOpenTool(mcpToolRowFor(row, group.serverName)), onSetToolEnabled: group.managed ? onSetToolEnabled : undefined, t: t }, tool.name))) }))] }), group.managed ? (_jsx("div", { className: css.mcpActions, children: !group.enabled ? (_jsx("span", { className: css.confHint, children: t('mcpNotEnabled') })) : (_jsx("button", { type: "button", className: `${css.mcpActionBtn} ${css.primary}`, disabled: discovering, onClick: () => void runDiscover(), children: discovering ? t('mcpDiscovering') : t('mcpDiscover') })) })) : null] }) }));
|
|
56
|
+
}
|
|
57
|
+
/** One tool row inside a MCP server group: name/desc (click to open the shared
|
|
58
|
+
* tool detail modal) + a per-tool enable switch (managed servers only). */
|
|
59
|
+
function McpToolRow({ tool, managed, serverName, onOpen, onSetToolEnabled, t }) {
|
|
60
|
+
const isMcp = managed && 'enabled' in tool;
|
|
61
|
+
const enabled = isMcp ? tool.enabled : true;
|
|
62
|
+
return (_jsx("div", { className: css.mcpToolCard, children: _jsxs("div", { className: css.mcpToolRow, children: [_jsxs("button", { type: "button", className: css.mcpToolMain, onClick: () => onOpen(tool), children: [_jsx("span", { className: css.mcpToolName, children: tool.name }), _jsx("span", { className: css.mcpToolDesc, children: tool.description })] }), isMcp ? (_jsxs("label", { className: `${css.switch} ${css.mcpToolToggle}`, onClick: (e) => e.stopPropagation(), title: t('mcpToolToggleHint'), children: [_jsx("input", { type: "checkbox", checked: enabled, onChange: (e) => void onSetToolEnabled?.(serverName, tool.name, e.target.checked), "aria-label": t('mcpEnabled') }), _jsx("span", { className: css.track }), _jsx("span", { className: css.thumb })] })) : null] }) }, tool.name));
|
|
63
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
export interface MetadataItem {
|
|
3
|
+
readonly label: ReactNode;
|
|
4
|
+
readonly value: ReactNode;
|
|
5
|
+
}
|
|
6
|
+
/** Presentational wrapping metadata row shared by content-detail dialogs. */
|
|
7
|
+
export declare function MetadataRow({ items }: {
|
|
8
|
+
items: readonly MetadataItem[];
|
|
9
|
+
}): import("react").JSX.Element;
|
|
10
|
+
//# sourceMappingURL=MetadataRow.d.ts.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import css from './CapabilityCatalogCard.module.css';
|
|
3
|
+
/** Presentational wrapping metadata row shared by content-detail dialogs. */
|
|
4
|
+
export function MetadataRow({ items }) {
|
|
5
|
+
return (_jsx("div", { className: css.meta, children: items.map((item, index) => (_jsxs("span", { className: css.metaItem, children: [_jsx("span", { className: css.metaKey, children: item.label }), _jsx("span", { className: css.metaVal, children: item.value })] }, index))) }));
|
|
6
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import type { CapabilityCatalogKey } from './locales.ts';
|
|
3
|
+
/** Shared modal chrome for every content dialog in the section (tool detail,
|
|
4
|
+
* skill detail, MCP manage, the two add forms): a centered overlay with a fixed
|
|
5
|
+
* head (title + ×) and a scrolling body. Esc and (optionally) mask-click close
|
|
6
|
+
* it — but only when it is the TOPMOST dialog in the DOM, so a stacked modal
|
|
7
|
+
* (tool detail over the MCP manage modal) never collapses the one beneath it. */
|
|
8
|
+
export declare function ModalShell({ title, onClose, children, className, closeOnMask, t }: {
|
|
9
|
+
title: string;
|
|
10
|
+
onClose: () => void;
|
|
11
|
+
children: ReactNode;
|
|
12
|
+
className?: string | undefined;
|
|
13
|
+
closeOnMask?: boolean;
|
|
14
|
+
t: (key: CapabilityCatalogKey) => string;
|
|
15
|
+
}): import("react").JSX.Element;
|
|
16
|
+
//# sourceMappingURL=ModalShell.d.ts.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef } from 'react';
|
|
3
|
+
import css from './CapabilityCatalogCard.module.css';
|
|
4
|
+
/** Shared modal chrome for every content dialog in the section (tool detail,
|
|
5
|
+
* skill detail, MCP manage, the two add forms): a centered overlay with a fixed
|
|
6
|
+
* head (title + ×) and a scrolling body. Esc and (optionally) mask-click close
|
|
7
|
+
* it — but only when it is the TOPMOST dialog in the DOM, so a stacked modal
|
|
8
|
+
* (tool detail over the MCP manage modal) never collapses the one beneath it. */
|
|
9
|
+
export function ModalShell({ title, onClose, children, className, closeOnMask = true, t }) {
|
|
10
|
+
const overlayRef = useRef(null);
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
const onKey = (e) => {
|
|
13
|
+
if (e.key !== 'Escape')
|
|
14
|
+
return;
|
|
15
|
+
const me = overlayRef.current;
|
|
16
|
+
if (me === null)
|
|
17
|
+
return;
|
|
18
|
+
const dialogs = document.querySelectorAll('[role="dialog"][aria-modal="true"]');
|
|
19
|
+
if (dialogs[dialogs.length - 1] === me)
|
|
20
|
+
onClose();
|
|
21
|
+
};
|
|
22
|
+
window.addEventListener('keydown', onKey);
|
|
23
|
+
return () => window.removeEventListener('keydown', onKey);
|
|
24
|
+
}, [onClose]);
|
|
25
|
+
return (_jsx("div", { ref: overlayRef, className: css.overlay, role: "dialog", "aria-modal": "true", onClick: (e) => { if (closeOnMask && e.target === e.currentTarget)
|
|
26
|
+
onClose(); }, children: _jsxs("div", { className: className === undefined ? css.modal : `${css.modal} ${className}`, children: [_jsxs("div", { className: css.modalHead, children: [_jsx("h3", { className: css.modalTitle, children: title }), _jsx("button", { type: "button", className: css.modalClose, onClick: onClose, "aria-label": t('detailClose'), children: "\u00D7" })] }), _jsx("div", { className: css.modalBody, children: children })] }) }));
|
|
27
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The mode chips on a card in the comparison view: which modes load THIS
|
|
3
|
+
* capability, each chip a one-click jump into that mode.
|
|
4
|
+
*
|
|
5
|
+
* The chips are buttons OUTSIDE the card's main button — a nested button is
|
|
6
|
+
* invalid HTML and would make one click do two things — so the card's open
|
|
7
|
+
* action and the mode jump stay separate.
|
|
8
|
+
*
|
|
9
|
+
* DENSITY is the whole design problem here. An instance can supply seven or
|
|
10
|
+
* more presets, and the most common capability is in most of them, so a literal
|
|
11
|
+
* chip per mode turns the row into a two-line wall of pills that says very
|
|
12
|
+
* little. Three shapes cover the cases, in this order:
|
|
13
|
+
*
|
|
14
|
+
* - **in every readable mode** → ONE summary pill (`全部模式 · 7`). This is the
|
|
15
|
+
* strongest and most frequent answer, and it is exactly the case where naming
|
|
16
|
+
* the modes adds nothing; the title still lists them.
|
|
17
|
+
* - **a few** (up to `VISIBLE_CHIPS`) → the chips themselves.
|
|
18
|
+
* - **many, but not all** → the first two chips plus `+N`, which expands the row
|
|
19
|
+
* IN PLACE. A hover tooltip would answer "which ones?" but not "take me
|
|
20
|
+
* there"; expanding keeps every chip's jump.
|
|
21
|
+
* @module @khorsheed/dsh-capability-catalog/client/mode-chips
|
|
22
|
+
*/
|
|
23
|
+
import type { CatalogModeChip } from './mode-model.ts';
|
|
24
|
+
import type { CapabilityCatalogKey } from './locales.ts';
|
|
25
|
+
/**
|
|
26
|
+
* The chip row, or nothing when the comparison attributed no mode (a managed
|
|
27
|
+
* skill no readable mode delivers, say).
|
|
28
|
+
* @param modes - the modes that load this capability, in roster order.
|
|
29
|
+
* @param total - how many modes the comparison actually read (`all` = this many).
|
|
30
|
+
*/
|
|
31
|
+
export declare function ModeChips({ modes, total, onSelect, t }: {
|
|
32
|
+
modes: readonly CatalogModeChip[];
|
|
33
|
+
/** Modes read by the comparison; the row says `全部模式 · N` when this many. */
|
|
34
|
+
total: number;
|
|
35
|
+
onSelect: (id: string) => void;
|
|
36
|
+
t: (key: CapabilityCatalogKey) => string;
|
|
37
|
+
}): import("react").JSX.Element | null;
|
|
38
|
+
//# sourceMappingURL=ModeChips.d.ts.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* The mode chips on a card in the comparison view: which modes load THIS
|
|
4
|
+
* capability, each chip a one-click jump into that mode.
|
|
5
|
+
*
|
|
6
|
+
* The chips are buttons OUTSIDE the card's main button — a nested button is
|
|
7
|
+
* invalid HTML and would make one click do two things — so the card's open
|
|
8
|
+
* action and the mode jump stay separate.
|
|
9
|
+
*
|
|
10
|
+
* DENSITY is the whole design problem here. An instance can supply seven or
|
|
11
|
+
* more presets, and the most common capability is in most of them, so a literal
|
|
12
|
+
* chip per mode turns the row into a two-line wall of pills that says very
|
|
13
|
+
* little. Three shapes cover the cases, in this order:
|
|
14
|
+
*
|
|
15
|
+
* - **in every readable mode** → ONE summary pill (`全部模式 · 7`). This is the
|
|
16
|
+
* strongest and most frequent answer, and it is exactly the case where naming
|
|
17
|
+
* the modes adds nothing; the title still lists them.
|
|
18
|
+
* - **a few** (up to `VISIBLE_CHIPS`) → the chips themselves.
|
|
19
|
+
* - **many, but not all** → the first two chips plus `+N`, which expands the row
|
|
20
|
+
* IN PLACE. A hover tooltip would answer "which ones?" but not "take me
|
|
21
|
+
* there"; expanding keeps every chip's jump.
|
|
22
|
+
* @module @khorsheed/dsh-capability-catalog/client/mode-chips
|
|
23
|
+
*/
|
|
24
|
+
import { useState } from 'react';
|
|
25
|
+
import css from './CapabilityCatalogCard.module.css';
|
|
26
|
+
/** How many mode chips a card shows before collapsing the rest into `+N`. */
|
|
27
|
+
const VISIBLE_CHIPS = 2;
|
|
28
|
+
/**
|
|
29
|
+
* The chip row, or nothing when the comparison attributed no mode (a managed
|
|
30
|
+
* skill no readable mode delivers, say).
|
|
31
|
+
* @param modes - the modes that load this capability, in roster order.
|
|
32
|
+
* @param total - how many modes the comparison actually read (`all` = this many).
|
|
33
|
+
*/
|
|
34
|
+
export function ModeChips({ modes, total, onSelect, t }) {
|
|
35
|
+
const [expanded, setExpanded] = useState(false);
|
|
36
|
+
if (modes.length === 0)
|
|
37
|
+
return null;
|
|
38
|
+
const names = modes.map(mode => mode.label).join('、');
|
|
39
|
+
const inEveryMode = modes.length > VISIBLE_CHIPS && modes.length >= total;
|
|
40
|
+
const hidden = modes.length - VISIBLE_CHIPS;
|
|
41
|
+
return (_jsxs("div", { className: css.pvModes, children: [_jsx("span", { className: css.pvModesLabel, children: t('modeIn') }), inEveryMode ? (_jsx("span", { className: css.modeChipAll, title: names, children: t('modeEvery').replace('{n}', String(modes.length)) })) : ((expanded ? modes : modes.slice(0, VISIBLE_CHIPS)).map(mode => (_jsx("button", { type: "button", className: css.modeChip, "data-default": mode.isDefault ? 'true' : undefined, title: t('modeChipHint'), onClick: () => onSelect(mode.id), children: mode.label }, mode.id)))), inEveryMode || hidden <= 0 ? null : (_jsx("button", { type: "button", className: css.modeChipMore, title: names, "aria-expanded": expanded, onClick: () => setExpanded(value => !value), children: expanded ? t('modeCollapse') : t('modeMore').replace('{n}', String(hidden)) }))] }));
|
|
42
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CatalogJsonValue } from '@khorsheed/dsh-capability-catalog/types';
|
|
2
|
+
import type { CapabilityCatalogKey } from './locales.ts';
|
|
3
|
+
/** Reusable parameter view: a schema tree with a 结构/JSON toggle and a
|
|
4
|
+
* one-click 复制 JSON, falling back to the raw JSON block when the schema is not
|
|
5
|
+
* an object with properties. Used by the (shared) tool detail modal. */
|
|
6
|
+
export declare function SchemaView({ parameters, title, t }: {
|
|
7
|
+
parameters: CatalogJsonValue | undefined;
|
|
8
|
+
title?: string;
|
|
9
|
+
t: (key: CapabilityCatalogKey) => string;
|
|
10
|
+
}): import("react").JSX.Element;
|
|
11
|
+
//# sourceMappingURL=SchemaView.d.ts.map
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
3
|
+
import { IconChevronDownOutlineMedium, IconChevronRightOutlineMedium } from './icons.js';
|
|
4
|
+
import css from './CapabilityCatalogCard.module.css';
|
|
5
|
+
/** Pretty-print a tool's parameter JSON (or a no-params hint). */
|
|
6
|
+
function formatParams(parameters, t) {
|
|
7
|
+
if (parameters === undefined || parameters === null)
|
|
8
|
+
return t('toolNoParams');
|
|
9
|
+
if (typeof parameters !== 'object')
|
|
10
|
+
return String(parameters);
|
|
11
|
+
if (Array.isArray(parameters))
|
|
12
|
+
return parameters.length === 0 ? t('toolNoParams') : JSON.stringify(parameters, null, 2);
|
|
13
|
+
if (Object.keys(parameters).length === 0)
|
|
14
|
+
return t('toolNoParams');
|
|
15
|
+
try {
|
|
16
|
+
return JSON.stringify(parameters, null, 2);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return t('toolNoParams');
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/** The candidate child schema for a property (array items object, or object with
|
|
23
|
+
* properties) — used to render an expandable nested table. */
|
|
24
|
+
function nestedSchemaOf(schema) {
|
|
25
|
+
if (schema === null || typeof schema !== 'object' || Array.isArray(schema))
|
|
26
|
+
return undefined;
|
|
27
|
+
const s = schema;
|
|
28
|
+
if (s['type'] === 'array') {
|
|
29
|
+
const items = s['items'];
|
|
30
|
+
if (items !== null && typeof items === 'object' && typeof items['properties'] === 'object') {
|
|
31
|
+
return items;
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
if (typeof s['properties'] === 'object' && s['properties'] !== null)
|
|
36
|
+
return schema;
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
/** The property entries of an object schema (or null when not tabular). */
|
|
40
|
+
function schemaProps(schema) {
|
|
41
|
+
if (schema === null || typeof schema !== 'object' || Array.isArray(schema))
|
|
42
|
+
return null;
|
|
43
|
+
const props = schema['properties'];
|
|
44
|
+
if (typeof props !== 'object' || props === null)
|
|
45
|
+
return null;
|
|
46
|
+
const entries = Object.entries(props);
|
|
47
|
+
return entries.length > 0 ? entries : null;
|
|
48
|
+
}
|
|
49
|
+
/** The required-name set of an object schema. */
|
|
50
|
+
function requiredNames(schema) {
|
|
51
|
+
if (schema === null || typeof schema !== 'object' || Array.isArray(schema))
|
|
52
|
+
return new Set();
|
|
53
|
+
const required = schema['required'];
|
|
54
|
+
return new Set(Array.isArray(required) ? required : []);
|
|
55
|
+
}
|
|
56
|
+
/** A compact type label (array<itemType> / object / string / …). */
|
|
57
|
+
function typeLabel(schema) {
|
|
58
|
+
const t = schema['type'];
|
|
59
|
+
if (Array.isArray(t))
|
|
60
|
+
return t.join('|');
|
|
61
|
+
if (t === 'array') {
|
|
62
|
+
const items = schema['items'];
|
|
63
|
+
if (items !== null && typeof items === 'object') {
|
|
64
|
+
const it = items['type'];
|
|
65
|
+
if (typeof it === 'string')
|
|
66
|
+
return `array<${it}>`;
|
|
67
|
+
if (typeof items['properties'] === 'object')
|
|
68
|
+
return 'array<object>';
|
|
69
|
+
}
|
|
70
|
+
return 'array';
|
|
71
|
+
}
|
|
72
|
+
if (t === undefined && typeof schema['properties'] === 'object')
|
|
73
|
+
return 'object';
|
|
74
|
+
return typeof t === 'string' ? t : '';
|
|
75
|
+
}
|
|
76
|
+
/** True when a property schema carries visible nested children (array-of-object
|
|
77
|
+
* items or object properties). Drives both the chevron and the level gutter. */
|
|
78
|
+
function hasNestedKids(schema) {
|
|
79
|
+
const nested = nestedSchemaOf(schema);
|
|
80
|
+
return nested !== undefined && schemaProps(nested) !== null;
|
|
81
|
+
}
|
|
82
|
+
/** One property node in the schema tree: a head row (chevron + mono name +
|
|
83
|
+
* required `*` + type chip) with the description on a second line aligned to
|
|
84
|
+
* the name, and nested children inside a guide-lined indent. A parent node's
|
|
85
|
+
* whole head row is the toggle button (big hit target, one focus stop). Levels
|
|
86
|
+
* where NO sibling is expandable drop the chevron gutter entirely, so flat
|
|
87
|
+
* schemas are not indented for nothing. */
|
|
88
|
+
function SchemaNode({ name, schema, required, path, gutter, collapsed, onToggle, t }) {
|
|
89
|
+
const nested = nestedSchemaOf(schema);
|
|
90
|
+
const kids = nested === undefined ? null : schemaProps(nested);
|
|
91
|
+
const kidRequired = nested === undefined ? null : requiredNames(nested);
|
|
92
|
+
const open = !collapsed.has(path);
|
|
93
|
+
const type = typeLabel(schema);
|
|
94
|
+
const desc = typeof schema['description'] === 'string' && schema['description'] !== '' ? schema['description'] : undefined;
|
|
95
|
+
const head = (_jsxs(_Fragment, { children: [_jsx("span", { className: css.schemaName, children: name }), required ? _jsx("span", { className: css.schemaReq, title: t('paramRequired'), children: "*" }) : null, type !== '' ? _jsx("span", { className: css.schemaType, children: type }) : null] }));
|
|
96
|
+
return (_jsxs("div", { className: css.schemaNode, children: [kids !== null ? (_jsxs("button", { type: "button", className: css.schemaHeadBtn, onClick: () => onToggle(path), "aria-expanded": open, children: [_jsx("span", { className: css.schemaChevron, children: open ? _jsx(IconChevronDownOutlineMedium, { size: 12 }) : _jsx(IconChevronRightOutlineMedium, { size: 12 }) }), head] })) : (_jsxs("div", { className: css.schemaHead, children: [gutter ? _jsx("span", { className: css.schemaChevronSpacer }) : null, head] })), desc !== undefined ? _jsx("div", { className: css.schemaDesc, "data-gutter": gutter || undefined, children: desc }) : null, kids !== null && open ? (_jsx("div", { className: css.schemaKids, children: kids.map(([kidName, kidSchema]) => {
|
|
97
|
+
const kidPath = `${path}.${kidName}`;
|
|
98
|
+
return (_jsx(SchemaNode, { name: kidName, schema: kidSchema, required: kidRequired?.has(kidName) ?? false, path: kidPath, gutter: kids.some(([, k]) => hasNestedKids(k)), collapsed: collapsed, onToggle: onToggle, t: t }, kidPath));
|
|
99
|
+
}) })) : null] }));
|
|
100
|
+
}
|
|
101
|
+
/** The schema tree: one SchemaNode per top-level property. All levels start
|
|
102
|
+
* EXPANDED (state tracks the collapsed set) — the guide lines keep the full
|
|
103
|
+
* hierarchy readable at a glance, and deep MCP schemas collapse per node. */
|
|
104
|
+
function SchemaTree({ schema, t }) {
|
|
105
|
+
const [collapsed, setCollapsed] = useState(() => new Set());
|
|
106
|
+
const toggle = (key) => {
|
|
107
|
+
setCollapsed((prev) => {
|
|
108
|
+
const next = new Set(prev);
|
|
109
|
+
if (next.has(key))
|
|
110
|
+
next.delete(key);
|
|
111
|
+
else
|
|
112
|
+
next.add(key);
|
|
113
|
+
return next;
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
const props = schemaProps(schema);
|
|
117
|
+
if (props === null)
|
|
118
|
+
return null;
|
|
119
|
+
const required = requiredNames(schema);
|
|
120
|
+
return (_jsx("div", { className: css.schemaTree, children: props.map(([name, p]) => (_jsx(SchemaNode, { name: name, schema: p, required: required.has(name), path: name, gutter: props.some(([, prop]) => hasNestedKids(prop)), collapsed: collapsed, onToggle: toggle, t: t }, name))) }));
|
|
121
|
+
}
|
|
122
|
+
/** Reusable parameter view: a schema tree with a 结构/JSON toggle and a
|
|
123
|
+
* one-click 复制 JSON, falling back to the raw JSON block when the schema is not
|
|
124
|
+
* an object with properties. Used by the (shared) tool detail modal. */
|
|
125
|
+
export function SchemaView({ parameters, title, t }) {
|
|
126
|
+
const hasTree = parameters !== undefined && parameters !== null && schemaProps(parameters) !== null;
|
|
127
|
+
const [view, setView] = useState('tree');
|
|
128
|
+
const [copied, setCopied] = useState(false);
|
|
129
|
+
useEffect(() => { if (!hasTree)
|
|
130
|
+
setView('json'); }, [hasTree]);
|
|
131
|
+
const paramsJson = useMemo(() => formatParams(parameters, t), [parameters, t]);
|
|
132
|
+
const copyJson = async () => {
|
|
133
|
+
if (parameters === undefined || parameters === null)
|
|
134
|
+
return;
|
|
135
|
+
try {
|
|
136
|
+
await navigator.clipboard.writeText(JSON.stringify(parameters, null, 2));
|
|
137
|
+
setCopied(true);
|
|
138
|
+
setTimeout(() => setCopied(false), 1200);
|
|
139
|
+
}
|
|
140
|
+
catch { /* clipboard may be blocked */ }
|
|
141
|
+
};
|
|
142
|
+
return (_jsxs("div", { children: [_jsxs("div", { className: css.toolParamsHead, children: [title !== undefined ? _jsx("span", { className: css.toolDetailParamsTitle, children: title }) : _jsx("span", {}), _jsxs("span", { className: css.toolParamsActions, children: [hasTree ? (_jsxs("span", { className: css.toolParamsTabs, children: [_jsx("button", { type: "button", className: css.toolParamsTab, "data-active": view === 'tree', onClick: () => setView('tree'), children: t('paramsTree') }), _jsx("button", { type: "button", className: css.toolParamsTab, "data-active": view === 'json', onClick: () => setView('json'), children: t('paramsJson') })] })) : null, _jsx("button", { type: "button", className: css.toolCopyBtn, disabled: parameters === undefined || parameters === null, onClick: () => void copyJson(), children: copied ? t('copied') : t('copyJson') })] })] }), parameters === undefined || parameters === null ? (_jsx("div", { className: css.toolParamsEmpty, children: t('toolNoParams') })) : hasTree && view === 'tree' ? (_jsx(SchemaTree, { schema: parameters, t: t })) : (_jsx("div", { className: css.toolParamsBody, children: paramsJson }))] }));
|
|
143
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { CatalogSkillRow } from '@khorsheed/dsh-capability-catalog/types';
|
|
2
|
+
import type { CatalogModeChip } from './mode-model.ts';
|
|
3
|
+
import type { CapabilityCatalogKey } from './locales.ts';
|
|
4
|
+
/** Preview card in the skills grid (Agent-preset anatomy): name + source tag,
|
|
5
|
+
* two-line description, provider subtitle, foot actions. In the comparison view
|
|
6
|
+
* a chip row names the modes that load it. */
|
|
7
|
+
export declare function SkillPreviewCard({ skill, tag, modes, modeTotal, onMode, onOpen, onDelete, t }: {
|
|
8
|
+
skill: CatalogSkillRow;
|
|
9
|
+
/** Overrides the source tag (the management surface shows a managed skill's
|
|
10
|
+
* preset scope there, where that policy is the point). */
|
|
11
|
+
tag?: string | undefined;
|
|
12
|
+
/** The modes that load this skill (comparison view only). */
|
|
13
|
+
modes?: readonly CatalogModeChip[] | undefined;
|
|
14
|
+
/** How many modes the comparison read, so an all-modes row can say so. */
|
|
15
|
+
modeTotal?: number | undefined;
|
|
16
|
+
onMode: (id: string) => void;
|
|
17
|
+
onOpen: () => void;
|
|
18
|
+
onDelete: () => void;
|
|
19
|
+
t: (key: CapabilityCatalogKey) => string;
|
|
20
|
+
}): import("react").JSX.Element;
|
|
21
|
+
/** Second-step modal confirming a destructive skill delete. */
|
|
22
|
+
export declare function DeleteSkillConfirm({ name, onCancel, onConfirm, t }: {
|
|
23
|
+
name: string;
|
|
24
|
+
onCancel: () => void;
|
|
25
|
+
onConfirm: () => void | Promise<void>;
|
|
26
|
+
t: (key: CapabilityCatalogKey) => string;
|
|
27
|
+
}): import("react").JSX.Element;
|
|
28
|
+
//# sourceMappingURL=SkillCards.d.ts.map
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { Button, Modal } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
4
|
+
import { IconBrowseOutlineMedium, IconTrashOutlineMedium } from './icons.js';
|
|
5
|
+
import { ModeChips } from './ModeChips.js';
|
|
6
|
+
import css from './CapabilityCatalogCard.module.css';
|
|
7
|
+
/** Skill `source` buckets that resolve to a local file the catalog may delete;
|
|
8
|
+
* everything else (bundled/runtime/skill-badge) is protected and not deletable. */
|
|
9
|
+
const DELETABLE_SOURCES = new Set(['user-dsh', 'user-agents', 'project-dsh', 'project-agents', 'custom']);
|
|
10
|
+
/** Short source tag shown on the card: 内置 / 插件 / 项目 / 自定义 / 用户. */
|
|
11
|
+
const skillSourceTag = (source, t) => {
|
|
12
|
+
switch (source) {
|
|
13
|
+
case 'bundled': return t('builtin');
|
|
14
|
+
case 'runtime': return t('toolPlugin');
|
|
15
|
+
case 'project-dsh':
|
|
16
|
+
case 'project-agents': return t('sourceProject');
|
|
17
|
+
case 'custom': return t('sourceCustom');
|
|
18
|
+
case 'user-dsh':
|
|
19
|
+
case 'user-agents': return t('sourceUser');
|
|
20
|
+
default: return source;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
/** Preview card in the skills grid (Agent-preset anatomy): name + source tag,
|
|
24
|
+
* two-line description, provider subtitle, foot actions. In the comparison view
|
|
25
|
+
* a chip row names the modes that load it. */
|
|
26
|
+
export function SkillPreviewCard({ skill, tag, modes, modeTotal, onMode, onOpen, onDelete, t }) {
|
|
27
|
+
const deletable = DELETABLE_SOURCES.has(skill.source);
|
|
28
|
+
return (_jsxs("div", { className: css.pvCard, "data-modes": modes === undefined ? undefined : 'true', children: [_jsxs("button", { type: "button", className: css.pvMain, onClick: onOpen, children: [_jsxs("span", { className: css.pvHead, children: [_jsx("span", { className: css.pvName, children: skill.name }), _jsx("span", { className: css.pvTag, children: tag ?? skillSourceTag(skill.source, t) })] }), _jsx("span", { className: css.pvDesc, children: skill.description }), _jsx("span", { className: css.pvSub, children: skill.provider })] }), modes === undefined ? null : _jsx(ModeChips, { modes: modes, total: modeTotal ?? 0, onSelect: onMode, t: t }), _jsxs("div", { className: css.pvFoot, children: [_jsx("button", { type: "button", className: css.iconButton, onClick: onOpen, "aria-label": t('viewDetail'), title: t('viewDetail'), children: _jsx(IconBrowseOutlineMedium, { size: 16 }) }), deletable ? (_jsx("button", { type: "button", className: `${css.iconButton} ${css.iconDanger}`, onClick: onDelete, "aria-label": t('delete'), title: t('delete'), children: _jsx(IconTrashOutlineMedium, { size: 16 }) })) : null] })] }));
|
|
29
|
+
}
|
|
30
|
+
/** Second-step modal confirming a destructive skill delete. */
|
|
31
|
+
export function DeleteSkillConfirm({ name, onCancel, onConfirm, t }) {
|
|
32
|
+
const [busy, setBusy] = useState(false);
|
|
33
|
+
const submit = async () => {
|
|
34
|
+
setBusy(true);
|
|
35
|
+
try {
|
|
36
|
+
await onConfirm();
|
|
37
|
+
}
|
|
38
|
+
finally {
|
|
39
|
+
setBusy(false);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
return (_jsx(Modal, { open: true, onClose: onCancel, title: t('delete'), closeLabel: t('close'), description: `${t('confirmDelete')}「${name}」?`, footer: (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", onClick: onCancel, children: t('cancel') }), _jsx(Button, { variant: "primary", disabled: busy, onClick: () => void submit(), children: t('delete') })] })) }));
|
|
43
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { CatalogPresetOption, CatalogPresetScopeEditResult, CatalogSkillDetail, CatalogSkillFileRead } from '@khorsheed/dsh-capability-catalog/types';
|
|
2
|
+
import type { CatalogModeChip } from './mode-model.ts';
|
|
3
|
+
import type { CapabilityCatalogKey } from './locales.ts';
|
|
4
|
+
export type DetailClaim = {
|
|
5
|
+
status: 'idle' | 'loading' | 'done';
|
|
6
|
+
data: CatalogSkillDetail | undefined;
|
|
7
|
+
};
|
|
8
|
+
/** The preset-scope editor's data face for one skill, when the deployment has one. */
|
|
9
|
+
export interface ScopeEditor {
|
|
10
|
+
/** Whether the skill lives in the plugin's managed root. */
|
|
11
|
+
managed: boolean;
|
|
12
|
+
/** The presets its frontmatter currently declares (empty = every preset). */
|
|
13
|
+
declared: readonly string[];
|
|
14
|
+
/** A default root still supplying this name, when delivery is refused. */
|
|
15
|
+
conflict?: string;
|
|
16
|
+
/** Whether the skill can be adopted out of a default root. */
|
|
17
|
+
adoptable: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Whether THIS skill's preset scope is writable at all. False for a
|
|
20
|
+
* plugin-provided or built-in skill: its modes follow the plugin's own row
|
|
21
|
+
* in each mode's composition, and the host refuses a scope write on it.
|
|
22
|
+
*/
|
|
23
|
+
writable: boolean;
|
|
24
|
+
/** The plugin that provides a non-writable skill, when the row names one. */
|
|
25
|
+
provider?: string;
|
|
26
|
+
/** Every preset the roster supplies. */
|
|
27
|
+
options: readonly CatalogPresetOption[];
|
|
28
|
+
save: (presets: readonly string[]) => Promise<CatalogPresetScopeEditResult>;
|
|
29
|
+
adopt: (presets: readonly string[]) => Promise<CatalogPresetScopeEditResult>;
|
|
30
|
+
release: () => Promise<CatalogPresetScopeEditResult>;
|
|
31
|
+
}
|
|
32
|
+
/** Centered modal with a skill's full detail, source browser, metadata and credential config. */
|
|
33
|
+
export declare function SkillDetailModal({ name, claim, onClose, setCredential, readSkillFile, t, scope, modeChips, modesLoading, onModeSelect }: {
|
|
34
|
+
name: string;
|
|
35
|
+
claim: DetailClaim;
|
|
36
|
+
onClose: () => void;
|
|
37
|
+
setCredential: (key: string, value: string) => Promise<boolean>;
|
|
38
|
+
readSkillFile: (name: string, path: string) => Promise<CatalogSkillFileRead | undefined>;
|
|
39
|
+
t: (key: CapabilityCatalogKey) => string;
|
|
40
|
+
scope?: ScopeEditor | undefined;
|
|
41
|
+
/** The modes that load this skill, when its scope section cannot say (a
|
|
42
|
+
* non-writable skill); undefined = not queried, [] = queried and in none. */
|
|
43
|
+
modeChips?: readonly CatalogModeChip[] | undefined;
|
|
44
|
+
/** Whether the ask is still in flight. */
|
|
45
|
+
modesLoading?: boolean;
|
|
46
|
+
onModeSelect?: (id: string) => void;
|
|
47
|
+
}): import("react").JSX.Element;
|
|
48
|
+
/**
|
|
49
|
+
* Whether a skill's metadata holds anything the modal does not already render.
|
|
50
|
+
* @param metadataText - the serialized metadata object.
|
|
51
|
+
* @returns whether the block is worth showing.
|
|
52
|
+
*/
|
|
53
|
+
export declare function hasUnrenderedMetadata(metadataText: string): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Replace secret-shaped VALUES in a metadata object before display.
|
|
56
|
+
*
|
|
57
|
+
* The metadata block prints whatever frontmatter carries, and today nothing
|
|
58
|
+
* secret lives there (credential values are stored in the credential service,
|
|
59
|
+
* never in the file). This is a display guard for the day someone puts an
|
|
60
|
+
* inline token in a metadata key: the block still shows the key and its shape,
|
|
61
|
+
* but not the value. Matching is on the KEY (recursively, so a nested
|
|
62
|
+
* `auth.token` is covered) — a value-shaped heuristic would redact prose.
|
|
63
|
+
* @param value - any parsed metadata value.
|
|
64
|
+
* @returns the same shape with secret-keyed values replaced.
|
|
65
|
+
*/
|
|
66
|
+
export declare function redactMetadataSecrets(value: unknown): unknown;
|
|
67
|
+
/** Pretty-print a JSON metadata string for display, with secret-shaped values redacted. */
|
|
68
|
+
export declare function formatMetadata(metadataText: string): string;
|
|
69
|
+
//# sourceMappingURL=SkillDetailModal.d.ts.map
|