@nospt/plugin-dev-ai-hub 0.2.4 → 0.3.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/dist/api/DevAiHubResourceClient.esm.js +97 -0
- package/dist/api/DevAiHubResourceClient.esm.js.map +1 -0
- package/dist/components/AiResourcesPage/AiResourcesPage.esm.js +150 -0
- package/dist/components/AiResourcesPage/AiResourcesPage.esm.js.map +1 -0
- package/dist/components/AiResourcesPage/AiResourcesPage.module.css.esm.js +8 -0
- package/dist/components/AiResourcesPage/AiResourcesPage.module.css.esm.js.map +1 -0
- package/dist/components/AiResourcesPage/ResourceCard.esm.js +112 -0
- package/dist/components/AiResourcesPage/ResourceCard.esm.js.map +1 -0
- package/dist/components/AiResourcesPage/ResourceCard.module.css.esm.js +8 -0
- package/dist/components/AiResourcesPage/ResourceCard.module.css.esm.js.map +1 -0
- package/dist/components/AiResourcesPage/ResourceDetailPanel.esm.js +238 -0
- package/dist/components/AiResourcesPage/ResourceDetailPanel.esm.js.map +1 -0
- package/dist/components/AiResourcesPage/ResourceDetailPanel.module.css.esm.js +8 -0
- package/dist/components/AiResourcesPage/ResourceDetailPanel.module.css.esm.js.map +1 -0
- package/dist/components/AiResourcesPage/ResourceFilters.esm.js +242 -0
- package/dist/components/AiResourcesPage/ResourceFilters.esm.js.map +1 -0
- package/dist/components/AiResourcesPage/ResourceFilters.module.css.esm.js +8 -0
- package/dist/components/AiResourcesPage/ResourceFilters.module.css.esm.js.map +1 -0
- package/dist/components/AiResourcesPage/ResourceInstallDialog.esm.js +366 -0
- package/dist/components/AiResourcesPage/ResourceInstallDialog.esm.js.map +1 -0
- package/dist/components/AiResourcesPage/ResourceInstallDialog.module.css.esm.js +8 -0
- package/dist/components/AiResourcesPage/ResourceInstallDialog.module.css.esm.js.map +1 -0
- package/dist/components/AiResourcesPage/StatTiles.aurora.module.css.esm.js +8 -0
- package/dist/components/AiResourcesPage/StatTiles.aurora.module.css.esm.js.map +1 -0
- package/dist/components/AiResourcesPage/StatTiles.console.module.css.esm.js +8 -0
- package/dist/components/AiResourcesPage/StatTiles.console.module.css.esm.js.map +1 -0
- package/dist/components/AiResourcesPage/StatTiles.esm.js +82 -0
- package/dist/components/AiResourcesPage/StatTiles.esm.js.map +1 -0
- package/dist/components/AiResourcesPage/StatTiles.module.css.esm.js +8 -0
- package/dist/components/AiResourcesPage/StatTiles.module.css.esm.js.map +1 -0
- package/dist/components/AiResourcesPage/index.esm.js +6 -0
- package/dist/components/AiResourcesPage/typeMeta.esm.js +34 -0
- package/dist/components/AiResourcesPage/typeMeta.esm.js.map +1 -0
- package/dist/components/ToolIcon/ToolIcon.esm.js +28 -10
- package/dist/components/ToolIcon/ToolIcon.esm.js.map +1 -1
- package/dist/hooks/useResourceBody.esm.js +39 -0
- package/dist/hooks/useResourceBody.esm.js.map +1 -0
- package/dist/hooks/useResources.esm.js +26 -0
- package/dist/hooks/useResources.esm.js.map +1 -0
- package/dist/hooks/useTelemetryCounts.esm.js +24 -0
- package/dist/hooks/useTelemetryCounts.esm.js.map +1 -0
- package/dist/index.d.ts +1 -38
- package/dist/index.esm.js +1 -3
- package/dist/index.esm.js.map +1 -1
- package/dist/plugin.esm.js +15 -24
- package/dist/plugin.esm.js.map +1 -1
- package/dist/routes.esm.js +1 -2
- package/dist/routes.esm.js.map +1 -1
- package/package.json +17 -10
- package/.eslintrc.js +0 -1
- package/dev/index.tsx +0 -32
- package/dist/api/DevAiHubClient.esm.js +0 -78
- package/dist/api/DevAiHubClient.esm.js.map +0 -1
- package/dist/components/AssetCard/AssetCard.esm.js +0 -120
- package/dist/components/AssetCard/AssetCard.esm.js.map +0 -1
- package/dist/components/AssetCard/AssetCard.module.css.esm.js +0 -8
- package/dist/components/AssetCard/AssetCard.module.css.esm.js.map +0 -1
- package/dist/components/AssetDetailPanel/AssetDetailPanel.esm.js +0 -218
- package/dist/components/AssetDetailPanel/AssetDetailPanel.esm.js.map +0 -1
- package/dist/components/AssetDetailPanel/AssetDetailPanel.module.css.esm.js +0 -8
- package/dist/components/AssetDetailPanel/AssetDetailPanel.module.css.esm.js.map +0 -1
- package/dist/components/AssetFilters/AssetFilters.esm.js +0 -176
- package/dist/components/AssetFilters/AssetFilters.esm.js.map +0 -1
- package/dist/components/AssetFilters/AssetFilters.module.css.esm.js +0 -8
- package/dist/components/AssetFilters/AssetFilters.module.css.esm.js.map +0 -1
- package/dist/components/AssetInstallDialog/AssetInstallDialog.esm.js +0 -138
- package/dist/components/AssetInstallDialog/AssetInstallDialog.esm.js.map +0 -1
- package/dist/components/AssetInstallDialog/AssetInstallDialog.module.css.esm.js +0 -8
- package/dist/components/AssetInstallDialog/AssetInstallDialog.module.css.esm.js.map +0 -1
- package/dist/components/DevAiHubPage/DevAiHubPage.esm.js +0 -195
- package/dist/components/DevAiHubPage/DevAiHubPage.esm.js.map +0 -1
- package/dist/components/DevAiHubPage/DevAiHubPage.module.css.esm.js +0 -8
- package/dist/components/DevAiHubPage/DevAiHubPage.module.css.esm.js.map +0 -1
- package/dist/components/DevAiHubPage/index.esm.js +0 -6
- package/dist/components/McpConfigPage/McpConfigPage.esm.js +0 -200
- package/dist/components/McpConfigPage/McpConfigPage.esm.js.map +0 -1
- package/dist/components/McpConfigPage/McpConfigPage.module.css.esm.js +0 -8
- package/dist/components/McpConfigPage/McpConfigPage.module.css.esm.js.map +0 -1
- package/dist/components/McpConfigPage/index.esm.js +0 -6
- package/dist/components/McpConfigPage/index.esm.js.map +0 -1
- package/dist/hooks/index.esm.js +0 -114
- package/dist/hooks/index.esm.js.map +0 -1
- package/dist/pluginLegacy.esm.js +0 -27
- package/dist/pluginLegacy.esm.js.map +0 -1
- package/package.json-prepack +0 -85
- package/src/api/DevAiHubClient.ts +0 -110
- package/src/components/AssetCard/AssetCard.module.css +0 -153
- package/src/components/AssetCard/AssetCard.test.tsx +0 -182
- package/src/components/AssetCard/AssetCard.tsx +0 -152
- package/src/components/AssetCard/index.ts +0 -1
- package/src/components/AssetDetailPanel/AssetDetailPanel.module.css +0 -207
- package/src/components/AssetDetailPanel/AssetDetailPanel.tsx +0 -313
- package/src/components/AssetDetailPanel/index.ts +0 -1
- package/src/components/AssetFilters/AssetFilters.module.css +0 -67
- package/src/components/AssetFilters/AssetFilters.tsx +0 -219
- package/src/components/AssetFilters/index.ts +0 -2
- package/src/components/AssetInstallDialog/AssetInstallDialog.module.css +0 -72
- package/src/components/AssetInstallDialog/AssetInstallDialog.tsx +0 -176
- package/src/components/AssetInstallDialog/index.ts +0 -1
- package/src/components/DevAiHubPage/DevAiHubPage.module.css +0 -105
- package/src/components/DevAiHubPage/DevAiHubPage.tsx +0 -197
- package/src/components/DevAiHubPage/index.ts +0 -1
- package/src/components/McpConfigDialog/McpConfigDialog.module.css +0 -111
- package/src/components/McpConfigDialog/McpConfigDialog.tsx +0 -271
- package/src/components/McpConfigDialog/index.ts +0 -1
- package/src/components/McpConfigPage/McpConfigPage.module.css +0 -121
- package/src/components/McpConfigPage/McpConfigPage.tsx +0 -242
- package/src/components/McpConfigPage/index.ts +0 -1
- package/src/components/ToolIcon/ToolIcon.tsx +0 -65
- package/src/components/ToolIcon/index.ts +0 -1
- package/src/css-modules.d.ts +0 -4
- package/src/hooks/index.ts +0 -152
- package/src/index.ts +0 -10
- package/src/plugin.tsx +0 -60
- package/src/pluginLegacy.ts +0 -42
- package/src/routes.ts +0 -4
- package/tests/e2e/.auth.json +0 -14
- package/tests/e2e/asset-detail.spec.ts +0 -90
- package/tests/e2e/asset-filtering.spec.ts +0 -145
- package/tests/e2e/asset-installation.spec.ts +0 -74
- package/tests/e2e/dev-ai-hub-page.spec.ts +0 -89
- package/tests/e2e/fixtures/base.ts +0 -61
- package/tests/e2e/fixtures/mock-api.ts +0 -221
- package/tests/e2e/mcp-config.spec.ts +0 -88
- package/tsconfig.json +0 -10
- /package/dist/components/{DevAiHubPage → AiResourcesPage}/index.esm.js.map +0 -0
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import ReactMarkdown from 'react-markdown';
|
|
4
|
+
import { Dialog, DialogHeader, DialogBody, Text, ButtonLink, Button, ButtonIcon } from '@backstage/ui';
|
|
5
|
+
import { RiFileCopyLine, RiDownloadLine, RiExternalLinkLine, RiCheckLine } from '@remixicon/react';
|
|
6
|
+
import { siClaude } from 'simple-icons';
|
|
7
|
+
import { useApi } from '@backstage/core-plugin-api';
|
|
8
|
+
import { getBodyShape, getMarketplaceRepoSlug, getMarketplaceAddCommands, getInstallSteps, expandInstallFrameworks, getResourceInstallTarget, hasCopyableBody, hasDownloadableArtifact, getAgentInstallLinks, getMcpInstallLinks, getMarketplaceInstallTemplate, getMarketplaceTeamSnippet } from '@nospt/plugin-dev-ai-hub-common';
|
|
9
|
+
import { devAiHubResourceApiRef } from '../../api/DevAiHubResourceClient.esm.js';
|
|
10
|
+
import { ToolIcon } from '../ToolIcon/ToolIcon.esm.js';
|
|
11
|
+
import { frameworkLabel } from './typeMeta.esm.js';
|
|
12
|
+
import styles from './ResourceInstallDialog.module.css.esm.js';
|
|
13
|
+
|
|
14
|
+
const TYPE_HINTS = {
|
|
15
|
+
skill: "Download the skill (multi-file skills arrive as one zip) and extract it into the path for your framework.",
|
|
16
|
+
agent: "Download or copy the agent definition into the path for your framework.",
|
|
17
|
+
hook: "Register the hook definition at the path for your framework \u2014 most hosts merge it into a shared settings file.",
|
|
18
|
+
"mcp-config": "Add this server entry to your MCP configuration file.",
|
|
19
|
+
plugin: "This plugin installs through its framework \u2014 follow the instructions below.",
|
|
20
|
+
marketplace: "Register this marketplace with your AI tool once, then install any of its plugins from it."
|
|
21
|
+
};
|
|
22
|
+
function VsCodeIcon() {
|
|
23
|
+
return /* @__PURE__ */ jsx(
|
|
24
|
+
"svg",
|
|
25
|
+
{
|
|
26
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
27
|
+
viewBox: "0 0 128 128",
|
|
28
|
+
width: 16,
|
|
29
|
+
height: 16,
|
|
30
|
+
fill: "currentColor",
|
|
31
|
+
"aria-hidden": true,
|
|
32
|
+
children: /* @__PURE__ */ jsx(
|
|
33
|
+
"path",
|
|
34
|
+
{
|
|
35
|
+
fillRule: "evenodd",
|
|
36
|
+
clipRule: "evenodd",
|
|
37
|
+
d: "M90.767 127.126a7.968 7.968 0 0 0 6.35-.244l26.353-12.681a8 8 0 0 0 4.53-7.209V21.009a8 8 0 0 0-4.53-7.21L97.117 1.12a7.97 7.97 0 0 0-9.093 1.548l-50.45 46.026L15.6 32.013a5.328 5.328 0 0 0-6.807.302l-7.048 6.411a5.335 5.335 0 0 0-.006 7.888L20.796 64 1.74 81.387a5.336 5.336 0 0 0 .006 7.887l7.048 6.411a5.327 5.327 0 0 0 6.807.303l21.974-16.68 50.45 46.025a7.96 7.96 0 0 0 2.743 1.793Zm5.252-92.183L57.74 64l38.28 29.058V34.943Z"
|
|
38
|
+
}
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
function ClaudeIcon() {
|
|
44
|
+
return /* @__PURE__ */ jsx(
|
|
45
|
+
"svg",
|
|
46
|
+
{
|
|
47
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
48
|
+
viewBox: "0 0 24 24",
|
|
49
|
+
width: 16,
|
|
50
|
+
height: 16,
|
|
51
|
+
fill: "currentColor",
|
|
52
|
+
"aria-hidden": true,
|
|
53
|
+
children: /* @__PURE__ */ jsx("path", { d: siClaude.path })
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
function getInstallLinks(resource, body) {
|
|
58
|
+
if (resource.type === "agent") {
|
|
59
|
+
return getAgentInstallLinks(
|
|
60
|
+
resource.sourceLocation,
|
|
61
|
+
resource.name,
|
|
62
|
+
resource.frameworks
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
if (resource.type === "mcp-config") {
|
|
66
|
+
return getMcpInstallLinks(
|
|
67
|
+
resource.frameworks,
|
|
68
|
+
resource.name,
|
|
69
|
+
body?.content
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
function launchBrand(label) {
|
|
75
|
+
switch (label) {
|
|
76
|
+
case "Claude":
|
|
77
|
+
return { className: styles.claudeLaunch, icon: /* @__PURE__ */ jsx(ClaudeIcon, {}) };
|
|
78
|
+
case "VS Code":
|
|
79
|
+
return { className: styles.vscodeLaunch, icon: /* @__PURE__ */ jsx(VsCodeIcon, {}) };
|
|
80
|
+
case "VS Code Insiders":
|
|
81
|
+
return { className: styles.insidersLaunch, icon: /* @__PURE__ */ jsx(VsCodeIcon, {}) };
|
|
82
|
+
case "Cursor":
|
|
83
|
+
return {
|
|
84
|
+
className: styles.cursorLaunch,
|
|
85
|
+
icon: /* @__PURE__ */ jsx(ToolIcon, { tool: "cursor", branded: false, size: 16 })
|
|
86
|
+
};
|
|
87
|
+
default:
|
|
88
|
+
return { icon: /* @__PURE__ */ jsx(RiExternalLinkLine, {}) };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function CopyCommandButton({ text }) {
|
|
92
|
+
const [copied, setCopied] = useState(false);
|
|
93
|
+
return /* @__PURE__ */ jsx(
|
|
94
|
+
ButtonIcon,
|
|
95
|
+
{
|
|
96
|
+
size: "small",
|
|
97
|
+
variant: "tertiary",
|
|
98
|
+
icon: copied ? /* @__PURE__ */ jsx(RiCheckLine, {}) : /* @__PURE__ */ jsx(RiFileCopyLine, {}),
|
|
99
|
+
"aria-label": copied ? "Copied" : "Copy command",
|
|
100
|
+
onPress: async () => {
|
|
101
|
+
await navigator.clipboard.writeText(text);
|
|
102
|
+
setCopied(true);
|
|
103
|
+
setTimeout(() => setCopied(false), 2e3);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
function CopyPromptButton({ text, label }) {
|
|
109
|
+
const [copied, setCopied] = useState(false);
|
|
110
|
+
return /* @__PURE__ */ jsx(
|
|
111
|
+
Button,
|
|
112
|
+
{
|
|
113
|
+
size: "small",
|
|
114
|
+
variant: "secondary",
|
|
115
|
+
iconStart: copied ? /* @__PURE__ */ jsx(RiCheckLine, {}) : /* @__PURE__ */ jsx(RiFileCopyLine, {}),
|
|
116
|
+
onPress: async () => {
|
|
117
|
+
await navigator.clipboard.writeText(text);
|
|
118
|
+
setCopied(true);
|
|
119
|
+
setTimeout(() => setCopied(false), 2e3);
|
|
120
|
+
},
|
|
121
|
+
children: copied ? "Copied!" : `Copy prompt for ${label}`
|
|
122
|
+
}
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
function MarketplaceJourney({
|
|
126
|
+
resource,
|
|
127
|
+
repoSlug,
|
|
128
|
+
addCommands
|
|
129
|
+
}) {
|
|
130
|
+
return /* @__PURE__ */ jsxs("div", { className: styles.journey, children: [
|
|
131
|
+
/* @__PURE__ */ jsx(Text, { variant: "body-small", as: "p", className: styles.stepTitle, children: "1. Add the marketplace to your AI tool" }),
|
|
132
|
+
/* @__PURE__ */ jsx("dl", { className: styles.commandList, children: addCommands.map(({ framework, command, deepLinks }) => /* @__PURE__ */ jsxs("div", { className: styles.commandRow, children: [
|
|
133
|
+
/* @__PURE__ */ jsxs("dt", { className: styles.commandLabel, children: [
|
|
134
|
+
/* @__PURE__ */ jsx(ToolIcon, { tool: framework, size: 16 }),
|
|
135
|
+
frameworkLabel(framework)
|
|
136
|
+
] }),
|
|
137
|
+
/* @__PURE__ */ jsxs("dd", { className: styles.commandCell, children: [
|
|
138
|
+
/* @__PURE__ */ jsx("code", { children: command }),
|
|
139
|
+
/* @__PURE__ */ jsx(CopyCommandButton, { text: command })
|
|
140
|
+
] }),
|
|
141
|
+
deepLinks.length > 0 && /* @__PURE__ */ jsx("dd", { className: styles.deepLinks, children: deepLinks.map((link) => {
|
|
142
|
+
const brand = launchBrand(link.label);
|
|
143
|
+
return /* @__PURE__ */ jsxs(
|
|
144
|
+
ButtonLink,
|
|
145
|
+
{
|
|
146
|
+
size: "small",
|
|
147
|
+
variant: "secondary",
|
|
148
|
+
className: brand.className,
|
|
149
|
+
iconStart: brand.icon,
|
|
150
|
+
href: link.href,
|
|
151
|
+
children: [
|
|
152
|
+
"Add in ",
|
|
153
|
+
link.label
|
|
154
|
+
]
|
|
155
|
+
},
|
|
156
|
+
link.label
|
|
157
|
+
);
|
|
158
|
+
}) })
|
|
159
|
+
] }, framework)) }),
|
|
160
|
+
/* @__PURE__ */ jsx(Text, { variant: "body-small", as: "p", className: styles.stepTitle, children: "2. Install plugins from it" }),
|
|
161
|
+
/* @__PURE__ */ jsx("div", { className: styles.commandCell, children: /* @__PURE__ */ jsx("code", { children: getMarketplaceInstallTemplate(resource.name) }) }),
|
|
162
|
+
/* @__PURE__ */ jsxs("details", { className: styles.teamSection, children: [
|
|
163
|
+
/* @__PURE__ */ jsx("summary", { children: "For teams: auto-install via project settings" }),
|
|
164
|
+
/* @__PURE__ */ jsxs(Text, { variant: "body-x-small", as: "p", children: [
|
|
165
|
+
"Commit this to your repository\u2019s ",
|
|
166
|
+
/* @__PURE__ */ jsx("code", { children: ".claude/settings.json" }),
|
|
167
|
+
" ",
|
|
168
|
+
"and collaborators are prompted to install the marketplace when they trust the folder."
|
|
169
|
+
] }),
|
|
170
|
+
/* @__PURE__ */ jsx("pre", { className: styles.codeBlock, children: /* @__PURE__ */ jsx("code", { children: getMarketplaceTeamSnippet(resource.name, repoSlug) }) }),
|
|
171
|
+
/* @__PURE__ */ jsx(
|
|
172
|
+
CopyCommandButton,
|
|
173
|
+
{
|
|
174
|
+
text: getMarketplaceTeamSnippet(resource.name, repoSlug)
|
|
175
|
+
}
|
|
176
|
+
)
|
|
177
|
+
] })
|
|
178
|
+
] });
|
|
179
|
+
}
|
|
180
|
+
function ResourceInstallDialog({
|
|
181
|
+
resource,
|
|
182
|
+
body,
|
|
183
|
+
isOpen,
|
|
184
|
+
onOpenChange
|
|
185
|
+
}) {
|
|
186
|
+
const api = useApi(devAiHubResourceApiRef);
|
|
187
|
+
const [copied, setCopied] = useState(false);
|
|
188
|
+
const [downloadError, setDownloadError] = useState(false);
|
|
189
|
+
const shape = getBodyShape(resource.type);
|
|
190
|
+
const installLinks = getInstallLinks(resource, body);
|
|
191
|
+
const marketplaceRepoSlug = resource.type === "marketplace" ? getMarketplaceRepoSlug(resource.sourceLocation) : void 0;
|
|
192
|
+
const marketplaceAddCommands = marketplaceRepoSlug ? getMarketplaceAddCommands(resource.frameworks, marketplaceRepoSlug) : [];
|
|
193
|
+
const hasMarketplaceJourney = marketplaceAddCommands.length > 0;
|
|
194
|
+
const showBodyDoc = resource.type === "plugin" || resource.type === "marketplace" && !hasMarketplaceJourney;
|
|
195
|
+
const usesPromptInstall = resource.type === "skill" || resource.type === "hook";
|
|
196
|
+
const installSteps = usesPromptInstall ? getInstallSteps(
|
|
197
|
+
resource.type,
|
|
198
|
+
resource.sourceLocation,
|
|
199
|
+
resource.name,
|
|
200
|
+
resource.frameworks
|
|
201
|
+
) : [];
|
|
202
|
+
const pathRows = expandInstallFrameworks(resource.frameworks).map((f) => ({
|
|
203
|
+
framework: f,
|
|
204
|
+
target: getResourceInstallTarget(resource.type, f, resource.name)
|
|
205
|
+
})).filter(
|
|
206
|
+
(row) => !!row.target
|
|
207
|
+
);
|
|
208
|
+
const hasMergeTarget = (installSteps.length > 0 ? installSteps.map((s) => s.target) : pathRows.map((r) => r.target)).some((t) => t.mode === "merge");
|
|
209
|
+
const handleCopy = async () => {
|
|
210
|
+
if (!body) return;
|
|
211
|
+
await navigator.clipboard.writeText(body.content);
|
|
212
|
+
api.track(resource.entityRef, "copy");
|
|
213
|
+
setCopied(true);
|
|
214
|
+
setTimeout(() => setCopied(false), 2e3);
|
|
215
|
+
};
|
|
216
|
+
const handleDownload = async () => {
|
|
217
|
+
setDownloadError(false);
|
|
218
|
+
try {
|
|
219
|
+
await api.downloadEntityBody(resource.entityRef);
|
|
220
|
+
api.track(resource.entityRef, "download");
|
|
221
|
+
} catch {
|
|
222
|
+
setDownloadError(true);
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
return /* @__PURE__ */ jsxs(
|
|
226
|
+
Dialog,
|
|
227
|
+
{
|
|
228
|
+
isOpen,
|
|
229
|
+
onOpenChange,
|
|
230
|
+
style: {
|
|
231
|
+
width: "fit-content",
|
|
232
|
+
minWidth: "min(480px, calc(100vw - 3rem))",
|
|
233
|
+
maxWidth: "min(760px, calc(100vw - 3rem))",
|
|
234
|
+
maxHeight: "min(80vh, calc(100vh - 4rem))"
|
|
235
|
+
},
|
|
236
|
+
children: [
|
|
237
|
+
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
238
|
+
"Install ",
|
|
239
|
+
resource.title ?? resource.name
|
|
240
|
+
] }),
|
|
241
|
+
/* @__PURE__ */ jsx(DialogBody, { children: /* @__PURE__ */ jsxs("div", { className: styles.content, children: [
|
|
242
|
+
/* @__PURE__ */ jsx(Text, { variant: "body-small", as: "p", children: TYPE_HINTS[resource.type] }),
|
|
243
|
+
body && shape === "json" && /* @__PURE__ */ jsx("pre", { className: styles.codeBlock, children: /* @__PURE__ */ jsx("code", { children: body.content }) }),
|
|
244
|
+
hasMarketplaceJourney && marketplaceRepoSlug && /* @__PURE__ */ jsx(
|
|
245
|
+
MarketplaceJourney,
|
|
246
|
+
{
|
|
247
|
+
resource,
|
|
248
|
+
repoSlug: marketplaceRepoSlug,
|
|
249
|
+
addCommands: marketplaceAddCommands
|
|
250
|
+
}
|
|
251
|
+
),
|
|
252
|
+
body && showBodyDoc && /* @__PURE__ */ jsx("div", { className: styles.markdown, children: /* @__PURE__ */ jsx(ReactMarkdown, { children: body.content }) }),
|
|
253
|
+
installSteps.length > 0 && /* @__PURE__ */ jsx("dl", { className: styles.commandList, children: installSteps.map(({ framework, target, prompt, link }) => /* @__PURE__ */ jsxs("div", { className: styles.commandRow, children: [
|
|
254
|
+
/* @__PURE__ */ jsxs("dt", { className: styles.commandLabel, children: [
|
|
255
|
+
/* @__PURE__ */ jsx(ToolIcon, { tool: framework, size: 16 }),
|
|
256
|
+
frameworkLabel(framework)
|
|
257
|
+
] }),
|
|
258
|
+
/* @__PURE__ */ jsxs("dd", { className: styles.pathRow, children: [
|
|
259
|
+
/* @__PURE__ */ jsx("code", { children: target.path }),
|
|
260
|
+
target.mode === "merge" && /* @__PURE__ */ jsx("span", { className: styles.mergeBadge, children: "merge into" })
|
|
261
|
+
] }),
|
|
262
|
+
/* @__PURE__ */ jsx("dd", { className: styles.deepLinks, children: link ? (() => {
|
|
263
|
+
const brand = launchBrand(link.label);
|
|
264
|
+
return /* @__PURE__ */ jsxs(
|
|
265
|
+
ButtonLink,
|
|
266
|
+
{
|
|
267
|
+
size: "small",
|
|
268
|
+
variant: "secondary",
|
|
269
|
+
className: brand.className,
|
|
270
|
+
iconStart: brand.icon,
|
|
271
|
+
href: link.href,
|
|
272
|
+
onPress: () => api.track(
|
|
273
|
+
resource.entityRef,
|
|
274
|
+
"install",
|
|
275
|
+
framework
|
|
276
|
+
),
|
|
277
|
+
children: [
|
|
278
|
+
"Install in ",
|
|
279
|
+
link.label
|
|
280
|
+
]
|
|
281
|
+
}
|
|
282
|
+
);
|
|
283
|
+
})() : (
|
|
284
|
+
/* No prompt URI for this host — the same instruction,
|
|
285
|
+
delivered by clipboard so the row is still actionable. */
|
|
286
|
+
/* @__PURE__ */ jsx(
|
|
287
|
+
CopyPromptButton,
|
|
288
|
+
{
|
|
289
|
+
text: prompt,
|
|
290
|
+
label: frameworkLabel(framework)
|
|
291
|
+
}
|
|
292
|
+
)
|
|
293
|
+
) })
|
|
294
|
+
] }, framework)) }),
|
|
295
|
+
installSteps.length === 0 && pathRows.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
296
|
+
/* @__PURE__ */ jsx("dl", { className: styles.pathList, children: pathRows.map(({ framework, target }) => /* @__PURE__ */ jsxs("div", { className: styles.pathRow, children: [
|
|
297
|
+
/* @__PURE__ */ jsx("dt", { children: framework === "default" ? "Any framework" : frameworkLabel(framework) }),
|
|
298
|
+
/* @__PURE__ */ jsxs("dd", { children: [
|
|
299
|
+
/* @__PURE__ */ jsx("code", { children: target.path }),
|
|
300
|
+
target.mode === "merge" && /* @__PURE__ */ jsx("span", { className: styles.mergeBadge, children: "merge into" })
|
|
301
|
+
] })
|
|
302
|
+
] }, framework)) }),
|
|
303
|
+
hasMergeTarget && /* @__PURE__ */ jsxs(
|
|
304
|
+
Text,
|
|
305
|
+
{
|
|
306
|
+
variant: "body-x-small",
|
|
307
|
+
as: "p",
|
|
308
|
+
className: styles.mergeNote,
|
|
309
|
+
children: [
|
|
310
|
+
"Paths marked ",
|
|
311
|
+
/* @__PURE__ */ jsx("strong", { children: "merge into" }),
|
|
312
|
+
" are shared configuration files. Add this entry to the existing file \u2014 replacing it discards your other settings."
|
|
313
|
+
]
|
|
314
|
+
}
|
|
315
|
+
)
|
|
316
|
+
] }),
|
|
317
|
+
/* @__PURE__ */ jsxs("div", { className: styles.actions, children: [
|
|
318
|
+
installLinks.map((link) => {
|
|
319
|
+
const brand = launchBrand(link.label);
|
|
320
|
+
return /* @__PURE__ */ jsxs(
|
|
321
|
+
ButtonLink,
|
|
322
|
+
{
|
|
323
|
+
size: "small",
|
|
324
|
+
variant: "secondary",
|
|
325
|
+
className: brand.className,
|
|
326
|
+
iconStart: brand.icon,
|
|
327
|
+
href: link.href,
|
|
328
|
+
children: [
|
|
329
|
+
"Install in ",
|
|
330
|
+
link.label
|
|
331
|
+
]
|
|
332
|
+
},
|
|
333
|
+
link.label
|
|
334
|
+
);
|
|
335
|
+
}),
|
|
336
|
+
hasCopyableBody(resource.type) && /* @__PURE__ */ jsx(
|
|
337
|
+
Button,
|
|
338
|
+
{
|
|
339
|
+
size: "small",
|
|
340
|
+
variant: "secondary",
|
|
341
|
+
iconStart: /* @__PURE__ */ jsx(RiFileCopyLine, {}),
|
|
342
|
+
isDisabled: !body,
|
|
343
|
+
onPress: handleCopy,
|
|
344
|
+
children: copied ? "Copied!" : "Copy content"
|
|
345
|
+
}
|
|
346
|
+
),
|
|
347
|
+
hasDownloadableArtifact(resource.type) && /* @__PURE__ */ jsx(
|
|
348
|
+
Button,
|
|
349
|
+
{
|
|
350
|
+
size: "small",
|
|
351
|
+
variant: "primary",
|
|
352
|
+
iconStart: /* @__PURE__ */ jsx(RiDownloadLine, {}),
|
|
353
|
+
onPress: handleDownload,
|
|
354
|
+
children: "Download"
|
|
355
|
+
}
|
|
356
|
+
)
|
|
357
|
+
] }),
|
|
358
|
+
downloadError && /* @__PURE__ */ jsx(Text, { variant: "body-x-small", as: "p", children: "Download failed \u2014 the source may be unreachable. Try again or use \u201CView source\u201D." })
|
|
359
|
+
] }) })
|
|
360
|
+
]
|
|
361
|
+
}
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
export { ResourceInstallDialog };
|
|
366
|
+
//# sourceMappingURL=ResourceInstallDialog.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ResourceInstallDialog.esm.js","sources":["../../../src/components/AiResourcesPage/ResourceInstallDialog.tsx"],"sourcesContent":["import { useState } from 'react';\nimport ReactMarkdown from 'react-markdown';\nimport {\n Button,\n ButtonIcon,\n ButtonLink,\n Dialog,\n DialogBody,\n DialogHeader,\n Text,\n} from '@backstage/ui';\nimport {\n RiCheckLine,\n RiDownloadLine,\n RiExternalLinkLine,\n RiFileCopyLine,\n} from '@remixicon/react';\nimport { siClaude } from 'simple-icons';\nimport { useApi } from '@backstage/core-plugin-api';\nimport {\n expandInstallFrameworks,\n getAgentInstallLinks,\n getBodyShape,\n getMarketplaceAddCommands,\n getMcpInstallLinks,\n getMarketplaceInstallTemplate,\n getMarketplaceRepoSlug,\n getMarketplaceTeamSnippet,\n getInstallSteps,\n getResourceInstallTarget,\n hasCopyableBody,\n hasDownloadableArtifact,\n type FrameworkToken,\n type MarketplaceAddCommand,\n type ResourceInstallTarget,\n type ResourceSummary,\n} from '@nospt/plugin-dev-ai-hub-common';\nimport {\n devAiHubResourceApiRef,\n type ResourceBody,\n} from '../../api/DevAiHubResourceClient';\nimport { ToolIcon } from '../ToolIcon';\nimport { frameworkLabel } from './typeMeta';\nimport styles from './ResourceInstallDialog.module.css';\n\ninterface ResourceInstallDialogProps {\n resource: ResourceSummary;\n body?: ResourceBody;\n isOpen: boolean;\n onOpenChange: (open: boolean) => void;\n}\n\nconst TYPE_HINTS: Record<ResourceSummary['type'], string> = {\n skill:\n 'Download the skill (multi-file skills arrive as one zip) and extract it into the path for your framework.',\n agent:\n 'Download or copy the agent definition into the path for your framework.',\n hook: 'Register the hook definition at the path for your framework — most hosts merge it into a shared settings file.',\n 'mcp-config': 'Add this server entry to your MCP configuration file.',\n plugin:\n 'This plugin installs through its framework — follow the instructions below.',\n marketplace:\n 'Register this marketplace with your AI tool once, then install any of its plugins from it.',\n};\n\n/** The VS Code chevron mark (devicon's mono path), for a button's iconStart slot. */\nfunction VsCodeIcon() {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 128 128\"\n width={16}\n height={16}\n fill=\"currentColor\"\n aria-hidden\n >\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M90.767 127.126a7.968 7.968 0 0 0 6.35-.244l26.353-12.681a8 8 0 0 0 4.53-7.209V21.009a8 8 0 0 0-4.53-7.21L97.117 1.12a7.97 7.97 0 0 0-9.093 1.548l-50.45 46.026L15.6 32.013a5.328 5.328 0 0 0-6.807.302l-7.048 6.411a5.335 5.335 0 0 0-.006 7.888L20.796 64 1.74 81.387a5.336 5.336 0 0 0 .006 7.887l7.048 6.411a5.327 5.327 0 0 0 6.807.303l21.974-16.68 50.45 46.025a7.96 7.96 0 0 0 2.743 1.793Zm5.252-92.183L57.74 64l38.28 29.058V34.943Z\"\n />\n </svg>\n );\n}\n\n/** The Claude starburst, sized for a button's iconStart slot. */\nfunction ClaudeIcon() {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n width={16}\n height={16}\n fill=\"currentColor\"\n aria-hidden\n >\n <path d={siClaude.path} />\n </svg>\n );\n}\n\n/**\n * The one-click install links for the types with a *native* install route,\n * rendered once in the actions row. `skill` and `hook` are absent on purpose:\n * their launchers are per-host and live in their own rows, so returning them\n * here too would render every button twice.\n */\nfunction getInstallLinks(resource: ResourceSummary, body?: ResourceBody) {\n if (resource.type === 'agent') {\n return getAgentInstallLinks(\n resource.sourceLocation,\n resource.name,\n resource.frameworks,\n );\n }\n if (resource.type === 'mcp-config') {\n return getMcpInstallLinks(\n resource.frameworks,\n resource.name,\n body?.content,\n );\n }\n return [];\n}\n\n/** Brand styling for the known deep-link hosts, keyed by link label. */\nfunction launchBrand(label: string): {\n className?: string;\n icon: React.ReactElement;\n} {\n switch (label) {\n case 'Claude':\n return { className: styles.claudeLaunch, icon: <ClaudeIcon /> };\n case 'VS Code':\n return { className: styles.vscodeLaunch, icon: <VsCodeIcon /> };\n case 'VS Code Insiders':\n return { className: styles.insidersLaunch, icon: <VsCodeIcon /> };\n case 'Cursor':\n return {\n className: styles.cursorLaunch,\n icon: <ToolIcon tool=\"cursor\" branded={false} size={16} />,\n };\n default:\n return { icon: <RiExternalLinkLine /> };\n }\n}\n\n/**\n * Icon-only copy button for a single generated command (marketplace journey\n * rows); flashes a check mark as the copied feedback.\n */\nfunction CopyCommandButton({ text }: { text: string }) {\n const [copied, setCopied] = useState(false);\n return (\n <ButtonIcon\n size=\"small\"\n variant=\"tertiary\"\n icon={copied ? <RiCheckLine /> : <RiFileCopyLine />}\n aria-label={copied ? 'Copied' : 'Copy command'}\n onPress={async () => {\n await navigator.clipboard.writeText(text);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n }}\n />\n );\n}\n\n/**\n * Clipboard fallback for a host with no prompt URI (Copilot, Gemini). It\n * carries the identical instruction a launcher would pre-fill, so the only\n * difference is the delivery — the user pastes it into their own agent.\n */\nfunction CopyPromptButton({ text, label }: { text: string; label: string }) {\n const [copied, setCopied] = useState(false);\n return (\n <Button\n size=\"small\"\n variant=\"secondary\"\n iconStart={copied ? <RiCheckLine /> : <RiFileCopyLine />}\n onPress={async () => {\n await navigator.clipboard.writeText(text);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n }}\n >\n {copied ? 'Copied!' : `Copy prompt for ${label}`}\n </Button>\n );\n}\n\n/**\n * The two-step marketplace journey (ADR-0010): ① register the catalog with\n * the AI tool, ② install plugins from it — plus a team-distribution snippet.\n * Whether this can render at all is decided by the caller, which needs the\n * same answer to know if the body doc is still required as a fallback.\n */\nfunction MarketplaceJourney({\n resource,\n repoSlug,\n addCommands,\n}: {\n resource: ResourceSummary;\n repoSlug: string;\n addCommands: MarketplaceAddCommand[];\n}) {\n return (\n <div className={styles.journey}>\n <Text variant=\"body-small\" as=\"p\" className={styles.stepTitle}>\n 1. Add the marketplace to your AI tool\n </Text>\n <dl className={styles.commandList}>\n {addCommands.map(({ framework, command, deepLinks }) => (\n <div key={framework} className={styles.commandRow}>\n <dt className={styles.commandLabel}>\n <ToolIcon tool={framework as FrameworkToken} size={16} />\n {frameworkLabel(framework)}\n </dt>\n <dd className={styles.commandCell}>\n <code>{command}</code>\n <CopyCommandButton text={command} />\n </dd>\n {deepLinks.length > 0 && (\n <dd className={styles.deepLinks}>\n {deepLinks.map(link => {\n const brand = launchBrand(link.label);\n return (\n <ButtonLink\n key={link.label}\n size=\"small\"\n variant=\"secondary\"\n className={brand.className}\n iconStart={brand.icon}\n href={link.href}\n >\n Add in {link.label}\n </ButtonLink>\n );\n })}\n </dd>\n )}\n </div>\n ))}\n </dl>\n\n <Text variant=\"body-small\" as=\"p\" className={styles.stepTitle}>\n 2. Install plugins from it\n </Text>\n <div className={styles.commandCell}>\n <code>{getMarketplaceInstallTemplate(resource.name)}</code>\n </div>\n\n <details className={styles.teamSection}>\n <summary>For teams: auto-install via project settings</summary>\n <Text variant=\"body-x-small\" as=\"p\">\n Commit this to your repository’s <code>.claude/settings.json</code>{' '}\n and collaborators are prompted to install the marketplace when they\n trust the folder.\n </Text>\n <pre className={styles.codeBlock}>\n <code>{getMarketplaceTeamSnippet(resource.name, repoSlug)}</code>\n </pre>\n <CopyCommandButton\n text={getMarketplaceTeamSnippet(resource.name, repoSlug)}\n />\n </details>\n </div>\n );\n}\n\n/**\n * Install guidance wired to the body resolver (issue #30): copy the body,\n * download the artifact (file or zip, ADR-0009), and per-framework install\n * paths. The body is canonical — nothing here is reconstructed from\n * annotations (CONTEXT.md: body).\n */\nexport function ResourceInstallDialog({\n resource,\n body,\n isOpen,\n onOpenChange,\n}: ResourceInstallDialogProps) {\n const api = useApi(devAiHubResourceApiRef);\n const [copied, setCopied] = useState(false);\n const [downloadError, setDownloadError] = useState(false);\n\n const shape = getBodyShape(resource.type);\n const installLinks = getInstallLinks(resource, body);\n\n // A marketplace body doc repeats the generated journey almost verbatim —\n // same two steps, same commands — so rendering both doubles the dialog's\n // height to say one thing twice. The journey wins when it can be derived;\n // the body stays as ADR-0010's fallback for when the slug cannot be.\n const marketplaceRepoSlug =\n resource.type === 'marketplace'\n ? getMarketplaceRepoSlug(resource.sourceLocation)\n : undefined;\n const marketplaceAddCommands = marketplaceRepoSlug\n ? getMarketplaceAddCommands(resource.frameworks, marketplaceRepoSlug)\n : [];\n const hasMarketplaceJourney = marketplaceAddCommands.length > 0;\n const showBodyDoc =\n resource.type === 'plugin' ||\n (resource.type === 'marketplace' && !hasMarketplaceJourney);\n\n // skill/hook install by instructing the agent, so each host gets a row that\n // is actionable on its own: a launcher where the host has a prompt URI, a\n // copyable prompt where it does not. Other types keep the plain path list —\n // their launchers are native routes, rendered once in the actions row.\n const usesPromptInstall =\n resource.type === 'skill' || resource.type === 'hook';\n const installSteps = usesPromptInstall\n ? getInstallSteps(\n resource.type,\n resource.sourceLocation,\n resource.name,\n resource.frameworks,\n )\n : [];\n const pathRows = expandInstallFrameworks(resource.frameworks)\n .map(f => ({\n framework: f,\n target: getResourceInstallTarget(resource.type, f, resource.name),\n }))\n .filter(\n (row): row is { framework: string; target: ResourceInstallTarget } =>\n !!row.target,\n );\n const hasMergeTarget = (\n installSteps.length > 0\n ? installSteps.map(s => s.target)\n : pathRows.map(r => r.target)\n ).some(t => t.mode === 'merge');\n\n const handleCopy = async () => {\n if (!body) return;\n await navigator.clipboard.writeText(body.content);\n api.track(resource.entityRef, 'copy');\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n };\n\n const handleDownload = async () => {\n setDownloadError(false);\n try {\n await api.downloadEntityBody(resource.entityRef);\n api.track(resource.entityRef, 'download');\n } catch {\n setDownloadError(true);\n }\n };\n\n return (\n <Dialog\n isOpen={isOpen}\n onOpenChange={onOpenChange}\n /* Size to the widest command line so generated commands stay unwrapped,\n clamped so prose-heavy bodies can't balloon the dialog. The height cap\n keeps a long fallback body scrolling inside the dialog rather than\n running off-screen — the install actions must always stay reachable. */\n style={{\n width: 'fit-content',\n minWidth: 'min(480px, calc(100vw - 3rem))',\n maxWidth: 'min(760px, calc(100vw - 3rem))',\n maxHeight: 'min(80vh, calc(100vh - 4rem))',\n }}\n >\n <DialogHeader>Install {resource.title ?? resource.name}</DialogHeader>\n <DialogBody>\n <div className={styles.content}>\n <Text variant=\"body-small\" as=\"p\">\n {TYPE_HINTS[resource.type]}\n </Text>\n\n {body && shape === 'json' && (\n <pre className={styles.codeBlock}>\n <code>{body.content}</code>\n </pre>\n )}\n\n {hasMarketplaceJourney && marketplaceRepoSlug && (\n <MarketplaceJourney\n resource={resource}\n repoSlug={marketplaceRepoSlug}\n addCommands={marketplaceAddCommands}\n />\n )}\n\n {body && showBodyDoc && (\n <div className={styles.markdown}>\n <ReactMarkdown>{body.content}</ReactMarkdown>\n </div>\n )}\n\n {installSteps.length > 0 && (\n <dl className={styles.commandList}>\n {installSteps.map(({ framework, target, prompt, link }) => (\n <div key={framework} className={styles.commandRow}>\n <dt className={styles.commandLabel}>\n <ToolIcon tool={framework as FrameworkToken} size={16} />\n {frameworkLabel(framework)}\n </dt>\n <dd className={styles.pathRow}>\n <code>{target.path}</code>\n {target.mode === 'merge' && (\n <span className={styles.mergeBadge}>merge into</span>\n )}\n </dd>\n <dd className={styles.deepLinks}>\n {link ? (\n (() => {\n const brand = launchBrand(link.label);\n return (\n <ButtonLink\n size=\"small\"\n variant=\"secondary\"\n className={brand.className}\n iconStart={brand.icon}\n href={link.href}\n onPress={() =>\n api.track(\n resource.entityRef,\n 'install',\n framework,\n )\n }\n >\n Install in {link.label}\n </ButtonLink>\n );\n })()\n ) : (\n /* No prompt URI for this host — the same instruction,\n delivered by clipboard so the row is still actionable. */\n <CopyPromptButton\n text={prompt}\n label={frameworkLabel(framework)}\n />\n )}\n </dd>\n </div>\n ))}\n </dl>\n )}\n\n {installSteps.length === 0 && pathRows.length > 0 && (\n <>\n <dl className={styles.pathList}>\n {pathRows.map(({ framework, target }) => (\n <div key={framework} className={styles.pathRow}>\n <dt>\n {framework === 'default'\n ? 'Any framework'\n : frameworkLabel(framework)}\n </dt>\n <dd>\n <code>{target.path}</code>\n {/* A merge target is a file the user already owns —\n flagged inline so the path is never mistaken for a\n drop-in destination to overwrite. */}\n {target.mode === 'merge' && (\n <span className={styles.mergeBadge}>merge into</span>\n )}\n </dd>\n </div>\n ))}\n </dl>\n {hasMergeTarget && (\n <Text\n variant=\"body-x-small\"\n as=\"p\"\n className={styles.mergeNote}\n >\n Paths marked <strong>merge into</strong> are shared\n configuration files. Add this entry to the existing file —\n replacing it discards your other settings.\n </Text>\n )}\n </>\n )}\n\n <div className={styles.actions}>\n {installLinks.map(link => {\n const brand = launchBrand(link.label);\n return (\n <ButtonLink\n key={link.label}\n size=\"small\"\n variant=\"secondary\"\n className={brand.className}\n iconStart={brand.icon}\n href={link.href}\n >\n Install in {link.label}\n </ButtonLink>\n );\n })}\n {hasCopyableBody(resource.type) && (\n <Button\n size=\"small\"\n variant=\"secondary\"\n iconStart={<RiFileCopyLine />}\n isDisabled={!body}\n onPress={handleCopy}\n >\n {copied ? 'Copied!' : 'Copy content'}\n </Button>\n )}\n {hasDownloadableArtifact(resource.type) && (\n <Button\n size=\"small\"\n variant=\"primary\"\n iconStart={<RiDownloadLine />}\n onPress={handleDownload}\n >\n Download\n </Button>\n )}\n </div>\n {downloadError && (\n <Text variant=\"body-x-small\" as=\"p\">\n Download failed — the source may be unreachable. Try again or use\n “View source”.\n </Text>\n )}\n </div>\n </DialogBody>\n </Dialog>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import styleInject from '../../node_modules_dist/style-inject/dist/style-inject.es.esm.js';
|
|
2
|
+
|
|
3
|
+
var css_248z = "@layer components {\n .ResourceInstallDialog_content__a8f0e4307c {\n display: flex;\n flex-direction: column;\n gap: var(--bui-space-4);\n /* Pairs with the dialog's maxHeight: overflow scrolls here instead of\n pushing the dialog past the viewport. */\n min-height: 0;\n overflow-y: auto;\n }\n\n .ResourceInstallDialog_codeBlock__a8f0e4307c {\n margin: 0;\n padding: var(--bui-space-3);\n background-color: var(--bui-bg-neutral-2);\n border: 1px solid var(--bui-border-1);\n border-radius: var(--bui-radius-2);\n font-size: 0.8125rem;\n overflow-x: auto;\n white-space: pre;\n }\n\n .ResourceInstallDialog_markdown__a8f0e4307c {\n font-size: 0.875rem;\n line-height: 1.55;\n overflow-wrap: anywhere;\n }\n\n .ResourceInstallDialog_markdown__a8f0e4307c pre {\n padding: var(--bui-space-3);\n background-color: var(--bui-bg-neutral-2);\n border-radius: var(--bui-radius-2);\n overflow-x: auto;\n }\n\n .ResourceInstallDialog_pathList__a8f0e4307c {\n margin: 0;\n display: flex;\n flex-direction: column;\n gap: var(--bui-space-2);\n }\n\n .ResourceInstallDialog_pathRow__a8f0e4307c {\n display: flex;\n align-items: baseline;\n gap: var(--bui-space-3);\n }\n\n .ResourceInstallDialog_pathRow__a8f0e4307c dt {\n flex-shrink: 0;\n min-width: 120px;\n font-size: 0.8125rem;\n color: var(--bui-fg-secondary);\n }\n\n .ResourceInstallDialog_pathRow__a8f0e4307c dd {\n margin: 0;\n display: flex;\n align-items: baseline;\n gap: var(--bui-space-2);\n }\n\n /* Marks a shared settings file rather than a drop-in destination; uses the\n warning intent so the difference survives a glance at the path list. */\n .ResourceInstallDialog_mergeBadge__a8f0e4307c {\n flex-shrink: 0;\n font-size: 0.6875rem;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n padding: 1px var(--bui-space-2);\n border-radius: var(--bui-radius-1);\n color: var(--bui-fg-warning);\n background-color: var(--bui-bg-warning-1);\n }\n\n .ResourceInstallDialog_mergeNote__a8f0e4307c {\n color: var(--bui-fg-secondary);\n }\n\n .ResourceInstallDialog_pathRow__a8f0e4307c code {\n font-size: 0.8125rem;\n background-color: var(--bui-bg-neutral-2);\n padding: 2px var(--bui-space-2);\n border-radius: var(--bui-radius-1);\n }\n\n .ResourceInstallDialog_actions__a8f0e4307c {\n display: flex;\n flex-wrap: wrap;\n gap: var(--bui-space-2);\n }\n\n .ResourceInstallDialog_journey__a8f0e4307c {\n display: flex;\n flex-direction: column;\n gap: var(--bui-space-3);\n }\n\n .ResourceInstallDialog_stepTitle__a8f0e4307c {\n font-weight: 600;\n }\n\n .ResourceInstallDialog_commandList__a8f0e4307c {\n margin: 0;\n display: flex;\n flex-direction: column;\n /* between one framework's block and the next */\n gap: var(--bui-space-4);\n }\n\n .ResourceInstallDialog_commandRow__a8f0e4307c {\n display: flex;\n flex-direction: column;\n /* between the label, the command box, and the launch buttons */\n gap: 6px;\n }\n\n .ResourceInstallDialog_commandLabel__a8f0e4307c {\n display: flex;\n align-items: center;\n gap: var(--bui-space-2);\n font-size: 0.8125rem;\n color: var(--bui-fg-secondary);\n }\n\n .ResourceInstallDialog_commandCell__a8f0e4307c {\n margin: 0;\n display: flex;\n align-items: center;\n gap: var(--bui-space-1);\n min-width: 0;\n background-color: var(--bui-bg-neutral-2);\n border: 1px solid var(--bui-border-1);\n border-radius: var(--bui-radius-2);\n padding: var(--bui-space-1);\n }\n\n .ResourceInstallDialog_deepLinks__a8f0e4307c {\n margin: 0;\n /* extra breathing room between the command box and the launch buttons,\n on top of the row gap */\n margin-block-start: 3px;\n display: flex;\n flex-wrap: wrap;\n gap: var(--bui-space-2);\n }\n\n /* Claude's brand terracotta (simple-icons siClaude hex); doubled selector\n outranks the BUI button variant styles in the same cascade layer. */\n .ResourceInstallDialog_claudeLaunch__a8f0e4307c.ResourceInstallDialog_claudeLaunch__a8f0e4307c {\n background-color: #d97757;\n border-color: #d97757;\n color: #1a1310;\n }\n\n .ResourceInstallDialog_claudeLaunch__a8f0e4307c.ResourceInstallDialog_claudeLaunch__a8f0e4307c:hover {\n background-color: color-mix(in srgb, #d97757 85%, black);\n border-color: color-mix(in srgb, #d97757 85%, black);\n }\n\n /* VS Code brand blue (stable product icon). */\n .ResourceInstallDialog_vscodeLaunch__a8f0e4307c.ResourceInstallDialog_vscodeLaunch__a8f0e4307c {\n background-color: #007acc;\n border-color: #007acc;\n color: #ffffff;\n }\n\n .ResourceInstallDialog_vscodeLaunch__a8f0e4307c.ResourceInstallDialog_vscodeLaunch__a8f0e4307c:hover {\n background-color: color-mix(in srgb, #007acc 85%, black);\n border-color: color-mix(in srgb, #007acc 85%, black);\n }\n\n /* VS Code Insiders brand teal (Insiders product icon); dark ink for contrast. */\n .ResourceInstallDialog_insidersLaunch__a8f0e4307c.ResourceInstallDialog_insidersLaunch__a8f0e4307c {\n background-color: #00b294;\n border-color: #00b294;\n color: #0e1f1a;\n }\n\n .ResourceInstallDialog_insidersLaunch__a8f0e4307c.ResourceInstallDialog_insidersLaunch__a8f0e4307c:hover {\n background-color: color-mix(in srgb, #00b294 85%, black);\n border-color: color-mix(in srgb, #00b294 85%, black);\n }\n\n /* Cursor's monochrome brand: inverted against the theme (black-on-light,\n white-on-dark), via the theme-adaptive foreground/background tokens. */\n .ResourceInstallDialog_cursorLaunch__a8f0e4307c.ResourceInstallDialog_cursorLaunch__a8f0e4307c {\n background-color: var(--bui-fg-primary);\n border-color: var(--bui-fg-primary);\n color: var(--bui-bg-app);\n }\n\n .ResourceInstallDialog_cursorLaunch__a8f0e4307c.ResourceInstallDialog_cursorLaunch__a8f0e4307c:hover {\n background-color: color-mix(\n in srgb,\n var(--bui-fg-primary) 80%,\n var(--bui-bg-app)\n );\n border-color: color-mix(\n in srgb,\n var(--bui-fg-primary) 80%,\n var(--bui-bg-app)\n );\n }\n\n .ResourceInstallDialog_commandCell__a8f0e4307c code {\n flex: 1;\n font-size: 0.8125rem;\n padding-left: var(--bui-space-1);\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n }\n\n .ResourceInstallDialog_teamSection__a8f0e4307c summary {\n cursor: pointer;\n font-size: 0.8125rem;\n color: var(--bui-fg-secondary);\n }\n\n .ResourceInstallDialog_teamSection__a8f0e4307c[open] summary {\n margin-bottom: var(--bui-space-2);\n }\n\n .ResourceInstallDialog_teamSection__a8f0e4307c pre {\n margin: var(--bui-space-2) 0;\n }\n}\n";
|
|
4
|
+
var styles = {"content":"ResourceInstallDialog_content__a8f0e4307c","codeBlock":"ResourceInstallDialog_codeBlock__a8f0e4307c","markdown":"ResourceInstallDialog_markdown__a8f0e4307c","pathList":"ResourceInstallDialog_pathList__a8f0e4307c","pathRow":"ResourceInstallDialog_pathRow__a8f0e4307c","mergeBadge":"ResourceInstallDialog_mergeBadge__a8f0e4307c","mergeNote":"ResourceInstallDialog_mergeNote__a8f0e4307c","actions":"ResourceInstallDialog_actions__a8f0e4307c","journey":"ResourceInstallDialog_journey__a8f0e4307c","stepTitle":"ResourceInstallDialog_stepTitle__a8f0e4307c","commandList":"ResourceInstallDialog_commandList__a8f0e4307c","commandRow":"ResourceInstallDialog_commandRow__a8f0e4307c","commandLabel":"ResourceInstallDialog_commandLabel__a8f0e4307c","commandCell":"ResourceInstallDialog_commandCell__a8f0e4307c","deepLinks":"ResourceInstallDialog_deepLinks__a8f0e4307c","claudeLaunch":"ResourceInstallDialog_claudeLaunch__a8f0e4307c","vscodeLaunch":"ResourceInstallDialog_vscodeLaunch__a8f0e4307c","insidersLaunch":"ResourceInstallDialog_insidersLaunch__a8f0e4307c","cursorLaunch":"ResourceInstallDialog_cursorLaunch__a8f0e4307c","teamSection":"ResourceInstallDialog_teamSection__a8f0e4307c"};
|
|
5
|
+
styleInject(css_248z);
|
|
6
|
+
|
|
7
|
+
export { styles as default };
|
|
8
|
+
//# sourceMappingURL=ResourceInstallDialog.module.css.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ResourceInstallDialog.module.css.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import styleInject from '../../node_modules_dist/style-inject/dist/style-inject.es.esm.js';
|
|
2
|
+
|
|
3
|
+
var css_248z = "/*\n * EXPERIMENT — tile variant \"aurora\" (see TILE_VARIANT in StatTiles.tsx).\n *\n * Frosted glass panes floating over a blurred colour field made from the six\n * brand hues. Each pane picks its type's colour up *through* the glass rather\n * than being filled with it, so — as in the console variant — colour is light\n * and text never sits on a saturated field.\n *\n * The material is deliberate: nosportugal/backstage already uses acrylic with\n * backdrop blur for its sidebar submenu, so this is the host app's own glass\n * turned expressive. Distinct from the rest of Backstage without looking like\n * a third-party widget bolted on.\n */\n@layer components {\n .StatTiles.aurora_grid__93817f37ed {\n position: relative;\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));\n gap: var(--bui-space-3);\n margin-bottom: var(--bui-space-5);\n padding: var(--bui-space-3);\n border-radius: var(--bui-radius-3);\n isolation: isolate;\n /* The field is clipped to the row: unclipped, a 36px blur bleeds across\n the page and washes out everything around it. */\n overflow: hidden;\n background: var(--bui-bg-neutral-1);\n }\n\n /* The aurora: the six brand hues as soft overlapping lights, blurred into a\n field. Sits behind the panes and is what they refract. */\n .StatTiles.aurora_grid__93817f37ed::before {\n content: '';\n position: absolute;\n inset: -20%;\n z-index: -1;\n background: radial-gradient(28% 60% at 8% 40%, #6ea514 0%, transparent 70%),\n radial-gradient(26% 60% at 26% 60%, #eb84cd 0%, transparent 70%),\n radial-gradient(26% 60% at 44% 35%, #fcd200 0%, transparent 70%),\n radial-gradient(26% 60% at 62% 65%, #4bdbc5 0%, transparent 70%),\n radial-gradient(26% 60% at 80% 38%, #4f60d2 0%, transparent 70%),\n radial-gradient(28% 60% at 96% 60%, #f26b43 0%, transparent 70%);\n filter: blur(44px) saturate(150%);\n opacity: 0.5;\n pointer-events: none;\n }\n\n .StatTiles.aurora_tile__93817f37ed {\n position: relative;\n /* Glass is transparent, so the text is the theme's own foreground rather\n than white — the only variant where that is true, and the reason these\n are tokens. */\n --devaihub-tile-fg: var(--bui-fg-primary);\n --devaihub-tile-fg-muted: var(--bui-fg-secondary);\n /* Frosted pane: a translucent tint of the page surface, blurred behind. */\n background: color-mix(in srgb, var(--bui-bg-app) 62%, transparent);\n backdrop-filter: blur(16px) saturate(150%);\n -webkit-backdrop-filter: blur(16px) saturate(150%);\n border: 1px solid rgba(255, 255, 255, 0.22);\n border-radius: var(--bui-radius-3);\n /* Tighter than it looks like it should be: the pane sits inside the grid's\n own padding, so the two paddings stack and \"MCP Configs\" wraps. */\n padding: var(--bui-space-3);\n padding-bottom: var(--bui-space-5);\n display: flex;\n align-items: center;\n gap: var(--bui-space-2);\n cursor: pointer;\n overflow: hidden;\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.28),\n 0 4px 20px -8px rgba(0, 0, 0, 0.35);\n transition: transform 180ms cubic-bezier(0.16, 1, 0.3, 1),\n border-color 180ms ease, background-color 180ms ease;\n }\n\n /* The type's own colour, caught in the glass. */\n .StatTiles.aurora_tile__93817f37ed::before {\n content: '';\n position: absolute;\n inset: 0;\n background: radial-gradient(\n 120% 100% at 0% 0%,\n color-mix(in srgb, var(--tile-fill) 34%, transparent),\n transparent 72%\n );\n pointer-events: none;\n transition: opacity 180ms ease;\n opacity: 0.9;\n }\n\n .StatTiles.aurora_tile__93817f37ed:hover {\n transform: translateY(-2px);\n border-color: color-mix(in srgb, var(--tile-fill) 55%, transparent);\n }\n\n .StatTiles.aurora_tile__93817f37ed:hover::before {\n opacity: 1;\n }\n\n .StatTiles.aurora_tile__93817f37ed:focus-visible {\n outline: 2px solid var(--bui-fg-primary);\n outline-offset: 3px;\n }\n\n .StatTiles.aurora_tileActive__93817f37ed {\n transform: translateY(-2px);\n border-color: color-mix(in srgb, var(--tile-fill) 75%, transparent);\n background: color-mix(in srgb, var(--bui-bg-app) 45%, transparent);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.35),\n 0 0 0 1px color-mix(in srgb, var(--tile-fill) 40%, transparent),\n 0 8px 28px -10px color-mix(in srgb, var(--tile-fill) 70%, transparent);\n }\n\n .StatTiles.aurora_tileIconBox__93817f37ed {\n position: relative;\n color: var(--tile-fill);\n background: color-mix(in srgb, var(--tile-fill) 16%, transparent);\n border-radius: var(--bui-radius-2);\n padding: var(--bui-space-1);\n display: flex;\n flex-shrink: 0;\n }\n\n .StatTiles.aurora_tileText__93817f37ed {\n position: relative;\n display: flex;\n flex-direction: column;\n min-width: 0;\n }\n\n .StatTiles.aurora_tile__93817f37ed .StatTiles.aurora_tileValue__93817f37ed {\n color: var(--bui-fg-primary);\n font-size: 1.75rem;\n font-weight: 700;\n line-height: 1.1;\n font-variant-numeric: tabular-nums;\n letter-spacing: -0.02em;\n display: block;\n }\n\n .StatTiles.aurora_tile__93817f37ed .StatTiles.aurora_tileLabel__93817f37ed {\n color: var(--bui-fg-secondary);\n font-size: 0.6875rem;\n font-weight: 600;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n line-height: 1.2;\n display: block;\n }\n\n .StatTiles.aurora_shareTrack__93817f37ed {\n position: absolute;\n left: var(--bui-space-3);\n right: var(--bui-space-3);\n bottom: var(--bui-space-3);\n height: 2px;\n border-radius: 999px;\n background: color-mix(in srgb, var(--bui-fg-primary) 12%, transparent);\n overflow: hidden;\n }\n\n .StatTiles.aurora_shareFill__93817f37ed {\n display: block;\n height: 100%;\n width: var(--tile-share);\n border-radius: inherit;\n background: var(--tile-fill);\n transition: width 320ms cubic-bezier(0.16, 1, 0.3, 1);\n }\n\n @media (prefers-reduced-motion: reduce) {\n .StatTiles.aurora_tile__93817f37ed,\n .StatTiles.aurora_tile__93817f37ed::before,\n .StatTiles.aurora_shareFill__93817f37ed {\n transition: none;\n }\n\n .StatTiles.aurora_tile__93817f37ed:hover,\n .StatTiles.aurora_tileActive__93817f37ed {\n transform: none;\n }\n }\n}\n";
|
|
4
|
+
var auroraStyles = {"grid":"StatTiles.aurora_grid__93817f37ed","tile":"StatTiles.aurora_tile__93817f37ed","tileActive":"StatTiles.aurora_tileActive__93817f37ed StatTiles.aurora_tile__93817f37ed","tileIconBox":"StatTiles.aurora_tileIconBox__93817f37ed","tileText":"StatTiles.aurora_tileText__93817f37ed","tileValue":"StatTiles.aurora_tileValue__93817f37ed","tileLabel":"StatTiles.aurora_tileLabel__93817f37ed","shareTrack":"StatTiles.aurora_shareTrack__93817f37ed","shareFill":"StatTiles.aurora_shareFill__93817f37ed"};
|
|
5
|
+
styleInject(css_248z);
|
|
6
|
+
|
|
7
|
+
export { auroraStyles as default };
|
|
8
|
+
//# sourceMappingURL=StatTiles.aurora.module.css.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StatTiles.aurora.module.css.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import styleInject from '../../node_modules_dist/style-inject/dist/style-inject.es.esm.js';
|
|
2
|
+
|
|
3
|
+
var css_248z = "/*\n * EXPERIMENT — tile variant \"console\" (see TILE_VARIANT in StatTiles.tsx).\n *\n * The six types as one instrument panel rather than six boxes: a single dark\n * recessed strip, each type a segment lit by its own colour.\n *\n * The central move is that colour is *light*, not paint — it appears as a glow,\n * a light bar and the icon, and never as a field behind text. Text is always\n * near-white on the same dark panel, so every type is equally legible and the\n * white-on-yellow problem that dogged the filled tiles cannot arise. The panel\n * is dark in both themes on purpose: it is the page's signature element.\n */\n@layer components {\n .StatTiles.console_grid__051b968612 {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));\n gap: 1px;\n /* The 1px gap plus this background paints the divider lines, so the row\n reads as one instrument with segments rather than six objects. */\n background: rgba(255, 255, 255, 0.07);\n padding: 1px;\n border-radius: var(--bui-radius-3);\n margin-bottom: var(--bui-space-5);\n overflow: hidden;\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06),\n 0 8px 24px -12px rgba(0, 0, 0, 0.5);\n }\n\n .StatTiles.console_tile__051b968612 {\n position: relative;\n --devaihub-tile-fg: #f2f4f8;\n --devaihub-tile-fg-muted: rgba(242, 244, 248, 0.62);\n background: #12141c;\n padding: var(--bui-space-4);\n padding-top: var(--bui-space-5);\n display: flex;\n align-items: center;\n gap: var(--bui-space-3);\n cursor: pointer;\n isolation: isolate;\n transition: background-color 180ms ease, box-shadow 180ms ease;\n }\n\n /* The segment's light: a bar across the top edge in the type's colour, with\n the glow it casts down into the panel. */\n .StatTiles.console_tile__051b968612::before {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n height: 2px;\n background: var(--tile-fill);\n box-shadow: 0 0 12px 0 var(--tile-fill);\n opacity: 0.75;\n transition: opacity 180ms ease, box-shadow 180ms ease;\n }\n\n /* The colour bleeding into the segment from that light source. */\n .StatTiles.console_tile__051b968612::after {\n content: '';\n position: absolute;\n inset: 0;\n background: radial-gradient(\n 120% 90% at 50% 0%,\n color-mix(in srgb, var(--tile-fill) 30%, transparent),\n transparent 70%\n );\n opacity: 0.55;\n pointer-events: none;\n z-index: -1;\n transition: opacity 180ms ease;\n }\n\n .StatTiles.console_tile__051b968612:hover {\n background: #171a24;\n }\n\n .StatTiles.console_tile__051b968612:hover::before {\n opacity: 1;\n box-shadow: 0 0 18px 1px var(--tile-fill);\n }\n\n .StatTiles.console_tile__051b968612:hover::after {\n opacity: 0.8;\n }\n\n .StatTiles.console_tile__051b968612:focus-visible {\n outline: 2px solid var(--tile-fill);\n outline-offset: -2px;\n }\n\n .StatTiles.console_tileActive__051b968612 {\n background: #1b1f2b;\n }\n\n .StatTiles.console_tileActive__051b968612::before {\n opacity: 1;\n height: 3px;\n box-shadow: 0 0 22px 2px var(--tile-fill);\n }\n\n .StatTiles.console_tileActive__051b968612::after {\n opacity: 1;\n }\n\n .StatTiles.console_tileIconBox__051b968612 {\n color: var(--tile-fill);\n display: flex;\n flex-shrink: 0;\n /* The glyph is a light source too, so it gets the same bloom. */\n filter: drop-shadow(\n 0 0 6px color-mix(in srgb, var(--tile-fill) 60%, transparent)\n );\n }\n\n .StatTiles.console_tileText__051b968612 {\n display: flex;\n flex-direction: column;\n min-width: 0;\n }\n\n .StatTiles.console_tile__051b968612 .StatTiles.console_tileValue__051b968612 {\n color: #f2f4f8;\n font-size: 1.625rem;\n font-weight: 700;\n line-height: 1.1;\n /* Tabular figures: the counts sit in a fixed column like an instrument\n readout instead of shifting as values change. */\n font-variant-numeric: tabular-nums;\n letter-spacing: 0.01em;\n display: block;\n }\n\n .StatTiles.console_tile__051b968612 .StatTiles.console_tileLabel__051b968612 {\n color: rgba(242, 244, 248, 0.62);\n font-size: 0.6875rem;\n font-weight: 600;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n line-height: 1.2;\n display: block;\n }\n\n /* Share readout, drawn in the segment's own light. */\n .StatTiles.console_shareTrack__051b968612 {\n position: absolute;\n left: var(--bui-space-4);\n right: var(--bui-space-4);\n bottom: var(--bui-space-3);\n height: 2px;\n border-radius: 999px;\n background: rgba(255, 255, 255, 0.09);\n overflow: hidden;\n }\n\n .StatTiles.console_shareFill__051b968612 {\n display: block;\n height: 100%;\n width: var(--tile-share);\n border-radius: inherit;\n background: var(--tile-fill);\n box-shadow: 0 0 8px 0 var(--tile-fill);\n transition: width 320ms cubic-bezier(0.16, 1, 0.3, 1);\n }\n\n @media (prefers-reduced-motion: reduce) {\n .StatTiles.console_tile__051b968612,\n .StatTiles.console_tile__051b968612::before,\n .StatTiles.console_tile__051b968612::after,\n .StatTiles.console_shareFill__051b968612 {\n transition: none;\n }\n }\n}\n";
|
|
4
|
+
var consoleStyles = {"grid":"StatTiles.console_grid__051b968612","tile":"StatTiles.console_tile__051b968612","tileActive":"StatTiles.console_tileActive__051b968612 StatTiles.console_tile__051b968612","tileIconBox":"StatTiles.console_tileIconBox__051b968612","tileText":"StatTiles.console_tileText__051b968612","tileValue":"StatTiles.console_tileValue__051b968612","tileLabel":"StatTiles.console_tileLabel__051b968612","shareTrack":"StatTiles.console_shareTrack__051b968612","shareFill":"StatTiles.console_shareFill__051b968612"};
|
|
5
|
+
styleInject(css_248z);
|
|
6
|
+
|
|
7
|
+
export { consoleStyles as default };
|
|
8
|
+
//# sourceMappingURL=StatTiles.console.module.css.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StatTiles.console.module.css.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import { Text } from '@backstage/ui';
|
|
3
|
+
import { RESOURCE_TYPES } from '@nospt/plugin-dev-ai-hub-common';
|
|
4
|
+
import { getTypeMeta } from './typeMeta.esm.js';
|
|
5
|
+
import solidStyles from './StatTiles.module.css.esm.js';
|
|
6
|
+
import consoleStyles from './StatTiles.console.module.css.esm.js';
|
|
7
|
+
import auroraStyles from './StatTiles.aurora.module.css.esm.js';
|
|
8
|
+
|
|
9
|
+
const TILE_VARIANT = "solid";
|
|
10
|
+
const VARIANT_STYLES = {
|
|
11
|
+
solid: solidStyles,
|
|
12
|
+
console: consoleStyles,
|
|
13
|
+
aurora: auroraStyles
|
|
14
|
+
};
|
|
15
|
+
const styles = VARIANT_STYLES[TILE_VARIANT];
|
|
16
|
+
function StatTiles({ counts, activeType, onToggle }) {
|
|
17
|
+
const total = RESOURCE_TYPES.reduce((sum, type) => sum + counts[type], 0);
|
|
18
|
+
return /* @__PURE__ */ jsx("div", { className: styles.grid, children: RESOURCE_TYPES.map((type) => {
|
|
19
|
+
const meta = getTypeMeta(type);
|
|
20
|
+
const isActive = activeType === type;
|
|
21
|
+
const share = total > 0 ? counts[type] / total : 0;
|
|
22
|
+
const percent = Math.round(share * 100);
|
|
23
|
+
return /* @__PURE__ */ jsxs(
|
|
24
|
+
"div",
|
|
25
|
+
{
|
|
26
|
+
className: isActive ? styles.tileActive : styles.tile,
|
|
27
|
+
style: {
|
|
28
|
+
"--tile-fill": meta.tileFill,
|
|
29
|
+
"--tile-share": `${share * 100}%`
|
|
30
|
+
},
|
|
31
|
+
title: `${counts[type]} of ${total} resources (${percent}%)`,
|
|
32
|
+
onClick: () => onToggle(type),
|
|
33
|
+
onKeyDown: (e) => {
|
|
34
|
+
if (e.key === "Enter" || e.key === " ") onToggle(type);
|
|
35
|
+
},
|
|
36
|
+
role: "button",
|
|
37
|
+
tabIndex: 0,
|
|
38
|
+
"aria-pressed": isActive,
|
|
39
|
+
"aria-label": `Filter by ${meta.pluralLabel}`,
|
|
40
|
+
children: [
|
|
41
|
+
/* @__PURE__ */ jsx("div", { className: styles.tileIconBox, "aria-hidden": true, children: /* @__PURE__ */ jsx(meta.Icon, { size: 20 }) }),
|
|
42
|
+
/* @__PURE__ */ jsxs("div", { className: styles.tileText, children: [
|
|
43
|
+
/* @__PURE__ */ jsx(
|
|
44
|
+
Text,
|
|
45
|
+
{
|
|
46
|
+
variant: "title-medium",
|
|
47
|
+
weight: "bold",
|
|
48
|
+
className: styles.tileValue,
|
|
49
|
+
style: {
|
|
50
|
+
color: "var(--devaihub-tile-fg)",
|
|
51
|
+
fontSize: "1.75rem",
|
|
52
|
+
lineHeight: 1.1
|
|
53
|
+
},
|
|
54
|
+
children: counts[type]
|
|
55
|
+
}
|
|
56
|
+
),
|
|
57
|
+
/* @__PURE__ */ jsx(
|
|
58
|
+
Text,
|
|
59
|
+
{
|
|
60
|
+
variant: "body-small",
|
|
61
|
+
className: styles.tileLabel,
|
|
62
|
+
style: {
|
|
63
|
+
color: "var(--devaihub-tile-fg-muted, var(--devaihub-tile-fg))",
|
|
64
|
+
fontSize: "0.75rem",
|
|
65
|
+
fontWeight: 600,
|
|
66
|
+
letterSpacing: "0.07em",
|
|
67
|
+
textTransform: "uppercase"
|
|
68
|
+
},
|
|
69
|
+
children: meta.pluralLabel
|
|
70
|
+
}
|
|
71
|
+
)
|
|
72
|
+
] }),
|
|
73
|
+
/* @__PURE__ */ jsx("div", { className: styles.shareTrack, "aria-hidden": true, children: /* @__PURE__ */ jsx("span", { className: styles.shareFill }) })
|
|
74
|
+
]
|
|
75
|
+
},
|
|
76
|
+
type
|
|
77
|
+
);
|
|
78
|
+
}) });
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export { StatTiles };
|
|
82
|
+
//# sourceMappingURL=StatTiles.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StatTiles.esm.js","sources":["../../../src/components/AiResourcesPage/StatTiles.tsx"],"sourcesContent":["import { Text } from '@backstage/ui';\nimport {\n RESOURCE_TYPES,\n type ResourceType,\n} from '@nospt/plugin-dev-ai-hub-common';\nimport { getTypeMeta } from './typeMeta';\nimport solidStyles from './StatTiles.module.css';\nimport consoleStyles from './StatTiles.console.module.css';\nimport auroraStyles from './StatTiles.aurora.module.css';\n\n/**\n * EXPERIMENT (2026-08-04) — three visual treatments of the type row, chosen at\n * build time. Every variant renders the same DOM and the same behaviour; they\n * differ only in stylesheet, so switching between them cannot change what the\n * component does.\n *\n * - `solid` — shipped design: brand-coloured blocks, white content.\n * - `console` — one dark instrument panel, each type a segment lit by its hue.\n * - `aurora` — frosted glass panes over a blurred field of the six hues.\n *\n * TO REVERT THE EXPERIMENT ENTIRELY: set this to `solid`, delete\n * `StatTiles.console.module.css` and `StatTiles.aurora.module.css`, and drop\n * their imports above. Nothing else in the plugin references them.\n *\n * Once a variant is chosen, collapse this: the winner's rules move into\n * `StatTiles.module.css` and the losers are deleted. Leaving three stylesheets\n * in the bundle is the cost of the experiment, not a design.\n */\nconst TILE_VARIANT: 'solid' | 'console' | 'aurora' = 'solid';\n\nconst VARIANT_STYLES = {\n solid: solidStyles,\n console: consoleStyles,\n aurora: auroraStyles,\n};\n\nconst styles = VARIANT_STYLES[TILE_VARIANT];\n\ninterface StatTilesProps {\n counts: Record<ResourceType, number>;\n activeType?: ResourceType;\n onToggle: (type: ResourceType) => void;\n}\n\n/**\n * The six clickable type tiles that act as the type filter (legacy pattern):\n * click filters to that type, click again clears.\n *\n * A share bar under each tile carries that type's proportion of the catalog,\n * so the row reads as a distribution (\"mostly skills, few hooks\") rather than\n * six identical objects — repetition is what makes attention fall off across a\n * row, and the count alone does nothing to break it.\n */\nexport function StatTiles({ counts, activeType, onToggle }: StatTilesProps) {\n const total = RESOURCE_TYPES.reduce((sum, type) => sum + counts[type], 0);\n\n return (\n <div className={styles.grid}>\n {RESOURCE_TYPES.map(type => {\n const meta = getTypeMeta(type);\n const isActive = activeType === type;\n const share = total > 0 ? counts[type] / total : 0;\n const percent = Math.round(share * 100);\n return (\n <div\n key={type}\n className={isActive ? styles.tileActive : styles.tile}\n style={\n {\n '--tile-fill': meta.tileFill,\n '--tile-share': `${share * 100}%`,\n } as React.CSSProperties\n }\n title={`${counts[type]} of ${total} resources (${percent}%)`}\n onClick={() => onToggle(type)}\n onKeyDown={e => {\n if (e.key === 'Enter' || e.key === ' ') onToggle(type);\n }}\n role=\"button\"\n tabIndex={0}\n aria-pressed={isActive}\n aria-label={`Filter by ${meta.pluralLabel}`}\n >\n {/* Echoes the card's tinted icon square, but inverted: a\n translucent white panel on the tile's own colour. Hidden from\n assistive tech — the tile is already labelled. */}\n <div className={styles.tileIconBox} aria-hidden>\n <meta.Icon size={20} />\n </div>\n <div className={styles.tileText}>\n {/* Typography is set inline as well as in the stylesheet: BUI's\n Text carries its own colour and size, and which of the two\n wins depends on the order BUI's styles land in the host app\n relative to this plugin's. In an app where BUI came last,\n these fell back to BUI's own values — which is how the tile\n labels once silently changed colour between themes. */}\n <Text\n variant=\"title-medium\"\n weight=\"bold\"\n className={styles.tileValue}\n style={{\n color: 'var(--devaihub-tile-fg)',\n fontSize: '1.75rem',\n lineHeight: 1.1,\n }}\n >\n {counts[type]}\n </Text>\n <Text\n variant=\"body-small\"\n className={styles.tileLabel}\n style={{\n color:\n 'var(--devaihub-tile-fg-muted, var(--devaihub-tile-fg))',\n fontSize: '0.75rem',\n fontWeight: 600,\n letterSpacing: '0.07em',\n textTransform: 'uppercase',\n }}\n >\n {meta.pluralLabel}\n </Text>\n </div>\n {/* The share bar. Decorative for assistive tech — the same figure\n is already in the tile's title and derivable from the count. */}\n <div className={styles.shareTrack} aria-hidden>\n <span className={styles.shareFill} />\n </div>\n </div>\n );\n })}\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
|