@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.
- package/LICENSE +21 -0
- package/README.md +106 -0
- package/dist/bin.d.ts +1 -0
- package/dist/bin.js +4783 -0
- package/dist/bin.js.map +1 -0
- package/dist/chunk-4FDQSGKX.js +786 -0
- package/dist/chunk-4FDQSGKX.js.map +1 -0
- package/dist/chunk-7H2MMGYG.js +369 -0
- package/dist/chunk-7H2MMGYG.js.map +1 -0
- package/dist/chunk-BSCG3IP7.js +619 -0
- package/dist/chunk-BSCG3IP7.js.map +1 -0
- package/dist/chunk-LY2CFFPY.js +898 -0
- package/dist/chunk-LY2CFFPY.js.map +1 -0
- package/dist/chunk-MUZ6CM66.js +6636 -0
- package/dist/chunk-MUZ6CM66.js.map +1 -0
- package/dist/chunk-OAENNG3G.js +1489 -0
- package/dist/chunk-OAENNG3G.js.map +1 -0
- package/dist/chunk-XHNKNI6J.js +235 -0
- package/dist/chunk-XHNKNI6J.js.map +1 -0
- package/dist/core-DWKLGY4N.js +68 -0
- package/dist/core-DWKLGY4N.js.map +1 -0
- package/dist/generate-4LQNJ7SX.js +249 -0
- package/dist/generate-4LQNJ7SX.js.map +1 -0
- package/dist/index.d.ts +775 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/init-EMVI47QG.js +416 -0
- package/dist/init-EMVI47QG.js.map +1 -0
- package/dist/mcp-bin.d.ts +1 -0
- package/dist/mcp-bin.js +1117 -0
- package/dist/mcp-bin.js.map +1 -0
- package/dist/scan-4YPRF7FV.js +12 -0
- package/dist/scan-4YPRF7FV.js.map +1 -0
- package/dist/service-QSZMZJBJ.js +208 -0
- package/dist/service-QSZMZJBJ.js.map +1 -0
- package/dist/static-viewer-MIPGZ4Z7.js +12 -0
- package/dist/static-viewer-MIPGZ4Z7.js.map +1 -0
- package/dist/test-SQ5ZHXWU.js +1067 -0
- package/dist/test-SQ5ZHXWU.js.map +1 -0
- package/dist/tokens-HSGMYK64.js +173 -0
- package/dist/tokens-HSGMYK64.js.map +1 -0
- package/dist/viewer-YRF4SQE4.js +11101 -0
- package/dist/viewer-YRF4SQE4.js.map +1 -0
- package/package.json +107 -0
- package/src/ai.ts +266 -0
- package/src/analyze.ts +265 -0
- package/src/bin.ts +916 -0
- package/src/build.ts +248 -0
- package/src/commands/a11y.ts +302 -0
- package/src/commands/add.ts +313 -0
- package/src/commands/audit.ts +195 -0
- package/src/commands/baseline.ts +221 -0
- package/src/commands/build.ts +144 -0
- package/src/commands/compare.ts +337 -0
- package/src/commands/context.ts +107 -0
- package/src/commands/dev.ts +107 -0
- package/src/commands/enhance.ts +858 -0
- package/src/commands/generate.ts +391 -0
- package/src/commands/init.ts +531 -0
- package/src/commands/link/figma.ts +645 -0
- package/src/commands/link/index.ts +10 -0
- package/src/commands/link/storybook.ts +267 -0
- package/src/commands/list.ts +49 -0
- package/src/commands/metrics.ts +114 -0
- package/src/commands/reset.ts +242 -0
- package/src/commands/scan.ts +537 -0
- package/src/commands/storygen.ts +207 -0
- package/src/commands/tokens.ts +251 -0
- package/src/commands/validate.ts +93 -0
- package/src/commands/verify.ts +215 -0
- package/src/core/composition.test.ts +262 -0
- package/src/core/composition.ts +255 -0
- package/src/core/config.ts +84 -0
- package/src/core/constants.ts +111 -0
- package/src/core/context.ts +380 -0
- package/src/core/defineSegment.ts +137 -0
- package/src/core/discovery.ts +337 -0
- package/src/core/figma.ts +263 -0
- package/src/core/fragment-types.ts +214 -0
- package/src/core/generators/context.ts +389 -0
- package/src/core/generators/index.ts +23 -0
- package/src/core/generators/registry.ts +364 -0
- package/src/core/generators/typescript-extractor.ts +374 -0
- package/src/core/importAnalyzer.ts +217 -0
- package/src/core/index.ts +149 -0
- package/src/core/loader.ts +155 -0
- package/src/core/node.ts +63 -0
- package/src/core/parser.ts +551 -0
- package/src/core/previewLoader.ts +172 -0
- package/src/core/schema/fragment.schema.json +189 -0
- package/src/core/schema/registry.schema.json +137 -0
- package/src/core/schema.ts +182 -0
- package/src/core/storyAdapter.test.ts +571 -0
- package/src/core/storyAdapter.ts +761 -0
- package/src/core/token-types.ts +287 -0
- package/src/core/types.ts +754 -0
- package/src/diff.ts +323 -0
- package/src/index.ts +43 -0
- package/src/mcp/__tests__/projectFields.test.ts +130 -0
- package/src/mcp/bin.ts +36 -0
- package/src/mcp/index.ts +8 -0
- package/src/mcp/server.ts +1310 -0
- package/src/mcp/utils.ts +54 -0
- package/src/mcp-bin.ts +36 -0
- package/src/migrate/__tests__/argTypes/argTypes.test.ts +189 -0
- package/src/migrate/__tests__/args/args.test.ts +452 -0
- package/src/migrate/__tests__/meta/meta.test.ts +198 -0
- package/src/migrate/__tests__/stories/stories.test.ts +278 -0
- package/src/migrate/__tests__/utils/utils.test.ts +371 -0
- package/src/migrate/__tests__/values/values.test.ts +303 -0
- package/src/migrate/bin.ts +108 -0
- package/src/migrate/converter.ts +658 -0
- package/src/migrate/detect.ts +196 -0
- package/src/migrate/index.ts +45 -0
- package/src/migrate/migrate.ts +163 -0
- package/src/migrate/parser.ts +1136 -0
- package/src/migrate/report.ts +624 -0
- package/src/migrate/types.ts +169 -0
- package/src/screenshot.ts +249 -0
- package/src/service/__tests__/ast-utils.test.ts +426 -0
- package/src/service/__tests__/enhance-scanner.test.ts +200 -0
- package/src/service/__tests__/figma/figma.test.ts +652 -0
- package/src/service/__tests__/metrics-store.test.ts +409 -0
- package/src/service/__tests__/patch-generator.test.ts +186 -0
- package/src/service/__tests__/props-extractor.test.ts +365 -0
- package/src/service/__tests__/token-registry.test.ts +267 -0
- package/src/service/analytics.ts +659 -0
- package/src/service/ast-utils.ts +444 -0
- package/src/service/browser-pool.ts +339 -0
- package/src/service/capture.ts +267 -0
- package/src/service/diff.ts +279 -0
- package/src/service/enhance/aggregator.ts +489 -0
- package/src/service/enhance/cache.ts +275 -0
- package/src/service/enhance/codebase-scanner.ts +357 -0
- package/src/service/enhance/context-generator.ts +529 -0
- package/src/service/enhance/doc-extractor.ts +523 -0
- package/src/service/enhance/index.ts +131 -0
- package/src/service/enhance/props-extractor.ts +665 -0
- package/src/service/enhance/scanner.ts +445 -0
- package/src/service/enhance/storybook-parser.ts +552 -0
- package/src/service/enhance/types.ts +346 -0
- package/src/service/enhance/variant-renderer.ts +479 -0
- package/src/service/figma.ts +1008 -0
- package/src/service/index.ts +249 -0
- package/src/service/metrics-store.ts +333 -0
- package/src/service/patch-generator.ts +349 -0
- package/src/service/report.ts +854 -0
- package/src/service/storage.ts +401 -0
- package/src/service/token-fixes.ts +281 -0
- package/src/service/token-parser.ts +504 -0
- package/src/service/token-registry.ts +721 -0
- package/src/service/utils.ts +172 -0
- package/src/setup.ts +241 -0
- package/src/shared/command-wrapper.ts +81 -0
- package/src/shared/dev-server-client.ts +199 -0
- package/src/shared/index.ts +8 -0
- package/src/shared/segment-loader.ts +59 -0
- package/src/shared/types.ts +147 -0
- package/src/static-viewer.ts +715 -0
- package/src/test/discovery.ts +172 -0
- package/src/test/index.ts +281 -0
- package/src/test/reporters/console.ts +194 -0
- package/src/test/reporters/json.ts +190 -0
- package/src/test/reporters/junit.ts +186 -0
- package/src/test/runner.ts +598 -0
- package/src/test/types.ts +245 -0
- package/src/test/watch.ts +200 -0
- package/src/validators.ts +152 -0
- package/src/viewer/__tests__/jsx-parser.test.ts +502 -0
- package/src/viewer/__tests__/render-utils.test.ts +232 -0
- package/src/viewer/__tests__/style-utils.test.ts +404 -0
- package/src/viewer/bin.ts +86 -0
- package/src/viewer/cli/health.ts +256 -0
- package/src/viewer/cli/index.ts +33 -0
- package/src/viewer/cli/scan.ts +124 -0
- package/src/viewer/cli/utils.ts +174 -0
- package/src/viewer/components/AccessibilityPanel.tsx +1404 -0
- package/src/viewer/components/ActionCapture.tsx +172 -0
- package/src/viewer/components/ActionsPanel.tsx +371 -0
- package/src/viewer/components/App.tsx +638 -0
- package/src/viewer/components/BottomPanel.tsx +224 -0
- package/src/viewer/components/CodePanel.tsx +589 -0
- package/src/viewer/components/CommandPalette.tsx +336 -0
- package/src/viewer/components/ComponentGraph.tsx +394 -0
- package/src/viewer/components/ComponentHeader.tsx +85 -0
- package/src/viewer/components/ContractPanel.tsx +234 -0
- package/src/viewer/components/ErrorBoundary.tsx +85 -0
- package/src/viewer/components/FigmaEmbed.tsx +231 -0
- package/src/viewer/components/FragmentEditor.tsx +485 -0
- package/src/viewer/components/HealthDashboard.tsx +452 -0
- package/src/viewer/components/HmrStatusIndicator.tsx +71 -0
- package/src/viewer/components/Icons.tsx +417 -0
- package/src/viewer/components/InteractionsPanel.tsx +720 -0
- package/src/viewer/components/IsolatedPreviewFrame.tsx +321 -0
- package/src/viewer/components/IsolatedRender.tsx +111 -0
- package/src/viewer/components/KeyboardShortcutsHelp.tsx +89 -0
- package/src/viewer/components/LandingPage.tsx +441 -0
- package/src/viewer/components/Layout.tsx +22 -0
- package/src/viewer/components/LeftSidebar.tsx +391 -0
- package/src/viewer/components/MultiViewportPreview.tsx +429 -0
- package/src/viewer/components/PreviewArea.tsx +404 -0
- package/src/viewer/components/PreviewFrameHost.tsx +310 -0
- package/src/viewer/components/PreviewPane.tsx +150 -0
- package/src/viewer/components/PreviewToolbar.tsx +176 -0
- package/src/viewer/components/PropsEditor.tsx +512 -0
- package/src/viewer/components/PropsTable.tsx +98 -0
- package/src/viewer/components/RelationsSection.tsx +57 -0
- package/src/viewer/components/ResizablePanel.tsx +328 -0
- package/src/viewer/components/RightSidebar.tsx +118 -0
- package/src/viewer/components/ScreenshotButton.tsx +90 -0
- package/src/viewer/components/Sidebar.tsx +169 -0
- package/src/viewer/components/SkeletonLoader.tsx +156 -0
- package/src/viewer/components/StoryRenderer.tsx +128 -0
- package/src/viewer/components/ThemeProvider.tsx +96 -0
- package/src/viewer/components/Toast.tsx +67 -0
- package/src/viewer/components/TokenStylePanel.tsx +708 -0
- package/src/viewer/components/UsageSection.tsx +95 -0
- package/src/viewer/components/VariantMatrix.tsx +350 -0
- package/src/viewer/components/VariantRenderer.tsx +131 -0
- package/src/viewer/components/VariantTabs.tsx +84 -0
- package/src/viewer/components/ViewportSelector.tsx +165 -0
- package/src/viewer/components/_future/CreatePage.tsx +836 -0
- package/src/viewer/composition-renderer.ts +381 -0
- package/src/viewer/constants/index.ts +1 -0
- package/src/viewer/constants/ui.ts +185 -0
- package/src/viewer/entry.tsx +299 -0
- package/src/viewer/hooks/index.ts +2 -0
- package/src/viewer/hooks/useA11yCache.ts +383 -0
- package/src/viewer/hooks/useA11yService.ts +498 -0
- package/src/viewer/hooks/useActions.ts +138 -0
- package/src/viewer/hooks/useAppState.ts +124 -0
- package/src/viewer/hooks/useFigmaIntegration.ts +132 -0
- package/src/viewer/hooks/useHmrStatus.ts +109 -0
- package/src/viewer/hooks/useKeyboardShortcuts.ts +222 -0
- package/src/viewer/hooks/usePreviewBridge.ts +347 -0
- package/src/viewer/hooks/useScrollSpy.ts +78 -0
- package/src/viewer/hooks/useUrlState.ts +330 -0
- package/src/viewer/hooks/useViewSettings.ts +125 -0
- package/src/viewer/index.html +28 -0
- package/src/viewer/index.ts +14 -0
- package/src/viewer/intelligence/healthReport.ts +505 -0
- package/src/viewer/intelligence/styleDrift.ts +340 -0
- package/src/viewer/intelligence/usageScanner.ts +309 -0
- package/src/viewer/jsx-parser.ts +485 -0
- package/src/viewer/postcss.config.js +6 -0
- package/src/viewer/preview-frame-entry.tsx +25 -0
- package/src/viewer/preview-frame.html +109 -0
- package/src/viewer/render-template.html +68 -0
- package/src/viewer/render-utils.ts +170 -0
- package/src/viewer/server.ts +276 -0
- package/src/viewer/style-utils.ts +414 -0
- package/src/viewer/styles/globals.css +355 -0
- package/src/viewer/tailwind.config.js +37 -0
- package/src/viewer/types/a11y.ts +197 -0
- package/src/viewer/utils/a11y-fixes.ts +471 -0
- package/src/viewer/utils/actionExport.ts +372 -0
- package/src/viewer/utils/colorSchemes.ts +201 -0
- package/src/viewer/utils/detectRelationships.ts +256 -0
- 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 };
|