@fragments-sdk/cli 0.9.0 → 0.9.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 +83 -33
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-WI6SLMSO.js → chunk-5GT62FCB.js} +2 -2
- package/dist/{chunk-CJEGT3WD.js → chunk-BW3ZATBW.js} +20 -3
- package/dist/chunk-BW3ZATBW.js.map +1 -0
- package/dist/{chunk-2JIKCJX3.js → chunk-D7372LQX.js} +13 -6
- package/dist/chunk-D7372LQX.js.map +1 -0
- package/dist/chunk-EZYXYWNF.js +131 -0
- package/dist/chunk-EZYXYWNF.js.map +1 -0
- package/dist/{chunk-NGIMCIK2.js → chunk-GF6OVPIN.js} +2 -2
- package/dist/{chunk-GOVI6COW.js → chunk-NVSPGSKB.js} +12 -4
- package/dist/chunk-NVSPGSKB.js.map +1 -0
- package/dist/core/index.d.ts +105 -3
- package/dist/core/index.js +12 -2
- package/dist/{defineFragment-D0UTve-I.d.ts → defineFragment-CBMS7Bab.d.ts} +21 -1
- package/dist/generate-LQA2R7FN.js +461 -0
- package/dist/generate-LQA2R7FN.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/{init-KSAAS7X3.js → init-2GEGVIUQ.js} +13 -75
- package/dist/init-2GEGVIUQ.js.map +1 -0
- package/dist/mcp-bin.js +4 -3
- package/dist/mcp-bin.js.map +1 -1
- package/dist/{scan-65RH3QMM.js → scan-JGS65S7P.js} +6 -5
- package/dist/{service-A5GIGGGK.js → service-XP2EAJXD.js} +4 -3
- package/dist/{static-viewer-NSODM5VX.js → static-viewer-XCS7UJTO.js} +4 -3
- package/dist/storyFilters-3LUYAFZF.js +15 -0
- package/dist/storyFilters-3LUYAFZF.js.map +1 -0
- package/dist/{test-RPWZAYSJ.js → test-TD6TJNVY.js} +3 -3
- package/dist/{tokens-NIXSZRX7.js → tokens-2EXPCVP3.js} +5 -4
- package/dist/{tokens-NIXSZRX7.js.map → tokens-2EXPCVP3.js.map} +1 -1
- package/dist/{viewer-SBTJDMP7.js → viewer-RFA2KVBG.js} +243 -18
- package/dist/viewer-RFA2KVBG.js.map +1 -0
- package/package.json +1 -1
- package/src/build.ts +12 -2
- package/src/commands/build.ts +16 -2
- package/src/commands/generate.ts +383 -68
- package/src/commands/init.ts +9 -51
- package/src/core/config.ts +15 -2
- package/src/core/generators/typescript-extractor.ts +10 -0
- package/src/core/index.ts +15 -0
- package/src/core/schema.ts +10 -2
- package/src/core/storyFilters.test.ts +350 -0
- package/src/core/storyFilters.ts +253 -0
- package/src/core/types.ts +22 -0
- package/src/migrate/converter.ts +9 -1
- package/src/migrate/parser.ts +2 -0
- package/src/migrate/types.ts +2 -0
- package/src/setup.ts +69 -24
- package/src/viewer/__tests__/viewer-integration.test.ts +1 -1
- package/src/viewer/components/AccessibilityPanel.tsx +305 -312
- package/src/viewer/components/ActionsPanel.tsx +31 -29
- package/src/viewer/components/AllVariantsPreview.tsx +78 -0
- package/src/viewer/components/App.tsx +187 -740
- package/src/viewer/components/BottomPanel.tsx +228 -132
- package/src/viewer/components/CodePanel.tsx +1 -1
- package/src/viewer/components/CommandPalette.tsx +7 -10
- package/src/viewer/components/ComponentDocView.tsx +164 -0
- package/src/viewer/components/ComponentGraph.tsx +111 -142
- package/src/viewer/components/ContractPanel.tsx +6 -6
- package/src/viewer/components/EmptyVariantMessage.tsx +54 -0
- package/src/viewer/components/FigmaEmbed.tsx +20 -18
- package/src/viewer/components/FragmentEditor.tsx +92 -115
- package/src/viewer/components/HeaderSearch.tsx +24 -0
- package/src/viewer/components/HealthDashboard.tsx +16 -2
- package/src/viewer/components/Icons.tsx +9 -0
- package/src/viewer/components/InteractionsPanel.tsx +101 -117
- package/src/viewer/components/IsolatedPreviewFrame.tsx +1 -0
- package/src/viewer/components/LandingPage.tsx +3 -3
- package/src/viewer/components/LeftSidebar.tsx +141 -63
- package/src/viewer/components/LoadErrorMessage.tsx +102 -0
- package/src/viewer/components/MultiViewportPreview.tsx +61 -142
- package/src/viewer/components/NoVariantsMessage.tsx +59 -0
- package/src/viewer/components/PanelShell.tsx +161 -0
- package/src/viewer/components/PerformancePanel.tsx +31 -28
- package/src/viewer/components/PreviewArea.tsx +1 -1
- package/src/viewer/components/PreviewAside.tsx +168 -0
- package/src/viewer/components/PreviewFrameHost.tsx +3 -3
- package/src/viewer/components/PropsEditor.tsx +70 -156
- package/src/viewer/components/ResizablePanel.tsx +103 -263
- package/src/viewer/components/RightSidebar.tsx +3 -9
- package/src/viewer/components/SkeletonLoader.tsx +13 -13
- package/src/viewer/components/TokenStylePanel.tsx +182 -209
- package/src/viewer/components/TopToolbar.tsx +159 -0
- package/src/viewer/components/VariantMatrix.tsx +42 -86
- package/src/viewer/components/VariantTabs.tsx +3 -3
- package/src/viewer/components/ViewerHeader.tsx +69 -0
- package/src/viewer/components/WebMCPDevTools.tsx +17 -23
- package/src/viewer/components/viewer-utils.ts +16 -0
- package/src/viewer/entry.tsx +5 -0
- package/src/viewer/hooks/useAppState.ts +27 -4
- package/src/viewer/hooks/usePreviewBridge.ts +2 -2
- package/src/viewer/preview-frame.html +6 -12
- package/src/viewer/server.ts +169 -2
- package/src/viewer/vendor/shared/src/ComponentDocContent.module.scss +10 -0
- package/src/viewer/vendor/shared/src/ComponentDocContent.module.scss.d.ts +2 -0
- package/src/viewer/vendor/shared/src/ComponentDocContent.tsx +274 -0
- package/src/viewer/vendor/shared/src/DocsHeaderBar.tsx +6 -18
- package/src/viewer/vendor/shared/src/DocsPageShell.tsx +5 -0
- package/src/viewer/vendor/shared/src/DocsSidebarNav.tsx +5 -16
- package/src/viewer/vendor/shared/src/PropsTable.module.scss +68 -0
- package/src/viewer/vendor/shared/src/PropsTable.module.scss.d.ts +2 -0
- package/src/viewer/vendor/shared/src/PropsTable.tsx +76 -0
- package/src/viewer/vendor/shared/src/VariantPreviewCard.module.scss +122 -0
- package/src/viewer/vendor/shared/src/VariantPreviewCard.module.scss.d.ts +2 -0
- package/src/viewer/vendor/shared/src/VariantPreviewCard.tsx +134 -0
- package/src/viewer/vendor/shared/src/index.ts +8 -0
- package/src/viewer/vendor/shared/src/types.ts +12 -0
- package/src/viewer/vite-plugin.ts +109 -4
- package/dist/chunk-2JIKCJX3.js.map +0 -1
- package/dist/chunk-CJEGT3WD.js.map +0 -1
- package/dist/chunk-GOVI6COW.js.map +0 -1
- package/dist/generate-35OIMW4Y.js +0 -252
- package/dist/generate-35OIMW4Y.js.map +0 -1
- package/dist/init-KSAAS7X3.js.map +0 -1
- package/dist/viewer-SBTJDMP7.js.map +0 -1
- /package/dist/{chunk-WI6SLMSO.js.map → chunk-5GT62FCB.js.map} +0 -0
- /package/dist/{chunk-NGIMCIK2.js.map → chunk-GF6OVPIN.js.map} +0 -0
- /package/dist/{scan-65RH3QMM.js.map → scan-JGS65S7P.js.map} +0 -0
- /package/dist/{service-A5GIGGGK.js.map → service-XP2EAJXD.js.map} +0 -0
- /package/dist/{static-viewer-NSODM5VX.js.map → static-viewer-XCS7UJTO.js.map} +0 -0
- /package/dist/{test-RPWZAYSJ.js.map → test-TD6TJNVY.js.map} +0 -0
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import { useState, useMemo, useRef, useCallback } from "react";
|
|
14
14
|
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
15
|
+
import { Box, Stack, Text, Button, Badge, EmptyState } from "@fragments-sdk/ui";
|
|
15
16
|
import type { FragmentVariant } from "../../core/index.js";
|
|
16
17
|
import { ErrorBoundary } from "./ErrorBoundary.js";
|
|
17
18
|
import { FragmentRenderer, LoaderIndicator } from "./FragmentRenderer.js";
|
|
@@ -65,7 +66,6 @@ export function VariantMatrix({
|
|
|
65
66
|
}: VariantMatrixProps) {
|
|
66
67
|
const [gridSize, setGridSize] = useState<GridSize>("medium");
|
|
67
68
|
const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
|
|
68
|
-
const [hoveredButton, setHoveredButton] = useState<GridSize | null>(null);
|
|
69
69
|
const scrollRef = useRef<HTMLDivElement>(null);
|
|
70
70
|
|
|
71
71
|
const gridConfig = GRID_SIZES[gridSize];
|
|
@@ -90,79 +90,43 @@ export function VariantMatrix({
|
|
|
90
90
|
|
|
91
91
|
if (variants.length === 0) {
|
|
92
92
|
return (
|
|
93
|
-
<
|
|
94
|
-
display
|
|
95
|
-
|
|
96
|
-
justifyContent: 'center',
|
|
97
|
-
height: '100%',
|
|
98
|
-
color: 'var(--text-muted)',
|
|
99
|
-
}}>
|
|
100
|
-
No variants to display
|
|
101
|
-
</div>
|
|
93
|
+
<EmptyState style={{ height: '100%' }}>
|
|
94
|
+
<EmptyState.Title>No variants to display</EmptyState.Title>
|
|
95
|
+
</EmptyState>
|
|
102
96
|
);
|
|
103
97
|
}
|
|
104
98
|
|
|
105
99
|
return (
|
|
106
|
-
<
|
|
100
|
+
<Stack style={{ height: '100%' }}>
|
|
107
101
|
{/* Toolbar */}
|
|
108
|
-
<
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
<button
|
|
133
|
-
key={size}
|
|
134
|
-
onClick={() => setGridSize(size)}
|
|
135
|
-
onMouseEnter={() => setHoveredButton(size)}
|
|
136
|
-
onMouseLeave={() => setHoveredButton(null)}
|
|
137
|
-
style={{
|
|
138
|
-
padding: '4px 8px',
|
|
139
|
-
fontSize: 12,
|
|
140
|
-
textTransform: 'capitalize',
|
|
141
|
-
transition: 'background-color 0.15s, color 0.15s',
|
|
142
|
-
border: 'none',
|
|
143
|
-
cursor: 'pointer',
|
|
144
|
-
background: gridSize === size
|
|
145
|
-
? 'var(--bg-hover)'
|
|
146
|
-
: hoveredButton === size
|
|
147
|
-
? 'var(--bg-hover)'
|
|
148
|
-
: 'transparent',
|
|
149
|
-
color: gridSize === size
|
|
150
|
-
? 'var(--text-primary)'
|
|
151
|
-
: hoveredButton === size
|
|
152
|
-
? 'var(--text-secondary)'
|
|
153
|
-
: 'var(--text-tertiary)',
|
|
154
|
-
}}
|
|
155
|
-
>
|
|
156
|
-
{size}
|
|
157
|
-
</button>
|
|
158
|
-
))}
|
|
159
|
-
</div>
|
|
160
|
-
</div>
|
|
161
|
-
</div>
|
|
102
|
+
<Box paddingX="md" paddingY="sm" borderBottom background="secondary" style={{ flexShrink: 0 }}>
|
|
103
|
+
<Stack direction="row" align="center" justify="between">
|
|
104
|
+
<Text size="sm" color="secondary">
|
|
105
|
+
{variants.length} variant{variants.length !== 1 ? "s" : ""}
|
|
106
|
+
{useVirtualization && <Text as="span" size="xs" color="tertiary"> (virtualized)</Text>}
|
|
107
|
+
</Text>
|
|
108
|
+
<Stack direction="row" align="center" gap="sm">
|
|
109
|
+
<Text size="xs" color="tertiary">Grid size:</Text>
|
|
110
|
+
<Stack direction="row" style={{ borderRadius: 6, border: '1px solid var(--border)', overflow: 'hidden' }}>
|
|
111
|
+
{(["small", "medium", "large"] as GridSize[]).map((size) => (
|
|
112
|
+
<Button
|
|
113
|
+
key={size}
|
|
114
|
+
variant={gridSize === size ? "secondary" : "ghost"}
|
|
115
|
+
size="sm"
|
|
116
|
+
onClick={() => setGridSize(size)}
|
|
117
|
+
style={{ textTransform: 'capitalize', borderRadius: 0 }}
|
|
118
|
+
>
|
|
119
|
+
{size}
|
|
120
|
+
</Button>
|
|
121
|
+
))}
|
|
122
|
+
</Stack>
|
|
123
|
+
</Stack>
|
|
124
|
+
</Stack>
|
|
125
|
+
</Box>
|
|
162
126
|
|
|
163
127
|
{/* Grid - Virtualized or Regular */}
|
|
164
128
|
{useVirtualization ? (
|
|
165
|
-
<
|
|
129
|
+
<Box ref={scrollRef} overflow="auto" padding="md" style={{ flex: 1 }}>
|
|
166
130
|
<div
|
|
167
131
|
style={{
|
|
168
132
|
height: `${rowVirtualizer.getTotalSize()}px`,
|
|
@@ -217,9 +181,9 @@ export function VariantMatrix({
|
|
|
217
181
|
);
|
|
218
182
|
})}
|
|
219
183
|
</div>
|
|
220
|
-
</
|
|
184
|
+
</Box>
|
|
221
185
|
) : (
|
|
222
|
-
<
|
|
186
|
+
<Box overflow="auto" padding="md" style={{ flex: 1 }}>
|
|
223
187
|
<div style={{
|
|
224
188
|
display: 'grid',
|
|
225
189
|
gap: 16,
|
|
@@ -243,9 +207,9 @@ export function VariantMatrix({
|
|
|
243
207
|
/>
|
|
244
208
|
))}
|
|
245
209
|
</div>
|
|
246
|
-
</
|
|
210
|
+
</Box>
|
|
247
211
|
)}
|
|
248
|
-
</
|
|
212
|
+
</Stack>
|
|
249
213
|
);
|
|
250
214
|
}
|
|
251
215
|
|
|
@@ -298,7 +262,7 @@ function VariantCard({
|
|
|
298
262
|
onMouseLeave={onLeave}
|
|
299
263
|
onClick={onClick}
|
|
300
264
|
>
|
|
301
|
-
{/* Header */}
|
|
265
|
+
{/* Header overlay - keep inline styles (CSS art) */}
|
|
302
266
|
<div style={{
|
|
303
267
|
position: 'absolute',
|
|
304
268
|
top: 0,
|
|
@@ -384,25 +348,25 @@ function VariantCard({
|
|
|
384
348
|
<ErrorBoundary
|
|
385
349
|
componentName={componentName}
|
|
386
350
|
fallback={
|
|
387
|
-
<
|
|
351
|
+
<Text size="xs" style={{ color: 'var(--color-danger)' }}>
|
|
388
352
|
Error rendering variant
|
|
389
|
-
</
|
|
353
|
+
</Text>
|
|
390
354
|
}
|
|
391
355
|
>
|
|
392
356
|
<FragmentRenderer variant={variant}>
|
|
393
357
|
{(content, isLoading, error) => {
|
|
394
358
|
if (isLoading) {
|
|
395
359
|
return (
|
|
396
|
-
<
|
|
360
|
+
<Stack align="center" justify="center" style={{ padding: 16 }}>
|
|
397
361
|
<LoaderIndicator />
|
|
398
|
-
</
|
|
362
|
+
</Stack>
|
|
399
363
|
);
|
|
400
364
|
}
|
|
401
365
|
if (error) {
|
|
402
366
|
return (
|
|
403
|
-
<
|
|
367
|
+
<Text size="xs" style={{ color: 'var(--color-danger)', padding: 8 }}>
|
|
404
368
|
{error.message}
|
|
405
|
-
</
|
|
369
|
+
</Text>
|
|
406
370
|
);
|
|
407
371
|
}
|
|
408
372
|
return content;
|
|
@@ -416,15 +380,7 @@ function VariantCard({
|
|
|
416
380
|
{/* Tags/badges */}
|
|
417
381
|
{variant.hasPlayFunction && (
|
|
418
382
|
<div style={{ position: 'absolute', bottom: 8, right: 8, zIndex: 10 }}>
|
|
419
|
-
<
|
|
420
|
-
padding: '2px 6px',
|
|
421
|
-
fontSize: 10,
|
|
422
|
-
background: '#9333ea',
|
|
423
|
-
color: '#ffffff',
|
|
424
|
-
borderRadius: 4,
|
|
425
|
-
}}>
|
|
426
|
-
play
|
|
427
|
-
</span>
|
|
383
|
+
<Badge variant="info" size="sm">play</Badge>
|
|
428
384
|
</div>
|
|
429
385
|
)}
|
|
430
386
|
</div>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Tabs } from '@fragments-sdk/ui';
|
|
1
|
+
import { Tabs, Stack } from '@fragments-sdk/ui';
|
|
2
2
|
import type { FragmentVariant } from '../../core/index.js';
|
|
3
3
|
import { PlayIcon } from './Icons.js';
|
|
4
4
|
|
|
@@ -24,14 +24,14 @@ export function VariantTabs({ variants, activeIndex, onSelect }: VariantTabsProp
|
|
|
24
24
|
<Tabs.List variant="pills">
|
|
25
25
|
{variants.map((variant) => (
|
|
26
26
|
<Tabs.Tab key={variant.name} value={variant.name}>
|
|
27
|
-
<
|
|
27
|
+
<Stack direction="row" align="center" gap="xs" as="span">
|
|
28
28
|
{variant.name}
|
|
29
29
|
{variant.hasPlayFunction && (
|
|
30
30
|
<span style={{ display: 'inline-flex', width: '12px', height: '12px', color: 'var(--color-accent)' }}>
|
|
31
31
|
<PlayIcon />
|
|
32
32
|
</span>
|
|
33
33
|
)}
|
|
34
|
-
</
|
|
34
|
+
</Stack>
|
|
35
35
|
</Tabs.Tab>
|
|
36
36
|
))}
|
|
37
37
|
</Tabs.List>
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { RefObject } from "react";
|
|
2
|
+
import { BRAND } from "../../core/index.js";
|
|
3
|
+
import {
|
|
4
|
+
Header,
|
|
5
|
+
Stack,
|
|
6
|
+
Text,
|
|
7
|
+
Button,
|
|
8
|
+
ThemeToggle,
|
|
9
|
+
FragmentsLogo,
|
|
10
|
+
} from "@fragments-sdk/ui";
|
|
11
|
+
import { GitHubIcon } from "./Icons.js";
|
|
12
|
+
import { HeaderSearch } from "./HeaderSearch.js";
|
|
13
|
+
import { useTheme } from "./ThemeProvider.js";
|
|
14
|
+
import { WebMCPStatusIndicator } from "./WebMCPStatusIndicator.js";
|
|
15
|
+
|
|
16
|
+
interface ViewerHeaderProps {
|
|
17
|
+
showHealth: boolean;
|
|
18
|
+
searchQuery: string;
|
|
19
|
+
onSearchChange: (value: string) => void;
|
|
20
|
+
searchInputRef: RefObject<HTMLInputElement>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function ViewerHeader({
|
|
24
|
+
showHealth,
|
|
25
|
+
searchQuery,
|
|
26
|
+
onSearchChange,
|
|
27
|
+
searchInputRef,
|
|
28
|
+
}: ViewerHeaderProps) {
|
|
29
|
+
const { setTheme, resolvedTheme } = useTheme();
|
|
30
|
+
return (
|
|
31
|
+
<Header aria-label="Fragments viewer header">
|
|
32
|
+
<Header.Trigger />
|
|
33
|
+
<Header.Brand>
|
|
34
|
+
<Stack direction="row" gap="sm" align="center">
|
|
35
|
+
<FragmentsLogo size={20} />
|
|
36
|
+
<Text weight="medium" size="sm">
|
|
37
|
+
{BRAND.name}
|
|
38
|
+
</Text>
|
|
39
|
+
<Text size="xs" color="tertiary">
|
|
40
|
+
{showHealth ? "health dashboard" : "preview"}
|
|
41
|
+
</Text>
|
|
42
|
+
</Stack>
|
|
43
|
+
</Header.Brand>
|
|
44
|
+
<HeaderSearch value={searchQuery} onChange={onSearchChange} inputRef={searchInputRef} />
|
|
45
|
+
<Header.Spacer />
|
|
46
|
+
<Header.Actions>
|
|
47
|
+
<WebMCPStatusIndicator />
|
|
48
|
+
<ThemeToggle
|
|
49
|
+
size="sm"
|
|
50
|
+
value={resolvedTheme}
|
|
51
|
+
onValueChange={(value) => setTheme(value)}
|
|
52
|
+
aria-label={`Theme: ${resolvedTheme}`}
|
|
53
|
+
/>
|
|
54
|
+
<Button
|
|
55
|
+
as="a"
|
|
56
|
+
variant="ghost"
|
|
57
|
+
size="sm"
|
|
58
|
+
icon
|
|
59
|
+
href="https://github.com/ConanMcN/fragments"
|
|
60
|
+
target="_blank"
|
|
61
|
+
rel="noopener noreferrer"
|
|
62
|
+
aria-label="View on GitHub"
|
|
63
|
+
>
|
|
64
|
+
<GitHubIcon />
|
|
65
|
+
</Button>
|
|
66
|
+
</Header.Actions>
|
|
67
|
+
</Header>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
@@ -190,17 +190,15 @@ export function WebMCPDevTools() {
|
|
|
190
190
|
([name, prop]) => (
|
|
191
191
|
<Box
|
|
192
192
|
key={name}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
backgroundColor: "var(--bg-secondary)",
|
|
197
|
-
}}
|
|
193
|
+
padding="sm"
|
|
194
|
+
rounded="sm"
|
|
195
|
+
background="secondary"
|
|
198
196
|
>
|
|
199
197
|
<Stack direction="row" gap="xs" align="center">
|
|
200
198
|
<Text
|
|
201
199
|
size="xs"
|
|
202
200
|
weight="medium"
|
|
203
|
-
|
|
201
|
+
font="mono"
|
|
204
202
|
>
|
|
205
203
|
{name}
|
|
206
204
|
</Text>
|
|
@@ -219,7 +217,7 @@ export function WebMCPDevTools() {
|
|
|
219
217
|
</Text>
|
|
220
218
|
)}
|
|
221
219
|
{prop.enum && (
|
|
222
|
-
<Text size="2xs" color="tertiary" style={{ marginTop: "2px"
|
|
220
|
+
<Text size="2xs" color="tertiary" font="mono" style={{ marginTop: "2px" }}>
|
|
223
221
|
enum: {prop.enum.join(" | ")}
|
|
224
222
|
</Text>
|
|
225
223
|
)}
|
|
@@ -292,9 +290,9 @@ export function WebMCPDevTools() {
|
|
|
292
290
|
/>
|
|
293
291
|
) : (
|
|
294
292
|
<Box
|
|
293
|
+
padding="sm"
|
|
294
|
+
rounded="md"
|
|
295
295
|
style={{
|
|
296
|
-
padding: "12px",
|
|
297
|
-
borderRadius: "var(--radius-md)",
|
|
298
296
|
backgroundColor: "var(--bg-danger, #fef2f2)",
|
|
299
297
|
}}
|
|
300
298
|
>
|
|
@@ -425,14 +423,12 @@ export function WebMCPDevTools() {
|
|
|
425
423
|
.map(([name, stat]) => (
|
|
426
424
|
<Box
|
|
427
425
|
key={name}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
backgroundColor: "var(--bg-secondary)",
|
|
432
|
-
}}
|
|
426
|
+
padding="sm"
|
|
427
|
+
rounded="sm"
|
|
428
|
+
background="secondary"
|
|
433
429
|
>
|
|
434
|
-
<Stack direction="row" gap="sm" align="center"
|
|
435
|
-
<Text size="xs" weight="medium"
|
|
430
|
+
<Stack direction="row" gap="sm" align="center" justify="between">
|
|
431
|
+
<Text size="xs" weight="medium" font="mono">
|
|
436
432
|
{name}
|
|
437
433
|
</Text>
|
|
438
434
|
<Stack direction="row" gap="xs">
|
|
@@ -461,14 +457,12 @@ export function WebMCPDevTools() {
|
|
|
461
457
|
{summary.commonFlows.map((flow, i) => (
|
|
462
458
|
<Box
|
|
463
459
|
key={i}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
backgroundColor: "var(--bg-secondary)",
|
|
468
|
-
}}
|
|
460
|
+
padding="sm"
|
|
461
|
+
rounded="sm"
|
|
462
|
+
background="secondary"
|
|
469
463
|
>
|
|
470
464
|
<Stack direction="row" gap="xs" align="center">
|
|
471
|
-
<Text size="2xs"
|
|
465
|
+
<Text size="2xs" font="mono">
|
|
472
466
|
{flow.sequence.join(' → ')}
|
|
473
467
|
</Text>
|
|
474
468
|
<Badge size="sm" variant="info">{flow.count}x</Badge>
|
|
@@ -486,10 +480,10 @@ export function WebMCPDevTools() {
|
|
|
486
480
|
|
|
487
481
|
{/* Protocol Status Footer */}
|
|
488
482
|
<Box
|
|
483
|
+
borderTop
|
|
489
484
|
style={{
|
|
490
485
|
marginTop: "auto",
|
|
491
486
|
paddingTop: "12px",
|
|
492
|
-
borderTop: "1px solid var(--border-subtle)",
|
|
493
487
|
}}
|
|
494
488
|
>
|
|
495
489
|
<Stack direction="row" gap="sm" align="center">
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared utility functions for viewer components.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export function normalizeAnchorSegment(value: string): string {
|
|
6
|
+
const normalized = value
|
|
7
|
+
.toLowerCase()
|
|
8
|
+
.trim()
|
|
9
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
10
|
+
.replace(/^-+|-+$/g, "");
|
|
11
|
+
return normalized || "variant";
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function getVariantSectionId(componentName: string, variantName: string): string {
|
|
15
|
+
return `preview-${normalizeAnchorSegment(componentName)}-${normalizeAnchorSegment(variantName)}`;
|
|
16
|
+
}
|
package/src/viewer/entry.tsx
CHANGED
|
@@ -137,6 +137,7 @@ declare global {
|
|
|
137
137
|
fragment: import("../core/index.js").FragmentDefinition;
|
|
138
138
|
}>;
|
|
139
139
|
__FRAGMENTS_CONFIG__?: import("../core/index.js").FragmentsConfig;
|
|
140
|
+
__FRAGMENTS_PACKAGE_NAME__?: string | null;
|
|
140
141
|
__FRAGMENTS_ERROR__?: string;
|
|
141
142
|
}
|
|
142
143
|
}
|
|
@@ -175,6 +176,10 @@ async function loadFragmentsFromVirtualModule(): Promise<void> {
|
|
|
175
176
|
fragments = virtualModule.fragments;
|
|
176
177
|
}
|
|
177
178
|
fragments = filterValidFragments(fragments);
|
|
179
|
+
// Store consumer package name for import path display
|
|
180
|
+
if (virtualModule.projectPackageName) {
|
|
181
|
+
window.__FRAGMENTS_PACKAGE_NAME__ = virtualModule.projectPackageName;
|
|
182
|
+
}
|
|
178
183
|
} catch (e) {
|
|
179
184
|
const error = e as Error;
|
|
180
185
|
loadError = error.message || "Failed to load fragments";
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { useReducer, useCallback, useMemo } from 'react';
|
|
7
7
|
|
|
8
|
-
export type ActivePanel = '
|
|
8
|
+
export type ActivePanel = 'styles' | 'accessibility' | 'interactions' | 'graph' | 'performance';
|
|
9
9
|
|
|
10
10
|
interface AppUIState {
|
|
11
11
|
activePanel: ActivePanel;
|
|
@@ -16,6 +16,7 @@ interface AppUIState {
|
|
|
16
16
|
showMatrixView: boolean;
|
|
17
17
|
showCommandPalette: boolean;
|
|
18
18
|
showMultiViewport: boolean;
|
|
19
|
+
showAside: boolean;
|
|
19
20
|
linkCopied: boolean;
|
|
20
21
|
previewKey: number;
|
|
21
22
|
}
|
|
@@ -34,17 +35,28 @@ type AppUIAction =
|
|
|
34
35
|
| { type: 'SET_MULTI_VIEWPORT'; payload: boolean }
|
|
35
36
|
| { type: 'SET_LINK_COPIED'; payload: boolean }
|
|
36
37
|
| { type: 'INCREMENT_PREVIEW_KEY' }
|
|
38
|
+
| { type: 'TOGGLE_ASIDE' }
|
|
39
|
+
| { type: 'SET_ASIDE'; payload: boolean }
|
|
37
40
|
| { type: 'CLOSE_ALL_MODALS' };
|
|
38
41
|
|
|
42
|
+
function getInitialAsideState(): boolean {
|
|
43
|
+
try {
|
|
44
|
+
const stored = localStorage.getItem('fragments-aside-visible');
|
|
45
|
+
if (stored !== null) return stored === 'true';
|
|
46
|
+
} catch { /* noop */ }
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
|
|
39
50
|
const initialState: AppUIState = {
|
|
40
|
-
activePanel: '
|
|
41
|
-
panelOpen:
|
|
51
|
+
activePanel: 'accessibility',
|
|
52
|
+
panelOpen: false,
|
|
42
53
|
showHealthDashboard: false,
|
|
43
|
-
showComparison:
|
|
54
|
+
showComparison: false,
|
|
44
55
|
showShortcutsHelp: false,
|
|
45
56
|
showMatrixView: false,
|
|
46
57
|
showCommandPalette: false,
|
|
47
58
|
showMultiViewport: false,
|
|
59
|
+
showAside: getInitialAsideState(),
|
|
48
60
|
linkCopied: false,
|
|
49
61
|
previewKey: 0,
|
|
50
62
|
};
|
|
@@ -87,6 +99,15 @@ function appUIReducer(state: AppUIState, action: AppUIAction): AppUIState {
|
|
|
87
99
|
return { ...state, linkCopied: action.payload };
|
|
88
100
|
case 'INCREMENT_PREVIEW_KEY':
|
|
89
101
|
return { ...state, previewKey: state.previewKey + 1 };
|
|
102
|
+
case 'TOGGLE_ASIDE': {
|
|
103
|
+
const next = !state.showAside;
|
|
104
|
+
try { localStorage.setItem('fragments-aside-visible', String(next)); } catch { /* noop */ }
|
|
105
|
+
return { ...state, showAside: next };
|
|
106
|
+
}
|
|
107
|
+
case 'SET_ASIDE': {
|
|
108
|
+
try { localStorage.setItem('fragments-aside-visible', String(action.payload)); } catch { /* noop */ }
|
|
109
|
+
return { ...state, showAside: action.payload };
|
|
110
|
+
}
|
|
90
111
|
case 'CLOSE_ALL_MODALS':
|
|
91
112
|
return {
|
|
92
113
|
...state,
|
|
@@ -115,6 +136,8 @@ export function useAppState() {
|
|
|
115
136
|
setMultiViewport: (show: boolean) => dispatch({ type: 'SET_MULTI_VIEWPORT', payload: show }),
|
|
116
137
|
setLinkCopied: (copied: boolean) => dispatch({ type: 'SET_LINK_COPIED', payload: copied }),
|
|
117
138
|
incrementPreviewKey: () => dispatch({ type: 'INCREMENT_PREVIEW_KEY' }),
|
|
139
|
+
toggleAside: () => dispatch({ type: 'TOGGLE_ASIDE' }),
|
|
140
|
+
setAside: (show: boolean) => dispatch({ type: 'SET_ASIDE', payload: show }),
|
|
118
141
|
closeAllModals: () => dispatch({ type: 'CLOSE_ALL_MODALS' }),
|
|
119
142
|
}), []);
|
|
120
143
|
|
|
@@ -57,8 +57,8 @@ export function usePreviewBridge(iframeRef: RefObject<HTMLIFrameElement | null>)
|
|
|
57
57
|
const initSentRef = useRef(false);
|
|
58
58
|
const currentRenderIdRef = useRef<string | null>(null);
|
|
59
59
|
|
|
60
|
-
// Render timeout duration (
|
|
61
|
-
const RENDER_TIMEOUT =
|
|
60
|
+
// Render timeout duration (30 seconds)
|
|
61
|
+
const RENDER_TIMEOUT = 30000;
|
|
62
62
|
|
|
63
63
|
// Listen for messages from iframe
|
|
64
64
|
useEffect(() => {
|
|
@@ -28,24 +28,18 @@
|
|
|
28
28
|
|
|
29
29
|
#preview-root {
|
|
30
30
|
width: 100%;
|
|
31
|
-
|
|
31
|
+
height: 100%;
|
|
32
|
+
min-height: 400px;
|
|
33
|
+
padding: 16px;
|
|
32
34
|
display: flex;
|
|
33
|
-
|
|
34
|
-
justify-content: center;
|
|
35
|
-
padding: 24px;
|
|
35
|
+
flex-direction: column;
|
|
36
36
|
box-sizing: border-box;
|
|
37
37
|
overflow: auto;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
align-items: flex-start;
|
|
42
|
-
justify-content: flex-start;
|
|
43
|
-
padding: 0;
|
|
44
|
-
min-height: 100vh;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
body[data-preview-mode='full-bleed'] #preview-root > * {
|
|
40
|
+
#preview-root > * {
|
|
48
41
|
width: 100%;
|
|
42
|
+
flex: 1;
|
|
49
43
|
}
|
|
50
44
|
|
|
51
45
|
/* Hide scrollbars but allow scrolling */
|