@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,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
|
+
}
|