@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,441 @@
1
+ import { useForm, Controller } from 'react-hook-form';
2
+ import { zodResolver } from '@hookform/resolvers/zod';
3
+ import { z } from 'zod';
4
+ import { useState, useRef, useEffect } from 'react';
5
+ import clsx from 'clsx';
6
+ import { HexColorPicker } from 'react-colorful';
7
+ import { generateColorScheme, type ColorSchemeType } from '../utils/colorSchemes.js';
8
+ import { ChevronDownIcon } from './Icons.js';
9
+
10
+ const formSchema = z.object({
11
+ projectName: z.string().min(2, 'Project name must be at least 2 characters'),
12
+ componentName: z.string().min(2, 'Component name must be at least 2 characters'),
13
+ description: z.string().optional(),
14
+ headlessLibrary: z.enum(['radix', 'base-ui']),
15
+ colorSchemeType: z.enum(['monochromatic', 'complementary', 'analogous', 'triadic', 'split-complementary', 'tetradic']),
16
+ primaryColor: z.string().regex(/^#[0-9A-Fa-f]{6}$/, 'Must be a valid hex color'),
17
+ darkMode: z.boolean(),
18
+ borderRadius: z.enum(['none', 'sm', 'md', 'lg', 'full']),
19
+ });
20
+
21
+ export type LandingFormData = z.infer<typeof formSchema>;
22
+
23
+ interface LandingPageProps {
24
+ onSubmit?: (data: LandingFormData) => void;
25
+ }
26
+
27
+ // Scheme options with color counts
28
+ const SCHEME_OPTIONS: Array<{
29
+ value: ColorSchemeType;
30
+ label: string;
31
+ description: string;
32
+ colorCount: number;
33
+ }> = [
34
+ { value: 'monochromatic', label: 'Monochromatic', description: 'Variations of a single hue', colorCount: 5 },
35
+ { value: 'complementary', label: 'Complementary', description: 'Two opposite colors', colorCount: 2 },
36
+ { value: 'analogous', label: 'Analogous', description: 'Three adjacent colors', colorCount: 3 },
37
+ { value: 'triadic', label: 'Triadic', description: 'Three evenly spaced colors', colorCount: 3 },
38
+ { value: 'split-complementary', label: 'Split Complementary', description: 'Base + two adjacent to complement', colorCount: 3 },
39
+ { value: 'tetradic', label: 'Tetradic (Square)', description: 'Four evenly spaced colors', colorCount: 4 },
40
+ ];
41
+
42
+ // Get the key colors for a scheme (not all variations, just the distinct hues)
43
+ function getSchemeKeyColors(baseColor: string, type: ColorSchemeType): string[] {
44
+ const fullPalette = generateColorScheme(baseColor, type);
45
+
46
+ switch (type) {
47
+ case 'monochromatic':
48
+ return fullPalette; // All 5 variations
49
+ case 'complementary':
50
+ return [fullPalette[1], fullPalette[3]]; // Base and complement
51
+ case 'analogous':
52
+ return [fullPalette[0], fullPalette[2], fullPalette[4]]; // -30°, base, +30°
53
+ case 'triadic':
54
+ return [fullPalette[0], fullPalette[2], fullPalette[4]]; // 0°, 120°, 240°
55
+ case 'split-complementary':
56
+ return [fullPalette[0], fullPalette[2], fullPalette[4]]; // Base, +150°, +210°
57
+ case 'tetradic':
58
+ return [fullPalette[0], fullPalette[1], fullPalette[2], fullPalette[3]]; // 0°, 90°, 180°, 270°
59
+ default:
60
+ return fullPalette;
61
+ }
62
+ }
63
+
64
+ export function LandingPage({ onSubmit }: LandingPageProps) {
65
+ const [showColorPicker, setShowColorPicker] = useState(false);
66
+ const [showSchemeDropdown, setShowSchemeDropdown] = useState(false);
67
+ const colorPickerRef = useRef<HTMLDivElement>(null);
68
+ const schemeDropdownRef = useRef<HTMLDivElement>(null);
69
+
70
+ const {
71
+ register,
72
+ handleSubmit,
73
+ watch,
74
+ control,
75
+ setValue,
76
+ formState: { errors, isSubmitting },
77
+ } = useForm<LandingFormData>({
78
+ resolver: zodResolver(formSchema),
79
+ defaultValues: {
80
+ projectName: '',
81
+ componentName: '',
82
+ description: '',
83
+ headlessLibrary: 'radix',
84
+ colorSchemeType: 'monochromatic',
85
+ primaryColor: '#10a37f',
86
+ darkMode: true,
87
+ borderRadius: 'md',
88
+ },
89
+ });
90
+
91
+ const primaryColor = watch('primaryColor');
92
+ const colorSchemeType = watch('colorSchemeType');
93
+
94
+ // Generate current palette based on selected color and scheme
95
+ const currentPalette = getSchemeKeyColors(primaryColor, colorSchemeType);
96
+ const currentScheme = SCHEME_OPTIONS.find(s => s.value === colorSchemeType);
97
+
98
+ // Close dropdowns when clicking outside
99
+ useEffect(() => {
100
+ const handleClickOutside = (event: MouseEvent) => {
101
+ if (colorPickerRef.current && !colorPickerRef.current.contains(event.target as Node)) {
102
+ setShowColorPicker(false);
103
+ }
104
+ if (schemeDropdownRef.current && !schemeDropdownRef.current.contains(event.target as Node)) {
105
+ setShowSchemeDropdown(false);
106
+ }
107
+ };
108
+ document.addEventListener('mousedown', handleClickOutside);
109
+ return () => document.removeEventListener('mousedown', handleClickOutside);
110
+ }, []);
111
+
112
+ const handleFormSubmit = (data: LandingFormData) => {
113
+ console.log('Form submitted:', data);
114
+ onSubmit?.(data);
115
+ };
116
+
117
+ return (
118
+ <div className="min-h-screen bg-[--bg-primary]">
119
+ <div className="max-w-2xl mx-auto px-6 py-16">
120
+ {/* Header */}
121
+ <div className="mb-12">
122
+ <h1 className="text-2xl font-semibold text-primary mb-2">
123
+ Create Component
124
+ </h1>
125
+ <p className="text-secondary">
126
+ Configure your component settings below.
127
+ </p>
128
+ </div>
129
+
130
+ <form onSubmit={handleSubmit(handleFormSubmit)} className="space-y-8">
131
+ {/* Project Name */}
132
+ <div>
133
+ <label className="block text-sm text-primary mb-2">
134
+ Project Name
135
+ </label>
136
+ <input
137
+ {...register('projectName')}
138
+ placeholder="my-design-system"
139
+ className={clsx(
140
+ 'w-full px-4 py-3 rounded-xl text-sm',
141
+ 'bg-[--bg-tertiary] text-primary placeholder:text-tertiary',
142
+ 'border border-transparent',
143
+ 'focus:outline-none focus:ring-1 focus:ring-[--border-strong]',
144
+ 'transition-all',
145
+ errors.projectName && 'ring-1 ring-red-500'
146
+ )}
147
+ />
148
+ {errors.projectName && (
149
+ <p className="mt-2 text-xs text-red-400">{errors.projectName.message}</p>
150
+ )}
151
+ </div>
152
+
153
+ {/* Component Name */}
154
+ <div>
155
+ <label className="block text-sm text-primary mb-2">
156
+ Component Name
157
+ </label>
158
+ <input
159
+ {...register('componentName')}
160
+ placeholder="Button"
161
+ className={clsx(
162
+ 'w-full px-4 py-3 rounded-xl text-sm',
163
+ 'bg-[--bg-tertiary] text-primary placeholder:text-tertiary',
164
+ 'border border-transparent',
165
+ 'focus:outline-none focus:ring-1 focus:ring-[--border-strong]',
166
+ 'transition-all',
167
+ errors.componentName && 'ring-1 ring-red-500'
168
+ )}
169
+ />
170
+ {errors.componentName && (
171
+ <p className="mt-2 text-xs text-red-400">{errors.componentName.message}</p>
172
+ )}
173
+ </div>
174
+
175
+ {/* Description */}
176
+ <div>
177
+ <label className="block text-sm text-primary mb-2">
178
+ Description
179
+ </label>
180
+ <textarea
181
+ {...register('description')}
182
+ rows={2}
183
+ placeholder="A brief description of the component"
184
+ className={clsx(
185
+ 'w-full px-4 py-3 rounded-xl text-sm resize-none',
186
+ 'bg-[--bg-tertiary] text-primary placeholder:text-tertiary',
187
+ 'border border-transparent',
188
+ 'focus:outline-none focus:ring-1 focus:ring-[--border-strong]',
189
+ 'transition-all'
190
+ )}
191
+ />
192
+ </div>
193
+
194
+ <div className="h-px bg-[--border]" />
195
+
196
+ {/* Headless Library */}
197
+ <div>
198
+ <label className="block text-sm text-primary mb-4">
199
+ Headless Library
200
+ </label>
201
+ <div className="grid grid-cols-2 gap-3">
202
+ {[
203
+ { value: 'radix', label: 'Radix UI', desc: 'Accessible primitives' },
204
+ { value: 'base-ui', label: 'Base UI', desc: 'Hooks-first approach' },
205
+ ].map((lib) => (
206
+ <label
207
+ key={lib.value}
208
+ className={clsx(
209
+ 'flex flex-col p-4 rounded-xl cursor-pointer transition-all',
210
+ 'border',
211
+ watch('headlessLibrary') === lib.value
212
+ ? 'border-[--color-accent] bg-[--color-accent]/5'
213
+ : 'border-[--border] hover:border-[--border-strong]'
214
+ )}
215
+ >
216
+ <input
217
+ type="radio"
218
+ {...register('headlessLibrary')}
219
+ value={lib.value}
220
+ className="sr-only"
221
+ />
222
+ <span className="text-sm font-medium text-primary">{lib.label}</span>
223
+ <span className="text-xs text-tertiary mt-0.5">{lib.desc}</span>
224
+ </label>
225
+ ))}
226
+ </div>
227
+ </div>
228
+
229
+ <div className="h-px bg-[--border]" />
230
+
231
+ {/* Color Scheme */}
232
+ <div>
233
+ <label className="block text-sm text-primary mb-4">
234
+ Color Scheme
235
+ </label>
236
+
237
+ <div className="space-y-4">
238
+ {/* Primary Color */}
239
+ <div className="flex items-center gap-4">
240
+ <div className="relative" ref={colorPickerRef}>
241
+ <button
242
+ type="button"
243
+ onClick={() => setShowColorPicker(!showColorPicker)}
244
+ className="w-12 h-12 rounded-lg border border-[--border] hover:border-[--border-strong] transition-colors"
245
+ style={{ backgroundColor: primaryColor }}
246
+ />
247
+ {showColorPicker && (
248
+ <div className="absolute top-14 left-0 z-50 p-3 rounded-xl bg-[--bg-elevated] border border-[--border] shadow-xl">
249
+ <Controller
250
+ name="primaryColor"
251
+ control={control}
252
+ render={({ field }) => (
253
+ <HexColorPicker color={field.value} onChange={field.onChange} />
254
+ )}
255
+ />
256
+ <input
257
+ type="text"
258
+ value={primaryColor}
259
+ onChange={(e) => setValue('primaryColor', e.target.value)}
260
+ className="w-full mt-3 px-3 py-2 rounded-lg text-xs font-mono bg-[--bg-tertiary] text-primary border border-transparent focus:outline-none focus:ring-1 focus:ring-[--border-strong]"
261
+ />
262
+ </div>
263
+ )}
264
+ </div>
265
+ <div className="flex-1">
266
+ <input
267
+ type="text"
268
+ value={primaryColor}
269
+ onChange={(e) => setValue('primaryColor', e.target.value)}
270
+ className="w-full px-4 py-3 rounded-xl text-sm font-mono bg-[--bg-tertiary] text-primary border border-transparent focus:outline-none focus:ring-1 focus:ring-[--border-strong]"
271
+ />
272
+ </div>
273
+ </div>
274
+
275
+ {/* Custom Scheme Type Dropdown */}
276
+ <div className="relative" ref={schemeDropdownRef}>
277
+ <button
278
+ type="button"
279
+ onClick={() => setShowSchemeDropdown(!showSchemeDropdown)}
280
+ className={clsx(
281
+ 'w-full px-4 py-3 rounded-xl text-sm text-left',
282
+ 'bg-[--bg-tertiary] text-primary',
283
+ 'border border-transparent',
284
+ 'focus:outline-none focus:ring-1 focus:ring-[--border-strong]',
285
+ 'flex items-center justify-between',
286
+ showSchemeDropdown && 'ring-1 ring-[--border-strong]'
287
+ )}
288
+ >
289
+ <div className="flex items-center gap-3">
290
+ {/* Mini palette preview */}
291
+ <div className="flex gap-0.5">
292
+ {getSchemeKeyColors(primaryColor, colorSchemeType).map((color, i) => (
293
+ <div
294
+ key={i}
295
+ className="w-4 h-4 rounded-sm first:rounded-l last:rounded-r"
296
+ style={{ backgroundColor: color }}
297
+ />
298
+ ))}
299
+ </div>
300
+ <span>{currentScheme?.label}</span>
301
+ <span className="text-tertiary">— {currentScheme?.description}</span>
302
+ </div>
303
+ <ChevronDownIcon className={clsx('w-4 h-4 text-tertiary transition-transform', showSchemeDropdown && 'rotate-180')} />
304
+ </button>
305
+
306
+ {showSchemeDropdown && (
307
+ <div className="absolute top-full left-0 right-0 mt-2 z-40 py-2 rounded-xl bg-[--bg-elevated] border border-[--border] shadow-xl">
308
+ {SCHEME_OPTIONS.map((scheme) => {
309
+ const schemeColors = getSchemeKeyColors(primaryColor, scheme.value);
310
+ const isSelected = colorSchemeType === scheme.value;
311
+
312
+ return (
313
+ <button
314
+ key={scheme.value}
315
+ type="button"
316
+ onClick={() => {
317
+ setValue('colorSchemeType', scheme.value);
318
+ setShowSchemeDropdown(false);
319
+ }}
320
+ className={clsx(
321
+ 'w-full px-4 py-3 text-left flex items-center gap-3',
322
+ 'hover:bg-[--bg-hover] transition-colors',
323
+ isSelected && 'bg-[--bg-tertiary]'
324
+ )}
325
+ >
326
+ {/* Color preview swatches */}
327
+ <div className="flex gap-0.5 shrink-0">
328
+ {schemeColors.map((color, i) => (
329
+ <div
330
+ key={i}
331
+ className="w-5 h-5 rounded-sm first:rounded-l last:rounded-r"
332
+ style={{ backgroundColor: color }}
333
+ />
334
+ ))}
335
+ </div>
336
+ <div className="flex-1 min-w-0">
337
+ <div className="flex items-center gap-2">
338
+ {isSelected && <span className="text-[--color-accent]">✓</span>}
339
+ <span className={clsx('text-sm', isSelected ? 'text-primary font-medium' : 'text-primary')}>
340
+ {scheme.label}
341
+ </span>
342
+ </div>
343
+ <span className="text-xs text-tertiary">{scheme.description}</span>
344
+ </div>
345
+ </button>
346
+ );
347
+ })}
348
+ </div>
349
+ )}
350
+ </div>
351
+
352
+ {/* Current Palette Preview */}
353
+ <div>
354
+ <div className="flex gap-1.5">
355
+ {currentPalette.map((color, i) => (
356
+ <div
357
+ key={i}
358
+ className="flex-1 h-12 rounded-lg first:rounded-l-xl last:rounded-r-xl transition-colors"
359
+ style={{ backgroundColor: color }}
360
+ />
361
+ ))}
362
+ </div>
363
+ <p className="text-xs text-tertiary mt-2">
364
+ {currentScheme?.colorCount} color {currentScheme?.label.toLowerCase()} palette
365
+ </p>
366
+ </div>
367
+ </div>
368
+ </div>
369
+
370
+ <div className="h-px bg-[--border]" />
371
+
372
+ {/* Style Options */}
373
+ <div className="space-y-6">
374
+ {/* Border Radius */}
375
+ <div className="flex items-center justify-between">
376
+ <div>
377
+ <span className="text-sm text-primary">Border Radius</span>
378
+ <p className="text-xs text-tertiary mt-0.5">Component corner style</p>
379
+ </div>
380
+ <div className="flex gap-1 bg-[--bg-tertiary] p-1 rounded-lg">
381
+ {(['none', 'sm', 'md', 'lg', 'full'] as const).map((radius) => (
382
+ <label
383
+ key={radius}
384
+ className={clsx(
385
+ 'px-3 py-1.5 text-xs font-medium rounded-md cursor-pointer transition-all',
386
+ watch('borderRadius') === radius
387
+ ? 'bg-[--bg-primary] text-primary shadow-sm'
388
+ : 'text-tertiary hover:text-secondary'
389
+ )}
390
+ >
391
+ <input
392
+ type="radio"
393
+ {...register('borderRadius')}
394
+ value={radius}
395
+ className="sr-only"
396
+ />
397
+ {radius}
398
+ </label>
399
+ ))}
400
+ </div>
401
+ </div>
402
+
403
+ {/* Dark Mode */}
404
+ <div className="flex items-center justify-between">
405
+ <div>
406
+ <span className="text-sm text-primary">Dark Mode</span>
407
+ <p className="text-xs text-tertiary mt-0.5">Include dark theme styles</p>
408
+ </div>
409
+ <label className="relative cursor-pointer">
410
+ <input
411
+ type="checkbox"
412
+ {...register('darkMode')}
413
+ className="sr-only peer"
414
+ />
415
+ <div className="w-11 h-6 bg-[--bg-tertiary] rounded-full peer-checked:bg-[--color-accent] transition-colors" />
416
+ <div className="absolute left-0.5 top-0.5 w-5 h-5 bg-white rounded-full shadow transition-transform peer-checked:translate-x-5" />
417
+ </label>
418
+ </div>
419
+ </div>
420
+
421
+ <div className="h-px bg-[--border]" />
422
+
423
+ {/* Submit */}
424
+ <button
425
+ type="submit"
426
+ disabled={isSubmitting}
427
+ className={clsx(
428
+ 'w-full py-3 px-6 rounded-xl text-sm font-medium transition-all',
429
+ 'bg-[--color-accent] text-white',
430
+ 'hover:brightness-110',
431
+ 'focus:outline-none focus:ring-2 focus:ring-[--color-accent] focus:ring-offset-2 focus:ring-offset-[--bg-primary]',
432
+ 'disabled:opacity-50 disabled:cursor-not-allowed'
433
+ )}
434
+ >
435
+ {isSubmitting ? 'Creating...' : 'Create Component'}
436
+ </button>
437
+ </form>
438
+ </div>
439
+ </div>
440
+ );
441
+ }
@@ -0,0 +1,22 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ interface LayoutProps {
4
+ leftSidebar: ReactNode;
5
+ children: ReactNode;
6
+ }
7
+
8
+ export function Layout({ leftSidebar, children }: LayoutProps) {
9
+ return (
10
+ <div className="flex h-screen overflow-hidden bg-[--bg-primary]">
11
+ {/* Left Sidebar */}
12
+ <aside className="hidden md:flex md:w-56 lg:w-64 flex-col border-r border-[--border] bg-[--bg-secondary] flex-shrink-0">
13
+ {leftSidebar}
14
+ </aside>
15
+
16
+ {/* Main Content - Full width */}
17
+ <main className="flex-1 flex flex-col overflow-hidden min-w-0">
18
+ {children}
19
+ </main>
20
+ </div>
21
+ );
22
+ }