@julianpedro/plugin-dev-ai-hub 0.1.6

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 (30) hide show
  1. package/dist/api/DevAiHubClient.esm.js +76 -0
  2. package/dist/api/DevAiHubClient.esm.js.map +1 -0
  3. package/dist/components/AssetCard/AssetCard.esm.js +200 -0
  4. package/dist/components/AssetCard/AssetCard.esm.js.map +1 -0
  5. package/dist/components/AssetDetailPanel/AssetDetailPanel.esm.js +327 -0
  6. package/dist/components/AssetDetailPanel/AssetDetailPanel.esm.js.map +1 -0
  7. package/dist/components/AssetFilters/AssetFilters.esm.js +216 -0
  8. package/dist/components/AssetFilters/AssetFilters.esm.js.map +1 -0
  9. package/dist/components/AssetInstallDialog/AssetInstallDialog.esm.js +194 -0
  10. package/dist/components/AssetInstallDialog/AssetInstallDialog.esm.js.map +1 -0
  11. package/dist/components/DevAiHubPage/DevAiHubPage.esm.js +320 -0
  12. package/dist/components/DevAiHubPage/DevAiHubPage.esm.js.map +1 -0
  13. package/dist/components/DevAiHubPage/index.esm.js +6 -0
  14. package/dist/components/DevAiHubPage/index.esm.js.map +1 -0
  15. package/dist/components/McpConfigDialog/McpConfigDialog.esm.js +314 -0
  16. package/dist/components/McpConfigDialog/McpConfigDialog.esm.js.map +1 -0
  17. package/dist/components/ToolIcon/ToolIcon.esm.js +41 -0
  18. package/dist/components/ToolIcon/ToolIcon.esm.js.map +1 -0
  19. package/dist/hooks/index.esm.js +114 -0
  20. package/dist/hooks/index.esm.js.map +1 -0
  21. package/dist/index.d.ts +104 -0
  22. package/dist/index.esm.js +5 -0
  23. package/dist/index.esm.js.map +1 -0
  24. package/dist/plugin.esm.js +40 -0
  25. package/dist/plugin.esm.js.map +1 -0
  26. package/dist/pluginLegacy.esm.js +27 -0
  27. package/dist/pluginLegacy.esm.js.map +1 -0
  28. package/dist/routes.esm.js +6 -0
  29. package/dist/routes.esm.js.map +1 -0
  30. package/package.json +89 -0
@@ -0,0 +1,76 @@
1
+ import { createApiRef } from '@backstage/core-plugin-api';
2
+
3
+ const devAiHubApiRef = createApiRef({
4
+ id: "plugin.dev-ai-hub.service"
5
+ });
6
+ class DevAiHubClient {
7
+ constructor(discoveryApi, fetchApi) {
8
+ this.discoveryApi = discoveryApi;
9
+ this.fetchApi = fetchApi;
10
+ }
11
+ async baseUrl() {
12
+ return this.discoveryApi.getBaseUrl("dev-ai-hub");
13
+ }
14
+ async fetch(path, init) {
15
+ const base = await this.baseUrl();
16
+ const response = await this.fetchApi.fetch(`${base}${path}`, init);
17
+ if (!response.ok) {
18
+ const text = await response.text();
19
+ throw new Error(
20
+ `Dev AI Hub API error ${response.status}: ${text}`
21
+ );
22
+ }
23
+ return response.json();
24
+ }
25
+ async listAssets(filter) {
26
+ const params = new URLSearchParams();
27
+ if (filter?.type) params.set("type", filter.type);
28
+ if (filter?.tool) params.set("tool", filter.tool);
29
+ if (filter?.search) params.set("search", filter.search);
30
+ if (filter?.providerId) params.set("provider", filter.providerId);
31
+ if (filter?.tags?.length) params.set("tags", filter.tags.join(","));
32
+ if (filter?.page) params.set("page", String(filter.page));
33
+ if (filter?.pageSize) params.set("pageSize", String(filter.pageSize));
34
+ const qs = params.toString();
35
+ return this.fetch(`/assets${qs ? `?${qs}` : ""}`);
36
+ }
37
+ async getAsset(id) {
38
+ return this.fetch(`/assets/${encodeURIComponent(id)}`);
39
+ }
40
+ async getAssetRaw(id) {
41
+ const base = await this.baseUrl();
42
+ const response = await this.fetchApi.fetch(
43
+ `${base}/assets/${encodeURIComponent(id)}/raw`
44
+ );
45
+ if (!response.ok) throw new Error(`Failed to fetch raw asset: ${response.status}`);
46
+ return response.text();
47
+ }
48
+ async getDownloadUrl(id) {
49
+ const base = await this.baseUrl();
50
+ return `${base}/assets/${encodeURIComponent(id)}/download`;
51
+ }
52
+ async trackInstall(id) {
53
+ await this.fetch(`/assets/${encodeURIComponent(id)}/track-install`, {
54
+ method: "POST"
55
+ });
56
+ }
57
+ async listProviders() {
58
+ return this.fetch("/providers");
59
+ }
60
+ async getProviderStatus(id) {
61
+ return this.fetch(
62
+ `/providers/${encodeURIComponent(id)}/status`
63
+ );
64
+ }
65
+ async triggerSync(id) {
66
+ await this.fetch(`/providers/${encodeURIComponent(id)}/sync`, {
67
+ method: "POST"
68
+ });
69
+ }
70
+ async getStats() {
71
+ return this.fetch("/stats");
72
+ }
73
+ }
74
+
75
+ export { DevAiHubClient, devAiHubApiRef };
76
+ //# sourceMappingURL=DevAiHubClient.esm.js.map
@@ -0,0 +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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"}
@@ -0,0 +1,200 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { useTheme, alpha } from '@mui/material/styles';
3
+ import Box from '@mui/material/Box';
4
+ import Card from '@mui/material/Card';
5
+ import CardActions from '@mui/material/CardActions';
6
+ import CardContent from '@mui/material/CardContent';
7
+ import Chip from '@mui/material/Chip';
8
+ import IconButton from '@mui/material/IconButton';
9
+ import Tooltip from '@mui/material/Tooltip';
10
+ import Typography from '@mui/material/Typography';
11
+ import OpenInNewIcon from '@mui/icons-material/OpenInNew';
12
+ import DownloadIcon from '@mui/icons-material/Download';
13
+ import ArticleIcon from '@mui/icons-material/Article';
14
+ import SmartToyIcon from '@mui/icons-material/SmartToy';
15
+ import BuildIcon from '@mui/icons-material/Build';
16
+ import AccountTreeIcon from '@mui/icons-material/AccountTree';
17
+ import { ToolIcon } from '../ToolIcon/ToolIcon.esm.js';
18
+
19
+ const POPULAR_THRESHOLD = 5;
20
+ const NEW_DAYS_MS = 14 * 24 * 60 * 60 * 1e3;
21
+ const TOOL_LABELS = {
22
+ "all": "Universal",
23
+ "claude-code": "Claude Code",
24
+ "github-copilot": "GitHub Copilot",
25
+ "google-gemini": "Google Gemini",
26
+ "cursor": "Cursor"
27
+ };
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 }
33
+ };
34
+ function AssetCard({ asset, onView, onInstall }) {
35
+ const theme = useTheme();
36
+ const isDark = theme.palette.mode === "dark" || theme.palette.type === "dark";
37
+ const cfg = TYPE_CONFIG[asset.type];
38
+ const TypeIcon = cfg.Icon;
39
+ const isPopular = asset.installCount >= POPULAR_THRESHOLD;
40
+ const isNew = Date.now() - new Date(asset.updatedAt).getTime() < NEW_DAYS_MS;
41
+ return /* @__PURE__ */ jsxs(
42
+ Card,
43
+ {
44
+ variant: "outlined",
45
+ sx: {
46
+ height: "100%",
47
+ display: "flex",
48
+ flexDirection: "column",
49
+ borderRadius: 2,
50
+ border: "1px solid",
51
+ borderColor: "divider",
52
+ borderLeft: `3px solid ${cfg.color}`,
53
+ transition: "all 0.18s ease",
54
+ "&:hover": {
55
+ boxShadow: `0 6px 24px ${cfg.color}30`,
56
+ borderColor: cfg.color,
57
+ transform: "translateY(-2px)"
58
+ }
59
+ },
60
+ children: [
61
+ /* @__PURE__ */ jsxs(CardContent, { sx: { p: 1.5, pb: "0 !important", flex: 1 }, children: [
62
+ /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "flex-start", gap: 1, mb: 1 }, children: [
63
+ /* @__PURE__ */ jsx(
64
+ Box,
65
+ {
66
+ sx: {
67
+ width: 40,
68
+ height: 40,
69
+ borderRadius: 1.5,
70
+ backgroundColor: alpha(cfg.color, 0.12),
71
+ display: "flex",
72
+ alignItems: "center",
73
+ justifyContent: "center",
74
+ flexShrink: 0,
75
+ boxShadow: `0 2px 8px ${cfg.color}25`
76
+ },
77
+ children: asset.icon ? /* @__PURE__ */ jsx(
78
+ Box,
79
+ {
80
+ component: "img",
81
+ src: asset.icon,
82
+ alt: asset.label ?? asset.name,
83
+ sx: { width: 26, height: 26, objectFit: "contain" },
84
+ onError: (e) => {
85
+ e.target.style.display = "none";
86
+ }
87
+ }
88
+ ) : /* @__PURE__ */ jsx(TypeIcon, { sx: { color: cfg.color, fontSize: "1.3rem" } })
89
+ }
90
+ ),
91
+ /* @__PURE__ */ jsxs(Box, { sx: { flex: 1, minWidth: 0 }, children: [
92
+ /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 0.5, mb: 0.2 }, children: [
93
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", fontWeight: 700, noWrap: true, title: asset.label ?? asset.name, sx: { lineHeight: 1.2, flex: 1 }, children: asset.label ?? asset.name }),
94
+ isNew && /* @__PURE__ */ jsx(
95
+ Chip,
96
+ {
97
+ label: "New",
98
+ size: "small",
99
+ sx: {
100
+ height: 18,
101
+ fontSize: "0.6rem",
102
+ fontWeight: 700,
103
+ backgroundColor: alpha("#059669", isDark ? 0.25 : 0.14),
104
+ backdropFilter: "blur(8px)",
105
+ color: isDark ? "#fff" : "#059669",
106
+ border: "1px solid",
107
+ borderColor: alpha("#059669", isDark ? 0.5 : 0.3),
108
+ borderRadius: 1,
109
+ flexShrink: 0,
110
+ "& .MuiChip-label": { px: "6px" }
111
+ }
112
+ }
113
+ )
114
+ ] }),
115
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { color: cfg.color, fontWeight: 600 }, children: cfg.label })
116
+ ] })
117
+ ] }),
118
+ /* @__PURE__ */ jsx(
119
+ Typography,
120
+ {
121
+ variant: "caption",
122
+ color: "text.secondary",
123
+ sx: {
124
+ mb: 1,
125
+ display: "-webkit-box",
126
+ WebkitLineClamp: 2,
127
+ WebkitBoxOrient: "vertical",
128
+ overflow: "hidden",
129
+ lineHeight: 1.4
130
+ },
131
+ children: asset.description
132
+ }
133
+ ),
134
+ /* @__PURE__ */ jsx(Box, { sx: { display: "flex", gap: 0.5, flexWrap: "wrap", mb: asset.tags.length > 0 ? 0.75 : 0 }, children: asset.tools.map((tool) => /* @__PURE__ */ jsx(
135
+ Chip,
136
+ {
137
+ icon: /* @__PURE__ */ jsx(ToolIcon, { tool, sx: { fontSize: "0.75rem !important" } }),
138
+ label: TOOL_LABELS[tool] ?? tool,
139
+ size: "small",
140
+ sx: {
141
+ height: 18,
142
+ fontSize: "0.65rem",
143
+ fontWeight: 600,
144
+ backgroundColor: "action.hover",
145
+ color: "text.secondary",
146
+ borderRadius: 1,
147
+ "& .MuiChip-icon": { ml: "4px" }
148
+ }
149
+ },
150
+ tool
151
+ )) }),
152
+ asset.tags.length > 0 && /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", gap: 0.5, flexWrap: "wrap" }, children: [
153
+ asset.tags.slice(0, 3).map((tag) => /* @__PURE__ */ jsx(
154
+ Chip,
155
+ {
156
+ label: `#${tag}`,
157
+ size: "small",
158
+ sx: {
159
+ height: 16,
160
+ fontSize: "0.6rem",
161
+ color: "text.disabled",
162
+ backgroundColor: "transparent",
163
+ border: "1px solid",
164
+ borderColor: "divider",
165
+ borderRadius: 1
166
+ }
167
+ },
168
+ tag
169
+ )),
170
+ asset.tags.length > 3 && /* @__PURE__ */ jsxs(Typography, { variant: "caption", color: "text.disabled", sx: { alignSelf: "center" }, children: [
171
+ "+",
172
+ asset.tags.length - 3
173
+ ] })
174
+ ] })
175
+ ] }),
176
+ /* @__PURE__ */ jsxs(CardActions, { sx: { px: 1.5, py: 1, justifyContent: "space-between", mt: "auto", borderTop: "1px solid", borderColor: "divider" }, children: [
177
+ /* @__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
+ ] }),
184
+ asset.installCount > 0 && /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 0.25 }, children: [
185
+ /* @__PURE__ */ jsx(Typography, { sx: { fontSize: "0.65rem", lineHeight: 1 }, children: isPopular ? "\u{1F525}" : "\u2193" }),
186
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.disabled", sx: { fontSize: "0.65rem" }, children: asset.installCount })
187
+ ] })
188
+ ] }),
189
+ /* @__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" }) }) })
192
+ ] })
193
+ ] })
194
+ ]
195
+ }
196
+ );
197
+ }
198
+
199
+ export { AssetCard };
200
+ //# sourceMappingURL=AssetCard.esm.js.map
@@ -0,0 +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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"}
@@ -0,0 +1,327 @@
1
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
+ import { useState } from 'react';
3
+ import { useTheme } from '@mui/material/styles';
4
+ import ReactMarkdown from 'react-markdown';
5
+ import remarkGfm from 'remark-gfm';
6
+ import Alert from '@mui/material/Alert';
7
+ import Box from '@mui/material/Box';
8
+ import Button from '@mui/material/Button';
9
+ import Chip from '@mui/material/Chip';
10
+ import CircularProgress from '@mui/material/CircularProgress';
11
+ import Divider from '@mui/material/Divider';
12
+ import Drawer from '@mui/material/Drawer';
13
+ import IconButton from '@mui/material/IconButton';
14
+ import Link from '@mui/material/Link';
15
+ import Snackbar from '@mui/material/Snackbar';
16
+ import Tab from '@mui/material/Tab';
17
+ import Tabs from '@mui/material/Tabs';
18
+ import Typography from '@mui/material/Typography';
19
+ import CloseIcon from '@mui/icons-material/Close';
20
+ import ContentCopyIcon from '@mui/icons-material/ContentCopy';
21
+ import FolderZipIcon from '@mui/icons-material/FolderZip';
22
+ import OpenInNewIcon from '@mui/icons-material/OpenInNew';
23
+ import { useAssetDetail } from '../../hooks/index.esm.js';
24
+
25
+ const SyntaxHighlighter = require("react-syntax-highlighter/dist/esm/prism").default;
26
+ const { oneLight, oneDark } = require("react-syntax-highlighter/dist/esm/styles/prism");
27
+ const TYPE_COLORS = {
28
+ instruction: "#1976d2",
29
+ agent: "#7b1fa2",
30
+ skill: "#388e3c",
31
+ workflow: "#f57c00"
32
+ };
33
+ function AssetDetailPanel({ assetId, onClose }) {
34
+ const [tab, setTab] = useState(0);
35
+ const [snackbar, setSnackbar] = useState(null);
36
+ const { asset, loading } = useAssetDetail(assetId);
37
+ const theme = useTheme();
38
+ const syntaxTheme = theme.palette.mode === "dark" ? oneDark : oneLight;
39
+ const handleCopy = () => {
40
+ if (!asset) return;
41
+ navigator.clipboard.writeText(asset.content).then(
42
+ () => setSnackbar("Markdown copied to clipboard!")
43
+ );
44
+ };
45
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
46
+ /* @__PURE__ */ jsx(
47
+ Drawer,
48
+ {
49
+ anchor: "right",
50
+ open: !!assetId,
51
+ onClose,
52
+ PaperProps: { sx: { width: { xs: "100vw", md: 640 } } },
53
+ children: /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", flexDirection: "column", height: "100%" }, children: [
54
+ /* @__PURE__ */ jsxs(
55
+ Box,
56
+ {
57
+ sx: {
58
+ p: 2,
59
+ display: "flex",
60
+ alignItems: "flex-start",
61
+ gap: 2,
62
+ borderBottom: 1,
63
+ borderColor: "divider"
64
+ },
65
+ children: [
66
+ /* @__PURE__ */ jsx(Box, { sx: { flex: 1 }, children: loading || !asset ? /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 1 }, children: [
67
+ /* @__PURE__ */ jsx(CircularProgress, { size: 16 }),
68
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", children: "Loading..." })
69
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
70
+ /* @__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 }),
72
+ /* @__PURE__ */ jsx(
73
+ Chip,
74
+ {
75
+ label: asset.type,
76
+ size: "small",
77
+ sx: {
78
+ backgroundColor: `${TYPE_COLORS[asset.type]}20`,
79
+ color: TYPE_COLORS[asset.type],
80
+ fontWeight: 600
81
+ }
82
+ }
83
+ )
84
+ ] }),
85
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", children: asset.description })
86
+ ] }) }),
87
+ /* @__PURE__ */ jsx(IconButton, { onClick: onClose, size: "small", children: /* @__PURE__ */ jsx(CloseIcon, {}) })
88
+ ]
89
+ }
90
+ ),
91
+ /* @__PURE__ */ jsxs(
92
+ Tabs,
93
+ {
94
+ value: tab,
95
+ onChange: (_, v) => setTab(v),
96
+ sx: { borderBottom: 1, borderColor: "divider", px: 2 },
97
+ children: [
98
+ /* @__PURE__ */ jsx(Tab, { label: "Preview" }),
99
+ /* @__PURE__ */ jsx(Tab, { label: "Metadata" }),
100
+ /* @__PURE__ */ jsx(Tab, { label: "Raw YAML" })
101
+ ]
102
+ }
103
+ ),
104
+ /* @__PURE__ */ jsxs(Box, { sx: { flex: 1, overflow: "auto", p: 2 }, children: [
105
+ loading && /* @__PURE__ */ jsx(Box, { sx: { display: "flex", justifyContent: "center", pt: 4 }, children: /* @__PURE__ */ jsx(CircularProgress, {}) }),
106
+ !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": {
125
+ border: "1px solid",
126
+ borderColor: "divider",
127
+ px: 1.5,
128
+ py: 0.75,
129
+ fontSize: "0.875rem"
130
+ },
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
165
+ }
166
+ ) : /* @__PURE__ */ jsx("code", { className, children });
167
+ }
168
+ },
169
+ children: asset.content
170
+ }
171
+ )
172
+ }
173
+ ),
174
+ 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 }),
181
+ /* @__PURE__ */ jsx(Divider, {}),
182
+ /* @__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)) })
185
+ ] }),
186
+ 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)) })
189
+ ] }),
190
+ asset.type === "skill" && /* @__PURE__ */ jsxs(Fragment, { children: [
191
+ /* @__PURE__ */ jsx(Divider, {}),
192
+ /* @__PURE__ */ jsxs(Box, { children: [
193
+ /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 0.75, mb: 0.5 }, children: [
194
+ /* @__PURE__ */ jsx(FolderZipIcon, { sx: { fontSize: "0.85rem", color: "text.secondary" } }),
195
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", children: "Bundled files" })
196
+ ] }),
197
+ asset.resourcesContent && Object.keys(asset.resourcesContent).length > 0 ? /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", flexDirection: "column", gap: 0.5, mt: 0.5 }, children: [
198
+ /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", gap: 0.5, flexWrap: "wrap" }, children: [
199
+ /* @__PURE__ */ jsx(
200
+ Chip,
201
+ {
202
+ label: "SKILL.md",
203
+ size: "small",
204
+ sx: { fontFamily: "monospace", fontSize: "0.7rem", height: 20 }
205
+ }
206
+ ),
207
+ Object.keys(asset.resourcesContent).map((p) => /* @__PURE__ */ jsx(
208
+ Chip,
209
+ {
210
+ label: p,
211
+ size: "small",
212
+ variant: "outlined",
213
+ sx: { fontFamily: "monospace", fontSize: "0.7rem", height: 20 }
214
+ },
215
+ p
216
+ ))
217
+ ] }),
218
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.disabled", children: "Downloads as .zip containing all files above." })
219
+ ] }) : /* @__PURE__ */ jsx(Box, { sx: { display: "flex", gap: 0.5, flexWrap: "wrap", mt: 0.5 }, children: /* @__PURE__ */ jsx(
220
+ Chip,
221
+ {
222
+ label: "SKILL.md",
223
+ size: "small",
224
+ sx: { fontFamily: "monospace", fontSize: "0.7rem", height: 20 }
225
+ }
226
+ ) })
227
+ ] })
228
+ ] }),
229
+ /* @__PURE__ */ jsx(Divider, {}),
230
+ /* @__PURE__ */ jsxs(Box, { children: [
231
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", children: "Repository" }),
232
+ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(
233
+ Link,
234
+ {
235
+ href: asset.repoUrl,
236
+ target: "_blank",
237
+ rel: "noopener noreferrer",
238
+ variant: "body2",
239
+ children: [
240
+ asset.repoUrl,
241
+ " ",
242
+ /* @__PURE__ */ jsx(OpenInNewIcon, { sx: { fontSize: 12 } })
243
+ ]
244
+ }
245
+ ) })
246
+ ] })
247
+ ] }),
248
+ tab === 2 && /* @__PURE__ */ jsx(
249
+ Box,
250
+ {
251
+ component: "pre",
252
+ sx: {
253
+ bgcolor: "action.hover",
254
+ p: 2,
255
+ borderRadius: 1,
256
+ overflow: "auto",
257
+ fontSize: "0.8rem",
258
+ fontFamily: "monospace",
259
+ whiteSpace: "pre-wrap",
260
+ wordBreak: "break-word",
261
+ border: "1px solid",
262
+ borderColor: "divider"
263
+ },
264
+ children: asset.yamlRaw
265
+ }
266
+ )
267
+ ] })
268
+ ] }),
269
+ /* @__PURE__ */ jsxs(
270
+ Box,
271
+ {
272
+ sx: {
273
+ p: 2,
274
+ borderTop: 1,
275
+ borderColor: "divider",
276
+ display: "flex",
277
+ gap: 1,
278
+ flexWrap: "wrap"
279
+ },
280
+ children: [
281
+ /* @__PURE__ */ jsx(
282
+ Button,
283
+ {
284
+ variant: "contained",
285
+ startIcon: /* @__PURE__ */ jsx(ContentCopyIcon, {}),
286
+ onClick: handleCopy,
287
+ disabled: !asset,
288
+ children: "Copy Markdown"
289
+ }
290
+ ),
291
+ /* @__PURE__ */ jsx(
292
+ Button,
293
+ {
294
+ variant: "outlined",
295
+ startIcon: /* @__PURE__ */ jsx(OpenInNewIcon, {}),
296
+ onClick: () => asset && window.open(asset.repoUrl, "_blank"),
297
+ disabled: !asset,
298
+ children: "Open in Repo"
299
+ }
300
+ )
301
+ ]
302
+ }
303
+ )
304
+ ] })
305
+ }
306
+ ),
307
+ /* @__PURE__ */ jsx(
308
+ Snackbar,
309
+ {
310
+ open: !!snackbar,
311
+ autoHideDuration: 2500,
312
+ onClose: () => setSnackbar(null),
313
+ anchorOrigin: { vertical: "bottom", horizontal: "center" },
314
+ children: /* @__PURE__ */ jsx(Alert, { severity: "success", onClose: () => setSnackbar(null), children: snackbar })
315
+ }
316
+ )
317
+ ] });
318
+ }
319
+ function MetaRow({ label, value }) {
320
+ return /* @__PURE__ */ jsxs(Box, { children: [
321
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", children: label }),
322
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", children: value })
323
+ ] });
324
+ }
325
+
326
+ export { AssetDetailPanel };
327
+ //# sourceMappingURL=AssetDetailPanel.esm.js.map