@fragments-sdk/cli 0.5.2 → 0.6.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/dist/bin.js +712 -39
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-ICAIQ57V.js → chunk-6JBGU74P.js} +5 -3
- package/dist/chunk-6JBGU74P.js.map +1 -0
- package/dist/{chunk-U4GQ2JTD.js → chunk-D35RGPAG.js} +412 -35
- package/dist/chunk-D35RGPAG.js.map +1 -0
- package/dist/{chunk-XNWDI6UT.js → chunk-F7ITZPDJ.js} +5 -5
- package/dist/{chunk-IOJE35DZ.js → chunk-NWQ4CJOQ.js} +3 -3
- package/dist/{chunk-V7YLRR4C.js → chunk-Q7GOHVOK.js} +3 -3
- package/dist/{chunk-2DJH4F4P.js → chunk-RVRTRESS.js} +3 -3
- package/dist/{chunk-2H2JAA3U.js → chunk-SSLQXHNX.js} +3 -3
- package/dist/{core-DKHB7FYV.js → core-SKRPJQZG.js} +4 -4
- package/dist/{generate-KL24VZVD.js → generate-7AF7WRVK.js} +5 -5
- package/dist/index.d.ts +1 -0
- package/dist/index.js +15 -7
- package/dist/index.js.map +1 -1
- package/dist/{init-NION5S3M.js → init-WKGDPYI4.js} +5 -5
- package/dist/mcp-bin.js +8 -220
- package/dist/mcp-bin.js.map +1 -1
- package/dist/scan-K6JNMCGM.js +12 -0
- package/dist/{service-RWUMZ3EW.js → service-F3E4JJM7.js} +5 -5
- package/dist/static-viewer-4LQZ5AGA.js +12 -0
- package/dist/{test-ECPEXFDN.js → test-CJDNJTPZ.js} +4 -4
- package/dist/{tokens-ITADYVPF.js → tokens-JAJABYXP.js} +6 -6
- package/dist/viewer-R3Q6WAMJ.js +1822 -0
- package/dist/viewer-R3Q6WAMJ.js.map +1 -0
- package/package.json +5 -4
- package/src/bin.ts +8 -0
- package/src/build.ts +104 -13
- package/src/cli-commands.ts +18 -0
- package/src/commands/__tests__/a11y-scoring.test.ts +278 -0
- package/src/commands/a11y-report.ts +625 -0
- package/src/commands/a11y.ts +168 -14
- package/src/commands/build.ts +16 -0
- package/src/core/auto-props.ts +464 -0
- package/src/core/schema.ts +2 -0
- package/src/core/types.ts +3 -1
- package/src/index.ts +4 -0
- package/src/mcp/server.ts +13 -220
- package/src/theme/__tests__/component-contrast.test.ts +338 -0
- package/src/theme/__tests__/contrast-validation.test.ts +326 -0
- package/src/theme/contrast.test.ts +331 -0
- package/src/theme/contrast.ts +246 -0
- package/src/theme/generator.ts +213 -1
- package/src/theme/index.ts +16 -0
- package/src/theme/types.ts +51 -0
- package/src/viewer/__tests__/a11y-fixes.test.ts +358 -0
- package/src/viewer/__tests__/viewer-integration.test.ts +2 -7
- package/src/viewer/components/AccessibilityPanel.tsx +493 -433
- package/src/viewer/components/ActionCapture.tsx +1 -1
- package/src/viewer/components/ActionsPanel.tsx +142 -183
- package/src/viewer/components/App.tsx +159 -164
- package/src/viewer/components/BottomPanel.tsx +40 -80
- package/src/viewer/components/CodePanel.tsx +9 -87
- package/src/viewer/components/CommandPalette.tsx +117 -74
- package/src/viewer/components/ComponentGraph.tsx +143 -126
- package/src/viewer/components/ComponentHeader.tsx +46 -43
- package/src/viewer/components/ContractPanel.tsx +124 -117
- package/src/viewer/components/ErrorBoundary.tsx +47 -35
- package/src/viewer/components/FigmaEmbed.tsx +18 -13
- package/src/viewer/components/FragmentEditor.tsx +126 -63
- package/src/viewer/components/HealthDashboard.tsx +146 -171
- package/src/viewer/components/HmrStatusIndicator.tsx +31 -41
- package/src/viewer/components/Icons.tsx +99 -98
- package/src/viewer/components/InteractionsPanel.tsx +317 -264
- package/src/viewer/components/IsolatedPreviewFrame.tsx +52 -27
- package/src/viewer/components/IsolatedRender.tsx +12 -6
- package/src/viewer/components/KeyboardShortcutsHelp.tsx +34 -70
- package/src/viewer/components/LandingPage.tsx +285 -305
- package/src/viewer/components/Layout.tsx +7 -9
- package/src/viewer/components/LeftSidebar.tsx +78 -108
- package/src/viewer/components/MultiViewportPreview.tsx +254 -63
- package/src/viewer/components/PreviewArea.tsx +113 -44
- package/src/viewer/components/PreviewFrameHost.tsx +6 -5
- package/src/viewer/components/PreviewPane.tsx +2 -3
- package/src/viewer/components/PreviewToolbar.tsx +61 -104
- package/src/viewer/components/PropsEditor.tsx +154 -74
- package/src/viewer/components/PropsTable.tsx +95 -82
- package/src/viewer/components/RelationsSection.tsx +71 -40
- package/src/viewer/components/ResizablePanel.tsx +158 -55
- package/src/viewer/components/RightSidebar.tsx +46 -56
- package/src/viewer/components/ScreenshotButton.tsx +12 -12
- package/src/viewer/components/SkeletonLoader.tsx +99 -83
- package/src/viewer/components/StoryRenderer.tsx +4 -11
- package/src/viewer/components/Toast.tsx +3 -67
- package/src/viewer/components/TokenStylePanel.tsx +136 -118
- package/src/viewer/components/UsageSection.tsx +26 -26
- package/src/viewer/components/VariantMatrix.tsx +140 -47
- package/src/viewer/components/VariantTabs.tsx +24 -68
- package/src/viewer/components/ViewportSelector.tsx +106 -110
- package/src/viewer/constants/ui.ts +19 -18
- package/src/viewer/entry.tsx +8 -3
- package/src/viewer/index.ts +3 -6
- package/src/viewer/preview-frame.html +21 -5
- package/src/viewer/server.ts +7 -16
- package/src/viewer/styles/globals.css +4 -4
- package/src/viewer/utils/a11y-fixes.ts +53 -30
- package/dist/chunk-ICAIQ57V.js.map +0 -1
- package/dist/chunk-U4GQ2JTD.js.map +0 -1
- package/dist/scan-ESEXV7LF.js +0 -12
- package/dist/static-viewer-O37MJ5B6.js +0 -12
- package/dist/viewer-YDGFDTK5.js +0 -11104
- package/dist/viewer-YDGFDTK5.js.map +0 -1
- package/src/viewer/postcss.config.js +0 -6
- package/src/viewer/tailwind.config.js +0 -37
- /package/dist/{chunk-XNWDI6UT.js.map → chunk-F7ITZPDJ.js.map} +0 -0
- /package/dist/{chunk-IOJE35DZ.js.map → chunk-NWQ4CJOQ.js.map} +0 -0
- /package/dist/{chunk-V7YLRR4C.js.map → chunk-Q7GOHVOK.js.map} +0 -0
- /package/dist/{chunk-2DJH4F4P.js.map → chunk-RVRTRESS.js.map} +0 -0
- /package/dist/{chunk-2H2JAA3U.js.map → chunk-SSLQXHNX.js.map} +0 -0
- /package/dist/{core-DKHB7FYV.js.map → core-SKRPJQZG.js.map} +0 -0
- /package/dist/{generate-KL24VZVD.js.map → generate-7AF7WRVK.js.map} +0 -0
- /package/dist/{init-NION5S3M.js.map → init-WKGDPYI4.js.map} +0 -0
- /package/dist/{scan-ESEXV7LF.js.map → scan-K6JNMCGM.js.map} +0 -0
- /package/dist/{service-RWUMZ3EW.js.map → service-F3E4JJM7.js.map} +0 -0
- /package/dist/{static-viewer-O37MJ5B6.js.map → static-viewer-4LQZ5AGA.js.map} +0 -0
- /package/dist/{test-ECPEXFDN.js.map → test-CJDNJTPZ.js.map} +0 -0
- /package/dist/{tokens-ITADYVPF.js.map → tokens-JAJABYXP.js.map} +0 -0
|
@@ -68,38 +68,80 @@ const DeviceMockup = memo(function DeviceMockup({ type, width, children }: Devic
|
|
|
68
68
|
const screenHeight = frameHeight - (isMobile ? 80 : 48);
|
|
69
69
|
|
|
70
70
|
return (
|
|
71
|
-
<div
|
|
71
|
+
<div style={{ position: 'relative', flexShrink: 0, width: `${frameWidth}px` }}>
|
|
72
72
|
<div
|
|
73
|
-
className="relative rounded-[40px] bg-[#1a1a1a] p-3 shadow-2xl"
|
|
74
73
|
style={{
|
|
74
|
+
position: 'relative',
|
|
75
|
+
borderRadius: '40px',
|
|
76
|
+
background: '#1a1a1a',
|
|
77
|
+
padding: '12px',
|
|
75
78
|
boxShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255,255,255,0.1)',
|
|
76
79
|
}}
|
|
77
80
|
>
|
|
78
81
|
{isMobile && (
|
|
79
82
|
<>
|
|
80
|
-
<div
|
|
81
|
-
<div
|
|
82
|
-
<div
|
|
83
|
-
<div
|
|
83
|
+
<div style={{ position: 'absolute', left: '-3px', top: '96px', width: '3px', height: '32px', background: '#2a2a2a', borderRadius: '4px 0 0 4px' }} />
|
|
84
|
+
<div style={{ position: 'absolute', left: '-3px', top: '144px', width: '3px', height: '48px', background: '#2a2a2a', borderRadius: '4px 0 0 4px' }} />
|
|
85
|
+
<div style={{ position: 'absolute', left: '-3px', top: '208px', width: '3px', height: '48px', background: '#2a2a2a', borderRadius: '4px 0 0 4px' }} />
|
|
86
|
+
<div style={{ position: 'absolute', right: '-3px', top: '128px', width: '3px', height: '64px', background: '#2a2a2a', borderRadius: '0 4px 4px 0' }} />
|
|
84
87
|
</>
|
|
85
88
|
)}
|
|
86
89
|
|
|
87
90
|
<div
|
|
88
|
-
|
|
89
|
-
|
|
91
|
+
style={{
|
|
92
|
+
position: 'relative',
|
|
93
|
+
borderRadius: '32px',
|
|
94
|
+
overflow: 'hidden',
|
|
95
|
+
background: 'white',
|
|
96
|
+
height: `${screenHeight}px`,
|
|
97
|
+
}}
|
|
90
98
|
>
|
|
91
99
|
{isMobile ? (
|
|
92
|
-
<div
|
|
93
|
-
|
|
94
|
-
|
|
100
|
+
<div style={{
|
|
101
|
+
position: 'absolute',
|
|
102
|
+
top: 0,
|
|
103
|
+
left: '50%',
|
|
104
|
+
transform: 'translateX(-50%)',
|
|
105
|
+
width: '120px',
|
|
106
|
+
height: '30px',
|
|
107
|
+
background: '#1a1a1a',
|
|
108
|
+
borderRadius: '0 0 16px 16px',
|
|
109
|
+
zIndex: 10,
|
|
110
|
+
display: 'flex',
|
|
111
|
+
alignItems: 'center',
|
|
112
|
+
justifyContent: 'center',
|
|
113
|
+
gap: '8px',
|
|
114
|
+
}}>
|
|
115
|
+
<div style={{ width: '8px', height: '8px', borderRadius: '50%', background: '#2a2a2a' }} />
|
|
116
|
+
<div style={{ width: '48px', height: '6px', borderRadius: '9999px', background: '#2a2a2a' }} />
|
|
95
117
|
</div>
|
|
96
118
|
) : (
|
|
97
|
-
<div
|
|
119
|
+
<div style={{
|
|
120
|
+
position: 'absolute',
|
|
121
|
+
top: '8px',
|
|
122
|
+
left: '50%',
|
|
123
|
+
transform: 'translateX(-50%)',
|
|
124
|
+
width: '12px',
|
|
125
|
+
height: '12px',
|
|
126
|
+
borderRadius: '50%',
|
|
127
|
+
background: '#2a2a2a',
|
|
128
|
+
zIndex: 10,
|
|
129
|
+
}} />
|
|
98
130
|
)}
|
|
99
131
|
|
|
100
|
-
<div
|
|
132
|
+
<div style={{ width: '100%', height: '100%', overflow: 'auto' }}>{children}</div>
|
|
101
133
|
|
|
102
|
-
<div
|
|
134
|
+
<div style={{
|
|
135
|
+
position: 'absolute',
|
|
136
|
+
bottom: '8px',
|
|
137
|
+
left: '50%',
|
|
138
|
+
transform: 'translateX(-50%)',
|
|
139
|
+
width: '100px',
|
|
140
|
+
height: '4px',
|
|
141
|
+
background: 'rgba(0, 0, 0, 0.2)',
|
|
142
|
+
borderRadius: '9999px',
|
|
143
|
+
zIndex: 10,
|
|
144
|
+
}} />
|
|
103
145
|
</div>
|
|
104
146
|
</div>
|
|
105
147
|
</div>
|
|
@@ -119,14 +161,16 @@ const PreviewContent = memo(function PreviewContent({ zoom, previewTheme, backgr
|
|
|
119
161
|
<div
|
|
120
162
|
data-preview-container="true"
|
|
121
163
|
data-theme={previewTheme}
|
|
122
|
-
className="w-full h-full overflow-auto"
|
|
123
164
|
style={{
|
|
165
|
+
width: '100%',
|
|
166
|
+
height: '100%',
|
|
167
|
+
overflow: 'auto',
|
|
124
168
|
backgroundColor: background === 'transparent' ? 'transparent' : undefined,
|
|
125
169
|
}}
|
|
126
170
|
>
|
|
127
171
|
<div
|
|
128
|
-
className="p-6"
|
|
129
172
|
style={{
|
|
173
|
+
padding: '24px',
|
|
130
174
|
transform: `scale(${zoom / 100})`,
|
|
131
175
|
transformOrigin: 'top left',
|
|
132
176
|
width: zoom !== 100 ? `${100 / (zoom / 100)}%` : '100%',
|
|
@@ -205,11 +249,11 @@ export function PreviewArea({
|
|
|
205
249
|
|
|
206
250
|
if (showComparison && figmaUrl) {
|
|
207
251
|
return (
|
|
208
|
-
<div
|
|
209
|
-
<div
|
|
210
|
-
<div
|
|
211
|
-
<div
|
|
212
|
-
<div
|
|
252
|
+
<div style={{ minHeight: '100%', display: 'flex', flexDirection: 'column', padding: '24px' }}>
|
|
253
|
+
<div style={{ display: 'flex', gap: '16px', flex: 1 }}>
|
|
254
|
+
<div style={{ flex: 1, display: 'flex', flexDirection: 'column' }}>
|
|
255
|
+
<div style={{ fontSize: '12px', fontWeight: 500, color: 'var(--text-tertiary)', marginBottom: '8px', textAlign: 'center' }}>Rendered</div>
|
|
256
|
+
<div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', ...backgroundStyle }}>
|
|
213
257
|
<DeviceMockup type={viewport as 'tablet' | 'mobile'} width={viewportWidth}>
|
|
214
258
|
{useIframeIsolation ? (
|
|
215
259
|
<IsolatedPreviewFrame
|
|
@@ -231,14 +275,19 @@ export function PreviewArea({
|
|
|
231
275
|
</div>
|
|
232
276
|
</div>
|
|
233
277
|
|
|
234
|
-
<div
|
|
235
|
-
<div
|
|
278
|
+
<div style={{ flex: 1, display: 'flex', flexDirection: 'column' }}>
|
|
279
|
+
<div style={{ fontSize: '12px', fontWeight: 500, color: 'var(--text-tertiary)', marginBottom: '8px', textAlign: 'center' }}>Figma Design</div>
|
|
236
280
|
<FigmaEmbed
|
|
237
281
|
figmaUrl={figmaUrl}
|
|
238
282
|
allFigmaUrls={allFigmaUrls}
|
|
239
283
|
zoom={zoom}
|
|
240
|
-
|
|
241
|
-
|
|
284
|
+
style={{
|
|
285
|
+
flex: 1,
|
|
286
|
+
borderRadius: '8px',
|
|
287
|
+
border: '1px solid var(--border)',
|
|
288
|
+
overflow: 'hidden',
|
|
289
|
+
...backgroundStyle,
|
|
290
|
+
}}
|
|
242
291
|
/>
|
|
243
292
|
</div>
|
|
244
293
|
</div>
|
|
@@ -247,7 +296,7 @@ export function PreviewArea({
|
|
|
247
296
|
}
|
|
248
297
|
|
|
249
298
|
return (
|
|
250
|
-
<div
|
|
299
|
+
<div style={{ minHeight: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '32px' }}>
|
|
251
300
|
<DeviceMockup type={viewport as 'tablet' | 'mobile'} width={viewportWidth}>
|
|
252
301
|
{useIframeIsolation ? (
|
|
253
302
|
<IsolatedPreviewFrame
|
|
@@ -273,13 +322,18 @@ export function PreviewArea({
|
|
|
273
322
|
// Side-by-side comparison view
|
|
274
323
|
if (showComparison && figmaUrl && variant) {
|
|
275
324
|
return (
|
|
276
|
-
<div
|
|
277
|
-
<div
|
|
278
|
-
<div
|
|
279
|
-
<div
|
|
325
|
+
<div style={{ minHeight: '100%', display: 'flex', flexDirection: 'column', padding: '24px' }}>
|
|
326
|
+
<div style={{ display: 'flex', gap: '16px', flex: 1 }}>
|
|
327
|
+
<div style={{ flex: 1, display: 'flex', flexDirection: 'column' }}>
|
|
328
|
+
<div style={{ fontSize: '12px', fontWeight: 500, color: 'var(--text-tertiary)', marginBottom: '8px', textAlign: 'center' }}>Rendered</div>
|
|
280
329
|
<div
|
|
281
|
-
|
|
282
|
-
|
|
330
|
+
style={{
|
|
331
|
+
flex: 1,
|
|
332
|
+
borderRadius: '8px',
|
|
333
|
+
border: '1px solid var(--border)',
|
|
334
|
+
overflow: 'auto',
|
|
335
|
+
...backgroundStyle,
|
|
336
|
+
}}
|
|
283
337
|
>
|
|
284
338
|
{useIframeIsolation ? (
|
|
285
339
|
<IsolatedPreviewFrame
|
|
@@ -293,7 +347,12 @@ export function PreviewArea({
|
|
|
293
347
|
/>
|
|
294
348
|
) : (
|
|
295
349
|
<div
|
|
296
|
-
|
|
350
|
+
style={{
|
|
351
|
+
display: 'flex',
|
|
352
|
+
alignItems: 'center',
|
|
353
|
+
justifyContent: 'center',
|
|
354
|
+
padding: '32px',
|
|
355
|
+
}}
|
|
297
356
|
data-preview-container="true"
|
|
298
357
|
data-theme={previewTheme}
|
|
299
358
|
>
|
|
@@ -314,14 +373,19 @@ export function PreviewArea({
|
|
|
314
373
|
</div>
|
|
315
374
|
</div>
|
|
316
375
|
|
|
317
|
-
<div
|
|
318
|
-
<div
|
|
376
|
+
<div style={{ flex: 1, display: 'flex', flexDirection: 'column' }}>
|
|
377
|
+
<div style={{ fontSize: '12px', fontWeight: 500, color: 'var(--text-tertiary)', marginBottom: '8px', textAlign: 'center' }}>Figma Design</div>
|
|
319
378
|
<FigmaEmbed
|
|
320
379
|
figmaUrl={figmaUrl}
|
|
321
380
|
allFigmaUrls={allFigmaUrls}
|
|
322
381
|
zoom={zoom}
|
|
323
|
-
|
|
324
|
-
|
|
382
|
+
style={{
|
|
383
|
+
flex: 1,
|
|
384
|
+
borderRadius: '8px',
|
|
385
|
+
border: '1px solid var(--border)',
|
|
386
|
+
overflow: 'hidden',
|
|
387
|
+
...backgroundStyle,
|
|
388
|
+
}}
|
|
325
389
|
/>
|
|
326
390
|
</div>
|
|
327
391
|
</div>
|
|
@@ -336,10 +400,14 @@ export function PreviewArea({
|
|
|
336
400
|
const isFullWidth = !viewportWidth;
|
|
337
401
|
|
|
338
402
|
return (
|
|
339
|
-
<div
|
|
403
|
+
<div style={isFullWidth
|
|
404
|
+
? { height: '100%', display: 'flex', flexDirection: 'column' }
|
|
405
|
+
: { minHeight: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '24px' }
|
|
406
|
+
}>
|
|
340
407
|
<div
|
|
341
|
-
className="relative transition-all duration-200"
|
|
342
408
|
style={{
|
|
409
|
+
position: 'relative',
|
|
410
|
+
transition: 'all 200ms',
|
|
343
411
|
width: viewportWidth ? `${viewportWidth}px` : '100%',
|
|
344
412
|
maxWidth: viewportWidth ? undefined : '100%',
|
|
345
413
|
height: isFullWidth ? '100%' : undefined,
|
|
@@ -367,12 +435,11 @@ export function PreviewArea({
|
|
|
367
435
|
|
|
368
436
|
// Fallback: Direct rendering without iframe isolation
|
|
369
437
|
return (
|
|
370
|
-
<div
|
|
438
|
+
<div style={{ minHeight: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '24px' }}>
|
|
371
439
|
<div
|
|
372
|
-
className="relative transition-all duration-200"
|
|
373
|
-
data-preview-container="true"
|
|
374
|
-
data-theme={previewTheme}
|
|
375
440
|
style={{
|
|
441
|
+
position: 'relative',
|
|
442
|
+
transition: 'all 200ms',
|
|
376
443
|
width: viewportWidth ? `${viewportWidth}px` : '100%',
|
|
377
444
|
maxWidth: viewportWidth ? undefined : '100%',
|
|
378
445
|
minHeight: viewportHeight ? `${viewportHeight}px` : '100%',
|
|
@@ -382,10 +449,12 @@ export function PreviewArea({
|
|
|
382
449
|
boxShadow: '0 0 0 1px var(--border), 0 4px 12px rgba(0,0,0,0.15)',
|
|
383
450
|
}),
|
|
384
451
|
}}
|
|
452
|
+
data-preview-container="true"
|
|
453
|
+
data-theme={previewTheme}
|
|
385
454
|
>
|
|
386
455
|
<div
|
|
387
|
-
className="p-8"
|
|
388
456
|
style={{
|
|
457
|
+
padding: '32px',
|
|
389
458
|
transform: `scale(${zoom / 100})`,
|
|
390
459
|
transformOrigin: 'top left',
|
|
391
460
|
width: zoom !== 100 ? `${100 / (zoom / 100)}%` : '100%',
|
|
@@ -87,7 +87,7 @@ function findVariant(segment: SegmentDefinition, variantName: string): SegmentVa
|
|
|
87
87
|
*/
|
|
88
88
|
function ErrorDisplay({ message, stack }: { message: string; stack?: string }) {
|
|
89
89
|
return (
|
|
90
|
-
<div
|
|
90
|
+
<div style={{ padding: '16px', color: '#dc2626', background: 'rgba(254, 242, 242, 0.95)', borderRadius: '8px', margin: '16px' }}>
|
|
91
91
|
<div style={{ fontWeight: 500, marginBottom: 8 }}>Render Error</div>
|
|
92
92
|
<div>{message}</div>
|
|
93
93
|
{stack && (
|
|
@@ -104,8 +104,9 @@ function ErrorDisplay({ message, stack }: { message: string; stack?: string }) {
|
|
|
104
104
|
*/
|
|
105
105
|
function LoadingIndicator() {
|
|
106
106
|
return (
|
|
107
|
-
<div
|
|
108
|
-
<div
|
|
107
|
+
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '32px', gap: '8px', color: '#6b7280' }}>
|
|
108
|
+
<div style={{ width: '16px', height: '16px', border: '2px solid #e5e7eb', borderTopColor: '#3b82f6', borderRadius: '50%', animation: 'spin 0.8s linear infinite' }} />
|
|
109
|
+
<style>{`@keyframes spin { to { transform: rotate(360deg); } }`}</style>
|
|
109
110
|
<span>Loading component...</span>
|
|
110
111
|
</div>
|
|
111
112
|
);
|
|
@@ -206,9 +207,9 @@ function VariantRenderer({
|
|
|
206
207
|
return (
|
|
207
208
|
<div
|
|
208
209
|
ref={containerRef}
|
|
209
|
-
className="transition-opacity duration-150"
|
|
210
210
|
style={{
|
|
211
211
|
display: 'inline-block',
|
|
212
|
+
transition: 'opacity 150ms',
|
|
212
213
|
opacity: content ? 1 : 0,
|
|
213
214
|
}}
|
|
214
215
|
>
|
|
@@ -289,7 +290,7 @@ export function PreviewFrameHost() {
|
|
|
289
290
|
// Show waiting state
|
|
290
291
|
if (!currentVariant) {
|
|
291
292
|
return (
|
|
292
|
-
<div
|
|
293
|
+
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '32px', gap: '8px', color: '#6b7280' }}>
|
|
293
294
|
<span>Waiting for render request...</span>
|
|
294
295
|
</div>
|
|
295
296
|
);
|
|
@@ -130,7 +130,6 @@ export function PreviewPane({ children, className, style, includeComponentStyles
|
|
|
130
130
|
return (
|
|
131
131
|
<div
|
|
132
132
|
ref={containerRef}
|
|
133
|
-
className={className}
|
|
134
133
|
style={{ minHeight: '120px', ...style }}
|
|
135
134
|
data-preview-wrapper="true"
|
|
136
135
|
/>
|
|
@@ -141,9 +140,9 @@ export function PreviewPane({ children, className, style, includeComponentStyles
|
|
|
141
140
|
* SimplePreviewPane - A simpler preview without Shadow DOM isolation.
|
|
142
141
|
* Use this when full isolation isn't needed.
|
|
143
142
|
*/
|
|
144
|
-
export function SimplePreviewPane({ children,
|
|
143
|
+
export function SimplePreviewPane({ children, style }: PreviewPaneProps) {
|
|
145
144
|
return (
|
|
146
|
-
<div
|
|
145
|
+
<div style={style}>
|
|
147
146
|
{children}
|
|
148
147
|
</div>
|
|
149
148
|
);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import { useEffect, useCallback } from 'react';
|
|
2
|
+
import { Button, Menu, Stack, Separator } from '@fragments/ui';
|
|
3
3
|
import {
|
|
4
4
|
ZOOM_LEVELS,
|
|
5
5
|
type ZoomLevel,
|
|
@@ -13,10 +13,10 @@ export { getBackgroundStyle } from '../constants/ui.js';
|
|
|
13
13
|
|
|
14
14
|
// Background options with display metadata
|
|
15
15
|
const BACKGROUND_OPTIONS_UI: { value: BackgroundOption; label: string; icon: string }[] = [
|
|
16
|
-
{ value: 'white', label: 'White', icon: '
|
|
17
|
-
{ value: 'black', label: 'Black', icon: '
|
|
18
|
-
{ value: 'checkerboard', label: 'Checkerboard', icon: '
|
|
19
|
-
{ value: 'transparent', label: 'Transparent', icon: '
|
|
16
|
+
{ value: 'white', label: 'White', icon: '\u25FB' },
|
|
17
|
+
{ value: 'black', label: 'Black', icon: '\u25FC' },
|
|
18
|
+
{ value: 'checkerboard', label: 'Checkerboard', icon: '\u25A6' },
|
|
19
|
+
{ value: 'transparent', label: 'Transparent', icon: '\u25C7' },
|
|
20
20
|
];
|
|
21
21
|
|
|
22
22
|
interface PreviewToolbarProps {
|
|
@@ -32,9 +32,6 @@ export function PreviewToolbar({
|
|
|
32
32
|
onZoomChange,
|
|
33
33
|
onBackgroundChange,
|
|
34
34
|
}: PreviewToolbarProps) {
|
|
35
|
-
const [zoomOpen, setZoomOpen] = useState(false);
|
|
36
|
-
const [bgOpen, setBgOpen] = useState(false);
|
|
37
|
-
|
|
38
35
|
// Keyboard shortcuts for zoom
|
|
39
36
|
const handleKeyDown = useCallback((e: KeyboardEvent) => {
|
|
40
37
|
// Don't handle if in input/textarea
|
|
@@ -66,111 +63,71 @@ export function PreviewToolbar({
|
|
|
66
63
|
return () => document.removeEventListener('keydown', handleKeyDown);
|
|
67
64
|
}, [handleKeyDown]);
|
|
68
65
|
|
|
69
|
-
// Close dropdowns when clicking outside
|
|
70
|
-
useEffect(() => {
|
|
71
|
-
const handleClick = () => {
|
|
72
|
-
setZoomOpen(false);
|
|
73
|
-
setBgOpen(false);
|
|
74
|
-
};
|
|
75
|
-
if (zoomOpen || bgOpen) {
|
|
76
|
-
document.addEventListener('click', handleClick);
|
|
77
|
-
return () => document.removeEventListener('click', handleClick);
|
|
78
|
-
}
|
|
79
|
-
}, [zoomOpen, bgOpen]);
|
|
80
|
-
|
|
81
66
|
return (
|
|
82
|
-
<
|
|
67
|
+
<Stack direction="row" gap="sm" align="center">
|
|
83
68
|
{/* Zoom dropdown */}
|
|
84
|
-
<
|
|
85
|
-
<
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
>
|
|
99
|
-
<
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
{zoomOpen && (
|
|
104
|
-
<div className="absolute top-full left-0 mt-1 py-1 min-w-[80px] bg-[--bg-elevated] border border-[--border] rounded-lg shadow-lg z-50">
|
|
69
|
+
<Menu>
|
|
70
|
+
<Menu.Trigger asChild>
|
|
71
|
+
<Button variant="ghost" size="sm" title="Zoom level (+/-/0)">
|
|
72
|
+
<Stack direction="row" gap="xs" align="center">
|
|
73
|
+
<span style={{ display: 'inline-flex', width: '14px', height: '14px' }}>
|
|
74
|
+
<ZoomIcon />
|
|
75
|
+
</span>
|
|
76
|
+
<span>{zoom}%</span>
|
|
77
|
+
<span style={{ display: 'inline-flex', width: '12px', height: '12px' }}>
|
|
78
|
+
<ChevronDownIcon />
|
|
79
|
+
</span>
|
|
80
|
+
</Stack>
|
|
81
|
+
</Button>
|
|
82
|
+
</Menu.Trigger>
|
|
83
|
+
<Menu.Content side="bottom" align="start">
|
|
84
|
+
<Menu.RadioGroup
|
|
85
|
+
value={String(zoom)}
|
|
86
|
+
onValueChange={(value: string) => onZoomChange(Number(value) as ZoomLevel)}
|
|
87
|
+
>
|
|
105
88
|
{ZOOM_LEVELS.map((level) => (
|
|
106
|
-
<
|
|
107
|
-
key={level}
|
|
108
|
-
onClick={(e) => {
|
|
109
|
-
e.stopPropagation();
|
|
110
|
-
onZoomChange(level);
|
|
111
|
-
setZoomOpen(false);
|
|
112
|
-
}}
|
|
113
|
-
className={clsx(
|
|
114
|
-
'w-full px-3 py-1.5 text-xs text-left',
|
|
115
|
-
'hover:bg-[--bg-hover] transition-colors',
|
|
116
|
-
level === zoom ? 'text-[--color-accent] font-medium' : 'text-secondary'
|
|
117
|
-
)}
|
|
118
|
-
>
|
|
89
|
+
<Menu.RadioItem key={level} value={String(level)}>
|
|
119
90
|
{level}%
|
|
120
|
-
</
|
|
91
|
+
</Menu.RadioItem>
|
|
121
92
|
))}
|
|
122
|
-
</
|
|
123
|
-
|
|
124
|
-
</
|
|
93
|
+
</Menu.RadioGroup>
|
|
94
|
+
</Menu.Content>
|
|
95
|
+
</Menu>
|
|
125
96
|
|
|
126
97
|
{/* Divider */}
|
|
127
|
-
<
|
|
98
|
+
<Separator orientation="vertical" />
|
|
128
99
|
|
|
129
100
|
{/* Background selector */}
|
|
130
|
-
<
|
|
131
|
-
<
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
>
|
|
145
|
-
<
|
|
146
|
-
{
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
<ChevronDownIcon className="w-3 h-3" />
|
|
150
|
-
</button>
|
|
151
|
-
{bgOpen && (
|
|
152
|
-
<div className="absolute top-full left-0 mt-1 py-1 min-w-[120px] bg-[--bg-elevated] border border-[--border] rounded-lg shadow-lg z-50">
|
|
101
|
+
<Menu>
|
|
102
|
+
<Menu.Trigger asChild>
|
|
103
|
+
<Button variant="ghost" size="sm" title="Background color">
|
|
104
|
+
<Stack direction="row" gap="xs" align="center">
|
|
105
|
+
<span style={{ fontSize: '14px' }}>
|
|
106
|
+
{BACKGROUND_OPTIONS_UI.find(o => o.value === background)?.icon}
|
|
107
|
+
</span>
|
|
108
|
+
<span style={{ textTransform: 'capitalize' }}>{background}</span>
|
|
109
|
+
<span style={{ display: 'inline-flex', width: '12px', height: '12px' }}>
|
|
110
|
+
<ChevronDownIcon />
|
|
111
|
+
</span>
|
|
112
|
+
</Stack>
|
|
113
|
+
</Button>
|
|
114
|
+
</Menu.Trigger>
|
|
115
|
+
<Menu.Content side="bottom" align="start">
|
|
116
|
+
<Menu.RadioGroup
|
|
117
|
+
value={background}
|
|
118
|
+
onValueChange={(value: string) => onBackgroundChange(value as BackgroundOption)}
|
|
119
|
+
>
|
|
153
120
|
{BACKGROUND_OPTIONS_UI.map((option) => (
|
|
154
|
-
<
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}}
|
|
161
|
-
className={clsx(
|
|
162
|
-
'w-full px-3 py-1.5 text-xs text-left flex items-center gap-2',
|
|
163
|
-
'hover:bg-[--bg-hover] transition-colors',
|
|
164
|
-
option.value === background ? 'text-[--color-accent] font-medium' : 'text-secondary'
|
|
165
|
-
)}
|
|
166
|
-
>
|
|
167
|
-
<span className="text-sm">{option.icon}</span>
|
|
168
|
-
{option.label}
|
|
169
|
-
</button>
|
|
121
|
+
<Menu.RadioItem key={option.value} value={option.value}>
|
|
122
|
+
<Stack direction="row" gap="sm" align="center">
|
|
123
|
+
<span style={{ fontSize: '14px' }}>{option.icon}</span>
|
|
124
|
+
{option.label}
|
|
125
|
+
</Stack>
|
|
126
|
+
</Menu.RadioItem>
|
|
170
127
|
))}
|
|
171
|
-
</
|
|
172
|
-
|
|
173
|
-
</
|
|
174
|
-
</
|
|
128
|
+
</Menu.RadioGroup>
|
|
129
|
+
</Menu.Content>
|
|
130
|
+
</Menu>
|
|
131
|
+
</Stack>
|
|
175
132
|
);
|
|
176
133
|
}
|