@fragments-sdk/cli 0.5.2 → 0.7.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 (124) hide show
  1. package/dist/bin.js +996 -79
  2. package/dist/bin.js.map +1 -1
  3. package/dist/{chunk-ICAIQ57V.js → chunk-6JBGU74P.js} +5 -3
  4. package/dist/chunk-6JBGU74P.js.map +1 -0
  5. package/dist/chunk-7OPWMLOE.js +1625 -0
  6. package/dist/chunk-7OPWMLOE.js.map +1 -0
  7. package/dist/{chunk-2H2JAA3U.js → chunk-CVXKXVOY.js} +3 -3
  8. package/dist/{chunk-2H2JAA3U.js.map → chunk-CVXKXVOY.js.map} +1 -1
  9. package/dist/{chunk-IOJE35DZ.js → chunk-NWQ4CJOQ.js} +3 -3
  10. package/dist/{chunk-2DJH4F4P.js → chunk-RVRTRESS.js} +3 -3
  11. package/dist/{chunk-V7YLRR4C.js → chunk-TJ34N7C7.js} +41 -4
  12. package/dist/{chunk-V7YLRR4C.js.map → chunk-TJ34N7C7.js.map} +1 -1
  13. package/dist/{chunk-XNWDI6UT.js → chunk-XHUDJNN3.js} +5 -5
  14. package/dist/{core-DKHB7FYV.js → core-W2HYIQW6.js} +4 -4
  15. package/dist/{generate-KL24VZVD.js → generate-LMTISDIJ.js} +5 -5
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.js +15 -7
  18. package/dist/index.js.map +1 -1
  19. package/dist/{init-NION5S3M.js → init-7CHRKQ7P.js} +5 -5
  20. package/dist/mcp-bin.js +8 -220
  21. package/dist/mcp-bin.js.map +1 -1
  22. package/dist/scan-WY23TJCP.js +12 -0
  23. package/dist/{service-RWUMZ3EW.js → service-T2L7VLTE.js} +5 -5
  24. package/dist/static-viewer-GBR7YNF3.js +12 -0
  25. package/dist/{test-ECPEXFDN.js → test-OJRXNDO2.js} +4 -4
  26. package/dist/{tokens-ITADYVPF.js → tokens-3BWDESVM.js} +6 -6
  27. package/dist/viewer-SUFOISZM.js +1822 -0
  28. package/dist/viewer-SUFOISZM.js.map +1 -0
  29. package/package.json +6 -5
  30. package/src/bin.ts +31 -0
  31. package/src/build.ts +147 -13
  32. package/src/cli-commands.ts +18 -0
  33. package/src/commands/__tests__/a11y-scoring.test.ts +278 -0
  34. package/src/commands/a11y-report.ts +625 -0
  35. package/src/commands/a11y.ts +168 -14
  36. package/src/commands/build.ts +16 -0
  37. package/src/commands/graph.ts +274 -0
  38. package/src/core/auto-props.ts +464 -0
  39. package/src/core/composition.ts +64 -1
  40. package/src/core/graph-extractor.test.ts +542 -0
  41. package/src/core/graph-extractor.ts +601 -0
  42. package/src/core/importAnalyzer.ts +5 -0
  43. package/src/core/schema.ts +2 -0
  44. package/src/core/types.ts +3 -1
  45. package/src/index.ts +4 -0
  46. package/src/mcp/server.ts +13 -220
  47. package/src/theme/__tests__/component-contrast.test.ts +338 -0
  48. package/src/theme/__tests__/contrast-validation.test.ts +326 -0
  49. package/src/theme/contrast.test.ts +331 -0
  50. package/src/theme/contrast.ts +246 -0
  51. package/src/theme/generator.ts +213 -1
  52. package/src/theme/index.ts +16 -0
  53. package/src/theme/types.ts +51 -0
  54. package/src/viewer/__tests__/a11y-fixes.test.ts +358 -0
  55. package/src/viewer/__tests__/viewer-integration.test.ts +2 -7
  56. package/src/viewer/components/AccessibilityPanel.tsx +493 -433
  57. package/src/viewer/components/ActionCapture.tsx +1 -1
  58. package/src/viewer/components/ActionsPanel.tsx +142 -183
  59. package/src/viewer/components/App.tsx +276 -183
  60. package/src/viewer/components/BottomPanel.tsx +40 -80
  61. package/src/viewer/components/CodePanel.tsx +9 -87
  62. package/src/viewer/components/CommandPalette.tsx +117 -74
  63. package/src/viewer/components/ComponentGraph.tsx +143 -126
  64. package/src/viewer/components/ComponentHeader.tsx +46 -43
  65. package/src/viewer/components/ContractPanel.tsx +124 -117
  66. package/src/viewer/components/ErrorBoundary.tsx +47 -35
  67. package/src/viewer/components/FigmaEmbed.tsx +18 -13
  68. package/src/viewer/components/FragmentEditor.tsx +126 -63
  69. package/src/viewer/components/HealthDashboard.tsx +146 -171
  70. package/src/viewer/components/HmrStatusIndicator.tsx +31 -41
  71. package/src/viewer/components/Icons.tsx +151 -98
  72. package/src/viewer/components/InteractionsPanel.tsx +317 -264
  73. package/src/viewer/components/IsolatedPreviewFrame.tsx +52 -27
  74. package/src/viewer/components/IsolatedRender.tsx +12 -6
  75. package/src/viewer/components/KeyboardShortcutsHelp.tsx +34 -70
  76. package/src/viewer/components/LandingPage.tsx +285 -305
  77. package/src/viewer/components/Layout.tsx +12 -10
  78. package/src/viewer/components/LeftSidebar.tsx +103 -155
  79. package/src/viewer/components/MultiViewportPreview.tsx +254 -63
  80. package/src/viewer/components/PreviewArea.tsx +113 -44
  81. package/src/viewer/components/PreviewFrameHost.tsx +36 -6
  82. package/src/viewer/components/PreviewPane.tsx +2 -3
  83. package/src/viewer/components/PreviewToolbar.tsx +109 -105
  84. package/src/viewer/components/PropsEditor.tsx +154 -74
  85. package/src/viewer/components/PropsTable.tsx +95 -82
  86. package/src/viewer/components/RelationsSection.tsx +71 -40
  87. package/src/viewer/components/ResizablePanel.tsx +158 -55
  88. package/src/viewer/components/RightSidebar.tsx +46 -56
  89. package/src/viewer/components/ScreenshotButton.tsx +12 -12
  90. package/src/viewer/components/SkeletonLoader.tsx +99 -83
  91. package/src/viewer/components/StoryRenderer.tsx +4 -11
  92. package/src/viewer/components/Toast.tsx +3 -67
  93. package/src/viewer/components/TokenStylePanel.tsx +136 -118
  94. package/src/viewer/components/UsageSection.tsx +26 -26
  95. package/src/viewer/components/VariantMatrix.tsx +140 -47
  96. package/src/viewer/components/VariantTabs.tsx +24 -68
  97. package/src/viewer/components/ViewportSelector.tsx +121 -114
  98. package/src/viewer/constants/ui.ts +23 -22
  99. package/src/viewer/entry.tsx +8 -3
  100. package/src/viewer/index.ts +3 -6
  101. package/src/viewer/preview-frame.html +43 -18
  102. package/src/viewer/server.ts +7 -16
  103. package/src/viewer/styles/globals.css +46 -85
  104. package/src/viewer/utils/a11y-fixes.ts +53 -30
  105. package/dist/chunk-ICAIQ57V.js.map +0 -1
  106. package/dist/chunk-U4GQ2JTD.js +0 -832
  107. package/dist/chunk-U4GQ2JTD.js.map +0 -1
  108. package/dist/scan-ESEXV7LF.js +0 -12
  109. package/dist/static-viewer-O37MJ5B6.js +0 -12
  110. package/dist/viewer-YDGFDTK5.js +0 -11104
  111. package/dist/viewer-YDGFDTK5.js.map +0 -1
  112. package/src/viewer/postcss.config.js +0 -6
  113. package/src/viewer/tailwind.config.js +0 -37
  114. /package/dist/{chunk-IOJE35DZ.js.map → chunk-NWQ4CJOQ.js.map} +0 -0
  115. /package/dist/{chunk-2DJH4F4P.js.map → chunk-RVRTRESS.js.map} +0 -0
  116. /package/dist/{chunk-XNWDI6UT.js.map → chunk-XHUDJNN3.js.map} +0 -0
  117. /package/dist/{core-DKHB7FYV.js.map → core-W2HYIQW6.js.map} +0 -0
  118. /package/dist/{generate-KL24VZVD.js.map → generate-LMTISDIJ.js.map} +0 -0
  119. /package/dist/{init-NION5S3M.js.map → init-7CHRKQ7P.js.map} +0 -0
  120. /package/dist/{scan-ESEXV7LF.js.map → scan-WY23TJCP.js.map} +0 -0
  121. /package/dist/{service-RWUMZ3EW.js.map → service-T2L7VLTE.js.map} +0 -0
  122. /package/dist/{static-viewer-O37MJ5B6.js.map → static-viewer-GBR7YNF3.js.map} +0 -0
  123. /package/dist/{test-ECPEXFDN.js.map → test-OJRXNDO2.js.map} +0 -0
  124. /package/dist/{tokens-ITADYVPF.js.map → tokens-3BWDESVM.js.map} +0 -0
@@ -5,7 +5,7 @@
5
5
 
6
6
  import { memo, useCallback } from 'react';
7
7
  import type { SegmentDefinition, SegmentVariant } from '../../core/index.js';
8
- import clsx from 'clsx';
8
+ import { Tabs, Badge } from '@fragments/ui';
9
9
  import { ResizablePanel } from './ResizablePanel.js';
10
10
  import { CodePanel } from './CodePanel.js';
11
11
  import { TokenStylePanel } from './TokenStylePanel.js';
@@ -48,35 +48,6 @@ interface BottomPanelProps {
48
48
  segmentKey: string;
49
49
  }
50
50
 
51
- // Tab button component
52
- interface TabButtonProps {
53
- active: boolean;
54
- onClick: () => void;
55
- children: React.ReactNode;
56
- badge?: number;
57
- }
58
-
59
- const TabButton = memo(function TabButton({ active, onClick, children, badge }: TabButtonProps) {
60
- return (
61
- <button
62
- onClick={onClick}
63
- className={clsx(
64
- "px-3 py-1 text-xs font-medium rounded relative",
65
- active
66
- ? "text-primary bg-[--bg-hover]"
67
- : "text-tertiary hover:text-secondary"
68
- )}
69
- >
70
- {children}
71
- {badge !== undefined && badge > 0 && (
72
- <span className="absolute -top-1 -right-1 w-4 h-4 text-[10px] bg-blue-500 text-white rounded-full flex items-center justify-center">
73
- {badge > 99 ? '99+' : badge}
74
- </span>
75
- )}
76
- </button>
77
- );
78
- });
79
-
80
51
  export const BottomPanel = memo(function BottomPanel({
81
52
  segment,
82
53
  variant,
@@ -102,63 +73,52 @@ export const BottomPanel = memo(function BottomPanel({
102
73
  setTimeout(onRefreshRendered, 100);
103
74
  }, [onPanelChange, onFetchFigma, onRefreshRendered]);
104
75
 
76
+ // Build tab change handler that also triggers side effects
77
+ const handleTabChange = useCallback((value: string | number) => {
78
+ const panel = String(value);
79
+ if (panel === 'styles') {
80
+ handleStylesClick();
81
+ } else {
82
+ onPanelChange(panel as ActivePanel);
83
+ }
84
+ }, [handleStylesClick, onPanelChange]);
85
+
105
86
  return (
106
87
  <ResizablePanel
107
88
  visible={true}
108
89
  header={
109
- <div className="flex items-center gap-1">
110
- <TabButton
111
- active={activePanel === 'code'}
112
- onClick={() => onPanelChange('code')}
113
- >
114
- Code
115
- </TabButton>
116
- {figmaUrl && (
117
- <TabButton
118
- active={activePanel === 'styles'}
119
- onClick={handleStylesClick}
120
- >
121
- Styles
122
- </TabButton>
123
- )}
124
- <TabButton
125
- active={activePanel === 'accessibility'}
126
- onClick={() => onPanelChange('accessibility')}
127
- >
128
- Accessibility
129
- </TabButton>
130
- {variant?.hasPlayFunction && (
131
- <TabButton
132
- active={activePanel === 'interactions'}
133
- onClick={() => onPanelChange('interactions')}
134
- >
135
- Interactions
136
- </TabButton>
137
- )}
138
- <TabButton
139
- active={activePanel === 'actions'}
140
- onClick={() => onPanelChange('actions')}
141
- badge={actionLogs.length}
142
- >
143
- Actions
144
- </TabButton>
145
- <TabButton
146
- active={activePanel === 'graph'}
147
- onClick={() => onPanelChange('graph')}
148
- >
149
- Graph
150
- </TabButton>
151
- <TabButton
152
- active={activePanel === 'contract'}
153
- onClick={() => onPanelChange('contract')}
154
- >
155
- Contract
156
- </TabButton>
157
- </div>
90
+ <Tabs value={activePanel} onValueChange={handleTabChange}>
91
+ <Tabs.List variant="pills">
92
+ <Tabs.Tab value="code">Code</Tabs.Tab>
93
+ {figmaUrl && <Tabs.Tab value="styles">Styles</Tabs.Tab>}
94
+ <Tabs.Tab value="accessibility">Accessibility</Tabs.Tab>
95
+ {variant?.hasPlayFunction && <Tabs.Tab value="interactions">Interactions</Tabs.Tab>}
96
+ <Tabs.Tab value="actions">
97
+ <span style={{ position: 'relative' }}>
98
+ Actions
99
+ {actionLogs.length > 0 && (
100
+ <Badge
101
+ variant="info"
102
+ size="sm"
103
+ style={{
104
+ position: 'absolute',
105
+ top: '-8px',
106
+ right: '-16px',
107
+ }}
108
+ >
109
+ {actionLogs.length > 99 ? '99+' : actionLogs.length}
110
+ </Badge>
111
+ )}
112
+ </span>
113
+ </Tabs.Tab>
114
+ <Tabs.Tab value="graph">Graph</Tabs.Tab>
115
+ <Tabs.Tab value="contract">Contract</Tabs.Tab>
116
+ </Tabs.List>
117
+ </Tabs>
158
118
  }
159
119
  >
160
120
  {activePanel === 'code' && (
161
- <div className="p-4">
121
+ <div style={{ padding: '16px' }}>
162
122
  <CodePanel
163
123
  variant={variant}
164
124
  componentName={segment.meta.name}
@@ -1,8 +1,6 @@
1
- import { useState, useCallback, useMemo, useEffect, isValidElement, type ReactNode } from 'react';
1
+ import { useMemo, isValidElement, type ReactNode } from 'react';
2
2
  import type { SegmentVariant, PropDefinition } from '../../core/index.js';
3
- import { codeToHtml } from 'shiki';
4
- import clsx from 'clsx';
5
- import { CopyIcon, CheckIcon } from './Icons.js';
3
+ import { CodeBlock } from '@fragments/ui';
6
4
 
7
5
  interface CodePanelProps {
8
6
  variant: SegmentVariant;
@@ -213,9 +211,6 @@ function extractRenderBody(renderFn: () => ReactNode): string | null {
213
211
  }
214
212
 
215
213
  export function CodePanel({ variant, componentName, compact = false, propDefs }: CodePanelProps) {
216
- const [copied, setCopied] = useState(false);
217
- const [highlightedHtml, setHighlightedHtml] = useState<string>('');
218
-
219
214
  // Generate code - extract from render function source for accuracy
220
215
  const generatedCode = useMemo(() => {
221
216
  // Priority 1: Use variant.code if available (from compiled JSON/AST)
@@ -243,90 +238,17 @@ export function CodePanel({ variant, componentName, compact = false, propDefs }:
243
238
  return generateCombinedCode(componentName, propDefs, effectiveArgs, needsState);
244
239
  }, [componentName, variant, propDefs]);
245
240
 
246
- // Apply syntax highlighting
247
- useEffect(() => {
248
- let cancelled = false;
249
-
250
- codeToHtml(generatedCode, {
251
- lang: 'tsx',
252
- theme: 'one-dark-pro',
253
- }).then(html => {
254
- if (!cancelled) {
255
- setHighlightedHtml(html);
256
- }
257
- }).catch(err => {
258
- console.error('Syntax highlighting failed:', err);
259
- if (!cancelled) {
260
- // Fallback to plain text
261
- setHighlightedHtml(`<pre><code>${escapeHtml(generatedCode)}</code></pre>`);
262
- }
263
- });
264
-
265
- return () => { cancelled = true; };
266
- }, [generatedCode]);
267
-
268
- const handleCopy = useCallback(async () => {
269
- try {
270
- await navigator.clipboard.writeText(generatedCode);
271
- setCopied(true);
272
- setTimeout(() => setCopied(false), 2000);
273
- } catch (err) {
274
- console.error('Failed to copy:', err);
275
- }
276
- }, [generatedCode]);
277
-
278
241
  return (
279
- <div className="relative">
280
- {/* Syntax highlighted code */}
281
- <div
282
- className={clsx(
283
- 'rounded-lg overflow-auto border border-[--border] bg-[#282c34]',
284
- '[&_pre]:!bg-transparent [&_pre]:!m-0 [&_pre]:p-4',
285
- '[&_code]:!bg-transparent [&_code]:text-[13px] [&_code]:leading-relaxed',
286
- '[&_.shiki]:!bg-transparent',
287
- // Custom selection highlight for better visibility
288
- '[&_*::selection]:bg-blue-500/40 [&_*::selection]:text-white',
289
- compact && '[&_code]:text-xs'
290
- )}
291
- style={{ maxHeight: 400 }}
292
- dangerouslySetInnerHTML={{ __html: highlightedHtml || '<div class="p-4 text-gray-500 text-sm">Loading...</div>' }}
293
- />
294
-
295
- {/* Copy button - fixed to top right of editor */}
296
- <button
297
- onClick={handleCopy}
298
- className={clsx(
299
- 'absolute top-2 right-2 flex items-center gap-1.5 px-2.5 py-1.5 text-xs font-medium rounded-md transition-all duration-200',
300
- copied
301
- ? 'bg-green-500/20 text-green-400 border border-green-500/30'
302
- : 'bg-white/10 text-gray-300 border border-white/10 hover:bg-white/15 hover:text-white'
303
- )}
304
- >
305
- {copied ? (
306
- <>
307
- <CheckIcon className="w-3.5 h-3.5" />
308
- <span>Copied!</span>
309
- </>
310
- ) : (
311
- <>
312
- <CopyIcon className="w-3.5 h-3.5" />
313
- <span>Copy</span>
314
- </>
315
- )}
316
- </button>
317
- </div>
242
+ <CodeBlock
243
+ code={generatedCode}
244
+ language="tsx"
245
+ showCopy
246
+ maxHeight={400}
247
+ compact={compact}
248
+ />
318
249
  );
319
250
  }
320
251
 
321
- function escapeHtml(str: string): string {
322
- return str
323
- .replace(/&/g, '&amp;')
324
- .replace(/</g, '&lt;')
325
- .replace(/>/g, '&gt;')
326
- .replace(/"/g, '&quot;')
327
- .replace(/'/g, '&#039;');
328
- }
329
-
330
252
  /**
331
253
  * Normalize indentation by removing common leading whitespace from all lines.
332
254
  * Handles JSX where first line may be at column 0 but inner content is indented.
@@ -5,10 +5,12 @@
5
5
  * - Quick navigation to components
6
6
  * - Fuzzy search across all components and variants
7
7
  * - Keyboard navigation (arrows, enter, escape)
8
+ *
9
+ * Uses Fragments UI Dialog for the overlay shell.
8
10
  */
9
11
 
10
12
  import { useState, useEffect, useRef, useMemo, useCallback } from "react";
11
- import clsx from "clsx";
13
+ import { Dialog, Stack, Text, Badge, Separator, Input } from '@fragments/ui';
12
14
  import type { SegmentDefinition } from "../../core/index.js";
13
15
  import { SearchIcon, ChevronRightIcon } from "./Icons.js";
14
16
 
@@ -168,96 +170,132 @@ export function CommandPalette({
168
170
  if (!isOpen) return null;
169
171
 
170
172
  return (
171
- <div className="fixed inset-0 z-50 flex items-start justify-center pt-[15vh]">
172
- {/* Backdrop */}
173
- <div
174
- className="absolute inset-0 bg-black/50 backdrop-blur-sm"
175
- onClick={onClose}
176
- />
177
-
178
- {/* Palette */}
179
- <div
180
- className="relative w-full max-w-xl bg-[--bg-primary] rounded-xl shadow-2xl border border-[--border] overflow-hidden"
181
- onKeyDown={handleKeyDown}
182
- >
173
+ <Dialog open={isOpen} onOpenChange={(open: boolean) => { if (!open) onClose(); }}>
174
+ <Dialog.Content size="lg">
183
175
  {/* Search input */}
184
- <div className="flex items-center px-4 py-3 border-b-2 border-blue-500">
185
- <SearchIcon className="w-5 h-5 text-blue-500 mr-3" />
176
+ <Stack direction="row" align="center" style={{
177
+ padding: '12px 16px',
178
+ borderBottom: '2px solid #3b82f6',
179
+ }}>
180
+ <SearchIcon style={{ width: 20, height: 20, color: '#3b82f6', marginRight: 12 }} />
186
181
  <input
187
182
  ref={inputRef}
188
183
  type="text"
189
184
  value={query}
190
185
  onChange={(e) => setQuery(e.target.value)}
186
+ onKeyDown={handleKeyDown}
191
187
  placeholder="Search components and variants..."
192
- className="flex-1 bg-transparent text-primary placeholder-tertiary text-base focus:outline-none"
188
+ style={{
189
+ flex: 1,
190
+ background: 'transparent',
191
+ color: 'var(--text-primary)',
192
+ fontSize: '16px',
193
+ border: 'none',
194
+ outline: 'none',
195
+ }}
193
196
  />
194
- <kbd className="hidden sm:inline-flex items-center gap-1 px-2 py-0.5 text-xs text-tertiary bg-[--bg-secondary] rounded border border-[--border]">
195
- esc
196
- </kbd>
197
- </div>
197
+ <Badge size="sm" variant="default">esc</Badge>
198
+ </Stack>
198
199
 
199
200
  {/* Results */}
200
- <div
201
- ref={listRef}
202
- className="max-h-80 overflow-y-auto py-2"
203
- >
201
+ <div ref={listRef} style={{ maxHeight: '320px', overflowY: 'auto', padding: '8px 0' }}>
204
202
  {results.length === 0 ? (
205
- <div className="px-4 py-8 text-center text-tertiary">
206
- {query ? (
207
- <>No results for "{query}"</>
208
- ) : (
209
- <>Start typing to search...</>
210
- )}
203
+ <div style={{ padding: '32px 16px', textAlign: 'center' }}>
204
+ <Text color="tertiary">
205
+ {query ? (
206
+ <>No results for &ldquo;{query}&rdquo;</>
207
+ ) : (
208
+ <>Start typing to search...</>
209
+ )}
210
+ </Text>
211
211
  </div>
212
212
  ) : (
213
213
  results.map((result, index) => (
214
214
  <button
215
215
  key={`${result.path}-${result.variantName || "component"}`}
216
216
  onClick={() => handleSelect(result)}
217
- className={clsx(
218
- "w-full px-4 py-2 flex items-center gap-3 text-left transition-colors",
219
- index === selectedIndex
220
- ? "bg-blue-500/10 text-primary"
221
- : "text-secondary hover:bg-[--bg-hover]"
222
- )}
217
+ style={{
218
+ width: '100%',
219
+ padding: '8px 16px',
220
+ display: 'flex',
221
+ alignItems: 'center',
222
+ gap: '12px',
223
+ textAlign: 'left',
224
+ transition: 'background-color 150ms',
225
+ border: 'none',
226
+ cursor: 'pointer',
227
+ backgroundColor: index === selectedIndex ? 'rgba(59, 130, 246, 0.1)' : 'transparent',
228
+ color: index === selectedIndex ? 'var(--text-primary)' : 'var(--text-secondary)',
229
+ }}
230
+ onMouseEnter={(e) => {
231
+ setSelectedIndex(index);
232
+ if (index !== selectedIndex) {
233
+ e.currentTarget.style.backgroundColor = 'var(--bg-hover)';
234
+ }
235
+ }}
236
+ onMouseLeave={(e) => {
237
+ if (index !== selectedIndex) {
238
+ e.currentTarget.style.backgroundColor = 'transparent';
239
+ }
240
+ }}
223
241
  >
224
242
  {/* Icon/Badge */}
225
- <div
226
- className={clsx(
227
- "flex-shrink-0 w-8 h-8 rounded-lg flex items-center justify-center text-xs font-medium",
228
- result.type === "component"
229
- ? "bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300"
230
- : "bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300"
231
- )}
243
+ <Badge
244
+ size="sm"
245
+ style={{
246
+ flexShrink: 0,
247
+ width: '32px',
248
+ height: '32px',
249
+ borderRadius: '8px',
250
+ display: 'flex',
251
+ alignItems: 'center',
252
+ justifyContent: 'center',
253
+ backgroundColor: result.type === "component"
254
+ ? 'rgba(59, 130, 246, 0.15)'
255
+ : 'rgba(168, 85, 247, 0.15)',
256
+ color: result.type === "component"
257
+ ? '#3b82f6'
258
+ : '#a855f7',
259
+ }}
232
260
  >
233
261
  {result.type === "component" ? "C" : "V"}
234
- </div>
262
+ </Badge>
235
263
 
236
264
  {/* Content */}
237
- <div className="flex-1 min-w-0">
238
- <div className="flex items-center gap-2">
239
- <span className="font-medium truncate">
265
+ <div style={{ flex: 1, minWidth: 0 }}>
266
+ <Stack direction="row" align="center" gap="sm">
267
+ <Text weight="medium" style={{
268
+ overflow: 'hidden',
269
+ textOverflow: 'ellipsis',
270
+ whiteSpace: 'nowrap',
271
+ }}>
240
272
  {result.componentName}
241
- </span>
273
+ </Text>
242
274
  {result.variantName && (
243
275
  <>
244
- <ChevronRightIcon className="w-3 h-3 text-tertiary flex-shrink-0" />
245
- <span className="text-secondary truncate">
276
+ <ChevronRightIcon style={{ width: 12, height: 12, color: 'var(--text-tertiary)', flexShrink: 0 }} />
277
+ <Text color="secondary" style={{
278
+ overflow: 'hidden',
279
+ textOverflow: 'ellipsis',
280
+ whiteSpace: 'nowrap',
281
+ }}>
246
282
  {result.variantName}
247
- </span>
283
+ </Text>
248
284
  </>
249
285
  )}
250
- </div>
251
- <div className="text-xs text-tertiary truncate">
286
+ </Stack>
287
+ <Text size="xs" color="tertiary" style={{
288
+ overflow: 'hidden',
289
+ textOverflow: 'ellipsis',
290
+ whiteSpace: 'nowrap',
291
+ }}>
252
292
  {result.category}
253
- </div>
293
+ </Text>
254
294
  </div>
255
295
 
256
296
  {/* Keyboard hint for selected */}
257
297
  {index === selectedIndex && (
258
- <kbd className="hidden sm:inline-flex items-center px-1.5 py-0.5 text-[10px] text-tertiary bg-[--bg-secondary] rounded border border-[--border]">
259
- enter
260
- </kbd>
298
+ <Badge size="sm" variant="default">enter</Badge>
261
299
  )}
262
300
  </button>
263
301
  ))
@@ -265,24 +303,29 @@ export function CommandPalette({
265
303
  </div>
266
304
 
267
305
  {/* Footer */}
268
- <div className="px-4 py-2 border-t border-[--border] flex items-center justify-between text-xs text-tertiary">
269
- <div className="flex items-center gap-3">
270
- <span className="flex items-center gap-1">
271
- <kbd className="px-1.5 py-0.5 bg-[--bg-secondary] rounded border border-[--border]">↑</kbd>
272
- <kbd className="px-1.5 py-0.5 bg-[--bg-secondary] rounded border border-[--border]">↓</kbd>
273
- to navigate
274
- </span>
275
- <span className="flex items-center gap-1">
276
- <kbd className="px-1.5 py-0.5 bg-[--bg-secondary] rounded border border-[--border]">↵</kbd>
277
- to select
278
- </span>
279
- </div>
280
- <span>
281
- {results.length} result{results.length !== 1 ? "s" : ""}
282
- </span>
306
+ <div style={{
307
+ padding: '8px 16px',
308
+ borderTop: '1px solid var(--border)',
309
+ }}>
310
+ <Stack direction="row" align="center" justify="between">
311
+ <Stack direction="row" align="center" gap="md">
312
+ <Stack direction="row" align="center" gap="xs">
313
+ <Badge size="sm" variant="default">&#8593;</Badge>
314
+ <Badge size="sm" variant="default">&#8595;</Badge>
315
+ <Text size="xs" color="tertiary">to navigate</Text>
316
+ </Stack>
317
+ <Stack direction="row" align="center" gap="xs">
318
+ <Badge size="sm" variant="default">&#8629;</Badge>
319
+ <Text size="xs" color="tertiary">to select</Text>
320
+ </Stack>
321
+ </Stack>
322
+ <Text size="xs" color="tertiary">
323
+ {results.length} result{results.length !== 1 ? "s" : ""}
324
+ </Text>
325
+ </Stack>
283
326
  </div>
284
- </div>
285
- </div>
327
+ </Dialog.Content>
328
+ </Dialog>
286
329
  );
287
330
  }
288
331