@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
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { memo } from 'react';
|
|
7
7
|
import type { SegmentContract } from '../../core/index.js';
|
|
8
|
+
import { Stack, Text, Card, Chip, Alert, EmptyState, CodeBlock, Badge } from '@fragments/ui';
|
|
8
9
|
|
|
9
10
|
interface ContractPanelProps {
|
|
10
11
|
contract?: SegmentContract;
|
|
@@ -18,10 +19,10 @@ export const ContractPanel = memo(function ContractPanel({
|
|
|
18
19
|
// Empty state when no contract metadata
|
|
19
20
|
if (!contract || isContractEmpty(contract)) {
|
|
20
21
|
return (
|
|
21
|
-
<
|
|
22
|
-
<
|
|
22
|
+
<EmptyState>
|
|
23
|
+
<EmptyState.Icon>
|
|
23
24
|
<svg
|
|
24
|
-
|
|
25
|
+
style={{ width: '48px', height: '48px', opacity: 0.5 }}
|
|
25
26
|
fill="none"
|
|
26
27
|
viewBox="0 0 24 24"
|
|
27
28
|
stroke="currentColor"
|
|
@@ -33,126 +34,132 @@ export const ContractPanel = memo(function ContractPanel({
|
|
|
33
34
|
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
|
|
34
35
|
/>
|
|
35
36
|
</svg>
|
|
36
|
-
|
|
37
|
-
</
|
|
38
|
-
<
|
|
39
|
-
Add a <code
|
|
40
|
-
</
|
|
41
|
-
<
|
|
42
|
-
<
|
|
37
|
+
</EmptyState.Icon>
|
|
38
|
+
<EmptyState.Title>No contract metadata</EmptyState.Title>
|
|
39
|
+
<EmptyState.Description>
|
|
40
|
+
Add a <code style={{ background: 'var(--bg-hover)', padding: '2px 4px', borderRadius: '4px' }}>contract</code> field to the segment definition to enable AI agent features.
|
|
41
|
+
</EmptyState.Description>
|
|
42
|
+
<EmptyState.Actions>
|
|
43
|
+
<CodeBlock
|
|
44
|
+
code={`contract: {
|
|
43
45
|
propsSummary: ['variant: primary|secondary'],
|
|
44
46
|
scenarioTags: ['form.submit', 'action.primary'],
|
|
45
47
|
a11yRules: ['A11Y_BTN_LABEL'],
|
|
46
|
-
}`}
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
}`}
|
|
49
|
+
language="typescript"
|
|
50
|
+
compact
|
|
51
|
+
/>
|
|
52
|
+
</EmptyState.Actions>
|
|
53
|
+
</EmptyState>
|
|
49
54
|
);
|
|
50
55
|
}
|
|
51
56
|
|
|
52
57
|
return (
|
|
53
|
-
<div
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
<
|
|
58
|
-
<
|
|
59
|
-
<
|
|
60
|
-
<
|
|
61
|
-
<
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
<tbody>
|
|
67
|
-
{contract.propsSummary.map((prop, i) => {
|
|
68
|
-
const parsed = parsePropSummary(prop);
|
|
69
|
-
return (
|
|
70
|
-
<tr key={i} className="border-b border-[--border-subtle] last:border-b-0">
|
|
71
|
-
<td className="py-2 pr-4 align-top">
|
|
72
|
-
<code className="text-xs font-medium text-primary">{parsed.name}</code>
|
|
73
|
-
</td>
|
|
74
|
-
<td className="py-2 pr-4 align-top">
|
|
75
|
-
<code className="text-xs text-purple-600 dark:text-purple-400">{parsed.type}</code>
|
|
76
|
-
</td>
|
|
77
|
-
<td className="py-2 align-top text-secondary">{parsed.description}</td>
|
|
58
|
+
<div style={{ padding: '16px', fontSize: '14px' }}>
|
|
59
|
+
<Stack gap="lg">
|
|
60
|
+
{/* Props Summary */}
|
|
61
|
+
{contract.propsSummary && contract.propsSummary.length > 0 && (
|
|
62
|
+
<Section title="Props Summary">
|
|
63
|
+
<Card>
|
|
64
|
+
<Card.Body>
|
|
65
|
+
<table style={{ width: '100%', fontSize: '12px', borderCollapse: 'collapse' }}>
|
|
66
|
+
<thead>
|
|
67
|
+
<tr style={{ borderBottom: '1px solid var(--border)' }}>
|
|
68
|
+
<th style={{ textAlign: 'left', padding: '8px 16px 8px 0', fontWeight: 500, color: 'var(--text-tertiary)' }}>Name</th>
|
|
69
|
+
<th style={{ textAlign: 'left', padding: '8px 16px 8px 0', fontWeight: 500, color: 'var(--text-tertiary)' }}>Type</th>
|
|
70
|
+
<th style={{ textAlign: 'left', padding: '8px 0', fontWeight: 500, color: 'var(--text-tertiary)' }}>Description</th>
|
|
78
71
|
</tr>
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
<
|
|
107
|
-
{
|
|
108
|
-
|
|
109
|
-
key={i}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
key={i}
|
|
131
|
-
className="p-3 bg-amber-500/10 border border-amber-500/20 rounded"
|
|
132
|
-
>
|
|
133
|
-
<div className="flex items-center gap-2 mb-1">
|
|
134
|
-
<span className="text-amber-500">
|
|
135
|
-
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
136
|
-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
|
72
|
+
</thead>
|
|
73
|
+
<tbody>
|
|
74
|
+
{contract.propsSummary.map((prop, i) => {
|
|
75
|
+
const parsed = parsePropSummary(prop);
|
|
76
|
+
return (
|
|
77
|
+
<tr key={i} style={{ borderBottom: i < contract.propsSummary!.length - 1 ? '1px solid var(--border)' : undefined }}>
|
|
78
|
+
<td style={{ padding: '8px 16px 8px 0', verticalAlign: 'top' }}>
|
|
79
|
+
<Text font="mono" size="xs" weight="medium">{parsed.name}</Text>
|
|
80
|
+
</td>
|
|
81
|
+
<td style={{ padding: '8px 16px 8px 0', verticalAlign: 'top' }}>
|
|
82
|
+
<Badge size="sm">{parsed.type}</Badge>
|
|
83
|
+
</td>
|
|
84
|
+
<td style={{ padding: '8px 0', verticalAlign: 'top' }}>
|
|
85
|
+
<Text size="xs" color="secondary">{parsed.description}</Text>
|
|
86
|
+
</td>
|
|
87
|
+
</tr>
|
|
88
|
+
);
|
|
89
|
+
})}
|
|
90
|
+
</tbody>
|
|
91
|
+
</table>
|
|
92
|
+
</Card.Body>
|
|
93
|
+
</Card>
|
|
94
|
+
</Section>
|
|
95
|
+
)}
|
|
96
|
+
|
|
97
|
+
{/* Scenario Tags */}
|
|
98
|
+
{contract.scenarioTags && contract.scenarioTags.length > 0 && (
|
|
99
|
+
<Section title="Scenario Tags" subtitle="Used by fragments_suggest for AI queries">
|
|
100
|
+
<Stack direction="row" gap="sm" style={{ flexWrap: 'wrap' }}>
|
|
101
|
+
{contract.scenarioTags.map((tag, i) => (
|
|
102
|
+
<Chip key={i}>{tag}</Chip>
|
|
103
|
+
))}
|
|
104
|
+
</Stack>
|
|
105
|
+
</Section>
|
|
106
|
+
)}
|
|
107
|
+
|
|
108
|
+
{/* Accessibility Rules */}
|
|
109
|
+
{contract.a11yRules && contract.a11yRules.length > 0 && (
|
|
110
|
+
<Section title="Accessibility Rules">
|
|
111
|
+
<Stack gap="sm">
|
|
112
|
+
{contract.a11yRules.map((rule, i) => (
|
|
113
|
+
<Stack
|
|
114
|
+
key={i}
|
|
115
|
+
direction="row"
|
|
116
|
+
align="center"
|
|
117
|
+
gap="sm"
|
|
118
|
+
style={{ padding: '8px', background: 'var(--bg-hover)', borderRadius: '4px' }}
|
|
119
|
+
>
|
|
120
|
+
<span style={{ color: '#22c55e' }}>
|
|
121
|
+
<svg style={{ width: '16px', height: '16px' }} fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
122
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
137
123
|
</svg>
|
|
138
124
|
</span>
|
|
139
|
-
<
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
125
|
+
<Text font="mono" size="xs" color="secondary">{rule}</Text>
|
|
126
|
+
</Stack>
|
|
127
|
+
))}
|
|
128
|
+
</Stack>
|
|
129
|
+
</Section>
|
|
130
|
+
)}
|
|
131
|
+
|
|
132
|
+
{/* Bans */}
|
|
133
|
+
{contract.bans && contract.bans.length > 0 && (
|
|
134
|
+
<Section title="Usage Bans" subtitle="Patterns to avoid">
|
|
135
|
+
<Stack gap="sm">
|
|
136
|
+
{contract.bans.map((ban, i) => (
|
|
137
|
+
<Alert key={i} severity="warning">
|
|
138
|
+
<Alert.Body>
|
|
139
|
+
<Alert.Title>
|
|
140
|
+
<Text font="mono" size="xs">{ban.pattern}</Text>
|
|
141
|
+
</Alert.Title>
|
|
142
|
+
<Alert.Content>
|
|
143
|
+
<Text size="xs" color="tertiary">{ban.message}</Text>
|
|
144
|
+
</Alert.Content>
|
|
145
|
+
</Alert.Body>
|
|
146
|
+
</Alert>
|
|
147
|
+
))}
|
|
148
|
+
</Stack>
|
|
149
|
+
</Section>
|
|
150
|
+
)}
|
|
151
|
+
|
|
152
|
+
{/* Empty sections message */}
|
|
153
|
+
{isPartialContract(contract) && (
|
|
154
|
+
<Alert severity="info">
|
|
155
|
+
<Alert.Body>
|
|
156
|
+
<Alert.Content>
|
|
157
|
+
Tip: Add more contract fields to help AI agents better understand this component.
|
|
158
|
+
</Alert.Content>
|
|
159
|
+
</Alert.Body>
|
|
160
|
+
</Alert>
|
|
161
|
+
)}
|
|
162
|
+
</Stack>
|
|
156
163
|
</div>
|
|
157
164
|
);
|
|
158
165
|
});
|
|
@@ -167,12 +174,12 @@ interface SectionProps {
|
|
|
167
174
|
function Section({ title, subtitle, children }: SectionProps) {
|
|
168
175
|
return (
|
|
169
176
|
<div>
|
|
170
|
-
<
|
|
171
|
-
{title}
|
|
177
|
+
<Stack direction="row" align="center" gap="sm" style={{ marginBottom: '8px' }}>
|
|
178
|
+
<Text as="h3" size="xs" weight="semibold">{title}</Text>
|
|
172
179
|
{subtitle && (
|
|
173
|
-
<
|
|
180
|
+
<Text size="xs" color="tertiary">({subtitle})</Text>
|
|
174
181
|
)}
|
|
175
|
-
</
|
|
182
|
+
</Stack>
|
|
176
183
|
{children}
|
|
177
184
|
</div>
|
|
178
185
|
);
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { Component, type ReactNode, type ErrorInfo } from 'react';
|
|
2
|
+
import { Button, Alert, CodeBlock, Collapsible, Stack, Text } from '@fragments/ui';
|
|
2
3
|
import { ErrorIcon, RefreshIcon } from './Icons.js';
|
|
3
4
|
|
|
4
5
|
interface ErrorBoundaryProps {
|
|
5
6
|
children: ReactNode;
|
|
6
7
|
componentName?: string;
|
|
8
|
+
fallback?: ReactNode;
|
|
7
9
|
onRetry?: () => void;
|
|
8
10
|
}
|
|
9
11
|
|
|
@@ -24,10 +26,8 @@ export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundarySt
|
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
|
27
|
-
// Log error to console with full stack trace
|
|
28
29
|
console.error('Component Error:', error);
|
|
29
30
|
console.error('Component Stack:', errorInfo.componentStack);
|
|
30
|
-
|
|
31
31
|
this.setState({ errorInfo });
|
|
32
32
|
}
|
|
33
33
|
|
|
@@ -38,45 +38,57 @@ export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundarySt
|
|
|
38
38
|
|
|
39
39
|
render() {
|
|
40
40
|
if (this.state.hasError) {
|
|
41
|
+
if (this.props.fallback) {
|
|
42
|
+
return this.props.fallback;
|
|
43
|
+
}
|
|
44
|
+
|
|
41
45
|
const { componentName } = this.props;
|
|
42
46
|
const { error, errorInfo } = this.state;
|
|
43
47
|
|
|
44
48
|
return (
|
|
45
|
-
<
|
|
46
|
-
<
|
|
47
|
-
<ErrorIcon
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
49
|
+
<Alert variant="error">
|
|
50
|
+
<Alert.Icon>
|
|
51
|
+
<ErrorIcon style={{ width: '24px', height: '24px' }} />
|
|
52
|
+
</Alert.Icon>
|
|
53
|
+
<Alert.Body>
|
|
54
|
+
<Alert.Title>
|
|
55
|
+
{componentName ? `Error rendering ${componentName}` : 'Component Error'}
|
|
56
|
+
</Alert.Title>
|
|
57
|
+
<Alert.Content>
|
|
58
|
+
<Stack direction="column" gap="sm">
|
|
59
|
+
{error && (
|
|
60
|
+
<Text size="sm" font="mono" style={{ color: '#dc2626' }}>
|
|
61
|
+
{error.message}
|
|
62
|
+
</Text>
|
|
63
|
+
)}
|
|
58
64
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
{errorInfo?.componentStack && (
|
|
66
|
+
<Collapsible>
|
|
67
|
+
<Collapsible.Trigger>
|
|
68
|
+
<Text size="xs" color="tertiary" style={{ cursor: 'pointer' }}>
|
|
69
|
+
Show stack trace
|
|
70
|
+
</Text>
|
|
71
|
+
</Collapsible.Trigger>
|
|
72
|
+
<Collapsible.Content>
|
|
73
|
+
<div style={{ marginTop: '8px' }}>
|
|
74
|
+
<CodeBlock language="plaintext">
|
|
75
|
+
{error?.stack || errorInfo.componentStack}
|
|
76
|
+
</CodeBlock>
|
|
77
|
+
</div>
|
|
78
|
+
</Collapsible.Content>
|
|
79
|
+
</Collapsible>
|
|
80
|
+
)}
|
|
69
81
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
</
|
|
77
|
-
</
|
|
78
|
-
</
|
|
79
|
-
</
|
|
82
|
+
<div>
|
|
83
|
+
<Button variant="danger" size="sm" onClick={this.handleRetry}>
|
|
84
|
+
<RefreshIcon style={{ width: '14px', height: '14px' }} />
|
|
85
|
+
Retry
|
|
86
|
+
</Button>
|
|
87
|
+
</div>
|
|
88
|
+
</Stack>
|
|
89
|
+
</Alert.Content>
|
|
90
|
+
</Alert.Body>
|
|
91
|
+
</Alert>
|
|
80
92
|
);
|
|
81
93
|
}
|
|
82
94
|
|
|
@@ -125,12 +125,12 @@ export function FigmaEmbed({ figmaUrl, allFigmaUrls, zoom = 100, className, styl
|
|
|
125
125
|
if (!currentParsed) {
|
|
126
126
|
return (
|
|
127
127
|
<div className={className} style={{ ...style, display: "flex", alignItems: "center", justifyContent: "center" }}>
|
|
128
|
-
<div
|
|
129
|
-
<FigmaIcon
|
|
130
|
-
<span
|
|
128
|
+
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '8px', color: 'var(--text-tertiary)', padding: '16px', textAlign: 'center' }}>
|
|
129
|
+
<FigmaIcon style={{ width: '24px', height: '24px' }} />
|
|
130
|
+
<span style={{ fontSize: '12px' }}>Unable to embed Figma design</span>
|
|
131
131
|
<button
|
|
132
132
|
onClick={() => window.open(figmaUrl, "_blank", "noopener,noreferrer")}
|
|
133
|
-
|
|
133
|
+
style={{ fontSize: '12px', color: 'var(--color-accent)', background: 'none', border: 'none', cursor: 'pointer', textDecoration: 'underline' }}
|
|
134
134
|
>
|
|
135
135
|
Open in Figma
|
|
136
136
|
</button>
|
|
@@ -143,20 +143,20 @@ export function FigmaEmbed({ figmaUrl, allFigmaUrls, zoom = 100, className, styl
|
|
|
143
143
|
<div className={className} style={{ ...style, position: "relative", overflow: "hidden" }}>
|
|
144
144
|
{/* Loading overlay - shows while current iframe is loading */}
|
|
145
145
|
{!isCurrentLoaded && (
|
|
146
|
-
<div
|
|
147
|
-
<div
|
|
148
|
-
<FigmaIcon
|
|
149
|
-
<span
|
|
146
|
+
<div style={{ position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', backgroundColor: 'var(--bg-secondary)', zIndex: 20 }}>
|
|
147
|
+
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '8px' }}>
|
|
148
|
+
<FigmaIcon style={{ width: '20px', height: '20px', color: 'var(--text-tertiary)' }} />
|
|
149
|
+
<span style={{ fontSize: '12px', color: 'var(--text-tertiary)' }}>Loading Figma...</span>
|
|
150
150
|
</div>
|
|
151
151
|
</div>
|
|
152
152
|
)}
|
|
153
153
|
|
|
154
154
|
{/* Error overlay */}
|
|
155
155
|
{error && (
|
|
156
|
-
<div
|
|
157
|
-
<div
|
|
158
|
-
<FigmaIcon
|
|
159
|
-
<span
|
|
156
|
+
<div style={{ position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', backgroundColor: 'var(--bg-secondary)', zIndex: 20 }}>
|
|
157
|
+
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '8px', color: 'var(--text-tertiary)' }}>
|
|
158
|
+
<FigmaIcon style={{ width: '24px', height: '24px' }} />
|
|
159
|
+
<span style={{ fontSize: '12px' }}>{error}</span>
|
|
160
160
|
</div>
|
|
161
161
|
</div>
|
|
162
162
|
)}
|
|
@@ -182,8 +182,13 @@ export function FigmaEmbed({ figmaUrl, allFigmaUrls, zoom = 100, className, styl
|
|
|
182
182
|
<iframe
|
|
183
183
|
key={key}
|
|
184
184
|
src={embedUrl}
|
|
185
|
-
className="absolute inset-0 w-full h-full border-0 transition-opacity duration-150"
|
|
186
185
|
style={{
|
|
186
|
+
position: 'absolute',
|
|
187
|
+
inset: 0,
|
|
188
|
+
width: '100%',
|
|
189
|
+
height: '100%',
|
|
190
|
+
border: 'none',
|
|
191
|
+
transition: 'opacity 150ms',
|
|
187
192
|
...zoomStyle,
|
|
188
193
|
opacity: isActive && isLoaded ? 1 : 0,
|
|
189
194
|
zIndex: isActive ? 10 : 1,
|