@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
@@ -1,10 +1,11 @@
1
1
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
- import { useState } from 'react';
2
+ import { useState, useEffect } from 'react';
3
3
  import { useTheme } from '@mui/material/styles';
4
4
  import ReactMarkdown from 'react-markdown';
5
5
  import remarkGfm from 'remark-gfm';
6
6
  import Alert from '@mui/material/Alert';
7
7
  import Box from '@mui/material/Box';
8
+ import Collapse from '@mui/material/Collapse';
8
9
  import Button from '@mui/material/Button';
9
10
  import Chip from '@mui/material/Chip';
10
11
  import CircularProgress from '@mui/material/CircularProgress';
@@ -18,28 +19,50 @@ import Tabs from '@mui/material/Tabs';
18
19
  import Typography from '@mui/material/Typography';
19
20
  import CloseIcon from '@mui/icons-material/Close';
20
21
  import ContentCopyIcon from '@mui/icons-material/ContentCopy';
22
+ import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
21
23
  import FolderZipIcon from '@mui/icons-material/FolderZip';
22
24
  import OpenInNewIcon from '@mui/icons-material/OpenInNew';
25
+ import Inventory2Icon from '@mui/icons-material/Inventory2';
26
+ import { useTranslationRef } from '@backstage/frontend-plugin-api';
23
27
  import { useAssetDetail } from '../../hooks/index.esm.js';
28
+ import { devAiHubTranslationRef } from '../../translation.esm.js';
24
29
 
25
30
  const SyntaxHighlighter = require("react-syntax-highlighter/dist/esm/prism").default;
26
31
  const { oneLight, oneDark } = require("react-syntax-highlighter/dist/esm/styles/prism");
32
+ function parseFrontmatter(md) {
33
+ const match = md.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
34
+ if (!match) return { meta: null, body: md };
35
+ const meta = {};
36
+ for (const line of match[1].split("\n")) {
37
+ const m = line.match(/^([^:#\s][^:]*?):\s*(.+)$/);
38
+ if (m) meta[m[1].trim()] = m[2].trim().replace(/^["']|["']$/g, "");
39
+ }
40
+ return { meta: Object.keys(meta).length > 0 ? meta : null, body: match[2].trimStart() };
41
+ }
27
42
  const TYPE_COLORS = {
28
43
  instruction: "#1976d2",
29
44
  agent: "#7b1fa2",
30
45
  skill: "#388e3c",
31
- workflow: "#f57c00"
46
+ workflow: "#f57c00",
47
+ prompt: "#EC4899",
48
+ bundle: "#8B5CF6"
32
49
  };
33
50
  function AssetDetailPanel({ assetId, onClose }) {
34
51
  const [tab, setTab] = useState(0);
35
52
  const [snackbar, setSnackbar] = useState(null);
53
+ const [frontmatterOpen, setFrontmatterOpen] = useState(false);
36
54
  const { asset, loading } = useAssetDetail(assetId);
37
55
  const theme = useTheme();
56
+ const { t } = useTranslationRef(devAiHubTranslationRef);
38
57
  const syntaxTheme = theme.palette.mode === "dark" ? oneDark : oneLight;
58
+ useEffect(() => {
59
+ setFrontmatterOpen(false);
60
+ }, [assetId]);
61
+ const parsed = asset ? parseFrontmatter(asset.content) : null;
39
62
  const handleCopy = () => {
40
63
  if (!asset) return;
41
64
  navigator.clipboard.writeText(asset.content).then(
42
- () => setSnackbar("Markdown copied to clipboard!")
65
+ () => setSnackbar(t("assetDetailPanel.copiedMessage"))
43
66
  );
44
67
  };
45
68
  return /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -65,19 +88,19 @@ function AssetDetailPanel({ assetId, onClose }) {
65
88
  children: [
66
89
  /* @__PURE__ */ jsx(Box, { sx: { flex: 1 }, children: loading || !asset ? /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 1 }, children: [
67
90
  /* @__PURE__ */ jsx(CircularProgress, { size: 16 }),
68
- /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", children: "Loading..." })
91
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", children: t("assetDetailPanel.loading") })
69
92
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
70
93
  /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 1, mb: 0.5 }, children: [
71
- /* @__PURE__ */ jsx(Typography, { variant: "h6", fontWeight: 700, children: asset.name }),
94
+ /* @__PURE__ */ jsx(Typography, { variant: "h6", fontWeight: 700, children: asset.label ?? asset.name }),
72
95
  /* @__PURE__ */ jsx(
73
96
  Chip,
74
97
  {
75
98
  label: asset.type,
76
99
  size: "small",
77
100
  sx: {
78
- backgroundColor: `${TYPE_COLORS[asset.type]}20`,
79
- color: TYPE_COLORS[asset.type],
80
- fontWeight: 600
101
+ backgroundColor: TYPE_COLORS[asset.type],
102
+ color: "#fff",
103
+ fontWeight: 700
81
104
  }
82
105
  }
83
106
  )
@@ -95,104 +118,265 @@ function AssetDetailPanel({ assetId, onClose }) {
95
118
  onChange: (_, v) => setTab(v),
96
119
  sx: { borderBottom: 1, borderColor: "divider", px: 2 },
97
120
  children: [
98
- /* @__PURE__ */ jsx(Tab, { label: "Preview" }),
99
- /* @__PURE__ */ jsx(Tab, { label: "Metadata" }),
100
- /* @__PURE__ */ jsx(Tab, { label: "Raw YAML" })
121
+ /* @__PURE__ */ jsx(Tab, { label: t("assetDetailPanel.tabPreview") }),
122
+ /* @__PURE__ */ jsx(Tab, { label: t("assetDetailPanel.tabMetadata") }),
123
+ /* @__PURE__ */ jsx(Tab, { label: t("assetDetailPanel.tabRawYaml") })
101
124
  ]
102
125
  }
103
126
  ),
104
127
  /* @__PURE__ */ jsxs(Box, { sx: { flex: 1, overflow: "auto", p: 2 }, children: [
105
128
  loading && /* @__PURE__ */ jsx(Box, { sx: { display: "flex", justifyContent: "center", pt: 4 }, children: /* @__PURE__ */ jsx(CircularProgress, {}) }),
106
129
  !loading && asset && /* @__PURE__ */ jsxs(Fragment, { children: [
107
- tab === 0 && /* @__PURE__ */ jsx(
108
- Box,
109
- {
110
- sx: {
111
- "& h1,h2,h3,h4,h5,h6": { mt: 2, mb: 1, fontWeight: 700 },
112
- "& p": { mb: 1, lineHeight: 1.7 },
113
- "& ul, & ol": { pl: 2.5, mb: 1 },
114
- "& li": { mb: 0.5 },
115
- "& blockquote": {
116
- borderLeft: "3px solid",
117
- borderColor: "primary.main",
118
- pl: 1.5,
119
- color: "text.secondary",
120
- my: 1,
121
- ml: 0
122
- },
123
- "& table": { width: "100%", borderCollapse: "collapse", mb: 1 },
124
- "& th, & td": {
130
+ tab === 0 && asset.type === "bundle" && /* @__PURE__ */ jsxs(Box, { children: [
131
+ /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 0.75, mb: 1.5 }, children: [
132
+ /* @__PURE__ */ jsx(Inventory2Icon, { sx: { fontSize: "1rem", color: "#8B5CF6" } }),
133
+ /* @__PURE__ */ jsx(Typography, { variant: "subtitle2", fontWeight: 600, color: "text.secondary", children: t("assetDetailPanel.bundlePreviewTitle", { total: String(asset.items?.length ?? 0) }) })
134
+ ] }),
135
+ asset.description && /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", sx: { mb: 2 }, children: asset.description }),
136
+ /* @__PURE__ */ jsx(Box, { sx: { display: "flex", flexDirection: "column", gap: 0.75 }, children: (asset.items ?? []).map((item, idx) => /* @__PURE__ */ jsx(
137
+ Box,
138
+ {
139
+ sx: {
140
+ display: "flex",
141
+ alignItems: "center",
142
+ gap: 1.5,
143
+ p: 1.25,
144
+ borderRadius: 1.5,
125
145
  border: "1px solid",
126
146
  borderColor: "divider",
127
- px: 1.5,
128
- py: 0.75,
129
- fontSize: "0.875rem"
147
+ bgcolor: "action.hover"
130
148
  },
131
- "& th": { backgroundColor: "action.hover", fontWeight: 700 },
132
- "& code": {
133
- bgcolor: "action.hover",
134
- px: 0.5,
135
- py: 0.2,
136
- borderRadius: 0.5,
137
- fontFamily: "monospace",
138
- fontSize: "0.875em"
139
- },
140
- "& pre code": { bgcolor: "transparent", px: 0, py: 0 }
141
- },
142
- children: /* @__PURE__ */ jsx(
143
- ReactMarkdown,
144
- {
145
- remarkPlugins: [remarkGfm],
146
- components: {
147
- pre: ({ children }) => /* @__PURE__ */ jsx(Box, { sx: { my: 1 }, children }),
148
- code({ className, children }) {
149
- const match = /language-(\w+)/.exec(className || "");
150
- const code = String(children).replace(/\n$/, "");
151
- const isBlock = code.includes("\n") || !!match;
152
- return isBlock ? /* @__PURE__ */ jsx(
153
- SyntaxHighlighter,
154
- {
155
- style: syntaxTheme,
156
- language: match?.[1] ?? "text",
157
- PreTag: "div",
158
- customStyle: {
159
- borderRadius: 8,
160
- fontSize: "0.8rem",
161
- margin: 0,
162
- border: `1px solid ${theme.palette.divider}`
163
- },
164
- children: code
149
+ children: /* @__PURE__ */ jsxs(Box, { sx: { flex: 1, minWidth: 0 }, children: [
150
+ /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 1, mb: item.description ? 0.25 : 0 }, children: [
151
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", fontWeight: 600, noWrap: true, title: item.label ?? item.name ?? item.ref ?? void 0, children: item.label ?? item.name ?? item.ref }),
152
+ item.type && /* @__PURE__ */ jsx(
153
+ Chip,
154
+ {
155
+ label: item.type,
156
+ size: "small",
157
+ sx: {
158
+ height: 18,
159
+ fontSize: "0.65rem",
160
+ bgcolor: TYPE_COLORS[item.type] ?? "#64748b",
161
+ color: "#fff",
162
+ fontWeight: 700,
163
+ flexShrink: 0
165
164
  }
166
- ) : /* @__PURE__ */ jsx("code", { className, children });
165
+ }
166
+ )
167
+ ] }),
168
+ item.description && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", noWrap: true, title: item.description, children: item.description }),
169
+ !item.assetId && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.disabled", sx: { display: "block" }, children: t("assetDetailPanel.bundleItemNotSynced", { ref: item.ref }) })
170
+ ] })
171
+ },
172
+ idx
173
+ )) })
174
+ ] }),
175
+ tab === 0 && asset.type !== "bundle" && /* @__PURE__ */ jsxs(Box, { children: [
176
+ parsed?.meta && /* @__PURE__ */ jsxs(
177
+ Box,
178
+ {
179
+ sx: {
180
+ mb: 1.5,
181
+ border: "1px solid",
182
+ borderColor: "divider",
183
+ borderRadius: 1.5,
184
+ overflow: "hidden"
185
+ },
186
+ children: [
187
+ /* @__PURE__ */ jsxs(
188
+ Box,
189
+ {
190
+ onClick: () => setFrontmatterOpen((v) => !v),
191
+ sx: {
192
+ display: "flex",
193
+ alignItems: "center",
194
+ justifyContent: "space-between",
195
+ px: 1.5,
196
+ py: 0.75,
197
+ cursor: "pointer",
198
+ bgcolor: "action.hover",
199
+ userSelect: "none",
200
+ "&:hover": { bgcolor: "action.selected" }
201
+ },
202
+ children: [
203
+ /* @__PURE__ */ jsx(
204
+ Typography,
205
+ {
206
+ variant: "caption",
207
+ fontWeight: 700,
208
+ color: "text.secondary",
209
+ sx: { fontFamily: "monospace", textTransform: "uppercase", letterSpacing: 0.6 },
210
+ children: "frontmatter"
211
+ }
212
+ ),
213
+ /* @__PURE__ */ jsx(
214
+ ExpandMoreIcon,
215
+ {
216
+ sx: {
217
+ fontSize: "1rem",
218
+ color: "text.secondary",
219
+ transform: frontmatterOpen ? "rotate(180deg)" : "none",
220
+ transition: "transform 0.2s ease"
221
+ }
222
+ }
223
+ )
224
+ ]
167
225
  }
226
+ ),
227
+ /* @__PURE__ */ jsx(Collapse, { in: frontmatterOpen, children: /* @__PURE__ */ jsx(Box, { sx: { px: 1.5, py: 1, display: "flex", flexDirection: "column", gap: 0.5 }, children: Object.entries(parsed.meta).map(([key, value]) => /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", gap: 1, alignItems: "baseline" }, children: [
228
+ /* @__PURE__ */ jsxs(
229
+ Typography,
230
+ {
231
+ variant: "caption",
232
+ sx: { fontFamily: "monospace", color: "primary.main", fontWeight: 600, minWidth: 90, flexShrink: 0 },
233
+ children: [
234
+ key,
235
+ ":"
236
+ ]
237
+ }
238
+ ),
239
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", sx: { fontFamily: "monospace", wordBreak: "break-all" }, children: value })
240
+ ] }, key)) }) })
241
+ ]
242
+ }
243
+ ),
244
+ /* @__PURE__ */ jsx(
245
+ Box,
246
+ {
247
+ sx: {
248
+ "& h1,h2,h3,h4,h5,h6": { mt: 2, mb: 1, fontWeight: 700 },
249
+ "& p": { mb: 1, lineHeight: 1.7 },
250
+ "& ul, & ol": { pl: 2.5, mb: 1 },
251
+ "& li": { mb: 0.5 },
252
+ "& blockquote": {
253
+ borderLeft: "3px solid",
254
+ borderColor: "primary.main",
255
+ pl: 1.5,
256
+ color: "text.secondary",
257
+ my: 1,
258
+ ml: 0
168
259
  },
169
- children: asset.content
170
- }
171
- )
172
- }
173
- ),
260
+ "& table": { width: "100%", borderCollapse: "collapse", mb: 1 },
261
+ "& th, & td": {
262
+ border: "1px solid",
263
+ borderColor: "divider",
264
+ px: 1.5,
265
+ py: 0.75,
266
+ fontSize: "0.875rem"
267
+ },
268
+ "& th": { backgroundColor: "action.hover", fontWeight: 700 },
269
+ "& code": {
270
+ bgcolor: "action.hover",
271
+ px: 0.5,
272
+ py: 0.2,
273
+ borderRadius: 0.5,
274
+ fontFamily: "monospace",
275
+ fontSize: "0.875em"
276
+ },
277
+ "& pre code": { bgcolor: "transparent", px: 0, py: 0 }
278
+ },
279
+ children: /* @__PURE__ */ jsx(
280
+ ReactMarkdown,
281
+ {
282
+ remarkPlugins: [remarkGfm],
283
+ components: {
284
+ pre: ({ children }) => /* @__PURE__ */ jsx(Box, { sx: { my: 1 }, children }),
285
+ code({ className, children }) {
286
+ const match = /language-(\w+)/.exec(className || "");
287
+ const code = String(children).replace(/\n$/, "");
288
+ const isBlock = code.includes("\n") || !!match;
289
+ return isBlock ? /* @__PURE__ */ jsx(
290
+ SyntaxHighlighter,
291
+ {
292
+ style: syntaxTheme,
293
+ language: match?.[1] ?? "text",
294
+ PreTag: "div",
295
+ customStyle: {
296
+ borderRadius: 8,
297
+ fontSize: "0.8rem",
298
+ margin: 0,
299
+ border: `1px solid ${theme.palette.divider}`
300
+ },
301
+ children: code
302
+ }
303
+ ) : /* @__PURE__ */ jsx("code", { className, children });
304
+ }
305
+ },
306
+ children: parsed?.body ?? asset.content
307
+ }
308
+ )
309
+ }
310
+ )
311
+ ] }),
174
312
  tab === 1 && /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", flexDirection: "column", gap: 2 }, children: [
175
- /* @__PURE__ */ jsx(MetaRow, { label: "Author", value: asset.author }),
176
- /* @__PURE__ */ jsx(MetaRow, { label: "Version", value: asset.version }),
177
- /* @__PURE__ */ jsx(MetaRow, { label: "Provider", value: asset.providerId }),
178
- asset.commitSha && /* @__PURE__ */ jsx(MetaRow, { label: "Commit", value: asset.commitSha.slice(0, 8) }),
179
- /* @__PURE__ */ jsx(MetaRow, { label: "Last synced", value: new Date(asset.syncedAt).toLocaleString() }),
180
- /* @__PURE__ */ jsx(MetaRow, { label: "Branch", value: asset.branch }),
313
+ /* @__PURE__ */ jsx(MetaRow, { label: t("assetDetailPanel.metaAuthor"), value: asset.author }),
314
+ /* @__PURE__ */ jsx(MetaRow, { label: t("assetDetailPanel.metaVersion"), value: asset.version }),
315
+ /* @__PURE__ */ jsx(MetaRow, { label: t("assetDetailPanel.metaProvider"), value: asset.providerId }),
316
+ asset.commitSha && /* @__PURE__ */ jsx(MetaRow, { label: t("assetDetailPanel.metaCommit"), value: asset.commitSha.slice(0, 8) }),
317
+ /* @__PURE__ */ jsx(MetaRow, { label: t("assetDetailPanel.metaLastSynced"), value: new Date(asset.syncedAt).toLocaleString() }),
318
+ /* @__PURE__ */ jsx(MetaRow, { label: t("assetDetailPanel.metaBranch"), value: asset.branch }),
181
319
  /* @__PURE__ */ jsx(Divider, {}),
182
320
  /* @__PURE__ */ jsxs(Box, { children: [
183
- /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", children: "Compatible tools" }),
184
- /* @__PURE__ */ jsx(Box, { sx: { display: "flex", gap: 1, mt: 0.5 }, children: asset.tools.map((t) => /* @__PURE__ */ jsx(Chip, { label: t, size: "small" }, t)) })
321
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", children: t("assetDetailPanel.compatibleTools") }),
322
+ /* @__PURE__ */ jsx(Box, { sx: { display: "flex", gap: 1, mt: 0.5 }, children: asset.tools.map((tool) => /* @__PURE__ */ jsx(Chip, { label: tool, size: "small" }, tool)) })
185
323
  ] }),
186
324
  asset.tags.length > 0 && /* @__PURE__ */ jsxs(Box, { children: [
187
- /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", children: "Tags" }),
188
- /* @__PURE__ */ jsx(Box, { sx: { display: "flex", gap: 0.5, flexWrap: "wrap", mt: 0.5 }, children: asset.tags.map((t) => /* @__PURE__ */ jsx(Chip, { label: t, size: "small", variant: "outlined" }, t)) })
325
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", children: t("assetDetailPanel.tagsLabel") }),
326
+ /* @__PURE__ */ jsx(Box, { sx: { display: "flex", gap: 0.5, flexWrap: "wrap", mt: 0.5 }, children: asset.tags.map((tag) => /* @__PURE__ */ jsx(Chip, { label: tag, size: "small", variant: "outlined" }, tag)) })
327
+ ] }),
328
+ asset.type === "bundle" && asset.items && asset.items.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
329
+ /* @__PURE__ */ jsx(Divider, {}),
330
+ /* @__PURE__ */ jsxs(Box, { children: [
331
+ /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 0.75, mb: 1 }, children: [
332
+ /* @__PURE__ */ jsx(Inventory2Icon, { sx: { fontSize: "0.85rem", color: "#8B5CF6" } }),
333
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", fontWeight: 600, children: t("assetDetailPanel.bundleContents", { total: String(asset.items.length) }) })
334
+ ] }),
335
+ /* @__PURE__ */ jsx(Box, { sx: { display: "flex", flexDirection: "column", gap: 0.75 }, children: asset.items.map((item, idx) => /* @__PURE__ */ jsx(
336
+ Box,
337
+ {
338
+ sx: {
339
+ display: "flex",
340
+ alignItems: "center",
341
+ gap: 1,
342
+ p: 1,
343
+ borderRadius: 1,
344
+ border: "1px solid",
345
+ borderColor: "divider",
346
+ bgcolor: "action.hover"
347
+ },
348
+ children: /* @__PURE__ */ jsxs(Box, { sx: { flex: 1, minWidth: 0 }, children: [
349
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", fontWeight: 600, noWrap: true, title: item.label ?? item.name ?? item.ref ?? void 0, children: item.label ?? item.name ?? item.ref }),
350
+ item.type && /* @__PURE__ */ jsx(
351
+ Chip,
352
+ {
353
+ label: item.type,
354
+ size: "small",
355
+ sx: {
356
+ height: 16,
357
+ fontSize: "0.6rem",
358
+ bgcolor: `${TYPE_COLORS[item.type] ?? "#666"}22`,
359
+ color: TYPE_COLORS[item.type] ?? "text.secondary",
360
+ border: "1px solid",
361
+ borderColor: `${TYPE_COLORS[item.type] ?? "#666"}55`,
362
+ fontWeight: 600,
363
+ mt: 0.25
364
+ }
365
+ }
366
+ ),
367
+ !item.assetId && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.disabled", sx: { display: "block" }, children: t("assetDetailPanel.bundleItemNotSyncedYet", { ref: item.ref }) })
368
+ ] })
369
+ },
370
+ idx
371
+ )) })
372
+ ] })
189
373
  ] }),
190
374
  asset.type === "skill" && /* @__PURE__ */ jsxs(Fragment, { children: [
191
375
  /* @__PURE__ */ jsx(Divider, {}),
192
376
  /* @__PURE__ */ jsxs(Box, { children: [
193
377
  /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 0.75, mb: 0.5 }, children: [
194
378
  /* @__PURE__ */ jsx(FolderZipIcon, { sx: { fontSize: "0.85rem", color: "text.secondary" } }),
195
- /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", children: "Bundled files" })
379
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", children: t("assetDetailPanel.bundledFiles") })
196
380
  ] }),
197
381
  asset.resourcesContent && Object.keys(asset.resourcesContent).length > 0 ? /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", flexDirection: "column", gap: 0.5, mt: 0.5 }, children: [
198
382
  /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", gap: 0.5, flexWrap: "wrap" }, children: [
@@ -215,7 +399,7 @@ function AssetDetailPanel({ assetId, onClose }) {
215
399
  p
216
400
  ))
217
401
  ] }),
218
- /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.disabled", children: "Downloads as .zip containing all files above." })
402
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.disabled", children: t("assetDetailPanel.zipDescription") })
219
403
  ] }) : /* @__PURE__ */ jsx(Box, { sx: { display: "flex", gap: 0.5, flexWrap: "wrap", mt: 0.5 }, children: /* @__PURE__ */ jsx(
220
404
  Chip,
221
405
  {
@@ -228,7 +412,7 @@ function AssetDetailPanel({ assetId, onClose }) {
228
412
  ] }),
229
413
  /* @__PURE__ */ jsx(Divider, {}),
230
414
  /* @__PURE__ */ jsxs(Box, { children: [
231
- /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", children: "Repository" }),
415
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", children: t("assetDetailPanel.repository") }),
232
416
  /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(
233
417
  Link,
234
418
  {
@@ -285,7 +469,7 @@ function AssetDetailPanel({ assetId, onClose }) {
285
469
  startIcon: /* @__PURE__ */ jsx(ContentCopyIcon, {}),
286
470
  onClick: handleCopy,
287
471
  disabled: !asset,
288
- children: "Copy Markdown"
472
+ children: t("assetDetailPanel.copyMarkdown")
289
473
  }
290
474
  ),
291
475
  /* @__PURE__ */ jsx(
@@ -295,7 +479,7 @@ function AssetDetailPanel({ assetId, onClose }) {
295
479
  startIcon: /* @__PURE__ */ jsx(OpenInNewIcon, {}),
296
480
  onClick: () => asset && window.open(asset.repoUrl, "_blank"),
297
481
  disabled: !asset,
298
- children: "Open in Repo"
482
+ children: t("assetDetailPanel.openInRepo")
299
483
  }
300
484
  )
301
485
  ]