@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,169 @@
1
+ /**
2
+ * Types for the Storybook migration tool.
3
+ */
4
+
5
+ /**
6
+ * Parsed Storybook story metadata
7
+ */
8
+ export interface ParsedMeta {
9
+ /** Full title path (e.g., "Components/Forms/Input") */
10
+ title: string;
11
+ /** Component name (inferred from title or component ref) */
12
+ componentName: string;
13
+ /** Component import path */
14
+ componentImport?: string;
15
+ /** Tags from the story */
16
+ tags?: string[];
17
+ /** Description from parameters.docs */
18
+ description?: string;
19
+ }
20
+
21
+ /**
22
+ * Parsed argType definition
23
+ */
24
+ export interface ParsedArgType {
25
+ /** Storybook control type */
26
+ control?: string;
27
+ /** Options for select/radio controls */
28
+ options?: string[];
29
+ /** Prop description */
30
+ description?: string;
31
+ /** Default value */
32
+ defaultValue?: unknown;
33
+ /** TypeScript type (if available) */
34
+ type?: string;
35
+ /** Whether the prop is required */
36
+ required?: boolean;
37
+ }
38
+
39
+ /**
40
+ * Parsed individual story
41
+ */
42
+ export interface ParsedStory {
43
+ /** Story name (export name) */
44
+ name: string;
45
+ /** Args passed to the component */
46
+ args: Record<string, unknown>;
47
+ /** Story description */
48
+ description?: string;
49
+ /** Whether story has a custom render function */
50
+ hasCustomRender?: boolean;
51
+ /** Raw render code if extractable */
52
+ renderCode?: string;
53
+ }
54
+
55
+ /**
56
+ * Complete parsed story file
57
+ */
58
+ export interface ParsedStoryFile {
59
+ /** Original file path */
60
+ filePath: string;
61
+ /** Parsed metadata */
62
+ meta: ParsedMeta;
63
+ /** Parsed argTypes */
64
+ argTypes: Record<string, ParsedArgType>;
65
+ /** Parsed stories */
66
+ stories: ParsedStory[];
67
+ /** Parse warnings */
68
+ warnings: string[];
69
+ /** Confidence score (0-1) - how reliable the extraction was */
70
+ confidence: number;
71
+ }
72
+
73
+ /**
74
+ * Detected Storybook configuration
75
+ */
76
+ export interface StorybookConfig {
77
+ /** Path to .storybook/main.* */
78
+ configPath: string;
79
+ /** Story glob patterns from config */
80
+ storyPatterns: string[];
81
+ /** Detected framework (react, vue, etc.) */
82
+ framework?: string;
83
+ /** Detected builder (vite, webpack) */
84
+ builder?: string;
85
+ /** Whether config was successfully parsed */
86
+ valid: boolean;
87
+ /** Parse errors if any */
88
+ errors?: string[];
89
+ }
90
+
91
+ /**
92
+ * Conversion result for a single story file
93
+ */
94
+ export interface ConversionResult {
95
+ /** Original story file path */
96
+ sourceFile: string;
97
+ /** Output segment file path */
98
+ outputFile: string;
99
+ /** Generated segment code */
100
+ code: string;
101
+ /** Component name */
102
+ componentName: string;
103
+ /** Category extracted from title */
104
+ category: string;
105
+ /** Number of variants generated */
106
+ variantCount: number;
107
+ /** Number of props extracted */
108
+ propCount: number;
109
+ /** Confidence score (0-1) - how reliable the extraction was */
110
+ confidence: number;
111
+ /** TODOs that need human attention */
112
+ todos: string[];
113
+ /** Warnings during conversion */
114
+ warnings: string[];
115
+ /** Whether conversion was successful */
116
+ success: boolean;
117
+ }
118
+
119
+ /**
120
+ * Migration report summary
121
+ */
122
+ export interface MigrationReport {
123
+ /** When the migration was run */
124
+ timestamp: Date;
125
+ /** Source directory */
126
+ sourceDir: string;
127
+ /** Output directory */
128
+ outputDir: string;
129
+ /** Total story files found */
130
+ totalStoryFiles: number;
131
+ /** Successfully converted files */
132
+ successfulConversions: number;
133
+ /** Failed conversions */
134
+ failedConversions: number;
135
+ /** Total variants generated */
136
+ totalVariants: number;
137
+ /** Total TODOs that need attention */
138
+ totalTodos: number;
139
+ /** Individual conversion results */
140
+ results: ConversionResult[];
141
+ /** Categories discovered */
142
+ categories: string[];
143
+ /** Analytics from the converted segments */
144
+ analytics?: {
145
+ overallScore: number;
146
+ coveragePercentage: number;
147
+ recommendations: string[];
148
+ };
149
+ }
150
+
151
+ /**
152
+ * Migration options
153
+ */
154
+ export interface MigrateOptions {
155
+ /** Source directory or file pattern */
156
+ from: string;
157
+ /** Output directory (default: same as source) */
158
+ to?: string;
159
+ /** Dry run - don't write files */
160
+ dryRun?: boolean;
161
+ /** Overwrite existing segment files */
162
+ overwrite?: boolean;
163
+ /** Generate HTML report */
164
+ report?: boolean;
165
+ /** Report output path */
166
+ reportPath?: string;
167
+ /** Verbose output */
168
+ verbose?: boolean;
169
+ }
@@ -0,0 +1,249 @@
1
+ import pc from 'picocolors';
2
+ import {
3
+ BRAND,
4
+ DEFAULTS,
5
+ type SegmentsConfig,
6
+ type SegmentDefinition,
7
+ type Theme,
8
+ } from './core/index.js';
9
+ import { discoverSegmentFiles, loadSegmentFile } from './core/node.js';
10
+ import {
11
+ BrowserPool,
12
+ CaptureEngine,
13
+ StorageManager,
14
+ formatMs,
15
+ type CaptureOptions,
16
+ } from './service/index.js';
17
+
18
+ /**
19
+ * Options for the screenshot command
20
+ */
21
+ export interface ScreenshotCommandOptions {
22
+ /** Specific component to capture */
23
+ component?: string;
24
+
25
+ /** Specific variant to capture */
26
+ variant?: string;
27
+
28
+ /** Theme to capture */
29
+ theme?: Theme;
30
+
31
+ /** Update existing baselines */
32
+ update?: boolean;
33
+
34
+ /** CI mode - no interactive prompts */
35
+ ci?: boolean;
36
+
37
+ /** Viewport width */
38
+ width?: number;
39
+
40
+ /** Viewport height */
41
+ height?: number;
42
+ }
43
+
44
+ /**
45
+ * Result of the screenshot command
46
+ */
47
+ export interface ScreenshotResult {
48
+ success: boolean;
49
+ captured: number;
50
+ skipped: number;
51
+ errors: Array<{ component: string; variant: string; error: string }>;
52
+ totalTimeMs: number;
53
+ }
54
+
55
+ /**
56
+ * Execute the screenshot command
57
+ */
58
+ export async function runScreenshotCommand(
59
+ config: SegmentsConfig,
60
+ configDir: string,
61
+ options: ScreenshotCommandOptions = {}
62
+ ): Promise<ScreenshotResult> {
63
+ const startTime = Date.now();
64
+ const errors: ScreenshotResult['errors'] = [];
65
+
66
+ // Initialize storage
67
+ const storage = new StorageManager({
68
+ projectRoot: configDir,
69
+ viewport: options.width && options.height
70
+ ? { width: options.width, height: options.height }
71
+ : config.screenshots?.viewport,
72
+ });
73
+ await storage.initialize();
74
+
75
+ // Discover segments
76
+ const segmentFiles = await discoverSegmentFiles(config, configDir);
77
+
78
+ if (segmentFiles.length === 0) {
79
+ console.log(pc.yellow('No segment files found.'));
80
+ return {
81
+ success: true,
82
+ captured: 0,
83
+ skipped: 0,
84
+ errors: [],
85
+ totalTimeMs: Date.now() - startTime,
86
+ };
87
+ }
88
+
89
+ // Load all segments
90
+ const segments: Array<{ path: string; segment: SegmentDefinition }> = [];
91
+
92
+ for (const file of segmentFiles) {
93
+ try {
94
+ const segment = await loadSegmentFile(file.absolutePath);
95
+ if (segment) {
96
+ segments.push({ path: file.relativePath, segment });
97
+ }
98
+ } catch (error) {
99
+ errors.push({
100
+ component: file.relativePath,
101
+ variant: '',
102
+ error: error instanceof Error ? error.message : String(error),
103
+ });
104
+ }
105
+ }
106
+
107
+ // Filter by component if specified
108
+ const filteredSegments = options.component
109
+ ? segments.filter((s) => s.segment.meta.name === options.component)
110
+ : segments;
111
+
112
+ if (options.component && filteredSegments.length === 0) {
113
+ console.log(pc.yellow(`Component "${options.component}" not found.`));
114
+ return {
115
+ success: false,
116
+ captured: 0,
117
+ skipped: 0,
118
+ errors: [],
119
+ totalTimeMs: Date.now() - startTime,
120
+ };
121
+ }
122
+
123
+ // Build list of variants to capture
124
+ const variantsToCapture: Array<{
125
+ component: string;
126
+ variant: string;
127
+ render: () => unknown;
128
+ }> = [];
129
+
130
+ for (const { segment } of filteredSegments) {
131
+ const variants = options.variant
132
+ ? segment.variants.filter((v) => v.name === options.variant)
133
+ : segment.variants;
134
+
135
+ for (const variant of variants) {
136
+ variantsToCapture.push({
137
+ component: segment.meta.name,
138
+ variant: variant.name,
139
+ render: variant.render,
140
+ });
141
+ }
142
+ }
143
+
144
+ if (variantsToCapture.length === 0) {
145
+ console.log(pc.yellow('No variants to capture.'));
146
+ return {
147
+ success: true,
148
+ captured: 0,
149
+ skipped: 0,
150
+ errors: [],
151
+ totalTimeMs: Date.now() - startTime,
152
+ };
153
+ }
154
+
155
+ // Determine theme
156
+ const theme: Theme = options.theme ?? DEFAULTS.theme;
157
+ const viewport = {
158
+ width: options.width ?? config.screenshots?.viewport?.width ?? DEFAULTS.viewport.width,
159
+ height: options.height ?? config.screenshots?.viewport?.height ?? DEFAULTS.viewport.height,
160
+ };
161
+
162
+ console.log(pc.cyan(`\n${BRAND.name} Screenshot\n`));
163
+ console.log(pc.dim(`Capturing variants (theme: ${theme}, viewport: ${viewport.width}x${viewport.height}):\n`));
164
+
165
+ // Initialize browser pool
166
+ const pool = new BrowserPool({
167
+ viewport,
168
+ });
169
+
170
+ // Create capture engine - use the viewer URL
171
+ const viewerPort = DEFAULTS.port;
172
+ const baseUrl = `http://localhost:${viewerPort}`;
173
+ const captureEngine = new CaptureEngine(pool, baseUrl);
174
+
175
+ let captured = 0;
176
+ let skipped = 0;
177
+
178
+ const captureOptions: CaptureOptions = {
179
+ theme,
180
+ viewport,
181
+ delay: config.screenshots?.delay ?? DEFAULTS.captureDelayMs,
182
+ };
183
+
184
+ try {
185
+ // Warm up the pool
186
+ console.log(pc.dim('Starting browser...'));
187
+ await pool.warmup();
188
+ console.log(pc.dim('Browser ready.\n'));
189
+
190
+ // Capture each variant
191
+ for (const { component, variant } of variantsToCapture) {
192
+ const hasExisting = storage.hasBaseline(component, variant, theme);
193
+
194
+ // Skip if exists and not updating
195
+ if (hasExisting && !options.update) {
196
+ console.log(` ${pc.dim('○')} ${component}/${variant} ${pc.dim('(skipped)')}`);
197
+ skipped++;
198
+ continue;
199
+ }
200
+
201
+ try {
202
+ const screenshot = await captureEngine.captureVariant(
203
+ component,
204
+ variant,
205
+ captureOptions
206
+ );
207
+
208
+ await storage.saveBaseline(screenshot);
209
+
210
+ const totalTime = screenshot.metadata.renderTimeMs + screenshot.metadata.captureTimeMs;
211
+ console.log(
212
+ ` ${pc.green('✓')} ${component}/${variant} ${pc.dim(formatMs(totalTime))}`
213
+ );
214
+ captured++;
215
+ } catch (error) {
216
+ const errorMsg = error instanceof Error ? error.message : String(error);
217
+ console.log(` ${pc.red('✗')} ${component}/${variant} ${pc.dim(errorMsg)}`);
218
+ errors.push({ component, variant, error: errorMsg });
219
+ }
220
+ }
221
+ } finally {
222
+ // Shutdown browser pool
223
+ await pool.shutdown();
224
+ }
225
+
226
+ const totalTimeMs = Date.now() - startTime;
227
+
228
+ // Print summary
229
+ console.log();
230
+ if (errors.length === 0) {
231
+ console.log(pc.green(`✓ Captured ${captured} screenshot(s) in ${formatMs(totalTimeMs)}`));
232
+ } else {
233
+ console.log(pc.yellow(`⚠ Captured ${captured} screenshot(s) with ${errors.length} error(s)`));
234
+ }
235
+
236
+ if (skipped > 0) {
237
+ console.log(pc.dim(` ${skipped} skipped (use --update to recapture)`));
238
+ }
239
+
240
+ console.log(pc.dim(` Stored in ${storage.screenshotsDirPath}\n`));
241
+
242
+ return {
243
+ success: errors.length === 0,
244
+ captured,
245
+ skipped,
246
+ errors,
247
+ totalTimeMs,
248
+ };
249
+ }