@nospt/plugin-dev-ai-hub 0.2.4 → 0.3.0

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.
Files changed (122) hide show
  1. package/dist/api/DevAiHubResourceClient.esm.js +97 -0
  2. package/dist/api/DevAiHubResourceClient.esm.js.map +1 -0
  3. package/dist/components/AiResourcesPage/AiResourcesPage.esm.js +150 -0
  4. package/dist/components/AiResourcesPage/AiResourcesPage.esm.js.map +1 -0
  5. package/dist/components/AiResourcesPage/AiResourcesPage.module.css.esm.js +8 -0
  6. package/dist/components/AiResourcesPage/AiResourcesPage.module.css.esm.js.map +1 -0
  7. package/dist/components/AiResourcesPage/ResourceCard.esm.js +158 -0
  8. package/dist/components/AiResourcesPage/ResourceCard.esm.js.map +1 -0
  9. package/dist/components/AiResourcesPage/ResourceCard.module.css.esm.js +8 -0
  10. package/dist/components/AiResourcesPage/ResourceCard.module.css.esm.js.map +1 -0
  11. package/dist/components/AiResourcesPage/ResourceDetailPanel.esm.js +234 -0
  12. package/dist/components/AiResourcesPage/ResourceDetailPanel.esm.js.map +1 -0
  13. package/dist/components/AiResourcesPage/ResourceDetailPanel.module.css.esm.js +8 -0
  14. package/dist/components/AiResourcesPage/ResourceDetailPanel.module.css.esm.js.map +1 -0
  15. package/dist/components/AiResourcesPage/ResourceFilters.esm.js +242 -0
  16. package/dist/components/AiResourcesPage/ResourceFilters.esm.js.map +1 -0
  17. package/dist/components/AiResourcesPage/ResourceFilters.module.css.esm.js +8 -0
  18. package/dist/components/AiResourcesPage/ResourceFilters.module.css.esm.js.map +1 -0
  19. package/dist/components/AiResourcesPage/ResourceInstallDialog.esm.js +366 -0
  20. package/dist/components/AiResourcesPage/ResourceInstallDialog.esm.js.map +1 -0
  21. package/dist/components/AiResourcesPage/ResourceInstallDialog.module.css.esm.js +8 -0
  22. package/dist/components/AiResourcesPage/ResourceInstallDialog.module.css.esm.js.map +1 -0
  23. package/dist/components/AiResourcesPage/StatTiles.esm.js +49 -0
  24. package/dist/components/AiResourcesPage/StatTiles.esm.js.map +1 -0
  25. package/dist/components/AiResourcesPage/StatTiles.module.css.esm.js +8 -0
  26. package/dist/components/AiResourcesPage/StatTiles.module.css.esm.js.map +1 -0
  27. package/dist/components/AiResourcesPage/index.esm.js +6 -0
  28. package/dist/components/AiResourcesPage/typeMeta.esm.js +35 -0
  29. package/dist/components/AiResourcesPage/typeMeta.esm.js.map +1 -0
  30. package/dist/components/ToolIcon/ToolIcon.esm.js +26 -9
  31. package/dist/components/ToolIcon/ToolIcon.esm.js.map +1 -1
  32. package/dist/hooks/useResourceBody.esm.js +39 -0
  33. package/dist/hooks/useResourceBody.esm.js.map +1 -0
  34. package/dist/hooks/useResources.esm.js +26 -0
  35. package/dist/hooks/useResources.esm.js.map +1 -0
  36. package/dist/hooks/useTelemetryCounts.esm.js +24 -0
  37. package/dist/hooks/useTelemetryCounts.esm.js.map +1 -0
  38. package/dist/index.d.ts +1 -38
  39. package/dist/index.esm.js +1 -3
  40. package/dist/index.esm.js.map +1 -1
  41. package/dist/plugin.esm.js +15 -24
  42. package/dist/plugin.esm.js.map +1 -1
  43. package/dist/routes.esm.js +1 -2
  44. package/dist/routes.esm.js.map +1 -1
  45. package/package.json +17 -10
  46. package/.eslintrc.js +0 -1
  47. package/dev/index.tsx +0 -32
  48. package/dist/api/DevAiHubClient.esm.js +0 -78
  49. package/dist/api/DevAiHubClient.esm.js.map +0 -1
  50. package/dist/components/AssetCard/AssetCard.esm.js +0 -120
  51. package/dist/components/AssetCard/AssetCard.esm.js.map +0 -1
  52. package/dist/components/AssetCard/AssetCard.module.css.esm.js +0 -8
  53. package/dist/components/AssetCard/AssetCard.module.css.esm.js.map +0 -1
  54. package/dist/components/AssetDetailPanel/AssetDetailPanel.esm.js +0 -218
  55. package/dist/components/AssetDetailPanel/AssetDetailPanel.esm.js.map +0 -1
  56. package/dist/components/AssetDetailPanel/AssetDetailPanel.module.css.esm.js +0 -8
  57. package/dist/components/AssetDetailPanel/AssetDetailPanel.module.css.esm.js.map +0 -1
  58. package/dist/components/AssetFilters/AssetFilters.esm.js +0 -176
  59. package/dist/components/AssetFilters/AssetFilters.esm.js.map +0 -1
  60. package/dist/components/AssetFilters/AssetFilters.module.css.esm.js +0 -8
  61. package/dist/components/AssetFilters/AssetFilters.module.css.esm.js.map +0 -1
  62. package/dist/components/AssetInstallDialog/AssetInstallDialog.esm.js +0 -138
  63. package/dist/components/AssetInstallDialog/AssetInstallDialog.esm.js.map +0 -1
  64. package/dist/components/AssetInstallDialog/AssetInstallDialog.module.css.esm.js +0 -8
  65. package/dist/components/AssetInstallDialog/AssetInstallDialog.module.css.esm.js.map +0 -1
  66. package/dist/components/DevAiHubPage/DevAiHubPage.esm.js +0 -195
  67. package/dist/components/DevAiHubPage/DevAiHubPage.esm.js.map +0 -1
  68. package/dist/components/DevAiHubPage/DevAiHubPage.module.css.esm.js +0 -8
  69. package/dist/components/DevAiHubPage/DevAiHubPage.module.css.esm.js.map +0 -1
  70. package/dist/components/DevAiHubPage/index.esm.js +0 -6
  71. package/dist/components/McpConfigPage/McpConfigPage.esm.js +0 -200
  72. package/dist/components/McpConfigPage/McpConfigPage.esm.js.map +0 -1
  73. package/dist/components/McpConfigPage/McpConfigPage.module.css.esm.js +0 -8
  74. package/dist/components/McpConfigPage/McpConfigPage.module.css.esm.js.map +0 -1
  75. package/dist/components/McpConfigPage/index.esm.js +0 -6
  76. package/dist/components/McpConfigPage/index.esm.js.map +0 -1
  77. package/dist/hooks/index.esm.js +0 -114
  78. package/dist/hooks/index.esm.js.map +0 -1
  79. package/dist/pluginLegacy.esm.js +0 -27
  80. package/dist/pluginLegacy.esm.js.map +0 -1
  81. package/package.json-prepack +0 -85
  82. package/src/api/DevAiHubClient.ts +0 -110
  83. package/src/components/AssetCard/AssetCard.module.css +0 -153
  84. package/src/components/AssetCard/AssetCard.test.tsx +0 -182
  85. package/src/components/AssetCard/AssetCard.tsx +0 -152
  86. package/src/components/AssetCard/index.ts +0 -1
  87. package/src/components/AssetDetailPanel/AssetDetailPanel.module.css +0 -207
  88. package/src/components/AssetDetailPanel/AssetDetailPanel.tsx +0 -313
  89. package/src/components/AssetDetailPanel/index.ts +0 -1
  90. package/src/components/AssetFilters/AssetFilters.module.css +0 -67
  91. package/src/components/AssetFilters/AssetFilters.tsx +0 -219
  92. package/src/components/AssetFilters/index.ts +0 -2
  93. package/src/components/AssetInstallDialog/AssetInstallDialog.module.css +0 -72
  94. package/src/components/AssetInstallDialog/AssetInstallDialog.tsx +0 -176
  95. package/src/components/AssetInstallDialog/index.ts +0 -1
  96. package/src/components/DevAiHubPage/DevAiHubPage.module.css +0 -105
  97. package/src/components/DevAiHubPage/DevAiHubPage.tsx +0 -197
  98. package/src/components/DevAiHubPage/index.ts +0 -1
  99. package/src/components/McpConfigDialog/McpConfigDialog.module.css +0 -111
  100. package/src/components/McpConfigDialog/McpConfigDialog.tsx +0 -271
  101. package/src/components/McpConfigDialog/index.ts +0 -1
  102. package/src/components/McpConfigPage/McpConfigPage.module.css +0 -121
  103. package/src/components/McpConfigPage/McpConfigPage.tsx +0 -242
  104. package/src/components/McpConfigPage/index.ts +0 -1
  105. package/src/components/ToolIcon/ToolIcon.tsx +0 -65
  106. package/src/components/ToolIcon/index.ts +0 -1
  107. package/src/css-modules.d.ts +0 -4
  108. package/src/hooks/index.ts +0 -152
  109. package/src/index.ts +0 -10
  110. package/src/plugin.tsx +0 -60
  111. package/src/pluginLegacy.ts +0 -42
  112. package/src/routes.ts +0 -4
  113. package/tests/e2e/.auth.json +0 -14
  114. package/tests/e2e/asset-detail.spec.ts +0 -90
  115. package/tests/e2e/asset-filtering.spec.ts +0 -145
  116. package/tests/e2e/asset-installation.spec.ts +0 -74
  117. package/tests/e2e/dev-ai-hub-page.spec.ts +0 -89
  118. package/tests/e2e/fixtures/base.ts +0 -61
  119. package/tests/e2e/fixtures/mock-api.ts +0 -221
  120. package/tests/e2e/mcp-config.spec.ts +0 -88
  121. package/tsconfig.json +0 -10
  122. /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,49 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { Box, Text } from '@backstage/ui';
3
+ import { RESOURCE_TYPES } from '@nospt/plugin-dev-ai-hub-common';
4
+ import { getTypeMeta } from './typeMeta.esm.js';
5
+ import styles from './StatTiles.module.css.esm.js';
6
+
7
+ function StatTiles({ counts, activeType, onToggle }) {
8
+ return /* @__PURE__ */ jsx("div", { className: styles.grid, children: RESOURCE_TYPES.map((type) => {
9
+ const meta = getTypeMeta(type);
10
+ const isActive = activeType === type;
11
+ return /* @__PURE__ */ jsx(
12
+ "div",
13
+ {
14
+ className: isActive ? styles.tileActive : styles.tile,
15
+ style: {
16
+ "--tile-color": meta.tileColor,
17
+ "--tile-deep": meta.tileDeep
18
+ },
19
+ onClick: () => onToggle(type),
20
+ onKeyDown: (e) => {
21
+ if (e.key === "Enter" || e.key === " ") onToggle(type);
22
+ },
23
+ role: "button",
24
+ tabIndex: 0,
25
+ "aria-pressed": isActive,
26
+ "aria-label": `Filter by ${meta.pluralLabel}`,
27
+ children: /* @__PURE__ */ jsxs("div", { className: styles.tileInner, children: [
28
+ /* @__PURE__ */ jsxs(Box, { children: [
29
+ /* @__PURE__ */ jsx(
30
+ Text,
31
+ {
32
+ variant: "title-large",
33
+ weight: "bold",
34
+ className: styles.tileValue,
35
+ children: counts[type]
36
+ }
37
+ ),
38
+ /* @__PURE__ */ jsx(Text, { variant: "body-small", className: styles.tileLabel, children: meta.pluralLabel })
39
+ ] }),
40
+ /* @__PURE__ */ jsx("div", { className: styles.tileIconBox, children: /* @__PURE__ */ jsx(meta.Icon, { size: 22 }) })
41
+ ] })
42
+ },
43
+ type
44
+ );
45
+ }) });
46
+ }
47
+
48
+ export { StatTiles };
49
+ //# sourceMappingURL=StatTiles.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StatTiles.esm.js","sources":["../../../src/components/AiResourcesPage/StatTiles.tsx"],"sourcesContent":["import { Box, Text } from '@backstage/ui';\nimport {\n RESOURCE_TYPES,\n type ResourceType,\n} from '@nospt/plugin-dev-ai-hub-common';\nimport { getTypeMeta } from './typeMeta';\nimport styles from './StatTiles.module.css';\n\ninterface StatTilesProps {\n counts: Record<ResourceType, number>;\n activeType?: ResourceType;\n onToggle: (type: ResourceType) => void;\n}\n\n/**\n * The five clickable stat tiles that act as the type filter (legacy pattern):\n * click filters to that type, click again clears.\n */\nexport function StatTiles({ counts, activeType, onToggle }: StatTilesProps) {\n return (\n <div className={styles.grid}>\n {RESOURCE_TYPES.map(type => {\n const meta = getTypeMeta(type);\n const isActive = activeType === type;\n return (\n <div\n key={type}\n className={isActive ? styles.tileActive : styles.tile}\n style={\n {\n '--tile-color': meta.tileColor,\n '--tile-deep': meta.tileDeep,\n } as React.CSSProperties\n }\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 <div className={styles.tileInner}>\n <Box>\n <Text\n variant=\"title-large\"\n weight=\"bold\"\n className={styles.tileValue}\n >\n {counts[type]}\n </Text>\n <Text variant=\"body-small\" className={styles.tileLabel}>\n {meta.pluralLabel}\n </Text>\n </Box>\n <div className={styles.tileIconBox}>\n <meta.Icon size={22} />\n </div>\n </div>\n </div>\n );\n })}\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;;AAkBO,SAAS,SAAA,CAAU,EAAE,MAAA,EAAQ,UAAA,EAAY,UAAS,EAAmB;AAC1E,EAAA,2BACG,KAAA,EAAA,EAAI,SAAA,EAAW,OAAO,IAAA,EACpB,QAAA,EAAA,cAAA,CAAe,IAAI,CAAA,IAAA,KAAQ;AAC1B,IAAA,MAAM,IAAA,GAAO,YAAY,IAAI,CAAA;AAC7B,IAAA,MAAM,WAAW,UAAA,KAAe,IAAA;AAChC,IAAA,uBACE,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QAEC,SAAA,EAAW,QAAA,GAAW,MAAA,CAAO,UAAA,GAAa,MAAA,CAAO,IAAA;AAAA,QACjD,KAAA,EACE;AAAA,UACE,gBAAgB,IAAA,CAAK,SAAA;AAAA,UACrB,eAAe,IAAA,CAAK;AAAA,SACtB;AAAA,QAEF,OAAA,EAAS,MAAM,QAAA,CAAS,IAAI,CAAA;AAAA,QAC5B,WAAW,CAAA,CAAA,KAAK;AACd,UAAA,IAAI,EAAE,GAAA,KAAQ,OAAA,IAAW,EAAE,GAAA,KAAQ,GAAA,WAAc,IAAI,CAAA;AAAA,QACvD,CAAA;AAAA,QACA,IAAA,EAAK,QAAA;AAAA,QACL,QAAA,EAAU,CAAA;AAAA,QACV,cAAA,EAAc,QAAA;AAAA,QACd,YAAA,EAAY,CAAA,UAAA,EAAa,IAAA,CAAK,WAAW,CAAA,CAAA;AAAA,QAEzC,QAAA,kBAAA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,MAAA,CAAO,SAAA,EACrB,QAAA,EAAA;AAAA,0BAAA,IAAA,CAAC,GAAA,EAAA,EACC,QAAA,EAAA;AAAA,4BAAA,GAAA;AAAA,cAAC,IAAA;AAAA,cAAA;AAAA,gBACC,OAAA,EAAQ,aAAA;AAAA,gBACR,MAAA,EAAO,MAAA;AAAA,gBACP,WAAW,MAAA,CAAO,SAAA;AAAA,gBAEjB,iBAAO,IAAI;AAAA;AAAA,aACd;AAAA,4BACA,GAAA,CAAC,QAAK,OAAA,EAAQ,YAAA,EAAa,WAAW,MAAA,CAAO,SAAA,EAC1C,eAAK,WAAA,EACR;AAAA,WAAA,EACF,CAAA;AAAA,0BACA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,MAAA,CAAO,WAAA,EACrB,QAAA,kBAAA,GAAA,CAAC,IAAA,CAAK,IAAA,EAAL,EAAU,IAAA,EAAM,EAAA,EAAI,CAAA,EACvB;AAAA,SAAA,EACF;AAAA,OAAA;AAAA,MAjCK;AAAA,KAkCP;AAAA,EAEJ,CAAC,CAAA,EACH,CAAA;AAEJ;;;;"}
@@ -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 /* Faithful to the legacy stat boxes: two-stop gradient, always-white text. */\n .StatTiles_grid__18dcbb619b {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));\n gap: var(--bui-space-4);\n margin-bottom: var(--bui-space-5);\n }\n\n .StatTiles_tile__18dcbb619b {\n background: linear-gradient(\n 135deg,\n var(--tile-color) 0%,\n var(--tile-deep) 100%\n );\n border-radius: var(--bui-radius-3);\n padding: var(--bui-space-4);\n cursor: pointer;\n box-shadow: 0 2px 8px color-mix(in srgb, var(--tile-color) 25%, transparent);\n transition: all 0.2s ease;\n }\n\n .StatTiles_tile__18dcbb619b:hover {\n transform: translateY(-2px);\n }\n\n .StatTiles_tileActive__18dcbb619b {\n box-shadow: 0 8px 24px\n color-mix(in srgb, var(--tile-color) 25%, transparent);\n transform: translateY(-2px);\n outline: 2px solid rgba(255, 255, 255, 0.6);\n outline-offset: 2px;\n }\n\n .StatTiles_tileInner__18dcbb619b {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--bui-space-2);\n }\n\n /* Scoped under .tile so they outrank BUI's Text colour rule regardless of\n stylesheet injection order (same layer, same base specificity). */\n .StatTiles_tile__18dcbb619b .StatTiles_tileValue__18dcbb619b {\n color: #fff;\n line-height: 1;\n display: block;\n }\n\n .StatTiles_tile__18dcbb619b .StatTiles_tileLabel__18dcbb619b {\n color: rgba(255, 255, 255, 0.85);\n font-weight: 500;\n margin-top: var(--bui-space-1);\n display: block;\n }\n\n .StatTiles_tileIconBox__18dcbb619b {\n width: 40px;\n height: 40px;\n border-radius: var(--bui-radius-2);\n background-color: rgba(255, 255, 255, 0.2);\n display: flex;\n align-items: center;\n justify-content: center;\n color: #fff;\n flex-shrink: 0;\n }\n}\n";
4
+ var styles = {"grid":"StatTiles_grid__18dcbb619b","tile":"StatTiles_tile__18dcbb619b","tileActive":"StatTiles_tileActive__18dcbb619b StatTiles_tile__18dcbb619b","tileInner":"StatTiles_tileInner__18dcbb619b","tileValue":"StatTiles_tileValue__18dcbb619b","tileLabel":"StatTiles_tileLabel__18dcbb619b","tileIconBox":"StatTiles_tileIconBox__18dcbb619b"};
5
+ styleInject(css_248z);
6
+
7
+ export { styles as default };
8
+ //# sourceMappingURL=StatTiles.module.css.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StatTiles.module.css.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}
@@ -0,0 +1,6 @@
1
+ import { AiResourcesPage } from './AiResourcesPage.esm.js';
2
+
3
+
4
+
5
+ export { AiResourcesPage };
6
+ //# sourceMappingURL=index.esm.js.map
@@ -0,0 +1,35 @@
1
+ import { RiStore2Line, RiPuzzleLine, RiPlugLine, RiFlashlightLine, RiRobot2Line, RiToolsLine } from '@remixicon/react';
2
+ import { RESOURCE_TYPE_REGISTRY } from '@nospt/plugin-dev-ai-hub-common';
3
+
4
+ const ICONS = {
5
+ tools: RiToolsLine,
6
+ robot: RiRobot2Line,
7
+ flash: RiFlashlightLine,
8
+ plug: RiPlugLine,
9
+ puzzle: RiPuzzleLine,
10
+ store: RiStore2Line
11
+ };
12
+ function getTypeMeta(type) {
13
+ const info = RESOURCE_TYPE_REGISTRY[type];
14
+ return {
15
+ ...info,
16
+ Icon: ICONS[info.icon],
17
+ color: `var(--devaihub-type-${info.colorRole})`,
18
+ colorBg: `var(--devaihub-type-${info.colorRole}-bg)`,
19
+ tileColor: `var(--devaihub-tile-${info.colorRole})`,
20
+ tileDeep: `var(--devaihub-tile-${info.colorRole}-deep)`
21
+ };
22
+ }
23
+ const FRAMEWORK_LABELS = {
24
+ "claude-code": "Claude Code",
25
+ "github-copilot": "GitHub Copilot",
26
+ "google-gemini": "Google Gemini",
27
+ cursor: "Cursor",
28
+ all: "All tools"
29
+ };
30
+ function frameworkLabel(token) {
31
+ return FRAMEWORK_LABELS[token] ?? token;
32
+ }
33
+
34
+ export { frameworkLabel, getTypeMeta };
35
+ //# sourceMappingURL=typeMeta.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typeMeta.esm.js","sources":["../../../src/components/AiResourcesPage/typeMeta.tsx"],"sourcesContent":["import type { ElementType } from 'react';\nimport {\n RiFlashlightLine,\n RiPlugLine,\n RiPuzzleLine,\n RiRobot2Line,\n RiStore2Line,\n RiToolsLine,\n} from '@remixicon/react';\nimport {\n RESOURCE_TYPE_REGISTRY,\n type ResourceType,\n type ResourceTypeIcon,\n type ResourceTypeInfo,\n} from '@nospt/plugin-dev-ai-hub-common';\n\n/** Maps the registry's React-free icon identifiers to components. */\nconst ICONS: Record<ResourceTypeIcon, ElementType> = {\n tools: RiToolsLine,\n robot: RiRobot2Line,\n flash: RiFlashlightLine,\n plug: RiPlugLine,\n puzzle: RiPuzzleLine,\n store: RiStore2Line,\n};\n\nexport interface TypeMeta extends ResourceTypeInfo {\n Icon: ElementType;\n /** The plugin-owned colour tokens for this role (ADR-0008). */\n color: string;\n colorBg: string;\n /** Stat-tile gradient pair (legacy boxes): start → deep. */\n tileColor: string;\n tileDeep: string;\n}\n\nexport function getTypeMeta(type: ResourceType): TypeMeta {\n const info = RESOURCE_TYPE_REGISTRY[type];\n return {\n ...info,\n Icon: ICONS[info.icon],\n color: `var(--devaihub-type-${info.colorRole})`,\n colorBg: `var(--devaihub-type-${info.colorRole}-bg)`,\n tileColor: `var(--devaihub-tile-${info.colorRole})`,\n tileDeep: `var(--devaihub-tile-${info.colorRole}-deep)`,\n };\n}\n\n/** Display labels for the known framework tokens; unknown pass through. */\nconst FRAMEWORK_LABELS: Record<string, string> = {\n 'claude-code': 'Claude Code',\n 'github-copilot': 'GitHub Copilot',\n 'google-gemini': 'Google Gemini',\n cursor: 'Cursor',\n all: 'All tools',\n};\n\nexport function frameworkLabel(token: string): string {\n return FRAMEWORK_LABELS[token] ?? token;\n}\n"],"names":[],"mappings":";;;AAiBA,MAAM,KAAA,GAA+C;AAAA,EACnD,KAAA,EAAO,WAAA;AAAA,EACP,KAAA,EAAO,YAAA;AAAA,EACP,KAAA,EAAO,gBAAA;AAAA,EACP,IAAA,EAAM,UAAA;AAAA,EACN,MAAA,EAAQ,YAAA;AAAA,EACR,KAAA,EAAO;AACT,CAAA;AAYO,SAAS,YAAY,IAAA,EAA8B;AACxD,EAAA,MAAM,IAAA,GAAO,uBAAuB,IAAI,CAAA;AACxC,EAAA,OAAO;AAAA,IACL,GAAG,IAAA;AAAA,IACH,IAAA,EAAM,KAAA,CAAM,IAAA,CAAK,IAAI,CAAA;AAAA,IACrB,KAAA,EAAO,CAAA,oBAAA,EAAuB,IAAA,CAAK,SAAS,CAAA,CAAA,CAAA;AAAA,IAC5C,OAAA,EAAS,CAAA,oBAAA,EAAuB,IAAA,CAAK,SAAS,CAAA,IAAA,CAAA;AAAA,IAC9C,SAAA,EAAW,CAAA,oBAAA,EAAuB,IAAA,CAAK,SAAS,CAAA,CAAA,CAAA;AAAA,IAChD,QAAA,EAAU,CAAA,oBAAA,EAAuB,IAAA,CAAK,SAAS,CAAA,MAAA;AAAA,GACjD;AACF;AAGA,MAAM,gBAAA,GAA2C;AAAA,EAC/C,aAAA,EAAe,aAAA;AAAA,EACf,gBAAA,EAAkB,gBAAA;AAAA,EAClB,eAAA,EAAiB,eAAA;AAAA,EACjB,MAAA,EAAQ,QAAA;AAAA,EACR,GAAA,EAAK;AACP,CAAA;AAEO,SAAS,eAAe,KAAA,EAAuB;AACpD,EAAA,OAAO,gBAAA,CAAiB,KAAK,CAAA,IAAK,KAAA;AACpC;;;;"}