@nospt/plugin-dev-ai-hub 0.2.3 → 0.3.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 (122) hide show
  1. package/dist/api/DevAiHubResourceClient.esm.js +97 -0
  2. package/dist/api/DevAiHubResourceClient.esm.js.map +1 -0
  3. package/dist/components/AiResourcesPage/AiResourcesPage.esm.js +150 -0
  4. package/dist/components/AiResourcesPage/AiResourcesPage.esm.js.map +1 -0
  5. package/dist/components/AiResourcesPage/AiResourcesPage.module.css.esm.js +8 -0
  6. package/dist/components/AiResourcesPage/AiResourcesPage.module.css.esm.js.map +1 -0
  7. package/dist/components/AiResourcesPage/ResourceCard.esm.js +158 -0
  8. package/dist/components/AiResourcesPage/ResourceCard.esm.js.map +1 -0
  9. package/dist/components/AiResourcesPage/ResourceCard.module.css.esm.js +8 -0
  10. package/dist/components/AiResourcesPage/ResourceCard.module.css.esm.js.map +1 -0
  11. package/dist/components/AiResourcesPage/ResourceDetailPanel.esm.js +234 -0
  12. package/dist/components/AiResourcesPage/ResourceDetailPanel.esm.js.map +1 -0
  13. package/dist/components/AiResourcesPage/ResourceDetailPanel.module.css.esm.js +8 -0
  14. package/dist/components/AiResourcesPage/ResourceDetailPanel.module.css.esm.js.map +1 -0
  15. package/dist/components/AiResourcesPage/ResourceFilters.esm.js +242 -0
  16. package/dist/components/AiResourcesPage/ResourceFilters.esm.js.map +1 -0
  17. package/dist/components/AiResourcesPage/ResourceFilters.module.css.esm.js +8 -0
  18. package/dist/components/AiResourcesPage/ResourceFilters.module.css.esm.js.map +1 -0
  19. package/dist/components/AiResourcesPage/ResourceInstallDialog.esm.js +366 -0
  20. package/dist/components/AiResourcesPage/ResourceInstallDialog.esm.js.map +1 -0
  21. package/dist/components/AiResourcesPage/ResourceInstallDialog.module.css.esm.js +8 -0
  22. package/dist/components/AiResourcesPage/ResourceInstallDialog.module.css.esm.js.map +1 -0
  23. package/dist/components/AiResourcesPage/StatTiles.esm.js +49 -0
  24. package/dist/components/AiResourcesPage/StatTiles.esm.js.map +1 -0
  25. package/dist/components/AiResourcesPage/StatTiles.module.css.esm.js +8 -0
  26. package/dist/components/AiResourcesPage/StatTiles.module.css.esm.js.map +1 -0
  27. package/dist/components/AiResourcesPage/index.esm.js +6 -0
  28. package/dist/components/AiResourcesPage/typeMeta.esm.js +35 -0
  29. package/dist/components/AiResourcesPage/typeMeta.esm.js.map +1 -0
  30. package/dist/components/ToolIcon/ToolIcon.esm.js +26 -9
  31. package/dist/components/ToolIcon/ToolIcon.esm.js.map +1 -1
  32. package/dist/hooks/useResourceBody.esm.js +39 -0
  33. package/dist/hooks/useResourceBody.esm.js.map +1 -0
  34. package/dist/hooks/useResources.esm.js +26 -0
  35. package/dist/hooks/useResources.esm.js.map +1 -0
  36. package/dist/hooks/useTelemetryCounts.esm.js +24 -0
  37. package/dist/hooks/useTelemetryCounts.esm.js.map +1 -0
  38. package/dist/index.d.ts +1 -38
  39. package/dist/index.esm.js +1 -3
  40. package/dist/index.esm.js.map +1 -1
  41. package/dist/plugin.esm.js +17 -26
  42. package/dist/plugin.esm.js.map +1 -1
  43. package/dist/routes.esm.js +1 -2
  44. package/dist/routes.esm.js.map +1 -1
  45. package/package.json +17 -10
  46. package/.eslintrc.js +0 -1
  47. package/dev/index.tsx +0 -32
  48. package/dist/api/DevAiHubClient.esm.js +0 -78
  49. package/dist/api/DevAiHubClient.esm.js.map +0 -1
  50. package/dist/components/AssetCard/AssetCard.esm.js +0 -120
  51. package/dist/components/AssetCard/AssetCard.esm.js.map +0 -1
  52. package/dist/components/AssetCard/AssetCard.module.css.esm.js +0 -8
  53. package/dist/components/AssetCard/AssetCard.module.css.esm.js.map +0 -1
  54. package/dist/components/AssetDetailPanel/AssetDetailPanel.esm.js +0 -218
  55. package/dist/components/AssetDetailPanel/AssetDetailPanel.esm.js.map +0 -1
  56. package/dist/components/AssetDetailPanel/AssetDetailPanel.module.css.esm.js +0 -8
  57. package/dist/components/AssetDetailPanel/AssetDetailPanel.module.css.esm.js.map +0 -1
  58. package/dist/components/AssetFilters/AssetFilters.esm.js +0 -176
  59. package/dist/components/AssetFilters/AssetFilters.esm.js.map +0 -1
  60. package/dist/components/AssetFilters/AssetFilters.module.css.esm.js +0 -8
  61. package/dist/components/AssetFilters/AssetFilters.module.css.esm.js.map +0 -1
  62. package/dist/components/AssetInstallDialog/AssetInstallDialog.esm.js +0 -138
  63. package/dist/components/AssetInstallDialog/AssetInstallDialog.esm.js.map +0 -1
  64. package/dist/components/AssetInstallDialog/AssetInstallDialog.module.css.esm.js +0 -8
  65. package/dist/components/AssetInstallDialog/AssetInstallDialog.module.css.esm.js.map +0 -1
  66. package/dist/components/DevAiHubPage/DevAiHubPage.esm.js +0 -195
  67. package/dist/components/DevAiHubPage/DevAiHubPage.esm.js.map +0 -1
  68. package/dist/components/DevAiHubPage/DevAiHubPage.module.css.esm.js +0 -8
  69. package/dist/components/DevAiHubPage/DevAiHubPage.module.css.esm.js.map +0 -1
  70. package/dist/components/DevAiHubPage/index.esm.js +0 -6
  71. package/dist/components/McpConfigPage/McpConfigPage.esm.js +0 -200
  72. package/dist/components/McpConfigPage/McpConfigPage.esm.js.map +0 -1
  73. package/dist/components/McpConfigPage/McpConfigPage.module.css.esm.js +0 -8
  74. package/dist/components/McpConfigPage/McpConfigPage.module.css.esm.js.map +0 -1
  75. package/dist/components/McpConfigPage/index.esm.js +0 -6
  76. package/dist/components/McpConfigPage/index.esm.js.map +0 -1
  77. package/dist/hooks/index.esm.js +0 -114
  78. package/dist/hooks/index.esm.js.map +0 -1
  79. package/dist/pluginLegacy.esm.js +0 -27
  80. package/dist/pluginLegacy.esm.js.map +0 -1
  81. package/package.json-prepack +0 -85
  82. package/src/api/DevAiHubClient.ts +0 -110
  83. package/src/components/AssetCard/AssetCard.module.css +0 -153
  84. package/src/components/AssetCard/AssetCard.test.tsx +0 -182
  85. package/src/components/AssetCard/AssetCard.tsx +0 -152
  86. package/src/components/AssetCard/index.ts +0 -1
  87. package/src/components/AssetDetailPanel/AssetDetailPanel.module.css +0 -207
  88. package/src/components/AssetDetailPanel/AssetDetailPanel.tsx +0 -313
  89. package/src/components/AssetDetailPanel/index.ts +0 -1
  90. package/src/components/AssetFilters/AssetFilters.module.css +0 -67
  91. package/src/components/AssetFilters/AssetFilters.tsx +0 -219
  92. package/src/components/AssetFilters/index.ts +0 -2
  93. package/src/components/AssetInstallDialog/AssetInstallDialog.module.css +0 -72
  94. package/src/components/AssetInstallDialog/AssetInstallDialog.tsx +0 -176
  95. package/src/components/AssetInstallDialog/index.ts +0 -1
  96. package/src/components/DevAiHubPage/DevAiHubPage.module.css +0 -105
  97. package/src/components/DevAiHubPage/DevAiHubPage.tsx +0 -197
  98. package/src/components/DevAiHubPage/index.ts +0 -1
  99. package/src/components/McpConfigDialog/McpConfigDialog.module.css +0 -111
  100. package/src/components/McpConfigDialog/McpConfigDialog.tsx +0 -271
  101. package/src/components/McpConfigDialog/index.ts +0 -1
  102. package/src/components/McpConfigPage/McpConfigPage.module.css +0 -121
  103. package/src/components/McpConfigPage/McpConfigPage.tsx +0 -242
  104. package/src/components/McpConfigPage/index.ts +0 -1
  105. package/src/components/ToolIcon/ToolIcon.tsx +0 -65
  106. package/src/components/ToolIcon/index.ts +0 -1
  107. package/src/css-modules.d.ts +0 -4
  108. package/src/hooks/index.ts +0 -152
  109. package/src/index.ts +0 -10
  110. package/src/plugin.tsx +0 -60
  111. package/src/pluginLegacy.ts +0 -42
  112. package/src/routes.ts +0 -4
  113. package/tests/e2e/.auth.json +0 -14
  114. package/tests/e2e/asset-detail.spec.ts +0 -90
  115. package/tests/e2e/asset-filtering.spec.ts +0 -145
  116. package/tests/e2e/asset-installation.spec.ts +0 -74
  117. package/tests/e2e/dev-ai-hub-page.spec.ts +0 -89
  118. package/tests/e2e/fixtures/base.ts +0 -61
  119. package/tests/e2e/fixtures/mock-api.ts +0 -221
  120. package/tests/e2e/mcp-config.spec.ts +0 -88
  121. package/tsconfig.json +0 -10
  122. /package/dist/components/{DevAiHubPage → AiResourcesPage}/index.esm.js.map +0 -0
@@ -1,219 +0,0 @@
1
- import type { ElementType } from 'react';
2
- import { Box, Flex, Text, SearchField, ToggleButton, ToggleButtonGroup } from '@backstage/ui';
3
- import { RiAppsLine, RiArticleLine, RiRobot2Line, RiToolsLine, RiGitBranchLine, RiDatabase2Line } from '@remixicon/react';
4
- import type { AssetType, AiTool, AiHubProvider } from '@nospt/plugin-dev-ai-hub-common';
5
- import { ToolIcon } from '../ToolIcon';
6
- import styles from './AssetFilters.module.css';
7
-
8
- const ASSET_TYPES: { value: AssetType | 'all'; label: string; color: string; Icon: ElementType }[] = [
9
- { value: 'all', label: 'All', color: '#DCDDE1', Icon: RiAppsLine },
10
- { value: 'instruction', label: 'Instructions', color: '#54A0FF', Icon: RiArticleLine },
11
- { value: 'agent', label: 'Agents', color: '#FF6B9D', Icon: RiRobot2Line },
12
- { value: 'skill', label: 'Skills', color: '#6AB04C', Icon: RiToolsLine },
13
- { value: 'workflow', label: 'Workflows', color: '#F9CA24', Icon: RiGitBranchLine },
14
- ];
15
-
16
- const AI_TOOLS: { value: AiTool | 'all'; label: string }[] = [
17
- { value: 'all', label: 'All Tools' },
18
- { value: 'claude-code', label: 'Claude Code' },
19
- { value: 'github-copilot', label: 'GitHub Copilot' },
20
- { value: 'google-gemini', label: 'Google Gemini' },
21
- { value: 'cursor', label: 'Cursor' },
22
- ];
23
-
24
- export interface AssetFiltersValue {
25
- types: AssetType[];
26
- tools: AiTool[];
27
- search: string;
28
- tags: string[];
29
- providerId?: string;
30
- }
31
-
32
- interface AssetFiltersProps {
33
- value: AssetFiltersValue;
34
- onChange: (value: AssetFiltersValue) => void;
35
- availableTags?: string[];
36
- providers?: AiHubProvider[];
37
- }
38
-
39
- export function AssetFilters({ value, onChange, availableTags = [], providers }: AssetFiltersProps) {
40
- const selectedType = value.types.length === 1 ? value.types[0] : 'all';
41
- const selectedTool = value.tools.length === 1 ? value.tools[0] : 'all';
42
- const showProviderFilter = providers && providers.length > 1;
43
-
44
- const handleTypeClick = (type: AssetType | 'all') => {
45
- onChange({ ...value, types: type === 'all' ? [] : [type] });
46
- };
47
-
48
- const handleToolClick = (tool: AiTool | 'all') => {
49
- onChange({ ...value, tools: tool === 'all' ? [] : [tool] });
50
- };
51
-
52
- const handleTagToggle = (tag: string) => {
53
- const next = value.tags.includes(tag)
54
- ? value.tags.filter(t => t !== tag)
55
- : [...value.tags, tag];
56
- onChange({ ...value, tags: next });
57
- };
58
-
59
- const handleProviderClick = (id: string | undefined) => {
60
- onChange({ ...value, providerId: id });
61
- };
62
-
63
- return (
64
- <Flex className={styles.container}>
65
- <SearchField
66
- aria-label="Search assets"
67
- className={styles.searchField}
68
- placeholder="Search assets by name, description or content…"
69
- value={value.search}
70
- onChange={v => onChange({ ...value, search: v })}
71
- />
72
-
73
- <Flex className={styles.filtersRow}>
74
- {/* Type filter */}
75
- <Box>
76
- <Text variant="body-x-small" color="secondary" className={styles.filterLabel}>
77
- Type
78
- </Text>
79
- <Flex className={styles.filterChips}>
80
- {ASSET_TYPES.map(t => {
81
- const isSelected = selectedType === t.value;
82
- const TypeIcon = t.Icon;
83
- return (
84
- <ToggleButton
85
- key={t.value}
86
- id={t.value}
87
- size="small"
88
- className={`${styles.filterChip} ${isSelected ? styles.filterChipSelected : ''}`}
89
- iconStart={<TypeIcon size={14} style={{ color: isSelected ? '#fff' : t.color }} />}
90
- isSelected={isSelected}
91
- onChange={() => handleTypeClick(t.value as AssetType | 'all')}
92
- style={{
93
- borderColor: isSelected ? t.color : `${t.color}30`,
94
- backgroundColor: isSelected ? t.color : `${t.color}10`,
95
- color: isSelected ? '#fff' : t.color,
96
- }}
97
- >
98
- {t.label}
99
- </ToggleButton>
100
- );
101
- })}
102
- </Flex>
103
- </Box>
104
-
105
- {/* AI Tool filter */}
106
- <Box>
107
- <Text variant="body-x-small" color="secondary" className={styles.filterLabel}>
108
- AI Tool
109
- </Text>
110
- <Flex className={styles.filterChips}>
111
- {AI_TOOLS.map(t => {
112
- const isSelected = selectedTool === t.value;
113
- const iconEl = t.value !== 'all'
114
- ? <ToolIcon tool={t.value as AiTool} branded size={14} />
115
- : undefined;
116
- return (
117
- <ToggleButton
118
- key={t.value}
119
- id={t.value}
120
- size="small"
121
- className={`${styles.filterChip} ${isSelected ? styles.toolChipSelected : ''}`}
122
- iconStart={iconEl}
123
- isSelected={isSelected}
124
- onChange={() => handleToolClick(t.value as AiTool | 'all')}
125
- style={{
126
- borderColor: isSelected ? 'var(--bui-fg-link)' : 'var(--bui-border-1)',
127
- backgroundColor: isSelected ? 'var(--bui-bg-accent-1)' : 'transparent',
128
- color: isSelected ? 'var(--bui-fg-link)' : 'var(--bui-fg-secondary)',
129
- }}
130
- >
131
- {t.label}
132
- </ToggleButton>
133
- );
134
- })}
135
- </Flex>
136
- </Box>
137
-
138
- {/* Provider filter — only shown when there are 2+ providers */}
139
- {showProviderFilter && (
140
- <Box>
141
- <Text variant="body-x-small" color="secondary" className={styles.filterLabel}>
142
- Provider
143
- </Text>
144
- <Flex className={styles.filterChips}>
145
- <ToggleButton
146
- id="all-providers"
147
- size="small"
148
- className={`${styles.filterChip} ${!value.providerId ? styles.filterChipSelected : ''}`}
149
- iconStart={<RiAppsLine size={14} />}
150
- isSelected={!value.providerId}
151
- onChange={() => handleProviderClick(undefined)}
152
- style={{
153
- borderColor: !value.providerId ? 'var(--bui-fg-primary)' : 'var(--bui-border-1)',
154
- backgroundColor: !value.providerId ? 'var(--bui-fg-primary)' : 'transparent',
155
- color: !value.providerId ? 'var(--bui-bg-neutral-1)' : 'var(--bui-fg-secondary)',
156
- }}
157
- >
158
- All
159
- </ToggleButton>
160
- {providers.map(p => {
161
- const isSelected = value.providerId === p.id;
162
- const label = p.target.split('/').slice(-1)[0]?.replace(/\.git$/, '') ?? p.id;
163
- return (
164
- <ToggleButton
165
- key={p.id}
166
- id={p.id}
167
- size="small"
168
- className={`${styles.filterChip} ${isSelected ? styles.filterChipSelected : ''}`}
169
- iconStart={<RiDatabase2Line size={14} style={{ color: isSelected ? 'var(--bui-bg-neutral-1)' : 'inherit' }} />}
170
- isSelected={isSelected}
171
- onChange={() => handleProviderClick(isSelected ? undefined : p.id)}
172
- style={{
173
- borderColor: isSelected ? 'var(--bui-fg-primary)' : 'var(--bui-border-1)',
174
- backgroundColor: isSelected ? 'var(--bui-fg-primary)' : 'transparent',
175
- color: isSelected ? 'var(--bui-bg-neutral-1)' : 'var(--bui-fg-secondary)',
176
- }}
177
- >
178
- {label}
179
- </ToggleButton>
180
- );
181
- })}
182
- </Flex>
183
- </Box>
184
- )}
185
- </Flex>
186
-
187
- {availableTags.length > 0 && (
188
- <Box>
189
- <Text variant="body-x-small" color="secondary" className={styles.filterLabel}>
190
- Tags
191
- </Text>
192
- <Flex className={styles.filterChips}>
193
- {availableTags.map(tag => {
194
- const isActive = value.tags.includes(tag);
195
- return (
196
- <ToggleButton
197
- key={tag}
198
- id={tag}
199
- size="small"
200
- className={`${styles.tagChip} ${isActive ? styles.tagChipSelected : ''}`}
201
- isSelected={isActive}
202
- onChange={() => handleTagToggle(tag)}
203
- style={{
204
- fontWeight: isActive ? 700 : 400,
205
- backgroundColor: isActive ? 'var(--bui-bg-solid)' : 'transparent',
206
- color: isActive ? '#fff' : 'var(--bui-fg-secondary)',
207
- borderColor: isActive ? 'var(--bui-bg-solid)' : 'var(--bui-border-1)',
208
- }}
209
- >
210
- #{tag}
211
- </ToggleButton>
212
- );
213
- })}
214
- </Flex>
215
- </Box>
216
- )}
217
- </Flex>
218
- );
219
- }
@@ -1,2 +0,0 @@
1
- export { AssetFilters } from './AssetFilters';
2
- export type { AssetFiltersValue } from './AssetFilters';
@@ -1,72 +0,0 @@
1
- @layer components {
2
- .subtitle {
3
- margin-top: var(--bui-space-1);
4
- font-weight: 400;
5
- }
6
-
7
- .loadingContainer {
8
- display: flex;
9
- justify-content: center;
10
- padding: var(--bui-space-6) 0;
11
- }
12
-
13
- .zipInfo {
14
- border: 1px solid #00D2D3;
15
- border-radius: var(--bui-radius-2);
16
- padding: var(--bui-space-3);
17
- background-color: color-mix(in srgb, #00D2D3 7%, transparent);
18
- }
19
-
20
- .zipHeader {
21
- display: flex;
22
- align-items: center;
23
- gap: var(--bui-space-2);
24
- margin-bottom: var(--bui-space-2);
25
- }
26
-
27
- .zipFiles {
28
- display: flex;
29
- gap: var(--bui-space-1);
30
- flex-wrap: wrap;
31
- }
32
-
33
- .monoTag {
34
- font-family: monospace;
35
- font-size: 0.7rem;
36
- height: 20px;
37
- }
38
-
39
- .toolSection {
40
- border: 1px solid var(--bui-border-1);
41
- border-radius: var(--bui-radius-2);
42
- padding: var(--bui-space-3);
43
- }
44
-
45
- .toolHeader {
46
- display: flex;
47
- align-items: center;
48
- gap: var(--bui-space-2);
49
- margin-bottom: var(--bui-space-2);
50
- }
51
-
52
- .installPathBox {
53
- background-color: var(--bui-bg-neutral-1-hover);
54
- border: 1px solid var(--bui-border-1);
55
- border-radius: var(--bui-radius-2);
56
- padding: var(--bui-space-2) var(--bui-space-3);
57
- font-family: monospace;
58
- font-size: 0.78rem;
59
- color: var(--bui-fg-primary);
60
- word-break: break-all;
61
- margin-bottom: 10px;
62
- }
63
-
64
- .toolActions {
65
- display: flex;
66
- gap: var(--bui-space-2);
67
- }
68
-
69
- .toolActions > * {
70
- flex: 1;
71
- }
72
- }
@@ -1,176 +0,0 @@
1
- import { useState } from 'react';
2
- import {
3
- Box, Flex, Text, Button, Tag, TagGroup, Skeleton,
4
- Dialog, DialogTrigger, DialogHeader, DialogBody, DialogFooter,
5
- Tooltip, TooltipTrigger,
6
- } from '@backstage/ui';
7
- import { RiFileCopyLine, RiDownloadLine, RiCheckLine, RiFolderZipLine } from '@remixicon/react';
8
- import type { AiTool } from '@nospt/plugin-dev-ai-hub-common';
9
- import { getInstallPathsForAsset } from '@nospt/plugin-dev-ai-hub-common';
10
- import { useApi } from '@backstage/core-plugin-api';
11
- import { devAiHubApiRef } from '../../api/DevAiHubClient';
12
- import { useAssetDetail } from '../../hooks';
13
- import { ToolIcon } from '../ToolIcon';
14
- import styles from './AssetInstallDialog.module.css';
15
-
16
- const TOOL_LABELS: Record<string, string> = {
17
- 'claude-code': 'Claude Code',
18
- 'github-copilot': 'GitHub Copilot',
19
- 'google-gemini': 'Google Gemini',
20
- 'cursor': 'Cursor',
21
- };
22
-
23
- interface AssetInstallDialogProps {
24
- assetId: string | null;
25
- onClose: () => void;
26
- }
27
-
28
- export function AssetInstallDialog({ assetId, onClose }: AssetInstallDialogProps) {
29
- const [copiedTool, setCopiedTool] = useState<string | null>(null);
30
- const api = useApi(devAiHubApiRef);
31
- const { asset, loading } = useAssetDetail(assetId);
32
-
33
- const handleClose = () => {
34
- onClose();
35
- setCopiedTool(null);
36
- };
37
-
38
- const handleCopy = (tool: string) => {
39
- if (!asset) return;
40
- navigator.clipboard.writeText(asset.content).then(() => {
41
- setCopiedTool(tool);
42
- setTimeout(() => setCopiedTool(null), 2000);
43
- });
44
- api.trackInstall(asset.id).catch(() => {});
45
- };
46
-
47
- const resourcePaths = asset?.resourcesContent
48
- ? Object.keys(asset.resourcesContent)
49
- : [];
50
- const isZipSkill = asset?.type === 'skill' && resourcePaths.length > 0;
51
-
52
- const handleDownload = async (_tool: string, installPath: string) => {
53
- if (!asset) return;
54
- if (isZipSkill) {
55
- const url = await api.getDownloadUrl(asset.id);
56
- const a = document.createElement('a');
57
- a.href = url;
58
- a.download = `${asset.name.replace(/\s+/g, '-').toLowerCase()}.zip`;
59
- a.click();
60
- } else {
61
- const filename = installPath.split('/').pop() ?? `${asset.name}.md`;
62
- const blob = new Blob([asset.content], { type: 'text/markdown' });
63
- const url = URL.createObjectURL(blob);
64
- const a = document.createElement('a');
65
- a.href = url;
66
- a.download = filename;
67
- a.click();
68
- URL.revokeObjectURL(url);
69
- }
70
- api.trackInstall(asset.id).catch(() => {});
71
- };
72
-
73
- const installPaths = asset
74
- ? getInstallPathsForAsset(asset.type, asset.tools, asset.name, {
75
- installPath: asset.installPath,
76
- installPaths: asset.installPaths,
77
- })
78
- : {};
79
-
80
- return (
81
- <DialogTrigger>
82
- <Dialog
83
- isOpen={!!assetId}
84
- isDismissable
85
- onOpenChange={open => { if (!open) handleClose(); }}
86
- >
87
- <DialogHeader>
88
- {asset ? `Install: ${asset.name}` : 'Install'}
89
- <Text variant="body-small" color="secondary" className={styles.subtitle}>
90
- Copy the content and place the file at the path shown for your tool.
91
- </Text>
92
- </DialogHeader>
93
-
94
- <DialogBody>
95
- <Flex direction="column" style={{ gap: 'var(--bui-space-3)' }}>
96
- {loading && (
97
- <Flex className={styles.loadingContainer}>
98
- <Skeleton style={{ width: '100%', height: 80 }} />
99
- </Flex>
100
- )}
101
-
102
- {!loading && asset && isZipSkill && (
103
- <Box className={styles.zipInfo}>
104
- <Flex className={styles.zipHeader}>
105
- <RiFolderZipLine size={16} style={{ color: 'var(--bui-fg-info)' }} />
106
- <Text variant="body-small" weight="bold" style={{ color: 'var(--bui-fg-info)' }}>
107
- Bundled skill — downloads as .zip
108
- </Text>
109
- </Flex>
110
- <Text variant="body-x-small" color="secondary" style={{ display: 'block', marginBottom: 'var(--bui-space-2)' }}>
111
- This skill includes resource files alongside <code>SKILL.md</code>.
112
- Extract the zip and place all files in the skill directory.
113
- </Text>
114
- <TagGroup aria-label="Bundled files">
115
- <Flex className={styles.zipFiles}>
116
- <Tag id="skill-md" size="small" className={styles.monoTag}>SKILL.md</Tag>
117
- {resourcePaths.map(p => (
118
- <Tag key={p} id={p} size="small" className={styles.monoTag}>{p}</Tag>
119
- ))}
120
- </Flex>
121
- </TagGroup>
122
- </Box>
123
- )}
124
-
125
- {!loading && asset && Object.entries(installPaths).map(([tool, installPath]) => (
126
- <Box key={tool} className={styles.toolSection}>
127
- <Flex className={styles.toolHeader}>
128
- <ToolIcon tool={tool as AiTool} size={16} />
129
- <Text variant="body-small" weight="bold">
130
- {TOOL_LABELS[tool] ?? tool}
131
- </Text>
132
- </Flex>
133
-
134
- <Text variant="body-x-small" color="secondary" style={{ display: 'block', marginBottom: 'var(--bui-space-1)' }}>
135
- Install path
136
- </Text>
137
- <Box className={styles.installPathBox}>
138
- {installPath}
139
- </Box>
140
-
141
- <Flex className={styles.toolActions}>
142
- <TooltipTrigger>
143
- <Button
144
- variant="secondary"
145
- onClick={() => handleCopy(tool)}
146
- >
147
- {copiedTool === tool ? <RiCheckLine size={14} /> : <RiFileCopyLine size={14} />}
148
- {copiedTool === tool ? 'Copied!' : 'Copy Content'}
149
- </Button>
150
- <Tooltip>{copiedTool === tool ? 'Copied!' : 'Copy markdown content'}</Tooltip>
151
- </TooltipTrigger>
152
- <TooltipTrigger>
153
- <Button
154
- variant="secondary"
155
- onClick={() => handleDownload(tool, installPath)}
156
- >
157
- {isZipSkill ? <RiFolderZipLine size={14} /> : <RiDownloadLine size={14} />}
158
- {isZipSkill ? 'Download .zip' : 'Download'}
159
- </Button>
160
- <Tooltip>{isZipSkill ? 'Download as .zip with all bundled files' : 'Download file with correct name'}</Tooltip>
161
- </TooltipTrigger>
162
- </Flex>
163
- </Box>
164
- ))}
165
- </Flex>
166
- </DialogBody>
167
-
168
- <DialogFooter>
169
- <Button onClick={handleClose} variant="secondary" slot="close">
170
- Close
171
- </Button>
172
- </DialogFooter>
173
- </Dialog>
174
- </DialogTrigger>
175
- );
176
- }
@@ -1 +0,0 @@
1
- export { AssetInstallDialog } from './AssetInstallDialog';
@@ -1,105 +0,0 @@
1
- @layer components {
2
- .pageRoot {
3
- display: flex;
4
- flex-direction: column;
5
- height: 100%;
6
- }
7
-
8
- .content {
9
- flex: 1;
10
- padding: var(--bui-space-6);
11
- overflow-y: auto;
12
- }
13
-
14
- .statsGrid {
15
- display: grid;
16
- grid-template-columns: repeat(4, 1fr);
17
- gap: var(--bui-space-4);
18
- margin-bottom: var(--bui-space-6);
19
- }
20
-
21
- @media (max-width: 600px) {
22
- .statsGrid {
23
- grid-template-columns: repeat(2, 1fr);
24
- }
25
- }
26
-
27
- .statCard {
28
- border-radius: var(--bui-radius-3);
29
- padding: var(--bui-space-4);
30
- cursor: pointer;
31
- transition: all 0.2s ease;
32
- }
33
-
34
- .statCard:hover {
35
- transform: translateY(-2px);
36
- }
37
-
38
- .statCardInner {
39
- display: flex;
40
- justify-content: space-between;
41
- align-items: center;
42
- }
43
-
44
- .statValue {
45
- color: #fff;
46
- line-height: 1;
47
- }
48
-
49
- .statLabel {
50
- color: rgba(255, 255, 255, 0.85);
51
- font-weight: 500;
52
- margin-top: var(--bui-space-1);
53
- }
54
-
55
- .statIconBox {
56
- width: 40px;
57
- height: 40px;
58
- border-radius: var(--bui-radius-2);
59
- background-color: rgba(255, 255, 255, 0.2);
60
- display: flex;
61
- align-items: center;
62
- justify-content: center;
63
- }
64
-
65
- .assetGrid {
66
- display: grid;
67
- grid-template-columns: repeat(4, 1fr);
68
- gap: var(--bui-space-3);
69
- }
70
-
71
- @media (max-width: 1200px) {
72
- .assetGrid {
73
- grid-template-columns: repeat(3, 1fr);
74
- }
75
- }
76
-
77
- @media (max-width: 900px) {
78
- .assetGrid {
79
- grid-template-columns: repeat(2, 1fr);
80
- }
81
- }
82
-
83
- @media (max-width: 600px) {
84
- .assetGrid {
85
- grid-template-columns: 1fr;
86
- }
87
- }
88
-
89
- .emptyState {
90
- padding: 96px 0;
91
- text-align: center;
92
- }
93
-
94
- .emptyEmoji {
95
- margin-bottom: var(--bui-space-4);
96
- opacity: 0.15;
97
- font-size: 5rem;
98
- }
99
-
100
- .paginationRow {
101
- display: flex;
102
- justify-content: center;
103
- margin-top: var(--bui-space-8);
104
- }
105
- }