@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
|
@@ -1,7 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import clsx from 'clsx';
|
|
1
|
+
import { Button, Menu, Stack, Input, Text, Box } from '@fragments/ui';
|
|
3
2
|
import { VIEWPORT_PRESETS, type ViewportPreset } from '../constants/ui.js';
|
|
4
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
ViewportIcon,
|
|
5
|
+
ChevronDownIcon,
|
|
6
|
+
ResponsiveIcon,
|
|
7
|
+
DesktopIcon,
|
|
8
|
+
TabletIcon,
|
|
9
|
+
MobileIcon,
|
|
10
|
+
SettingsIcon,
|
|
11
|
+
} from './Icons.js';
|
|
5
12
|
|
|
6
13
|
// Re-export for consumers
|
|
7
14
|
export type { ViewportPreset };
|
|
@@ -16,9 +23,24 @@ const VIEWPORT_PRESETS_UI = Object.entries(VIEWPORT_PRESETS).map(([value, config
|
|
|
16
23
|
value: value as ViewportPreset,
|
|
17
24
|
label: config.label,
|
|
18
25
|
width: config.width,
|
|
19
|
-
icon: config.icon,
|
|
20
26
|
}));
|
|
21
27
|
|
|
28
|
+
function PresetIcon({ preset }: { preset: ViewportPreset }) {
|
|
29
|
+
const commonStyle = { width: '16px', height: '16px' };
|
|
30
|
+
|
|
31
|
+
switch (preset) {
|
|
32
|
+
case 'desktop':
|
|
33
|
+
return <DesktopIcon style={commonStyle} />;
|
|
34
|
+
case 'tablet':
|
|
35
|
+
return <TabletIcon style={commonStyle} />;
|
|
36
|
+
case 'mobile':
|
|
37
|
+
return <MobileIcon style={commonStyle} />;
|
|
38
|
+
case 'responsive':
|
|
39
|
+
default:
|
|
40
|
+
return <ResponsiveIcon style={commonStyle} />;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
22
44
|
interface ViewportSelectorProps {
|
|
23
45
|
viewport: ViewportPreset;
|
|
24
46
|
customSize: ViewportSize;
|
|
@@ -32,128 +54,113 @@ export function ViewportSelector({
|
|
|
32
54
|
onViewportChange,
|
|
33
55
|
onCustomSizeChange,
|
|
34
56
|
}: ViewportSelectorProps) {
|
|
35
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
36
|
-
const [showCustom, setShowCustom] = useState(false);
|
|
37
|
-
|
|
38
|
-
// Close dropdown when clicking outside
|
|
39
|
-
useEffect(() => {
|
|
40
|
-
const handleClick = () => {
|
|
41
|
-
setIsOpen(false);
|
|
42
|
-
};
|
|
43
|
-
if (isOpen) {
|
|
44
|
-
document.addEventListener('click', handleClick);
|
|
45
|
-
return () => document.removeEventListener('click', handleClick);
|
|
46
|
-
}
|
|
47
|
-
}, [isOpen]);
|
|
48
|
-
|
|
49
57
|
const currentPreset = VIEWPORT_PRESETS_UI.find(p => p.value === viewport);
|
|
58
|
+
const isCustomViewport = viewport === 'custom';
|
|
50
59
|
const displayLabel = viewport === 'custom'
|
|
51
|
-
? `${customSize.width || '?'}
|
|
60
|
+
? `${customSize.width || '?'}\u00D7${customSize.height || 'auto'}`
|
|
52
61
|
: currentPreset?.label || 'Responsive';
|
|
53
62
|
|
|
54
63
|
return (
|
|
55
|
-
<
|
|
56
|
-
<
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
>
|
|
69
|
-
|
|
70
|
-
<
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
64
|
+
<Menu>
|
|
65
|
+
<Menu.Trigger asChild>
|
|
66
|
+
<Button variant="ghost" size="sm" title="Viewport size">
|
|
67
|
+
<Stack direction="row" gap="xs" align="center">
|
|
68
|
+
<span style={{ display: 'inline-flex', width: '14px', height: '14px' }}>
|
|
69
|
+
<ViewportIcon />
|
|
70
|
+
</span>
|
|
71
|
+
<span>{displayLabel}</span>
|
|
72
|
+
<span style={{ display: 'inline-flex', width: '12px', height: '12px' }}>
|
|
73
|
+
<ChevronDownIcon />
|
|
74
|
+
</span>
|
|
75
|
+
</Stack>
|
|
76
|
+
</Button>
|
|
77
|
+
</Menu.Trigger>
|
|
78
|
+
<Menu.Content side="bottom" align="end">
|
|
79
|
+
<Menu.RadioGroup
|
|
80
|
+
value={isCustomViewport ? 'custom' : viewport}
|
|
81
|
+
onValueChange={(value: string) => {
|
|
82
|
+
if (value === 'custom') {
|
|
83
|
+
onViewportChange('custom');
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
onViewportChange(value as ViewportPreset);
|
|
87
|
+
}}
|
|
78
88
|
>
|
|
79
89
|
{VIEWPORT_PRESETS_UI.map((preset) => (
|
|
80
|
-
<
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
>
|
|
93
|
-
<span className="flex items-center gap-2">
|
|
94
|
-
<span>{preset.icon}</span>
|
|
95
|
-
{preset.label}
|
|
96
|
-
</span>
|
|
97
|
-
{preset.width && (
|
|
98
|
-
<span className="text-[10px] text-tertiary">{preset.width}px</span>
|
|
99
|
-
)}
|
|
100
|
-
</button>
|
|
90
|
+
<Menu.RadioItem key={preset.value} value={preset.value}>
|
|
91
|
+
<Stack direction="row" gap="sm" align="center" justify="between" style={{ width: '100%' }}>
|
|
92
|
+
<Stack direction="row" gap="sm" align="center">
|
|
93
|
+
<PresetIcon preset={preset.value} />
|
|
94
|
+
<span>{preset.label}</span>
|
|
95
|
+
</Stack>
|
|
96
|
+
{preset.width && (
|
|
97
|
+
<Text size="2xs" color="tertiary">
|
|
98
|
+
{preset.width}px
|
|
99
|
+
</Text>
|
|
100
|
+
)}
|
|
101
|
+
</Stack>
|
|
102
|
+
</Menu.RadioItem>
|
|
101
103
|
))}
|
|
104
|
+
</Menu.RadioGroup>
|
|
102
105
|
|
|
103
|
-
|
|
106
|
+
<Menu.Separator />
|
|
104
107
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
viewport === 'custom' ? 'text-[--color-accent] font-medium' : 'text-secondary'
|
|
113
|
-
)}
|
|
114
|
-
>
|
|
115
|
-
<span>⚙</span>
|
|
108
|
+
<Menu.Item onSelect={() => onViewportChange('custom')}>
|
|
109
|
+
<Stack direction="row" gap="sm" align="center" style={{
|
|
110
|
+
width: '100%',
|
|
111
|
+
fontWeight: isCustomViewport ? 600 : 400,
|
|
112
|
+
color: isCustomViewport ? 'var(--color-accent)' : undefined,
|
|
113
|
+
}}>
|
|
114
|
+
<SettingsIcon style={{ width: '16px', height: '16px' }} />
|
|
116
115
|
Custom size
|
|
117
|
-
|
|
118
|
-
|
|
116
|
+
</Stack>
|
|
117
|
+
</Menu.Item>
|
|
119
118
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
119
|
+
{isCustomViewport && (
|
|
120
|
+
<div
|
|
121
|
+
onClick={(e) => e.stopPropagation()}
|
|
122
|
+
onKeyDown={(e) => e.stopPropagation()}
|
|
123
|
+
>
|
|
124
|
+
<Box padding="sm">
|
|
125
|
+
<Stack gap="sm">
|
|
126
|
+
<Stack direction="row" gap="sm" align="center">
|
|
127
|
+
<Text size="2xs" color="tertiary" style={{ width: '20px' }}>W:</Text>
|
|
128
|
+
<Input
|
|
129
|
+
type="number"
|
|
130
|
+
size="sm"
|
|
131
|
+
value={customSize.width != null ? String(customSize.width) : ''}
|
|
132
|
+
onChange={(value) => {
|
|
133
|
+
const width = value ? parseInt(value, 10) : null;
|
|
134
|
+
onCustomSizeChange({ ...customSize, width });
|
|
135
|
+
onViewportChange('custom');
|
|
136
|
+
}}
|
|
137
|
+
placeholder="auto"
|
|
138
|
+
style={{ width: '64px' }}
|
|
139
|
+
/>
|
|
140
|
+
<Text size="2xs" color="tertiary">px</Text>
|
|
141
|
+
</Stack>
|
|
142
|
+
<Stack direction="row" gap="sm" align="center">
|
|
143
|
+
<Text size="2xs" color="tertiary" style={{ width: '20px' }}>H:</Text>
|
|
144
|
+
<Input
|
|
145
|
+
type="number"
|
|
146
|
+
size="sm"
|
|
147
|
+
value={customSize.height != null ? String(customSize.height) : ''}
|
|
148
|
+
onChange={(value) => {
|
|
149
|
+
const height = value ? parseInt(value, 10) : null;
|
|
150
|
+
onCustomSizeChange({ ...customSize, height });
|
|
151
|
+
onViewportChange('custom');
|
|
152
|
+
}}
|
|
153
|
+
placeholder="auto"
|
|
154
|
+
style={{ width: '64px' }}
|
|
155
|
+
/>
|
|
156
|
+
<Text size="2xs" color="tertiary">px</Text>
|
|
157
|
+
</Stack>
|
|
158
|
+
</Stack>
|
|
159
|
+
</Box>
|
|
160
|
+
</div>
|
|
161
|
+
)}
|
|
162
|
+
</Menu.Content>
|
|
163
|
+
</Menu>
|
|
157
164
|
);
|
|
158
165
|
}
|
|
159
166
|
|
|
@@ -58,37 +58,38 @@ export function getStatusConfig(status: string | undefined) {
|
|
|
58
58
|
|
|
59
59
|
/**
|
|
60
60
|
* Relationship type styling for related components.
|
|
61
|
+
* Uses inline-style-compatible color values (not Tailwind classes).
|
|
61
62
|
*/
|
|
62
63
|
export const RELATIONSHIP_CONFIG = {
|
|
63
64
|
alternative: {
|
|
64
65
|
label: 'Alternative',
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
bgColor: 'rgba(59, 130, 246, 0.1)',
|
|
67
|
+
textColor: '#3b82f6',
|
|
67
68
|
},
|
|
68
69
|
parent: {
|
|
69
70
|
label: 'Parent',
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
bgColor: 'rgba(147, 51, 234, 0.1)',
|
|
72
|
+
textColor: '#a855f7',
|
|
72
73
|
},
|
|
73
74
|
child: {
|
|
74
75
|
label: 'Child',
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
bgColor: 'rgba(16, 185, 129, 0.1)',
|
|
77
|
+
textColor: '#10b981',
|
|
77
78
|
},
|
|
78
79
|
sibling: {
|
|
79
80
|
label: 'Sibling',
|
|
80
|
-
|
|
81
|
-
|
|
81
|
+
bgColor: 'rgba(245, 158, 11, 0.1)',
|
|
82
|
+
textColor: '#f59e0b',
|
|
82
83
|
},
|
|
83
84
|
related: {
|
|
84
85
|
label: 'Related',
|
|
85
|
-
|
|
86
|
-
|
|
86
|
+
bgColor: 'var(--bg-tertiary)',
|
|
87
|
+
textColor: 'var(--text-secondary)',
|
|
87
88
|
},
|
|
88
89
|
composition: {
|
|
89
90
|
label: 'Uses',
|
|
90
|
-
|
|
91
|
-
|
|
91
|
+
bgColor: 'rgba(6, 182, 212, 0.1)',
|
|
92
|
+
textColor: '#06b6d4',
|
|
92
93
|
},
|
|
93
94
|
} as const;
|
|
94
95
|
|
|
@@ -117,10 +118,10 @@ export type ZoomLevel = (typeof ZOOM_LEVELS)[number];
|
|
|
117
118
|
* Viewport presets.
|
|
118
119
|
*/
|
|
119
120
|
export const VIEWPORT_PRESETS = {
|
|
120
|
-
responsive: { label: 'Responsive', width: null, icon: '
|
|
121
|
-
desktop: { label: 'Desktop', width: 1280, icon: '
|
|
122
|
-
tablet: { label: 'Tablet', width: 768, icon: '
|
|
123
|
-
mobile: { label: 'Mobile', width: 375, icon: '
|
|
121
|
+
responsive: { label: 'Responsive', width: null, icon: 'responsive' },
|
|
122
|
+
desktop: { label: 'Desktop', width: 1280, icon: 'desktop' },
|
|
123
|
+
tablet: { label: 'Tablet', width: 768, icon: 'tablet' },
|
|
124
|
+
mobile: { label: 'Mobile', width: 375, icon: 'mobile' },
|
|
124
125
|
} as const;
|
|
125
126
|
|
|
126
127
|
export type ViewportPreset = keyof typeof VIEWPORT_PRESETS | 'custom';
|
|
@@ -167,18 +168,18 @@ export const STORAGE_KEYS = {
|
|
|
167
168
|
export const HMR_STATUS = {
|
|
168
169
|
connected: {
|
|
169
170
|
label: 'Connected',
|
|
170
|
-
color: '
|
|
171
|
-
bg: '
|
|
171
|
+
color: '#10b981',
|
|
172
|
+
bg: '#10b981',
|
|
172
173
|
},
|
|
173
174
|
reconnecting: {
|
|
174
175
|
label: 'Reconnecting...',
|
|
175
|
-
color: '
|
|
176
|
-
bg: '
|
|
176
|
+
color: '#f59e0b',
|
|
177
|
+
bg: '#f59e0b',
|
|
177
178
|
},
|
|
178
179
|
disconnected: {
|
|
179
180
|
label: 'Disconnected',
|
|
180
|
-
color: '
|
|
181
|
-
bg: '
|
|
181
|
+
color: '#ef4444',
|
|
182
|
+
bg: '#ef4444',
|
|
182
183
|
},
|
|
183
184
|
} as const;
|
|
184
185
|
|
package/src/viewer/entry.tsx
CHANGED
|
@@ -2,10 +2,13 @@ import { createRoot, type Root } from "react-dom/client";
|
|
|
2
2
|
import { Component, type ReactNode, type ErrorInfo } from "react";
|
|
3
3
|
import { App } from "./components/App.js";
|
|
4
4
|
import { ThemeProvider } from "./components/ThemeProvider.js";
|
|
5
|
+
import { ToastProvider } from "./components/Toast.js";
|
|
5
6
|
import { AppSkeleton } from "./components/SkeletonLoader.js";
|
|
6
7
|
// Viewer shell styles - independent from UI library
|
|
7
8
|
// UI library styles are only loaded in the isolated preview area
|
|
8
9
|
import "./styles/globals.css";
|
|
10
|
+
// Fragments UI globals - adds --fui-* CSS variables for dogfooding UI components
|
|
11
|
+
import "@fragments/ui";
|
|
9
12
|
|
|
10
13
|
// App-level error boundary that catches any unhandled errors
|
|
11
14
|
class AppErrorBoundary extends Component<{ children: ReactNode }, { hasError: boolean; error: Error | null; errorInfo: ErrorInfo | null }> {
|
|
@@ -244,9 +247,11 @@ if (rootElement) {
|
|
|
244
247
|
|
|
245
248
|
appRoot?.render(
|
|
246
249
|
<ThemeProvider>
|
|
247
|
-
<
|
|
248
|
-
<
|
|
249
|
-
|
|
250
|
+
<ToastProvider position="bottom-right" duration={3000}>
|
|
251
|
+
<AppErrorBoundary>
|
|
252
|
+
<App segments={currentSegments} />
|
|
253
|
+
</AppErrorBoundary>
|
|
254
|
+
</ToastProvider>
|
|
250
255
|
</ThemeProvider>
|
|
251
256
|
);
|
|
252
257
|
};
|
package/src/viewer/index.ts
CHANGED
|
@@ -6,9 +6,6 @@ export type { DevServerOptions } from "./server.js";
|
|
|
6
6
|
export { segmentsPlugin } from "./vite-plugin.js";
|
|
7
7
|
export type { SegmentsPluginOptions } from "./vite-plugin.js";
|
|
8
8
|
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
export { LeftSidebar } from "./components/LeftSidebar.js";
|
|
13
|
-
export { RightSidebar } from "./components/RightSidebar.js";
|
|
14
|
-
export { ThemeProvider, useTheme } from "./components/ThemeProvider.js";
|
|
9
|
+
// Note: Viewer components (App, Layout, etc.) are NOT exported here.
|
|
10
|
+
// They use @fragments/ui which is a Vite-only alias and can't be resolved by Node.js.
|
|
11
|
+
// Vite serves these components directly from source during development.
|
|
@@ -4,12 +4,6 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Fragment Preview</title>
|
|
7
|
-
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
8
|
-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
9
|
-
<link
|
|
10
|
-
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
|
|
11
|
-
rel="stylesheet"
|
|
12
|
-
/>
|
|
13
7
|
<style>
|
|
14
8
|
/* Reset and base styles for isolated preview */
|
|
15
9
|
*, *::before, *::after {
|
|
@@ -19,27 +13,42 @@
|
|
|
19
13
|
html, body {
|
|
20
14
|
margin: 0;
|
|
21
15
|
padding: 0;
|
|
22
|
-
font-family: '
|
|
16
|
+
font-family: var(--fui-font-sans, 'Geist Sans', Inter, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);
|
|
23
17
|
-webkit-font-smoothing: antialiased;
|
|
24
18
|
-moz-osx-font-smoothing: grayscale;
|
|
25
19
|
width: 100%;
|
|
26
20
|
height: 100%;
|
|
27
|
-
overflow:
|
|
21
|
+
overflow: hidden;
|
|
28
22
|
}
|
|
29
23
|
|
|
30
24
|
body {
|
|
31
|
-
background:
|
|
25
|
+
background: var(--fui-bg-primary, #ffffff);
|
|
32
26
|
min-height: 100%;
|
|
33
27
|
}
|
|
34
28
|
|
|
35
29
|
#preview-root {
|
|
36
30
|
width: 100%;
|
|
37
|
-
min-height:
|
|
31
|
+
min-height: 100vh;
|
|
38
32
|
display: flex;
|
|
39
|
-
align-items:
|
|
40
|
-
justify-content:
|
|
33
|
+
align-items: flex-start;
|
|
34
|
+
justify-content: flex-start;
|
|
41
35
|
padding: 24px;
|
|
42
36
|
box-sizing: border-box;
|
|
37
|
+
overflow: auto;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
body[data-preview-mode='centered'] #preview-root {
|
|
41
|
+
align-items: center;
|
|
42
|
+
justify-content: center;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
body[data-preview-mode='full-bleed'] #preview-root {
|
|
46
|
+
padding: 0;
|
|
47
|
+
min-height: 100vh;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
body[data-preview-mode='full-bleed'] #preview-root > * {
|
|
51
|
+
width: 100%;
|
|
43
52
|
}
|
|
44
53
|
|
|
45
54
|
/* Hide scrollbars but allow scrolling */
|
|
@@ -73,6 +82,19 @@
|
|
|
73
82
|
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
|
74
83
|
}
|
|
75
84
|
|
|
85
|
+
/* Visually hidden but accessible to screen readers */
|
|
86
|
+
.sr-only {
|
|
87
|
+
position: absolute;
|
|
88
|
+
width: 1px;
|
|
89
|
+
height: 1px;
|
|
90
|
+
padding: 0;
|
|
91
|
+
margin: -1px;
|
|
92
|
+
overflow: hidden;
|
|
93
|
+
clip: rect(0, 0, 0, 0);
|
|
94
|
+
white-space: nowrap;
|
|
95
|
+
border-width: 0;
|
|
96
|
+
}
|
|
97
|
+
|
|
76
98
|
/* Loading indicator */
|
|
77
99
|
.preview-loading {
|
|
78
100
|
display: flex;
|
|
@@ -97,13 +119,16 @@
|
|
|
97
119
|
}
|
|
98
120
|
</style>
|
|
99
121
|
</head>
|
|
100
|
-
<body>
|
|
101
|
-
<
|
|
102
|
-
<
|
|
103
|
-
|
|
104
|
-
<
|
|
122
|
+
<body data-preview-mode="centered">
|
|
123
|
+
<main>
|
|
124
|
+
<h1 class="sr-only">Component Preview</h1>
|
|
125
|
+
<div id="preview-root">
|
|
126
|
+
<div class="preview-loading">
|
|
127
|
+
<div class="spinner"></div>
|
|
128
|
+
<span>Loading preview...</span>
|
|
129
|
+
</div>
|
|
105
130
|
</div>
|
|
106
|
-
</
|
|
131
|
+
</main>
|
|
107
132
|
<script type="module" src="/src/preview-frame-entry.tsx"></script>
|
|
108
133
|
</body>
|
|
109
134
|
</html>
|
package/src/viewer/server.ts
CHANGED
|
@@ -18,8 +18,6 @@ import {
|
|
|
18
18
|
type InlineConfig,
|
|
19
19
|
} from "vite";
|
|
20
20
|
import react from "@vitejs/plugin-react";
|
|
21
|
-
import tailwindcss from "tailwindcss";
|
|
22
|
-
import autoprefixer from "autoprefixer";
|
|
23
21
|
import { resolve, dirname, join } from "node:path";
|
|
24
22
|
import { existsSync, realpathSync } from "node:fs";
|
|
25
23
|
import { fileURLToPath } from "node:url";
|
|
@@ -30,8 +28,8 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
30
28
|
// At runtime, __dirname is dist/. Viewer assets live in src/viewer/.
|
|
31
29
|
const cliPackageRoot = resolve(__dirname, "..");
|
|
32
30
|
const viewerRoot = resolve(cliPackageRoot, "src/viewer");
|
|
33
|
-
const viewerTailwindConfig = resolve(viewerRoot, "tailwind.config.js");
|
|
34
31
|
const packagesRoot = resolve(cliPackageRoot, "..");
|
|
32
|
+
const uiLibRoot = resolve(packagesRoot, "../libs/ui/src");
|
|
35
33
|
|
|
36
34
|
export interface DevServerOptions {
|
|
37
35
|
/** Port to run the server on */
|
|
@@ -124,8 +122,8 @@ export async function createDevServer(
|
|
|
124
122
|
port,
|
|
125
123
|
open: open ? "/fragments/" : false,
|
|
126
124
|
fs: {
|
|
127
|
-
// Allow serving files from viewer package, project, and node_modules root
|
|
128
|
-
allow: [viewerRoot, projectRoot, configDir, dirname(nodeModulesPath), ...installedPkgRoots],
|
|
125
|
+
// Allow serving files from viewer package, project, UI library, and node_modules root
|
|
126
|
+
allow: [viewerRoot, uiLibRoot, projectRoot, configDir, dirname(nodeModulesPath), ...installedPkgRoots],
|
|
129
127
|
},
|
|
130
128
|
},
|
|
131
129
|
|
|
@@ -141,17 +139,8 @@ export async function createDevServer(
|
|
|
141
139
|
}),
|
|
142
140
|
],
|
|
143
141
|
|
|
144
|
-
// CSS configuration
|
|
145
|
-
css: {
|
|
146
|
-
postcss: {
|
|
147
|
-
plugins: [
|
|
148
|
-
tailwindcss({
|
|
149
|
-
config: viewerTailwindConfig,
|
|
150
|
-
}),
|
|
151
|
-
autoprefixer(),
|
|
152
|
-
],
|
|
153
|
-
},
|
|
154
|
-
},
|
|
142
|
+
// CSS configuration
|
|
143
|
+
css: {},
|
|
155
144
|
|
|
156
145
|
optimizeDeps: {
|
|
157
146
|
// Include common dependencies for faster startup
|
|
@@ -165,6 +154,8 @@ export async function createDevServer(
|
|
|
165
154
|
alias: {
|
|
166
155
|
// Allow importing from viewer package
|
|
167
156
|
"@fragments/viewer": viewerRoot,
|
|
157
|
+
// Resolve @fragments/ui to the UI library source for dogfooding
|
|
158
|
+
"@fragments/ui": resolve(uiLibRoot, "index.ts"),
|
|
168
159
|
// Resolve @fragments/core to the consolidated core source
|
|
169
160
|
"@fragments/core": resolve(cliPackageRoot, "src/core/index.ts"),
|
|
170
161
|
// Ensure ALL react imports resolve to project's node_modules
|