@fragments-sdk/cli 0.2.2

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 (259) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +106 -0
  3. package/dist/bin.d.ts +1 -0
  4. package/dist/bin.js +4783 -0
  5. package/dist/bin.js.map +1 -0
  6. package/dist/chunk-4FDQSGKX.js +786 -0
  7. package/dist/chunk-4FDQSGKX.js.map +1 -0
  8. package/dist/chunk-7H2MMGYG.js +369 -0
  9. package/dist/chunk-7H2MMGYG.js.map +1 -0
  10. package/dist/chunk-BSCG3IP7.js +619 -0
  11. package/dist/chunk-BSCG3IP7.js.map +1 -0
  12. package/dist/chunk-LY2CFFPY.js +898 -0
  13. package/dist/chunk-LY2CFFPY.js.map +1 -0
  14. package/dist/chunk-MUZ6CM66.js +6636 -0
  15. package/dist/chunk-MUZ6CM66.js.map +1 -0
  16. package/dist/chunk-OAENNG3G.js +1489 -0
  17. package/dist/chunk-OAENNG3G.js.map +1 -0
  18. package/dist/chunk-XHNKNI6J.js +235 -0
  19. package/dist/chunk-XHNKNI6J.js.map +1 -0
  20. package/dist/core-DWKLGY4N.js +68 -0
  21. package/dist/core-DWKLGY4N.js.map +1 -0
  22. package/dist/generate-4LQNJ7SX.js +249 -0
  23. package/dist/generate-4LQNJ7SX.js.map +1 -0
  24. package/dist/index.d.ts +775 -0
  25. package/dist/index.js +41 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/init-EMVI47QG.js +416 -0
  28. package/dist/init-EMVI47QG.js.map +1 -0
  29. package/dist/mcp-bin.d.ts +1 -0
  30. package/dist/mcp-bin.js +1117 -0
  31. package/dist/mcp-bin.js.map +1 -0
  32. package/dist/scan-4YPRF7FV.js +12 -0
  33. package/dist/scan-4YPRF7FV.js.map +1 -0
  34. package/dist/service-QSZMZJBJ.js +208 -0
  35. package/dist/service-QSZMZJBJ.js.map +1 -0
  36. package/dist/static-viewer-MIPGZ4Z7.js +12 -0
  37. package/dist/static-viewer-MIPGZ4Z7.js.map +1 -0
  38. package/dist/test-SQ5ZHXWU.js +1067 -0
  39. package/dist/test-SQ5ZHXWU.js.map +1 -0
  40. package/dist/tokens-HSGMYK64.js +173 -0
  41. package/dist/tokens-HSGMYK64.js.map +1 -0
  42. package/dist/viewer-YRF4SQE4.js +11101 -0
  43. package/dist/viewer-YRF4SQE4.js.map +1 -0
  44. package/package.json +107 -0
  45. package/src/ai.ts +266 -0
  46. package/src/analyze.ts +265 -0
  47. package/src/bin.ts +916 -0
  48. package/src/build.ts +248 -0
  49. package/src/commands/a11y.ts +302 -0
  50. package/src/commands/add.ts +313 -0
  51. package/src/commands/audit.ts +195 -0
  52. package/src/commands/baseline.ts +221 -0
  53. package/src/commands/build.ts +144 -0
  54. package/src/commands/compare.ts +337 -0
  55. package/src/commands/context.ts +107 -0
  56. package/src/commands/dev.ts +107 -0
  57. package/src/commands/enhance.ts +858 -0
  58. package/src/commands/generate.ts +391 -0
  59. package/src/commands/init.ts +531 -0
  60. package/src/commands/link/figma.ts +645 -0
  61. package/src/commands/link/index.ts +10 -0
  62. package/src/commands/link/storybook.ts +267 -0
  63. package/src/commands/list.ts +49 -0
  64. package/src/commands/metrics.ts +114 -0
  65. package/src/commands/reset.ts +242 -0
  66. package/src/commands/scan.ts +537 -0
  67. package/src/commands/storygen.ts +207 -0
  68. package/src/commands/tokens.ts +251 -0
  69. package/src/commands/validate.ts +93 -0
  70. package/src/commands/verify.ts +215 -0
  71. package/src/core/composition.test.ts +262 -0
  72. package/src/core/composition.ts +255 -0
  73. package/src/core/config.ts +84 -0
  74. package/src/core/constants.ts +111 -0
  75. package/src/core/context.ts +380 -0
  76. package/src/core/defineSegment.ts +137 -0
  77. package/src/core/discovery.ts +337 -0
  78. package/src/core/figma.ts +263 -0
  79. package/src/core/fragment-types.ts +214 -0
  80. package/src/core/generators/context.ts +389 -0
  81. package/src/core/generators/index.ts +23 -0
  82. package/src/core/generators/registry.ts +364 -0
  83. package/src/core/generators/typescript-extractor.ts +374 -0
  84. package/src/core/importAnalyzer.ts +217 -0
  85. package/src/core/index.ts +149 -0
  86. package/src/core/loader.ts +155 -0
  87. package/src/core/node.ts +63 -0
  88. package/src/core/parser.ts +551 -0
  89. package/src/core/previewLoader.ts +172 -0
  90. package/src/core/schema/fragment.schema.json +189 -0
  91. package/src/core/schema/registry.schema.json +137 -0
  92. package/src/core/schema.ts +182 -0
  93. package/src/core/storyAdapter.test.ts +571 -0
  94. package/src/core/storyAdapter.ts +761 -0
  95. package/src/core/token-types.ts +287 -0
  96. package/src/core/types.ts +754 -0
  97. package/src/diff.ts +323 -0
  98. package/src/index.ts +43 -0
  99. package/src/mcp/__tests__/projectFields.test.ts +130 -0
  100. package/src/mcp/bin.ts +36 -0
  101. package/src/mcp/index.ts +8 -0
  102. package/src/mcp/server.ts +1310 -0
  103. package/src/mcp/utils.ts +54 -0
  104. package/src/mcp-bin.ts +36 -0
  105. package/src/migrate/__tests__/argTypes/argTypes.test.ts +189 -0
  106. package/src/migrate/__tests__/args/args.test.ts +452 -0
  107. package/src/migrate/__tests__/meta/meta.test.ts +198 -0
  108. package/src/migrate/__tests__/stories/stories.test.ts +278 -0
  109. package/src/migrate/__tests__/utils/utils.test.ts +371 -0
  110. package/src/migrate/__tests__/values/values.test.ts +303 -0
  111. package/src/migrate/bin.ts +108 -0
  112. package/src/migrate/converter.ts +658 -0
  113. package/src/migrate/detect.ts +196 -0
  114. package/src/migrate/index.ts +45 -0
  115. package/src/migrate/migrate.ts +163 -0
  116. package/src/migrate/parser.ts +1136 -0
  117. package/src/migrate/report.ts +624 -0
  118. package/src/migrate/types.ts +169 -0
  119. package/src/screenshot.ts +249 -0
  120. package/src/service/__tests__/ast-utils.test.ts +426 -0
  121. package/src/service/__tests__/enhance-scanner.test.ts +200 -0
  122. package/src/service/__tests__/figma/figma.test.ts +652 -0
  123. package/src/service/__tests__/metrics-store.test.ts +409 -0
  124. package/src/service/__tests__/patch-generator.test.ts +186 -0
  125. package/src/service/__tests__/props-extractor.test.ts +365 -0
  126. package/src/service/__tests__/token-registry.test.ts +267 -0
  127. package/src/service/analytics.ts +659 -0
  128. package/src/service/ast-utils.ts +444 -0
  129. package/src/service/browser-pool.ts +339 -0
  130. package/src/service/capture.ts +267 -0
  131. package/src/service/diff.ts +279 -0
  132. package/src/service/enhance/aggregator.ts +489 -0
  133. package/src/service/enhance/cache.ts +275 -0
  134. package/src/service/enhance/codebase-scanner.ts +357 -0
  135. package/src/service/enhance/context-generator.ts +529 -0
  136. package/src/service/enhance/doc-extractor.ts +523 -0
  137. package/src/service/enhance/index.ts +131 -0
  138. package/src/service/enhance/props-extractor.ts +665 -0
  139. package/src/service/enhance/scanner.ts +445 -0
  140. package/src/service/enhance/storybook-parser.ts +552 -0
  141. package/src/service/enhance/types.ts +346 -0
  142. package/src/service/enhance/variant-renderer.ts +479 -0
  143. package/src/service/figma.ts +1008 -0
  144. package/src/service/index.ts +249 -0
  145. package/src/service/metrics-store.ts +333 -0
  146. package/src/service/patch-generator.ts +349 -0
  147. package/src/service/report.ts +854 -0
  148. package/src/service/storage.ts +401 -0
  149. package/src/service/token-fixes.ts +281 -0
  150. package/src/service/token-parser.ts +504 -0
  151. package/src/service/token-registry.ts +721 -0
  152. package/src/service/utils.ts +172 -0
  153. package/src/setup.ts +241 -0
  154. package/src/shared/command-wrapper.ts +81 -0
  155. package/src/shared/dev-server-client.ts +199 -0
  156. package/src/shared/index.ts +8 -0
  157. package/src/shared/segment-loader.ts +59 -0
  158. package/src/shared/types.ts +147 -0
  159. package/src/static-viewer.ts +715 -0
  160. package/src/test/discovery.ts +172 -0
  161. package/src/test/index.ts +281 -0
  162. package/src/test/reporters/console.ts +194 -0
  163. package/src/test/reporters/json.ts +190 -0
  164. package/src/test/reporters/junit.ts +186 -0
  165. package/src/test/runner.ts +598 -0
  166. package/src/test/types.ts +245 -0
  167. package/src/test/watch.ts +200 -0
  168. package/src/validators.ts +152 -0
  169. package/src/viewer/__tests__/jsx-parser.test.ts +502 -0
  170. package/src/viewer/__tests__/render-utils.test.ts +232 -0
  171. package/src/viewer/__tests__/style-utils.test.ts +404 -0
  172. package/src/viewer/bin.ts +86 -0
  173. package/src/viewer/cli/health.ts +256 -0
  174. package/src/viewer/cli/index.ts +33 -0
  175. package/src/viewer/cli/scan.ts +124 -0
  176. package/src/viewer/cli/utils.ts +174 -0
  177. package/src/viewer/components/AccessibilityPanel.tsx +1404 -0
  178. package/src/viewer/components/ActionCapture.tsx +172 -0
  179. package/src/viewer/components/ActionsPanel.tsx +371 -0
  180. package/src/viewer/components/App.tsx +638 -0
  181. package/src/viewer/components/BottomPanel.tsx +224 -0
  182. package/src/viewer/components/CodePanel.tsx +589 -0
  183. package/src/viewer/components/CommandPalette.tsx +336 -0
  184. package/src/viewer/components/ComponentGraph.tsx +394 -0
  185. package/src/viewer/components/ComponentHeader.tsx +85 -0
  186. package/src/viewer/components/ContractPanel.tsx +234 -0
  187. package/src/viewer/components/ErrorBoundary.tsx +85 -0
  188. package/src/viewer/components/FigmaEmbed.tsx +231 -0
  189. package/src/viewer/components/FragmentEditor.tsx +485 -0
  190. package/src/viewer/components/HealthDashboard.tsx +452 -0
  191. package/src/viewer/components/HmrStatusIndicator.tsx +71 -0
  192. package/src/viewer/components/Icons.tsx +417 -0
  193. package/src/viewer/components/InteractionsPanel.tsx +720 -0
  194. package/src/viewer/components/IsolatedPreviewFrame.tsx +321 -0
  195. package/src/viewer/components/IsolatedRender.tsx +111 -0
  196. package/src/viewer/components/KeyboardShortcutsHelp.tsx +89 -0
  197. package/src/viewer/components/LandingPage.tsx +441 -0
  198. package/src/viewer/components/Layout.tsx +22 -0
  199. package/src/viewer/components/LeftSidebar.tsx +391 -0
  200. package/src/viewer/components/MultiViewportPreview.tsx +429 -0
  201. package/src/viewer/components/PreviewArea.tsx +404 -0
  202. package/src/viewer/components/PreviewFrameHost.tsx +310 -0
  203. package/src/viewer/components/PreviewPane.tsx +150 -0
  204. package/src/viewer/components/PreviewToolbar.tsx +176 -0
  205. package/src/viewer/components/PropsEditor.tsx +512 -0
  206. package/src/viewer/components/PropsTable.tsx +98 -0
  207. package/src/viewer/components/RelationsSection.tsx +57 -0
  208. package/src/viewer/components/ResizablePanel.tsx +328 -0
  209. package/src/viewer/components/RightSidebar.tsx +118 -0
  210. package/src/viewer/components/ScreenshotButton.tsx +90 -0
  211. package/src/viewer/components/Sidebar.tsx +169 -0
  212. package/src/viewer/components/SkeletonLoader.tsx +156 -0
  213. package/src/viewer/components/StoryRenderer.tsx +128 -0
  214. package/src/viewer/components/ThemeProvider.tsx +96 -0
  215. package/src/viewer/components/Toast.tsx +67 -0
  216. package/src/viewer/components/TokenStylePanel.tsx +708 -0
  217. package/src/viewer/components/UsageSection.tsx +95 -0
  218. package/src/viewer/components/VariantMatrix.tsx +350 -0
  219. package/src/viewer/components/VariantRenderer.tsx +131 -0
  220. package/src/viewer/components/VariantTabs.tsx +84 -0
  221. package/src/viewer/components/ViewportSelector.tsx +165 -0
  222. package/src/viewer/components/_future/CreatePage.tsx +836 -0
  223. package/src/viewer/composition-renderer.ts +381 -0
  224. package/src/viewer/constants/index.ts +1 -0
  225. package/src/viewer/constants/ui.ts +185 -0
  226. package/src/viewer/entry.tsx +299 -0
  227. package/src/viewer/hooks/index.ts +2 -0
  228. package/src/viewer/hooks/useA11yCache.ts +383 -0
  229. package/src/viewer/hooks/useA11yService.ts +498 -0
  230. package/src/viewer/hooks/useActions.ts +138 -0
  231. package/src/viewer/hooks/useAppState.ts +124 -0
  232. package/src/viewer/hooks/useFigmaIntegration.ts +132 -0
  233. package/src/viewer/hooks/useHmrStatus.ts +109 -0
  234. package/src/viewer/hooks/useKeyboardShortcuts.ts +222 -0
  235. package/src/viewer/hooks/usePreviewBridge.ts +347 -0
  236. package/src/viewer/hooks/useScrollSpy.ts +78 -0
  237. package/src/viewer/hooks/useUrlState.ts +330 -0
  238. package/src/viewer/hooks/useViewSettings.ts +125 -0
  239. package/src/viewer/index.html +28 -0
  240. package/src/viewer/index.ts +14 -0
  241. package/src/viewer/intelligence/healthReport.ts +505 -0
  242. package/src/viewer/intelligence/styleDrift.ts +340 -0
  243. package/src/viewer/intelligence/usageScanner.ts +309 -0
  244. package/src/viewer/jsx-parser.ts +485 -0
  245. package/src/viewer/postcss.config.js +6 -0
  246. package/src/viewer/preview-frame-entry.tsx +25 -0
  247. package/src/viewer/preview-frame.html +109 -0
  248. package/src/viewer/render-template.html +68 -0
  249. package/src/viewer/render-utils.ts +170 -0
  250. package/src/viewer/server.ts +276 -0
  251. package/src/viewer/style-utils.ts +414 -0
  252. package/src/viewer/styles/globals.css +355 -0
  253. package/src/viewer/tailwind.config.js +37 -0
  254. package/src/viewer/types/a11y.ts +197 -0
  255. package/src/viewer/utils/a11y-fixes.ts +471 -0
  256. package/src/viewer/utils/actionExport.ts +372 -0
  257. package/src/viewer/utils/colorSchemes.ts +201 -0
  258. package/src/viewer/utils/detectRelationships.ts +256 -0
  259. package/src/viewer/vite-plugin.ts +2143 -0
@@ -0,0 +1,429 @@
1
+ // @ts-nocheck
2
+ /**
3
+ * Multi-Viewport Preview - Show component at multiple sizes simultaneously
4
+ *
5
+ * Displays the component at mobile, tablet, and desktop widths
6
+ * with proper device mockups and horizontal scrolling.
7
+ */
8
+
9
+ import { useState, type ReactNode } from "react";
10
+ import { ErrorBoundary } from "./ErrorBoundary.js";
11
+ import { IsolatedPreviewFrame } from "./IsolatedPreviewFrame.js";
12
+ import { ChevronDownIcon } from "./Icons.js";
13
+ import { getBackgroundStyle, type BackgroundOption } from "./PreviewToolbar.js";
14
+
15
+ interface ViewportConfig {
16
+ name: string;
17
+ width: number;
18
+ height: number;
19
+ type: "mobile" | "tablet" | "desktop";
20
+ }
21
+
22
+ interface MobilePreset {
23
+ name: string;
24
+ width: number;
25
+ height: number;
26
+ }
27
+
28
+ const MOBILE_PRESETS: MobilePreset[] = [
29
+ { name: "iPhone SE", width: 375, height: 667 },
30
+ { name: "iPhone 14", width: 390, height: 844 },
31
+ { name: "iPhone 14 Pro Max", width: 430, height: 932 },
32
+ { name: "Pixel 7", width: 412, height: 915 },
33
+ { name: "Galaxy S21", width: 360, height: 800 },
34
+ ];
35
+
36
+ const DEFAULT_VIEWPORTS: ViewportConfig[] = [
37
+ { name: "Mobile", width: 375, height: 667, type: "mobile" },
38
+ { name: "Tablet", width: 768, height: 1024, type: "tablet" },
39
+ { name: "Desktop", width: 1280, height: 800, type: "desktop" },
40
+ ];
41
+
42
+ interface MultiViewportPreviewProps {
43
+ /** Component name for error boundary */
44
+ componentName: string;
45
+ /** Segment path for iframe rendering */
46
+ segmentPath: string;
47
+ /** Variant name for iframe rendering */
48
+ variantName: string;
49
+ /** Render function that returns the component (fallback) */
50
+ renderContent: () => ReactNode;
51
+ /** Preview theme */
52
+ previewTheme: "light" | "dark";
53
+ /** Background option for preview */
54
+ background: BackgroundOption;
55
+ /** Base zoom level (used for scaling if needed) */
56
+ zoom: number;
57
+ /** Whether to use iframe isolation */
58
+ useIframeIsolation?: boolean;
59
+ }
60
+
61
+ export function MultiViewportPreview({
62
+ componentName,
63
+ segmentPath,
64
+ variantName,
65
+ renderContent,
66
+ previewTheme,
67
+ background,
68
+ zoom,
69
+ useIframeIsolation = true,
70
+ }: MultiViewportPreviewProps) {
71
+ const [selectedMobile, setSelectedMobile] = useState<MobilePreset>(MOBILE_PRESETS[0]);
72
+ const [showMobileDropdown, setShowMobileDropdown] = useState(false);
73
+
74
+ // Build viewports with selected mobile preset
75
+ const viewports: ViewportConfig[] = [
76
+ { name: "Mobile", width: selectedMobile.width, height: selectedMobile.height, type: "mobile" },
77
+ { name: "Tablet", width: 768, height: 1024, type: "tablet" },
78
+ { name: "Desktop", width: 1280, height: 800, type: "desktop" },
79
+ ];
80
+
81
+ return (
82
+ <div className="h-full flex flex-col bg-gray-100 dark:bg-gray-900">
83
+ {/* Header */}
84
+ <div className="flex-shrink-0 px-4 py-3 border-b border-[--border] bg-[--bg-secondary] flex items-center justify-center gap-8">
85
+ {/* Mobile with dropdown */}
86
+ <div className="relative">
87
+ <button
88
+ onClick={() => setShowMobileDropdown(!showMobileDropdown)}
89
+ className="flex items-center gap-2 text-sm hover:bg-[--bg-hover] px-2 py-1 rounded transition-colors"
90
+ >
91
+ <span className="text-lg">📱</span>
92
+ <span className="font-medium text-secondary">Mobile</span>
93
+ <span className="text-xs text-tertiary">({selectedMobile.width}px)</span>
94
+ <ChevronDownIcon className="w-3 h-3 text-tertiary" />
95
+ </button>
96
+
97
+ {showMobileDropdown && (
98
+ <>
99
+ <div
100
+ className="fixed inset-0 z-10"
101
+ onClick={() => setShowMobileDropdown(false)}
102
+ />
103
+ <div className="absolute top-full left-0 mt-1 z-20 bg-[--bg-elevated] border border-[--border] rounded-lg shadow-lg py-1 min-w-[180px]">
104
+ {MOBILE_PRESETS.map((preset) => (
105
+ <button
106
+ key={preset.name}
107
+ onClick={() => {
108
+ setSelectedMobile(preset);
109
+ setShowMobileDropdown(false);
110
+ }}
111
+ className={`w-full px-3 py-1.5 text-left text-xs hover:bg-[--bg-hover] transition-colors flex items-center justify-between ${
112
+ selectedMobile.name === preset.name ? 'text-blue-500' : 'text-secondary'
113
+ }`}
114
+ >
115
+ <span>{preset.name}</span>
116
+ <span className="text-tertiary">{preset.width}×{preset.height}</span>
117
+ </button>
118
+ ))}
119
+ </div>
120
+ </>
121
+ )}
122
+ </div>
123
+
124
+ {/* Tablet */}
125
+ <div className="flex items-center gap-2 text-sm">
126
+ <span className="text-lg">📱</span>
127
+ <span className="font-medium text-secondary">Tablet</span>
128
+ <span className="text-xs text-tertiary">(768px)</span>
129
+ </div>
130
+
131
+ {/* Desktop */}
132
+ <div className="flex items-center gap-2 text-sm">
133
+ <span className="text-lg">🖥️</span>
134
+ <span className="font-medium text-secondary">Desktop</span>
135
+ <span className="text-xs text-tertiary">(1280px)</span>
136
+ </div>
137
+ </div>
138
+
139
+ {/* Viewport panels with horizontal scroll */}
140
+ <div className="flex-1 overflow-x-auto overflow-y-auto">
141
+ <div className="flex gap-8 p-8 min-w-max">
142
+ {viewports.map((vp) => (
143
+ <ViewportPanel
144
+ key={`${vp.name}-${vp.width}`}
145
+ viewport={vp}
146
+ componentName={componentName}
147
+ segmentPath={segmentPath}
148
+ variantName={variantName}
149
+ renderContent={renderContent}
150
+ previewTheme={previewTheme}
151
+ background={background}
152
+ useIframeIsolation={useIframeIsolation}
153
+ />
154
+ ))}
155
+ </div>
156
+ </div>
157
+ </div>
158
+ );
159
+ }
160
+
161
+ interface ViewportPanelProps {
162
+ viewport: ViewportConfig;
163
+ componentName: string;
164
+ segmentPath: string;
165
+ variantName: string;
166
+ renderContent: () => ReactNode;
167
+ previewTheme: "light" | "dark";
168
+ background: BackgroundOption;
169
+ useIframeIsolation: boolean;
170
+ }
171
+
172
+ function ViewportPanel({
173
+ viewport,
174
+ componentName,
175
+ segmentPath,
176
+ variantName,
177
+ renderContent,
178
+ previewTheme,
179
+ background,
180
+ useIframeIsolation,
181
+ }: ViewportPanelProps) {
182
+ if (viewport.type === "desktop") {
183
+ return (
184
+ <DesktopMockup
185
+ width={viewport.width}
186
+ height={viewport.height}
187
+ label={viewport.name}
188
+ previewTheme={previewTheme}
189
+ background={background}
190
+ componentName={componentName}
191
+ segmentPath={segmentPath}
192
+ variantName={variantName}
193
+ renderContent={renderContent}
194
+ useIframeIsolation={useIframeIsolation}
195
+ />
196
+ );
197
+ }
198
+
199
+ return (
200
+ <DeviceMockup
201
+ type={viewport.type}
202
+ width={viewport.width}
203
+ height={viewport.height}
204
+ label={viewport.name}
205
+ previewTheme={previewTheme}
206
+ background={background}
207
+ componentName={componentName}
208
+ segmentPath={segmentPath}
209
+ variantName={variantName}
210
+ renderContent={renderContent}
211
+ useIframeIsolation={useIframeIsolation}
212
+ />
213
+ );
214
+ }
215
+
216
+ interface DeviceMockupProps {
217
+ type: "mobile" | "tablet";
218
+ width: number;
219
+ height: number;
220
+ label: string;
221
+ previewTheme: "light" | "dark";
222
+ background: BackgroundOption;
223
+ componentName: string;
224
+ segmentPath: string;
225
+ variantName: string;
226
+ renderContent: () => ReactNode;
227
+ useIframeIsolation: boolean;
228
+ }
229
+
230
+ function DeviceMockup({
231
+ type,
232
+ width,
233
+ height,
234
+ label,
235
+ previewTheme,
236
+ background,
237
+ componentName,
238
+ segmentPath,
239
+ variantName,
240
+ renderContent,
241
+ useIframeIsolation,
242
+ }: DeviceMockupProps) {
243
+ const isMobile = type === "mobile";
244
+ const frameWidth = width + 24; // Add bezel width
245
+ const screenHeight = height;
246
+ const backgroundStyle = getBackgroundStyle(background);
247
+
248
+ return (
249
+ <div className="flex flex-col items-center">
250
+ {/* Label */}
251
+ <div className="mb-3 text-sm font-medium text-tertiary">
252
+ {label}
253
+ </div>
254
+
255
+ {/* Device frame */}
256
+ <div
257
+ className="relative flex-shrink-0"
258
+ style={{ width: `${frameWidth}px` }}
259
+ >
260
+ <div
261
+ className="relative rounded-[40px] bg-[#1a1a1a] p-3 shadow-2xl"
262
+ style={{
263
+ boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255,255,255,0.1)",
264
+ }}
265
+ >
266
+ {/* Side buttons (mobile only) */}
267
+ {isMobile && (
268
+ <>
269
+ <div className="absolute -left-[3px] top-24 w-[3px] h-8 bg-[#2a2a2a] rounded-l" />
270
+ <div className="absolute -left-[3px] top-36 w-[3px] h-12 bg-[#2a2a2a] rounded-l" />
271
+ <div className="absolute -left-[3px] top-52 w-[3px] h-12 bg-[#2a2a2a] rounded-l" />
272
+ <div className="absolute -right-[3px] top-32 w-[3px] h-16 bg-[#2a2a2a] rounded-r" />
273
+ </>
274
+ )}
275
+
276
+ {/* Screen container */}
277
+ <div
278
+ className="relative rounded-[32px] overflow-hidden bg-white"
279
+ style={{ height: `${screenHeight}px`, width: `${width}px` }}
280
+ >
281
+ {/* Notch (mobile) or Camera (tablet) */}
282
+ {isMobile ? (
283
+ <div className="absolute top-0 left-1/2 -translate-x-1/2 w-[120px] h-[30px] bg-[#1a1a1a] rounded-b-2xl z-10 flex items-center justify-center gap-2">
284
+ <div className="w-2 h-2 rounded-full bg-[#2a2a2a]" />
285
+ <div className="w-12 h-1.5 rounded-full bg-[#2a2a2a]" />
286
+ </div>
287
+ ) : (
288
+ <div className="absolute top-2 left-1/2 -translate-x-1/2 w-3 h-3 rounded-full bg-[#2a2a2a] z-10" />
289
+ )}
290
+
291
+ {/* Screen content */}
292
+ <div
293
+ className="w-full h-full overflow-hidden"
294
+ data-theme={previewTheme}
295
+ >
296
+ {useIframeIsolation ? (
297
+ <IsolatedPreviewFrame
298
+ segmentPath={segmentPath}
299
+ variantName={variantName}
300
+ theme={previewTheme}
301
+ width="100%"
302
+ height="100%"
303
+ minHeight={screenHeight}
304
+ />
305
+ ) : (
306
+ <div className={isMobile ? "pt-10 px-4 pb-4" : "pt-6 px-4 pb-4"}>
307
+ <ErrorBoundary
308
+ componentName={componentName}
309
+ fallback={
310
+ <div className="text-xs text-red-500 p-2">
311
+ Error rendering at {width}px
312
+ </div>
313
+ }
314
+ >
315
+ {renderContent()}
316
+ </ErrorBoundary>
317
+ </div>
318
+ )}
319
+ </div>
320
+
321
+ {/* Home indicator */}
322
+ <div className="absolute bottom-2 left-1/2 -translate-x-1/2 w-[100px] h-1 bg-black/20 rounded-full z-10" />
323
+ </div>
324
+ </div>
325
+ </div>
326
+ </div>
327
+ );
328
+ }
329
+
330
+ interface DesktopMockupProps {
331
+ width: number;
332
+ height: number;
333
+ label: string;
334
+ previewTheme: "light" | "dark";
335
+ background: BackgroundOption;
336
+ componentName: string;
337
+ segmentPath: string;
338
+ variantName: string;
339
+ renderContent: () => ReactNode;
340
+ useIframeIsolation: boolean;
341
+ }
342
+
343
+ function DesktopMockup({
344
+ width,
345
+ height,
346
+ label,
347
+ previewTheme,
348
+ background,
349
+ componentName,
350
+ segmentPath,
351
+ variantName,
352
+ renderContent,
353
+ useIframeIsolation,
354
+ }: DesktopMockupProps) {
355
+ const backgroundStyle = getBackgroundStyle(background);
356
+
357
+ return (
358
+ <div className="flex flex-col items-center">
359
+ {/* Label */}
360
+ <div className="mb-3 text-sm font-medium text-tertiary">
361
+ {label}
362
+ </div>
363
+
364
+ {/* Monitor frame */}
365
+ <div className="flex flex-col items-center">
366
+ {/* Screen */}
367
+ <div
368
+ className="relative bg-[#1a1a1a] rounded-lg p-2 shadow-2xl"
369
+ style={{
370
+ boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255,255,255,0.1)",
371
+ }}
372
+ >
373
+ {/* Browser chrome */}
374
+ <div className="flex items-center gap-2 px-3 py-2 bg-[#2a2a2a] rounded-t-md">
375
+ <div className="flex items-center gap-1.5">
376
+ <div className="w-3 h-3 rounded-full bg-[#ff5f57]" />
377
+ <div className="w-3 h-3 rounded-full bg-[#ffbd2e]" />
378
+ <div className="w-3 h-3 rounded-full bg-[#28ca41]" />
379
+ </div>
380
+ <div className="flex-1 mx-4">
381
+ <div className="h-6 bg-[#3a3a3a] rounded-md flex items-center justify-center px-3">
382
+ <span className="text-xs text-gray-400 truncate">
383
+ localhost:6006 — {width}px
384
+ </span>
385
+ </div>
386
+ </div>
387
+ </div>
388
+
389
+ {/* Screen content */}
390
+ <div
391
+ className="overflow-hidden"
392
+ style={{ width: `${width}px`, height: `${height}px`, ...backgroundStyle }}
393
+ data-theme={previewTheme}
394
+ >
395
+ {useIframeIsolation ? (
396
+ <IsolatedPreviewFrame
397
+ segmentPath={segmentPath}
398
+ variantName={variantName}
399
+ theme={previewTheme}
400
+ width="100%"
401
+ height="100%"
402
+ minHeight={height}
403
+ />
404
+ ) : (
405
+ <div className="p-6">
406
+ <ErrorBoundary
407
+ componentName={componentName}
408
+ fallback={
409
+ <div className="text-xs text-red-500 p-2">
410
+ Error rendering at {width}px
411
+ </div>
412
+ }
413
+ >
414
+ {renderContent()}
415
+ </ErrorBoundary>
416
+ </div>
417
+ )}
418
+ </div>
419
+ </div>
420
+
421
+ {/* Monitor stand */}
422
+ <div className="w-20 h-4 bg-gradient-to-b from-[#2a2a2a] to-[#1a1a1a] rounded-b-sm" />
423
+ <div className="w-32 h-2 bg-[#1a1a1a] rounded-full shadow-lg" />
424
+ </div>
425
+ </div>
426
+ );
427
+ }
428
+
429
+ export { DEFAULT_VIEWPORTS as VIEWPORTS, MOBILE_PRESETS };