@fragments-sdk/viewer 0.2.1 → 0.2.4

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.
Files changed (60) hide show
  1. package/package.json +10 -3
  2. package/src/components/App.tsx +67 -4
  3. package/src/components/BottomPanel.tsx +31 -1
  4. package/src/components/ComponentGraph.tsx +1 -1
  5. package/src/components/EmptyVariantMessage.tsx +1 -1
  6. package/src/components/ErrorBoundary.tsx +2 -4
  7. package/src/components/FragmentRenderer.tsx +27 -4
  8. package/src/components/HeaderSearch.tsx +1 -1
  9. package/src/components/InteractionsPanel.tsx +5 -5
  10. package/src/components/LoadErrorMessage.tsx +26 -30
  11. package/src/components/NoVariantsMessage.tsx +1 -1
  12. package/src/components/PanelShell.tsx +1 -1
  13. package/src/components/PerformancePanel.tsx +4 -4
  14. package/src/components/PreviewArea.tsx +6 -0
  15. package/src/components/PropsEditor.tsx +33 -17
  16. package/src/components/SkeletonLoader.tsx +0 -1
  17. package/src/components/TokenStylePanel.tsx +3 -3
  18. package/src/components/TopToolbar.tsx +1 -1
  19. package/src/components/VariantMatrix.tsx +11 -1
  20. package/src/components/ViewerHeader.tsx +1 -1
  21. package/src/components/WebMCPDevTools.tsx +2 -2
  22. package/src/entry.tsx +4 -6
  23. package/src/hooks/useAppState.ts +1 -1
  24. package/src/preview-frame-entry.tsx +0 -2
  25. package/src/shared/DocsHeaderBar.module.scss +174 -0
  26. package/src/shared/DocsHeaderBar.tsx +149 -14
  27. package/src/shared/DocsSidebarNav.tsx +3 -3
  28. package/src/shared/index.ts +4 -1
  29. package/src/shared/types.ts +29 -3
  30. package/src/style-utils.ts +1 -1
  31. package/src/webmcp/runtime-tools.ts +1 -1
  32. package/tsconfig.json +2 -2
  33. package/src/assets/fragments-logo.ts +0 -4
  34. package/src/components/ActionsPanel.tsx +0 -332
  35. package/src/components/ComponentHeader.tsx +0 -88
  36. package/src/components/ContractPanel.tsx +0 -241
  37. package/src/components/FragmentEditor.tsx +0 -525
  38. package/src/components/HmrStatusIndicator.tsx +0 -61
  39. package/src/components/LandingPage.tsx +0 -420
  40. package/src/components/PropsTable.tsx +0 -111
  41. package/src/components/RelationsSection.tsx +0 -88
  42. package/src/components/ResizablePanel.tsx +0 -271
  43. package/src/components/RightSidebar.tsx +0 -102
  44. package/src/components/ScreenshotButton.tsx +0 -90
  45. package/src/components/Sidebar.tsx +0 -169
  46. package/src/components/UsageSection.tsx +0 -95
  47. package/src/components/VariantTabs.tsx +0 -40
  48. package/src/components/ViewportSelector.tsx +0 -172
  49. package/src/components/_future/CreatePage.tsx +0 -835
  50. package/src/composition-renderer.ts +0 -381
  51. package/src/constants/index.ts +0 -1
  52. package/src/hooks/index.ts +0 -2
  53. package/src/hooks/useHmrStatus.ts +0 -109
  54. package/src/hooks/useScrollSpy.ts +0 -78
  55. package/src/intelligence/healthReport.ts +0 -505
  56. package/src/intelligence/styleDrift.ts +0 -340
  57. package/src/intelligence/usageScanner.ts +0 -309
  58. package/src/utils/actionExport.ts +0 -372
  59. package/src/utils/colorSchemes.ts +0 -201
  60. package/src/webmcp/index.ts +0 -3
@@ -1,95 +0,0 @@
1
- import type { FragmentUsage } from '@fragments-sdk/core';
2
- import { CheckIcon, XIcon, AccessibilityIcon } from './Icons.js';
3
-
4
- interface UsageSectionProps {
5
- usage: FragmentUsage;
6
- }
7
-
8
- export function UsageSection({ usage }: UsageSectionProps) {
9
- const hasWhen = usage.when && usage.when.length > 0;
10
- const hasWhenNot = usage.whenNot && usage.whenNot.length > 0;
11
- const hasGuidelines = usage.guidelines && usage.guidelines.length > 0;
12
- const hasAccessibility = usage.accessibility && usage.accessibility.length > 0;
13
-
14
- if (!hasWhen && !hasWhenNot && !hasGuidelines && !hasAccessibility) {
15
- return null;
16
- }
17
-
18
- return (
19
- <section id="usage" style={{ scrollMarginTop: '96px' }}>
20
- <h2 style={{ fontSize: '16px', fontWeight: 600, color: 'var(--text-primary)', marginBottom: '20px' }}>Usage</h2>
21
-
22
- {/* When to use / When not to use */}
23
- {(hasWhen || hasWhenNot) && (
24
- <div style={{ display: 'grid', gridTemplateColumns: hasWhen && hasWhenNot ? '1fr 1fr' : '1fr', gap: '32px', marginBottom: '32px' }}>
25
- {hasWhen && (
26
- <div style={{ padding: '16px', borderRadius: '12px', background: 'var(--color-success-bg)', border: '1px solid rgba(16, 163, 127, 0.2)' }}>
27
- <h3 style={{ fontSize: '13px', fontWeight: 500, color: 'var(--color-success)', marginBottom: '12px', display: 'flex', alignItems: 'center', gap: '8px' }}>
28
- <CheckIcon style={{ width: '16px', height: '16px' }} />
29
- When to use
30
- </h3>
31
- <ul style={{ listStyle: 'none', padding: 0, margin: 0, display: 'flex', flexDirection: 'column', gap: '8px' }}>
32
- {usage.when!.map((item, index) => (
33
- <li key={index} style={{ fontSize: '13px', color: 'var(--text-primary)', lineHeight: 1.6, display: 'flex', alignItems: 'flex-start', gap: '8px' }}>
34
- <span style={{ color: 'var(--color-success)', marginTop: '6px', fontSize: '12px' }}>&#8226;</span>
35
- <span>{item}</span>
36
- </li>
37
- ))}
38
- </ul>
39
- </div>
40
- )}
41
-
42
- {hasWhenNot && (
43
- <div style={{ padding: '16px', borderRadius: '12px', background: 'var(--color-danger-bg)', border: '1px solid rgba(239, 68, 68, 0.2)' }}>
44
- <h3 style={{ fontSize: '13px', fontWeight: 500, color: 'var(--color-danger)', marginBottom: '12px', display: 'flex', alignItems: 'center', gap: '8px' }}>
45
- <XIcon style={{ width: '16px', height: '16px' }} />
46
- When not to use
47
- </h3>
48
- <ul style={{ listStyle: 'none', padding: 0, margin: 0, display: 'flex', flexDirection: 'column', gap: '8px' }}>
49
- {usage.whenNot!.map((item, index) => (
50
- <li key={index} style={{ fontSize: '13px', color: 'var(--text-primary)', lineHeight: 1.6, display: 'flex', alignItems: 'flex-start', gap: '8px' }}>
51
- <span style={{ color: 'var(--color-danger)', marginTop: '6px', fontSize: '12px' }}>&#8226;</span>
52
- <span>{item}</span>
53
- </li>
54
- ))}
55
- </ul>
56
- </div>
57
- )}
58
- </div>
59
- )}
60
-
61
- {/* Guidelines */}
62
- {hasGuidelines && (
63
- <div style={{ marginBottom: '24px' }}>
64
- <h3 style={{ fontSize: '13px', fontWeight: 500, color: 'var(--text-primary)', marginBottom: '12px' }}>Guidelines</h3>
65
- <ul style={{ listStyle: 'none', padding: 0, margin: 0, display: 'flex', flexDirection: 'column', gap: '8px' }}>
66
- {usage.guidelines!.map((item, index) => (
67
- <li key={index} style={{ fontSize: '13px', color: 'var(--text-secondary)', lineHeight: 1.6, display: 'flex', alignItems: 'flex-start', gap: '8px' }}>
68
- <span style={{ color: 'var(--color-accent)', marginTop: '6px', fontSize: '12px' }}>&#8226;</span>
69
- <span>{item}</span>
70
- </li>
71
- ))}
72
- </ul>
73
- </div>
74
- )}
75
-
76
- {/* Accessibility */}
77
- {hasAccessibility && (
78
- <div style={{ padding: '16px', borderRadius: '12px', border: '1px solid var(--border)', background: 'var(--bg-secondary)' }}>
79
- <h3 style={{ fontSize: '13px', fontWeight: 500, color: 'var(--text-primary)', marginBottom: '12px', display: 'flex', alignItems: 'center', gap: '8px' }}>
80
- <AccessibilityIcon style={{ width: '16px', height: '16px', color: 'var(--text-secondary)' }} />
81
- Accessibility
82
- </h3>
83
- <ul style={{ listStyle: 'none', padding: 0, margin: 0, display: 'flex', flexDirection: 'column', gap: '8px' }}>
84
- {usage.accessibility!.map((item, index) => (
85
- <li key={index} style={{ fontSize: '13px', color: 'var(--text-secondary)', lineHeight: 1.6, display: 'flex', alignItems: 'flex-start', gap: '8px' }}>
86
- <span style={{ color: 'var(--text-tertiary)', marginTop: '6px', fontSize: '12px' }}>&#8226;</span>
87
- <span>{item}</span>
88
- </li>
89
- ))}
90
- </ul>
91
- </div>
92
- )}
93
- </section>
94
- );
95
- }
@@ -1,40 +0,0 @@
1
- import { Tabs, Stack } from '@fragments-sdk/ui';
2
- import type { FragmentVariant } from '@fragments-sdk/core';
3
- import { PlayIcon } from './Icons.js';
4
-
5
- interface VariantTabsProps {
6
- variants: FragmentVariant[];
7
- activeIndex: number;
8
- onSelect: (index: number) => void;
9
- }
10
-
11
- export function VariantTabs({ variants, activeIndex, onSelect }: VariantTabsProps) {
12
- if (variants.length === 0) return null;
13
-
14
- const activeValue = variants[activeIndex]?.name ?? '';
15
-
16
- return (
17
- <Tabs
18
- value={activeValue}
19
- onValueChange={(value: string | number) => {
20
- const index = variants.findIndex(v => v.name === value);
21
- if (index >= 0) onSelect(index);
22
- }}
23
- >
24
- <Tabs.List variant="pills">
25
- {variants.map((variant) => (
26
- <Tabs.Tab key={variant.name} value={variant.name}>
27
- <Stack direction="row" align="center" gap="xs" as="span">
28
- {variant.name}
29
- {variant.hasPlayFunction && (
30
- <span style={{ display: 'inline-flex', width: '12px', height: '12px', color: 'var(--color-accent)' }}>
31
- <PlayIcon />
32
- </span>
33
- )}
34
- </Stack>
35
- </Tabs.Tab>
36
- ))}
37
- </Tabs.List>
38
- </Tabs>
39
- );
40
- }
@@ -1,172 +0,0 @@
1
- import { Button, Menu, Stack, Input, Text, Box } from '@fragments-sdk/ui';
2
- import { VIEWPORT_PRESETS, type ViewportPreset } from '../constants/ui.js';
3
- import {
4
- ViewportIcon,
5
- ChevronDownIcon,
6
- ResponsiveIcon,
7
- DesktopIcon,
8
- TabletIcon,
9
- MobileIcon,
10
- SettingsIcon,
11
- } from './Icons.js';
12
-
13
- // Re-export for consumers
14
- export type { ViewportPreset };
15
-
16
- export interface ViewportSize {
17
- width: number | null;
18
- height: number | null;
19
- }
20
-
21
- // Viewport presets with display metadata
22
- const VIEWPORT_PRESETS_UI = Object.entries(VIEWPORT_PRESETS).map(([value, config]) => ({
23
- value: value as ViewportPreset,
24
- label: config.label,
25
- width: config.width,
26
- }));
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
-
44
- interface ViewportSelectorProps {
45
- viewport: ViewportPreset;
46
- customSize: ViewportSize;
47
- onViewportChange: (viewport: ViewportPreset) => void;
48
- onCustomSizeChange: (size: ViewportSize) => void;
49
- }
50
-
51
- export function ViewportSelector({
52
- viewport,
53
- customSize,
54
- onViewportChange,
55
- onCustomSizeChange,
56
- }: ViewportSelectorProps) {
57
- const currentPreset = VIEWPORT_PRESETS_UI.find(p => p.value === viewport);
58
- const isCustomViewport = viewport === 'custom';
59
- const displayLabel = viewport === 'custom'
60
- ? `${customSize.width || '?'}\u00D7${customSize.height || 'auto'}`
61
- : currentPreset?.label || 'Responsive';
62
-
63
- return (
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
- }}
88
- >
89
- {VIEWPORT_PRESETS_UI.map((preset) => (
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>
103
- ))}
104
- </Menu.RadioGroup>
105
-
106
- <Menu.Separator />
107
-
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' }} />
115
- Custom size
116
- </Stack>
117
- </Menu.Item>
118
-
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>
164
- );
165
- }
166
-
167
- // Get viewport width from preset
168
- export function getViewportWidth(viewport: ViewportPreset, customSize: ViewportSize): number | null {
169
- if (viewport === 'custom') return customSize.width;
170
- if (viewport === 'responsive') return null;
171
- return VIEWPORT_PRESETS[viewport]?.width ?? null;
172
- }