@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
@@ -8,6 +8,8 @@ class DevAiHubClient {
8
8
  this.discoveryApi = discoveryApi;
9
9
  this.fetchApi = fetchApi;
10
10
  }
11
+ discoveryApi;
12
+ fetchApi;
11
13
  async baseUrl() {
12
14
  return this.discoveryApi.getBaseUrl("dev-ai-hub");
13
15
  }
@@ -45,9 +47,14 @@ class DevAiHubClient {
45
47
  if (!response.ok) throw new Error(`Failed to fetch raw asset: ${response.status}`);
46
48
  return response.text();
47
49
  }
48
- async getDownloadUrl(id) {
50
+ async getDownloadUrl(id, tool) {
49
51
  const base = await this.baseUrl();
50
- return `${base}/assets/${encodeURIComponent(id)}/download`;
52
+ const url = `${base}/assets/${encodeURIComponent(id)}/download`;
53
+ return tool ? `${url}?tool=${encodeURIComponent(tool)}` : url;
54
+ }
55
+ async getAgentMdUrl(id) {
56
+ const base = await this.baseUrl();
57
+ return `${base}/assets/${encodeURIComponent(id)}/agent-md`;
51
58
  }
52
59
  async trackInstall(id) {
53
60
  await this.fetch(`/assets/${encodeURIComponent(id)}/track-install`, {
@@ -70,6 +77,12 @@ class DevAiHubClient {
70
77
  async getStats() {
71
78
  return this.fetch("/stats");
72
79
  }
80
+ async getMcpCatalog() {
81
+ return this.fetch("/mcp-catalog");
82
+ }
83
+ async getUiConfig() {
84
+ return this.fetch("/ui-config");
85
+ }
73
86
  }
74
87
 
75
88
  export { DevAiHubClient, devAiHubApiRef };
@@ -1 +1 @@
1
- {"version":3,"file":"DevAiHubClient.esm.js","sources":["../../src/api/DevAiHubClient.ts"],"sourcesContent":["import {\n createApiRef,\n DiscoveryApi,\n FetchApi,\n} from '@backstage/core-plugin-api';\nimport type {\n AiAsset,\n AiAssetListResponse,\n AiHubProvider,\n AiHubStats,\n AssetListFilter,\n} from '@julianpedro/plugin-dev-ai-hub-common';\n\nexport const devAiHubApiRef = createApiRef<DevAiHubApi>({\n id: 'plugin.dev-ai-hub.service',\n});\n\nexport interface DevAiHubApi {\n listAssets(filter?: AssetListFilter): Promise<AiAssetListResponse>;\n getAsset(id: string): Promise<AiAsset>;\n getAssetRaw(id: string): Promise<string>;\n /** Returns the absolute URL for the download endpoint (zip for skills, md for others). */\n getDownloadUrl(id: string): Promise<string>;\n trackInstall(id: string): Promise<void>;\n listProviders(): Promise<AiHubProvider[]>;\n getProviderStatus(id: string): Promise<AiHubProvider>;\n triggerSync(id: string): Promise<void>;\n getStats(): Promise<AiHubStats>;\n}\n\nexport class DevAiHubClient implements DevAiHubApi {\n constructor(\n private readonly discoveryApi: DiscoveryApi,\n private readonly fetchApi: FetchApi,\n ) {}\n\n private async baseUrl(): Promise<string> {\n return this.discoveryApi.getBaseUrl('dev-ai-hub');\n }\n\n private async fetch<T>(path: string, init?: RequestInit): Promise<T> {\n const base = await this.baseUrl();\n const response = await this.fetchApi.fetch(`${base}${path}`, init);\n if (!response.ok) {\n const text = await response.text();\n throw new Error(\n `Dev AI Hub API error ${response.status}: ${text}`,\n );\n }\n return response.json() as Promise<T>;\n }\n\n async listAssets(filter?: AssetListFilter): Promise<AiAssetListResponse> {\n const params = new URLSearchParams();\n if (filter?.type) params.set('type', filter.type);\n if (filter?.tool) params.set('tool', filter.tool);\n if (filter?.search) params.set('search', filter.search);\n if (filter?.providerId) params.set('provider', filter.providerId);\n if (filter?.tags?.length) params.set('tags', filter.tags.join(','));\n if (filter?.page) params.set('page', String(filter.page));\n if (filter?.pageSize) params.set('pageSize', String(filter.pageSize));\n\n const qs = params.toString();\n return this.fetch<AiAssetListResponse>(`/assets${qs ? `?${qs}` : ''}`);\n }\n\n async getAsset(id: string): Promise<AiAsset> {\n return this.fetch<AiAsset>(`/assets/${encodeURIComponent(id)}`);\n }\n\n async getAssetRaw(id: string): Promise<string> {\n const base = await this.baseUrl();\n const response = await this.fetchApi.fetch(\n `${base}/assets/${encodeURIComponent(id)}/raw`,\n );\n if (!response.ok) throw new Error(`Failed to fetch raw asset: ${response.status}`);\n return response.text();\n }\n\n async getDownloadUrl(id: string): Promise<string> {\n const base = await this.baseUrl();\n return `${base}/assets/${encodeURIComponent(id)}/download`;\n }\n\n async trackInstall(id: string): Promise<void> {\n await this.fetch(`/assets/${encodeURIComponent(id)}/track-install`, {\n method: 'POST',\n });\n }\n\n async listProviders(): Promise<AiHubProvider[]> {\n return this.fetch<AiHubProvider[]>('/providers');\n }\n\n async getProviderStatus(id: string): Promise<AiHubProvider> {\n return this.fetch<AiHubProvider>(\n `/providers/${encodeURIComponent(id)}/status`,\n );\n }\n\n async triggerSync(id: string): Promise<void> {\n await this.fetch(`/providers/${encodeURIComponent(id)}/sync`, {\n method: 'POST',\n });\n }\n\n async getStats(): Promise<AiHubStats> {\n return this.fetch<AiHubStats>('/stats');\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"DevAiHubClient.esm.js","sources":["../../src/api/DevAiHubClient.ts"],"sourcesContent":["import {\n createApiRef,\n DiscoveryApi,\n FetchApi,\n} from '@backstage/core-plugin-api';\nimport type {\n AiAsset,\n AiAssetListResponse,\n AiHubProvider,\n AiHubStats,\n AssetListFilter,\n McpCatalogEntry,\n} from '@julianpedro/plugin-dev-ai-hub-common';\n\nexport const devAiHubApiRef = createApiRef<DevAiHubApi>({\n id: 'plugin.dev-ai-hub.service',\n});\n\nexport interface UiConfig {\n typeColors: Partial<Record<string, string>>;\n statsCards: string[];\n}\n\nexport interface DevAiHubApi {\n listAssets(filter?: AssetListFilter): Promise<AiAssetListResponse>;\n getAsset(id: string): Promise<AiAsset>;\n getAssetRaw(id: string): Promise<string>;\n /** Returns the absolute URL for the download endpoint (zip for skills/bundles, md for others). */\n getDownloadUrl(id: string, tool?: string): Promise<string>;\n /** Returns the absolute URL for the .agent.md endpoint used in VSCode deep links. */\n getAgentMdUrl(id: string): Promise<string>;\n trackInstall(id: string): Promise<void>;\n listProviders(): Promise<AiHubProvider[]>;\n getProviderStatus(id: string): Promise<AiHubProvider>;\n triggerSync(id: string): Promise<void>;\n getStats(): Promise<AiHubStats>;\n getMcpCatalog(): Promise<McpCatalogEntry[]>;\n getUiConfig(): Promise<UiConfig>;\n}\n\nexport class DevAiHubClient implements DevAiHubApi {\n constructor(\n private readonly discoveryApi: DiscoveryApi,\n private readonly fetchApi: FetchApi,\n ) {}\n\n private async baseUrl(): Promise<string> {\n return this.discoveryApi.getBaseUrl('dev-ai-hub');\n }\n\n private async fetch<T>(path: string, init?: RequestInit): Promise<T> {\n const base = await this.baseUrl();\n const response = await this.fetchApi.fetch(`${base}${path}`, init);\n if (!response.ok) {\n const text = await response.text();\n throw new Error(\n `Dev AI Hub API error ${response.status}: ${text}`,\n );\n }\n return response.json() as Promise<T>;\n }\n\n async listAssets(filter?: AssetListFilter): Promise<AiAssetListResponse> {\n const params = new URLSearchParams();\n if (filter?.type) params.set('type', filter.type);\n if (filter?.tool) params.set('tool', filter.tool);\n if (filter?.search) params.set('search', filter.search);\n if (filter?.providerId) params.set('provider', filter.providerId);\n if (filter?.tags?.length) params.set('tags', filter.tags.join(','));\n if (filter?.page) params.set('page', String(filter.page));\n if (filter?.pageSize) params.set('pageSize', String(filter.pageSize));\n\n const qs = params.toString();\n return this.fetch<AiAssetListResponse>(`/assets${qs ? `?${qs}` : ''}`);\n }\n\n async getAsset(id: string): Promise<AiAsset> {\n return this.fetch<AiAsset>(`/assets/${encodeURIComponent(id)}`);\n }\n\n async getAssetRaw(id: string): Promise<string> {\n const base = await this.baseUrl();\n const response = await this.fetchApi.fetch(\n `${base}/assets/${encodeURIComponent(id)}/raw`,\n );\n if (!response.ok) throw new Error(`Failed to fetch raw asset: ${response.status}`);\n return response.text();\n }\n\n async getDownloadUrl(id: string, tool?: string): Promise<string> {\n const base = await this.baseUrl();\n const url = `${base}/assets/${encodeURIComponent(id)}/download`;\n return tool ? `${url}?tool=${encodeURIComponent(tool)}` : url;\n }\n\n async getAgentMdUrl(id: string): Promise<string> {\n const base = await this.baseUrl();\n return `${base}/assets/${encodeURIComponent(id)}/agent-md`;\n }\n\n async trackInstall(id: string): Promise<void> {\n await this.fetch(`/assets/${encodeURIComponent(id)}/track-install`, {\n method: 'POST',\n });\n }\n\n async listProviders(): Promise<AiHubProvider[]> {\n return this.fetch<AiHubProvider[]>('/providers');\n }\n\n async getProviderStatus(id: string): Promise<AiHubProvider> {\n return this.fetch<AiHubProvider>(\n `/providers/${encodeURIComponent(id)}/status`,\n );\n }\n\n async triggerSync(id: string): Promise<void> {\n await this.fetch(`/providers/${encodeURIComponent(id)}/sync`, {\n method: 'POST',\n });\n }\n\n async getStats(): Promise<AiHubStats> {\n return this.fetch<AiHubStats>('/stats');\n }\n\n async getMcpCatalog(): Promise<McpCatalogEntry[]> {\n return this.fetch<McpCatalogEntry[]>('/mcp-catalog');\n }\n\n async getUiConfig(): Promise<UiConfig> {\n return this.fetch<UiConfig>('/ui-config');\n }\n}"],"names":[],"mappings":"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"}
@@ -0,0 +1,106 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { useState } from 'react';
3
+ import Box from '@mui/material/Box';
4
+ import Button from '@mui/material/Button';
5
+ import CircularProgress from '@mui/material/CircularProgress';
6
+ import Divider from '@mui/material/Divider';
7
+ import IconButton from '@mui/material/IconButton';
8
+ import Snackbar from '@mui/material/Snackbar';
9
+ import Tooltip from '@mui/material/Tooltip';
10
+ import Typography from '@mui/material/Typography';
11
+ import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
12
+ import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline';
13
+ import SyncIcon from '@mui/icons-material/Sync';
14
+ import { Content } from '@backstage/core-components';
15
+ import { useTranslationRef } from '@backstage/frontend-plugin-api';
16
+ import { usePermission } from '@backstage/plugin-permission-react';
17
+ import { devAiHubSyncPermission } from '@julianpedro/plugin-dev-ai-hub-common';
18
+ import { devAiHubTranslationRef } from '../../translation.esm.js';
19
+ import { useProviders, useSyncProvider } from '../../hooks/index.esm.js';
20
+
21
+ function timeAgo(iso, translate) {
22
+ const diff = Math.floor((Date.now() - new Date(iso).getTime()) / 1e3);
23
+ if (diff < 60) return translate("devAiHubPage.timeJustNow") ?? "";
24
+ if (diff < 3600) return translate("devAiHubPage.timeMinutesAgo", { count: Math.floor(diff / 60) }) ?? "";
25
+ if (diff < 86400) return translate("devAiHubPage.timeHoursAgo", { count: Math.floor(diff / 3600) }) ?? "";
26
+ return translate("devAiHubPage.timeDaysAgo", { count: Math.floor(diff / 86400) }) ?? "";
27
+ }
28
+ function AdminPage() {
29
+ const { t } = useTranslationRef(devAiHubTranslationRef);
30
+ const [syncSnackbar, setSyncSnackbar] = useState(false);
31
+ const { allowed: canSync } = usePermission({ permission: devAiHubSyncPermission });
32
+ const { providers } = useProviders();
33
+ const { syncing, triggerSync, triggerSyncAll } = useSyncProvider();
34
+ return /* @__PURE__ */ jsxs(Content, { children: [
35
+ /* @__PURE__ */ jsxs(Box, { sx: { maxWidth: 720 }, children: [
36
+ /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", justifyContent: "space-between", mb: 3 }, children: [
37
+ /* @__PURE__ */ jsxs(Box, { children: [
38
+ /* @__PURE__ */ jsx(Typography, { variant: "h6", fontWeight: 700, children: t("devAiHubPage.providersSectionTitle") }),
39
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", children: t(providers.length === 1 ? "devAiHubPage.providerCountOne" : "devAiHubPage.providerCountOther", { n: String(providers.length) }) })
40
+ ] }),
41
+ canSync && providers.length > 1 && /* @__PURE__ */ jsx(
42
+ Button,
43
+ {
44
+ size: "small",
45
+ variant: "outlined",
46
+ startIcon: providers.some((p) => syncing[p.id]) ? /* @__PURE__ */ jsx(CircularProgress, { size: 14 }) : /* @__PURE__ */ jsx(SyncIcon, {}),
47
+ disabled: providers.some((p) => syncing[p.id]),
48
+ onClick: async () => {
49
+ await triggerSyncAll(providers.map((p) => p.id));
50
+ setSyncSnackbar(true);
51
+ },
52
+ children: t("devAiHubPage.syncAllButton")
53
+ }
54
+ )
55
+ ] }),
56
+ providers.length === 0 ? /* @__PURE__ */ jsx(Box, { sx: { border: "1px dashed", borderColor: "divider", borderRadius: 2, py: 6, textAlign: "center" }, children: /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", children: t("devAiHubPage.noProvidersConfigured") }) }) : /* @__PURE__ */ jsx(Box, { sx: { border: "1px solid", borderColor: "divider", borderRadius: 2, overflow: "hidden" }, children: providers.map((provider, idx) => /* @__PURE__ */ jsxs(Box, { children: [
57
+ /* @__PURE__ */ jsxs(Box, { sx: { px: 2.5, py: 2, display: "flex", alignItems: "center", gap: 1.5 }, children: [
58
+ provider.status === "error" && /* @__PURE__ */ jsx(Tooltip, { title: provider.error ?? t("devAiHubPage.providerStatusError"), children: /* @__PURE__ */ jsx(ErrorOutlineIcon, { sx: { fontSize: "1.1rem", color: "error.main", flexShrink: 0 } }) }),
59
+ provider.status === "syncing" && /* @__PURE__ */ jsx(CircularProgress, { size: 16, sx: { flexShrink: 0 } }),
60
+ provider.status !== "error" && provider.status !== "syncing" && /* @__PURE__ */ jsx(CheckCircleOutlineIcon, { sx: { fontSize: "1.1rem", color: "success.main", flexShrink: 0 } }),
61
+ /* @__PURE__ */ jsxs(Box, { sx: { flex: 1, minWidth: 0 }, children: [
62
+ /* @__PURE__ */ jsx(
63
+ Typography,
64
+ {
65
+ variant: "body2",
66
+ fontWeight: 600,
67
+ noWrap: true,
68
+ title: provider.target,
69
+ sx: { fontFamily: "monospace", fontSize: "0.8rem" },
70
+ children: provider.target
71
+ }
72
+ ),
73
+ provider.lastSync && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.disabled", children: timeAgo(provider.lastSync, t) }),
74
+ provider.status === "error" && provider.error && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "error", sx: { display: "block" }, noWrap: true, title: provider.error, children: provider.error })
75
+ ] }),
76
+ canSync && /* @__PURE__ */ jsx(Tooltip, { title: t("devAiHubPage.syncButton"), children: /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(
77
+ IconButton,
78
+ {
79
+ size: "small",
80
+ "aria-label": t("devAiHubPage.syncButton"),
81
+ disabled: !!syncing[provider.id],
82
+ onClick: async () => {
83
+ await triggerSync(provider.id);
84
+ setSyncSnackbar(true);
85
+ },
86
+ children: syncing[provider.id] ? /* @__PURE__ */ jsx(CircularProgress, { size: 16 }) : /* @__PURE__ */ jsx(SyncIcon, { fontSize: "small" })
87
+ }
88
+ ) }) })
89
+ ] }),
90
+ idx < providers.length - 1 && /* @__PURE__ */ jsx(Divider, {})
91
+ ] }, provider.id)) })
92
+ ] }),
93
+ /* @__PURE__ */ jsx(
94
+ Snackbar,
95
+ {
96
+ open: syncSnackbar,
97
+ autoHideDuration: 3e3,
98
+ onClose: () => setSyncSnackbar(false),
99
+ message: t("devAiHubPage.syncTriggered")
100
+ }
101
+ )
102
+ ] });
103
+ }
104
+
105
+ export { AdminPage };
106
+ //# sourceMappingURL=AdminPage.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdminPage.esm.js","sources":["../../../src/components/AdminPage/AdminPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport CircularProgress from '@mui/material/CircularProgress';\nimport Divider from '@mui/material/Divider';\nimport IconButton from '@mui/material/IconButton';\nimport Snackbar from '@mui/material/Snackbar';\nimport Tooltip from '@mui/material/Tooltip';\nimport Typography from '@mui/material/Typography';\nimport CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';\nimport ErrorOutlineIcon from '@mui/icons-material/ErrorOutline';\nimport SyncIcon from '@mui/icons-material/Sync';\nimport { Content } from '@backstage/core-components';\nimport { useTranslationRef } from '@backstage/frontend-plugin-api';\nimport { usePermission } from '@backstage/plugin-permission-react';\nimport { devAiHubSyncPermission } from '@julianpedro/plugin-dev-ai-hub-common';\nimport { devAiHubTranslationRef } from '../../translation';\nimport { useProviders, useSyncProvider } from '../../hooks';\n\ntype TFunc = (key: string, params?: Record<string, unknown>) => string | undefined;\n\nfunction timeAgo(iso: string, translate: TFunc): string {\n const diff = Math.floor((Date.now() - new Date(iso).getTime()) / 1000);\n if (diff < 60) return (translate('devAiHubPage.timeJustNow') ?? '') as string;\n if (diff < 3600) return (translate('devAiHubPage.timeMinutesAgo', { count: Math.floor(diff / 60) }) ?? '') as string;\n if (diff < 86400) return (translate('devAiHubPage.timeHoursAgo', { count: Math.floor(diff / 3600) }) ?? '') as string;\n return (translate('devAiHubPage.timeDaysAgo', { count: Math.floor(diff / 86400) }) ?? '') as string;\n}\n\nexport function AdminPage() {\n const { t } = useTranslationRef(devAiHubTranslationRef);\n const [syncSnackbar, setSyncSnackbar] = useState(false);\n const { allowed: canSync } = usePermission({ permission: devAiHubSyncPermission });\n const { providers } = useProviders();\n const { syncing, triggerSync, triggerSyncAll } = useSyncProvider();\n\n return (\n <Content>\n <Box sx={{ maxWidth: 720 }}>\n {/* Section header */}\n <Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 3 }}>\n <Box>\n <Typography variant=\"h6\" fontWeight={700}>\n {t('devAiHubPage.providersSectionTitle')}\n </Typography>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {t(providers.length === 1 ? 'devAiHubPage.providerCountOne' : 'devAiHubPage.providerCountOther', { n: String(providers.length) })}\n </Typography>\n </Box>\n {canSync && providers.length > 1 && (\n <Button\n size=\"small\"\n variant=\"outlined\"\n startIcon={providers.some(p => syncing[p.id]) ? <CircularProgress size={14} /> : <SyncIcon />}\n disabled={providers.some(p => syncing[p.id])}\n onClick={async () => {\n await triggerSyncAll(providers.map(p => p.id));\n setSyncSnackbar(true);\n }}\n >\n {t('devAiHubPage.syncAllButton')}\n </Button>\n )}\n </Box>\n\n {/* Provider list */}\n {providers.length === 0 ? (\n <Box sx={{ border: '1px dashed', borderColor: 'divider', borderRadius: 2, py: 6, textAlign: 'center' }}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {t('devAiHubPage.noProvidersConfigured')}\n </Typography>\n </Box>\n ) : (\n <Box sx={{ border: '1px solid', borderColor: 'divider', borderRadius: 2, overflow: 'hidden' }}>\n {providers.map((provider, idx) => (\n <Box key={provider.id}>\n <Box sx={{ px: 2.5, py: 2, display: 'flex', alignItems: 'center', gap: 1.5 }}>\n {provider.status === 'error' && (\n <Tooltip title={provider.error ?? t('devAiHubPage.providerStatusError')}>\n <ErrorOutlineIcon sx={{ fontSize: '1.1rem', color: 'error.main', flexShrink: 0 }} />\n </Tooltip>\n )}\n {provider.status === 'syncing' && (\n <CircularProgress size={16} sx={{ flexShrink: 0 }} />\n )}\n {provider.status !== 'error' && provider.status !== 'syncing' && (\n <CheckCircleOutlineIcon sx={{ fontSize: '1.1rem', color: 'success.main', flexShrink: 0 }} />\n )}\n\n <Box sx={{ flex: 1, minWidth: 0 }}>\n <Typography\n variant=\"body2\" fontWeight={600} noWrap title={provider.target}\n sx={{ fontFamily: 'monospace', fontSize: '0.8rem' }}\n >\n {provider.target}\n </Typography>\n {provider.lastSync && (\n <Typography variant=\"caption\" color=\"text.disabled\">\n {timeAgo(provider.lastSync, t as TFunc)}\n </Typography>\n )}\n {provider.status === 'error' && provider.error && (\n <Typography variant=\"caption\" color=\"error\" sx={{ display: 'block' }} noWrap title={provider.error}>\n {provider.error}\n </Typography>\n )}\n </Box>\n\n {canSync && (\n <Tooltip title={t('devAiHubPage.syncButton')}>\n <span>\n <IconButton\n size=\"small\"\n aria-label={t('devAiHubPage.syncButton') as string}\n disabled={!!syncing[provider.id]}\n onClick={async () => {\n await triggerSync(provider.id);\n setSyncSnackbar(true);\n }}\n >\n {syncing[provider.id]\n ? <CircularProgress size={16} />\n : <SyncIcon fontSize=\"small\" />}\n </IconButton>\n </span>\n </Tooltip>\n )}\n </Box>\n {idx < providers.length - 1 && <Divider />}\n </Box>\n ))}\n </Box>\n )}\n </Box>\n\n <Snackbar\n open={syncSnackbar}\n autoHideDuration={3000}\n onClose={() => setSyncSnackbar(false)}\n message={t('devAiHubPage.syncTriggered')}\n />\n </Content>\n );\n}"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { AdminPage } from './AdminPage.esm.js';
2
+
3
+
4
+
5
+ export { AdminPage };
6
+ //# sourceMappingURL=index.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
@@ -10,14 +10,26 @@ import Tooltip from '@mui/material/Tooltip';
10
10
  import Typography from '@mui/material/Typography';
11
11
  import OpenInNewIcon from '@mui/icons-material/OpenInNew';
12
12
  import DownloadIcon from '@mui/icons-material/Download';
13
+ import HelpOutlineIcon from '@mui/icons-material/HelpOutline';
13
14
  import ArticleIcon from '@mui/icons-material/Article';
14
15
  import SmartToyIcon from '@mui/icons-material/SmartToy';
15
16
  import BuildIcon from '@mui/icons-material/Build';
16
17
  import AccountTreeIcon from '@mui/icons-material/AccountTree';
18
+ import ChatIcon from '@mui/icons-material/Chat';
19
+ import Inventory2Icon from '@mui/icons-material/Inventory2';
20
+ import StorageIcon from '@mui/icons-material/Storage';
21
+ import { useTranslationRef } from '@backstage/frontend-plugin-api';
17
22
  import { ToolIcon } from '../ToolIcon/ToolIcon.esm.js';
23
+ import { ModelBadge } from '../ModelIcon/ModelBadge.esm.js';
24
+ import { devAiHubTranslationRef } from '../../translation.esm.js';
25
+ import 'react';
26
+ import '@backstage/core-plugin-api';
27
+ import '../../api/DevAiHubClient.esm.js';
28
+ import { useTypeConfig } from '../../context/UiConfigContext.esm.js';
18
29
 
19
30
  const POPULAR_THRESHOLD = 5;
20
31
  const NEW_DAYS_MS = 14 * 24 * 60 * 60 * 1e3;
32
+ const UPDATED_DAYS_MS = 7 * 24 * 60 * 60 * 1e3;
21
33
  const TOOL_LABELS = {
22
34
  "all": "Universal",
23
35
  "claude-code": "Claude Code",
@@ -25,19 +37,39 @@ const TOOL_LABELS = {
25
37
  "google-gemini": "Google Gemini",
26
38
  "cursor": "Cursor"
27
39
  };
28
- const TYPE_CONFIG = {
29
- instruction: { label: "Instruction", color: "#2563EB", Icon: ArticleIcon },
30
- agent: { label: "Agent", color: "#7C3AED", Icon: SmartToyIcon },
31
- skill: { label: "Skill", color: "#059669", Icon: BuildIcon },
32
- workflow: { label: "Workflow", color: "#D97706", Icon: AccountTreeIcon }
40
+ const TYPE_LABELS = {
41
+ instruction: "Instruction",
42
+ agent: "Agent",
43
+ skill: "Skill",
44
+ workflow: "Workflow",
45
+ prompt: "Prompt",
46
+ bundle: "Bundle"
33
47
  };
34
- function AssetCard({ asset, onView, onInstall }) {
48
+ const TYPE_ICONS = {
49
+ instruction: ArticleIcon,
50
+ agent: SmartToyIcon,
51
+ skill: BuildIcon,
52
+ workflow: AccountTreeIcon,
53
+ prompt: ChatIcon,
54
+ bundle: Inventory2Icon
55
+ };
56
+ function resolveMcp(req, catalog) {
57
+ const entry = catalog.find((e) => e.id === req.id);
58
+ return {
59
+ name: req.name ?? entry?.name ?? req.id,
60
+ icon: req.icon ?? entry?.icon
61
+ };
62
+ }
63
+ function AssetCard({ asset, onView, onInstall, onHelp, onOpenMcpCatalog, mcpCatalog = [] }) {
64
+ const { t } = useTranslationRef(devAiHubTranslationRef);
35
65
  const theme = useTheme();
66
+ const { typeColors } = useTypeConfig();
36
67
  const isDark = theme.palette.mode === "dark" || theme.palette.type === "dark";
37
- const cfg = TYPE_CONFIG[asset.type];
38
- const TypeIcon = cfg.Icon;
68
+ const color = typeColors[asset.type];
69
+ const TypeIcon = TYPE_ICONS[asset.type];
39
70
  const isPopular = asset.installCount >= POPULAR_THRESHOLD;
40
- const isNew = Date.now() - new Date(asset.updatedAt).getTime() < NEW_DAYS_MS;
71
+ const isNew = Date.now() - new Date(asset.createdAt).getTime() < NEW_DAYS_MS;
72
+ const isUpdated = !isNew && Date.now() - new Date(asset.updatedAt).getTime() < UPDATED_DAYS_MS;
41
73
  return /* @__PURE__ */ jsxs(
42
74
  Card,
43
75
  {
@@ -49,11 +81,11 @@ function AssetCard({ asset, onView, onInstall }) {
49
81
  borderRadius: 2,
50
82
  border: "1px solid",
51
83
  borderColor: "divider",
52
- borderLeft: `3px solid ${cfg.color}`,
84
+ borderLeft: `3px solid ${color}`,
53
85
  transition: "all 0.18s ease",
54
86
  "&:hover": {
55
- boxShadow: `0 6px 24px ${cfg.color}30`,
56
- borderColor: cfg.color,
87
+ boxShadow: `0 6px 24px ${color}30`,
88
+ borderColor: color,
57
89
  transform: "translateY(-2px)"
58
90
  }
59
91
  },
@@ -67,12 +99,12 @@ function AssetCard({ asset, onView, onInstall }) {
67
99
  width: 40,
68
100
  height: 40,
69
101
  borderRadius: 1.5,
70
- backgroundColor: alpha(cfg.color, 0.12),
102
+ backgroundColor: alpha(color, 0.12),
71
103
  display: "flex",
72
104
  alignItems: "center",
73
105
  justifyContent: "center",
74
106
  flexShrink: 0,
75
- boxShadow: `0 2px 8px ${cfg.color}25`
107
+ boxShadow: `0 2px 8px ${color}25`
76
108
  },
77
109
  children: asset.icon ? /* @__PURE__ */ jsx(
78
110
  Box,
@@ -85,7 +117,7 @@ function AssetCard({ asset, onView, onInstall }) {
85
117
  e.target.style.display = "none";
86
118
  }
87
119
  }
88
- ) : /* @__PURE__ */ jsx(TypeIcon, { sx: { color: cfg.color, fontSize: "1.3rem" } })
120
+ ) : /* @__PURE__ */ jsx(TypeIcon, { sx: { color, fontSize: "1.3rem" } })
89
121
  }
90
122
  ),
91
123
  /* @__PURE__ */ jsxs(Box, { sx: { flex: 1, minWidth: 0 }, children: [
@@ -94,7 +126,7 @@ function AssetCard({ asset, onView, onInstall }) {
94
126
  isNew && /* @__PURE__ */ jsx(
95
127
  Chip,
96
128
  {
97
- label: "New",
129
+ label: t("assetCard.newBadge"),
98
130
  size: "small",
99
131
  sx: {
100
132
  height: 18,
@@ -102,7 +134,7 @@ function AssetCard({ asset, onView, onInstall }) {
102
134
  fontWeight: 700,
103
135
  backgroundColor: alpha("#059669", isDark ? 0.25 : 0.14),
104
136
  backdropFilter: "blur(8px)",
105
- color: isDark ? "#fff" : "#059669",
137
+ color: isDark ? "#6ee7b7" : "#059669",
106
138
  border: "1px solid",
107
139
  borderColor: alpha("#059669", isDark ? 0.5 : 0.3),
108
140
  borderRadius: 1,
@@ -110,9 +142,31 @@ function AssetCard({ asset, onView, onInstall }) {
110
142
  "& .MuiChip-label": { px: "6px" }
111
143
  }
112
144
  }
145
+ ),
146
+ isUpdated && /* @__PURE__ */ jsx(
147
+ Chip,
148
+ {
149
+ label: t("assetCard.updatedBadge"),
150
+ size: "small",
151
+ sx: {
152
+ height: 18,
153
+ fontSize: "0.6rem",
154
+ fontWeight: 700,
155
+ backgroundColor: alpha("#D97706", isDark ? 0.25 : 0.12),
156
+ color: isDark ? "#fcd34d" : "#D97706",
157
+ border: "1px solid",
158
+ borderColor: alpha("#D97706", isDark ? 0.5 : 0.3),
159
+ borderRadius: 1,
160
+ flexShrink: 0,
161
+ "& .MuiChip-label": { px: "6px" }
162
+ }
163
+ }
113
164
  )
114
165
  ] }),
115
- /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { color: cfg.color, fontWeight: 600 }, children: cfg.label })
166
+ /* @__PURE__ */ jsxs(Box, { children: [
167
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { color, fontWeight: 600 }, children: TYPE_LABELS[asset.type] }),
168
+ asset.type === "agent" && asset.model && /* @__PURE__ */ jsx(Box, { sx: { mt: 0.3 }, children: /* @__PURE__ */ jsx(ModelBadge, { model: asset.model }) })
169
+ ] })
116
170
  ] })
117
171
  ] }),
118
172
  /* @__PURE__ */ jsx(
@@ -120,6 +174,7 @@ function AssetCard({ asset, onView, onInstall }) {
120
174
  {
121
175
  variant: "caption",
122
176
  color: "text.secondary",
177
+ title: asset.description,
123
178
  sx: {
124
179
  mb: 1,
125
180
  display: "-webkit-box",
@@ -171,24 +226,60 @@ function AssetCard({ asset, onView, onInstall }) {
171
226
  "+",
172
227
  asset.tags.length - 3
173
228
  ] })
229
+ ] }),
230
+ asset.mcps && asset.mcps.length > 0 && /* @__PURE__ */ jsxs(Box, { sx: { mt: 0.75, mb: 1 }, children: [
231
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.disabled", sx: { fontSize: "0.6rem", fontWeight: 600, textTransform: "uppercase", letterSpacing: 0.4, display: "block", mb: 0.5 }, children: t("assetCard.mcpsRequired") }),
232
+ /* @__PURE__ */ jsx(Box, { sx: { display: "flex", gap: 0.5, flexWrap: "wrap" }, children: asset.mcps.map((req) => {
233
+ const { name, icon } = resolveMcp(req, mcpCatalog);
234
+ return /* @__PURE__ */ jsx(
235
+ Box,
236
+ {
237
+ title: name,
238
+ onClick: onOpenMcpCatalog,
239
+ sx: {
240
+ width: 26,
241
+ height: 26,
242
+ borderRadius: "50%",
243
+ backgroundColor: "action.hover",
244
+ display: "flex",
245
+ alignItems: "center",
246
+ justifyContent: "center",
247
+ flexShrink: 0,
248
+ overflow: "hidden",
249
+ cursor: onOpenMcpCatalog ? "pointer" : "default",
250
+ transition: "background-color 0.15s ease",
251
+ "&:hover": onOpenMcpCatalog ? { backgroundColor: "action.selected" } : {}
252
+ },
253
+ children: icon ? /* @__PURE__ */ jsx(
254
+ Box,
255
+ {
256
+ component: "img",
257
+ src: icon,
258
+ alt: name,
259
+ sx: { width: 18, height: 18, objectFit: "contain" },
260
+ onError: (e) => {
261
+ e.target.style.display = "none";
262
+ }
263
+ }
264
+ ) : /* @__PURE__ */ jsx(StorageIcon, { sx: { fontSize: "0.9rem", color: "text.secondary" } })
265
+ },
266
+ req.id
267
+ );
268
+ }) })
174
269
  ] })
175
270
  ] }),
176
271
  /* @__PURE__ */ jsxs(CardActions, { sx: { px: 1.5, py: 1, justifyContent: "space-between", mt: "auto", borderTop: "1px solid", borderColor: "divider" }, children: [
177
272
  /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 1 }, children: [
178
- /* @__PURE__ */ jsxs(Typography, { variant: "caption", color: "text.disabled", sx: { fontSize: "0.65rem" }, children: [
179
- "v",
180
- asset.version,
181
- " \xB7 ",
182
- asset.author
183
- ] }),
273
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.disabled", sx: { fontSize: "0.65rem" }, children: asset.type === "bundle" && asset.itemCount !== void 0 ? t("assetCard.bundleFooter", { itemCount: String(asset.itemCount), author: asset.author }) : t("assetCard.versionFooter", { version: asset.version, author: asset.author }) }),
184
274
  asset.installCount > 0 && /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 0.25 }, children: [
185
275
  /* @__PURE__ */ jsx(Typography, { sx: { fontSize: "0.65rem", lineHeight: 1 }, children: isPopular ? "\u{1F525}" : "\u2193" }),
186
276
  /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.disabled", sx: { fontSize: "0.65rem" }, children: asset.installCount })
187
277
  ] })
188
278
  ] }),
189
279
  /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", gap: 0.25 }, children: [
190
- /* @__PURE__ */ jsx(Tooltip, { title: "Install in editor", children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: () => onInstall(asset.id), color: "primary", children: /* @__PURE__ */ jsx(DownloadIcon, { fontSize: "small" }) }) }),
191
- /* @__PURE__ */ jsx(Tooltip, { title: "View details", children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: () => onView(asset.id), children: /* @__PURE__ */ jsx(OpenInNewIcon, { fontSize: "small" }) }) })
280
+ /* @__PURE__ */ jsx(Tooltip, { title: t("assetCard.installTooltip"), children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: () => onInstall(asset.id), color: "primary", children: /* @__PURE__ */ jsx(DownloadIcon, { fontSize: "small" }) }) }),
281
+ /* @__PURE__ */ jsx(Tooltip, { title: t("assetCard.detailsTooltip"), children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: () => onView(asset.id), children: /* @__PURE__ */ jsx(OpenInNewIcon, { fontSize: "small" }) }) }),
282
+ asset.helpText && onHelp && /* @__PURE__ */ jsx(Tooltip, { title: t("assetCard.helpTooltip"), children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: () => onHelp(asset.id), children: /* @__PURE__ */ jsx(HelpOutlineIcon, { fontSize: "small" }) }) })
192
283
  ] })
193
284
  ] })
194
285
  ]
@@ -1 +1 @@
1
- {"version":3,"file":"AssetCard.esm.js","sources":["../../../src/components/AssetCard/AssetCard.tsx"],"sourcesContent":["import type { ElementType } from 'react';\nimport { alpha, useTheme } from '@mui/material/styles';\nimport Box from '@mui/material/Box';\nimport Card from '@mui/material/Card';\nimport CardActions from '@mui/material/CardActions';\nimport CardContent from '@mui/material/CardContent';\nimport Chip from '@mui/material/Chip';\nimport IconButton from '@mui/material/IconButton';\nimport Tooltip from '@mui/material/Tooltip';\nimport Typography from '@mui/material/Typography';\nimport OpenInNewIcon from '@mui/icons-material/OpenInNew';\nimport DownloadIcon from '@mui/icons-material/Download';\nimport ArticleIcon from '@mui/icons-material/Article';\nimport SmartToyIcon from '@mui/icons-material/SmartToy';\nimport BuildIcon from '@mui/icons-material/Build';\nimport AccountTreeIcon from '@mui/icons-material/AccountTree';\nimport type { AiAssetSummary, AssetType, AiTool } from '@julianpedro/plugin-dev-ai-hub-common';\nimport { ToolIcon } from '../ToolIcon';\n\nconst POPULAR_THRESHOLD = 5;\nconst NEW_DAYS_MS = 14 * 24 * 60 * 60 * 1000;\n\nconst TOOL_LABELS: Record<AiTool, string> = {\n 'all': 'Universal',\n 'claude-code': 'Claude Code',\n 'github-copilot': 'GitHub Copilot',\n 'google-gemini': 'Google Gemini',\n 'cursor': 'Cursor',\n};\n\nconst TYPE_CONFIG: Record<AssetType, { label: string; color: string; Icon: ElementType }> = {\n instruction: { label: 'Instruction', color: '#2563EB', Icon: ArticleIcon },\n agent: { label: 'Agent', color: '#7C3AED', Icon: SmartToyIcon },\n skill: { label: 'Skill', color: '#059669', Icon: BuildIcon },\n workflow: { label: 'Workflow', color: '#D97706', Icon: AccountTreeIcon },\n};\n\ninterface AssetCardProps {\n asset: AiAssetSummary;\n onView: (id: string) => void;\n onInstall: (id: string) => void;\n}\n\nexport function AssetCard({ asset, onView, onInstall }: AssetCardProps) {\n const theme = useTheme();\n const isDark = (theme.palette as any).mode === 'dark' || (theme.palette as any).type === 'dark';\n const cfg = TYPE_CONFIG[asset.type];\n const TypeIcon = cfg.Icon;\n const isPopular = asset.installCount >= POPULAR_THRESHOLD;\n const isNew = Date.now() - new Date(asset.updatedAt).getTime() < NEW_DAYS_MS;\n\n return (\n <Card\n variant=\"outlined\"\n sx={{\n height: '100%',\n display: 'flex',\n flexDirection: 'column',\n borderRadius: 2,\n border: '1px solid',\n borderColor: 'divider',\n borderLeft: `3px solid ${cfg.color}`,\n transition: 'all 0.18s ease',\n '&:hover': {\n boxShadow: `0 6px 24px ${cfg.color}30`,\n borderColor: cfg.color,\n transform: 'translateY(-2px)',\n },\n }}\n >\n <CardContent sx={{ p: 1.5, pb: '0 !important', flex: 1 }}>\n {/* Header */}\n <Box sx={{ display: 'flex', alignItems: 'flex-start', gap: 1, mb: 1 }}>\n <Box\n sx={{\n width: 40,\n height: 40,\n borderRadius: 1.5,\n backgroundColor: alpha(cfg.color, 0.12),\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n flexShrink: 0,\n boxShadow: `0 2px 8px ${cfg.color}25`,\n }}\n >\n {asset.icon ? (\n <Box\n component=\"img\"\n src={asset.icon}\n alt={asset.label ?? asset.name}\n sx={{ width: 26, height: 26, objectFit: 'contain' }}\n onError={e => { (e.target as HTMLImageElement).style.display = 'none'; }}\n />\n ) : (\n <TypeIcon sx={{ color: cfg.color, fontSize: '1.3rem' }} />\n )}\n </Box>\n\n <Box sx={{ flex: 1, minWidth: 0 }}>\n <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5, mb: 0.2 }}>\n <Typography variant=\"body2\" fontWeight={700} noWrap title={asset.label ?? asset.name} sx={{ lineHeight: 1.2, flex: 1 }}>\n {asset.label ?? asset.name}\n </Typography>\n {isNew && (\n <Chip\n label=\"New\"\n size=\"small\"\n sx={{\n height: 18,\n fontSize: '0.6rem',\n fontWeight: 700,\n backgroundColor: alpha('#059669', isDark ? 0.25 : 0.14),\n backdropFilter: 'blur(8px)',\n color: isDark ? '#fff' : '#059669',\n border: '1px solid',\n borderColor: alpha('#059669', isDark ? 0.5 : 0.3),\n borderRadius: 1,\n flexShrink: 0,\n '& .MuiChip-label': { px: '6px' },\n }}\n />\n )}\n </Box>\n <Typography variant=\"caption\" sx={{ color: cfg.color, fontWeight: 600 }}>\n {cfg.label}\n </Typography>\n </Box>\n </Box>\n\n {/* Description */}\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{\n mb: 1,\n display: '-webkit-box',\n WebkitLineClamp: 2,\n WebkitBoxOrient: 'vertical',\n overflow: 'hidden',\n lineHeight: 1.4,\n }}\n >\n {asset.description}\n </Typography>\n\n {/* Tools */}\n <Box sx={{ display: 'flex', gap: 0.5, flexWrap: 'wrap', mb: asset.tags.length > 0 ? 0.75 : 0 }}>\n {asset.tools.map(tool => (\n <Chip\n key={tool}\n icon={<ToolIcon tool={tool as AiTool} sx={{ fontSize: '0.75rem !important' }} />}\n label={TOOL_LABELS[tool as AiTool] ?? tool}\n size=\"small\"\n sx={{\n height: 18,\n fontSize: '0.65rem',\n fontWeight: 600,\n backgroundColor: 'action.hover',\n color: 'text.secondary',\n borderRadius: 1,\n '& .MuiChip-icon': { ml: '4px' },\n }}\n />\n ))}\n </Box>\n\n {/* Tags */}\n {asset.tags.length > 0 && (\n <Box sx={{ display: 'flex', gap: 0.5, flexWrap: 'wrap' }}>\n {asset.tags.slice(0, 3).map(tag => (\n <Chip\n key={tag}\n label={`#${tag}`}\n size=\"small\"\n sx={{\n height: 16,\n fontSize: '0.6rem',\n color: 'text.disabled',\n backgroundColor: 'transparent',\n border: '1px solid',\n borderColor: 'divider',\n borderRadius: 1,\n }}\n />\n ))}\n {asset.tags.length > 3 && (\n <Typography variant=\"caption\" color=\"text.disabled\" sx={{ alignSelf: 'center' }}>\n +{asset.tags.length - 3}\n </Typography>\n )}\n </Box>\n )}\n </CardContent>\n\n <CardActions sx={{ px: 1.5, py: 1, justifyContent: 'space-between', mt: 'auto', borderTop: '1px solid', borderColor: 'divider' }}>\n <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>\n <Typography variant=\"caption\" color=\"text.disabled\" sx={{ fontSize: '0.65rem' }}>\n v{asset.version} ยท {asset.author}\n </Typography>\n {asset.installCount > 0 && (\n <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.25 }}>\n <Typography sx={{ fontSize: '0.65rem', lineHeight: 1 }}>\n {isPopular ? '๐Ÿ”ฅ' : 'โ†“'}\n </Typography>\n <Typography variant=\"caption\" color=\"text.disabled\" sx={{ fontSize: '0.65rem' }}>\n {asset.installCount}\n </Typography>\n </Box>\n )}\n </Box>\n <Box sx={{ display: 'flex', gap: 0.25 }}>\n <Tooltip title=\"Install in editor\">\n <IconButton size=\"small\" onClick={() => onInstall(asset.id)} color=\"primary\">\n <DownloadIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n <Tooltip title=\"View details\">\n <IconButton size=\"small\" onClick={() => onView(asset.id)}>\n <OpenInNewIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n </Box>\n </CardActions>\n </Card>\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AssetCard.esm.js","sources":["../../../src/components/AssetCard/AssetCard.tsx"],"sourcesContent":["import type { ElementType } from 'react';\nimport { alpha, useTheme } from '@mui/material/styles';\nimport Box from '@mui/material/Box';\nimport Card from '@mui/material/Card';\nimport CardActions from '@mui/material/CardActions';\nimport CardContent from '@mui/material/CardContent';\nimport Chip from '@mui/material/Chip';\nimport IconButton from '@mui/material/IconButton';\nimport Tooltip from '@mui/material/Tooltip';\nimport Typography from '@mui/material/Typography';\nimport OpenInNewIcon from '@mui/icons-material/OpenInNew';\nimport DownloadIcon from '@mui/icons-material/Download';\nimport HelpOutlineIcon from '@mui/icons-material/HelpOutline';\nimport ArticleIcon from '@mui/icons-material/Article';\nimport SmartToyIcon from '@mui/icons-material/SmartToy';\nimport BuildIcon from '@mui/icons-material/Build';\nimport AccountTreeIcon from '@mui/icons-material/AccountTree';\nimport ChatIcon from '@mui/icons-material/Chat';\nimport Inventory2Icon from '@mui/icons-material/Inventory2';\nimport StorageIcon from '@mui/icons-material/Storage';\nimport { useTranslationRef } from '@backstage/frontend-plugin-api';\nimport type { AiAssetSummary, AssetType, AiTool, McpCatalogEntry, McpRequirement } from '@julianpedro/plugin-dev-ai-hub-common';\nimport { ToolIcon } from '../ToolIcon';\nimport { ModelBadge } from '../ModelIcon';\nimport { devAiHubTranslationRef } from '../../translation';\nimport { useTypeConfig } from '../../hooks';\n\nconst POPULAR_THRESHOLD = 5;\nconst NEW_DAYS_MS = 14 * 24 * 60 * 60 * 1000;\nconst UPDATED_DAYS_MS = 7 * 24 * 60 * 60 * 1000;\n\nconst TOOL_LABELS: Record<AiTool, string> = {\n 'all': 'Universal',\n 'claude-code': 'Claude Code',\n 'github-copilot': 'GitHub Copilot',\n 'google-gemini': 'Google Gemini',\n 'cursor': 'Cursor',\n};\n\nconst TYPE_LABELS: Record<AssetType, string> = {\n instruction: 'Instruction',\n agent: 'Agent',\n skill: 'Skill',\n workflow: 'Workflow',\n prompt: 'Prompt',\n bundle: 'Bundle',\n};\n\nconst TYPE_ICONS: Record<AssetType, ElementType> = {\n instruction: ArticleIcon,\n agent: SmartToyIcon,\n skill: BuildIcon,\n workflow: AccountTreeIcon,\n prompt: ChatIcon,\n bundle: Inventory2Icon,\n};\n\nfunction resolveMcp(req: McpRequirement, catalog: McpCatalogEntry[]): { name: string; icon?: string } {\n const entry = catalog.find(e => e.id === req.id);\n return {\n name: req.name ?? entry?.name ?? req.id,\n icon: req.icon ?? entry?.icon,\n };\n}\n\ninterface AssetCardProps {\n asset: AiAssetSummary;\n onView: (id: string) => void;\n onInstall: (id: string) => void;\n onHelp?: (id: string) => void;\n onOpenMcpCatalog?: () => void;\n mcpCatalog?: McpCatalogEntry[];\n}\n\nexport function AssetCard({ asset, onView, onInstall, onHelp, onOpenMcpCatalog, mcpCatalog = [] }: AssetCardProps) {\n const { t } = useTranslationRef(devAiHubTranslationRef);\n const theme = useTheme();\n const { typeColors } = useTypeConfig();\n const isDark = (theme.palette as any).mode === 'dark' || (theme.palette as any).type === 'dark';\n const color = typeColors[asset.type];\n const TypeIcon = TYPE_ICONS[asset.type];\n const isPopular = asset.installCount >= POPULAR_THRESHOLD;\n const isNew = Date.now() - new Date(asset.createdAt).getTime() < NEW_DAYS_MS;\n const isUpdated = !isNew && Date.now() - new Date(asset.updatedAt).getTime() < UPDATED_DAYS_MS;\n\n return (\n <Card\n variant=\"outlined\"\n sx={{\n height: '100%',\n display: 'flex',\n flexDirection: 'column',\n borderRadius: 2,\n border: '1px solid',\n borderColor: 'divider',\n borderLeft: `3px solid ${color}`,\n transition: 'all 0.18s ease',\n '&:hover': {\n boxShadow: `0 6px 24px ${color}30`,\n borderColor: color,\n transform: 'translateY(-2px)',\n },\n }}\n >\n <CardContent sx={{ p: 1.5, pb: '0 !important', flex: 1 }}>\n {/* Header */}\n <Box sx={{ display: 'flex', alignItems: 'flex-start', gap: 1, mb: 1 }}>\n <Box\n sx={{\n width: 40,\n height: 40,\n borderRadius: 1.5,\n backgroundColor: alpha(color, 0.12),\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n flexShrink: 0,\n boxShadow: `0 2px 8px ${color}25`,\n }}\n >\n {asset.icon ? (\n <Box\n component=\"img\"\n src={asset.icon}\n alt={asset.label ?? asset.name}\n sx={{ width: 26, height: 26, objectFit: 'contain' }}\n onError={e => { (e.target as HTMLImageElement).style.display = 'none'; }}\n />\n ) : (\n <TypeIcon sx={{ color, fontSize: '1.3rem' }} />\n )}\n </Box>\n\n <Box sx={{ flex: 1, minWidth: 0 }}>\n <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5, mb: 0.2 }}>\n <Typography variant=\"body2\" fontWeight={700} noWrap title={asset.label ?? asset.name} sx={{ lineHeight: 1.2, flex: 1 }}>\n {asset.label ?? asset.name}\n </Typography>\n {isNew && (\n <Chip\n label={t('assetCard.newBadge')}\n size=\"small\"\n sx={{\n height: 18,\n fontSize: '0.6rem',\n fontWeight: 700,\n backgroundColor: alpha('#059669', isDark ? 0.25 : 0.14),\n backdropFilter: 'blur(8px)',\n color: isDark ? '#6ee7b7' : '#059669',\n border: '1px solid',\n borderColor: alpha('#059669', isDark ? 0.5 : 0.3),\n borderRadius: 1,\n flexShrink: 0,\n '& .MuiChip-label': { px: '6px' },\n }}\n />\n )}\n {isUpdated && (\n <Chip\n label={t('assetCard.updatedBadge')}\n size=\"small\"\n sx={{\n height: 18,\n fontSize: '0.6rem',\n fontWeight: 700,\n backgroundColor: alpha('#D97706', isDark ? 0.25 : 0.12),\n color: isDark ? '#fcd34d' : '#D97706',\n border: '1px solid',\n borderColor: alpha('#D97706', isDark ? 0.5 : 0.3),\n borderRadius: 1,\n flexShrink: 0,\n '& .MuiChip-label': { px: '6px' },\n }}\n />\n )}\n </Box>\n <Box>\n <Typography variant=\"caption\" sx={{ color, fontWeight: 600 }}>\n {TYPE_LABELS[asset.type]}\n </Typography>\n {asset.type === 'agent' && asset.model && (\n <Box sx={{ mt: 0.3 }}>\n <ModelBadge model={asset.model} />\n </Box>\n )}\n </Box>\n </Box>\n </Box>\n\n {/* Description */}\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n title={asset.description}\n sx={{\n mb: 1,\n display: '-webkit-box',\n WebkitLineClamp: 2,\n WebkitBoxOrient: 'vertical',\n overflow: 'hidden',\n lineHeight: 1.4,\n }}\n >\n {asset.description}\n </Typography>\n\n {/* Tools */}\n <Box sx={{ display: 'flex', gap: 0.5, flexWrap: 'wrap', mb: asset.tags.length > 0 ? 0.75 : 0 }}>\n {asset.tools.map(tool => (\n <Chip\n key={tool}\n icon={<ToolIcon tool={tool as AiTool} sx={{ fontSize: '0.75rem !important' }} />}\n label={TOOL_LABELS[tool as AiTool] ?? tool}\n size=\"small\"\n sx={{\n height: 18,\n fontSize: '0.65rem',\n fontWeight: 600,\n backgroundColor: 'action.hover',\n color: 'text.secondary',\n borderRadius: 1,\n '& .MuiChip-icon': { ml: '4px' },\n }}\n />\n ))}\n </Box>\n\n {/* Tags */}\n {asset.tags.length > 0 && (\n <Box sx={{ display: 'flex', gap: 0.5, flexWrap: 'wrap' }}>\n {asset.tags.slice(0, 3).map(tag => (\n <Chip\n key={tag}\n label={`#${tag}`}\n size=\"small\"\n sx={{\n height: 16,\n fontSize: '0.6rem',\n color: 'text.disabled',\n backgroundColor: 'transparent',\n border: '1px solid',\n borderColor: 'divider',\n borderRadius: 1,\n }}\n />\n ))}\n {asset.tags.length > 3 && (\n <Typography variant=\"caption\" color=\"text.disabled\" sx={{ alignSelf: 'center' }}>\n +{asset.tags.length - 3}\n </Typography>\n )}\n </Box>\n )}\n\n {/* Required MCPs โ€” circular icon-only badges, below tags */}\n {asset.mcps && asset.mcps.length > 0 && (\n <Box sx={{ mt: 0.75, mb: 1 }}>\n <Typography variant=\"caption\" color=\"text.disabled\" sx={{ fontSize: '0.6rem', fontWeight: 600, textTransform: 'uppercase', letterSpacing: 0.4, display: 'block', mb: 0.5 }}>\n {t('assetCard.mcpsRequired')}\n </Typography>\n <Box sx={{ display: 'flex', gap: 0.5, flexWrap: 'wrap' }}>\n {asset.mcps.map(req => {\n const { name, icon } = resolveMcp(req, mcpCatalog);\n return (\n <Box\n key={req.id}\n title={name}\n onClick={onOpenMcpCatalog}\n sx={{\n width: 26,\n height: 26,\n borderRadius: '50%',\n backgroundColor: 'action.hover',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n flexShrink: 0,\n overflow: 'hidden',\n cursor: onOpenMcpCatalog ? 'pointer' : 'default',\n transition: 'background-color 0.15s ease',\n '&:hover': onOpenMcpCatalog ? { backgroundColor: 'action.selected' } : {},\n }}\n >\n {icon ? (\n <Box\n component=\"img\"\n src={icon}\n alt={name}\n sx={{ width: 18, height: 18, objectFit: 'contain' }}\n onError={e => { (e.target as HTMLImageElement).style.display = 'none'; }}\n />\n ) : (\n <StorageIcon sx={{ fontSize: '0.9rem', color: 'text.secondary' }} />\n )}\n </Box>\n );\n })}\n </Box>\n </Box>\n )}\n </CardContent>\n\n <CardActions sx={{ px: 1.5, py: 1, justifyContent: 'space-between', mt: 'auto', borderTop: '1px solid', borderColor: 'divider' }}>\n <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>\n <Typography variant=\"caption\" color=\"text.disabled\" sx={{ fontSize: '0.65rem' }}>\n {asset.type === 'bundle' && asset.itemCount !== undefined\n ? t('assetCard.bundleFooter', { itemCount: String(asset.itemCount), author: asset.author })\n : t('assetCard.versionFooter', { version: asset.version, author: asset.author })}\n </Typography>\n {asset.installCount > 0 && (\n <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.25 }}>\n <Typography sx={{ fontSize: '0.65rem', lineHeight: 1 }}>\n {isPopular ? '๐Ÿ”ฅ' : 'โ†“'}\n </Typography>\n <Typography variant=\"caption\" color=\"text.disabled\" sx={{ fontSize: '0.65rem' }}>\n {asset.installCount}\n </Typography>\n </Box>\n )}\n </Box>\n <Box sx={{ display: 'flex', gap: 0.25 }}>\n <Tooltip title={t('assetCard.installTooltip')}>\n <IconButton size=\"small\" onClick={() => onInstall(asset.id)} color=\"primary\">\n <DownloadIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n <Tooltip title={t('assetCard.detailsTooltip')}>\n <IconButton size=\"small\" onClick={() => onView(asset.id)}>\n <OpenInNewIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n {asset.helpText && onHelp && (\n <Tooltip title={t('assetCard.helpTooltip')}>\n <IconButton size=\"small\" onClick={() => onHelp(asset.id)}>\n <HelpOutlineIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n )}\n </Box>\n </CardActions>\n </Card>\n );\n}"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,MAAM,iBAAA,GAAoB,CAAA;AAC1B,MAAM,WAAA,GAAkB,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,GAAA;AAC5C,MAAM,eAAA,GAAmB,CAAA,GAAI,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,GAAA;AAE5C,MAAM,WAAA,GAAsC;AAAA,EAC1C,KAAA,EAAkB,WAAA;AAAA,EAClB,aAAA,EAAkB,aAAA;AAAA,EAClB,gBAAA,EAAkB,gBAAA;AAAA,EAClB,eAAA,EAAkB,eAAA;AAAA,EAClB,QAAA,EAAkB;AACpB,CAAA;AAEA,MAAM,WAAA,GAAyC;AAAA,EAC7C,WAAA,EAAa,aAAA;AAAA,EACb,KAAA,EAAa,OAAA;AAAA,EACb,KAAA,EAAa,OAAA;AAAA,EACb,QAAA,EAAa,UAAA;AAAA,EACb,MAAA,EAAa,QAAA;AAAA,EACb,MAAA,EAAa;AACf,CAAA;AAEA,MAAM,UAAA,GAA6C;AAAA,EACjD,WAAA,EAAa,WAAA;AAAA,EACb,KAAA,EAAa,YAAA;AAAA,EACb,KAAA,EAAa,SAAA;AAAA,EACb,QAAA,EAAa,eAAA;AAAA,EACb,MAAA,EAAa,QAAA;AAAA,EACb,MAAA,EAAa;AACf,CAAA;AAEA,SAAS,UAAA,CAAW,KAAqB,OAAA,EAA6D;AACpG,EAAA,MAAM,QAAQ,OAAA,CAAQ,IAAA,CAAK,OAAK,CAAA,CAAE,EAAA,KAAO,IAAI,EAAE,CAAA;AAC/C,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,GAAA,CAAI,IAAA,IAAQ,KAAA,EAAO,QAAQ,GAAA,CAAI,EAAA;AAAA,IACrC,IAAA,EAAM,GAAA,CAAI,IAAA,IAAQ,KAAA,EAAO;AAAA,GAC3B;AACF;AAWO,SAAS,SAAA,CAAU,EAAE,KAAA,EAAO,MAAA,EAAQ,SAAA,EAAW,QAAQ,gBAAA,EAAkB,UAAA,GAAa,EAAC,EAAE,EAAmB;AACjH,EAAA,MAAM,EAAE,CAAA,EAAE,GAAI,iBAAA,CAAkB,sBAAsB,CAAA;AACtD,EAAA,MAAM,QAAQ,QAAA,EAAS;AACvB,EAAA,MAAM,EAAE,UAAA,EAAW,GAAI,aAAA,EAAc;AACrC,EAAA,MAAM,SAAU,KAAA,CAAM,OAAA,CAAgB,SAAS,MAAA,IAAW,KAAA,CAAM,QAAgB,IAAA,KAAS,MAAA;AACzF,EAAA,MAAM,KAAA,GAAQ,UAAA,CAAW,KAAA,CAAM,IAAI,CAAA;AACnC,EAAA,MAAM,QAAA,GAAW,UAAA,CAAW,KAAA,CAAM,IAAI,CAAA;AACtC,EAAA,MAAM,SAAA,GAAY,MAAM,YAAA,IAAgB,iBAAA;AACxC,EAAA,MAAM,KAAA,GAAY,IAAA,CAAK,GAAA,EAAI,GAAI,IAAI,KAAK,KAAA,CAAM,SAAS,CAAA,CAAE,OAAA,EAAQ,GAAI,WAAA;AACrE,EAAA,MAAM,SAAA,GAAY,CAAC,KAAA,IAAS,IAAA,CAAK,GAAA,EAAI,GAAI,IAAI,IAAA,CAAK,KAAA,CAAM,SAAS,CAAA,CAAE,OAAA,EAAQ,GAAI,eAAA;AAE/E,EAAA,uBACE,IAAA;AAAA,IAAC,IAAA;AAAA,IAAA;AAAA,MACC,OAAA,EAAQ,UAAA;AAAA,MACR,EAAA,EAAI;AAAA,QACF,MAAA,EAAQ,MAAA;AAAA,QACR,OAAA,EAAS,MAAA;AAAA,QACT,aAAA,EAAe,QAAA;AAAA,QACf,YAAA,EAAc,CAAA;AAAA,QACd,MAAA,EAAQ,WAAA;AAAA,QACR,WAAA,EAAa,SAAA;AAAA,QACb,UAAA,EAAY,aAAa,KAAK,CAAA,CAAA;AAAA,QAC9B,UAAA,EAAY,gBAAA;AAAA,QACZ,SAAA,EAAW;AAAA,UACT,SAAA,EAAW,cAAc,KAAK,CAAA,EAAA,CAAA;AAAA,UAC9B,WAAA,EAAa,KAAA;AAAA,UACb,SAAA,EAAW;AAAA;AACb,OACF;AAAA,MAEA,QAAA,EAAA;AAAA,wBAAA,IAAA,CAAC,WAAA,EAAA,EAAY,IAAI,EAAE,CAAA,EAAG,KAAK,EAAA,EAAI,cAAA,EAAgB,IAAA,EAAM,CAAA,EAAE,EAErD,QAAA,EAAA;AAAA,0BAAA,IAAA,CAAC,GAAA,EAAA,EAAI,EAAA,EAAI,EAAE,OAAA,EAAS,MAAA,EAAQ,UAAA,EAAY,YAAA,EAAc,GAAA,EAAK,CAAA,EAAG,EAAA,EAAI,CAAA,EAAE,EAClE,QAAA,EAAA;AAAA,4BAAA,GAAA;AAAA,cAAC,GAAA;AAAA,cAAA;AAAA,gBACC,EAAA,EAAI;AAAA,kBACF,KAAA,EAAO,EAAA;AAAA,kBACP,MAAA,EAAQ,EAAA;AAAA,kBACR,YAAA,EAAc,GAAA;AAAA,kBACd,eAAA,EAAiB,KAAA,CAAM,KAAA,EAAO,IAAI,CAAA;AAAA,kBAClC,OAAA,EAAS,MAAA;AAAA,kBACT,UAAA,EAAY,QAAA;AAAA,kBACZ,cAAA,EAAgB,QAAA;AAAA,kBAChB,UAAA,EAAY,CAAA;AAAA,kBACZ,SAAA,EAAW,aAAa,KAAK,CAAA,EAAA;AAAA,iBAC/B;AAAA,gBAEC,gBAAM,IAAA,mBACL,GAAA;AAAA,kBAAC,GAAA;AAAA,kBAAA;AAAA,oBACC,SAAA,EAAU,KAAA;AAAA,oBACV,KAAK,KAAA,CAAM,IAAA;AAAA,oBACX,GAAA,EAAK,KAAA,CAAM,KAAA,IAAS,KAAA,CAAM,IAAA;AAAA,oBAC1B,IAAI,EAAE,KAAA,EAAO,IAAI,MAAA,EAAQ,EAAA,EAAI,WAAW,SAAA,EAAU;AAAA,oBAClD,SAAS,CAAA,CAAA,KAAK;AAAE,sBAAC,CAAA,CAAE,MAAA,CAA4B,KAAA,CAAM,OAAA,GAAU,MAAA;AAAA,oBAAQ;AAAA;AAAA,iBACzE,uBAEC,QAAA,EAAA,EAAS,EAAA,EAAI,EAAE,KAAA,EAAO,QAAA,EAAU,UAAS,EAAG;AAAA;AAAA,aAEjD;AAAA,4BAEA,IAAA,CAAC,OAAI,EAAA,EAAI,EAAE,MAAM,CAAA,EAAG,QAAA,EAAU,GAAE,EAC9B,QAAA,EAAA;AAAA,8BAAA,IAAA,CAAC,GAAA,EAAA,EAAI,EAAA,EAAI,EAAE,OAAA,EAAS,MAAA,EAAQ,UAAA,EAAY,QAAA,EAAU,GAAA,EAAK,GAAA,EAAK,EAAA,EAAI,GAAA,EAAI,EAClE,QAAA,EAAA;AAAA,gCAAA,GAAA,CAAC,UAAA,EAAA,EAAW,SAAQ,OAAA,EAAQ,UAAA,EAAY,KAAK,MAAA,EAAM,IAAA,EAAC,KAAA,EAAO,KAAA,CAAM,KAAA,IAAS,KAAA,CAAM,MAAM,EAAA,EAAI,EAAE,YAAY,GAAA,EAAK,IAAA,EAAM,GAAE,EAClH,QAAA,EAAA,KAAA,CAAM,KAAA,IAAS,KAAA,CAAM,IAAA,EACxB,CAAA;AAAA,gBACC,KAAA,oBACC,GAAA;AAAA,kBAAC,IAAA;AAAA,kBAAA;AAAA,oBACC,KAAA,EAAO,EAAE,oBAAoB,CAAA;AAAA,oBAC7B,IAAA,EAAK,OAAA;AAAA,oBACL,EAAA,EAAI;AAAA,sBACF,MAAA,EAAQ,EAAA;AAAA,sBACR,QAAA,EAAU,QAAA;AAAA,sBACV,UAAA,EAAY,GAAA;AAAA,sBACZ,eAAA,EAAiB,KAAA,CAAM,SAAA,EAAW,MAAA,GAAS,OAAO,IAAI,CAAA;AAAA,sBACtD,cAAA,EAAgB,WAAA;AAAA,sBAChB,KAAA,EAAO,SAAS,SAAA,GAAY,SAAA;AAAA,sBAC5B,MAAA,EAAQ,WAAA;AAAA,sBACR,WAAA,EAAa,KAAA,CAAM,SAAA,EAAW,MAAA,GAAS,MAAM,GAAG,CAAA;AAAA,sBAChD,YAAA,EAAc,CAAA;AAAA,sBACd,UAAA,EAAY,CAAA;AAAA,sBACZ,kBAAA,EAAoB,EAAE,EAAA,EAAI,KAAA;AAAM;AAClC;AAAA,iBACF;AAAA,gBAED,SAAA,oBACC,GAAA;AAAA,kBAAC,IAAA;AAAA,kBAAA;AAAA,oBACC,KAAA,EAAO,EAAE,wBAAwB,CAAA;AAAA,oBACjC,IAAA,EAAK,OAAA;AAAA,oBACL,EAAA,EAAI;AAAA,sBACF,MAAA,EAAQ,EAAA;AAAA,sBACR,QAAA,EAAU,QAAA;AAAA,sBACV,UAAA,EAAY,GAAA;AAAA,sBACZ,eAAA,EAAiB,KAAA,CAAM,SAAA,EAAW,MAAA,GAAS,OAAO,IAAI,CAAA;AAAA,sBACtD,KAAA,EAAO,SAAS,SAAA,GAAY,SAAA;AAAA,sBAC5B,MAAA,EAAQ,WAAA;AAAA,sBACR,WAAA,EAAa,KAAA,CAAM,SAAA,EAAW,MAAA,GAAS,MAAM,GAAG,CAAA;AAAA,sBAChD,YAAA,EAAc,CAAA;AAAA,sBACd,UAAA,EAAY,CAAA;AAAA,sBACZ,kBAAA,EAAoB,EAAE,EAAA,EAAI,KAAA;AAAM;AAClC;AAAA;AACF,eAAA,EAEJ,CAAA;AAAA,mCACC,GAAA,EAAA,EACC,QAAA,EAAA;AAAA,gCAAA,GAAA,CAAC,UAAA,EAAA,EAAW,OAAA,EAAQ,SAAA,EAAU,EAAA,EAAI,EAAE,KAAA,EAAO,UAAA,EAAY,GAAA,EAAI,EACxD,QAAA,EAAA,WAAA,CAAY,KAAA,CAAM,IAAI,CAAA,EACzB,CAAA;AAAA,gBACC,MAAM,IAAA,KAAS,OAAA,IAAW,KAAA,CAAM,KAAA,wBAC9B,GAAA,EAAA,EAAI,EAAA,EAAI,EAAE,EAAA,EAAI,KAAI,EACjB,QAAA,kBAAA,GAAA,CAAC,cAAW,KAAA,EAAO,KAAA,CAAM,OAAO,CAAA,EAClC;AAAA,eAAA,EAEJ;AAAA,aAAA,EACF;AAAA,WAAA,EACF,CAAA;AAAA,0BAGA,GAAA;AAAA,YAAC,UAAA;AAAA,YAAA;AAAA,cACC,OAAA,EAAQ,SAAA;AAAA,cACR,KAAA,EAAM,gBAAA;AAAA,cACN,OAAO,KAAA,CAAM,WAAA;AAAA,cACb,EAAA,EAAI;AAAA,gBACF,EAAA,EAAI,CAAA;AAAA,gBACJ,OAAA,EAAS,aAAA;AAAA,gBACT,eAAA,EAAiB,CAAA;AAAA,gBACjB,eAAA,EAAiB,UAAA;AAAA,gBACjB,QAAA,EAAU,QAAA;AAAA,gBACV,UAAA,EAAY;AAAA,eACd;AAAA,cAEC,QAAA,EAAA,KAAA,CAAM;AAAA;AAAA,WACT;AAAA,0BAGA,GAAA,CAAC,OAAI,EAAA,EAAI,EAAE,SAAS,MAAA,EAAQ,GAAA,EAAK,KAAK,QAAA,EAAU,MAAA,EAAQ,IAAI,KAAA,CAAM,IAAA,CAAK,SAAS,CAAA,GAAI,IAAA,GAAO,GAAE,EAC1F,QAAA,EAAA,KAAA,CAAM,KAAA,CAAM,GAAA,CAAI,CAAA,IAAA,qBACf,GAAA;AAAA,YAAC,IAAA;AAAA,YAAA;AAAA,cAEC,IAAA,sBAAO,QAAA,EAAA,EAAS,IAAA,EAAsB,IAAI,EAAE,QAAA,EAAU,sBAAqB,EAAG,CAAA;AAAA,cAC9E,KAAA,EAAO,WAAA,CAAY,IAAc,CAAA,IAAK,IAAA;AAAA,cACtC,IAAA,EAAK,OAAA;AAAA,cACL,EAAA,EAAI;AAAA,gBACF,MAAA,EAAQ,EAAA;AAAA,gBACR,QAAA,EAAU,SAAA;AAAA,gBACV,UAAA,EAAY,GAAA;AAAA,gBACZ,eAAA,EAAiB,cAAA;AAAA,gBACjB,KAAA,EAAO,gBAAA;AAAA,gBACP,YAAA,EAAc,CAAA;AAAA,gBACd,iBAAA,EAAmB,EAAE,EAAA,EAAI,KAAA;AAAM;AACjC,aAAA;AAAA,YAZK;AAAA,WAcR,CAAA,EACH,CAAA;AAAA,UAGC,KAAA,CAAM,IAAA,CAAK,MAAA,GAAS,CAAA,yBAClB,GAAA,EAAA,EAAI,EAAA,EAAI,EAAE,OAAA,EAAS,MAAA,EAAQ,GAAA,EAAK,GAAA,EAAK,QAAA,EAAU,QAAO,EACpD,QAAA,EAAA;AAAA,YAAA,KAAA,CAAM,KAAK,KAAA,CAAM,CAAA,EAAG,CAAC,CAAA,CAAE,IAAI,CAAA,GAAA,qBAC1B,GAAA;AAAA,cAAC,IAAA;AAAA,cAAA;AAAA,gBAEC,KAAA,EAAO,IAAI,GAAG,CAAA,CAAA;AAAA,gBACd,IAAA,EAAK,OAAA;AAAA,gBACL,EAAA,EAAI;AAAA,kBACF,MAAA,EAAQ,EAAA;AAAA,kBACR,QAAA,EAAU,QAAA;AAAA,kBACV,KAAA,EAAO,eAAA;AAAA,kBACP,eAAA,EAAiB,aAAA;AAAA,kBACjB,MAAA,EAAQ,WAAA;AAAA,kBACR,WAAA,EAAa,SAAA;AAAA,kBACb,YAAA,EAAc;AAAA;AAChB,eAAA;AAAA,cAXK;AAAA,aAaR,CAAA;AAAA,YACA,KAAA,CAAM,IAAA,CAAK,MAAA,GAAS,CAAA,yBAClB,UAAA,EAAA,EAAW,OAAA,EAAQ,SAAA,EAAU,KAAA,EAAM,eAAA,EAAgB,EAAA,EAAI,EAAE,SAAA,EAAW,UAAS,EAAG,QAAA,EAAA;AAAA,cAAA,GAAA;AAAA,cAC7E,KAAA,CAAM,KAAK,MAAA,GAAS;AAAA,aAAA,EACxB;AAAA,WAAA,EAEJ,CAAA;AAAA,UAID,KAAA,CAAM,IAAA,IAAQ,KAAA,CAAM,IAAA,CAAK,SAAS,CAAA,oBACjC,IAAA,CAAC,GAAA,EAAA,EAAI,EAAA,EAAI,EAAE,EAAA,EAAI,IAAA,EAAM,EAAA,EAAI,GAAE,EACzB,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,UAAA,EAAA,EAAW,SAAQ,SAAA,EAAU,KAAA,EAAM,iBAAgB,EAAA,EAAI,EAAE,QAAA,EAAU,QAAA,EAAU,UAAA,EAAY,GAAA,EAAK,eAAe,WAAA,EAAa,aAAA,EAAe,KAAK,OAAA,EAAS,OAAA,EAAS,IAAI,GAAA,EAAI,EACtK,QAAA,EAAA,CAAA,CAAE,wBAAwB,CAAA,EAC7B,CAAA;AAAA,4BACA,GAAA,CAAC,GAAA,EAAA,EAAI,EAAA,EAAI,EAAE,SAAS,MAAA,EAAQ,GAAA,EAAK,GAAA,EAAK,QAAA,EAAU,MAAA,EAAO,EACpD,QAAA,EAAA,KAAA,CAAM,IAAA,CAAK,IAAI,CAAA,GAAA,KAAO;AACrB,cAAA,MAAM,EAAE,IAAA,EAAM,IAAA,EAAK,GAAI,UAAA,CAAW,KAAK,UAAU,CAAA;AACjD,cAAA,uBACE,GAAA;AAAA,gBAAC,GAAA;AAAA,gBAAA;AAAA,kBAEC,KAAA,EAAO,IAAA;AAAA,kBACP,OAAA,EAAS,gBAAA;AAAA,kBACT,EAAA,EAAI;AAAA,oBACF,KAAA,EAAO,EAAA;AAAA,oBACP,MAAA,EAAQ,EAAA;AAAA,oBACR,YAAA,EAAc,KAAA;AAAA,oBACd,eAAA,EAAiB,cAAA;AAAA,oBACjB,OAAA,EAAS,MAAA;AAAA,oBACT,UAAA,EAAY,QAAA;AAAA,oBACZ,cAAA,EAAgB,QAAA;AAAA,oBAChB,UAAA,EAAY,CAAA;AAAA,oBACZ,QAAA,EAAU,QAAA;AAAA,oBACV,MAAA,EAAQ,mBAAmB,SAAA,GAAY,SAAA;AAAA,oBACvC,UAAA,EAAY,6BAAA;AAAA,oBACZ,WAAW,gBAAA,GAAmB,EAAE,eAAA,EAAiB,iBAAA,KAAsB;AAAC,mBAC1E;AAAA,kBAEC,QAAA,EAAA,IAAA,mBACC,GAAA;AAAA,oBAAC,GAAA;AAAA,oBAAA;AAAA,sBACC,SAAA,EAAU,KAAA;AAAA,sBACV,GAAA,EAAK,IAAA;AAAA,sBACL,GAAA,EAAK,IAAA;AAAA,sBACL,IAAI,EAAE,KAAA,EAAO,IAAI,MAAA,EAAQ,EAAA,EAAI,WAAW,SAAA,EAAU;AAAA,sBAClD,SAAS,CAAA,CAAA,KAAK;AAAE,wBAAC,CAAA,CAAE,MAAA,CAA4B,KAAA,CAAM,OAAA,GAAU,MAAA;AAAA,sBAAQ;AAAA;AAAA,mBACzE,uBAEC,WAAA,EAAA,EAAY,EAAA,EAAI,EAAE,QAAA,EAAU,QAAA,EAAU,KAAA,EAAO,gBAAA,EAAiB,EAAG;AAAA,iBAAA;AAAA,gBA3B/D,GAAA,CAAI;AAAA,eA6BX;AAAA,YAEJ,CAAC,CAAA,EACH;AAAA,WAAA,EACF;AAAA,SAAA,EAEJ,CAAA;AAAA,6BAEC,WAAA,EAAA,EAAY,EAAA,EAAI,EAAE,EAAA,EAAI,KAAK,EAAA,EAAI,CAAA,EAAG,cAAA,EAAgB,eAAA,EAAiB,IAAI,MAAA,EAAQ,SAAA,EAAW,WAAA,EAAa,WAAA,EAAa,WAAU,EAC7H,QAAA,EAAA;AAAA,0BAAA,IAAA,CAAC,GAAA,EAAA,EAAI,IAAI,EAAE,OAAA,EAAS,QAAQ,UAAA,EAAY,QAAA,EAAU,GAAA,EAAK,CAAA,EAAE,EACvD,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,cAAW,OAAA,EAAQ,SAAA,EAAU,KAAA,EAAM,eAAA,EAAgB,IAAI,EAAE,QAAA,EAAU,SAAA,EAAU,EAC3E,gBAAM,IAAA,KAAS,QAAA,IAAY,MAAM,SAAA,KAAc,MAAA,GAC5C,EAAE,wBAAA,EAA0B,EAAE,SAAA,EAAW,MAAA,CAAO,MAAM,SAAS,CAAA,EAAG,QAAQ,KAAA,CAAM,MAAA,EAAQ,CAAA,GACxF,CAAA,CAAE,yBAAA,EAA2B,EAAE,SAAS,KAAA,CAAM,OAAA,EAAS,QAAQ,KAAA,CAAM,MAAA,EAAQ,CAAA,EACnF,CAAA;AAAA,YACC,KAAA,CAAM,YAAA,GAAe,CAAA,oBACpB,IAAA,CAAC,GAAA,EAAA,EAAI,EAAA,EAAI,EAAE,OAAA,EAAS,MAAA,EAAQ,UAAA,EAAY,QAAA,EAAU,GAAA,EAAK,MAAK,EAC1D,QAAA,EAAA;AAAA,8BAAA,GAAA,CAAC,UAAA,EAAA,EAAW,EAAA,EAAI,EAAE,QAAA,EAAU,SAAA,EAAW,YAAY,CAAA,EAAE,EAClD,QAAA,EAAA,SAAA,GAAY,WAAA,GAAO,QAAA,EACtB,CAAA;AAAA,8BACA,GAAA,CAAC,UAAA,EAAA,EAAW,OAAA,EAAQ,SAAA,EAAU,KAAA,EAAM,eAAA,EAAgB,EAAA,EAAI,EAAE,QAAA,EAAU,SAAA,EAAU,EAC3E,QAAA,EAAA,KAAA,CAAM,YAAA,EACT;AAAA,aAAA,EACF;AAAA,WAAA,EAEJ,CAAA;AAAA,0BACA,IAAA,CAAC,OAAI,EAAA,EAAI,EAAE,SAAS,MAAA,EAAQ,GAAA,EAAK,MAAK,EACpC,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,OAAA,EAAA,EAAQ,OAAO,CAAA,CAAE,0BAA0B,GAC1C,QAAA,kBAAA,GAAA,CAAC,UAAA,EAAA,EAAW,IAAA,EAAK,OAAA,EAAQ,OAAA,EAAS,MAAM,UAAU,KAAA,CAAM,EAAE,GAAG,KAAA,EAAM,SAAA,EACjE,8BAAC,YAAA,EAAA,EAAa,QAAA,EAAS,OAAA,EAAQ,CAAA,EACjC,CAAA,EACF,CAAA;AAAA,4BACA,GAAA,CAAC,WAAQ,KAAA,EAAO,CAAA,CAAE,0BAA0B,CAAA,EAC1C,QAAA,kBAAA,GAAA,CAAC,cAAW,IAAA,EAAK,OAAA,EAAQ,SAAS,MAAM,MAAA,CAAO,MAAM,EAAE,CAAA,EACrD,8BAAC,aAAA,EAAA,EAAc,QAAA,EAAS,OAAA,EAAQ,CAAA,EAClC,CAAA,EACF,CAAA;AAAA,YACC,KAAA,CAAM,QAAA,IAAY,MAAA,oBACjB,GAAA,CAAC,OAAA,EAAA,EAAQ,OAAO,CAAA,CAAE,uBAAuB,CAAA,EACvC,QAAA,kBAAA,GAAA,CAAC,UAAA,EAAA,EAAW,IAAA,EAAK,SAAQ,OAAA,EAAS,MAAM,MAAA,CAAO,KAAA,CAAM,EAAE,CAAA,EACrD,8BAAC,eAAA,EAAA,EAAgB,QAAA,EAAS,OAAA,EAAQ,CAAA,EACpC,CAAA,EACF;AAAA,WAAA,EAEJ;AAAA,SAAA,EACF;AAAA;AAAA;AAAA,GACF;AAEJ;;;;"}