@nospt/plugin-dev-ai-hub 0.1.0 โ†’ 0.2.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 (71) hide show
  1. package/dev/index.tsx +17 -217
  2. package/dist/api/DevAiHubClient.esm.js +2 -0
  3. package/dist/api/DevAiHubClient.esm.js.map +1 -1
  4. package/dist/components/AssetCard/AssetCard.esm.js +60 -134
  5. package/dist/components/AssetCard/AssetCard.esm.js.map +1 -1
  6. package/dist/components/AssetCard/AssetCard.module.css.esm.js +8 -0
  7. package/dist/components/AssetCard/AssetCard.module.css.esm.js.map +1 -0
  8. package/dist/components/AssetDetailPanel/AssetDetailPanel.esm.js +175 -281
  9. package/dist/components/AssetDetailPanel/AssetDetailPanel.esm.js.map +1 -1
  10. package/dist/components/AssetDetailPanel/AssetDetailPanel.module.css.esm.js +8 -0
  11. package/dist/components/AssetDetailPanel/AssetDetailPanel.module.css.esm.js.map +1 -0
  12. package/dist/components/AssetFilters/AssetFilters.esm.js +94 -134
  13. package/dist/components/AssetFilters/AssetFilters.esm.js.map +1 -1
  14. package/dist/components/AssetFilters/AssetFilters.module.css.esm.js +8 -0
  15. package/dist/components/AssetFilters/AssetFilters.module.css.esm.js.map +1 -0
  16. package/dist/components/AssetInstallDialog/AssetInstallDialog.esm.js +70 -126
  17. package/dist/components/AssetInstallDialog/AssetInstallDialog.esm.js.map +1 -1
  18. package/dist/components/AssetInstallDialog/AssetInstallDialog.module.css.esm.js +8 -0
  19. package/dist/components/AssetInstallDialog/AssetInstallDialog.module.css.esm.js.map +1 -0
  20. package/dist/components/DevAiHubPage/DevAiHubPage.esm.js +64 -189
  21. package/dist/components/DevAiHubPage/DevAiHubPage.esm.js.map +1 -1
  22. package/dist/components/DevAiHubPage/DevAiHubPage.module.css.esm.js +8 -0
  23. package/dist/components/DevAiHubPage/DevAiHubPage.module.css.esm.js.map +1 -0
  24. package/dist/components/McpConfigPage/McpConfigPage.esm.js +200 -0
  25. package/dist/components/McpConfigPage/McpConfigPage.esm.js.map +1 -0
  26. package/dist/components/McpConfigPage/McpConfigPage.module.css.esm.js +8 -0
  27. package/dist/components/McpConfigPage/McpConfigPage.module.css.esm.js.map +1 -0
  28. package/dist/components/McpConfigPage/index.esm.js +6 -0
  29. package/dist/components/McpConfigPage/index.esm.js.map +1 -0
  30. package/dist/components/ToolIcon/ToolIcon.esm.js +25 -13
  31. package/dist/components/ToolIcon/ToolIcon.esm.js.map +1 -1
  32. package/dist/index.d.ts +10 -5
  33. package/dist/index.esm.js +1 -1
  34. package/dist/node_modules_dist/style-inject/dist/style-inject.es.esm.js +29 -0
  35. package/dist/node_modules_dist/style-inject/dist/style-inject.es.esm.js.map +1 -0
  36. package/dist/plugin.esm.js +22 -4
  37. package/dist/plugin.esm.js.map +1 -1
  38. package/dist/routes.esm.js +2 -1
  39. package/dist/routes.esm.js.map +1 -1
  40. package/package.json +24 -14
  41. package/package.json-prepack +24 -14
  42. package/src/components/AssetCard/AssetCard.module.css +153 -0
  43. package/src/components/AssetCard/AssetCard.tsx +79 -148
  44. package/src/components/AssetDetailPanel/AssetDetailPanel.module.css +207 -0
  45. package/src/components/AssetDetailPanel/AssetDetailPanel.tsx +228 -292
  46. package/src/components/AssetFilters/AssetFilters.module.css +67 -0
  47. package/src/components/AssetFilters/AssetFilters.tsx +136 -178
  48. package/src/components/AssetInstallDialog/AssetInstallDialog.module.css +72 -0
  49. package/src/components/AssetInstallDialog/AssetInstallDialog.tsx +95 -135
  50. package/src/components/DevAiHubPage/DevAiHubPage.module.css +105 -0
  51. package/src/components/DevAiHubPage/DevAiHubPage.tsx +73 -222
  52. package/src/components/McpConfigDialog/McpConfigDialog.module.css +111 -0
  53. package/src/components/McpConfigDialog/McpConfigDialog.tsx +178 -208
  54. package/src/components/McpConfigPage/McpConfigPage.module.css +121 -0
  55. package/src/components/McpConfigPage/McpConfigPage.tsx +242 -0
  56. package/src/components/McpConfigPage/index.ts +1 -0
  57. package/src/components/ToolIcon/ToolIcon.tsx +31 -15
  58. package/src/css-modules.d.ts +4 -0
  59. package/src/index.ts +1 -1
  60. package/src/plugin.tsx +21 -1
  61. package/src/routes.ts +1 -0
  62. package/tests/e2e/.auth.json +14 -0
  63. package/tests/e2e/asset-detail.spec.ts +90 -0
  64. package/tests/e2e/asset-filtering.spec.ts +145 -0
  65. package/tests/e2e/asset-installation.spec.ts +74 -0
  66. package/tests/e2e/dev-ai-hub-page.spec.ts +89 -0
  67. package/tests/e2e/fixtures/base.ts +61 -0
  68. package/tests/e2e/fixtures/mock-api.ts +221 -0
  69. package/tests/e2e/mcp-config.spec.ts +88 -0
  70. package/dist/components/McpConfigDialog/McpConfigDialog.esm.js +0 -274
  71. package/dist/components/McpConfigDialog/McpConfigDialog.esm.js.map +0 -1
package/dev/index.tsx CHANGED
@@ -1,232 +1,32 @@
1
1
  import { createDevApp } from '@backstage/dev-utils';
2
+ import { discoveryApiRef, fetchApiRef } from '@backstage/core-plugin-api';
2
3
  import { DevAiHubPage } from '../src/components/DevAiHubPage';
3
- import { devAiHubApiRef } from '../src/api/DevAiHubClient';
4
- import type {
5
- AiAsset,
6
- AiAssetListResponse,
7
- AiHubProvider,
8
- AiHubStats,
9
- AssetListFilter,
10
- } from '@nospt/plugin-dev-ai-hub-common';
4
+ import { McpConfigPage } from '../src/components/McpConfigPage';
5
+ import { devAiHubApiRef, DevAiHubClient } from '../src/api/DevAiHubClient';
11
6
 
12
- const MOCK_ASSETS: AiAsset[] = [
13
- {
14
- id: 'mock-1',
15
- providerId: 'mock-provider',
16
- name: 'TypeScript Best Practices',
17
- description: 'Coding standards and best practices for TypeScript projects.',
18
- type: 'instruction',
19
- tools: ['claude-code', 'github-copilot'],
20
- tags: ['typescript', 'best-practices'],
21
- author: 'Platform Team',
22
- version: '1.0.0',
23
- content: '# TypeScript Best Practices\n\nAlways use strict mode. Prefer `interface` over `type` for object shapes. Use explicit return types on public functions.',
24
- yamlRaw: 'name: TypeScript Best Practices\ntype: instruction',
25
- yamlPath: 'instructions/typescript-best-practices.yaml',
26
- mdPath: 'instructions/typescript-best-practices.md',
27
- repoUrl: 'https://github.com/example/ai-assets',
28
- branch: 'main',
29
- installCount: 14,
30
- syncedAt: new Date(Date.now() - 5 * 60 * 1000).toISOString(),
31
- createdAt: '2026-03-10T10:00:00Z',
32
- updatedAt: '2026-03-10T12:00:00Z',
33
- },
34
- {
35
- id: 'mock-2',
36
- providerId: 'mock-provider',
37
- name: 'Code Review Agent',
38
- description: 'Specialized agent for performing thorough and constructive code reviews.',
39
- type: 'agent',
40
- tools: ['claude-code'],
41
- tags: ['code-review', 'quality'],
42
- author: 'Platform Team',
43
- version: '1.2.0',
44
- content: '# Code Review Agent\n\nYou are an expert code reviewer. Focus on correctness, performance, security, and maintainability.',
45
- yamlRaw: 'name: Code Review Agent\ntype: agent',
46
- yamlPath: 'agents/code-review.yaml',
47
- mdPath: 'agents/code-review.md',
48
- repoUrl: 'https://github.com/example/ai-assets',
49
- branch: 'main',
50
- installCount: 8,
51
- syncedAt: new Date(Date.now() - 5 * 60 * 1000).toISOString(),
52
- createdAt: '2024-02-01T10:00:00Z',
53
- updatedAt: '2024-03-10T09:00:00Z',
54
- },
55
- {
56
- id: 'mock-3',
57
- providerId: 'mock-provider',
58
- name: 'Git Commit',
59
- description: 'Skill for creating well-structured, conventional git commit messages.',
60
- type: 'skill',
61
- tools: ['all'],
62
- tags: ['git', 'commits', 'conventional-commits'],
63
- author: 'Platform Team',
64
- version: '1.0.0',
65
- content: '# Git Commit Skill\n\nCreate commit messages following the Conventional Commits specification.',
66
- yamlRaw: 'name: Git Commit\ntype: skill',
67
- yamlPath: 'skills/git-commit/skill.yaml',
68
- mdPath: 'skills/git-commit/SKILL.md',
69
- repoUrl: 'https://github.com/example/ai-assets',
70
- branch: 'main',
71
- installCount: 22,
72
- syncedAt: new Date(Date.now() - 5 * 60 * 1000).toISOString(),
73
- createdAt: '2024-01-20T10:00:00Z',
74
- updatedAt: '2024-02-15T14:00:00Z',
75
- },
76
- {
77
- id: 'mock-4',
78
- providerId: 'mock-provider',
79
- name: 'Feature Development Workflow',
80
- description: 'Step-by-step workflow for planning, implementing and shipping a new feature.',
81
- type: 'workflow',
82
- tools: ['github-copilot', 'cursor'],
83
- tags: ['workflow', 'feature', 'development'],
84
- author: 'Platform Team',
85
- version: '2.0.0',
86
- content: '# Feature Development Workflow\n\n## Step 1: Planning\n\n## Step 2: Implementation\n\n## Step 3: Testing\n\n## Step 4: Review',
87
- yamlRaw: 'name: Feature Development Workflow\ntype: workflow',
88
- yamlPath: 'workflows/feature-development.yaml',
89
- mdPath: 'workflows/feature-development.md',
90
- repoUrl: 'https://github.com/example/ai-assets',
91
- branch: 'main',
92
- installCount: 5,
93
- syncedAt: new Date(Date.now() - 5 * 60 * 1000).toISOString(),
94
- createdAt: '2024-03-01T10:00:00Z',
95
- updatedAt: '2024-03-20T16:00:00Z',
96
- },
97
- {
98
- id: 'mock-5',
99
- providerId: 'mock-provider',
100
- name: 'Security Guidelines',
101
- description: 'Security best practices and guidelines for backend development.',
102
- type: 'instruction',
103
- tools: ['google-gemini', 'claude-code'],
104
- tags: ['security', 'backend', 'best-practices'],
105
- author: 'Security Team',
106
- version: '1.1.0',
107
- content: '# Security Guidelines\n\nNever store secrets in code. Always validate user input. Use parameterized queries.',
108
- yamlRaw: 'name: Security Guidelines\ntype: instruction',
109
- yamlPath: 'instructions/security-guidelines.yaml',
110
- mdPath: 'instructions/security-guidelines.md',
111
- repoUrl: 'https://github.com/example/ai-assets',
112
- branch: 'main',
113
- installCount: 3,
114
- syncedAt: new Date(Date.now() - 5 * 60 * 1000).toISOString(),
115
- createdAt: '2024-02-10T10:00:00Z',
116
- updatedAt: '2024-03-05T11:00:00Z',
117
- },
118
- {
119
- id: 'mock-6',
120
- providerId: 'mock-provider',
121
- name: 'Product Manager',
122
- description: 'Data-driven product decision assistant for roadmap and prioritization.',
123
- type: 'agent',
124
- tools: ['github-copilot'],
125
- tags: ['product', 'roadmap', 'prioritization'],
126
- author: 'Product Team',
127
- version: '1.0.0',
128
- content: '# Product Manager Agent\n\nYou are a data-driven product manager. Help prioritize features using impact/effort frameworks.',
129
- yamlRaw: 'name: Product Manager\ntype: agent',
130
- yamlPath: 'agents/product-manager.yaml',
131
- mdPath: 'agents/product-manager.md',
132
- repoUrl: 'https://github.com/example/ai-assets',
133
- branch: 'main',
134
- installCount: 0,
135
- syncedAt: new Date(Date.now() - 5 * 60 * 1000).toISOString(),
136
- createdAt: '2024-03-15T10:00:00Z',
137
- updatedAt: '2024-03-15T10:00:00Z',
138
- },
139
- ];
140
-
141
- const MOCK_PROVIDER: AiHubProvider = {
142
- id: 'mock-provider',
143
- type: 'github',
144
- target: 'https://github.com/example/ai-assets.git',
145
- branch: 'main',
146
- lastSync: new Date(Date.now() - 5 * 60 * 1000).toISOString(),
147
- status: 'idle',
148
- assetCount: MOCK_ASSETS.length,
149
- };
150
-
151
- const MOCK_STATS: AiHubStats = {
152
- totalAssets: MOCK_ASSETS.length,
153
- byType: {
154
- instruction: MOCK_ASSETS.filter(a => a.type === 'instruction').length,
155
- agent: MOCK_ASSETS.filter(a => a.type === 'agent').length,
156
- skill: MOCK_ASSETS.filter(a => a.type === 'skill').length,
157
- workflow: MOCK_ASSETS.filter(a => a.type === 'workflow').length,
158
- },
159
- byTool: {
160
- 'claude-code': MOCK_ASSETS.filter(a => a.tools.includes('claude-code') || a.tools.includes('all')).length,
161
- 'github-copilot': MOCK_ASSETS.filter(a => a.tools.includes('github-copilot') || a.tools.includes('all')).length,
162
- 'google-gemini': MOCK_ASSETS.filter(a => a.tools.includes('google-gemini') || a.tools.includes('all')).length,
163
- 'cursor': MOCK_ASSETS.filter(a => a.tools.includes('cursor') || a.tools.includes('all')).length,
164
- },
165
- byProvider: { 'mock-provider': MOCK_ASSETS.length },
166
- lastSync: MOCK_PROVIDER.lastSync,
167
- };
168
-
169
- function applyFilter(assets: AiAsset[], filter?: AssetListFilter): AiAsset[] {
170
- let result = [...assets];
171
- if (filter?.type) result = result.filter(a => a.type === filter.type);
172
- if (filter?.tool) result = result.filter(a => a.tools.includes(filter.tool as any) || a.tools.includes('all'));
173
- if (filter?.search) {
174
- const q = filter.search.toLowerCase();
175
- result = result.filter(a =>
176
- a.name.toLowerCase().includes(q) ||
177
- a.description.toLowerCase().includes(q) ||
178
- a.tags.some(t => t.toLowerCase().includes(q)),
179
- );
180
- }
181
- if (filter?.tags?.length) {
182
- result = result.filter(a => filter.tags!.every(t => a.tags.includes(t)));
183
- }
184
- if (filter?.providerId) {
185
- result = result.filter(a => a.providerId === filter.providerId);
186
- }
187
- return result;
7
+ // Bypass Backstage guest auth without a running auth backend.
8
+ // The legacy token path generates the token locally โ€” same keys the e2e
9
+ // base fixture injects via page.addInitScript().
10
+ if (typeof window !== 'undefined') {
11
+ localStorage.setItem('@backstage/core:SignInPage:provider', 'guest');
12
+ localStorage.setItem('enableLegacyGuestToken', 'true');
188
13
  }
189
14
 
190
15
  createDevApp()
191
16
  .registerApi({
192
17
  api: devAiHubApiRef,
193
- deps: {},
194
- factory: () => ({
195
- listAssets: async (filter?: AssetListFilter): Promise<AiAssetListResponse> => {
196
- const filtered = applyFilter(MOCK_ASSETS, filter);
197
- const page = filter?.page ?? 1;
198
- const pageSize = filter?.pageSize ?? 24;
199
- const start = (page - 1) * pageSize;
200
- return {
201
- items: filtered.slice(start, start + pageSize),
202
- totalCount: filtered.length,
203
- page,
204
- pageSize,
205
- };
206
- },
207
- getAsset: async (id: string): Promise<AiAsset> => {
208
- const asset = MOCK_ASSETS.find(a => a.id === id);
209
- if (!asset) throw new Error(`Asset not found: ${id}`);
210
- return asset;
211
- },
212
- getAssetRaw: async (id: string): Promise<string> => {
213
- const asset = MOCK_ASSETS.find(a => a.id === id);
214
- return asset?.content ?? '';
215
- },
216
- getDownloadUrl: async (id: string): Promise<string> =>
217
- `/api/dev-ai-hub/assets/${encodeURIComponent(id)}/download`,
218
- trackInstall: async (_id: string): Promise<void> => { },
219
- listProviders: async (): Promise<AiHubProvider[]> => [MOCK_PROVIDER],
220
- getProviderStatus: async (_id: string): Promise<AiHubProvider> => MOCK_PROVIDER,
221
- triggerSync: async (_id: string): Promise<void> => {
222
- await new Promise(r => setTimeout(r, 1000));
223
- },
224
- getStats: async (): Promise<AiHubStats> => MOCK_STATS,
225
- }),
18
+ deps: { discoveryApi: discoveryApiRef, fetchApi: fetchApiRef },
19
+ factory: ({ discoveryApi, fetchApi }) =>
20
+ new DevAiHubClient(discoveryApi, fetchApi),
226
21
  })
227
22
  .addPage({
228
23
  element: <DevAiHubPage />,
229
24
  title: 'Dev AI Hub',
230
25
  path: '/dev-ai-hub',
231
26
  })
27
+ .addPage({
28
+ element: <McpConfigPage />,
29
+ title: 'MCP Config',
30
+ path: '/mcp-config',
31
+ })
232
32
  .render();
@@ -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
  }
@@ -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 '@nospt/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":";;AAaO,MAAM,iBAAiB,YAAA,CAA0B;AAAA,EACtD,EAAA,EAAI;AACN,CAAC;AAeM,MAAM,cAAA,CAAsC;AAAA,EACjD,WAAA,CACmB,cACA,QAAA,EACjB;AAFiB,IAAA,IAAA,CAAA,YAAA,GAAA,YAAA;AACA,IAAA,IAAA,CAAA,QAAA,GAAA,QAAA;AAAA,EAChB;AAAA,EAEH,MAAc,OAAA,GAA2B;AACvC,IAAA,OAAO,IAAA,CAAK,YAAA,CAAa,UAAA,CAAW,YAAY,CAAA;AAAA,EAClD;AAAA,EAEA,MAAc,KAAA,CAAS,IAAA,EAAc,IAAA,EAAgC;AACnE,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,OAAA,EAAQ;AAChC,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,QAAA,CAAS,KAAA,CAAM,GAAG,IAAI,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,IAAI,CAAA;AACjE,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AACjC,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,qBAAA,EAAwB,QAAA,CAAS,MAAM,CAAA,EAAA,EAAK,IAAI,CAAA;AAAA,OAClD;AAAA,IACF;AACA,IAAA,OAAO,SAAS,IAAA,EAAK;AAAA,EACvB;AAAA,EAEA,MAAM,WAAW,MAAA,EAAwD;AACvE,IAAA,MAAM,MAAA,GAAS,IAAI,eAAA,EAAgB;AACnC,IAAA,IAAI,QAAQ,IAAA,EAAM,MAAA,CAAO,GAAA,CAAI,MAAA,EAAQ,OAAO,IAAI,CAAA;AAChD,IAAA,IAAI,QAAQ,IAAA,EAAM,MAAA,CAAO,GAAA,CAAI,MAAA,EAAQ,OAAO,IAAI,CAAA;AAChD,IAAA,IAAI,QAAQ,MAAA,EAAQ,MAAA,CAAO,GAAA,CAAI,QAAA,EAAU,OAAO,MAAM,CAAA;AACtD,IAAA,IAAI,QAAQ,UAAA,EAAY,MAAA,CAAO,GAAA,CAAI,UAAA,EAAY,OAAO,UAAU,CAAA;AAChE,IAAA,IAAI,MAAA,EAAQ,IAAA,EAAM,MAAA,EAAQ,MAAA,CAAO,GAAA,CAAI,QAAQ,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,GAAG,CAAC,CAAA;AAClE,IAAA,IAAI,MAAA,EAAQ,MAAM,MAAA,CAAO,GAAA,CAAI,QAAQ,MAAA,CAAO,MAAA,CAAO,IAAI,CAAC,CAAA;AACxD,IAAA,IAAI,MAAA,EAAQ,UAAU,MAAA,CAAO,GAAA,CAAI,YAAY,MAAA,CAAO,MAAA,CAAO,QAAQ,CAAC,CAAA;AAEpE,IAAA,MAAM,EAAA,GAAK,OAAO,QAAA,EAAS;AAC3B,IAAA,OAAO,IAAA,CAAK,MAA2B,CAAA,OAAA,EAAU,EAAA,GAAK,IAAI,EAAE,CAAA,CAAA,GAAK,EAAE,CAAA,CAAE,CAAA;AAAA,EACvE;AAAA,EAEA,MAAM,SAAS,EAAA,EAA8B;AAC3C,IAAA,OAAO,KAAK,KAAA,CAAe,CAAA,QAAA,EAAW,kBAAA,CAAmB,EAAE,CAAC,CAAA,CAAE,CAAA;AAAA,EAChE;AAAA,EAEA,MAAM,YAAY,EAAA,EAA6B;AAC7C,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,OAAA,EAAQ;AAChC,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,QAAA,CAAS,KAAA;AAAA,MACnC,CAAA,EAAG,IAAI,CAAA,QAAA,EAAW,kBAAA,CAAmB,EAAE,CAAC,CAAA,IAAA;AAAA,KAC1C;AACA,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI,MAAM,IAAI,KAAA,CAAM,CAAA,2BAAA,EAA8B,QAAA,CAAS,MAAM,CAAA,CAAE,CAAA;AACjF,IAAA,OAAO,SAAS,IAAA,EAAK;AAAA,EACvB;AAAA,EAEA,MAAM,eAAe,EAAA,EAA6B;AAChD,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,OAAA,EAAQ;AAChC,IAAA,OAAO,CAAA,EAAG,IAAI,CAAA,QAAA,EAAW,kBAAA,CAAmB,EAAE,CAAC,CAAA,SAAA,CAAA;AAAA,EACjD;AAAA,EAEA,MAAM,aAAa,EAAA,EAA2B;AAC5C,IAAA,MAAM,KAAK,KAAA,CAAM,CAAA,QAAA,EAAW,kBAAA,CAAmB,EAAE,CAAC,CAAA,cAAA,CAAA,EAAkB;AAAA,MAClE,MAAA,EAAQ;AAAA,KACT,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,aAAA,GAA0C;AAC9C,IAAA,OAAO,IAAA,CAAK,MAAuB,YAAY,CAAA;AAAA,EACjD;AAAA,EAEA,MAAM,kBAAkB,EAAA,EAAoC;AAC1D,IAAA,OAAO,IAAA,CAAK,KAAA;AAAA,MACV,CAAA,WAAA,EAAc,kBAAA,CAAmB,EAAE,CAAC,CAAA,OAAA;AAAA,KACtC;AAAA,EACF;AAAA,EAEA,MAAM,YAAY,EAAA,EAA2B;AAC3C,IAAA,MAAM,KAAK,KAAA,CAAM,CAAA,WAAA,EAAc,kBAAA,CAAmB,EAAE,CAAC,CAAA,KAAA,CAAA,EAAS;AAAA,MAC5D,MAAA,EAAQ;AAAA,KACT,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,QAAA,GAAgC;AACpC,IAAA,OAAO,IAAA,CAAK,MAAkB,QAAQ,CAAA;AAAA,EACxC;AACF;;;;"}
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 '@nospt/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,19 +1,8 @@
1
- import { jsxs, jsx } from 'react/jsx-runtime';
2
- import Box from '@mui/material/Box';
3
- import Card from '@mui/material/Card';
4
- import CardActions from '@mui/material/CardActions';
5
- import CardContent from '@mui/material/CardContent';
6
- import Chip from '@mui/material/Chip';
7
- import IconButton from '@mui/material/IconButton';
8
- import Tooltip from '@mui/material/Tooltip';
9
- import Typography from '@mui/material/Typography';
10
- import OpenInNewIcon from '@mui/icons-material/OpenInNew';
11
- import DownloadIcon from '@mui/icons-material/Download';
12
- import ArticleIcon from '@mui/icons-material/Article';
13
- import SmartToyIcon from '@mui/icons-material/SmartToy';
14
- import BuildIcon from '@mui/icons-material/Build';
15
- import AccountTreeIcon from '@mui/icons-material/AccountTree';
1
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
+ import { Card, CardBody, Flex, Box, Text, TagGroup, Tag, CardFooter, TooltipTrigger, ButtonIcon, Tooltip } from '@backstage/ui';
3
+ import { RiGitBranchLine, RiToolsLine, RiRobot2Line, RiArticleLine, RiDownloadLine, RiExternalLinkLine } from '@remixicon/react';
16
4
  import { ToolIcon } from '../ToolIcon/ToolIcon.esm.js';
5
+ import styles from './AssetCard.module.css.esm.js';
17
6
 
18
7
  const POPULAR_THRESHOLD = 5;
19
8
  const NEW_DAYS_MS = 14 * 24 * 60 * 60 * 1e3;
@@ -25,10 +14,10 @@ const TOOL_LABELS = {
25
14
  "cursor": "Cursor"
26
15
  };
27
16
  const TYPE_CONFIG = {
28
- instruction: { label: "Instruction", color: "#2563EB", bg: "#EFF6FF", Icon: ArticleIcon },
29
- agent: { label: "Agent", color: "#7C3AED", bg: "#F5F3FF", Icon: SmartToyIcon },
30
- skill: { label: "Skill", color: "#059669", bg: "#ECFDF5", Icon: BuildIcon },
31
- workflow: { label: "Workflow", color: "#D97706", bg: "#FFFBEB", Icon: AccountTreeIcon }
17
+ instruction: { label: "Instruction", color: "#54A0FF", bg: "rgba(84, 160, 255, 0.15)", Icon: RiArticleLine },
18
+ agent: { label: "Agent", color: "#FF6B9D", bg: "rgba(255, 107, 157, 0.15)", Icon: RiRobot2Line },
19
+ skill: { label: "Skill", color: "#6AB04C", bg: "rgba(106, 176, 76, 0.15)", Icon: RiToolsLine },
20
+ workflow: { label: "Workflow", color: "#F9CA24", bg: "rgba(249, 202, 36, 0.15)", Icon: RiGitBranchLine }
32
21
  };
33
22
  function AssetCard({ asset, onView, onInstall }) {
34
23
  const cfg = TYPE_CONFIG[asset.type];
@@ -38,151 +27,88 @@ function AssetCard({ asset, onView, onInstall }) {
38
27
  return /* @__PURE__ */ jsxs(
39
28
  Card,
40
29
  {
41
- variant: "outlined",
42
- sx: {
43
- height: "100%",
44
- display: "flex",
45
- flexDirection: "column",
46
- borderRadius: 2,
47
- border: "1px solid",
48
- borderColor: "divider",
49
- borderLeft: `3px solid ${cfg.color}`,
50
- transition: "all 0.18s ease",
51
- "&:hover": {
52
- boxShadow: `0 6px 24px ${cfg.color}30`,
53
- borderColor: cfg.color,
54
- transform: "translateY(-2px)"
55
- }
56
- },
30
+ className: styles.card,
31
+ style: { "--card-accent": cfg.color },
57
32
  children: [
58
- /* @__PURE__ */ jsxs(CardContent, { sx: { p: 1.5, pb: "0 !important", flex: 1 }, children: [
59
- /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "flex-start", gap: 1, mb: 1 }, children: [
33
+ /* @__PURE__ */ jsxs(CardBody, { className: styles.cardContent, children: [
34
+ /* @__PURE__ */ jsxs(Flex, { className: styles.header, children: [
60
35
  /* @__PURE__ */ jsx(
61
36
  Box,
62
37
  {
63
- sx: {
64
- width: 36,
65
- height: 36,
66
- borderRadius: 1.5,
67
- backgroundColor: cfg.bg,
68
- display: "flex",
69
- alignItems: "center",
70
- justifyContent: "center",
71
- flexShrink: 0,
72
- boxShadow: `0 2px 8px ${cfg.color}25`
73
- },
38
+ className: styles.iconBox,
39
+ style: { backgroundColor: cfg.bg },
74
40
  children: asset.icon ? /* @__PURE__ */ jsx(
75
- Box,
41
+ "img",
76
42
  {
77
- component: "img",
78
43
  src: asset.icon,
79
44
  alt: asset.label ?? asset.name,
80
- sx: { width: 20, height: 20, objectFit: "contain" },
45
+ className: styles.iconImage,
81
46
  onError: (e) => {
82
47
  e.target.style.display = "none";
83
48
  }
84
49
  }
85
- ) : /* @__PURE__ */ jsx(TypeIcon, { sx: { color: cfg.color, fontSize: "1.1rem" } })
50
+ ) : /* @__PURE__ */ jsx(TypeIcon, { size: 18, style: { color: cfg.color } })
86
51
  }
87
52
  ),
88
- /* @__PURE__ */ jsxs(Box, { sx: { flex: 1, minWidth: 0 }, children: [
89
- /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 0.5, mb: 0.2 }, children: [
90
- /* @__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 }),
91
- isNew && /* @__PURE__ */ jsx(
92
- Chip,
93
- {
94
- label: "New",
95
- size: "small",
96
- sx: {
97
- height: 16,
98
- fontSize: "0.58rem",
99
- fontWeight: 700,
100
- backgroundColor: "#059669",
101
- color: "#fff",
102
- borderRadius: 1,
103
- flexShrink: 0,
104
- "& .MuiChip-label": { px: "5px" }
105
- }
106
- }
107
- )
53
+ /* @__PURE__ */ jsxs(Box, { className: styles.headerInfo, children: [
54
+ /* @__PURE__ */ jsxs(Flex, { className: styles.titleRow, children: [
55
+ /* @__PURE__ */ jsx(Text, { variant: "body-medium", weight: "bold", className: styles.title, title: asset.label ?? asset.name, children: asset.label ?? asset.name }),
56
+ isNew && /* @__PURE__ */ jsx(TagGroup, { "aria-label": "Status", children: /* @__PURE__ */ jsx(Tag, { id: "new", size: "small", className: styles.newBadge, children: "New" }) })
108
57
  ] }),
109
- /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { color: cfg.color, fontWeight: 600 }, children: cfg.label })
58
+ /* @__PURE__ */ jsx(Text, { variant: "body-x-small", style: { color: cfg.color, fontWeight: 600 }, children: cfg.label })
110
59
  ] })
111
60
  ] }),
112
- /* @__PURE__ */ jsx(
113
- Typography,
114
- {
115
- variant: "caption",
116
- color: "text.secondary",
117
- sx: {
118
- mb: 1,
119
- display: "-webkit-box",
120
- WebkitLineClamp: 2,
121
- WebkitBoxOrient: "vertical",
122
- overflow: "hidden",
123
- lineHeight: 1.4
124
- },
125
- children: asset.description
126
- }
127
- ),
128
- /* @__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(
129
- Chip,
130
- {
131
- icon: /* @__PURE__ */ jsx(ToolIcon, { tool, sx: { fontSize: "0.75rem !important" } }),
132
- label: TOOL_LABELS[tool] ?? tool,
133
- size: "small",
134
- sx: {
135
- height: 18,
136
- fontSize: "0.65rem",
137
- fontWeight: 600,
138
- backgroundColor: "action.hover",
139
- color: "text.secondary",
140
- borderRadius: 1,
141
- "& .MuiChip-icon": { ml: "4px" }
142
- }
143
- },
144
- tool
145
- )) }),
146
- asset.tags.length > 0 && /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", gap: 0.5, flexWrap: "wrap" }, children: [
147
- asset.tags.slice(0, 3).map((tag) => /* @__PURE__ */ jsx(
148
- Chip,
149
- {
150
- label: `#${tag}`,
151
- size: "small",
152
- sx: {
153
- height: 16,
154
- fontSize: "0.6rem",
155
- color: "text.disabled",
156
- backgroundColor: "transparent",
157
- border: "1px solid",
158
- borderColor: "divider",
159
- borderRadius: 1
160
- }
161
- },
61
+ /* @__PURE__ */ jsx(Text, { variant: "body-x-small", color: "secondary", className: styles.description, children: asset.description }),
62
+ /* @__PURE__ */ jsx(TagGroup, { "aria-label": "Compatible tools", className: `${styles.toolsRow} ${asset.tags.length > 0 ? styles.toolsRowWithTags : ""}`, children: asset.tools.map((tool) => /* @__PURE__ */ jsx(Tag, { id: tool, size: "small", className: styles.toolChip, icon: /* @__PURE__ */ jsx(ToolIcon, { tool, size: 12 }), children: TOOL_LABELS[tool] ?? tool }, tool)) }),
63
+ asset.tags.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
64
+ /* @__PURE__ */ jsx(TagGroup, { "aria-label": "Tags", className: styles.tagsRow, children: asset.tags.slice(0, 3).map((tag) => /* @__PURE__ */ jsxs(Tag, { id: tag, size: "small", className: styles.tagChip, children: [
65
+ "#",
162
66
  tag
163
- )),
164
- asset.tags.length > 3 && /* @__PURE__ */ jsxs(Typography, { variant: "caption", color: "text.disabled", sx: { alignSelf: "center" }, children: [
67
+ ] }, tag)) }),
68
+ asset.tags.length > 3 && /* @__PURE__ */ jsxs(Text, { variant: "body-x-small", color: "secondary", style: { alignSelf: "center" }, children: [
165
69
  "+",
166
70
  asset.tags.length - 3
167
71
  ] })
168
72
  ] })
169
73
  ] }),
170
- /* @__PURE__ */ jsxs(CardActions, { sx: { px: 1.5, py: 1, justifyContent: "space-between", mt: "auto", borderTop: "1px solid", borderColor: "divider" }, children: [
171
- /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 1 }, children: [
172
- /* @__PURE__ */ jsxs(Typography, { variant: "caption", color: "text.disabled", sx: { fontSize: "0.65rem" }, children: [
74
+ /* @__PURE__ */ jsxs(CardFooter, { className: styles.cardActions, children: [
75
+ /* @__PURE__ */ jsxs(Flex, { className: styles.metaRow, children: [
76
+ /* @__PURE__ */ jsxs(Text, { variant: "body-x-small", color: "secondary", className: styles.metaText, children: [
173
77
  "v",
174
78
  asset.version,
175
79
  " \xB7 ",
176
80
  asset.author
177
81
  ] }),
178
- asset.installCount > 0 && /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 0.25 }, children: [
179
- /* @__PURE__ */ jsx(Typography, { sx: { fontSize: "0.65rem", lineHeight: 1 }, children: isPopular ? "\u{1F525}" : "\u2193" }),
180
- /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.disabled", sx: { fontSize: "0.65rem" }, children: asset.installCount })
82
+ asset.installCount > 0 && /* @__PURE__ */ jsxs(Flex, { className: styles.installCountRow, children: [
83
+ /* @__PURE__ */ jsx(Text, { className: styles.installEmoji, children: isPopular ? "\u{1F525}" : "\u2193" }),
84
+ /* @__PURE__ */ jsx(Text, { variant: "body-x-small", color: "secondary", className: styles.metaText, children: asset.installCount })
181
85
  ] })
182
86
  ] }),
183
- /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", gap: 0.25 }, children: [
184
- /* @__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" }) }) }),
185
- /* @__PURE__ */ jsx(Tooltip, { title: "View details", children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: () => onView(asset.id), children: /* @__PURE__ */ jsx(OpenInNewIcon, { fontSize: "small" }) }) })
87
+ /* @__PURE__ */ jsxs(Flex, { className: styles.actionsRow, children: [
88
+ /* @__PURE__ */ jsxs(TooltipTrigger, { children: [
89
+ /* @__PURE__ */ jsx(
90
+ ButtonIcon,
91
+ {
92
+ "aria-label": "Install in editor",
93
+ icon: /* @__PURE__ */ jsx(RiDownloadLine, { size: 16 }),
94
+ variant: "tertiary",
95
+ onPress: () => onInstall(asset.id)
96
+ }
97
+ ),
98
+ /* @__PURE__ */ jsx(Tooltip, { children: "Install in editor" })
99
+ ] }),
100
+ /* @__PURE__ */ jsxs(TooltipTrigger, { children: [
101
+ /* @__PURE__ */ jsx(
102
+ ButtonIcon,
103
+ {
104
+ "aria-label": "View details",
105
+ icon: /* @__PURE__ */ jsx(RiExternalLinkLine, { size: 16 }),
106
+ variant: "tertiary",
107
+ onPress: () => onView(asset.id)
108
+ }
109
+ ),
110
+ /* @__PURE__ */ jsx(Tooltip, { children: "View details" })
111
+ ] })
186
112
  ] })
187
113
  ] })
188
114
  ]
@@ -1 +1 @@
1
- {"version":3,"file":"AssetCard.esm.js","sources":["../../../src/components/AssetCard/AssetCard.tsx"],"sourcesContent":["import type { ElementType } from 'react';\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 '@nospt/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; bg: string; Icon: ElementType }> = {\n instruction: { label: 'Instruction', color: '#2563EB', bg: '#EFF6FF', Icon: ArticleIcon },\n agent: { label: 'Agent', color: '#7C3AED', bg: '#F5F3FF', Icon: SmartToyIcon },\n skill: { label: 'Skill', color: '#059669', bg: '#ECFDF5', Icon: BuildIcon },\n workflow: { label: 'Workflow', color: '#D97706', bg: '#FFFBEB', 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 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: 36,\n height: 36,\n borderRadius: 1.5,\n backgroundColor: cfg.bg,\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: 20, height: 20, objectFit: 'contain' }}\n onError={e => { (e.target as HTMLImageElement).style.display = 'none'; }}\n />\n ) : (\n <TypeIcon sx={{ color: cfg.color, fontSize: '1.1rem' }} />\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: 16,\n fontSize: '0.58rem',\n fontWeight: 700,\n backgroundColor: '#059669',\n color: '#fff',\n borderRadius: 1,\n flexShrink: 0,\n '& .MuiChip-label': { px: '5px' },\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 { Box, Flex, Text, Card, CardBody, CardFooter, Tag, TagGroup, ButtonIcon, Tooltip, TooltipTrigger } from '@backstage/ui';\nimport { RiDownloadLine, RiExternalLinkLine } from '@remixicon/react';\nimport { RiArticleLine, RiRobot2Line, RiToolsLine, RiGitBranchLine } from '@remixicon/react';\nimport type { AiAssetSummary, AssetType, AiTool } from '@nospt/plugin-dev-ai-hub-common';\nimport { ToolIcon } from '../ToolIcon';\nimport styles from './AssetCard.module.css';\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; bg: string; Icon: ElementType }> = {\n instruction: { label: 'Instruction', color: '#54A0FF', bg: 'rgba(84, 160, 255, 0.15)', Icon: RiArticleLine },\n agent: { label: 'Agent', color: '#FF6B9D', bg: 'rgba(255, 107, 157, 0.15)', Icon: RiRobot2Line },\n skill: { label: 'Skill', color: '#6AB04C', bg: 'rgba(106, 176, 76, 0.15)', Icon: RiToolsLine },\n workflow: { label: 'Workflow', color: '#F9CA24', bg: 'rgba(249, 202, 36, 0.15)', Icon: RiGitBranchLine },\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 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 className={styles.card}\n style={{ '--card-accent': cfg.color } as React.CSSProperties}\n >\n <CardBody className={styles.cardContent}>\n {/* Header */}\n <Flex className={styles.header}>\n <Box\n className={styles.iconBox}\n style={{ backgroundColor: cfg.bg }}\n >\n {asset.icon ? (\n <img\n src={asset.icon}\n alt={asset.label ?? asset.name}\n className={styles.iconImage}\n onError={e => { (e.target as HTMLImageElement).style.display = 'none'; }}\n />\n ) : (\n <TypeIcon size={18} style={{ color: cfg.color }} />\n )}\n </Box>\n\n <Box className={styles.headerInfo}>\n <Flex className={styles.titleRow}>\n <Text variant=\"body-medium\" weight=\"bold\" className={styles.title} title={asset.label ?? asset.name}>\n {asset.label ?? asset.name}\n </Text>\n {isNew && (\n <TagGroup aria-label=\"Status\">\n <Tag id=\"new\" size=\"small\" className={styles.newBadge}>New</Tag>\n </TagGroup>\n )}\n </Flex>\n <Text variant=\"body-x-small\" style={{ color: cfg.color, fontWeight: 600 }}>\n {cfg.label}\n </Text>\n </Box>\n </Flex>\n\n {/* Description */}\n <Text variant=\"body-x-small\" color=\"secondary\" className={styles.description}>\n {asset.description}\n </Text>\n\n {/* Tools */}\n <TagGroup aria-label=\"Compatible tools\" className={`${styles.toolsRow} ${asset.tags.length > 0 ? styles.toolsRowWithTags : ''}`}>\n {asset.tools.map(tool => (\n <Tag key={tool} id={tool} size=\"small\" className={styles.toolChip} icon={<ToolIcon tool={tool as AiTool} size={12} />}>\n {TOOL_LABELS[tool as AiTool] ?? tool}\n </Tag>\n ))}\n </TagGroup>\n\n {/* Tags */}\n {asset.tags.length > 0 && (\n <>\n <TagGroup aria-label=\"Tags\" className={styles.tagsRow}>\n {asset.tags.slice(0, 3).map(tag => (\n <Tag key={tag} id={tag} size=\"small\" className={styles.tagChip}>\n #{tag}\n </Tag>\n ))}\n </TagGroup>\n {asset.tags.length > 3 && (\n <Text variant=\"body-x-small\" color=\"secondary\" style={{ alignSelf: 'center' }}>\n +{asset.tags.length - 3}\n </Text>\n )}\n </>\n )}\n </CardBody>\n\n <CardFooter className={styles.cardActions}>\n <Flex className={styles.metaRow}>\n <Text variant=\"body-x-small\" color=\"secondary\" className={styles.metaText}>\n v{asset.version} ยท {asset.author}\n </Text>\n {asset.installCount > 0 && (\n <Flex className={styles.installCountRow}>\n <Text className={styles.installEmoji}>\n {isPopular ? '๐Ÿ”ฅ' : 'โ†“'}\n </Text>\n <Text variant=\"body-x-small\" color=\"secondary\" className={styles.metaText}>\n {asset.installCount}\n </Text>\n </Flex>\n )}\n </Flex>\n <Flex className={styles.actionsRow}>\n <TooltipTrigger>\n <ButtonIcon\n aria-label=\"Install in editor\"\n icon={<RiDownloadLine size={16} />}\n variant=\"tertiary\"\n onPress={() => onInstall(asset.id)}\n />\n <Tooltip>Install in editor</Tooltip>\n </TooltipTrigger>\n <TooltipTrigger>\n <ButtonIcon\n aria-label=\"View details\"\n icon={<RiExternalLinkLine size={16} />}\n variant=\"tertiary\"\n onPress={() => onView(asset.id)}\n />\n <Tooltip>View details</Tooltip>\n </TooltipTrigger>\n </Flex>\n </CardFooter>\n </Card>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import styleInject from '../../node_modules_dist/style-inject/dist/style-inject.es.esm.js';
2
+
3
+ var css_248z = "@layer components {\n .AssetCard_card__9c326df4e1 {\n height: 100%;\n display: flex;\n flex-direction: column;\n border-radius: var(--bui-radius-2);\n border: 1px solid var(--bui-border-1);\n border-left: 3px solid var(--card-accent);\n background-color: var(--bui-bg-neutral-1);\n transition: all 0.18s ease;\n }\n\n .AssetCard_card__9c326df4e1:hover {\n box-shadow: 0 6px 24px color-mix(in srgb, var(--card-accent) 19%, transparent);\n border-color: var(--card-accent);\n transform: translateY(-2px);\n }\n\n .AssetCard_cardContent__9c326df4e1 {\n padding: var(--bui-space-3);\n padding-bottom: 0;\n flex: 1;\n }\n\n .AssetCard_header__9c326df4e1 {\n display: flex;\n align-items: flex-start;\n gap: var(--bui-space-2);\n margin-bottom: var(--bui-space-2);\n }\n\n .AssetCard_iconBox__9c326df4e1 {\n width: 36px;\n height: 36px;\n border-radius: var(--bui-radius-2);\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n box-shadow: 0 2px 8px color-mix(in srgb, var(--card-accent) 15%, transparent);\n }\n\n .AssetCard_iconImage__9c326df4e1 {\n width: 20px;\n height: 20px;\n object-fit: contain;\n }\n\n .AssetCard_headerInfo__9c326df4e1 {\n flex: 1;\n min-width: 0;\n }\n\n .AssetCard_titleRow__9c326df4e1 {\n display: flex;\n align-items: center;\n gap: var(--bui-space-1);\n margin-bottom: 2px;\n }\n\n .AssetCard_title__9c326df4e1 {\n line-height: 1.2;\n flex: 1;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n }\n\n .AssetCard_newBadge__9c326df4e1 {\n height: 16px;\n flex-shrink: 0;\n font-size: 0.58rem;\n font-weight: 700;\n background-color: #6AB04C;\n color: #fff;\n }\n\n .AssetCard_description__9c326df4e1 {\n margin-bottom: var(--bui-space-2);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n line-height: 1.4;\n }\n\n .AssetCard_toolsRow__9c326df4e1 .bui-TagList {\n display: flex;\n gap: var(--bui-space-1);\n flex-wrap: wrap;\n }\n\n .AssetCard_toolsRowWithTags__9c326df4e1 {\n margin-bottom: 6px;\n }\n\n .AssetCard_toolChip__9c326df4e1 {\n height: 18px;\n font-size: 0.65rem;\n font-weight: 600;\n background-color: var(--bui-bg-neutral-1-hover);\n color: var(--bui-fg-secondary);\n }\n\n .AssetCard_tagsRow__9c326df4e1 .bui-TagList {\n display: flex;\n gap: var(--bui-space-1);\n flex-wrap: wrap;\n }\n\n .AssetCard_tagChip__9c326df4e1 {\n height: 16px;\n font-size: 0.6rem;\n color: var(--bui-fg-secondary);\n background-color: transparent;\n border: 1px solid var(--bui-border-1);\n }\n\n .AssetCard_cardActions__9c326df4e1 {\n padding: var(--bui-space-2) var(--bui-space-3);\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-top: auto;\n border-top: 1px solid var(--bui-border-1);\n }\n\n .AssetCard_metaRow__9c326df4e1 {\n display: flex;\n align-items: center;\n gap: var(--bui-space-2);\n }\n\n .AssetCard_metaText__9c326df4e1 {\n font-size: 0.65rem;\n }\n\n .AssetCard_installCountRow__9c326df4e1 {\n display: flex;\n align-items: center;\n gap: 2px;\n }\n\n .AssetCard_installEmoji__9c326df4e1 {\n font-size: 0.65rem;\n line-height: 1;\n }\n\n .AssetCard_actionsRow__9c326df4e1 {\n display: flex;\n gap: 2px;\n }\n}\n";
4
+ var styles = {"card":"AssetCard_card__9c326df4e1","cardContent":"AssetCard_cardContent__9c326df4e1","header":"AssetCard_header__9c326df4e1","iconBox":"AssetCard_iconBox__9c326df4e1","iconImage":"AssetCard_iconImage__9c326df4e1","headerInfo":"AssetCard_headerInfo__9c326df4e1","titleRow":"AssetCard_titleRow__9c326df4e1","title":"AssetCard_title__9c326df4e1","newBadge":"AssetCard_newBadge__9c326df4e1","description":"AssetCard_description__9c326df4e1","toolsRow":"AssetCard_toolsRow__9c326df4e1","toolsRowWithTags":"AssetCard_toolsRowWithTags__9c326df4e1","toolChip":"AssetCard_toolChip__9c326df4e1","tagsRow":"AssetCard_tagsRow__9c326df4e1","tagChip":"AssetCard_tagChip__9c326df4e1","cardActions":"AssetCard_cardActions__9c326df4e1","metaRow":"AssetCard_metaRow__9c326df4e1","metaText":"AssetCard_metaText__9c326df4e1","installCountRow":"AssetCard_installCountRow__9c326df4e1","installEmoji":"AssetCard_installEmoji__9c326df4e1","actionsRow":"AssetCard_actionsRow__9c326df4e1"};
5
+ styleInject(css_248z);
6
+
7
+ export { styles as default };
8
+ //# sourceMappingURL=AssetCard.module.css.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AssetCard.module.css.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}