@fragments-sdk/cli 0.6.0 → 0.7.1
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 +529 -285
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-F7ITZPDJ.js → chunk-32VIEOQY.js} +18 -18
- package/dist/chunk-32VIEOQY.js.map +1 -0
- package/dist/{chunk-SSLQXHNX.js → chunk-5ITIP3ES.js} +27 -27
- package/dist/chunk-5ITIP3ES.js.map +1 -0
- package/dist/{chunk-RVRTRESS.js → chunk-DQHWLAUV.js} +29 -29
- package/dist/chunk-DQHWLAUV.js.map +1 -0
- package/dist/{chunk-Q7GOHVOK.js → chunk-GCZMFLDI.js} +67 -32
- package/dist/chunk-GCZMFLDI.js.map +1 -0
- package/dist/{chunk-6JBGU74P.js → chunk-GHYYFAQN.js} +23 -23
- package/dist/chunk-GHYYFAQN.js.map +1 -0
- package/dist/{chunk-NWQ4CJOQ.js → chunk-GKX2HPZ6.js} +40 -40
- package/dist/chunk-GKX2HPZ6.js.map +1 -0
- package/dist/{chunk-D35RGPAG.js → chunk-U6VTHBNI.js} +499 -83
- package/dist/chunk-U6VTHBNI.js.map +1 -0
- package/dist/{core-SKRPJQZG.js → core-SFHPYR5H.js} +24 -26
- package/dist/{generate-7AF7WRVK.js → generate-54GJAWUY.js} +5 -5
- package/dist/generate-54GJAWUY.js.map +1 -0
- package/dist/index.d.ts +23 -27
- package/dist/index.js +10 -10
- package/dist/{init-WKGDPYI4.js → init-EIM5WNMP.js} +5 -5
- package/dist/{init-WKGDPYI4.js.map → init-EIM5WNMP.js.map} +1 -1
- package/dist/mcp-bin.js +73 -73
- package/dist/mcp-bin.js.map +1 -1
- package/dist/scan-KQBKUS64.js +12 -0
- package/dist/{service-F3E4JJM7.js → service-ED2LNCTU.js} +6 -6
- package/dist/{static-viewer-4LQZ5AGA.js → static-viewer-Q4F4QP5M.js} +4 -4
- package/dist/{test-CJDNJTPZ.js → test-6VN2DA3S.js} +19 -19
- package/dist/test-6VN2DA3S.js.map +1 -0
- package/dist/{tokens-JAJABYXP.js → tokens-P2B7ZAM3.js} +5 -5
- package/dist/{viewer-R3Q6WAMJ.js → viewer-GM7IQPPB.js} +199 -199
- package/dist/viewer-GM7IQPPB.js.map +1 -0
- package/package.json +2 -2
- package/src/ai.ts +5 -5
- package/src/analyze.ts +11 -11
- package/src/bin.ts +24 -1
- package/src/build.ts +64 -21
- package/src/commands/a11y.ts +6 -6
- package/src/commands/add.ts +11 -11
- package/src/commands/audit.ts +4 -4
- package/src/commands/baseline.ts +3 -3
- package/src/commands/build.ts +8 -8
- package/src/commands/compare.ts +20 -20
- package/src/commands/context.ts +16 -16
- package/src/commands/enhance.ts +36 -36
- package/src/commands/generate.ts +1 -1
- package/src/commands/graph.ts +274 -0
- package/src/commands/init.ts +1 -1
- package/src/commands/link/figma.ts +82 -82
- package/src/commands/link/index.ts +3 -3
- package/src/commands/link/storybook.ts +9 -9
- package/src/commands/list.ts +2 -2
- package/src/commands/reset.ts +15 -15
- package/src/commands/scan.ts +27 -27
- package/src/commands/storygen.ts +24 -24
- package/src/commands/validate.ts +2 -2
- package/src/commands/verify.ts +8 -8
- package/src/core/auto-props.ts +4 -4
- package/src/core/composition.test.ts +36 -36
- package/src/core/composition.ts +83 -20
- package/src/core/config.ts +6 -6
- package/src/core/{defineSegment.ts → defineFragment.ts} +16 -22
- package/src/core/discovery.ts +6 -6
- package/src/core/figma.ts +2 -2
- package/src/core/graph-extractor.test.ts +542 -0
- package/src/core/graph-extractor.ts +601 -0
- package/src/core/importAnalyzer.ts +6 -1
- package/src/core/index.ts +22 -23
- package/src/core/loader.ts +22 -22
- package/src/core/node.ts +5 -5
- package/src/core/parser.ts +31 -31
- package/src/core/previewLoader.ts +1 -1
- package/src/core/schema.ts +16 -16
- package/src/core/storyAdapter.test.ts +87 -87
- package/src/core/storyAdapter.ts +16 -16
- package/src/core/types.ts +21 -26
- package/src/diff.ts +22 -22
- package/src/index.ts +2 -2
- package/src/mcp/server.ts +80 -80
- package/src/migrate/__tests__/utils/utils.test.ts +3 -3
- package/src/migrate/bin.ts +4 -4
- package/src/migrate/converter.ts +16 -16
- package/src/migrate/index.ts +3 -3
- package/src/migrate/migrate.ts +3 -3
- package/src/migrate/parser.ts +8 -8
- package/src/migrate/report.ts +2 -2
- package/src/migrate/types.ts +4 -4
- package/src/screenshot.ts +22 -22
- package/src/service/__tests__/props-extractor.test.ts +15 -15
- package/src/service/analytics.ts +39 -39
- package/src/service/enhance/codebase-scanner.ts +1 -1
- package/src/service/enhance/index.ts +1 -1
- package/src/service/enhance/props-extractor.ts +2 -2
- package/src/service/enhance/types.ts +2 -2
- package/src/service/index.ts +2 -2
- package/src/service/metrics-store.ts +1 -1
- package/src/service/patch-generator.ts +1 -1
- package/src/setup.ts +52 -52
- package/src/shared/dev-server-client.ts +7 -7
- package/src/shared/fragment-loader.ts +59 -0
- package/src/shared/index.ts +1 -1
- package/src/shared/types.ts +4 -4
- package/src/static-viewer.ts +35 -35
- package/src/test/discovery.ts +6 -6
- package/src/test/index.ts +5 -5
- package/src/test/reporters/console.ts +1 -1
- package/src/test/reporters/junit.ts +1 -1
- package/src/test/runner.ts +7 -7
- package/src/test/types.ts +3 -3
- package/src/test/watch.ts +9 -9
- package/src/validators.ts +26 -26
- package/src/viewer/__tests__/render-utils.test.ts +28 -28
- package/src/viewer/__tests__/viewer-integration.test.ts +4 -4
- package/src/viewer/cli/health.ts +26 -26
- package/src/viewer/components/App.tsx +201 -103
- package/src/viewer/components/BottomPanel.tsx +17 -17
- package/src/viewer/components/CodePanel.tsx +3 -3
- package/src/viewer/components/CommandPalette.tsx +11 -11
- package/src/viewer/components/ComponentGraph.tsx +28 -28
- package/src/viewer/components/ComponentHeader.tsx +2 -2
- package/src/viewer/components/ContractPanel.tsx +6 -6
- package/src/viewer/components/FigmaEmbed.tsx +9 -9
- package/src/viewer/components/HealthDashboard.tsx +17 -17
- package/src/viewer/components/Icons.tsx +53 -1
- package/src/viewer/components/InteractionsPanel.tsx +2 -2
- package/src/viewer/components/IsolatedPreviewFrame.tsx +6 -6
- package/src/viewer/components/IsolatedRender.tsx +10 -10
- package/src/viewer/components/Layout.tsx +7 -3
- package/src/viewer/components/LeftSidebar.tsx +92 -114
- package/src/viewer/components/MultiViewportPreview.tsx +14 -14
- package/src/viewer/components/PreviewArea.tsx +11 -11
- package/src/viewer/components/PreviewFrameHost.tsx +77 -48
- package/src/viewer/components/PreviewToolbar.tsx +57 -10
- package/src/viewer/components/RightSidebar.tsx +9 -9
- package/src/viewer/components/Sidebar.tsx +17 -17
- package/src/viewer/components/StoryRenderer.tsx +2 -2
- package/src/viewer/components/TokenStylePanel.tsx +1 -1
- package/src/viewer/components/UsageSection.tsx +2 -2
- package/src/viewer/components/VariantMatrix.tsx +11 -11
- package/src/viewer/components/VariantRenderer.tsx +3 -3
- package/src/viewer/components/VariantTabs.tsx +2 -2
- package/src/viewer/components/ViewportSelector.tsx +56 -45
- package/src/viewer/components/_future/CreatePage.tsx +6 -6
- package/src/viewer/composition-renderer.ts +11 -11
- package/src/viewer/constants/ui.ts +4 -4
- package/src/viewer/entry.tsx +40 -40
- package/src/viewer/hooks/useFigmaIntegration.ts +1 -1
- package/src/viewer/hooks/usePreviewBridge.ts +5 -5
- package/src/viewer/hooks/useUrlState.ts +6 -6
- package/src/viewer/index.ts +2 -2
- package/src/viewer/intelligence/healthReport.ts +17 -17
- package/src/viewer/intelligence/styleDrift.ts +1 -1
- package/src/viewer/intelligence/usageScanner.ts +1 -1
- package/src/viewer/preview-frame.html +22 -13
- package/src/viewer/render-template.html +1 -1
- package/src/viewer/render-utils.ts +21 -21
- package/src/viewer/server.ts +18 -18
- package/src/viewer/styles/globals.css +42 -81
- package/src/viewer/utils/detectRelationships.ts +22 -22
- package/src/viewer/vite-plugin.ts +213 -213
- package/dist/chunk-6JBGU74P.js.map +0 -1
- package/dist/chunk-D35RGPAG.js.map +0 -1
- package/dist/chunk-F7ITZPDJ.js.map +0 -1
- package/dist/chunk-NWQ4CJOQ.js.map +0 -1
- package/dist/chunk-Q7GOHVOK.js.map +0 -1
- package/dist/chunk-RVRTRESS.js.map +0 -1
- package/dist/chunk-SSLQXHNX.js.map +0 -1
- package/dist/generate-7AF7WRVK.js.map +0 -1
- package/dist/scan-K6JNMCGM.js +0 -12
- package/dist/test-CJDNJTPZ.js.map +0 -1
- package/dist/viewer-R3Q6WAMJ.js.map +0 -1
- package/src/shared/segment-loader.ts +0 -59
- /package/dist/{core-SKRPJQZG.js.map → core-SFHPYR5H.js.map} +0 -0
- /package/dist/{scan-K6JNMCGM.js.map → scan-KQBKUS64.js.map} +0 -0
- /package/dist/{service-F3E4JJM7.js.map → service-ED2LNCTU.js.map} +0 -0
- /package/dist/{static-viewer-4LQZ5AGA.js.map → static-viewer-Q4F4QP5M.js.map} +0 -0
- /package/dist/{tokens-JAJABYXP.js.map → tokens-P2B7ZAM3.js.map} +0 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* This component runs inside the preview iframe and:
|
|
5
5
|
* 1. Listens for render requests from the parent window
|
|
6
|
-
* 2. Loads and renders the requested
|
|
6
|
+
* 2. Loads and renders the requested fragment variant
|
|
7
7
|
* 3. Applies theme styling
|
|
8
8
|
* 4. Reports render status back to parent
|
|
9
9
|
*/
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
import { useState, useEffect, useRef, type ReactNode } from 'react';
|
|
12
12
|
import { useFrameBridge } from '../hooks/usePreviewBridge.js';
|
|
13
13
|
|
|
14
|
-
// Types for
|
|
15
|
-
interface
|
|
14
|
+
// Types for fragment data
|
|
15
|
+
interface FragmentVariant {
|
|
16
16
|
name: string;
|
|
17
17
|
render: (options?: { loadedData?: Record<string, unknown> }) => ReactNode;
|
|
18
18
|
loaders?: Array<() => Promise<Record<string, unknown>>>;
|
|
@@ -20,66 +20,84 @@ interface SegmentVariant {
|
|
|
20
20
|
hasPlayFunction?: boolean;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
interface
|
|
23
|
+
interface FragmentDefinition {
|
|
24
24
|
meta: {
|
|
25
25
|
name: string;
|
|
26
26
|
description?: string;
|
|
27
|
+
category?: string;
|
|
27
28
|
};
|
|
28
|
-
variants?:
|
|
29
|
+
variants?: FragmentVariant[];
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
interface
|
|
32
|
+
interface FragmentItem {
|
|
32
33
|
path: string;
|
|
33
|
-
|
|
34
|
+
fragment: FragmentDefinition;
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
// Cached
|
|
37
|
-
let
|
|
38
|
-
let
|
|
37
|
+
// Cached fragments
|
|
38
|
+
let cachedFragments: FragmentItem[] | null = null;
|
|
39
|
+
let fragmentsPromise: Promise<FragmentItem[]> | null = null;
|
|
39
40
|
|
|
40
41
|
/**
|
|
41
|
-
* Load
|
|
42
|
+
* Load fragments from the virtual module
|
|
42
43
|
*/
|
|
43
|
-
async function
|
|
44
|
-
if (
|
|
45
|
-
return
|
|
44
|
+
async function loadFragments(): Promise<FragmentItem[]> {
|
|
45
|
+
if (cachedFragments) {
|
|
46
|
+
return cachedFragments;
|
|
46
47
|
}
|
|
47
48
|
|
|
48
|
-
if (
|
|
49
|
-
return
|
|
49
|
+
if (fragmentsPromise) {
|
|
50
|
+
return fragmentsPromise;
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
|
|
53
|
+
fragmentsPromise = (async () => {
|
|
53
54
|
try {
|
|
54
55
|
// @ts-expect-error Virtual module
|
|
55
56
|
const module = await import('virtual:fragments');
|
|
56
|
-
if (module.
|
|
57
|
-
|
|
57
|
+
if (module.fragmentsPromise) {
|
|
58
|
+
cachedFragments = await module.fragmentsPromise;
|
|
58
59
|
} else {
|
|
59
|
-
|
|
60
|
+
cachedFragments = module.fragments || [];
|
|
60
61
|
}
|
|
61
|
-
return
|
|
62
|
+
return cachedFragments!;
|
|
62
63
|
} catch (error) {
|
|
63
|
-
console.error('[PreviewFrameHost] Failed to load
|
|
64
|
+
console.error('[PreviewFrameHost] Failed to load fragments:', error);
|
|
64
65
|
throw error;
|
|
65
66
|
}
|
|
66
67
|
})();
|
|
67
68
|
|
|
68
|
-
return
|
|
69
|
+
return fragmentsPromise;
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
/**
|
|
72
|
-
* Find a
|
|
73
|
+
* Find a fragment by its path
|
|
73
74
|
*/
|
|
74
|
-
function
|
|
75
|
-
return
|
|
75
|
+
function findFragmentByPath(fragments: FragmentItem[], path: string): FragmentItem | undefined {
|
|
76
|
+
return fragments.find(s => s.path === path);
|
|
76
77
|
}
|
|
77
78
|
|
|
78
79
|
/**
|
|
79
|
-
* Find a variant by name within a
|
|
80
|
+
* Find a variant by name within a fragment
|
|
80
81
|
*/
|
|
81
|
-
function findVariant(
|
|
82
|
-
return
|
|
82
|
+
function findVariant(fragment: FragmentDefinition, variantName: string): FragmentVariant | undefined {
|
|
83
|
+
return fragment.variants?.find(v => v.name === variantName);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
type PreviewMode = 'centered' | 'full-bleed';
|
|
87
|
+
|
|
88
|
+
function resolvePreviewMode(fragment: FragmentDefinition): PreviewMode {
|
|
89
|
+
const name = fragment.meta.name.toLowerCase();
|
|
90
|
+
const category = (fragment.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';
|
|
83
101
|
}
|
|
84
102
|
|
|
85
103
|
/**
|
|
@@ -118,11 +136,13 @@ function LoadingIndicator() {
|
|
|
118
136
|
function VariantRenderer({
|
|
119
137
|
variant,
|
|
120
138
|
props,
|
|
139
|
+
mode,
|
|
121
140
|
onRendered,
|
|
122
141
|
onError,
|
|
123
142
|
}: {
|
|
124
|
-
variant:
|
|
143
|
+
variant: FragmentVariant;
|
|
125
144
|
props?: Record<string, unknown>;
|
|
145
|
+
mode: PreviewMode;
|
|
126
146
|
onRendered: (width: number, height: number) => void;
|
|
127
147
|
onError: (message: string, stack?: string) => void;
|
|
128
148
|
}) {
|
|
@@ -208,7 +228,9 @@ function VariantRenderer({
|
|
|
208
228
|
<div
|
|
209
229
|
ref={containerRef}
|
|
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,
|
|
212
234
|
transition: 'opacity 150ms',
|
|
213
235
|
opacity: content ? 1 : 0,
|
|
214
236
|
}}
|
|
@@ -223,10 +245,11 @@ function VariantRenderer({
|
|
|
223
245
|
*/
|
|
224
246
|
export function PreviewFrameHost() {
|
|
225
247
|
const { renderRequest, theme, notifyReady, notifyRendered, notifyError } = useFrameBridge();
|
|
226
|
-
const [
|
|
248
|
+
const [fragments, setFragments] = useState<FragmentItem[] | null>(null);
|
|
227
249
|
const [loadError, setLoadError] = useState<string | null>(null);
|
|
228
|
-
const [currentVariant, setCurrentVariant] = useState<
|
|
250
|
+
const [currentVariant, setCurrentVariant] = useState<FragmentVariant | null>(null);
|
|
229
251
|
const [currentProps, setCurrentProps] = useState<Record<string, unknown> | undefined>(undefined);
|
|
252
|
+
const [previewMode, setPreviewMode] = useState<PreviewMode>('centered');
|
|
230
253
|
|
|
231
254
|
// Apply theme to document
|
|
232
255
|
useEffect(() => {
|
|
@@ -237,15 +260,19 @@ export function PreviewFrameHost() {
|
|
|
237
260
|
}
|
|
238
261
|
}, [theme]);
|
|
239
262
|
|
|
240
|
-
// Load segments on mount
|
|
241
263
|
useEffect(() => {
|
|
242
|
-
|
|
264
|
+
document.body.setAttribute('data-preview-mode', previewMode);
|
|
265
|
+
}, [previewMode]);
|
|
266
|
+
|
|
267
|
+
// Load fragments on mount
|
|
268
|
+
useEffect(() => {
|
|
269
|
+
loadFragments()
|
|
243
270
|
.then(segs => {
|
|
244
|
-
|
|
271
|
+
setFragments(segs);
|
|
245
272
|
notifyReady();
|
|
246
273
|
})
|
|
247
274
|
.catch(err => {
|
|
248
|
-
const message = err instanceof Error ? err.message : 'Failed to load
|
|
275
|
+
const message = err instanceof Error ? err.message : 'Failed to load fragments';
|
|
249
276
|
setLoadError(message);
|
|
250
277
|
notifyError(message);
|
|
251
278
|
});
|
|
@@ -253,32 +280,33 @@ export function PreviewFrameHost() {
|
|
|
253
280
|
|
|
254
281
|
// Handle render requests
|
|
255
282
|
useEffect(() => {
|
|
256
|
-
if (!renderRequest || !
|
|
283
|
+
if (!renderRequest || !fragments) return;
|
|
257
284
|
|
|
258
|
-
const {
|
|
285
|
+
const { fragmentPath, variantName, props } = renderRequest;
|
|
259
286
|
|
|
260
|
-
// Find
|
|
261
|
-
const
|
|
262
|
-
if (!
|
|
263
|
-
notifyError(`
|
|
287
|
+
// Find fragment
|
|
288
|
+
const fragmentItem = findFragmentByPath(fragments, fragmentPath);
|
|
289
|
+
if (!fragmentItem) {
|
|
290
|
+
notifyError(`Fragment not found: ${fragmentPath}`);
|
|
264
291
|
setCurrentVariant(null);
|
|
265
292
|
return;
|
|
266
293
|
}
|
|
267
294
|
|
|
268
295
|
// Find variant
|
|
269
|
-
const variant = findVariant(
|
|
296
|
+
const variant = findVariant(fragmentItem.fragment, variantName);
|
|
270
297
|
if (!variant) {
|
|
271
|
-
notifyError(`Variant not found: ${variantName} in ${
|
|
298
|
+
notifyError(`Variant not found: ${variantName} in ${fragmentPath}`);
|
|
272
299
|
setCurrentVariant(null);
|
|
273
300
|
return;
|
|
274
301
|
}
|
|
275
302
|
|
|
303
|
+
setPreviewMode(resolvePreviewMode(fragmentItem.fragment));
|
|
276
304
|
setCurrentVariant(variant);
|
|
277
305
|
setCurrentProps(props);
|
|
278
|
-
}, [renderRequest,
|
|
306
|
+
}, [renderRequest, fragments, notifyError]);
|
|
279
307
|
|
|
280
308
|
// Show loading state
|
|
281
|
-
if (!
|
|
309
|
+
if (!fragments && !loadError) {
|
|
282
310
|
return <LoadingIndicator />;
|
|
283
311
|
}
|
|
284
312
|
|
|
@@ -299,9 +327,10 @@ export function PreviewFrameHost() {
|
|
|
299
327
|
// Render the variant
|
|
300
328
|
return (
|
|
301
329
|
<VariantRenderer
|
|
302
|
-
key={`${renderRequest?.
|
|
330
|
+
key={`${renderRequest?.fragmentPath}-${renderRequest?.variantName}`}
|
|
303
331
|
variant={currentVariant}
|
|
304
332
|
props={currentProps}
|
|
333
|
+
mode={previewMode}
|
|
305
334
|
onRendered={notifyRendered}
|
|
306
335
|
onError={notifyError}
|
|
307
336
|
/>
|
|
@@ -12,13 +12,62 @@ export type { ZoomLevel, BackgroundOption };
|
|
|
12
12
|
export { getBackgroundStyle } from '../constants/ui.js';
|
|
13
13
|
|
|
14
14
|
// Background options with display metadata
|
|
15
|
-
const BACKGROUND_OPTIONS_UI: { value: BackgroundOption; label: string
|
|
16
|
-
{ value: 'white', label: 'White'
|
|
17
|
-
{ value: 'black', label: 'Black'
|
|
18
|
-
{ value: 'checkerboard', label: 'Checkerboard'
|
|
19
|
-
{ value: 'transparent', label: 'Transparent'
|
|
15
|
+
const BACKGROUND_OPTIONS_UI: { value: BackgroundOption; label: string }[] = [
|
|
16
|
+
{ value: 'white', label: 'White' },
|
|
17
|
+
{ value: 'black', label: 'Black' },
|
|
18
|
+
{ value: 'checkerboard', label: 'Checkerboard' },
|
|
19
|
+
{ value: 'transparent', label: 'Transparent' },
|
|
20
20
|
];
|
|
21
21
|
|
|
22
|
+
function BackgroundSwatch({ background }: { background: BackgroundOption }) {
|
|
23
|
+
const baseStyle = {
|
|
24
|
+
width: '14px',
|
|
25
|
+
height: '14px',
|
|
26
|
+
borderRadius: '4px',
|
|
27
|
+
border: '1px solid var(--border)',
|
|
28
|
+
flexShrink: 0,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
if (background === 'white') {
|
|
32
|
+
return <span aria-hidden="true" style={{ ...baseStyle, backgroundColor: '#ffffff' }} />;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (background === 'black') {
|
|
36
|
+
return <span aria-hidden="true" style={{ ...baseStyle, backgroundColor: '#171717', borderColor: '#2a2a2a' }} />;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (background === 'checkerboard') {
|
|
40
|
+
return (
|
|
41
|
+
<span
|
|
42
|
+
aria-hidden="true"
|
|
43
|
+
style={{
|
|
44
|
+
...baseStyle,
|
|
45
|
+
backgroundColor: '#ffffff',
|
|
46
|
+
backgroundImage: `
|
|
47
|
+
linear-gradient(45deg, #d4d4d8 25%, transparent 25%),
|
|
48
|
+
linear-gradient(-45deg, #d4d4d8 25%, transparent 25%),
|
|
49
|
+
linear-gradient(45deg, transparent 75%, #d4d4d8 75%),
|
|
50
|
+
linear-gradient(-45deg, transparent 75%, #d4d4d8 75%)
|
|
51
|
+
`,
|
|
52
|
+
backgroundSize: '8px 8px',
|
|
53
|
+
backgroundPosition: '0 0, 0 4px, 4px -4px, -4px 0',
|
|
54
|
+
}}
|
|
55
|
+
/>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<span
|
|
61
|
+
aria-hidden="true"
|
|
62
|
+
style={{
|
|
63
|
+
...baseStyle,
|
|
64
|
+
backgroundColor: 'transparent',
|
|
65
|
+
backgroundImage: 'linear-gradient(135deg, transparent 42%, var(--text-tertiary) 43%, var(--text-tertiary) 57%, transparent 58%)',
|
|
66
|
+
}}
|
|
67
|
+
/>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
22
71
|
interface PreviewToolbarProps {
|
|
23
72
|
zoom: ZoomLevel;
|
|
24
73
|
background: BackgroundOption;
|
|
@@ -102,10 +151,8 @@ export function PreviewToolbar({
|
|
|
102
151
|
<Menu.Trigger asChild>
|
|
103
152
|
<Button variant="ghost" size="sm" title="Background color">
|
|
104
153
|
<Stack direction="row" gap="xs" align="center">
|
|
105
|
-
<
|
|
106
|
-
|
|
107
|
-
</span>
|
|
108
|
-
<span style={{ textTransform: 'capitalize' }}>{background}</span>
|
|
154
|
+
<BackgroundSwatch background={background} />
|
|
155
|
+
<span>{BACKGROUND_OPTIONS_UI.find(o => o.value === background)?.label}</span>
|
|
109
156
|
<span style={{ display: 'inline-flex', width: '12px', height: '12px' }}>
|
|
110
157
|
<ChevronDownIcon />
|
|
111
158
|
</span>
|
|
@@ -120,7 +167,7 @@ export function PreviewToolbar({
|
|
|
120
167
|
{BACKGROUND_OPTIONS_UI.map((option) => (
|
|
121
168
|
<Menu.RadioItem key={option.value} value={option.value}>
|
|
122
169
|
<Stack direction="row" gap="sm" align="center">
|
|
123
|
-
<
|
|
170
|
+
<BackgroundSwatch background={option.value} />
|
|
124
171
|
{option.label}
|
|
125
172
|
</Stack>
|
|
126
173
|
</Menu.RadioItem>
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { FragmentDefinition } from '../../core/index.js';
|
|
2
2
|
import { useScrollSpy } from '../hooks/useScrollSpy.js';
|
|
3
3
|
import { Sidebar } from '@fragments/ui';
|
|
4
4
|
|
|
5
5
|
interface RightSidebarProps {
|
|
6
|
-
|
|
6
|
+
fragment: FragmentDefinition;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
interface TocItem {
|
|
@@ -12,19 +12,19 @@ interface TocItem {
|
|
|
12
12
|
children?: TocItem[];
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
export function RightSidebar({
|
|
16
|
-
// Build table of contents from
|
|
15
|
+
export function RightSidebar({ fragment }: RightSidebarProps) {
|
|
16
|
+
// Build table of contents from fragment
|
|
17
17
|
const tocItems: TocItem[] = [];
|
|
18
18
|
|
|
19
19
|
// Overview section
|
|
20
20
|
tocItems.push({ id: 'overview', label: 'Overview' });
|
|
21
21
|
|
|
22
22
|
// Variants section with nested items
|
|
23
|
-
if (
|
|
23
|
+
if (fragment.variants && fragment.variants.length > 0) {
|
|
24
24
|
tocItems.push({
|
|
25
25
|
id: 'variants',
|
|
26
26
|
label: 'Variants',
|
|
27
|
-
children:
|
|
27
|
+
children: fragment.variants.map((variant, index) => ({
|
|
28
28
|
id: `variant-${index}`,
|
|
29
29
|
label: variant.name,
|
|
30
30
|
})),
|
|
@@ -32,17 +32,17 @@ export function RightSidebar({ segment }: RightSidebarProps) {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
// Usage section
|
|
35
|
-
if (
|
|
35
|
+
if (fragment.usage) {
|
|
36
36
|
tocItems.push({ id: 'usage', label: 'Usage' });
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
// Props section
|
|
40
|
-
if (
|
|
40
|
+
if (fragment.props && Object.keys(fragment.props).length > 0) {
|
|
41
41
|
tocItems.push({ id: 'props', label: 'Props' });
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
// Relations section
|
|
45
|
-
if (
|
|
45
|
+
if (fragment.relations && fragment.relations.length > 0) {
|
|
46
46
|
tocItems.push({ id: 'relations', label: 'Relations' });
|
|
47
47
|
}
|
|
48
48
|
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import React, { useState, useMemo } from 'react';
|
|
2
|
-
import type {
|
|
2
|
+
import type { FragmentDefinition } from '../../core/index.js';
|
|
3
3
|
|
|
4
4
|
interface SidebarProps {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
fragments: Array<{ path: string; fragment: FragmentDefinition }>;
|
|
6
|
+
activeFragment: string | null;
|
|
7
7
|
onSelect: (path: string) => void;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
export function Sidebar({
|
|
10
|
+
export function Sidebar({ fragments, activeFragment, onSelect }: SidebarProps): React.ReactElement {
|
|
11
11
|
const [search, setSearch] = useState('');
|
|
12
12
|
|
|
13
|
-
// Group
|
|
13
|
+
// Group fragments by category
|
|
14
14
|
const grouped = useMemo(() => {
|
|
15
|
-
const groups: Record<string, typeof
|
|
15
|
+
const groups: Record<string, typeof fragments> = {};
|
|
16
16
|
|
|
17
|
-
for (const item of
|
|
18
|
-
const category = item.
|
|
17
|
+
for (const item of fragments) {
|
|
18
|
+
const category = item.fragment.meta.category || 'uncategorized';
|
|
19
19
|
if (!groups[category]) {
|
|
20
20
|
groups[category] = [];
|
|
21
21
|
}
|
|
@@ -24,12 +24,12 @@ export function Sidebar({ segments, activeSegment, onSelect }: SidebarProps): Re
|
|
|
24
24
|
|
|
25
25
|
// Filter by search
|
|
26
26
|
if (search) {
|
|
27
|
-
const filtered: Record<string, typeof
|
|
27
|
+
const filtered: Record<string, typeof fragments> = {};
|
|
28
28
|
for (const [category, items] of Object.entries(groups)) {
|
|
29
29
|
const matchingItems = items.filter(
|
|
30
30
|
(item) =>
|
|
31
|
-
item.
|
|
32
|
-
item.
|
|
31
|
+
item.fragment.meta.name.toLowerCase().includes(search.toLowerCase()) ||
|
|
32
|
+
item.fragment.meta.description.toLowerCase().includes(search.toLowerCase())
|
|
33
33
|
);
|
|
34
34
|
if (matchingItems.length > 0) {
|
|
35
35
|
filtered[category] = matchingItems;
|
|
@@ -39,7 +39,7 @@ export function Sidebar({ segments, activeSegment, onSelect }: SidebarProps): Re
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
return groups;
|
|
42
|
-
}, [
|
|
42
|
+
}, [fragments, search]);
|
|
43
43
|
|
|
44
44
|
return (
|
|
45
45
|
<div
|
|
@@ -68,7 +68,7 @@ export function Sidebar({ segments, activeSegment, onSelect }: SidebarProps): Re
|
|
|
68
68
|
color: '#111827',
|
|
69
69
|
}}
|
|
70
70
|
>
|
|
71
|
-
|
|
71
|
+
Fragments
|
|
72
72
|
</h1>
|
|
73
73
|
<p
|
|
74
74
|
style={{
|
|
@@ -77,7 +77,7 @@ export function Sidebar({ segments, activeSegment, onSelect }: SidebarProps): Re
|
|
|
77
77
|
color: '#6b7280',
|
|
78
78
|
}}
|
|
79
79
|
>
|
|
80
|
-
{
|
|
80
|
+
{fragments.length} component{fragments.length !== 1 ? 's' : ''}
|
|
81
81
|
</p>
|
|
82
82
|
</div>
|
|
83
83
|
|
|
@@ -132,7 +132,7 @@ export function Sidebar({ segments, activeSegment, onSelect }: SidebarProps): Re
|
|
|
132
132
|
padding: '8px 12px',
|
|
133
133
|
border: 'none',
|
|
134
134
|
borderRadius: '6px',
|
|
135
|
-
background:
|
|
135
|
+
background: activeFragment === item.path ? '#e5e7eb' : 'transparent',
|
|
136
136
|
textAlign: 'left',
|
|
137
137
|
cursor: 'pointer',
|
|
138
138
|
transition: 'background 0.15s',
|
|
@@ -145,7 +145,7 @@ export function Sidebar({ segments, activeSegment, onSelect }: SidebarProps): Re
|
|
|
145
145
|
color: '#111827',
|
|
146
146
|
}}
|
|
147
147
|
>
|
|
148
|
-
{item.
|
|
148
|
+
{item.fragment.meta.name}
|
|
149
149
|
</div>
|
|
150
150
|
<div
|
|
151
151
|
style={{
|
|
@@ -157,7 +157,7 @@ export function Sidebar({ segments, activeSegment, onSelect }: SidebarProps): Re
|
|
|
157
157
|
whiteSpace: 'nowrap',
|
|
158
158
|
}}
|
|
159
159
|
>
|
|
160
|
-
{item.
|
|
160
|
+
{item.fragment.meta.description}
|
|
161
161
|
</div>
|
|
162
162
|
</button>
|
|
163
163
|
))}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { useState, useEffect, useMemo, type ReactNode } from "react";
|
|
2
|
-
import type {
|
|
2
|
+
import type { FragmentVariant } from "../../core/index.js";
|
|
3
3
|
|
|
4
4
|
interface StoryRendererProps {
|
|
5
5
|
/** The variant to render */
|
|
6
|
-
variant:
|
|
6
|
+
variant: FragmentVariant;
|
|
7
7
|
/** Children render function - receives rendered content */
|
|
8
8
|
children: (content: ReactNode | null, isLoading: boolean, error: Error | null) => ReactNode;
|
|
9
9
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { FragmentUsage } from '../../core/index.js';
|
|
2
2
|
import { CheckIcon, XIcon, AccessibilityIcon } from './Icons.js';
|
|
3
3
|
|
|
4
4
|
interface UsageSectionProps {
|
|
5
|
-
usage:
|
|
5
|
+
usage: FragmentUsage;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export function UsageSection({ usage }: UsageSectionProps) {
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import { useState, useMemo, useRef, useCallback } from "react";
|
|
14
14
|
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
15
|
-
import type {
|
|
15
|
+
import type { FragmentVariant } from "../../core/index.js";
|
|
16
16
|
import { ErrorBoundary } from "./ErrorBoundary.js";
|
|
17
17
|
import { StoryRenderer, LoaderIndicator } from "./StoryRenderer.js";
|
|
18
18
|
import { IsolatedPreviewFrame } from "./IsolatedPreviewFrame.js";
|
|
@@ -21,11 +21,11 @@ import { getBackgroundStyle, type BackgroundOption } from "./PreviewToolbar.js";
|
|
|
21
21
|
|
|
22
22
|
interface VariantMatrixProps {
|
|
23
23
|
/** All variants to display */
|
|
24
|
-
variants:
|
|
24
|
+
variants: FragmentVariant[];
|
|
25
25
|
/** Component name for error display */
|
|
26
26
|
componentName: string;
|
|
27
|
-
/**
|
|
28
|
-
|
|
27
|
+
/** Fragment path for iframe rendering */
|
|
28
|
+
fragmentPath: string;
|
|
29
29
|
/** Current zoom level */
|
|
30
30
|
zoom: number;
|
|
31
31
|
/** Preview theme */
|
|
@@ -60,7 +60,7 @@ const VIRTUALIZATION_THRESHOLD = 12;
|
|
|
60
60
|
export function VariantMatrix({
|
|
61
61
|
variants,
|
|
62
62
|
componentName,
|
|
63
|
-
|
|
63
|
+
fragmentPath,
|
|
64
64
|
zoom,
|
|
65
65
|
previewTheme,
|
|
66
66
|
background,
|
|
@@ -204,7 +204,7 @@ export function VariantMatrix({
|
|
|
204
204
|
variant={variant}
|
|
205
205
|
index={index}
|
|
206
206
|
componentName={componentName}
|
|
207
|
-
|
|
207
|
+
fragmentPath={fragmentPath}
|
|
208
208
|
scale={effectiveScale}
|
|
209
209
|
minHeight={gridConfig.minHeight}
|
|
210
210
|
previewTheme={previewTheme}
|
|
@@ -236,7 +236,7 @@ export function VariantMatrix({
|
|
|
236
236
|
variant={variant}
|
|
237
237
|
index={index}
|
|
238
238
|
componentName={componentName}
|
|
239
|
-
|
|
239
|
+
fragmentPath={fragmentPath}
|
|
240
240
|
scale={effectiveScale}
|
|
241
241
|
minHeight={gridConfig.minHeight}
|
|
242
242
|
previewTheme={previewTheme}
|
|
@@ -256,10 +256,10 @@ export function VariantMatrix({
|
|
|
256
256
|
}
|
|
257
257
|
|
|
258
258
|
interface VariantCardProps {
|
|
259
|
-
variant:
|
|
259
|
+
variant: FragmentVariant;
|
|
260
260
|
index: number;
|
|
261
261
|
componentName: string;
|
|
262
|
-
|
|
262
|
+
fragmentPath: string;
|
|
263
263
|
scale: number;
|
|
264
264
|
minHeight: string;
|
|
265
265
|
previewTheme: "light" | "dark";
|
|
@@ -275,7 +275,7 @@ function VariantCard({
|
|
|
275
275
|
variant,
|
|
276
276
|
index,
|
|
277
277
|
componentName,
|
|
278
|
-
|
|
278
|
+
fragmentPath,
|
|
279
279
|
scale,
|
|
280
280
|
minHeight,
|
|
281
281
|
previewTheme,
|
|
@@ -378,7 +378,7 @@ function VariantCard({
|
|
|
378
378
|
>
|
|
379
379
|
{useIframeIsolation ? (
|
|
380
380
|
<IsolatedPreviewFrame
|
|
381
|
-
|
|
381
|
+
fragmentPath={fragmentPath}
|
|
382
382
|
variantName={variant.name}
|
|
383
383
|
theme={previewTheme}
|
|
384
384
|
width="100%"
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
2
|
import React, { type ReactNode, Suspense } from 'react';
|
|
3
|
-
import type {
|
|
3
|
+
import type { FragmentVariant } from '../../core/index.js';
|
|
4
4
|
import { ErrorBoundary } from './ErrorBoundary.js';
|
|
5
5
|
|
|
6
6
|
interface VariantRendererProps {
|
|
7
7
|
/** The variant to render */
|
|
8
|
-
variant:
|
|
8
|
+
variant: FragmentVariant;
|
|
9
9
|
|
|
10
10
|
/** Optional loading fallback for async components */
|
|
11
11
|
loadingFallback?: ReactNode;
|
|
@@ -54,7 +54,7 @@ export function VariantRenderer({
|
|
|
54
54
|
|
|
55
55
|
interface VariantGridProps {
|
|
56
56
|
/** Variants to render */
|
|
57
|
-
variants:
|
|
57
|
+
variants: FragmentVariant[];
|
|
58
58
|
|
|
59
59
|
/** Number of columns in the grid */
|
|
60
60
|
columns?: number;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Tabs } from '@fragments/ui';
|
|
2
|
-
import type {
|
|
2
|
+
import type { FragmentVariant } from '../../core/index.js';
|
|
3
3
|
import { PlayIcon } from './Icons.js';
|
|
4
4
|
|
|
5
5
|
interface VariantTabsProps {
|
|
6
|
-
variants:
|
|
6
|
+
variants: FragmentVariant[];
|
|
7
7
|
activeIndex: number;
|
|
8
8
|
onSelect: (index: number) => void;
|
|
9
9
|
}
|