@fragments-sdk/cli 0.10.1 → 0.12.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.
Files changed (223) hide show
  1. package/dist/ai-client-I6MDWNYA.js +21 -0
  2. package/dist/bin.js +292 -367
  3. package/dist/bin.js.map +1 -1
  4. package/dist/{chunk-PW7QTQA6.js → chunk-4OC7FTJB.js} +2 -2
  5. package/dist/{chunk-HRFUSSZI.js → chunk-AM4MRTMN.js} +2 -2
  6. package/dist/{chunk-5G3VZH43.js → chunk-GVDSFQ4E.js} +281 -351
  7. package/dist/chunk-GVDSFQ4E.js.map +1 -0
  8. package/dist/chunk-JJ2VRTBU.js +626 -0
  9. package/dist/chunk-JJ2VRTBU.js.map +1 -0
  10. package/dist/{chunk-D5PYOXEI.js → chunk-LVWFOLUZ.js} +148 -13
  11. package/dist/{chunk-D5PYOXEI.js.map → chunk-LVWFOLUZ.js.map} +1 -1
  12. package/dist/{chunk-WXSR2II7.js → chunk-OQKMEFOS.js} +58 -6
  13. package/dist/chunk-OQKMEFOS.js.map +1 -0
  14. package/dist/chunk-SXTKFDCR.js +104 -0
  15. package/dist/chunk-SXTKFDCR.js.map +1 -0
  16. package/dist/chunk-T5OMVL7E.js +443 -0
  17. package/dist/chunk-T5OMVL7E.js.map +1 -0
  18. package/dist/{chunk-ZM4ZQZWZ.js → chunk-TPWGL2XS.js} +39 -37
  19. package/dist/chunk-TPWGL2XS.js.map +1 -0
  20. package/dist/{chunk-OQO55NKV.js → chunk-WFS63PCW.js} +85 -11
  21. package/dist/chunk-WFS63PCW.js.map +1 -0
  22. package/dist/core/index.js +9 -1
  23. package/dist/{discovery-NEOY4MPN.js → discovery-ZJQSXF56.js} +3 -3
  24. package/dist/{generate-FBHSXR3D.js → generate-RJFS2JWA.js} +4 -4
  25. package/dist/index.js +7 -6
  26. package/dist/index.js.map +1 -1
  27. package/dist/init-ZSX3NRCZ.js +636 -0
  28. package/dist/init-ZSX3NRCZ.js.map +1 -0
  29. package/dist/mcp-bin.js +2 -2
  30. package/dist/{scan-CJF2DOQW.js → scan-3PMCJ4RB.js} +6 -6
  31. package/dist/scan-generate-SYU4PYZD.js +1115 -0
  32. package/dist/scan-generate-SYU4PYZD.js.map +1 -0
  33. package/dist/{service-TQYWY65E.js → service-VMGNJZ42.js} +3 -3
  34. package/dist/snapshot-XOISO2IS.js +139 -0
  35. package/dist/snapshot-XOISO2IS.js.map +1 -0
  36. package/dist/{static-viewer-NUBFPKWH.js → static-viewer-5GXH2MGE.js} +3 -3
  37. package/dist/static-viewer-5GXH2MGE.js.map +1 -0
  38. package/dist/{test-Z5LVO724.js → test-SI4NSHQX.js} +4 -4
  39. package/dist/{tokens-CE46OTMD.js → tokens-T6SIVUT5.js} +5 -5
  40. package/dist/{viewer-DNMNC5VS.js → viewer-7ZEAFBVN.js} +80 -58
  41. package/dist/viewer-7ZEAFBVN.js.map +1 -0
  42. package/package.json +6 -14
  43. package/src/ai-client.ts +156 -0
  44. package/src/bin.ts +74 -2
  45. package/src/build.ts +95 -33
  46. package/src/commands/__tests__/drift-sync.test.ts +252 -0
  47. package/src/commands/__tests__/scan-generate.test.ts +497 -45
  48. package/src/commands/enhance.ts +11 -35
  49. package/src/commands/init.ts +296 -193
  50. package/src/commands/scan-generate.ts +740 -139
  51. package/src/commands/scan.ts +37 -32
  52. package/src/commands/setup.ts +143 -52
  53. package/src/commands/snapshot.ts +197 -0
  54. package/src/commands/sync.ts +357 -0
  55. package/src/commands/validate.ts +43 -1
  56. package/src/core/component-extractor.test.ts +282 -0
  57. package/src/core/component-extractor.ts +1030 -0
  58. package/src/core/discovery.ts +93 -7
  59. package/src/service/enhance/props-extractor.ts +235 -13
  60. package/src/validators.ts +236 -0
  61. package/src/viewer/__tests__/viewer-integration.test.ts +85 -74
  62. package/src/viewer/server.ts +37 -22
  63. package/src/viewer/vite-plugin.ts +25 -9
  64. package/dist/chunk-5G3VZH43.js.map +0 -1
  65. package/dist/chunk-OQO55NKV.js.map +0 -1
  66. package/dist/chunk-WXSR2II7.js.map +0 -1
  67. package/dist/chunk-ZM4ZQZWZ.js.map +0 -1
  68. package/dist/init-NDQXUWDU.js +0 -796
  69. package/dist/init-NDQXUWDU.js.map +0 -1
  70. package/dist/scan-generate-SJAN5MVI.js +0 -691
  71. package/dist/scan-generate-SJAN5MVI.js.map +0 -1
  72. package/dist/viewer-DNMNC5VS.js.map +0 -1
  73. package/src/ai.ts +0 -266
  74. package/src/commands/init-framework.ts +0 -414
  75. package/src/mcp/bin.ts +0 -36
  76. package/src/migrate/bin.ts +0 -114
  77. package/src/theme/index.ts +0 -77
  78. package/src/viewer/__tests__/a11y-fixes.test.ts +0 -358
  79. package/src/viewer/__tests__/jsx-parser.test.ts +0 -502
  80. package/src/viewer/__tests__/render-utils.test.ts +0 -232
  81. package/src/viewer/__tests__/style-utils.test.ts +0 -404
  82. package/src/viewer/assets/fragments-logo.ts +0 -4
  83. package/src/viewer/assets/fragments_logo.png +0 -0
  84. package/src/viewer/bin.ts +0 -86
  85. package/src/viewer/cli/health.ts +0 -256
  86. package/src/viewer/cli/index.ts +0 -33
  87. package/src/viewer/cli/scan.ts +0 -124
  88. package/src/viewer/cli/utils.ts +0 -174
  89. package/src/viewer/components/AccessibilityPanel.tsx +0 -1457
  90. package/src/viewer/components/ActionCapture.tsx +0 -172
  91. package/src/viewer/components/ActionsPanel.tsx +0 -332
  92. package/src/viewer/components/AllVariantsPreview.tsx +0 -78
  93. package/src/viewer/components/App.tsx +0 -582
  94. package/src/viewer/components/BottomPanel.tsx +0 -288
  95. package/src/viewer/components/CodePanel.naming.test.tsx +0 -59
  96. package/src/viewer/components/CodePanel.tsx +0 -118
  97. package/src/viewer/components/CommandPalette.tsx +0 -392
  98. package/src/viewer/components/ComponentDocView.tsx +0 -164
  99. package/src/viewer/components/ComponentGraph.tsx +0 -380
  100. package/src/viewer/components/ComponentHeader.tsx +0 -88
  101. package/src/viewer/components/ContractPanel.tsx +0 -241
  102. package/src/viewer/components/EmptyVariantMessage.tsx +0 -54
  103. package/src/viewer/components/ErrorBoundary.tsx +0 -97
  104. package/src/viewer/components/FigmaEmbed.tsx +0 -238
  105. package/src/viewer/components/FragmentEditor.tsx +0 -525
  106. package/src/viewer/components/FragmentRenderer.tsx +0 -61
  107. package/src/viewer/components/HeaderSearch.tsx +0 -24
  108. package/src/viewer/components/HealthDashboard.tsx +0 -441
  109. package/src/viewer/components/HmrStatusIndicator.tsx +0 -61
  110. package/src/viewer/components/Icons.tsx +0 -479
  111. package/src/viewer/components/InteractionsPanel.tsx +0 -757
  112. package/src/viewer/components/IsolatedPreviewFrame.tsx +0 -346
  113. package/src/viewer/components/IsolatedRender.tsx +0 -113
  114. package/src/viewer/components/KeyboardShortcutsHelp.tsx +0 -53
  115. package/src/viewer/components/LandingPage.tsx +0 -421
  116. package/src/viewer/components/Layout.tsx +0 -27
  117. package/src/viewer/components/LeftSidebar.tsx +0 -472
  118. package/src/viewer/components/LoadErrorMessage.tsx +0 -102
  119. package/src/viewer/components/MultiViewportPreview.tsx +0 -522
  120. package/src/viewer/components/NoVariantsMessage.tsx +0 -59
  121. package/src/viewer/components/PanelShell.tsx +0 -161
  122. package/src/viewer/components/PerformancePanel.tsx +0 -304
  123. package/src/viewer/components/PreviewArea.tsx +0 -472
  124. package/src/viewer/components/PreviewAside.tsx +0 -168
  125. package/src/viewer/components/PreviewFrameHost.tsx +0 -303
  126. package/src/viewer/components/PreviewPane.tsx +0 -149
  127. package/src/viewer/components/PreviewToolbar.tsx +0 -80
  128. package/src/viewer/components/PropsEditor.tsx +0 -506
  129. package/src/viewer/components/PropsTable.tsx +0 -111
  130. package/src/viewer/components/RelationsSection.tsx +0 -88
  131. package/src/viewer/components/ResizablePanel.tsx +0 -271
  132. package/src/viewer/components/RightSidebar.tsx +0 -102
  133. package/src/viewer/components/RuntimeToolsRegistrar.tsx +0 -17
  134. package/src/viewer/components/ScreenshotButton.tsx +0 -90
  135. package/src/viewer/components/Sidebar.tsx +0 -169
  136. package/src/viewer/components/SkeletonLoader.tsx +0 -161
  137. package/src/viewer/components/ThemeProvider.tsx +0 -42
  138. package/src/viewer/components/Toast.tsx +0 -3
  139. package/src/viewer/components/TokenStylePanel.tsx +0 -699
  140. package/src/viewer/components/TopToolbar.tsx +0 -159
  141. package/src/viewer/components/UsageSection.tsx +0 -95
  142. package/src/viewer/components/VariantMatrix.tsx +0 -388
  143. package/src/viewer/components/VariantRenderer.tsx +0 -131
  144. package/src/viewer/components/VariantTabs.tsx +0 -40
  145. package/src/viewer/components/ViewerHeader.tsx +0 -69
  146. package/src/viewer/components/ViewerStateSync.tsx +0 -52
  147. package/src/viewer/components/ViewportSelector.tsx +0 -172
  148. package/src/viewer/components/WebMCPDevTools.tsx +0 -503
  149. package/src/viewer/components/WebMCPIntegration.tsx +0 -47
  150. package/src/viewer/components/WebMCPStatusIndicator.tsx +0 -60
  151. package/src/viewer/components/_future/CreatePage.tsx +0 -836
  152. package/src/viewer/components/viewer-utils.ts +0 -16
  153. package/src/viewer/composition-renderer.ts +0 -381
  154. package/src/viewer/constants/index.ts +0 -1
  155. package/src/viewer/constants/ui.ts +0 -166
  156. package/src/viewer/entry.tsx +0 -335
  157. package/src/viewer/hooks/index.ts +0 -2
  158. package/src/viewer/hooks/useA11yCache.ts +0 -383
  159. package/src/viewer/hooks/useA11yService.ts +0 -364
  160. package/src/viewer/hooks/useActions.ts +0 -138
  161. package/src/viewer/hooks/useAppState.ts +0 -147
  162. package/src/viewer/hooks/useCompiledFragments.ts +0 -42
  163. package/src/viewer/hooks/useFigmaIntegration.ts +0 -132
  164. package/src/viewer/hooks/useHmrStatus.ts +0 -109
  165. package/src/viewer/hooks/useKeyboardShortcuts.ts +0 -270
  166. package/src/viewer/hooks/usePreviewBridge.ts +0 -347
  167. package/src/viewer/hooks/useScrollSpy.ts +0 -78
  168. package/src/viewer/hooks/useUrlState.ts +0 -318
  169. package/src/viewer/hooks/useViewSettings.ts +0 -111
  170. package/src/viewer/index.html +0 -28
  171. package/src/viewer/intelligence/healthReport.ts +0 -505
  172. package/src/viewer/intelligence/styleDrift.ts +0 -340
  173. package/src/viewer/intelligence/usageScanner.ts +0 -309
  174. package/src/viewer/jsx-parser.ts +0 -486
  175. package/src/viewer/preview-frame-entry.tsx +0 -25
  176. package/src/viewer/preview-frame.html +0 -125
  177. package/src/viewer/public/favicon.ico +0 -0
  178. package/src/viewer/render-template.html +0 -68
  179. package/src/viewer/styles/globals.css +0 -278
  180. package/src/viewer/types/a11y.ts +0 -197
  181. package/src/viewer/utils/a11y-fixes.ts +0 -509
  182. package/src/viewer/utils/actionExport.ts +0 -372
  183. package/src/viewer/utils/colorSchemes.ts +0 -201
  184. package/src/viewer/utils/detectRelationships.ts +0 -256
  185. package/src/viewer/vendor/shared/src/ComponentDocContent.module.scss +0 -10
  186. package/src/viewer/vendor/shared/src/ComponentDocContent.module.scss.d.ts +0 -2
  187. package/src/viewer/vendor/shared/src/ComponentDocContent.tsx +0 -274
  188. package/src/viewer/vendor/shared/src/DocsHeaderBar.tsx +0 -129
  189. package/src/viewer/vendor/shared/src/DocsPageAsideHost.tsx +0 -89
  190. package/src/viewer/vendor/shared/src/DocsPageShell.tsx +0 -124
  191. package/src/viewer/vendor/shared/src/DocsSearchCommand.tsx +0 -99
  192. package/src/viewer/vendor/shared/src/DocsSidebarNav.tsx +0 -66
  193. package/src/viewer/vendor/shared/src/PropsTable.module.scss +0 -68
  194. package/src/viewer/vendor/shared/src/PropsTable.module.scss.d.ts +0 -2
  195. package/src/viewer/vendor/shared/src/PropsTable.tsx +0 -76
  196. package/src/viewer/vendor/shared/src/VariantPreviewCard.module.scss +0 -114
  197. package/src/viewer/vendor/shared/src/VariantPreviewCard.module.scss.d.ts +0 -2
  198. package/src/viewer/vendor/shared/src/VariantPreviewCard.tsx +0 -137
  199. package/src/viewer/vendor/shared/src/docs-data/index.ts +0 -32
  200. package/src/viewer/vendor/shared/src/docs-data/mcp-configs.ts +0 -72
  201. package/src/viewer/vendor/shared/src/docs-data/palettes.ts +0 -75
  202. package/src/viewer/vendor/shared/src/docs-data/setup-examples.ts +0 -55
  203. package/src/viewer/vendor/shared/src/docs-layout.scss +0 -28
  204. package/src/viewer/vendor/shared/src/docs-layout.scss.d.ts +0 -2
  205. package/src/viewer/vendor/shared/src/index.ts +0 -34
  206. package/src/viewer/vendor/shared/src/types.ts +0 -53
  207. package/src/viewer/webmcp/__tests__/analytics.test.ts +0 -108
  208. package/src/viewer/webmcp/analytics.ts +0 -165
  209. package/src/viewer/webmcp/index.ts +0 -3
  210. package/src/viewer/webmcp/posthog-bridge.ts +0 -39
  211. package/src/viewer/webmcp/runtime-tools.ts +0 -152
  212. package/src/viewer/webmcp/scan-utils.ts +0 -135
  213. package/src/viewer/webmcp/use-tool-analytics.ts +0 -69
  214. package/src/viewer/webmcp/viewer-state.ts +0 -45
  215. /package/dist/{discovery-NEOY4MPN.js.map → ai-client-I6MDWNYA.js.map} +0 -0
  216. /package/dist/{chunk-PW7QTQA6.js.map → chunk-4OC7FTJB.js.map} +0 -0
  217. /package/dist/{chunk-HRFUSSZI.js.map → chunk-AM4MRTMN.js.map} +0 -0
  218. /package/dist/{scan-CJF2DOQW.js.map → discovery-ZJQSXF56.js.map} +0 -0
  219. /package/dist/{generate-FBHSXR3D.js.map → generate-RJFS2JWA.js.map} +0 -0
  220. /package/dist/{service-TQYWY65E.js.map → scan-3PMCJ4RB.js.map} +0 -0
  221. /package/dist/{static-viewer-NUBFPKWH.js.map → service-VMGNJZ42.js.map} +0 -0
  222. /package/dist/{test-Z5LVO724.js.map → test-SI4NSHQX.js.map} +0 -0
  223. /package/dist/{tokens-CE46OTMD.js.map → tokens-T6SIVUT5.js.map} +0 -0
@@ -1,159 +0,0 @@
1
- import type { RefObject } from "react";
2
- import type { FragmentDefinition } from "../../core/index.js";
3
- import type { useViewSettings } from "../hooks/useViewSettings.js";
4
- import type { useAppState } from "../hooks/useAppState.js";
5
- import {
6
- Header,
7
- Stack,
8
- Text,
9
- Separator,
10
- Tooltip,
11
- Button,
12
- ThemeToggle,
13
- FragmentsLogo,
14
- } from "@fragments-sdk/ui";
15
- import { DeviceMobile, GridFour, SidebarSimple } from "@phosphor-icons/react";
16
- import { GitHubIcon, FigmaIcon, CompareIcon } from "./Icons.js";
17
- import { PreviewToolbar } from "./PreviewToolbar.js";
18
- import { HeaderSearch } from "./HeaderSearch.js";
19
- import { useTheme } from "./ThemeProvider.js";
20
- import { WebMCPStatusIndicator } from "./WebMCPStatusIndicator.js";
21
-
22
- /** Normalize category to Title Case for display */
23
- function titleCase(str: string): string {
24
- return str.replace(/\b\w/g, (c) => c.toUpperCase());
25
- }
26
-
27
- interface TopToolbarProps {
28
- fragment: { path: string; fragment: FragmentDefinition };
29
- viewSettings: ReturnType<typeof useViewSettings>;
30
- uiState: ReturnType<typeof useAppState>["state"];
31
- uiActions: ReturnType<typeof useAppState>["actions"];
32
- figmaUrl?: string;
33
- searchQuery: string;
34
- onSearchChange: (value: string) => void;
35
- searchInputRef: RefObject<HTMLInputElement>;
36
- }
37
-
38
- export function TopToolbar({
39
- fragment,
40
- viewSettings,
41
- uiState,
42
- uiActions,
43
- figmaUrl,
44
- searchQuery,
45
- onSearchChange,
46
- searchInputRef,
47
- }: TopToolbarProps) {
48
- const { setTheme, resolvedTheme } = useTheme();
49
- return (
50
- <Header aria-label="Component preview toolbar">
51
- <Header.Trigger />
52
- <Header.Brand>
53
- <Stack direction="row" align="center" gap="sm">
54
- <FragmentsLogo size={20} />
55
- <Text weight="medium" size="sm">
56
- {fragment.fragment.meta.name}
57
- </Text>
58
- <Text size="xs" color="tertiary">
59
- {titleCase(fragment.fragment.meta.category || '')}
60
- </Text>
61
- </Stack>
62
- </Header.Brand>
63
- <HeaderSearch value={searchQuery} onChange={onSearchChange} inputRef={searchInputRef} />
64
- <Header.Spacer />
65
- <Header.Actions>
66
- <PreviewToolbar zoom={viewSettings.zoom} onZoomChange={viewSettings.setZoom} />
67
- <Separator orientation="vertical" style={{ height: "16px" }} />
68
- <Tooltip content={uiState.showMatrixView ? "Disable matrix view" : "Enable matrix view"}>
69
- <Button
70
- variant={uiState.showMatrixView ? "secondary" : "ghost"}
71
- size="sm"
72
- icon
73
- aria-pressed={uiState.showMatrixView}
74
- aria-label="Toggle matrix view"
75
- onClick={() => uiActions.setMatrixView(!uiState.showMatrixView)}
76
- >
77
- <GridFour size={16} />
78
- </Button>
79
- </Tooltip>
80
- <Tooltip
81
- content={uiState.showMultiViewport ? "Disable responsive view" : "Enable responsive view"}
82
- >
83
- <Button
84
- variant={uiState.showMultiViewport ? "secondary" : "ghost"}
85
- size="sm"
86
- icon
87
- aria-pressed={uiState.showMultiViewport}
88
- aria-label="Toggle responsive view"
89
- onClick={() => uiActions.setMultiViewport(!uiState.showMultiViewport)}
90
- >
91
- <DeviceMobile size={16} />
92
- </Button>
93
- </Tooltip>
94
- <Separator orientation="vertical" style={{ height: "16px" }} />
95
- {figmaUrl && (
96
- <>
97
- <Tooltip
98
- content={
99
- uiState.showComparison ? "Hide Figma comparison" : "Compare with Figma design"
100
- }
101
- >
102
- <Button
103
- variant={uiState.showComparison ? "secondary" : "ghost"}
104
- size="sm"
105
- icon
106
- onClick={uiActions.toggleComparison}
107
- >
108
- <CompareIcon style={{ width: "16px", height: "16px" }} />
109
- </Button>
110
- </Tooltip>
111
- <Tooltip content="View in Figma">
112
- <Button
113
- onClick={() => window.open(figmaUrl, "_blank", "noopener,noreferrer")}
114
- variant="ghost"
115
- size="sm"
116
- icon
117
- >
118
- <FigmaIcon style={{ width: "16px", height: "16px" }} />
119
- </Button>
120
- </Tooltip>
121
- <Separator orientation="vertical" style={{ height: "16px" }} />
122
- </>
123
- )}
124
- <WebMCPStatusIndicator />
125
- <Tooltip content={uiState.showAside ? "Hide side panel" : "Show side panel"}>
126
- <Button
127
- variant={uiState.showAside ? "secondary" : "ghost"}
128
- size="sm"
129
- icon
130
- aria-pressed={uiState.showAside}
131
- aria-label="Toggle side panel"
132
- onClick={uiActions.toggleAside}
133
- >
134
- <SidebarSimple size={16} style={{ transform: "scaleX(-1)" }} />
135
- </Button>
136
- </Tooltip>
137
- <Separator orientation="vertical" style={{ height: "16px" }} />
138
- <ThemeToggle
139
- size="sm"
140
- value={resolvedTheme}
141
- onValueChange={(value) => setTheme(value)}
142
- aria-label={`Theme: ${resolvedTheme}`}
143
- />
144
- <Button
145
- as="a"
146
- variant="ghost"
147
- size="sm"
148
- icon
149
- href="https://github.com/ConanMcN/fragments"
150
- target="_blank"
151
- rel="noopener noreferrer"
152
- aria-label="View on GitHub"
153
- >
154
- <GitHubIcon />
155
- </Button>
156
- </Header.Actions>
157
- </Header>
158
- );
159
- }
@@ -1,95 +0,0 @@
1
- import type { FragmentUsage } from '../../core/index.js';
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,388 +0,0 @@
1
- // @ts-nocheck
2
- /**
3
- * Variant Matrix View - Display all variants in a grid
4
- *
5
- * Shows all variants of a component simultaneously, making it easy to:
6
- * - Compare states/variants at a glance
7
- * - Spot visual inconsistencies
8
- * - Review all component states quickly
9
- *
10
- * Uses virtualization to only render visible variants for better performance.
11
- */
12
-
13
- import { useState, useMemo, useRef, useCallback } from "react";
14
- import { useVirtualizer } from "@tanstack/react-virtual";
15
- import { Box, Stack, Text, Button, Badge, EmptyState } from "@fragments-sdk/ui";
16
- import type { FragmentVariant } from "../../core/index.js";
17
- import { ErrorBoundary } from "./ErrorBoundary.js";
18
- import { FragmentRenderer, LoaderIndicator } from "./FragmentRenderer.js";
19
- import { IsolatedPreviewFrame } from "./IsolatedPreviewFrame.js";
20
- import { ChevronDownIcon } from "./Icons.js";
21
-
22
- interface VariantMatrixProps {
23
- /** All variants to display */
24
- variants: FragmentVariant[];
25
- /** Component name for error display */
26
- componentName: string;
27
- /** Fragment path for iframe rendering */
28
- fragmentPath: string;
29
- /** Current zoom level */
30
- zoom: number;
31
- /** Preview theme */
32
- previewTheme: "light" | "dark";
33
- /** Whether to use iframe isolation */
34
- useIframeIsolation?: boolean;
35
- /** Callback when a variant is clicked to focus on it */
36
- onSelectVariant?: (index: number) => void;
37
- }
38
-
39
- type GridSize = "small" | "medium" | "large";
40
-
41
- interface GridConfig {
42
- gridTemplateColumns: string;
43
- minHeight: string;
44
- heightPx: number; // For virtualization
45
- scale: number;
46
- colCount: number; // Default column count for virtualization
47
- }
48
-
49
- const GRID_SIZES: Record<GridSize, GridConfig> = {
50
- small: { gridTemplateColumns: "repeat(4, 1fr)", minHeight: "150px", heightPx: 150, scale: 0.5, colCount: 4 },
51
- medium: { gridTemplateColumns: "repeat(3, 1fr)", minHeight: "200px", heightPx: 200, scale: 0.75, colCount: 3 },
52
- large: { gridTemplateColumns: "repeat(2, 1fr)", minHeight: "300px", heightPx: 300, scale: 1, colCount: 2 },
53
- };
54
-
55
- /** Threshold for enabling virtualization */
56
- const VIRTUALIZATION_THRESHOLD = 12;
57
-
58
- export function VariantMatrix({
59
- variants,
60
- componentName,
61
- fragmentPath,
62
- zoom,
63
- previewTheme,
64
- useIframeIsolation = true,
65
- onSelectVariant,
66
- }: VariantMatrixProps) {
67
- const [gridSize, setGridSize] = useState<GridSize>("medium");
68
- const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
69
- const scrollRef = useRef<HTMLDivElement>(null);
70
-
71
- const gridConfig = GRID_SIZES[gridSize];
72
- const effectiveScale = (zoom / 100) * gridConfig.scale;
73
-
74
- // Determine if we should use virtualization
75
- const useVirtualization = variants.length > VIRTUALIZATION_THRESHOLD;
76
-
77
- // Calculate number of rows for virtualization
78
- const columns = gridConfig.colCount;
79
- const rowCount = Math.ceil(variants.length / columns);
80
-
81
- // Row height includes card height + gap
82
- const rowHeight = gridConfig.heightPx + 16; // 16px gap
83
-
84
- const rowVirtualizer = useVirtualizer({
85
- count: rowCount,
86
- getScrollElement: () => scrollRef.current,
87
- estimateSize: () => rowHeight,
88
- overscan: 2, // Render 2 extra rows above/below for smooth scrolling
89
- });
90
-
91
- if (variants.length === 0) {
92
- return (
93
- <EmptyState style={{ height: '100%' }}>
94
- <EmptyState.Title>No variants to display</EmptyState.Title>
95
- </EmptyState>
96
- );
97
- }
98
-
99
- return (
100
- <Stack style={{ height: '100%' }}>
101
- {/* Toolbar */}
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>
126
-
127
- {/* Grid - Virtualized or Regular */}
128
- {useVirtualization ? (
129
- <Box ref={scrollRef} overflow="auto" padding="md" style={{ flex: 1 }}>
130
- <div
131
- style={{
132
- height: `${rowVirtualizer.getTotalSize()}px`,
133
- width: "100%",
134
- position: "relative",
135
- }}
136
- >
137
- {rowVirtualizer.getVirtualItems().map((virtualRow) => {
138
- const startIndex = virtualRow.index * columns;
139
- const rowVariants = variants.slice(startIndex, startIndex + columns);
140
-
141
- return (
142
- <div
143
- key={virtualRow.key}
144
- style={{
145
- position: "absolute",
146
- top: 0,
147
- left: 0,
148
- width: "100%",
149
- height: `${virtualRow.size}px`,
150
- transform: `translateY(${virtualRow.start}px)`,
151
- }}
152
- >
153
- <div style={{
154
- display: 'grid',
155
- gap: 16,
156
- gridTemplateColumns: gridConfig.gridTemplateColumns,
157
- height: gridConfig.minHeight,
158
- }}>
159
- {rowVariants.map((variant, colIndex) => {
160
- const index = startIndex + colIndex;
161
- return (
162
- <VariantCard
163
- key={variant.name}
164
- variant={variant}
165
- index={index}
166
- componentName={componentName}
167
- fragmentPath={fragmentPath}
168
- scale={effectiveScale}
169
- minHeight={gridConfig.minHeight}
170
- previewTheme={previewTheme}
171
- useIframeIsolation={useIframeIsolation}
172
- isHovered={hoveredIndex === index}
173
- onHover={() => setHoveredIndex(index)}
174
- onLeave={() => setHoveredIndex(null)}
175
- onClick={() => onSelectVariant?.(index)}
176
- />
177
- );
178
- })}
179
- </div>
180
- </div>
181
- );
182
- })}
183
- </div>
184
- </Box>
185
- ) : (
186
- <Box overflow="auto" padding="md" style={{ flex: 1 }}>
187
- <div style={{
188
- display: 'grid',
189
- gap: 16,
190
- gridTemplateColumns: gridConfig.gridTemplateColumns,
191
- }}>
192
- {variants.map((variant, index) => (
193
- <VariantCard
194
- key={variant.name}
195
- variant={variant}
196
- index={index}
197
- componentName={componentName}
198
- fragmentPath={fragmentPath}
199
- scale={effectiveScale}
200
- minHeight={gridConfig.minHeight}
201
- previewTheme={previewTheme}
202
- useIframeIsolation={useIframeIsolation}
203
- isHovered={hoveredIndex === index}
204
- onHover={() => setHoveredIndex(index)}
205
- onLeave={() => setHoveredIndex(null)}
206
- onClick={() => onSelectVariant?.(index)}
207
- />
208
- ))}
209
- </div>
210
- </Box>
211
- )}
212
- </Stack>
213
- );
214
- }
215
-
216
- interface VariantCardProps {
217
- variant: FragmentVariant;
218
- index: number;
219
- componentName: string;
220
- fragmentPath: string;
221
- scale: number;
222
- minHeight: string;
223
- previewTheme: "light" | "dark";
224
- useIframeIsolation: boolean;
225
- isHovered: boolean;
226
- onHover: () => void;
227
- onLeave: () => void;
228
- onClick: () => void;
229
- }
230
-
231
- function VariantCard({
232
- variant,
233
- index,
234
- componentName,
235
- fragmentPath,
236
- scale,
237
- minHeight,
238
- previewTheme,
239
- useIframeIsolation,
240
- isHovered,
241
- onHover,
242
- onLeave,
243
- onClick,
244
- }: VariantCardProps) {
245
- return (
246
- <div
247
- style={{
248
- position: 'relative',
249
- borderRadius: 8,
250
- border: isHovered
251
- ? '2px solid #3b82f6'
252
- : '1px solid var(--border)',
253
- overflow: 'hidden',
254
- transition: 'border-color 0.2s, box-shadow 0.2s',
255
- cursor: 'pointer',
256
- minHeight,
257
- boxShadow: isHovered
258
- ? '0 10px 15px -3px rgba(0,0,0,0.1), 0 0 0 3px rgba(59,130,246,0.2)'
259
- : 'none',
260
- }}
261
- onMouseEnter={onHover}
262
- onMouseLeave={onLeave}
263
- onClick={onClick}
264
- >
265
- {/* Header overlay - keep inline styles (CSS art) */}
266
- <div style={{
267
- position: 'absolute',
268
- top: 0,
269
- left: 0,
270
- right: 0,
271
- zIndex: 10,
272
- padding: '4px 8px',
273
- background: 'linear-gradient(to bottom, rgba(0,0,0,0.6), transparent)',
274
- }}>
275
- <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
276
- <span style={{
277
- fontSize: 12,
278
- fontWeight: 500,
279
- color: '#ffffff',
280
- overflow: 'hidden',
281
- textOverflow: 'ellipsis',
282
- whiteSpace: 'nowrap',
283
- }}>
284
- {variant.name}
285
- </span>
286
- <span style={{ fontSize: 10, color: 'rgba(255,255,255,0.7)' }}>
287
- #{index + 1}
288
- </span>
289
- </div>
290
- </div>
291
-
292
- {/* Click to view overlay */}
293
- <div
294
- style={{
295
- position: 'absolute',
296
- inset: 0,
297
- zIndex: 10,
298
- display: 'flex',
299
- alignItems: 'center',
300
- justifyContent: 'center',
301
- background: 'rgba(0,0,0,0.4)',
302
- transition: 'opacity 0.2s',
303
- opacity: isHovered ? 1 : 0,
304
- pointerEvents: isHovered ? 'auto' : 'none',
305
- }}
306
- >
307
- <span style={{
308
- padding: '6px 12px',
309
- background: '#2563eb',
310
- color: '#ffffff',
311
- fontSize: 12,
312
- fontWeight: 500,
313
- borderRadius: 9999,
314
- boxShadow: '0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -4px rgba(0,0,0,0.1)',
315
- }}>
316
- Click to focus
317
- </span>
318
- </div>
319
-
320
- {/* Preview content */}
321
- <div
322
- data-theme={previewTheme}
323
- style={{
324
- height: '100%',
325
- width: '100%',
326
- overflow: 'hidden',
327
- display: 'flex',
328
- alignItems: 'center',
329
- justifyContent: 'center',
330
- }}
331
- >
332
- {useIframeIsolation ? (
333
- <IsolatedPreviewFrame
334
- fragmentPath={fragmentPath}
335
- variantName={variant.name}
336
- theme={previewTheme}
337
- width="100%"
338
- height="100%"
339
- minHeight={minHeight}
340
- />
341
- ) : (
342
- <div
343
- style={{
344
- padding: 16,
345
- transform: `scale(${scale})`,
346
- }}
347
- >
348
- <ErrorBoundary
349
- componentName={componentName}
350
- fallback={
351
- <Text size="xs" style={{ color: 'var(--color-danger)' }}>
352
- Error rendering variant
353
- </Text>
354
- }
355
- >
356
- <FragmentRenderer variant={variant}>
357
- {(content, isLoading, error) => {
358
- if (isLoading) {
359
- return (
360
- <Stack align="center" justify="center" style={{ padding: 16 }}>
361
- <LoaderIndicator />
362
- </Stack>
363
- );
364
- }
365
- if (error) {
366
- return (
367
- <Text size="xs" style={{ color: 'var(--color-danger)', padding: 8 }}>
368
- {error.message}
369
- </Text>
370
- );
371
- }
372
- return content;
373
- }}
374
- </FragmentRenderer>
375
- </ErrorBoundary>
376
- </div>
377
- )}
378
- </div>
379
-
380
- {/* Tags/badges */}
381
- {variant.hasPlayFunction && (
382
- <div style={{ position: 'absolute', bottom: 8, right: 8, zIndex: 10 }}>
383
- <Badge variant="info" size="sm">play</Badge>
384
- </div>
385
- )}
386
- </div>
387
- );
388
- }