radtools 0.1.0
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/README.md +108 -0
- package/bin/radtools.js +5 -0
- package/dist/cli/index.js +427 -0
- package/package.json +55 -0
- package/templates/api-routes/assets/optimize/route.ts +94 -0
- package/templates/api-routes/assets/route.ts +159 -0
- package/templates/api-routes/components/create-folder/route.ts +55 -0
- package/templates/api-routes/components/route.ts +156 -0
- package/templates/api-routes/fonts/route.ts +96 -0
- package/templates/api-routes/fonts/upload/route.ts +79 -0
- package/templates/api-routes/read-css/route.ts +29 -0
- package/templates/api-routes/write-css/route.ts +423 -0
- package/templates/components/Rad_os/AppWindow.tsx +423 -0
- package/templates/components/Rad_os/MobileAppModal.tsx +76 -0
- package/templates/components/Rad_os/WindowTitleBar.tsx +290 -0
- package/templates/components/icons/Icon.tsx +224 -0
- package/templates/components/icons/README.md +85 -0
- package/templates/components/icons/index.ts +20 -0
- package/templates/components/icons.tsx +164 -0
- package/templates/components/ui/Accordion.tsx +268 -0
- package/templates/components/ui/Alert.tsx +111 -0
- package/templates/components/ui/Badge.tsx +87 -0
- package/templates/components/ui/Breadcrumbs.tsx +88 -0
- package/templates/components/ui/Button.tsx +249 -0
- package/templates/components/ui/Card.tsx +137 -0
- package/templates/components/ui/Checkbox.tsx +137 -0
- package/templates/components/ui/ContextMenu.tsx +220 -0
- package/templates/components/ui/Dialog.tsx +264 -0
- package/templates/components/ui/Divider.tsx +70 -0
- package/templates/components/ui/DropdownMenu.tsx +301 -0
- package/templates/components/ui/HelpPanel.tsx +119 -0
- package/templates/components/ui/Input.tsx +176 -0
- package/templates/components/ui/Popover.tsx +211 -0
- package/templates/components/ui/Progress.tsx +158 -0
- package/templates/components/ui/Select.tsx +134 -0
- package/templates/components/ui/Sheet.tsx +316 -0
- package/templates/components/ui/Slider.tsx +223 -0
- package/templates/components/ui/Switch.tsx +155 -0
- package/templates/components/ui/Tabs.tsx +253 -0
- package/templates/components/ui/Toast.tsx +192 -0
- package/templates/components/ui/Tooltip.tsx +129 -0
- package/templates/components/ui/hooks/useModalBehavior.ts +66 -0
- package/templates/components/ui/index.ts +84 -0
- package/templates/devtools/DevToolsPanel.tsx +261 -0
- package/templates/devtools/DevToolsProvider.tsx +43 -0
- package/templates/devtools/components/BreakpointIndicator.tsx +49 -0
- package/templates/devtools/components/ColorPicker.tsx +33 -0
- package/templates/devtools/components/ComponentsSecondaryNav.tsx +44 -0
- package/templates/devtools/components/ContextualFooter.tsx +56 -0
- package/templates/devtools/components/DraggablePanel.tsx +43 -0
- package/templates/devtools/components/PrimaryNavigationFooter.tsx +254 -0
- package/templates/devtools/components/SearchableColorDropdown.tsx +253 -0
- package/templates/devtools/components/SecondaryNavigation.tsx +36 -0
- package/templates/devtools/components/TokenDropdown.tsx +47 -0
- package/templates/devtools/components/TypographyFooter.tsx +145 -0
- package/templates/devtools/hooks/useMockState.ts +16 -0
- package/templates/devtools/index.ts +17 -0
- package/templates/devtools/lib/componentScanner.ts +78 -0
- package/templates/devtools/lib/cssParser.ts +465 -0
- package/templates/devtools/lib/searchIndexes.ts +45 -0
- package/templates/devtools/lib/selectorGenerator.ts +86 -0
- package/templates/devtools/store/index.ts +66 -0
- package/templates/devtools/store/slices/assetsSlice.ts +106 -0
- package/templates/devtools/store/slices/componentsSlice.ts +59 -0
- package/templates/devtools/store/slices/mockStatesSlice.ts +77 -0
- package/templates/devtools/store/slices/panelSlice.ts +17 -0
- package/templates/devtools/store/slices/typographySlice.ts +538 -0
- package/templates/devtools/store/slices/variablesSlice.ts +167 -0
- package/templates/devtools/tabs/AssetsTab/AssetGrid.tsx +76 -0
- package/templates/devtools/tabs/AssetsTab/FolderTree.tsx +53 -0
- package/templates/devtools/tabs/AssetsTab/UploadDropzone.tsx +76 -0
- package/templates/devtools/tabs/AssetsTab/index.tsx +182 -0
- package/templates/devtools/tabs/ComponentsTab/AddTabButton.tsx +63 -0
- package/templates/devtools/tabs/ComponentsTab/ComponentList.tsx +153 -0
- package/templates/devtools/tabs/ComponentsTab/DesignSystemTab.tsx +1515 -0
- package/templates/devtools/tabs/ComponentsTab/DynamicFolderTab.tsx +113 -0
- package/templates/devtools/tabs/ComponentsTab/PropDisplay.tsx +55 -0
- package/templates/devtools/tabs/ComponentsTab/index.tsx +167 -0
- package/templates/devtools/tabs/ComponentsTab/previews/.gitkeep +4 -0
- package/templates/devtools/tabs/ComponentsTab/previews/Rad_os.tsx +262 -0
- package/templates/devtools/tabs/ComponentsTab/tabConfig.ts +53 -0
- package/templates/devtools/tabs/MockStatesTab/index.tsx +29 -0
- package/templates/devtools/tabs/TypographyTab/FontManager.tsx +421 -0
- package/templates/devtools/tabs/TypographyTab/TypographyStylesDisplay.tsx +290 -0
- package/templates/devtools/tabs/TypographyTab/index.tsx +98 -0
- package/templates/devtools/tabs/VariablesTab/BaseColorEditor.tsx +267 -0
- package/templates/devtools/tabs/VariablesTab/BorderRadiusEditor.tsx +37 -0
- package/templates/devtools/tabs/VariablesTab/ColorModeSelector.tsx +235 -0
- package/templates/devtools/tabs/VariablesTab/index.tsx +100 -0
- package/templates/devtools/types/index.ts +99 -0
- package/templates/globals.css +574 -0
- package/templates/hooks/index.ts +1 -0
- package/templates/hooks/useWindowManager.ts +212 -0
- package/templates/public/assets/icons/avatar.svg +18 -0
- package/templates/public/assets/icons/checkmark-filled.svg +14 -0
- package/templates/public/assets/icons/checkmark.svg +14 -0
- package/templates/public/assets/icons/chevron-down.svg +14 -0
- package/templates/public/assets/icons/close.svg +14 -0
- package/templates/public/assets/icons/copy.svg +14 -0
- package/templates/public/assets/icons/download.svg +14 -0
- package/templates/public/assets/icons/expand.svg +31 -0
- package/templates/public/assets/icons/file-blank.svg +17 -0
- package/templates/public/assets/icons/file-image.svg +19 -0
- package/templates/public/assets/icons/file-written.svg +17 -0
- package/templates/public/assets/icons/folder-closed.svg +17 -0
- package/templates/public/assets/icons/folder-open.svg +17 -0
- package/templates/public/assets/icons/hamburger.svg +18 -0
- package/templates/public/assets/icons/home-outline.svg +28 -0
- package/templates/public/assets/icons/home.svg +30 -0
- package/templates/public/assets/icons/hourglass.svg +25 -0
- package/templates/public/assets/icons/information-circle.svg +14 -0
- package/templates/public/assets/icons/information.svg +17 -0
- package/templates/public/assets/icons/lightning.svg +14 -0
- package/templates/public/assets/icons/locked.svg +17 -0
- package/templates/public/assets/icons/not-allowed.svg +14 -0
- package/templates/public/assets/icons/plus.svg +5 -0
- package/templates/public/assets/icons/power-thin.svg +17 -0
- package/templates/public/assets/icons/power.svg +17 -0
- package/templates/public/assets/icons/question-block.svg +14 -0
- package/templates/public/assets/icons/question.svg +17 -0
- package/templates/public/assets/icons/refresh-block.svg +14 -0
- package/templates/public/assets/icons/refresh.svg +17 -0
- package/templates/public/assets/icons/save.svg +14 -0
- package/templates/public/assets/icons/search.svg +25 -0
- package/templates/public/assets/icons/settings.svg +14 -0
- package/templates/public/assets/icons/trash-full.svg +21 -0
- package/templates/public/assets/icons/trash-open.svg +23 -0
- package/templates/public/assets/icons/trash.svg +18 -0
- package/templates/public/assets/icons/unlocked.svg +17 -0
- package/templates/public/assets/icons/waring-triangle-filled.svg +17 -0
- package/templates/public/assets/icons/warning-triangle-filled-2.svg +30 -0
- package/templates/public/assets/icons/warning-triangle-lines.svg +29 -0
- package/templates/public/assets/icons/wrench.svg +17 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useDevToolsStore } from '../../store';
|
|
4
|
+
import { useEffect, useState } from 'react';
|
|
5
|
+
import { Button } from '@/components/ui/Button';
|
|
6
|
+
import { useToast } from '@/components/ui/Toast';
|
|
7
|
+
|
|
8
|
+
// Available semantic tokens that can be overridden in color modes
|
|
9
|
+
const OVERRIDABLE_TOKENS = [
|
|
10
|
+
{ id: 'primary', label: 'Primary' },
|
|
11
|
+
{ id: 'secondary', label: 'Secondary' },
|
|
12
|
+
{ id: 'tertiary', label: 'Tertiary' },
|
|
13
|
+
{ id: 'alternate', label: 'Alternate' },
|
|
14
|
+
{ id: 'heading', label: 'Heading' },
|
|
15
|
+
{ id: 'body', label: 'Body' },
|
|
16
|
+
{ id: 'caption', label: 'Caption' },
|
|
17
|
+
{ id: 'border', label: 'Border' },
|
|
18
|
+
{ id: 'muted', label: 'Muted' },
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
export function ColorModeSelector() {
|
|
22
|
+
const { colorModes, activeColorMode, setActiveColorMode, addColorMode, deleteColorMode, baseColors } = useDevToolsStore();
|
|
23
|
+
const [showAddForm, setShowAddForm] = useState(false);
|
|
24
|
+
const [newModeName, setNewModeName] = useState('');
|
|
25
|
+
const [overrides, setOverrides] = useState<Record<string, string>>({});
|
|
26
|
+
const { addToast } = useToast();
|
|
27
|
+
|
|
28
|
+
// Apply color mode to document
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
const html = document.documentElement;
|
|
31
|
+
|
|
32
|
+
// Remove all color mode classes
|
|
33
|
+
colorModes.forEach((mode) => {
|
|
34
|
+
html.classList.remove(mode.name);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Add active color mode class
|
|
38
|
+
if (activeColorMode) {
|
|
39
|
+
const activeMode = colorModes.find((m) => m.id === activeColorMode);
|
|
40
|
+
if (activeMode) {
|
|
41
|
+
html.classList.add(activeMode.name);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}, [activeColorMode, colorModes]);
|
|
45
|
+
|
|
46
|
+
const handleAddOverride = (token: string, colorId: string) => {
|
|
47
|
+
if (colorId) {
|
|
48
|
+
setOverrides({ ...overrides, [token]: colorId });
|
|
49
|
+
} else {
|
|
50
|
+
const { [token]: _, ...rest } = overrides;
|
|
51
|
+
setOverrides(rest);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const handleCreateColorMode = () => {
|
|
56
|
+
if (!newModeName.trim()) {
|
|
57
|
+
addToast({
|
|
58
|
+
title: 'Missing name',
|
|
59
|
+
description: 'Please enter a mode name',
|
|
60
|
+
variant: 'warning',
|
|
61
|
+
});
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (Object.keys(overrides).length === 0) {
|
|
65
|
+
addToast({
|
|
66
|
+
title: 'No overrides',
|
|
67
|
+
description: 'Please add at least one override',
|
|
68
|
+
variant: 'warning',
|
|
69
|
+
});
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Convert color IDs to CSS variable references
|
|
74
|
+
const cssOverrides: Record<string, string> = {};
|
|
75
|
+
for (const [token, colorId] of Object.entries(overrides)) {
|
|
76
|
+
const color = baseColors.find(c => c.id === colorId);
|
|
77
|
+
if (color) {
|
|
78
|
+
// Store as reference format: "neutral-darkest" or "sun-yellow"
|
|
79
|
+
const varName = color.category === 'neutral' ? `neutral-${color.name}` : color.name;
|
|
80
|
+
cssOverrides[token] = varName;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
addColorMode({
|
|
85
|
+
name: newModeName.toLowerCase().replace(/\s+/g, '-'),
|
|
86
|
+
className: `.${newModeName.toLowerCase().replace(/\s+/g, '-')}`,
|
|
87
|
+
overrides: cssOverrides,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Reset form
|
|
91
|
+
setNewModeName('');
|
|
92
|
+
setOverrides({});
|
|
93
|
+
setShowAddForm(false);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const handleDeleteColorMode = (id: string) => {
|
|
97
|
+
if (activeColorMode === id) {
|
|
98
|
+
setActiveColorMode(null);
|
|
99
|
+
}
|
|
100
|
+
deleteColorMode(id);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// Get all base colors for the dropdown
|
|
104
|
+
const allColors = [...baseColors.filter(c => c.category === 'brand'), ...baseColors.filter(c => c.category === 'neutral')];
|
|
105
|
+
|
|
106
|
+
return (
|
|
107
|
+
<div className="space-y-3">
|
|
108
|
+
<div className="flex items-center justify-between">
|
|
109
|
+
<h3>Color Mode</h3>
|
|
110
|
+
</div>
|
|
111
|
+
|
|
112
|
+
<div className="flex flex-wrap gap-2">
|
|
113
|
+
<Button
|
|
114
|
+
variant={activeColorMode === null ? 'primary' : 'ghost'}
|
|
115
|
+
size="md"
|
|
116
|
+
onClick={() => setActiveColorMode(null)}
|
|
117
|
+
className={activeColorMode === null ? 'border-2' : ''}
|
|
118
|
+
>
|
|
119
|
+
Default
|
|
120
|
+
</Button>
|
|
121
|
+
<Button
|
|
122
|
+
variant="outline"
|
|
123
|
+
size="md"
|
|
124
|
+
iconName={showAddForm ? "close" : undefined}
|
|
125
|
+
onClick={() => setShowAddForm(!showAddForm)}
|
|
126
|
+
>
|
|
127
|
+
{showAddForm ? 'Cancel' : '+ Add Mode'}
|
|
128
|
+
</Button>
|
|
129
|
+
|
|
130
|
+
{colorModes.map((mode) => (
|
|
131
|
+
<div key={mode.id} className="flex items-center gap-1">
|
|
132
|
+
<Button
|
|
133
|
+
variant={activeColorMode === mode.id ? 'primary' : 'ghost'}
|
|
134
|
+
size="md"
|
|
135
|
+
onClick={() => setActiveColorMode(mode.id)}
|
|
136
|
+
className={activeColorMode === mode.id ? 'border-2' : ''}
|
|
137
|
+
>
|
|
138
|
+
{mode.name}
|
|
139
|
+
</Button>
|
|
140
|
+
<Button
|
|
141
|
+
variant="ghost"
|
|
142
|
+
size="md"
|
|
143
|
+
iconOnly={true}
|
|
144
|
+
iconName="close"
|
|
145
|
+
onClick={() => handleDeleteColorMode(mode.id)}
|
|
146
|
+
className="text-error-red hover:bg-error-red/10 active:bg-error-red/20"
|
|
147
|
+
title="Delete color mode"
|
|
148
|
+
/>
|
|
149
|
+
</div>
|
|
150
|
+
))}
|
|
151
|
+
</div>
|
|
152
|
+
|
|
153
|
+
{/* Add Color Mode Form */}
|
|
154
|
+
{showAddForm && (
|
|
155
|
+
<div className="p-3 bg-black/10 rounded-md space-y-3">
|
|
156
|
+
<h4>Create Color Mode</h4>
|
|
157
|
+
|
|
158
|
+
<div>
|
|
159
|
+
<label className="block font-mondwest text-sm text-black/60 mb-1">Mode Name</label>
|
|
160
|
+
<input
|
|
161
|
+
type="text"
|
|
162
|
+
value={newModeName}
|
|
163
|
+
onChange={(e) => setNewModeName(e.target.value)}
|
|
164
|
+
placeholder="e.g., dark"
|
|
165
|
+
className="w-full px-2 py-1.5 font-mondwest text-base bg-warm-cloud border border-black rounded-sm text-black"
|
|
166
|
+
/>
|
|
167
|
+
</div>
|
|
168
|
+
|
|
169
|
+
<div>
|
|
170
|
+
<label className="block font-mondwest text-sm text-black/60 mb-2">Token Overrides</label>
|
|
171
|
+
<div className="space-y-2">
|
|
172
|
+
{OVERRIDABLE_TOKENS.map((token) => (
|
|
173
|
+
<div key={token.id} className="flex items-center gap-2">
|
|
174
|
+
<span className="w-24 font-mondwest text-sm text-black">{token.label}</span>
|
|
175
|
+
<select
|
|
176
|
+
value={overrides[token.id] || ''}
|
|
177
|
+
onChange={(e) => handleAddOverride(token.id, e.target.value)}
|
|
178
|
+
className="flex-1 px-2 py-1 font-mondwest text-sm bg-warm-cloud border border-black rounded-sm text-black"
|
|
179
|
+
>
|
|
180
|
+
<option value="">— No override —</option>
|
|
181
|
+
<optgroup label="Brand Colors">
|
|
182
|
+
{allColors.filter(c => c.category === 'brand').map((color) => (
|
|
183
|
+
<option key={color.id} value={color.id}>
|
|
184
|
+
{color.displayName}
|
|
185
|
+
</option>
|
|
186
|
+
))}
|
|
187
|
+
</optgroup>
|
|
188
|
+
<optgroup label="Neutral Colors">
|
|
189
|
+
{allColors.filter(c => c.category === 'neutral').map((color) => (
|
|
190
|
+
<option key={color.id} value={color.id}>
|
|
191
|
+
{color.displayName}
|
|
192
|
+
</option>
|
|
193
|
+
))}
|
|
194
|
+
</optgroup>
|
|
195
|
+
</select>
|
|
196
|
+
{overrides[token.id] && (
|
|
197
|
+
<div
|
|
198
|
+
className="w-6 h-6 rounded-sm border border-black"
|
|
199
|
+
style={{ backgroundColor: baseColors.find(c => c.id === overrides[token.id])?.value }}
|
|
200
|
+
/>
|
|
201
|
+
)}
|
|
202
|
+
</div>
|
|
203
|
+
))}
|
|
204
|
+
</div>
|
|
205
|
+
</div>
|
|
206
|
+
|
|
207
|
+
<Button
|
|
208
|
+
variant="secondary"
|
|
209
|
+
size="md"
|
|
210
|
+
onClick={handleCreateColorMode}
|
|
211
|
+
>
|
|
212
|
+
Create Mode
|
|
213
|
+
</Button>
|
|
214
|
+
</div>
|
|
215
|
+
)}
|
|
216
|
+
|
|
217
|
+
{/* Active Overrides Display */}
|
|
218
|
+
{activeColorMode && (
|
|
219
|
+
<div className="p-3 bg-black/10 rounded-md">
|
|
220
|
+
<h4 className="mb-2">Active Overrides</h4>
|
|
221
|
+
<div className="space-y-1">
|
|
222
|
+
{Object.entries(colorModes.find((m) => m.id === activeColorMode)?.overrides || {}).map(([token, ref]) => (
|
|
223
|
+
<div key={token} className="flex items-center gap-2 font-mondwest text-base">
|
|
224
|
+
<span className="text-black font-mono">{token}</span>
|
|
225
|
+
<span className="text-black/60">→</span>
|
|
226
|
+
<span className="text-black/60 font-mono">{ref}</span>
|
|
227
|
+
</div>
|
|
228
|
+
))}
|
|
229
|
+
</div>
|
|
230
|
+
</div>
|
|
231
|
+
)}
|
|
232
|
+
</div>
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState, useRef } from 'react';
|
|
4
|
+
import { useDevToolsStore } from '../../store';
|
|
5
|
+
import { BaseColorEditor } from './BaseColorEditor';
|
|
6
|
+
import { ColorModeSelector } from './ColorModeSelector';
|
|
7
|
+
import { BorderRadiusEditor } from './BorderRadiusEditor';
|
|
8
|
+
import { Button } from '@/components/ui/Button';
|
|
9
|
+
import { Divider } from '@/components/ui/Divider';
|
|
10
|
+
|
|
11
|
+
export function VariablesTab() {
|
|
12
|
+
const { loadFromCSS, syncToCSS, baseColors } = useDevToolsStore();
|
|
13
|
+
const [isSaving, setIsSaving] = useState(false);
|
|
14
|
+
const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null);
|
|
15
|
+
const messageTimerRef = useRef<NodeJS.Timeout>(undefined);
|
|
16
|
+
|
|
17
|
+
// Cleanup timeout on unmount
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
return () => {
|
|
20
|
+
if (messageTimerRef.current) clearTimeout(messageTimerRef.current);
|
|
21
|
+
};
|
|
22
|
+
}, []);
|
|
23
|
+
|
|
24
|
+
// Load CSS on mount to sync with actual file
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
loadFromCSS();
|
|
27
|
+
}, [loadFromCSS]);
|
|
28
|
+
|
|
29
|
+
const handleSave = async () => {
|
|
30
|
+
setIsSaving(true);
|
|
31
|
+
setMessage(null);
|
|
32
|
+
try {
|
|
33
|
+
await syncToCSS();
|
|
34
|
+
setMessage({ type: 'success', text: 'Saved to globals.css!' });
|
|
35
|
+
} catch {
|
|
36
|
+
setMessage({ type: 'error', text: 'Failed to save. Check console.' });
|
|
37
|
+
} finally {
|
|
38
|
+
setIsSaving(false);
|
|
39
|
+
if (messageTimerRef.current) clearTimeout(messageTimerRef.current);
|
|
40
|
+
messageTimerRef.current = setTimeout(() => setMessage(null), 3000);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const handleReload = async () => {
|
|
45
|
+
await loadFromCSS();
|
|
46
|
+
setMessage({ type: 'success', text: 'Reloaded from CSS!' });
|
|
47
|
+
if (messageTimerRef.current) clearTimeout(messageTimerRef.current);
|
|
48
|
+
messageTimerRef.current = setTimeout(() => setMessage(null), 3000);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<div className="flex flex-col h-full overflow-auto pt-4 pb-4 pl-4 pr-2 bg-[var(--color-white)] border border-black rounded space-y-4">
|
|
53
|
+
{/* Header Actions */}
|
|
54
|
+
<div className="flex items-center justify-between">
|
|
55
|
+
<h2>Design Tokens</h2>
|
|
56
|
+
<div className="flex items-center gap-2">
|
|
57
|
+
<Button
|
|
58
|
+
variant="outline"
|
|
59
|
+
size="md"
|
|
60
|
+
iconName="refresh"
|
|
61
|
+
onClick={handleReload}
|
|
62
|
+
>
|
|
63
|
+
Reload
|
|
64
|
+
</Button>
|
|
65
|
+
<Button
|
|
66
|
+
variant="secondary"
|
|
67
|
+
size="md"
|
|
68
|
+
iconName="save"
|
|
69
|
+
onClick={handleSave}
|
|
70
|
+
disabled={isSaving}
|
|
71
|
+
>
|
|
72
|
+
{isSaving ? 'Saving...' : 'Save to CSS'}
|
|
73
|
+
</Button>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
{/* Status Message */}
|
|
78
|
+
{message && (
|
|
79
|
+
<div
|
|
80
|
+
className={`px-3 py-2 font-mondwest text-base rounded-sm ${
|
|
81
|
+
message.type === 'success'
|
|
82
|
+
? 'bg-green text-black'
|
|
83
|
+
: 'bg-error-red text-warm-cloud'
|
|
84
|
+
}`}
|
|
85
|
+
>
|
|
86
|
+
{message.text}
|
|
87
|
+
</div>
|
|
88
|
+
)}
|
|
89
|
+
|
|
90
|
+
{/* Editors */}
|
|
91
|
+
<div className="space-y-4" data-edit-scope="theme-variables">
|
|
92
|
+
<ColorModeSelector />
|
|
93
|
+
<Divider />
|
|
94
|
+
<BaseColorEditor />
|
|
95
|
+
<Divider />
|
|
96
|
+
<BorderRadiusEditor />
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// Design Token Types
|
|
2
|
+
|
|
3
|
+
// Base Color (brand or neutral primitive)
|
|
4
|
+
export interface BaseColor {
|
|
5
|
+
id: string;
|
|
6
|
+
name: string; // e.g., "sun-yellow", "lightest"
|
|
7
|
+
displayName: string; // e.g., "Sun Yellow", "Lightest"
|
|
8
|
+
value: string; // e.g., "#FCE184"
|
|
9
|
+
category: 'brand' | 'neutral';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Legacy alias for backwards compatibility
|
|
13
|
+
export type BrandColor = BaseColor;
|
|
14
|
+
|
|
15
|
+
export interface ColorMode {
|
|
16
|
+
id: string;
|
|
17
|
+
name: string; // e.g., "dark"
|
|
18
|
+
className: string; // e.g., ".dark"
|
|
19
|
+
overrides: Record<string, string>; // token name -> brand color reference
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Component Discovery Types
|
|
23
|
+
export interface DiscoveredComponent {
|
|
24
|
+
name: string;
|
|
25
|
+
path: string;
|
|
26
|
+
props: PropDefinition[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface PropDefinition {
|
|
30
|
+
name: string;
|
|
31
|
+
type: string;
|
|
32
|
+
required: boolean;
|
|
33
|
+
defaultValue?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Asset Types
|
|
37
|
+
export interface AssetFile {
|
|
38
|
+
name: string;
|
|
39
|
+
path: string;
|
|
40
|
+
type: 'image' | 'video' | 'other';
|
|
41
|
+
size: number;
|
|
42
|
+
dimensions?: { width: number; height: number };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface AssetFolder {
|
|
46
|
+
name: string;
|
|
47
|
+
path: string;
|
|
48
|
+
children: (AssetFolder | AssetFile)[];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Mock State Types
|
|
52
|
+
export interface MockState {
|
|
53
|
+
id: string;
|
|
54
|
+
name: string;
|
|
55
|
+
description: string;
|
|
56
|
+
category: string;
|
|
57
|
+
values: Record<string, unknown>;
|
|
58
|
+
active: boolean;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Tab Types
|
|
62
|
+
export type Tab = 'variables' | 'typography' | 'components' | 'assets' | 'mockStates';
|
|
63
|
+
|
|
64
|
+
// Typography Types
|
|
65
|
+
|
|
66
|
+
// Font file within a font definition
|
|
67
|
+
export interface FontFile {
|
|
68
|
+
id: string;
|
|
69
|
+
weight: number; // 400, 700, etc.
|
|
70
|
+
style: string; // "normal", "italic"
|
|
71
|
+
format: 'woff2' | 'woff' | 'ttf' | 'otf';
|
|
72
|
+
path: string; // "/fonts/Mondwest-Regular.woff2"
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Font Definition
|
|
76
|
+
export interface FontDefinition {
|
|
77
|
+
id: string;
|
|
78
|
+
name: string; // "Mondwest", "Joystix Monospace"
|
|
79
|
+
family: string; // CSS font-family value
|
|
80
|
+
files: FontFile[]; // Array of font files (Regular, Bold, etc.)
|
|
81
|
+
weights: number[]; // [400, 700]
|
|
82
|
+
styles: string[]; // ["normal", "italic"]
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Typography Style Definition (for @layer base HTML elements)
|
|
86
|
+
// Matches Webflow style guide sizing/font choices but uses Tailwind @apply
|
|
87
|
+
export interface TypographyStyle {
|
|
88
|
+
id: string;
|
|
89
|
+
element: string; // "h1", "h2", "h3", "h4", "h5", "h6", "p", "a", "ul", "ol", "li"
|
|
90
|
+
fontFamilyId: string; // References FontDefinition.id
|
|
91
|
+
fontSize: string; // Tailwind class: "text-4xl", "text-3xl", etc.
|
|
92
|
+
lineHeight?: string; // Tailwind class: "leading-relaxed", "leading-tight", etc.
|
|
93
|
+
letterSpacing?: string; // Tailwind class: "tracking-wide", "tracking-normal", etc.
|
|
94
|
+
fontWeight: string; // Tailwind class: "font-bold", "font-semibold", "font-normal", etc.
|
|
95
|
+
baseColorId: string; // References BaseColor.id for text color
|
|
96
|
+
displayName: string; // "Heading 1", "Heading 2", "Paragraph", etc.
|
|
97
|
+
utilities?: string[]; // Additional Tailwind utilities: ["underline"], ["hover:text-link-hover"]
|
|
98
|
+
}
|
|
99
|
+
|