@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.
- package/dev/index.tsx +17 -217
- package/dist/api/DevAiHubClient.esm.js +2 -0
- package/dist/api/DevAiHubClient.esm.js.map +1 -1
- package/dist/components/AssetCard/AssetCard.esm.js +60 -134
- package/dist/components/AssetCard/AssetCard.esm.js.map +1 -1
- package/dist/components/AssetCard/AssetCard.module.css.esm.js +8 -0
- package/dist/components/AssetCard/AssetCard.module.css.esm.js.map +1 -0
- package/dist/components/AssetDetailPanel/AssetDetailPanel.esm.js +175 -281
- package/dist/components/AssetDetailPanel/AssetDetailPanel.esm.js.map +1 -1
- package/dist/components/AssetDetailPanel/AssetDetailPanel.module.css.esm.js +8 -0
- package/dist/components/AssetDetailPanel/AssetDetailPanel.module.css.esm.js.map +1 -0
- package/dist/components/AssetFilters/AssetFilters.esm.js +94 -134
- package/dist/components/AssetFilters/AssetFilters.esm.js.map +1 -1
- package/dist/components/AssetFilters/AssetFilters.module.css.esm.js +8 -0
- package/dist/components/AssetFilters/AssetFilters.module.css.esm.js.map +1 -0
- package/dist/components/AssetInstallDialog/AssetInstallDialog.esm.js +70 -126
- package/dist/components/AssetInstallDialog/AssetInstallDialog.esm.js.map +1 -1
- package/dist/components/AssetInstallDialog/AssetInstallDialog.module.css.esm.js +8 -0
- package/dist/components/AssetInstallDialog/AssetInstallDialog.module.css.esm.js.map +1 -0
- package/dist/components/DevAiHubPage/DevAiHubPage.esm.js +64 -189
- package/dist/components/DevAiHubPage/DevAiHubPage.esm.js.map +1 -1
- package/dist/components/DevAiHubPage/DevAiHubPage.module.css.esm.js +8 -0
- package/dist/components/DevAiHubPage/DevAiHubPage.module.css.esm.js.map +1 -0
- package/dist/components/McpConfigPage/McpConfigPage.esm.js +200 -0
- package/dist/components/McpConfigPage/McpConfigPage.esm.js.map +1 -0
- package/dist/components/McpConfigPage/McpConfigPage.module.css.esm.js +8 -0
- package/dist/components/McpConfigPage/McpConfigPage.module.css.esm.js.map +1 -0
- package/dist/components/McpConfigPage/index.esm.js +6 -0
- package/dist/components/McpConfigPage/index.esm.js.map +1 -0
- package/dist/components/ToolIcon/ToolIcon.esm.js +25 -13
- package/dist/components/ToolIcon/ToolIcon.esm.js.map +1 -1
- package/dist/index.d.ts +10 -5
- package/dist/index.esm.js +1 -1
- package/dist/node_modules_dist/style-inject/dist/style-inject.es.esm.js +29 -0
- package/dist/node_modules_dist/style-inject/dist/style-inject.es.esm.js.map +1 -0
- package/dist/plugin.esm.js +22 -4
- package/dist/plugin.esm.js.map +1 -1
- package/dist/routes.esm.js +2 -1
- package/dist/routes.esm.js.map +1 -1
- package/package.json +24 -14
- package/package.json-prepack +24 -14
- package/src/components/AssetCard/AssetCard.module.css +153 -0
- package/src/components/AssetCard/AssetCard.tsx +79 -148
- package/src/components/AssetDetailPanel/AssetDetailPanel.module.css +207 -0
- package/src/components/AssetDetailPanel/AssetDetailPanel.tsx +228 -292
- package/src/components/AssetFilters/AssetFilters.module.css +67 -0
- package/src/components/AssetFilters/AssetFilters.tsx +136 -178
- package/src/components/AssetInstallDialog/AssetInstallDialog.module.css +72 -0
- package/src/components/AssetInstallDialog/AssetInstallDialog.tsx +95 -135
- package/src/components/DevAiHubPage/DevAiHubPage.module.css +105 -0
- package/src/components/DevAiHubPage/DevAiHubPage.tsx +73 -222
- package/src/components/McpConfigDialog/McpConfigDialog.module.css +111 -0
- package/src/components/McpConfigDialog/McpConfigDialog.tsx +178 -208
- package/src/components/McpConfigPage/McpConfigPage.module.css +121 -0
- package/src/components/McpConfigPage/McpConfigPage.tsx +242 -0
- package/src/components/McpConfigPage/index.ts +1 -0
- package/src/components/ToolIcon/ToolIcon.tsx +31 -15
- package/src/css-modules.d.ts +4 -0
- package/src/index.ts +1 -1
- package/src/plugin.tsx +21 -1
- package/src/routes.ts +1 -0
- package/tests/e2e/.auth.json +14 -0
- package/tests/e2e/asset-detail.spec.ts +90 -0
- package/tests/e2e/asset-filtering.spec.ts +145 -0
- package/tests/e2e/asset-installation.spec.ts +74 -0
- package/tests/e2e/dev-ai-hub-page.spec.ts +89 -0
- package/tests/e2e/fixtures/base.ts +61 -0
- package/tests/e2e/fixtures/mock-api.ts +221 -0
- package/tests/e2e/mcp-config.spec.ts +88 -0
- package/dist/components/McpConfigDialog/McpConfigDialog.esm.js +0 -274
- package/dist/components/McpConfigDialog/McpConfigDialog.esm.js.map +0 -1
|
@@ -1,26 +1,16 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react';
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import
|
|
9
|
-
import FormControlLabel from '@mui/material/FormControlLabel';
|
|
10
|
-
import IconButton from '@mui/material/IconButton';
|
|
11
|
-
import Switch from '@mui/material/Switch';
|
|
12
|
-
import Tab from '@mui/material/Tab';
|
|
13
|
-
import Tabs from '@mui/material/Tabs';
|
|
14
|
-
import Tooltip from '@mui/material/Tooltip';
|
|
15
|
-
import Typography from '@mui/material/Typography';
|
|
16
|
-
import { useTheme } from '@mui/material/styles';
|
|
17
|
-
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
|
|
18
|
-
import CheckIcon from '@mui/icons-material/Check';
|
|
19
|
-
import StorageIcon from '@mui/icons-material/Storage';
|
|
2
|
+
import {
|
|
3
|
+
Box, Flex, Text, Button, ButtonIcon, ToggleButton, Switch,
|
|
4
|
+
Dialog, DialogTrigger, DialogHeader, DialogBody, DialogFooter,
|
|
5
|
+
Tabs, TabList, Tab, TabPanel,
|
|
6
|
+
Tooltip, TooltipTrigger,
|
|
7
|
+
} from '@backstage/ui';
|
|
8
|
+
import { RiFileCopyLine, RiCheckLine, RiDatabase2Line } from '@remixicon/react';
|
|
20
9
|
import { useApi, discoveryApiRef } from '@backstage/core-plugin-api';
|
|
21
10
|
import { ToolIcon } from '../ToolIcon';
|
|
22
11
|
import { useCopyToClipboard, useProviders } from '../../hooks';
|
|
23
12
|
import type { AiTool } from '@nospt/plugin-dev-ai-hub-common';
|
|
13
|
+
import styles from './McpConfigDialog.module.css';
|
|
24
14
|
|
|
25
15
|
interface ToolConfig {
|
|
26
16
|
tool: AiTool;
|
|
@@ -70,11 +60,10 @@ interface McpConfigDialogProps {
|
|
|
70
60
|
}
|
|
71
61
|
|
|
72
62
|
export function McpConfigDialog({ open, onClose }: McpConfigDialogProps) {
|
|
73
|
-
const theme = useTheme();
|
|
74
63
|
const discoveryApi = useApi(discoveryApiRef);
|
|
75
64
|
const { copy: copyUrl, copied: copiedUrl } = useCopyToClipboard();
|
|
76
65
|
const { copy: copySnippet, copied: copiedSnippet } = useCopyToClipboard();
|
|
77
|
-
const [
|
|
66
|
+
const [selectedToolKey, setSelectedToolKey] = useState<string>('claude-code');
|
|
78
67
|
const [baseUrl, setBaseUrl] = useState('');
|
|
79
68
|
const [selectedProvider, setSelectedProvider] = useState<string>('');
|
|
80
69
|
const [proactiveEnabled, setProactiveEnabled] = useState(false);
|
|
@@ -88,7 +77,7 @@ export function McpConfigDialog({ open, onClose }: McpConfigDialogProps) {
|
|
|
88
77
|
}
|
|
89
78
|
}, [open, discoveryApi]);
|
|
90
79
|
|
|
91
|
-
const cfg = TOOL_CONFIGS[
|
|
80
|
+
const cfg = TOOL_CONFIGS.find(t => t.tool === selectedToolKey) ?? TOOL_CONFIGS[0];
|
|
92
81
|
|
|
93
82
|
const buildMcpUrl = () => {
|
|
94
83
|
if (!baseUrl) return 'loading...';
|
|
@@ -102,200 +91,181 @@ export function McpConfigDialog({ open, onClose }: McpConfigDialogProps) {
|
|
|
102
91
|
const mcpUrl = buildMcpUrl();
|
|
103
92
|
const configSnippet = baseUrl ? cfg.buildConfig(mcpUrl) : '';
|
|
104
93
|
|
|
94
|
+
// Detect dark mode by observing BUI's data-theme-mode attribute, which
|
|
95
|
+
// reflects the Backstage theme toggle (not the OS preference).
|
|
96
|
+
const [isDark, setIsDark] = useState(() => {
|
|
97
|
+
if (typeof document === 'undefined') return false;
|
|
98
|
+
return document.documentElement.getAttribute('data-theme-mode') === 'dark' ||
|
|
99
|
+
document.body.getAttribute('data-theme-mode') === 'dark';
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
const getIsDark = () =>
|
|
104
|
+
document.documentElement.getAttribute('data-theme-mode') === 'dark' ||
|
|
105
|
+
document.body.getAttribute('data-theme-mode') === 'dark';
|
|
106
|
+
|
|
107
|
+
const observer = new MutationObserver(() => setIsDark(getIsDark()));
|
|
108
|
+
observer.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme-mode'] });
|
|
109
|
+
observer.observe(document.body, { attributes: true, attributeFilter: ['data-theme-mode'] });
|
|
110
|
+
return () => observer.disconnect();
|
|
111
|
+
}, []);
|
|
112
|
+
|
|
105
113
|
return (
|
|
106
|
-
<
|
|
107
|
-
<
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
114
|
+
<DialogTrigger>
|
|
115
|
+
<Dialog
|
|
116
|
+
isOpen={open}
|
|
117
|
+
isDismissable
|
|
118
|
+
onOpenChange={o => { if (!o) onClose(); }}
|
|
119
|
+
>
|
|
120
|
+
<DialogHeader>
|
|
121
|
+
<Text variant="title-small" weight="bold">Configure MCP Server</Text>
|
|
122
|
+
<Text variant="body-small" color="secondary" className={styles.subtitle}>
|
|
123
|
+
Connect your AI tool to the Dev AI Hub via Model Context Protocol.
|
|
124
|
+
</Text>
|
|
125
|
+
</DialogHeader>
|
|
113
126
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
<ToolIcon
|
|
127
|
-
tool={t.tool}
|
|
128
|
-
sx={{ fontSize: '1rem', color: theme.palette.mode === 'dark' ? '#fff' : undefined }}
|
|
129
|
-
/>
|
|
130
|
-
<span>{t.label}</span>
|
|
131
|
-
</Box>
|
|
132
|
-
}
|
|
133
|
-
/>
|
|
134
|
-
))}
|
|
135
|
-
</Tabs>
|
|
127
|
+
<DialogBody>
|
|
128
|
+
<Tabs selectedKey={selectedToolKey} onSelectionChange={key => setSelectedToolKey(key as string)}>
|
|
129
|
+
<TabList className={styles.tabBar}>
|
|
130
|
+
{TOOL_CONFIGS.map(t => (
|
|
131
|
+
<Tab key={t.tool} id={t.tool}>
|
|
132
|
+
<span className={styles.tabContent}>
|
|
133
|
+
<ToolIcon tool={t.tool} size={16} />
|
|
134
|
+
<span>{t.label}</span>
|
|
135
|
+
</span>
|
|
136
|
+
</Tab>
|
|
137
|
+
))}
|
|
138
|
+
</TabList>
|
|
136
139
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
)}
|
|
140
|
+
{/* All tab panels share the same content below */}
|
|
141
|
+
{TOOL_CONFIGS.map(t => (
|
|
142
|
+
<TabPanel key={t.tool} id={t.tool}>
|
|
143
|
+
{/* Provider filter — only shown when there are 2+ providers */}
|
|
144
|
+
{showProviderFilter && (
|
|
145
|
+
<Box className={styles.providerSection}>
|
|
146
|
+
<Text variant="body-x-small" color="secondary" className={styles.sectionLabel}>
|
|
147
|
+
Scope to Provider
|
|
148
|
+
</Text>
|
|
149
|
+
<Flex className={styles.chipsRow}>
|
|
150
|
+
<ToggleButton
|
|
151
|
+
id="all-providers"
|
|
152
|
+
size="small"
|
|
153
|
+
className={styles.filterChip}
|
|
154
|
+
isSelected={!selectedProvider}
|
|
155
|
+
onChange={() => setSelectedProvider('')}
|
|
156
|
+
style={{
|
|
157
|
+
borderColor: !selectedProvider ? 'var(--bui-fg-primary)' : 'var(--bui-border-1)',
|
|
158
|
+
backgroundColor: !selectedProvider ? 'var(--bui-fg-primary)' : 'transparent',
|
|
159
|
+
color: !selectedProvider ? 'var(--bui-bg-neutral-1)' : 'var(--bui-fg-secondary)',
|
|
160
|
+
}}
|
|
161
|
+
>
|
|
162
|
+
All providers
|
|
163
|
+
</ToggleButton>
|
|
164
|
+
{providers.map(p => {
|
|
165
|
+
const isSelected = selectedProvider === p.id;
|
|
166
|
+
const label = providerLabel(p.target);
|
|
167
|
+
return (
|
|
168
|
+
<ToggleButton
|
|
169
|
+
key={p.id}
|
|
170
|
+
id={p.id}
|
|
171
|
+
size="small"
|
|
172
|
+
className={styles.filterChip}
|
|
173
|
+
iconStart={<RiDatabase2Line size={12} style={{ color: isSelected ? 'var(--bui-bg-neutral-1)' : 'inherit' }} />}
|
|
174
|
+
isSelected={isSelected}
|
|
175
|
+
onChange={() => setSelectedProvider(isSelected ? '' : p.id)}
|
|
176
|
+
style={{
|
|
177
|
+
borderColor: isSelected ? 'var(--bui-fg-primary)' : 'var(--bui-border-1)',
|
|
178
|
+
backgroundColor: isSelected ? 'var(--bui-fg-primary)' : 'transparent',
|
|
179
|
+
color: isSelected ? 'var(--bui-bg-neutral-1)' : 'var(--bui-fg-secondary)',
|
|
180
|
+
}}
|
|
181
|
+
>
|
|
182
|
+
{label}
|
|
183
|
+
</ToggleButton>
|
|
184
|
+
);
|
|
185
|
+
})}
|
|
186
|
+
</Flex>
|
|
187
|
+
</Box>
|
|
188
|
+
)}
|
|
187
189
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
</
|
|
205
|
-
</Box>
|
|
206
|
-
}
|
|
207
|
-
sx={{ alignItems: 'flex-start', ml: 0, gap: 1 }}
|
|
208
|
-
/>
|
|
209
|
-
</Box>
|
|
190
|
+
{/* Proactive suggestions toggle */}
|
|
191
|
+
<Flex className={styles.proactiveSection}>
|
|
192
|
+
<Flex className={styles.proactiveRow}>
|
|
193
|
+
<Switch
|
|
194
|
+
isSelected={proactiveEnabled}
|
|
195
|
+
onChange={setProactiveEnabled}
|
|
196
|
+
aria-label="Proactive suggestions"
|
|
197
|
+
/>
|
|
198
|
+
<Box>
|
|
199
|
+
<Text variant="body-small" weight="bold">Proactive suggestions</Text>
|
|
200
|
+
<Text variant="body-x-small" color="secondary">
|
|
201
|
+
The AI will automatically suggest relevant assets based on your project context.
|
|
202
|
+
Disable if you prefer to search manually.
|
|
203
|
+
</Text>
|
|
204
|
+
</Box>
|
|
205
|
+
</Flex>
|
|
206
|
+
</Flex>
|
|
210
207
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
variant="body2"
|
|
232
|
-
sx={{ flex: 1, fontFamily: 'monospace', fontSize: '0.8rem', wordBreak: 'break-all' }}
|
|
233
|
-
>
|
|
234
|
-
{mcpUrl}
|
|
235
|
-
</Typography>
|
|
236
|
-
<Tooltip title={copiedUrl ? 'Copied!' : 'Copy URL'}>
|
|
237
|
-
<IconButton size="small" onClick={() => copyUrl(mcpUrl)}>
|
|
238
|
-
{copiedUrl ? <CheckIcon fontSize="small" color="success" /> : <ContentCopyIcon fontSize="small" />}
|
|
239
|
-
</IconButton>
|
|
240
|
-
</Tooltip>
|
|
241
|
-
</Box>
|
|
208
|
+
{/* MCP URL */}
|
|
209
|
+
<Box className={styles.urlSection}>
|
|
210
|
+
<Text variant="body-x-small" color="secondary" className={styles.sectionLabel}>
|
|
211
|
+
MCP Endpoint
|
|
212
|
+
</Text>
|
|
213
|
+
<Flex className={styles.urlBox}>
|
|
214
|
+
<Text variant="body-small" className={styles.urlText}>
|
|
215
|
+
{mcpUrl}
|
|
216
|
+
</Text>
|
|
217
|
+
<TooltipTrigger>
|
|
218
|
+
<ButtonIcon
|
|
219
|
+
aria-label="Copy URL"
|
|
220
|
+
icon={copiedUrl ? <RiCheckLine size={14} style={{ color: 'var(--bui-fg-success)' }} /> : <RiFileCopyLine size={14} />}
|
|
221
|
+
variant="tertiary"
|
|
222
|
+
onPress={() => copyUrl(mcpUrl)}
|
|
223
|
+
/>
|
|
224
|
+
<Tooltip>{copiedUrl ? 'Copied!' : 'Copy URL'}</Tooltip>
|
|
225
|
+
</TooltipTrigger>
|
|
226
|
+
</Flex>
|
|
227
|
+
</Box>
|
|
242
228
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
}}
|
|
266
|
-
>
|
|
267
|
-
{configSnippet}
|
|
268
|
-
</Box>
|
|
269
|
-
<Tooltip title={copiedSnippet ? 'Copied!' : 'Copy config'}>
|
|
270
|
-
<IconButton
|
|
271
|
-
size="small"
|
|
272
|
-
onClick={() => copySnippet(configSnippet)}
|
|
273
|
-
sx={{
|
|
274
|
-
position: 'absolute',
|
|
275
|
-
top: 8,
|
|
276
|
-
right: 8,
|
|
277
|
-
backgroundColor: theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.1)' : 'rgba(0,0,0,0.06)',
|
|
278
|
-
'&:hover': {
|
|
279
|
-
backgroundColor: theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.2)' : 'rgba(0,0,0,0.12)',
|
|
280
|
-
},
|
|
281
|
-
}}
|
|
282
|
-
>
|
|
283
|
-
{copiedSnippet ? <CheckIcon fontSize="small" color="success" /> : <ContentCopyIcon fontSize="small" />}
|
|
284
|
-
</IconButton>
|
|
285
|
-
</Tooltip>
|
|
286
|
-
</Box>
|
|
229
|
+
{/* Config snippet */}
|
|
230
|
+
<Text variant="body-x-small" color="secondary" className={styles.sectionLabel}>
|
|
231
|
+
{t.file}
|
|
232
|
+
</Text>
|
|
233
|
+
<Text variant="body-x-small" color="secondary" style={{ display: 'block', marginBottom: 'var(--bui-space-2)', marginTop: 'var(--bui-space-1)' }}>
|
|
234
|
+
{t.description}
|
|
235
|
+
</Text>
|
|
236
|
+
<div className={styles.snippetContainer}>
|
|
237
|
+
<pre className={`${styles.snippetPre} ${isDark ? styles.snippetPreDark : styles.snippetPreLight}`}>
|
|
238
|
+
{configSnippet}
|
|
239
|
+
</pre>
|
|
240
|
+
<TooltipTrigger>
|
|
241
|
+
<ButtonIcon
|
|
242
|
+
aria-label="Copy config"
|
|
243
|
+
className={styles.copySnippetButton}
|
|
244
|
+
icon={copiedSnippet ? <RiCheckLine size={14} style={{ color: 'var(--bui-fg-success)' }} /> : <RiFileCopyLine size={14} />}
|
|
245
|
+
variant="tertiary"
|
|
246
|
+
onPress={() => copySnippet(configSnippet)}
|
|
247
|
+
/>
|
|
248
|
+
<Tooltip>{copiedSnippet ? 'Copied!' : 'Copy config'}</Tooltip>
|
|
249
|
+
</TooltipTrigger>
|
|
250
|
+
</div>
|
|
287
251
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
252
|
+
<Text variant="body-x-small" color="secondary" className={styles.hint}>
|
|
253
|
+
💡 Omit <code>?tool=</code> from the URL to receive assets for all AI tools.
|
|
254
|
+
{showProviderFilter && ' Omit ?provider= to receive assets from all repositories.'}
|
|
255
|
+
{' '}Proactive suggestions add <code>?proactive=true</code> and register the{' '}
|
|
256
|
+
<code>suggest_assets</code> tool and <code>check_for_assets</code> prompt.
|
|
257
|
+
</Text>
|
|
258
|
+
</TabPanel>
|
|
259
|
+
))}
|
|
260
|
+
</Tabs>
|
|
261
|
+
</DialogBody>
|
|
295
262
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
263
|
+
<DialogFooter>
|
|
264
|
+
<Button onClick={onClose} variant="secondary" slot="close">
|
|
265
|
+
Close
|
|
266
|
+
</Button>
|
|
267
|
+
</DialogFooter>
|
|
268
|
+
</Dialog>
|
|
269
|
+
</DialogTrigger>
|
|
300
270
|
);
|
|
301
271
|
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
@layer components {
|
|
2
|
+
.pageRoot {
|
|
3
|
+
padding: var(--bui-space-6);
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.subtitle {
|
|
7
|
+
margin-top: var(--bui-space-1);
|
|
8
|
+
display: block;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.configContent {
|
|
12
|
+
margin-top: var(--bui-space-6);
|
|
13
|
+
max-width: 720px;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.tabBar {
|
|
17
|
+
margin-bottom: var(--bui-space-4);
|
|
18
|
+
border-bottom: 1px solid var(--bui-border-1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.tabContent {
|
|
22
|
+
display: flex;
|
|
23
|
+
align-items: center;
|
|
24
|
+
gap: var(--bui-space-2);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.sectionLabel {
|
|
28
|
+
text-transform: uppercase;
|
|
29
|
+
letter-spacing: 0.5px;
|
|
30
|
+
display: block;
|
|
31
|
+
margin-bottom: 6px;
|
|
32
|
+
font-weight: 600;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.providerSection {
|
|
36
|
+
margin-bottom: var(--bui-space-4);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.chipsRow {
|
|
40
|
+
display: flex;
|
|
41
|
+
gap: 6px;
|
|
42
|
+
flex-wrap: wrap;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.filterChip {
|
|
46
|
+
font-weight: 600;
|
|
47
|
+
font-size: 0.75rem;
|
|
48
|
+
border-radius: var(--bui-radius-2);
|
|
49
|
+
border: 1.5px solid;
|
|
50
|
+
transition: all 0.15s ease;
|
|
51
|
+
cursor: pointer;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.proactiveSection {
|
|
55
|
+
margin-bottom: var(--bui-space-4);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.proactiveRow {
|
|
59
|
+
display: flex;
|
|
60
|
+
align-items: flex-start;
|
|
61
|
+
gap: var(--bui-space-2);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.urlSection {
|
|
65
|
+
margin-bottom: var(--bui-space-4);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.urlBox {
|
|
69
|
+
display: flex;
|
|
70
|
+
align-items: center;
|
|
71
|
+
gap: var(--bui-space-2);
|
|
72
|
+
margin-top: var(--bui-space-1);
|
|
73
|
+
padding: var(--bui-space-2) var(--bui-space-3);
|
|
74
|
+
border-radius: var(--bui-radius-2);
|
|
75
|
+
border: 1px solid var(--bui-border-1);
|
|
76
|
+
background-color: var(--bui-bg-neutral-1-hover);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.urlText {
|
|
80
|
+
flex: 1;
|
|
81
|
+
font-family: monospace;
|
|
82
|
+
font-size: 0.8rem;
|
|
83
|
+
word-break: break-all;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.snippetContainer {
|
|
87
|
+
position: relative;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.snippetPre {
|
|
91
|
+
margin: 0;
|
|
92
|
+
padding: var(--bui-space-4);
|
|
93
|
+
border-radius: var(--bui-radius-2);
|
|
94
|
+
border: 1px solid var(--bui-border-1);
|
|
95
|
+
font-family: monospace;
|
|
96
|
+
font-size: 0.8rem;
|
|
97
|
+
overflow-x: auto;
|
|
98
|
+
white-space: pre;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.snippetPreLight {
|
|
102
|
+
background-color: #f6f8fa;
|
|
103
|
+
color: #24292f;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.snippetPreDark {
|
|
107
|
+
background-color: #1E1E1E;
|
|
108
|
+
color: #DCDDE1;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.copySnippetButton {
|
|
112
|
+
position: absolute;
|
|
113
|
+
top: 8px;
|
|
114
|
+
right: 8px;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.hint {
|
|
118
|
+
display: block;
|
|
119
|
+
margin-top: var(--bui-space-3);
|
|
120
|
+
}
|
|
121
|
+
}
|