@julianpedro/plugin-dev-ai-hub 0.2.0 → 0.4.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 (50) hide show
  1. package/dist/api/DevAiHubClient.esm.js +15 -2
  2. package/dist/api/DevAiHubClient.esm.js.map +1 -1
  3. package/dist/components/AdminPage/AdminPage.esm.js +106 -0
  4. package/dist/components/AdminPage/AdminPage.esm.js.map +1 -0
  5. package/dist/components/AdminPage/index.esm.js +6 -0
  6. package/dist/components/AdminPage/index.esm.js.map +1 -0
  7. package/dist/components/AssetCard/AssetCard.esm.js +117 -26
  8. package/dist/components/AssetCard/AssetCard.esm.js.map +1 -1
  9. package/dist/components/AssetDetailPanel/AssetDetailPanel.esm.js +271 -87
  10. package/dist/components/AssetDetailPanel/AssetDetailPanel.esm.js.map +1 -1
  11. package/dist/components/AssetFilters/AssetFilters.esm.js +105 -44
  12. package/dist/components/AssetFilters/AssetFilters.esm.js.map +1 -1
  13. package/dist/components/AssetHelpDialog/AssetHelpDialog.esm.js +87 -0
  14. package/dist/components/AssetHelpDialog/AssetHelpDialog.esm.js.map +1 -0
  15. package/dist/components/AssetInstallDialog/AssetInstallDialog.esm.js +328 -108
  16. package/dist/components/AssetInstallDialog/AssetInstallDialog.esm.js.map +1 -1
  17. package/dist/components/AssetsTab/AssetsTab.esm.js +221 -0
  18. package/dist/components/AssetsTab/AssetsTab.esm.js.map +1 -0
  19. package/dist/components/AssetsTab/index.esm.js +6 -0
  20. package/dist/components/AssetsTab/index.esm.js.map +1 -0
  21. package/dist/components/DevAiHubPage/DevAiHubPage.esm.js +266 -134
  22. package/dist/components/DevAiHubPage/DevAiHubPage.esm.js.map +1 -1
  23. package/dist/components/McpConfigDialog/McpConfigDialog.esm.js +20 -297
  24. package/dist/components/McpConfigDialog/McpConfigDialog.esm.js.map +1 -1
  25. package/dist/components/McpPage/McpPage.esm.js +478 -0
  26. package/dist/components/McpPage/McpPage.esm.js.map +1 -0
  27. package/dist/components/McpPage/index.esm.js +6 -0
  28. package/dist/components/McpPage/index.esm.js.map +1 -0
  29. package/dist/components/ModelIcon/ModelBadge.esm.js +73 -0
  30. package/dist/components/ModelIcon/ModelBadge.esm.js.map +1 -0
  31. package/dist/components/ModelIcon/ModelIcon.esm.js +45 -0
  32. package/dist/components/ModelIcon/ModelIcon.esm.js.map +1 -0
  33. package/dist/components/ToolIcon/ToolIcon.esm.js +4 -1
  34. package/dist/components/ToolIcon/ToolIcon.esm.js.map +1 -1
  35. package/dist/context/UiConfigContext.esm.js +79 -0
  36. package/dist/context/UiConfigContext.esm.js.map +1 -0
  37. package/dist/hooks/index.esm.js +36 -1
  38. package/dist/hooks/index.esm.js.map +1 -1
  39. package/dist/index.d.ts +146 -23
  40. package/dist/index.esm.js +1 -0
  41. package/dist/index.esm.js.map +1 -1
  42. package/dist/locales/es.esm.js +121 -0
  43. package/dist/locales/es.esm.js.map +1 -0
  44. package/dist/locales/pt-BR.esm.js +121 -0
  45. package/dist/locales/pt-BR.esm.js.map +1 -0
  46. package/dist/plugin.esm.js +35 -6
  47. package/dist/plugin.esm.js.map +1 -1
  48. package/dist/translation.esm.js +151 -0
  49. package/dist/translation.esm.js.map +1 -0
  50. package/package.json +15 -5
@@ -0,0 +1,73 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { useTheme, alpha } from '@mui/material/styles';
3
+ import Chip from '@mui/material/Chip';
4
+ import { detectFamily, ModelIcon } from './ModelIcon.esm.js';
5
+
6
+ const BADGE_COLORS = {
7
+ Anthropic: "#CC6B3D",
8
+ // Anthropic terra-cotta orange
9
+ OpenAI: "#10A37F",
10
+ // ChatGPT green (more recognizable than their dark purple)
11
+ Google: "#3D5AFE",
12
+ // Gemini indigo
13
+ Meta: "#0467DF",
14
+ // Meta blue
15
+ Ollama: "#4F7FBF"
16
+ // Neutral blue (Ollama has no distinctive brand color)
17
+ };
18
+ function ModelBadge({ model }) {
19
+ const theme = useTheme();
20
+ const isDark = theme.palette.mode === "dark";
21
+ const family = detectFamily(model);
22
+ const hex = family ? BADGE_COLORS[family.label] ?? `#${family.hex}` : void 0;
23
+ let bg;
24
+ let fg;
25
+ let border;
26
+ if (hex) {
27
+ bg = isDark ? alpha(hex, 0.22) : hex;
28
+ fg = isDark ? hex : "#fff";
29
+ border = isDark ? alpha(hex, 0.4) : hex;
30
+ } else {
31
+ bg = isDark ? "rgba(255,255,255,0.08)" : "rgba(0,0,0,0.06)";
32
+ fg = isDark ? "rgba(255,255,255,0.55)" : "rgba(0,0,0,0.45)";
33
+ border = "transparent";
34
+ }
35
+ return /* @__PURE__ */ jsx(
36
+ Chip,
37
+ {
38
+ icon: /* @__PURE__ */ jsx(
39
+ ModelIcon,
40
+ {
41
+ model,
42
+ branded: false,
43
+ sx: { fontSize: "0.72rem !important", ml: "4px !important", color: `${fg} !important` }
44
+ }
45
+ ),
46
+ label: model,
47
+ size: "small",
48
+ title: model,
49
+ sx: {
50
+ height: 18,
51
+ fontSize: "0.6rem",
52
+ fontWeight: 700,
53
+ maxWidth: 160,
54
+ backgroundColor: bg,
55
+ color: fg,
56
+ border: "1px solid",
57
+ borderColor: border,
58
+ borderRadius: 1,
59
+ flexShrink: 0,
60
+ "& .MuiChip-label": {
61
+ px: "5px",
62
+ overflow: "hidden",
63
+ textOverflow: "ellipsis",
64
+ whiteSpace: "nowrap"
65
+ },
66
+ "& .MuiChip-icon": { ml: 0, mr: 0, flexShrink: 0 }
67
+ }
68
+ }
69
+ );
70
+ }
71
+
72
+ export { ModelBadge };
73
+ //# sourceMappingURL=ModelBadge.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ModelBadge.esm.js","sources":["../../../src/components/ModelIcon/ModelBadge.tsx"],"sourcesContent":["import { alpha, useTheme } from '@mui/material/styles';\nimport Chip from '@mui/material/Chip';\nimport { ModelIcon } from './ModelIcon';\nimport { detectFamily } from './ModelIcon';\n\n// simple-icons hex values are often too dark (Anthropic=191919, Ollama=000000).\n// These are curated badge colors optimized for readability as a filled chip background.\nconst BADGE_COLORS: Record<string, string> = {\n Anthropic: '#CC6B3D', // Anthropic terra-cotta orange\n OpenAI: '#10A37F', // ChatGPT green (more recognizable than their dark purple)\n Google: '#3D5AFE', // Gemini indigo\n Meta: '#0467DF', // Meta blue\n Ollama: '#4F7FBF', // Neutral blue (Ollama has no distinctive brand color)\n};\n\ninterface ModelBadgeProps {\n model: string;\n}\n\nexport function ModelBadge({ model }: ModelBadgeProps) {\n const theme = useTheme();\n const isDark = theme.palette.mode === 'dark';\n const family = detectFamily(model);\n const hex = family ? (BADGE_COLORS[family.label] ?? `#${family.hex}`) : undefined;\n\n // Light mode: solid brand fill + white text. Dark mode: translucent fill + brand text.\n let bg: string;\n let fg: string;\n let border: string;\n if (hex) {\n bg = isDark ? alpha(hex, 0.22) : hex;\n fg = isDark ? hex : '#fff';\n border = isDark ? alpha(hex, 0.40) : hex;\n } else {\n bg = isDark ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.06)';\n fg = isDark ? 'rgba(255,255,255,0.55)' : 'rgba(0,0,0,0.45)';\n border = 'transparent';\n }\n\n return (\n <Chip\n icon={\n <ModelIcon\n model={model}\n branded={false}\n sx={{ fontSize: '0.72rem !important', ml: '4px !important', color: `${fg} !important` }}\n />\n }\n label={model}\n size=\"small\"\n title={model}\n sx={{\n height: 18,\n fontSize: '0.6rem',\n fontWeight: 700,\n maxWidth: 160,\n backgroundColor: bg,\n color: fg,\n border: '1px solid',\n borderColor: border,\n borderRadius: 1,\n flexShrink: 0,\n '& .MuiChip-label': {\n px: '5px',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n },\n '& .MuiChip-icon': { ml: 0, mr: 0, flexShrink: 0 },\n }}\n />\n );\n}"],"names":[],"mappings":";;;;;AAOA,MAAM,YAAA,GAAuC;AAAA,EAC3C,SAAA,EAAW,SAAA;AAAA;AAAA,EACX,MAAA,EAAW,SAAA;AAAA;AAAA,EACX,MAAA,EAAW,SAAA;AAAA;AAAA,EACX,IAAA,EAAW,SAAA;AAAA;AAAA,EACX,MAAA,EAAW;AAAA;AACb,CAAA;AAMO,SAAS,UAAA,CAAW,EAAE,KAAA,EAAM,EAAoB;AACrD,EAAA,MAAM,QAAQ,QAAA,EAAS;AACvB,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,OAAA,CAAQ,IAAA,KAAS,MAAA;AACtC,EAAA,MAAM,MAAA,GAAS,aAAa,KAAK,CAAA;AACjC,EAAA,MAAM,GAAA,GAAM,SAAU,YAAA,CAAa,MAAA,CAAO,KAAK,CAAA,IAAK,CAAA,CAAA,EAAI,MAAA,CAAO,GAAG,CAAA,CAAA,GAAM,MAAA;AAGxE,EAAA,IAAI,EAAA;AACJ,EAAA,IAAI,EAAA;AACJ,EAAA,IAAI,MAAA;AACJ,EAAA,IAAI,GAAA,EAAK;AACP,IAAA,EAAA,GAAS,MAAA,GAAS,KAAA,CAAM,GAAA,EAAK,IAAI,CAAA,GAAI,GAAA;AACrC,IAAA,EAAA,GAAS,SAAS,GAAA,GAAM,MAAA;AACxB,IAAA,MAAA,GAAS,MAAA,GAAS,KAAA,CAAM,GAAA,EAAK,GAAI,CAAA,GAAI,GAAA;AAAA,EACvC,CAAA,MAAO;AACL,IAAA,EAAA,GAAS,SAAS,wBAAA,GAA2B,kBAAA;AAC7C,IAAA,EAAA,GAAS,SAAS,wBAAA,GAA2B,kBAAA;AAC7C,IAAA,MAAA,GAAS,aAAA;AAAA,EACX;AAEA,EAAA,uBACE,GAAA;AAAA,IAAC,IAAA;AAAA,IAAA;AAAA,MACC,IAAA,kBACE,GAAA;AAAA,QAAC,SAAA;AAAA,QAAA;AAAA,UACC,KAAA;AAAA,UACA,OAAA,EAAS,KAAA;AAAA,UACT,EAAA,EAAI,EAAE,QAAA,EAAU,oBAAA,EAAsB,IAAI,gBAAA,EAAkB,KAAA,EAAO,CAAA,EAAG,EAAE,CAAA,WAAA,CAAA;AAAc;AAAA,OACxF;AAAA,MAEF,KAAA,EAAO,KAAA;AAAA,MACP,IAAA,EAAK,OAAA;AAAA,MACL,KAAA,EAAO,KAAA;AAAA,MACP,EAAA,EAAI;AAAA,QACF,MAAA,EAAQ,EAAA;AAAA,QACR,QAAA,EAAU,QAAA;AAAA,QACV,UAAA,EAAY,GAAA;AAAA,QACZ,QAAA,EAAU,GAAA;AAAA,QACV,eAAA,EAAiB,EAAA;AAAA,QACjB,KAAA,EAAO,EAAA;AAAA,QACP,MAAA,EAAQ,WAAA;AAAA,QACR,WAAA,EAAa,MAAA;AAAA,QACb,YAAA,EAAc,CAAA;AAAA,QACd,UAAA,EAAY,CAAA;AAAA,QACZ,kBAAA,EAAoB;AAAA,UAClB,EAAA,EAAI,KAAA;AAAA,UACJ,QAAA,EAAU,QAAA;AAAA,UACV,YAAA,EAAc,UAAA;AAAA,UACd,UAAA,EAAY;AAAA,SACd;AAAA,QACA,mBAAmB,EAAE,EAAA,EAAI,GAAG,EAAA,EAAI,CAAA,EAAG,YAAY,CAAA;AAAE;AACnD;AAAA,GACF;AAEJ;;;;"}
@@ -0,0 +1,45 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import SvgIcon from '@mui/material/SvgIcon';
3
+ import SmartToyIcon from '@mui/icons-material/SmartToy';
4
+ import { siAnthropic, siOpenai, siGooglegemini, siMeta, siOllama } from 'simple-icons';
5
+
6
+ function detectFamily(model) {
7
+ const m = model.toLowerCase();
8
+ if (m.startsWith("claude"))
9
+ return { path: siAnthropic.path, hex: siAnthropic.hex, label: "Anthropic" };
10
+ if (m.startsWith("gpt") || m.startsWith("o1") || m.startsWith("o3") || m.startsWith("chatgpt"))
11
+ return { path: siOpenai.path, hex: siOpenai.hex, label: "OpenAI" };
12
+ if (m.startsWith("gemini"))
13
+ return { path: siGooglegemini.path, hex: siGooglegemini.hex, label: "Google" };
14
+ if (m.startsWith("llama") || m.startsWith("meta-"))
15
+ return { path: siMeta.path, hex: siMeta.hex, label: "Meta" };
16
+ if (m.startsWith("ollama"))
17
+ return { path: siOllama.path, hex: siOllama.hex, label: "Ollama" };
18
+ return null;
19
+ }
20
+ function ModelIcon({ model, branded = true, sx, ...props }) {
21
+ const family = detectFamily(model);
22
+ if (!family) {
23
+ return /* @__PURE__ */ jsx(
24
+ SmartToyIcon,
25
+ {
26
+ ...props,
27
+ sx: { color: "text.secondary", ...sx },
28
+ titleAccess: model
29
+ }
30
+ );
31
+ }
32
+ return /* @__PURE__ */ jsx(
33
+ SvgIcon,
34
+ {
35
+ ...props,
36
+ sx: { color: branded ? `#${family.hex}` : "inherit", ...sx },
37
+ titleAccess: `${family.label} \u2014 ${model}`,
38
+ viewBox: "0 0 24 24",
39
+ children: /* @__PURE__ */ jsx("path", { d: family.path })
40
+ }
41
+ );
42
+ }
43
+
44
+ export { ModelIcon, detectFamily };
45
+ //# sourceMappingURL=ModelIcon.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ModelIcon.esm.js","sources":["../../../src/components/ModelIcon/ModelIcon.tsx"],"sourcesContent":["import SvgIcon, { type SvgIconProps } from '@mui/material/SvgIcon';\nimport SmartToyIcon from '@mui/icons-material/SmartToy';\nimport { siAnthropic, siGooglegemini, siMeta, siOpenai, siOllama } from 'simple-icons';\n\ninterface ModelFamily {\n path: string;\n hex: string;\n label: string;\n}\n\nexport function detectFamily(model: string): ModelFamily | null {\n const m = model.toLowerCase();\n if (m.startsWith('claude'))\n return { path: siAnthropic.path, hex: siAnthropic.hex, label: 'Anthropic' };\n if (m.startsWith('gpt') || m.startsWith('o1') || m.startsWith('o3') || m.startsWith('chatgpt'))\n return { path: siOpenai.path, hex: siOpenai.hex, label: 'OpenAI' };\n if (m.startsWith('gemini'))\n return { path: siGooglegemini.path, hex: siGooglegemini.hex, label: 'Google' };\n if (m.startsWith('llama') || m.startsWith('meta-'))\n return { path: siMeta.path, hex: siMeta.hex, label: 'Meta' };\n if (m.startsWith('ollama'))\n return { path: siOllama.path, hex: siOllama.hex, label: 'Ollama' };\n return null;\n}\n\ninterface ModelIconProps extends Omit<SvgIconProps, 'color'> {\n model: string;\n /** Use the brand's official color. Defaults to true. */\n branded?: boolean;\n}\n\nexport function ModelIcon({ model, branded = true, sx, ...props }: ModelIconProps) {\n const family = detectFamily(model);\n\n if (!family) {\n return (\n <SmartToyIcon\n {...props}\n sx={{ color: 'text.secondary', ...sx }}\n titleAccess={model}\n />\n );\n }\n\n return (\n <SvgIcon\n {...props}\n sx={{ color: branded ? `#${family.hex}` : 'inherit', ...sx }}\n titleAccess={`${family.label} — ${model}`}\n viewBox=\"0 0 24 24\"\n >\n <path d={family.path} />\n </SvgIcon>\n );\n}"],"names":[],"mappings":";;;;;AAUO,SAAS,aAAa,KAAA,EAAmC;AAC9D,EAAA,MAAM,CAAA,GAAI,MAAM,WAAA,EAAY;AAC5B,EAAA,IAAI,CAAA,CAAE,WAAW,QAAQ,CAAA;AACvB,IAAA,OAAO,EAAE,MAAM,WAAA,CAAY,IAAA,EAAM,KAAK,WAAA,CAAY,GAAA,EAAK,OAAO,WAAA,EAAY;AAC5E,EAAA,IAAI,CAAA,CAAE,UAAA,CAAW,KAAK,CAAA,IAAK,EAAE,UAAA,CAAW,IAAI,CAAA,IAAK,CAAA,CAAE,UAAA,CAAW,IAAI,CAAA,IAAK,CAAA,CAAE,WAAW,SAAS,CAAA;AAC3F,IAAA,OAAO,EAAE,MAAM,QAAA,CAAS,IAAA,EAAM,KAAK,QAAA,CAAS,GAAA,EAAK,OAAO,QAAA,EAAS;AACnE,EAAA,IAAI,CAAA,CAAE,WAAW,QAAQ,CAAA;AACvB,IAAA,OAAO,EAAE,MAAM,cAAA,CAAe,IAAA,EAAM,KAAK,cAAA,CAAe,GAAA,EAAK,OAAO,QAAA,EAAS;AAC/E,EAAA,IAAI,EAAE,UAAA,CAAW,OAAO,CAAA,IAAK,CAAA,CAAE,WAAW,OAAO,CAAA;AAC/C,IAAA,OAAO,EAAE,MAAM,MAAA,CAAO,IAAA,EAAM,KAAK,MAAA,CAAO,GAAA,EAAK,OAAO,MAAA,EAAO;AAC7D,EAAA,IAAI,CAAA,CAAE,WAAW,QAAQ,CAAA;AACvB,IAAA,OAAO,EAAE,MAAM,QAAA,CAAS,IAAA,EAAM,KAAK,QAAA,CAAS,GAAA,EAAK,OAAO,QAAA,EAAS;AACnE,EAAA,OAAO,IAAA;AACT;AAQO,SAAS,SAAA,CAAU,EAAE,KAAA,EAAO,OAAA,GAAU,MAAM,EAAA,EAAI,GAAG,OAAM,EAAmB;AACjF,EAAA,MAAM,MAAA,GAAS,aAAa,KAAK,CAAA;AAEjC,EAAA,IAAI,CAAC,MAAA,EAAQ;AACX,IAAA,uBACE,GAAA;AAAA,MAAC,YAAA;AAAA,MAAA;AAAA,QACE,GAAG,KAAA;AAAA,QACJ,EAAA,EAAI,EAAE,KAAA,EAAO,gBAAA,EAAkB,GAAG,EAAA,EAAG;AAAA,QACrC,WAAA,EAAa;AAAA;AAAA,KACf;AAAA,EAEJ;AAEA,EAAA,uBACE,GAAA;AAAA,IAAC,OAAA;AAAA,IAAA;AAAA,MACE,GAAG,KAAA;AAAA,MACJ,EAAA,EAAI,EAAE,KAAA,EAAO,OAAA,GAAU,CAAA,CAAA,EAAI,OAAO,GAAG,CAAA,CAAA,GAAK,SAAA,EAAW,GAAG,EAAA,EAAG;AAAA,MAC3D,WAAA,EAAa,CAAA,EAAG,MAAA,CAAO,KAAK,WAAM,KAAK,CAAA,CAAA;AAAA,MACvC,OAAA,EAAQ,WAAA;AAAA,MAER,QAAA,kBAAA,GAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EAAG,MAAA,CAAO,IAAA,EAAM;AAAA;AAAA,GACxB;AAEJ;;;;"}
@@ -3,6 +3,8 @@ import SvgIcon from '@mui/material/SvgIcon';
3
3
  import AllInclusiveIcon from '@mui/icons-material/AllInclusive';
4
4
  import NearMeIcon from '@mui/icons-material/NearMe';
5
5
  import { siGooglegemini, siGithub, siAnthropic } from 'simple-icons';
6
+ import { useTranslationRef } from '@backstage/frontend-plugin-api';
7
+ import { devAiHubTranslationRef } from '../../translation.esm.js';
6
8
 
7
9
  const TOOL_ICON = {
8
10
  "claude-code": { ...siAnthropic, label: "Claude Code" },
@@ -10,8 +12,9 @@ const TOOL_ICON = {
10
12
  "google-gemini": { ...siGooglegemini, label: "Google Gemini" }
11
13
  };
12
14
  function ToolIcon({ tool, branded = true, sx, ...props }) {
15
+ const { t } = useTranslationRef(devAiHubTranslationRef);
13
16
  if (tool === "all") {
14
- return /* @__PURE__ */ jsx(AllInclusiveIcon, { ...props, sx: { color: "text.secondary", ...sx }, titleAccess: "Universal" });
17
+ return /* @__PURE__ */ jsx(AllInclusiveIcon, { ...props, sx: { color: "text.secondary", ...sx }, titleAccess: t("toolIcon.universal") });
15
18
  }
16
19
  if (tool === "cursor") {
17
20
  return /* @__PURE__ */ jsx(
@@ -1 +1 @@
1
- {"version":3,"file":"ToolIcon.esm.js","sources":["../../../src/components/ToolIcon/ToolIcon.tsx"],"sourcesContent":["import SvgIcon, { SvgIconProps } from '@mui/material/SvgIcon';\nimport AllInclusiveIcon from '@mui/icons-material/AllInclusive';\nimport NearMeIcon from '@mui/icons-material/NearMe';\nimport { siAnthropic, siGithub, siGooglegemini } from 'simple-icons';\nimport type { AiTool } from '@julianpedro/plugin-dev-ai-hub-common';\n\ntype SvgTool = Exclude<AiTool, 'all' | 'cursor'>;\n\nconst TOOL_ICON: Record<SvgTool, { path: string; hex: string; label: string }> = {\n 'claude-code': { ...siAnthropic, label: 'Claude Code' },\n 'github-copilot': { ...siGithub, label: 'GitHub Copilot' },\n 'google-gemini': { ...siGooglegemini, label: 'Google Gemini' },\n};\n\ninterface ToolIconProps extends Omit<SvgIconProps, 'color'> {\n tool: AiTool;\n /** Use the brand's official color. Defaults to true. */\n branded?: boolean;\n}\n\nexport function ToolIcon({ tool, branded = true, sx, ...props }: ToolIconProps) {\n if (tool === 'all') {\n return <AllInclusiveIcon {...props} sx={{ color: 'text.secondary', ...sx }} titleAccess=\"Universal\" />;\n }\n\n if (tool === 'cursor') {\n return (\n <NearMeIcon\n {...props}\n sx={{ color: branded ? 'text.primary' : 'inherit', ...sx }}\n titleAccess=\"Cursor\"\n />\n );\n }\n\n const cfg = TOOL_ICON[tool as SvgTool];\n if (!cfg) return null;\n\n return (\n <SvgIcon\n {...props}\n sx={{ color: branded ? `#${cfg.hex}` : 'inherit', ...sx }}\n titleAccess={cfg.label}\n viewBox=\"0 0 24 24\"\n >\n <path d={cfg.path} />\n </SvgIcon>\n );\n}\n"],"names":[],"mappings":";;;;;;AAQA,MAAM,SAAA,GAA2E;AAAA,EAC/E,aAAA,EAAkB,EAAE,GAAG,WAAA,EAAgB,OAAO,aAAA,EAAc;AAAA,EAC5D,gBAAA,EAAkB,EAAE,GAAG,QAAA,EAAgB,OAAO,gBAAA,EAAiB;AAAA,EAC/D,eAAA,EAAkB,EAAE,GAAG,cAAA,EAAgB,OAAO,eAAA;AAChD,CAAA;AAQO,SAAS,QAAA,CAAS,EAAE,IAAA,EAAM,OAAA,GAAU,MAAM,EAAA,EAAI,GAAG,OAAM,EAAkB;AAC9E,EAAA,IAAI,SAAS,KAAA,EAAO;AAClB,IAAA,uBAAO,GAAA,CAAC,gBAAA,EAAA,EAAkB,GAAG,KAAA,EAAO,EAAA,EAAI,EAAE,KAAA,EAAO,gBAAA,EAAkB,GAAG,EAAA,EAAG,EAAG,WAAA,EAAY,WAAA,EAAY,CAAA;AAAA,EACtG;AAEA,EAAA,IAAI,SAAS,QAAA,EAAU;AACrB,IAAA,uBACE,GAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACE,GAAG,KAAA;AAAA,QACJ,IAAI,EAAE,KAAA,EAAO,UAAU,cAAA,GAAiB,SAAA,EAAW,GAAG,EAAA,EAAG;AAAA,QACzD,WAAA,EAAY;AAAA;AAAA,KACd;AAAA,EAEJ;AAEA,EAAA,MAAM,GAAA,GAAM,UAAU,IAAe,CAAA;AACrC,EAAA,IAAI,CAAC,KAAK,OAAO,IAAA;AAEjB,EAAA,uBACE,GAAA;AAAA,IAAC,OAAA;AAAA,IAAA;AAAA,MACE,GAAG,KAAA;AAAA,MACJ,EAAA,EAAI,EAAE,KAAA,EAAO,OAAA,GAAU,CAAA,CAAA,EAAI,IAAI,GAAG,CAAA,CAAA,GAAK,SAAA,EAAW,GAAG,EAAA,EAAG;AAAA,MACxD,aAAa,GAAA,CAAI,KAAA;AAAA,MACjB,OAAA,EAAQ,WAAA;AAAA,MAER,QAAA,kBAAA,GAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EAAG,GAAA,CAAI,IAAA,EAAM;AAAA;AAAA,GACrB;AAEJ;;;;"}
1
+ {"version":3,"file":"ToolIcon.esm.js","sources":["../../../src/components/ToolIcon/ToolIcon.tsx"],"sourcesContent":["import SvgIcon, { SvgIconProps } from '@mui/material/SvgIcon';\nimport AllInclusiveIcon from '@mui/icons-material/AllInclusive';\nimport NearMeIcon from '@mui/icons-material/NearMe';\nimport { siAnthropic, siGithub, siGooglegemini } from 'simple-icons';\nimport { useTranslationRef } from '@backstage/frontend-plugin-api';\nimport type { AiTool } from '@julianpedro/plugin-dev-ai-hub-common';\nimport { devAiHubTranslationRef } from '../../translation';\n\ntype SvgTool = Exclude<AiTool, 'all' | 'cursor'>;\n\nconst TOOL_ICON: Record<SvgTool, { path: string; hex: string; label: string }> = {\n 'claude-code': { ...siAnthropic, label: 'Claude Code' },\n 'github-copilot': { ...siGithub, label: 'GitHub Copilot' },\n 'google-gemini': { ...siGooglegemini, label: 'Google Gemini' },\n};\n\ninterface ToolIconProps extends Omit<SvgIconProps, 'color'> {\n tool: AiTool;\n /** Use the brand's official color. Defaults to true. */\n branded?: boolean;\n}\n\nexport function ToolIcon({ tool, branded = true, sx, ...props }: ToolIconProps) {\n const { t } = useTranslationRef(devAiHubTranslationRef);\n\n if (tool === 'all') {\n return <AllInclusiveIcon {...props} sx={{ color: 'text.secondary', ...sx }} titleAccess={t('toolIcon.universal')} />;\n }\n\n if (tool === 'cursor') {\n return (\n <NearMeIcon\n {...props}\n sx={{ color: branded ? 'text.primary' : 'inherit', ...sx }}\n titleAccess=\"Cursor\"\n />\n );\n }\n\n const cfg = TOOL_ICON[tool as SvgTool];\n if (!cfg) return null;\n\n return (\n <SvgIcon\n {...props}\n sx={{ color: branded ? `#${cfg.hex}` : 'inherit', ...sx }}\n titleAccess={cfg.label}\n viewBox=\"0 0 24 24\"\n >\n <path d={cfg.path} />\n </SvgIcon>\n );\n}\n"],"names":[],"mappings":";;;;;;;;AAUA,MAAM,SAAA,GAA2E;AAAA,EAC/E,aAAA,EAAkB,EAAE,GAAG,WAAA,EAAgB,OAAO,aAAA,EAAc;AAAA,EAC5D,gBAAA,EAAkB,EAAE,GAAG,QAAA,EAAgB,OAAO,gBAAA,EAAiB;AAAA,EAC/D,eAAA,EAAkB,EAAE,GAAG,cAAA,EAAgB,OAAO,eAAA;AAChD,CAAA;AAQO,SAAS,QAAA,CAAS,EAAE,IAAA,EAAM,OAAA,GAAU,MAAM,EAAA,EAAI,GAAG,OAAM,EAAkB;AAC9E,EAAA,MAAM,EAAE,CAAA,EAAE,GAAI,iBAAA,CAAkB,sBAAsB,CAAA;AAEtD,EAAA,IAAI,SAAS,KAAA,EAAO;AAClB,IAAA,uBAAO,GAAA,CAAC,gBAAA,EAAA,EAAkB,GAAG,KAAA,EAAO,IAAI,EAAE,KAAA,EAAO,gBAAA,EAAkB,GAAG,EAAA,EAAG,EAAG,WAAA,EAAa,CAAA,CAAE,oBAAoB,CAAA,EAAG,CAAA;AAAA,EACpH;AAEA,EAAA,IAAI,SAAS,QAAA,EAAU;AACrB,IAAA,uBACE,GAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACE,GAAG,KAAA;AAAA,QACJ,IAAI,EAAE,KAAA,EAAO,UAAU,cAAA,GAAiB,SAAA,EAAW,GAAG,EAAA,EAAG;AAAA,QACzD,WAAA,EAAY;AAAA;AAAA,KACd;AAAA,EAEJ;AAEA,EAAA,MAAM,GAAA,GAAM,UAAU,IAAe,CAAA;AACrC,EAAA,IAAI,CAAC,KAAK,OAAO,IAAA;AAEjB,EAAA,uBACE,GAAA;AAAA,IAAC,OAAA;AAAA,IAAA;AAAA,MACE,GAAG,KAAA;AAAA,MACJ,EAAA,EAAI,EAAE,KAAA,EAAO,OAAA,GAAU,CAAA,CAAA,EAAI,IAAI,GAAG,CAAA,CAAA,GAAK,SAAA,EAAW,GAAG,EAAA,EAAG;AAAA,MACxD,aAAa,GAAA,CAAI,KAAA;AAAA,MACjB,OAAA,EAAQ,WAAA;AAAA,MAER,QAAA,kBAAA,GAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EAAG,GAAA,CAAI,IAAA,EAAM;AAAA;AAAA,GACrB;AAEJ;;;;"}
@@ -0,0 +1,79 @@
1
+ import { useState, useEffect } from 'react';
2
+ import { useApi } from '@backstage/core-plugin-api';
3
+ import { devAiHubApiRef } from '../api/DevAiHubClient.esm.js';
4
+
5
+ const CACHE_KEY = "dev-ai-hub:ui-config-v1";
6
+ const ALL_TYPES = ["instruction", "agent", "skill", "workflow", "prompt", "bundle"];
7
+ const DEFAULT_TYPE_COLORS = {
8
+ instruction: "#2563EB",
9
+ agent: "#7C3AED",
10
+ skill: "#059669",
11
+ workflow: "#D97706",
12
+ prompt: "#EC4899",
13
+ bundle: "#8B5CF6"
14
+ };
15
+ const DEFAULT_STATS_CARDS = ["instruction", "agent", "skill", "workflow"];
16
+ function buildTypeColors(raw) {
17
+ return { ...DEFAULT_TYPE_COLORS, ...raw };
18
+ }
19
+ function buildStatsCards(raw) {
20
+ const configured = raw.filter((t) => ALL_TYPES.includes(t)).slice(0, 4);
21
+ if (!configured.length) return DEFAULT_STATS_CARDS;
22
+ const fill = DEFAULT_STATS_CARDS.filter((t) => !configured.includes(t));
23
+ return [...configured, ...fill].slice(0, 4);
24
+ }
25
+ const subscribers = /* @__PURE__ */ new Set();
26
+ let currentConfig = null;
27
+ let fetchInitiated = false;
28
+ function readLocalStorage() {
29
+ try {
30
+ const raw = localStorage.getItem(CACHE_KEY);
31
+ if (!raw) return null;
32
+ const cfg = JSON.parse(raw);
33
+ return {
34
+ typeColors: buildTypeColors(cfg.typeColors ?? {}),
35
+ statsCards: buildStatsCards(cfg.statsCards ?? [])
36
+ };
37
+ } catch {
38
+ return null;
39
+ }
40
+ }
41
+ function getInitialConfig() {
42
+ if (currentConfig) return currentConfig;
43
+ const cached = readLocalStorage();
44
+ currentConfig = cached ?? { typeColors: DEFAULT_TYPE_COLORS, statsCards: DEFAULT_STATS_CARDS };
45
+ return currentConfig;
46
+ }
47
+ function applyRemoteConfig(raw) {
48
+ try {
49
+ localStorage.setItem(CACHE_KEY, JSON.stringify(raw));
50
+ } catch (_e) {
51
+ }
52
+ currentConfig = {
53
+ typeColors: buildTypeColors(raw.typeColors ?? {}),
54
+ statsCards: buildStatsCards(raw.statsCards ?? [])
55
+ };
56
+ subscribers.forEach((fn) => fn(currentConfig));
57
+ }
58
+ function useTypeConfig() {
59
+ const api = useApi(devAiHubApiRef);
60
+ const [config, setConfig] = useState(getInitialConfig);
61
+ useEffect(() => {
62
+ subscribers.add(setConfig);
63
+ if (!fetchInitiated) {
64
+ fetchInitiated = true;
65
+ api.getUiConfig().then((cfg) => applyRemoteConfig(cfg)).catch(() => {
66
+ fetchInitiated = false;
67
+ });
68
+ } else if (currentConfig) {
69
+ setConfig(currentConfig);
70
+ }
71
+ return () => {
72
+ subscribers.delete(setConfig);
73
+ };
74
+ }, []);
75
+ return config;
76
+ }
77
+
78
+ export { useTypeConfig };
79
+ //# sourceMappingURL=UiConfigContext.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UiConfigContext.esm.js","sources":["../../src/context/UiConfigContext.tsx"],"sourcesContent":["import { useState, useEffect } from 'react';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { devAiHubApiRef } from '../api/DevAiHubClient';\nimport type { AssetType } from '@julianpedro/plugin-dev-ai-hub-common';\n\nconst CACHE_KEY = 'dev-ai-hub:ui-config-v1';\n\nconst ALL_TYPES: AssetType[] = ['instruction', 'agent', 'skill', 'workflow', 'prompt', 'bundle'];\n\nconst DEFAULT_TYPE_COLORS: Record<AssetType, string> = {\n instruction: '#2563EB',\n agent: '#7C3AED',\n skill: '#059669',\n workflow: '#D97706',\n prompt: '#EC4899',\n bundle: '#8B5CF6',\n};\n\nconst DEFAULT_STATS_CARDS: AssetType[] = ['instruction', 'agent', 'skill', 'workflow'];\n\ninterface UiConfigValue {\n typeColors: Record<AssetType, string>;\n statsCards: AssetType[];\n}\n\nfunction buildTypeColors(raw: Record<string, string>): Record<AssetType, string> {\n return { ...DEFAULT_TYPE_COLORS, ...(raw as Record<AssetType, string>) };\n}\n\nfunction buildStatsCards(raw: string[]): AssetType[] {\n const configured = raw\n .filter(t => ALL_TYPES.includes(t as AssetType))\n .slice(0, 4) as AssetType[];\n if (!configured.length) return DEFAULT_STATS_CARDS;\n const fill = DEFAULT_STATS_CARDS.filter(t => !configured.includes(t));\n return [...configured, ...fill].slice(0, 4);\n}\n\n// ── Module-level store ─────────────────────────────────────────────────────\n// Shared across all components in the same page session.\n// On first mount: reads localStorage (instant, no flash on revisits).\n// Then fetches from backend once and notifies all subscribers.\n\nconst subscribers = new Set<(v: UiConfigValue) => void>();\nlet currentConfig: UiConfigValue | null = null;\nlet fetchInitiated = false;\n\nfunction readLocalStorage(): UiConfigValue | null {\n try {\n const raw = localStorage.getItem(CACHE_KEY);\n if (!raw) return null;\n const cfg = JSON.parse(raw) as { typeColors: Record<string, string>; statsCards: string[] };\n return {\n typeColors: buildTypeColors(cfg.typeColors ?? {}),\n statsCards: buildStatsCards(cfg.statsCards ?? []),\n };\n } catch { return null; }\n}\n\nfunction getInitialConfig(): UiConfigValue {\n if (currentConfig) return currentConfig;\n const cached = readLocalStorage();\n currentConfig = cached ?? { typeColors: DEFAULT_TYPE_COLORS, statsCards: DEFAULT_STATS_CARDS };\n return currentConfig;\n}\n\nfunction applyRemoteConfig(raw: { typeColors: Record<string, string>; statsCards: string[] }) {\n try { localStorage.setItem(CACHE_KEY, JSON.stringify(raw)); } catch (_e) { /* storage unavailable */ }\n currentConfig = {\n typeColors: buildTypeColors(raw.typeColors ?? {}),\n statsCards: buildStatsCards(raw.statsCards ?? []),\n };\n subscribers.forEach(fn => fn(currentConfig!));\n}\n\n// ── Hook ───────────────────────────────────────────────────────────────────\n\nexport function useTypeConfig(): UiConfigValue {\n const api = useApi(devAiHubApiRef);\n const [config, setConfig] = useState<UiConfigValue>(getInitialConfig);\n\n useEffect(() => {\n subscribers.add(setConfig);\n\n // One fetch per page session regardless of how many components mount\n if (!fetchInitiated) {\n fetchInitiated = true;\n api.getUiConfig()\n .then(cfg => applyRemoteConfig(cfg as { typeColors: Record<string, string>; statsCards: string[] }))\n .catch(() => { fetchInitiated = false; }); // allow retry on transient error\n } else if (currentConfig) {\n // Fetch already done or in progress — sync to latest known value\n setConfig(currentConfig);\n }\n\n return () => { subscribers.delete(setConfig); };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return config;\n}"],"names":[],"mappings":"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"}
@@ -97,6 +97,41 @@ function useStats() {
97
97
  }, [api]);
98
98
  return { stats, loading, error };
99
99
  }
100
+ function useMcpCatalog() {
101
+ const api = useApi(devAiHubApiRef);
102
+ const [catalog, setCatalog] = useState([]);
103
+ const [loading, setLoading] = useState(false);
104
+ const [error, setError] = useState(null);
105
+ useEffect(() => {
106
+ setLoading(true);
107
+ api.getMcpCatalog().then((data) => {
108
+ setCatalog(data);
109
+ setError(null);
110
+ }).catch((err) => setError(err)).finally(() => setLoading(false));
111
+ }, [api]);
112
+ return { catalog, loading, error };
113
+ }
114
+ function useSyncProvider() {
115
+ const api = useApi(devAiHubApiRef);
116
+ const [syncing, setSyncing] = useState({});
117
+ const triggerSync = useCallback(async (id) => {
118
+ setSyncing((s) => ({ ...s, [id]: true }));
119
+ try {
120
+ await api.triggerSync(id);
121
+ } finally {
122
+ setSyncing((s) => ({ ...s, [id]: false }));
123
+ }
124
+ }, [api]);
125
+ const triggerSyncAll = useCallback(async (ids) => {
126
+ setSyncing(Object.fromEntries(ids.map((id) => [id, true])));
127
+ try {
128
+ await Promise.all(ids.map((id) => api.triggerSync(id)));
129
+ } finally {
130
+ setSyncing({});
131
+ }
132
+ }, [api]);
133
+ return { syncing, triggerSync, triggerSyncAll };
134
+ }
100
135
  function useCopyToClipboard() {
101
136
  const [copied, setCopied] = useState(false);
102
137
  const timerRef = useRef(null);
@@ -110,5 +145,5 @@ function useCopyToClipboard() {
110
145
  return { copy, copied };
111
146
  }
112
147
 
113
- export { useAssetDetail, useAssets, useCopyToClipboard, useProviders, useStats };
148
+ export { useAssetDetail, useAssets, useCopyToClipboard, useMcpCatalog, useProviders, useStats, useSyncProvider };
114
149
  //# sourceMappingURL=index.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":["../../src/hooks/index.ts"],"sourcesContent":["import { useState, useEffect, useCallback, useRef } from 'react';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { devAiHubApiRef } from '../api/DevAiHubClient';\nimport type {\n AiAsset,\n AiAssetListResponse,\n AiHubProvider,\n AiHubStats,\n AssetListFilter,\n} from '@julianpedro/plugin-dev-ai-hub-common';\n\nfunction useDebounce<T>(value: T, delay: number): T {\n const [debounced, setDebounced] = useState(value);\n useEffect(() => {\n const timer = setTimeout(() => setDebounced(value), delay);\n return () => clearTimeout(timer);\n }, [value, delay]);\n return debounced;\n}\n\nexport function useAssets(filter: AssetListFilter) {\n const api = useApi(devAiHubApiRef);\n const [result, setResult] = useState<AiAssetListResponse | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const debouncedSearch = useDebounce(filter.search, 300);\n\n const effectiveFilter = { ...filter, search: debouncedSearch };\n const filterKey = JSON.stringify(effectiveFilter);\n\n useEffect(() => {\n let cancelled = false;\n setLoading(true);\n api\n .listAssets(effectiveFilter)\n .then(data => {\n if (!cancelled) {\n setResult(data);\n setError(null);\n }\n })\n .catch(err => {\n if (!cancelled) setError(err);\n })\n .finally(() => {\n if (!cancelled) setLoading(false);\n });\n return () => {\n cancelled = true;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [filterKey, api]);\n\n return { result, loading, error };\n}\n\nexport function useAssetDetail(id: string | null) {\n const api = useApi(devAiHubApiRef);\n const [asset, setAsset] = useState<AiAsset | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n if (!id) {\n setAsset(null);\n return () => {};\n }\n let cancelled = false;\n setLoading(true);\n api\n .getAsset(id)\n .then(data => {\n if (!cancelled) {\n setAsset(data);\n setError(null);\n }\n })\n .catch(err => {\n if (!cancelled) setError(err);\n })\n .finally(() => {\n if (!cancelled) setLoading(false);\n });\n return () => {\n cancelled = true;\n };\n }, [id, api]);\n\n return { asset, loading, error };\n}\n\nexport function useProviders() {\n const api = useApi(devAiHubApiRef);\n const [providers, setProviders] = useState<AiHubProvider[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const load = useCallback(() => {\n setLoading(true);\n api\n .listProviders()\n .then(data => {\n setProviders(data);\n setError(null);\n })\n .catch(err => setError(err))\n .finally(() => setLoading(false));\n }, [api]);\n\n useEffect(() => {\n load();\n }, [load]);\n\n return { providers, loading, error, reload: load };\n}\n\nexport function useStats() {\n const api = useApi(devAiHubApiRef);\n const [stats, setStats] = useState<AiHubStats | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n setLoading(true);\n api\n .getStats()\n .then(data => {\n setStats(data);\n setError(null);\n })\n .catch(err => setError(err))\n .finally(() => setLoading(false));\n }, [api]);\n\n return { stats, loading, error };\n}\n\nexport function useCopyToClipboard() {\n const [copied, setCopied] = useState(false);\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const copy = useCallback((text: string) => {\n navigator.clipboard.writeText(text).then(() => {\n setCopied(true);\n if (timerRef.current) clearTimeout(timerRef.current);\n timerRef.current = setTimeout(() => setCopied(false), 2000);\n });\n }, []);\n\n return { copy, copied };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.esm.js","sources":["../../src/hooks/index.ts"],"sourcesContent":["import { useState, useEffect, useCallback, useRef } from 'react';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { devAiHubApiRef } from '../api/DevAiHubClient';\nimport type {\n AiAsset,\n AiAssetListResponse,\n AiHubProvider,\n AiHubStats,\n AssetListFilter,\n McpCatalogEntry,\n} from '@julianpedro/plugin-dev-ai-hub-common';\n\nfunction useDebounce<T>(value: T, delay: number): T {\n const [debounced, setDebounced] = useState(value);\n useEffect(() => {\n const timer = setTimeout(() => setDebounced(value), delay);\n return () => clearTimeout(timer);\n }, [value, delay]);\n return debounced;\n}\n\nexport function useAssets(filter: AssetListFilter) {\n const api = useApi(devAiHubApiRef);\n const [result, setResult] = useState<AiAssetListResponse | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const debouncedSearch = useDebounce(filter.search, 300);\n\n const effectiveFilter = { ...filter, search: debouncedSearch };\n const filterKey = JSON.stringify(effectiveFilter);\n\n useEffect(() => {\n let cancelled = false;\n setLoading(true);\n api\n .listAssets(effectiveFilter)\n .then(data => {\n if (!cancelled) {\n setResult(data);\n setError(null);\n }\n })\n .catch(err => {\n if (!cancelled) setError(err);\n })\n .finally(() => {\n if (!cancelled) setLoading(false);\n });\n return () => {\n cancelled = true;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [filterKey, api]);\n\n return { result, loading, error };\n}\n\nexport function useAssetDetail(id: string | null) {\n const api = useApi(devAiHubApiRef);\n const [asset, setAsset] = useState<AiAsset | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n if (!id) {\n setAsset(null);\n return () => {};\n }\n let cancelled = false;\n setLoading(true);\n api\n .getAsset(id)\n .then(data => {\n if (!cancelled) {\n setAsset(data);\n setError(null);\n }\n })\n .catch(err => {\n if (!cancelled) setError(err);\n })\n .finally(() => {\n if (!cancelled) setLoading(false);\n });\n return () => {\n cancelled = true;\n };\n }, [id, api]);\n\n return { asset, loading, error };\n}\n\nexport function useProviders() {\n const api = useApi(devAiHubApiRef);\n const [providers, setProviders] = useState<AiHubProvider[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const load = useCallback(() => {\n setLoading(true);\n api\n .listProviders()\n .then(data => {\n setProviders(data);\n setError(null);\n })\n .catch(err => setError(err))\n .finally(() => setLoading(false));\n }, [api]);\n\n useEffect(() => {\n load();\n }, [load]);\n\n return { providers, loading, error, reload: load };\n}\n\nexport function useStats() {\n const api = useApi(devAiHubApiRef);\n const [stats, setStats] = useState<AiHubStats | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n setLoading(true);\n api\n .getStats()\n .then(data => {\n setStats(data);\n setError(null);\n })\n .catch(err => setError(err))\n .finally(() => setLoading(false));\n }, [api]);\n\n return { stats, loading, error };\n}\n\nexport function useMcpCatalog() {\n const api = useApi(devAiHubApiRef);\n const [catalog, setCatalog] = useState<McpCatalogEntry[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n setLoading(true);\n api\n .getMcpCatalog()\n .then(data => {\n setCatalog(data);\n setError(null);\n })\n .catch(err => setError(err))\n .finally(() => setLoading(false));\n }, [api]);\n\n return { catalog, loading, error };\n}\n\nexport function useSyncProvider() {\n const api = useApi(devAiHubApiRef);\n const [syncing, setSyncing] = useState<Record<string, boolean>>({});\n\n const triggerSync = useCallback(async (id: string) => {\n setSyncing(s => ({ ...s, [id]: true }));\n try { await api.triggerSync(id); }\n finally { setSyncing(s => ({ ...s, [id]: false })); }\n }, [api]);\n\n const triggerSyncAll = useCallback(async (ids: string[]) => {\n setSyncing(Object.fromEntries(ids.map(id => [id, true])));\n try { await Promise.all(ids.map(id => api.triggerSync(id))); }\n finally { setSyncing({}); }\n }, [api]);\n\n return { syncing, triggerSync, triggerSyncAll };\n}\n\nexport { useTypeConfig } from '../context/UiConfigContext';\n\nexport function useCopyToClipboard() {\n const [copied, setCopied] = useState(false);\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const copy = useCallback((text: string) => {\n navigator.clipboard.writeText(text).then(() => {\n setCopied(true);\n if (timerRef.current) clearTimeout(timerRef.current);\n timerRef.current = setTimeout(() => setCopied(false), 2000);\n });\n }, []);\n\n return { copy, copied };\n}"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import { ComponentType } from 'react';
3
3
  import * as _backstage_frontend_plugin_api from '@backstage/frontend-plugin-api';
4
- import { AssetListFilter, AiAssetListResponse, AiAsset, AiHubProvider, AiHubStats } from '@julianpedro/plugin-dev-ai-hub-common';
4
+ import { AssetListFilter, AiAssetListResponse, AiAsset, AiHubProvider, AiHubStats, McpCatalogEntry } from '@julianpedro/plugin-dev-ai-hub-common';
5
5
 
6
6
  declare const devAiHubPlugin: _backstage_frontend_plugin_api.OverridableFrontendPlugin<{
7
7
  root: _backstage_frontend_plugin_api.RouteRef<undefined>;
@@ -15,23 +15,6 @@ declare const devAiHubPlugin: _backstage_frontend_plugin_api.OverridableFrontend
15
15
  inputs: {};
16
16
  params: <TApi, TImpl extends TApi, TDeps extends { [name in string]: unknown; }>(params: _backstage_frontend_plugin_api.ApiFactory<TApi, TImpl, TDeps>) => _backstage_frontend_plugin_api.ExtensionBlueprintParams<_backstage_frontend_plugin_api.AnyApiFactory>;
17
17
  }>;
18
- "nav-item:dev-ai-hub": _backstage_frontend_plugin_api.OverridableExtensionDefinition<{
19
- kind: "nav-item";
20
- name: undefined;
21
- config: {};
22
- configInput: {};
23
- output: _backstage_frontend_plugin_api.ExtensionDataRef<{
24
- title: string;
25
- icon: _backstage_frontend_plugin_api.IconComponent;
26
- routeRef: _backstage_frontend_plugin_api.RouteRef<undefined>;
27
- }, "core.nav-item.target", {}>;
28
- inputs: {};
29
- params: {
30
- title: string;
31
- icon: _backstage_frontend_plugin_api.IconComponent;
32
- routeRef: _backstage_frontend_plugin_api.RouteRef<undefined>;
33
- };
34
- }>;
35
18
  "page:dev-ai-hub": _backstage_frontend_plugin_api.OverridableExtensionDefinition<{
36
19
  kind: "page";
37
20
  name: undefined;
@@ -40,8 +23,8 @@ declare const devAiHubPlugin: _backstage_frontend_plugin_api.OverridableFrontend
40
23
  title: string | undefined;
41
24
  };
42
25
  configInput: {
43
- title?: string | undefined;
44
26
  path?: string | undefined;
27
+ title?: string | undefined;
45
28
  };
46
29
  output: _backstage_frontend_plugin_api.ExtensionDataRef<string, "core.routing.path", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<_backstage_frontend_plugin_api.RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams>, "core.routing.ref", {
47
30
  optional: true;
@@ -64,7 +47,6 @@ declare const devAiHubPlugin: _backstage_frontend_plugin_api.OverridableFrontend
64
47
  }>;
65
48
  };
66
49
  params: {
67
- defaultPath?: [Error: `Use the 'path' param instead`];
68
50
  path: string;
69
51
  title?: string;
70
52
  icon?: _backstage_frontend_plugin_api.IconElement;
@@ -73,6 +55,19 @@ declare const devAiHubPlugin: _backstage_frontend_plugin_api.OverridableFrontend
73
55
  noHeader?: boolean;
74
56
  };
75
57
  }>;
58
+ "translation:dev-ai-hub": _backstage_frontend_plugin_api.OverridableExtensionDefinition<{
59
+ kind: "translation";
60
+ name: undefined;
61
+ config: {};
62
+ configInput: {};
63
+ output: _backstage_frontend_plugin_api.ExtensionDataRef<_backstage_frontend_plugin_api.TranslationResource<string> | _backstage_frontend_plugin_api.TranslationMessages<string, {
64
+ [x: string]: string;
65
+ }, boolean>, "core.translation.translation", {}>;
66
+ inputs: {};
67
+ params: {
68
+ resource: _backstage_frontend_plugin_api.TranslationResource | _backstage_frontend_plugin_api.TranslationMessages;
69
+ };
70
+ }>;
76
71
  }>;
77
72
 
78
73
  /**
@@ -85,20 +80,148 @@ declare const devAiHubPlugin: _backstage_frontend_plugin_api.OverridableFrontend
85
80
  declare const DevAiHubPage: ComponentType;
86
81
 
87
82
  declare const devAiHubApiRef: _backstage_frontend_plugin_api.ApiRef<DevAiHubApi>;
83
+ interface UiConfig {
84
+ typeColors: Partial<Record<string, string>>;
85
+ statsCards: string[];
86
+ }
88
87
  interface DevAiHubApi {
89
88
  listAssets(filter?: AssetListFilter): Promise<AiAssetListResponse>;
90
89
  getAsset(id: string): Promise<AiAsset>;
91
90
  getAssetRaw(id: string): Promise<string>;
92
- /** Returns the absolute URL for the download endpoint (zip for skills, md for others). */
93
- getDownloadUrl(id: string): Promise<string>;
91
+ /** Returns the absolute URL for the download endpoint (zip for skills/bundles, md for others). */
92
+ getDownloadUrl(id: string, tool?: string): Promise<string>;
93
+ /** Returns the absolute URL for the .agent.md endpoint used in VSCode deep links. */
94
+ getAgentMdUrl(id: string): Promise<string>;
94
95
  trackInstall(id: string): Promise<void>;
95
96
  listProviders(): Promise<AiHubProvider[]>;
96
97
  getProviderStatus(id: string): Promise<AiHubProvider>;
97
98
  triggerSync(id: string): Promise<void>;
98
99
  getStats(): Promise<AiHubStats>;
100
+ getMcpCatalog(): Promise<McpCatalogEntry[]>;
101
+ getUiConfig(): Promise<UiConfig>;
99
102
  }
100
103
 
101
104
  declare const rootRouteRef: _backstage_frontend_plugin_api.RouteRef<undefined>;
102
105
 
103
- export { DevAiHubPage, devAiHubApiRef, devAiHubPlugin, rootRouteRef };
106
+ declare const devAiHubTranslationRef: _backstage_frontend_plugin_api.TranslationRef<"dev-ai-hub", {
107
+ readonly "toolIcon.universal": "Universal";
108
+ readonly "assetCard.newBadge": "New";
109
+ readonly "assetCard.updatedBadge": "Updated";
110
+ readonly "assetCard.installTooltip": "Install in editor";
111
+ readonly "assetCard.detailsTooltip": "View details";
112
+ readonly "assetCard.helpTooltip": "Usage guide";
113
+ readonly "assetCard.mcpsRequired": "Required MCPs";
114
+ readonly "assetCard.bundleFooter": "{{itemCount}} assets · {{author}}";
115
+ readonly "assetCard.versionFooter": "v{{version}} · {{author}}";
116
+ readonly "assetCard.moreTags": "+{{count}}";
117
+ readonly "assetDetailPanel.loading": "Loading...";
118
+ readonly "assetDetailPanel.tabPreview": "Preview";
119
+ readonly "assetDetailPanel.tabMetadata": "Metadata";
120
+ readonly "assetDetailPanel.tabRawYaml": "Raw YAML";
121
+ readonly "assetDetailPanel.bundlePreviewTitle": "Bundle contents — {{total}} assets";
122
+ readonly "assetDetailPanel.bundleItemNotSynced": "Not synced — {{ref}}";
123
+ readonly "assetDetailPanel.metaAuthor": "Author";
124
+ readonly "assetDetailPanel.metaVersion": "Version";
125
+ readonly "assetDetailPanel.metaProvider": "Provider";
126
+ readonly "assetDetailPanel.metaCommit": "Commit";
127
+ readonly "assetDetailPanel.metaLastSynced": "Last synced";
128
+ readonly "assetDetailPanel.metaBranch": "Branch";
129
+ readonly "assetDetailPanel.compatibleTools": "Compatible tools";
130
+ readonly "assetDetailPanel.tagsLabel": "Tags";
131
+ readonly "assetDetailPanel.bundleContents": "Bundle contents ({{total}} assets)";
132
+ readonly "assetDetailPanel.bundleItemNotSyncedYet": "Not synced yet — {{ref}}";
133
+ readonly "assetDetailPanel.bundledFiles": "Bundled files";
134
+ readonly "assetDetailPanel.zipDescription": "Downloads as .zip containing all files above.";
135
+ readonly "assetDetailPanel.repository": "Repository";
136
+ readonly "assetDetailPanel.copyMarkdown": "Copy Markdown";
137
+ readonly "assetDetailPanel.openInRepo": "Open in Repo";
138
+ readonly "assetDetailPanel.copiedMessage": "Markdown copied to clipboard!";
139
+ readonly "assetFilters.searchPlaceholder": "Search assets by name, description or content…";
140
+ readonly "assetFilters.typeHeader": "Type";
141
+ readonly "assetFilters.typeAll": "All";
142
+ readonly "assetFilters.aiToolHeader": "AI Tool";
143
+ readonly "assetFilters.allTools": "All Tools";
144
+ readonly "assetFilters.providerHeader": "Provider";
145
+ readonly "assetFilters.tagsHeader": "Tags";
146
+ readonly "assetFilters.tagsShowMore": "+{{remaining}} more";
147
+ readonly "assetFilters.tagsShowLess": "Show less";
148
+ readonly "assetInstallDialog.bundledSkillTitle": "Bundled skill — downloads as .zip";
149
+ readonly "assetInstallDialog.bundledSkillDescription": "This skill includes resource files alongside SKILL.md. Extract the zip and place all files in the skill directory.";
150
+ readonly "assetInstallDialog.installPathLabel": "Install path";
151
+ readonly "assetInstallDialog.installInVscode": "Install in VSCode";
152
+ readonly "assetInstallDialog.copied": "Copied!";
153
+ readonly "assetInstallDialog.copyTooltip": "Copy markdown content";
154
+ readonly "assetInstallDialog.copyContent": "Copy Content";
155
+ readonly "assetInstallDialog.downloadZipTooltip": "Download as .zip with all bundled files";
156
+ readonly "assetInstallDialog.downloadTooltip": "Download file with correct name";
157
+ readonly "assetInstallDialog.downloadZip": "Download .zip";
158
+ readonly "assetInstallDialog.download": "Download";
159
+ readonly "assetInstallDialog.stepProgress": "Step {{current}} of {{total}}";
160
+ readonly "assetInstallDialog.notSyncedTitle": "This asset has not been synced yet.";
161
+ readonly "assetInstallDialog.notSyncedRef": "Ref: {{ref}}";
162
+ readonly "assetInstallDialog.dialogTitleBundle": "Install Bundle: {{name}}";
163
+ readonly "assetInstallDialog.dialogSubtitleBundle": "Install each asset in the bundle step by step.";
164
+ readonly "assetInstallDialog.dialogTitle": "Install: {{name}}";
165
+ readonly "assetInstallDialog.dialogSubtitle": "Copy the content and place the file at the path shown for your tool.";
166
+ readonly "assetInstallDialog.bundleProgress": "{{current}} / {{total}} assets";
167
+ readonly "assetInstallDialog.bundleEmpty": "This bundle has no items yet.";
168
+ readonly "assetInstallDialog.downloadBundle": "Download Bundle .zip";
169
+ readonly "assetInstallDialog.back": "Back";
170
+ readonly "assetInstallDialog.next": "Next";
171
+ readonly "assetInstallDialog.finish": "Finish";
172
+ readonly "assetInstallDialog.close": "Close";
173
+ readonly "mcpConfigDialog.installInVscode": "Install in VSCode";
174
+ readonly "mcpConfigDialog.close": "Close";
175
+ readonly "mcpConfigDialog.title": "MCP Configuration";
176
+ readonly "mcpConfigDialog.subtitle": "Browse the catalog of ready-to-install MCP servers, or configure the Dev AI Hub MCP for your AI tool.";
177
+ readonly "mcpConfigDialog.catalogTab": "MCP Catalog";
178
+ readonly "mcpConfigDialog.catalogDescription": "Pre-configured MCP servers you can install with one click in VSCode or Cursor.";
179
+ readonly "mcpConfigDialog.catalogEmpty": "No MCP servers configured in the catalog.";
180
+ readonly "mcpConfigDialog.catalogAddHint": "Add a mcp-catalog.yaml file to the root of your assets repository to manage the catalog.";
181
+ readonly "mcpConfigDialog.toolConfigSection": "Configure Dev AI Hub MCP";
182
+ readonly "mcpConfigDialog.scopeToProvider": "Scope to Provider";
183
+ readonly "mcpConfigDialog.allProviders": "All providers";
184
+ readonly "mcpConfigDialog.proactiveSuggestions": "Proactive suggestions";
185
+ readonly "mcpConfigDialog.proactiveDescription": "The AI will automatically suggest relevant assets based on your project context. Disable if you prefer to search manually.";
186
+ readonly "mcpConfigDialog.mcpEndpoint": "MCP Endpoint";
187
+ readonly "mcpConfigDialog.copyUrl": "Copy URL";
188
+ readonly "mcpConfigDialog.installInCursor": "Install in Cursor";
189
+ readonly "mcpConfigDialog.manualConfig": "Manual config — {{file}}";
190
+ readonly "mcpConfigDialog.claudeConfigDesc": "Add to .mcp.json in your project root (or run via claude mcp add):";
191
+ readonly "mcpConfigDialog.copilotConfigDesc": "Add to your VS Code settings (.vscode/settings.json or user settings):";
192
+ readonly "mcpConfigDialog.geminiConfigDesc": "Add to your Gemini CLI configuration:";
193
+ readonly "mcpConfigDialog.cursorConfigDesc": "Add to .cursor/mcp.json in your project root:";
194
+ readonly "mcpConfigDialog.omitToolHint": "Omit ?tool= to receive all assets regardless of tool compatibility.";
195
+ readonly "devAiHubPage.title": "Dev AI Hub";
196
+ readonly "devAiHubPage.subtitle": "Centralized AI assets for your team";
197
+ readonly "devAiHubPage.statsInstructions": "{{count}} Instructions";
198
+ readonly "devAiHubPage.statsAgents": "{{count}} Agents";
199
+ readonly "devAiHubPage.statsSkills": "{{count}} Skills";
200
+ readonly "devAiHubPage.statsWorkflows": "{{count}} Workflows";
201
+ readonly "devAiHubPage.lastSync": "Last sync: {{time}}";
202
+ readonly "devAiHubPage.configMcp": "MCP Servers";
203
+ readonly "devAiHubPage.assetCountOne": "{{n}} asset";
204
+ readonly "devAiHubPage.assetCountOther": "{{n}} assets";
205
+ readonly "devAiHubPage.providerCountOne": "{{n}} provider";
206
+ readonly "devAiHubPage.providerCountOther": "{{n}} providers";
207
+ readonly "devAiHubPage.totalStats": "{{totalAssets}} assets · {{providers}}";
208
+ readonly "devAiHubPage.noAssetsTitle": "No assets found";
209
+ readonly "devAiHubPage.noAssetsSubtitle": "Try adjusting your filters or search terms.";
210
+ readonly "devAiHubPage.timeJustNow": "just now";
211
+ readonly "devAiHubPage.timeMinutesAgo": "{{count}}m ago";
212
+ readonly "devAiHubPage.timeHoursAgo": "{{count}}h ago";
213
+ readonly "devAiHubPage.timeDaysAgo": "{{count}}d ago";
214
+ readonly "devAiHubPage.providersSectionTitle": "Providers";
215
+ readonly "devAiHubPage.syncAllButton": "Sync All";
216
+ readonly "devAiHubPage.syncButton": "Sync";
217
+ readonly "devAiHubPage.syncTriggered": "Sync triggered";
218
+ readonly "devAiHubPage.providerStatusError": "Error";
219
+ readonly "devAiHubPage.noProvidersConfigured": "No providers configured.";
220
+ readonly "devAiHubPage.tabAssets": "Assets";
221
+ readonly "devAiHubPage.tabMcp": "MCP";
222
+ readonly "devAiHubPage.tabAdmin": "Admin";
223
+ }>;
224
+ declare const devAiHubTranslationResource: _backstage_frontend_plugin_api.TranslationResource<"dev-ai-hub">;
225
+
226
+ export { DevAiHubPage, devAiHubApiRef, devAiHubPlugin, devAiHubTranslationRef, devAiHubTranslationResource, rootRouteRef };
104
227
  export type { DevAiHubApi };
package/dist/index.esm.js CHANGED
@@ -2,4 +2,5 @@ export { devAiHubPlugin } from './plugin.esm.js';
2
2
  export { DevAiHubPage } from './pluginLegacy.esm.js';
3
3
  export { devAiHubApiRef } from './api/DevAiHubClient.esm.js';
4
4
  export { rootRouteRef } from './routes.esm.js';
5
+ export { devAiHubTranslationRef, devAiHubTranslationResource } from './translation.esm.js';
5
6
  //# sourceMappingURL=index.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;"}
1
+ {"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}