@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.
- package/dist/bin.js +996 -79
- 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-7OPWMLOE.js +1625 -0
- package/dist/chunk-7OPWMLOE.js.map +1 -0
- package/dist/{chunk-2H2JAA3U.js → chunk-CVXKXVOY.js} +3 -3
- package/dist/{chunk-2H2JAA3U.js.map → chunk-CVXKXVOY.js.map} +1 -1
- package/dist/{chunk-IOJE35DZ.js → chunk-NWQ4CJOQ.js} +3 -3
- package/dist/{chunk-2DJH4F4P.js → chunk-RVRTRESS.js} +3 -3
- package/dist/{chunk-V7YLRR4C.js → chunk-TJ34N7C7.js} +41 -4
- package/dist/{chunk-V7YLRR4C.js.map → chunk-TJ34N7C7.js.map} +1 -1
- package/dist/{chunk-XNWDI6UT.js → chunk-XHUDJNN3.js} +5 -5
- package/dist/{core-DKHB7FYV.js → core-W2HYIQW6.js} +4 -4
- package/dist/{generate-KL24VZVD.js → generate-LMTISDIJ.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-7CHRKQ7P.js} +5 -5
- package/dist/mcp-bin.js +8 -220
- package/dist/mcp-bin.js.map +1 -1
- package/dist/scan-WY23TJCP.js +12 -0
- package/dist/{service-RWUMZ3EW.js → service-T2L7VLTE.js} +5 -5
- package/dist/static-viewer-GBR7YNF3.js +12 -0
- package/dist/{test-ECPEXFDN.js → test-OJRXNDO2.js} +4 -4
- package/dist/{tokens-ITADYVPF.js → tokens-3BWDESVM.js} +6 -6
- package/dist/viewer-SUFOISZM.js +1822 -0
- package/dist/viewer-SUFOISZM.js.map +1 -0
- package/package.json +6 -5
- package/src/bin.ts +31 -0
- package/src/build.ts +147 -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/commands/graph.ts +274 -0
- package/src/core/auto-props.ts +464 -0
- package/src/core/composition.ts +64 -1
- package/src/core/graph-extractor.test.ts +542 -0
- package/src/core/graph-extractor.ts +601 -0
- package/src/core/importAnalyzer.ts +5 -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 +276 -183
- 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 +151 -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 +12 -10
- package/src/viewer/components/LeftSidebar.tsx +103 -155
- package/src/viewer/components/MultiViewportPreview.tsx +254 -63
- package/src/viewer/components/PreviewArea.tsx +113 -44
- package/src/viewer/components/PreviewFrameHost.tsx +36 -6
- package/src/viewer/components/PreviewPane.tsx +2 -3
- package/src/viewer/components/PreviewToolbar.tsx +109 -105
- 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 +121 -114
- package/src/viewer/constants/ui.ts +23 -22
- package/src/viewer/entry.tsx +8 -3
- package/src/viewer/index.ts +3 -6
- package/src/viewer/preview-frame.html +43 -18
- package/src/viewer/server.ts +7 -16
- package/src/viewer/styles/globals.css +46 -85
- package/src/viewer/utils/a11y-fixes.ts +53 -30
- package/dist/chunk-ICAIQ57V.js.map +0 -1
- package/dist/chunk-U4GQ2JTD.js +0 -832
- 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-IOJE35DZ.js.map → chunk-NWQ4CJOQ.js.map} +0 -0
- /package/dist/{chunk-2DJH4F4P.js.map → chunk-RVRTRESS.js.map} +0 -0
- /package/dist/{chunk-XNWDI6UT.js.map → chunk-XHUDJNN3.js.map} +0 -0
- /package/dist/{core-DKHB7FYV.js.map → core-W2HYIQW6.js.map} +0 -0
- /package/dist/{generate-KL24VZVD.js.map → generate-LMTISDIJ.js.map} +0 -0
- /package/dist/{init-NION5S3M.js.map → init-7CHRKQ7P.js.map} +0 -0
- /package/dist/{scan-ESEXV7LF.js.map → scan-WY23TJCP.js.map} +0 -0
- /package/dist/{service-RWUMZ3EW.js.map → service-T2L7VLTE.js.map} +0 -0
- /package/dist/{static-viewer-O37MJ5B6.js.map → static-viewer-GBR7YNF3.js.map} +0 -0
- /package/dist/{test-ECPEXFDN.js.map → test-OJRXNDO2.js.map} +0 -0
- /package/dist/{tokens-ITADYVPF.js.map → tokens-3BWDESVM.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%',
|
|
@@ -24,6 +24,7 @@ interface SegmentDefinition {
|
|
|
24
24
|
meta: {
|
|
25
25
|
name: string;
|
|
26
26
|
description?: string;
|
|
27
|
+
category?: string;
|
|
27
28
|
};
|
|
28
29
|
variants?: SegmentVariant[];
|
|
29
30
|
}
|
|
@@ -82,12 +83,29 @@ function findVariant(segment: SegmentDefinition, variantName: string): SegmentVa
|
|
|
82
83
|
return segment.variants?.find(v => v.name === variantName);
|
|
83
84
|
}
|
|
84
85
|
|
|
86
|
+
type PreviewMode = 'centered' | 'full-bleed';
|
|
87
|
+
|
|
88
|
+
function resolvePreviewMode(segment: SegmentDefinition): PreviewMode {
|
|
89
|
+
const name = segment.meta.name.toLowerCase();
|
|
90
|
+
const category = (segment.meta.category || '').toLowerCase();
|
|
91
|
+
|
|
92
|
+
if (category === 'layout' || category === 'navigation') {
|
|
93
|
+
return 'full-bleed';
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (name.includes('appshell') || name.includes('sidebar') || name.includes('header') || name.includes('layout')) {
|
|
97
|
+
return 'full-bleed';
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return 'centered';
|
|
101
|
+
}
|
|
102
|
+
|
|
85
103
|
/**
|
|
86
104
|
* Error boundary for catching render errors
|
|
87
105
|
*/
|
|
88
106
|
function ErrorDisplay({ message, stack }: { message: string; stack?: string }) {
|
|
89
107
|
return (
|
|
90
|
-
<div
|
|
108
|
+
<div style={{ padding: '16px', color: '#dc2626', background: 'rgba(254, 242, 242, 0.95)', borderRadius: '8px', margin: '16px' }}>
|
|
91
109
|
<div style={{ fontWeight: 500, marginBottom: 8 }}>Render Error</div>
|
|
92
110
|
<div>{message}</div>
|
|
93
111
|
{stack && (
|
|
@@ -104,8 +122,9 @@ function ErrorDisplay({ message, stack }: { message: string; stack?: string }) {
|
|
|
104
122
|
*/
|
|
105
123
|
function LoadingIndicator() {
|
|
106
124
|
return (
|
|
107
|
-
<div
|
|
108
|
-
<div
|
|
125
|
+
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '32px', gap: '8px', color: '#6b7280' }}>
|
|
126
|
+
<div style={{ width: '16px', height: '16px', border: '2px solid #e5e7eb', borderTopColor: '#3b82f6', borderRadius: '50%', animation: 'spin 0.8s linear infinite' }} />
|
|
127
|
+
<style>{`@keyframes spin { to { transform: rotate(360deg); } }`}</style>
|
|
109
128
|
<span>Loading component...</span>
|
|
110
129
|
</div>
|
|
111
130
|
);
|
|
@@ -117,11 +136,13 @@ function LoadingIndicator() {
|
|
|
117
136
|
function VariantRenderer({
|
|
118
137
|
variant,
|
|
119
138
|
props,
|
|
139
|
+
mode,
|
|
120
140
|
onRendered,
|
|
121
141
|
onError,
|
|
122
142
|
}: {
|
|
123
143
|
variant: SegmentVariant;
|
|
124
144
|
props?: Record<string, unknown>;
|
|
145
|
+
mode: PreviewMode;
|
|
125
146
|
onRendered: (width: number, height: number) => void;
|
|
126
147
|
onError: (message: string, stack?: string) => void;
|
|
127
148
|
}) {
|
|
@@ -206,9 +227,11 @@ function VariantRenderer({
|
|
|
206
227
|
return (
|
|
207
228
|
<div
|
|
208
229
|
ref={containerRef}
|
|
209
|
-
className="transition-opacity duration-150"
|
|
210
230
|
style={{
|
|
211
|
-
display: 'inline-block',
|
|
231
|
+
display: mode === 'full-bleed' ? 'block' : 'inline-block',
|
|
232
|
+
width: mode === 'full-bleed' ? '100%' : undefined,
|
|
233
|
+
minHeight: mode === 'full-bleed' ? '100vh' : undefined,
|
|
234
|
+
transition: 'opacity 150ms',
|
|
212
235
|
opacity: content ? 1 : 0,
|
|
213
236
|
}}
|
|
214
237
|
>
|
|
@@ -226,6 +249,7 @@ export function PreviewFrameHost() {
|
|
|
226
249
|
const [loadError, setLoadError] = useState<string | null>(null);
|
|
227
250
|
const [currentVariant, setCurrentVariant] = useState<SegmentVariant | null>(null);
|
|
228
251
|
const [currentProps, setCurrentProps] = useState<Record<string, unknown> | undefined>(undefined);
|
|
252
|
+
const [previewMode, setPreviewMode] = useState<PreviewMode>('centered');
|
|
229
253
|
|
|
230
254
|
// Apply theme to document
|
|
231
255
|
useEffect(() => {
|
|
@@ -236,6 +260,10 @@ export function PreviewFrameHost() {
|
|
|
236
260
|
}
|
|
237
261
|
}, [theme]);
|
|
238
262
|
|
|
263
|
+
useEffect(() => {
|
|
264
|
+
document.body.setAttribute('data-preview-mode', previewMode);
|
|
265
|
+
}, [previewMode]);
|
|
266
|
+
|
|
239
267
|
// Load segments on mount
|
|
240
268
|
useEffect(() => {
|
|
241
269
|
loadSegments()
|
|
@@ -272,6 +300,7 @@ export function PreviewFrameHost() {
|
|
|
272
300
|
return;
|
|
273
301
|
}
|
|
274
302
|
|
|
303
|
+
setPreviewMode(resolvePreviewMode(segmentItem.segment));
|
|
275
304
|
setCurrentVariant(variant);
|
|
276
305
|
setCurrentProps(props);
|
|
277
306
|
}, [renderRequest, segments, notifyError]);
|
|
@@ -289,7 +318,7 @@ export function PreviewFrameHost() {
|
|
|
289
318
|
// Show waiting state
|
|
290
319
|
if (!currentVariant) {
|
|
291
320
|
return (
|
|
292
|
-
<div
|
|
321
|
+
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '32px', gap: '8px', color: '#6b7280' }}>
|
|
293
322
|
<span>Waiting for render request...</span>
|
|
294
323
|
</div>
|
|
295
324
|
);
|
|
@@ -301,6 +330,7 @@ export function PreviewFrameHost() {
|
|
|
301
330
|
key={`${renderRequest?.segmentPath}-${renderRequest?.variantName}`}
|
|
302
331
|
variant={currentVariant}
|
|
303
332
|
props={currentProps}
|
|
333
|
+
mode={previewMode}
|
|
304
334
|
onRendered={notifyRendered}
|
|
305
335
|
onError={notifyError}
|
|
306
336
|
/>
|
|
@@ -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
|
);
|