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,538 @@
|
|
|
1
|
+
import { StateCreator } from 'zustand';
|
|
2
|
+
import type { FontDefinition, FontFile, TypographyStyle } from '../../types';
|
|
3
|
+
|
|
4
|
+
export interface TypographySlice {
|
|
5
|
+
// State
|
|
6
|
+
fonts: FontDefinition[];
|
|
7
|
+
typographyStyles: TypographyStyle[];
|
|
8
|
+
|
|
9
|
+
// Actions - Fonts
|
|
10
|
+
addFont: (font: Omit<FontDefinition, 'id'>) => void;
|
|
11
|
+
updateFont: (id: string, updates: Partial<FontDefinition>) => void;
|
|
12
|
+
deleteFont: (id: string) => void;
|
|
13
|
+
addFontFile: (fontId: string, file: Omit<FontFile, 'id'>) => void;
|
|
14
|
+
removeFontFile: (fontId: string, fileId: string) => void;
|
|
15
|
+
|
|
16
|
+
// Actions - Typography Styles
|
|
17
|
+
addTypographyStyle: (style: Omit<TypographyStyle, 'id'>) => void;
|
|
18
|
+
updateTypographyStyle: (id: string, updates: Partial<TypographyStyle>) => void;
|
|
19
|
+
deleteTypographyStyle: (id: string) => void;
|
|
20
|
+
|
|
21
|
+
// Helpers
|
|
22
|
+
getFontById: (id: string) => FontDefinition | undefined;
|
|
23
|
+
getFontByFamily: (family: string) => FontDefinition | undefined;
|
|
24
|
+
|
|
25
|
+
// Sync
|
|
26
|
+
syncTypographyToCSS: () => Promise<void>;
|
|
27
|
+
loadTypographyFromCSS: () => Promise<void>;
|
|
28
|
+
loadFontsFromFilesystem: () => Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Default fonts matching radOS
|
|
32
|
+
const defaultFonts: FontDefinition[] = [
|
|
33
|
+
{
|
|
34
|
+
id: 'mondwest',
|
|
35
|
+
name: 'Mondwest',
|
|
36
|
+
family: 'Mondwest',
|
|
37
|
+
files: [
|
|
38
|
+
{ id: 'mondwest-regular', weight: 400, style: 'normal', format: 'woff2', path: '/fonts/Mondwest-Regular.woff2' },
|
|
39
|
+
{ id: 'mondwest-bold', weight: 700, style: 'normal', format: 'woff2', path: '/fonts/Mondwest-Bold.woff2' },
|
|
40
|
+
],
|
|
41
|
+
weights: [400, 700],
|
|
42
|
+
styles: ['normal'],
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: 'joystix',
|
|
46
|
+
name: 'Joystix Monospace',
|
|
47
|
+
family: 'Joystix Monospace',
|
|
48
|
+
files: [
|
|
49
|
+
{ id: 'joystix-regular', weight: 400, style: 'normal', format: 'ttf', path: '/fonts/joystix_monospace.ttf' },
|
|
50
|
+
],
|
|
51
|
+
weights: [400],
|
|
52
|
+
styles: ['normal'],
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
// Default typography styles matching Webflow style guide with @layer base
|
|
57
|
+
const defaultTypographyStyles: TypographyStyle[] = [
|
|
58
|
+
{
|
|
59
|
+
id: 'h1',
|
|
60
|
+
element: 'h1',
|
|
61
|
+
fontFamilyId: 'mondwest',
|
|
62
|
+
fontSize: 'text-4xl',
|
|
63
|
+
lineHeight: 'leading-tight',
|
|
64
|
+
fontWeight: 'font-bold',
|
|
65
|
+
baseColorId: 'black',
|
|
66
|
+
displayName: 'Heading 1',
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
id: 'h2',
|
|
70
|
+
element: 'h2',
|
|
71
|
+
fontFamilyId: 'mondwest',
|
|
72
|
+
fontSize: 'text-3xl',
|
|
73
|
+
lineHeight: 'leading-tight',
|
|
74
|
+
fontWeight: 'font-normal',
|
|
75
|
+
baseColorId: 'black',
|
|
76
|
+
displayName: 'Heading 2',
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
id: 'h3',
|
|
80
|
+
element: 'h3',
|
|
81
|
+
fontFamilyId: 'mondwest',
|
|
82
|
+
fontSize: 'text-2xl',
|
|
83
|
+
lineHeight: 'leading-snug',
|
|
84
|
+
fontWeight: 'font-semibold',
|
|
85
|
+
baseColorId: 'black',
|
|
86
|
+
displayName: 'Heading 3',
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
id: 'h4',
|
|
90
|
+
element: 'h4',
|
|
91
|
+
fontFamilyId: 'mondwest',
|
|
92
|
+
fontSize: 'text-xl',
|
|
93
|
+
lineHeight: 'leading-snug',
|
|
94
|
+
fontWeight: 'font-medium',
|
|
95
|
+
baseColorId: 'black',
|
|
96
|
+
displayName: 'Heading 4',
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
id: 'h5',
|
|
100
|
+
element: 'h5',
|
|
101
|
+
fontFamilyId: 'mondwest',
|
|
102
|
+
fontSize: 'text-lg',
|
|
103
|
+
lineHeight: 'leading-normal',
|
|
104
|
+
fontWeight: 'font-medium',
|
|
105
|
+
baseColorId: 'black',
|
|
106
|
+
displayName: 'Heading 5',
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: 'h6',
|
|
110
|
+
element: 'h6',
|
|
111
|
+
fontFamilyId: 'mondwest',
|
|
112
|
+
fontSize: 'text-base',
|
|
113
|
+
lineHeight: 'leading-normal',
|
|
114
|
+
fontWeight: 'font-medium',
|
|
115
|
+
baseColorId: 'black',
|
|
116
|
+
displayName: 'Heading 6',
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
id: 'p',
|
|
120
|
+
element: 'p',
|
|
121
|
+
fontFamilyId: 'mondwest',
|
|
122
|
+
fontSize: 'text-base',
|
|
123
|
+
lineHeight: 'leading-relaxed',
|
|
124
|
+
fontWeight: 'font-normal',
|
|
125
|
+
baseColorId: 'black',
|
|
126
|
+
displayName: 'Paragraph',
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
id: 'a',
|
|
130
|
+
element: 'a',
|
|
131
|
+
fontFamilyId: 'mondwest',
|
|
132
|
+
fontSize: 'text-base',
|
|
133
|
+
lineHeight: 'leading-normal',
|
|
134
|
+
fontWeight: 'font-normal',
|
|
135
|
+
baseColorId: 'sky-blue',
|
|
136
|
+
displayName: 'Link',
|
|
137
|
+
utilities: ['underline', 'hover:opacity-80'],
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
id: 'ul',
|
|
141
|
+
element: 'ul',
|
|
142
|
+
fontFamilyId: 'mondwest',
|
|
143
|
+
fontSize: 'text-base',
|
|
144
|
+
lineHeight: 'leading-relaxed',
|
|
145
|
+
fontWeight: 'font-normal',
|
|
146
|
+
baseColorId: 'black',
|
|
147
|
+
displayName: 'Unordered List',
|
|
148
|
+
utilities: ['pl-6'],
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
id: 'ol',
|
|
152
|
+
element: 'ol',
|
|
153
|
+
fontFamilyId: 'mondwest',
|
|
154
|
+
fontSize: 'text-base',
|
|
155
|
+
lineHeight: 'leading-relaxed',
|
|
156
|
+
fontWeight: 'font-normal',
|
|
157
|
+
baseColorId: 'black',
|
|
158
|
+
displayName: 'Ordered List',
|
|
159
|
+
utilities: ['pl-6'],
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
id: 'li',
|
|
163
|
+
element: 'li',
|
|
164
|
+
fontFamilyId: 'mondwest',
|
|
165
|
+
fontSize: 'text-base',
|
|
166
|
+
lineHeight: 'leading-relaxed',
|
|
167
|
+
fontWeight: 'font-normal',
|
|
168
|
+
baseColorId: 'black',
|
|
169
|
+
displayName: 'List Item',
|
|
170
|
+
utilities: ['mb-2'],
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
id: 'small',
|
|
174
|
+
element: 'small',
|
|
175
|
+
fontFamilyId: 'mondwest',
|
|
176
|
+
fontSize: 'text-sm',
|
|
177
|
+
lineHeight: 'leading-normal',
|
|
178
|
+
fontWeight: 'font-normal',
|
|
179
|
+
baseColorId: 'black',
|
|
180
|
+
displayName: 'Small Text',
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
id: 'strong',
|
|
184
|
+
element: 'strong',
|
|
185
|
+
fontFamilyId: 'mondwest',
|
|
186
|
+
fontSize: 'text-base',
|
|
187
|
+
lineHeight: 'leading-normal',
|
|
188
|
+
fontWeight: 'font-bold',
|
|
189
|
+
baseColorId: 'black',
|
|
190
|
+
displayName: 'Strong',
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
id: 'em',
|
|
194
|
+
element: 'em',
|
|
195
|
+
fontFamilyId: 'mondwest',
|
|
196
|
+
fontSize: 'text-base',
|
|
197
|
+
lineHeight: 'leading-normal',
|
|
198
|
+
fontWeight: 'font-normal',
|
|
199
|
+
baseColorId: 'black',
|
|
200
|
+
displayName: 'Emphasis',
|
|
201
|
+
utilities: ['italic'],
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
id: 'code',
|
|
205
|
+
element: 'code',
|
|
206
|
+
fontFamilyId: 'joystix',
|
|
207
|
+
fontSize: 'text-sm',
|
|
208
|
+
lineHeight: 'leading-normal',
|
|
209
|
+
fontWeight: 'font-normal',
|
|
210
|
+
baseColorId: 'black',
|
|
211
|
+
displayName: 'Inline Code',
|
|
212
|
+
utilities: ['bg-black/10', 'px-1', 'py-0.5', 'rounded-sm'],
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
id: 'pre',
|
|
216
|
+
element: 'pre',
|
|
217
|
+
fontFamilyId: 'joystix',
|
|
218
|
+
fontSize: 'text-sm',
|
|
219
|
+
lineHeight: 'leading-relaxed',
|
|
220
|
+
fontWeight: 'font-normal',
|
|
221
|
+
baseColorId: 'black',
|
|
222
|
+
displayName: 'Code Block',
|
|
223
|
+
utilities: ['bg-black/10', 'p-4', 'rounded-sm', 'overflow-x-auto'],
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
id: 'kbd',
|
|
227
|
+
element: 'kbd',
|
|
228
|
+
fontFamilyId: 'joystix',
|
|
229
|
+
fontSize: 'text-xs',
|
|
230
|
+
lineHeight: 'leading-normal',
|
|
231
|
+
fontWeight: 'font-normal',
|
|
232
|
+
baseColorId: 'cream',
|
|
233
|
+
displayName: 'Keyboard Input',
|
|
234
|
+
utilities: ['bg-black', 'px-1', 'py-0.5', 'rounded-sm'],
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
id: 'mark',
|
|
238
|
+
element: 'mark',
|
|
239
|
+
fontFamilyId: 'mondwest',
|
|
240
|
+
fontSize: 'text-base',
|
|
241
|
+
lineHeight: 'leading-normal',
|
|
242
|
+
fontWeight: 'font-normal',
|
|
243
|
+
baseColorId: 'black',
|
|
244
|
+
displayName: 'Highlighted Text',
|
|
245
|
+
utilities: ['bg-sun-yellow'],
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
id: 'blockquote',
|
|
249
|
+
element: 'blockquote',
|
|
250
|
+
fontFamilyId: 'mondwest',
|
|
251
|
+
fontSize: 'text-base',
|
|
252
|
+
lineHeight: 'leading-relaxed',
|
|
253
|
+
fontWeight: 'font-normal',
|
|
254
|
+
baseColorId: 'black',
|
|
255
|
+
displayName: 'Block Quote',
|
|
256
|
+
utilities: ['border-l-4', 'border-black', 'pl-4', 'italic'],
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
id: 'cite',
|
|
260
|
+
element: 'cite',
|
|
261
|
+
fontFamilyId: 'mondwest',
|
|
262
|
+
fontSize: 'text-sm',
|
|
263
|
+
lineHeight: 'leading-normal',
|
|
264
|
+
fontWeight: 'font-normal',
|
|
265
|
+
baseColorId: 'black',
|
|
266
|
+
displayName: 'Citation',
|
|
267
|
+
utilities: ['italic'],
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
id: 'abbr',
|
|
271
|
+
element: 'abbr',
|
|
272
|
+
fontFamilyId: 'mondwest',
|
|
273
|
+
fontSize: 'text-base',
|
|
274
|
+
lineHeight: 'leading-normal',
|
|
275
|
+
fontWeight: 'font-normal',
|
|
276
|
+
baseColorId: 'black',
|
|
277
|
+
displayName: 'Abbreviation',
|
|
278
|
+
utilities: ['underline', 'decoration-dotted'],
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
id: 'dfn',
|
|
282
|
+
element: 'dfn',
|
|
283
|
+
fontFamilyId: 'mondwest',
|
|
284
|
+
fontSize: 'text-base',
|
|
285
|
+
lineHeight: 'leading-normal',
|
|
286
|
+
fontWeight: 'font-normal',
|
|
287
|
+
baseColorId: 'black',
|
|
288
|
+
displayName: 'Definition Term',
|
|
289
|
+
utilities: ['italic'],
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
id: 'q',
|
|
293
|
+
element: 'q',
|
|
294
|
+
fontFamilyId: 'mondwest',
|
|
295
|
+
fontSize: 'text-base',
|
|
296
|
+
lineHeight: 'leading-normal',
|
|
297
|
+
fontWeight: 'font-normal',
|
|
298
|
+
baseColorId: 'black',
|
|
299
|
+
displayName: 'Inline Quote',
|
|
300
|
+
utilities: ['italic'],
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
id: 'sub',
|
|
304
|
+
element: 'sub',
|
|
305
|
+
fontFamilyId: 'mondwest',
|
|
306
|
+
fontSize: 'text-xs',
|
|
307
|
+
lineHeight: 'leading-none',
|
|
308
|
+
fontWeight: 'font-normal',
|
|
309
|
+
baseColorId: 'black',
|
|
310
|
+
displayName: 'Subscript',
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
id: 'sup',
|
|
314
|
+
element: 'sup',
|
|
315
|
+
fontFamilyId: 'mondwest',
|
|
316
|
+
fontSize: 'text-xs',
|
|
317
|
+
lineHeight: 'leading-none',
|
|
318
|
+
fontWeight: 'font-normal',
|
|
319
|
+
baseColorId: 'black',
|
|
320
|
+
displayName: 'Superscript',
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
id: 'del',
|
|
324
|
+
element: 'del',
|
|
325
|
+
fontFamilyId: 'mondwest',
|
|
326
|
+
fontSize: 'text-base',
|
|
327
|
+
lineHeight: 'leading-normal',
|
|
328
|
+
fontWeight: 'font-normal',
|
|
329
|
+
baseColorId: 'black',
|
|
330
|
+
displayName: 'Deleted Text',
|
|
331
|
+
utilities: ['line-through'],
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
id: 'ins',
|
|
335
|
+
element: 'ins',
|
|
336
|
+
fontFamilyId: 'mondwest',
|
|
337
|
+
fontSize: 'text-base',
|
|
338
|
+
lineHeight: 'leading-normal',
|
|
339
|
+
fontWeight: 'font-normal',
|
|
340
|
+
baseColorId: 'black',
|
|
341
|
+
displayName: 'Inserted Text',
|
|
342
|
+
utilities: ['underline'],
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
id: 'caption',
|
|
346
|
+
element: 'caption',
|
|
347
|
+
fontFamilyId: 'mondwest',
|
|
348
|
+
fontSize: 'text-xs',
|
|
349
|
+
lineHeight: 'leading-normal',
|
|
350
|
+
fontWeight: 'font-normal',
|
|
351
|
+
baseColorId: 'black',
|
|
352
|
+
displayName: 'Caption',
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
id: 'label',
|
|
356
|
+
element: 'label',
|
|
357
|
+
fontFamilyId: 'mondwest',
|
|
358
|
+
fontSize: 'text-xs',
|
|
359
|
+
lineHeight: 'leading-normal',
|
|
360
|
+
fontWeight: 'font-medium',
|
|
361
|
+
baseColorId: 'black',
|
|
362
|
+
displayName: 'Form Label',
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
id: 'figcaption',
|
|
366
|
+
element: 'figcaption',
|
|
367
|
+
fontFamilyId: 'mondwest',
|
|
368
|
+
fontSize: 'text-xs',
|
|
369
|
+
lineHeight: 'leading-normal',
|
|
370
|
+
fontWeight: 'font-normal',
|
|
371
|
+
baseColorId: 'black',
|
|
372
|
+
displayName: 'Figure Caption',
|
|
373
|
+
},
|
|
374
|
+
];
|
|
375
|
+
|
|
376
|
+
export const createTypographySlice: StateCreator<TypographySlice, [], [], TypographySlice> = (set, get) => ({
|
|
377
|
+
fonts: defaultFonts,
|
|
378
|
+
typographyStyles: defaultTypographyStyles,
|
|
379
|
+
|
|
380
|
+
// Font Actions
|
|
381
|
+
addFont: (font) => set((state) => ({
|
|
382
|
+
fonts: [...state.fonts, { ...font, id: crypto.randomUUID() }]
|
|
383
|
+
})),
|
|
384
|
+
|
|
385
|
+
updateFont: (id, updates) => set((state) => ({
|
|
386
|
+
fonts: state.fonts.map((f) =>
|
|
387
|
+
f.id === id ? { ...f, ...updates } : f
|
|
388
|
+
)
|
|
389
|
+
})),
|
|
390
|
+
|
|
391
|
+
deleteFont: (id) => set((state) => ({
|
|
392
|
+
fonts: state.fonts.filter((f) => f.id !== id)
|
|
393
|
+
})),
|
|
394
|
+
|
|
395
|
+
addFontFile: (fontId, file) => set((state) => ({
|
|
396
|
+
fonts: state.fonts.map((f) => {
|
|
397
|
+
if (f.id !== fontId) return f;
|
|
398
|
+
const newFile = { ...file, id: crypto.randomUUID() };
|
|
399
|
+
return {
|
|
400
|
+
...f,
|
|
401
|
+
files: [...f.files, newFile],
|
|
402
|
+
weights: Array.from(new Set([...f.weights, file.weight])).sort((a, b) => a - b),
|
|
403
|
+
styles: Array.from(new Set([...f.styles, file.style])),
|
|
404
|
+
};
|
|
405
|
+
})
|
|
406
|
+
})),
|
|
407
|
+
|
|
408
|
+
removeFontFile: (fontId, fileId) => set((state) => ({
|
|
409
|
+
fonts: state.fonts.map((f) => {
|
|
410
|
+
if (f.id !== fontId) return f;
|
|
411
|
+
const newFiles = f.files.filter((file) => file.id !== fileId);
|
|
412
|
+
return {
|
|
413
|
+
...f,
|
|
414
|
+
files: newFiles,
|
|
415
|
+
weights: Array.from(new Set(newFiles.map(file => file.weight))).sort((a, b) => a - b),
|
|
416
|
+
styles: Array.from(new Set(newFiles.map(file => file.style))),
|
|
417
|
+
};
|
|
418
|
+
})
|
|
419
|
+
})),
|
|
420
|
+
|
|
421
|
+
// Typography Style Actions
|
|
422
|
+
addTypographyStyle: (style) => set((state) => ({
|
|
423
|
+
typographyStyles: [...state.typographyStyles, { ...style, id: crypto.randomUUID() }]
|
|
424
|
+
})),
|
|
425
|
+
|
|
426
|
+
updateTypographyStyle: (id, updates) => set((state) => ({
|
|
427
|
+
typographyStyles: state.typographyStyles.map((s) =>
|
|
428
|
+
s.id === id ? { ...s, ...updates } : s
|
|
429
|
+
)
|
|
430
|
+
})),
|
|
431
|
+
|
|
432
|
+
deleteTypographyStyle: (id) => set((state) => ({
|
|
433
|
+
typographyStyles: state.typographyStyles.filter((s) => s.id !== id)
|
|
434
|
+
})),
|
|
435
|
+
|
|
436
|
+
// Helpers
|
|
437
|
+
getFontById: (id) => get().fonts.find((f) => f.id === id),
|
|
438
|
+
getFontByFamily: (family) => get().fonts.find((f) => f.family === family),
|
|
439
|
+
|
|
440
|
+
// Sync typography to CSS
|
|
441
|
+
syncTypographyToCSS: async () => {
|
|
442
|
+
const state = get();
|
|
443
|
+
try {
|
|
444
|
+
const response = await fetch('/api/devtools/write-css', {
|
|
445
|
+
method: 'POST',
|
|
446
|
+
headers: { 'Content-Type': 'application/json' },
|
|
447
|
+
body: JSON.stringify({
|
|
448
|
+
fonts: state.fonts,
|
|
449
|
+
typographyStyles: state.typographyStyles,
|
|
450
|
+
}),
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
if (!response.ok) {
|
|
454
|
+
const error = await response.json();
|
|
455
|
+
throw new Error(error.error || 'Failed to sync typography CSS');
|
|
456
|
+
}
|
|
457
|
+
} catch (error) {
|
|
458
|
+
throw error;
|
|
459
|
+
}
|
|
460
|
+
},
|
|
461
|
+
|
|
462
|
+
// Load typography from CSS (only typography styles, not fonts)
|
|
463
|
+
loadTypographyFromCSS: async () => {
|
|
464
|
+
try {
|
|
465
|
+
const res = await fetch('/api/devtools/read-css');
|
|
466
|
+
if (!res.ok) {
|
|
467
|
+
throw new Error('Failed to fetch CSS');
|
|
468
|
+
}
|
|
469
|
+
const css = await res.text();
|
|
470
|
+
|
|
471
|
+
// Import parser dynamically to avoid SSR issues
|
|
472
|
+
const { parseLayerBase } = await import('../../lib/cssParser');
|
|
473
|
+
|
|
474
|
+
// Only load typography styles, not fonts (fonts should come from filesystem)
|
|
475
|
+
const typographyStyles = parseLayerBase(css);
|
|
476
|
+
|
|
477
|
+
if (typographyStyles.length > 0) {
|
|
478
|
+
set((state) => ({ ...state, typographyStyles }));
|
|
479
|
+
}
|
|
480
|
+
} catch (error) {
|
|
481
|
+
// Failed to load typography from CSS
|
|
482
|
+
}
|
|
483
|
+
},
|
|
484
|
+
|
|
485
|
+
// Load fonts from filesystem
|
|
486
|
+
loadFontsFromFilesystem: async () => {
|
|
487
|
+
try {
|
|
488
|
+
const res = await fetch('/api/devtools/fonts');
|
|
489
|
+
if (!res.ok) {
|
|
490
|
+
throw new Error('Failed to fetch fonts from filesystem');
|
|
491
|
+
}
|
|
492
|
+
const data = await res.json();
|
|
493
|
+
|
|
494
|
+
// Import parser dynamically to avoid SSR issues
|
|
495
|
+
const { detectFontPropertiesFromFilename } = await import('../../lib/cssParser');
|
|
496
|
+
|
|
497
|
+
// Convert filesystem fonts to FontDefinition format
|
|
498
|
+
const fontMap = new Map<string, FontDefinition>();
|
|
499
|
+
|
|
500
|
+
for (const { family, files } of data.fonts || []) {
|
|
501
|
+
const fontId = family.toLowerCase().replace(/\s+/g, '-');
|
|
502
|
+
const fontFiles: FontFile[] = [];
|
|
503
|
+
const weights = new Set<number>();
|
|
504
|
+
const styles = new Set<string>();
|
|
505
|
+
|
|
506
|
+
for (const file of files) {
|
|
507
|
+
const { weight, style } = detectFontPropertiesFromFilename(file.filename);
|
|
508
|
+
weights.add(weight);
|
|
509
|
+
styles.add(style);
|
|
510
|
+
|
|
511
|
+
fontFiles.push({
|
|
512
|
+
id: `${fontId}-${file.filename.replace(/\.[^.]+$/, '')}`,
|
|
513
|
+
weight,
|
|
514
|
+
style,
|
|
515
|
+
format: file.format as FontFile['format'],
|
|
516
|
+
path: file.path,
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
fontMap.set(fontId, {
|
|
521
|
+
id: fontId,
|
|
522
|
+
name: family,
|
|
523
|
+
family: family,
|
|
524
|
+
files: fontFiles,
|
|
525
|
+
weights: Array.from(weights).sort((a, b) => a - b),
|
|
526
|
+
styles: Array.from(styles),
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
if (fontMap.size > 0) {
|
|
531
|
+
set({ fonts: Array.from(fontMap.values()) });
|
|
532
|
+
}
|
|
533
|
+
} catch (error) {
|
|
534
|
+
// Failed to load fonts from filesystem
|
|
535
|
+
}
|
|
536
|
+
},
|
|
537
|
+
});
|
|
538
|
+
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { StateCreator } from 'zustand';
|
|
2
|
+
import type { BaseColor, ColorMode } from '../../types';
|
|
3
|
+
|
|
4
|
+
export interface VariablesSlice {
|
|
5
|
+
// State
|
|
6
|
+
baseColors: BaseColor[];
|
|
7
|
+
colorModes: ColorMode[];
|
|
8
|
+
activeColorMode: string | null;
|
|
9
|
+
borderRadius: Record<string, string>;
|
|
10
|
+
|
|
11
|
+
// Actions - Base Colors
|
|
12
|
+
addBaseColor: (color: Omit<BaseColor, 'id'>) => void;
|
|
13
|
+
updateBaseColor: (id: string, updates: Partial<BaseColor>) => void;
|
|
14
|
+
deleteBaseColor: (id: string) => void;
|
|
15
|
+
|
|
16
|
+
// Actions - Color Modes
|
|
17
|
+
addColorMode: (mode: Omit<ColorMode, 'id'>) => void;
|
|
18
|
+
updateColorMode: (id: string, updates: Partial<ColorMode>) => void;
|
|
19
|
+
deleteColorMode: (id: string) => void;
|
|
20
|
+
setActiveColorMode: (id: string | null) => void;
|
|
21
|
+
|
|
22
|
+
// Actions - Border Radius
|
|
23
|
+
updateBorderRadius: (key: string, value: string) => void;
|
|
24
|
+
addBorderRadius: (key: string, value: string) => void;
|
|
25
|
+
deleteBorderRadius: (key: string) => void;
|
|
26
|
+
|
|
27
|
+
// Helper - Get base color by ID
|
|
28
|
+
getBaseColorById: (id: string) => BaseColor | undefined;
|
|
29
|
+
|
|
30
|
+
// Sync
|
|
31
|
+
syncToCSS: () => Promise<void>;
|
|
32
|
+
loadFromCSS: () => Promise<void>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Default base colors matching radOS
|
|
36
|
+
const defaultBaseColors: BaseColor[] = [
|
|
37
|
+
// Brand colors
|
|
38
|
+
{ id: 'cream', name: 'cream', displayName: 'Cream', value: '#FEF8E2', category: 'brand' },
|
|
39
|
+
{ id: 'black', name: 'black', displayName: 'Black', value: '#0F0E0C', category: 'brand' },
|
|
40
|
+
{ id: 'sun-yellow', name: 'sun-yellow', displayName: 'Sun Yellow', value: '#FCE184', category: 'brand' },
|
|
41
|
+
{ id: 'sky-blue', name: 'sky-blue', displayName: 'Sky Blue', value: '#95BAD2', category: 'brand' },
|
|
42
|
+
{ id: 'warm-cloud', name: 'warm-cloud', displayName: 'Warm Cloud', value: '#FEF8E2', category: 'brand' },
|
|
43
|
+
{ id: 'sunset-fuzz', name: 'sunset-fuzz', displayName: 'Sunset Fuzz', value: '#FCC383', category: 'brand' },
|
|
44
|
+
{ id: 'sun-red', name: 'sun-red', displayName: 'Sun Red', value: '#FF6B63', category: 'brand' },
|
|
45
|
+
{ id: 'green', name: 'green', displayName: 'Green', value: '#CEF5CA', category: 'brand' },
|
|
46
|
+
{ id: 'white', name: 'white', displayName: 'White', value: '#FFFFFF', category: 'brand' },
|
|
47
|
+
// Neutral colors
|
|
48
|
+
{ id: 'lightest', name: 'lightest', displayName: 'Lightest', value: '#FEF8E2', category: 'neutral' },
|
|
49
|
+
{ id: 'lighter', name: 'lighter', displayName: 'Lighter', value: '#CCCCCC', category: 'neutral' },
|
|
50
|
+
{ id: 'light', name: 'light', displayName: 'Light', value: '#AAAAAA', category: 'neutral' },
|
|
51
|
+
{ id: 'dark', name: 'dark', displayName: 'Dark', value: '#444444', category: 'neutral' },
|
|
52
|
+
{ id: 'darker', name: 'darker', displayName: 'Darker', value: '#222222', category: 'neutral' },
|
|
53
|
+
{ id: 'darkest', name: 'darkest', displayName: 'Darkest', value: '#000000', category: 'neutral' },
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
export const createVariablesSlice: StateCreator<VariablesSlice, [], [], VariablesSlice> = (set, get) => ({
|
|
57
|
+
baseColors: defaultBaseColors,
|
|
58
|
+
colorModes: [],
|
|
59
|
+
activeColorMode: null,
|
|
60
|
+
borderRadius: {
|
|
61
|
+
none: '0',
|
|
62
|
+
xs: '0.125rem',
|
|
63
|
+
sm: '0.25rem',
|
|
64
|
+
md: '0.5rem',
|
|
65
|
+
lg: '1rem',
|
|
66
|
+
full: '9999px',
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
// Base Color Actions
|
|
70
|
+
addBaseColor: (color) => set((state) => ({
|
|
71
|
+
baseColors: [...state.baseColors, { ...color, id: crypto.randomUUID() }]
|
|
72
|
+
})),
|
|
73
|
+
|
|
74
|
+
updateBaseColor: (id, updates) => set((state) => ({
|
|
75
|
+
baseColors: state.baseColors.map((c) =>
|
|
76
|
+
c.id === id ? { ...c, ...updates } : c
|
|
77
|
+
)
|
|
78
|
+
})),
|
|
79
|
+
|
|
80
|
+
deleteBaseColor: (id) => set((state) => ({
|
|
81
|
+
baseColors: state.baseColors.filter((c) => c.id !== id)
|
|
82
|
+
})),
|
|
83
|
+
|
|
84
|
+
// Color Mode Actions
|
|
85
|
+
addColorMode: (mode) => set((state) => ({
|
|
86
|
+
colorModes: [...state.colorModes, { ...mode, id: crypto.randomUUID() }]
|
|
87
|
+
})),
|
|
88
|
+
|
|
89
|
+
updateColorMode: (id, updates) => set((state) => ({
|
|
90
|
+
colorModes: state.colorModes.map((m) =>
|
|
91
|
+
m.id === id ? { ...m, ...updates } : m
|
|
92
|
+
)
|
|
93
|
+
})),
|
|
94
|
+
|
|
95
|
+
deleteColorMode: (id) => set((state) => ({
|
|
96
|
+
colorModes: state.colorModes.filter((m) => m.id !== id)
|
|
97
|
+
})),
|
|
98
|
+
|
|
99
|
+
setActiveColorMode: (id) => set({ activeColorMode: id }),
|
|
100
|
+
|
|
101
|
+
// Border Radius Actions
|
|
102
|
+
updateBorderRadius: (key, value) => set((state) => ({
|
|
103
|
+
borderRadius: { ...state.borderRadius, [key]: value }
|
|
104
|
+
})),
|
|
105
|
+
|
|
106
|
+
addBorderRadius: (key, value) => set((state) => ({
|
|
107
|
+
borderRadius: { ...state.borderRadius, [key]: value }
|
|
108
|
+
})),
|
|
109
|
+
|
|
110
|
+
deleteBorderRadius: (key) => set((state) => {
|
|
111
|
+
const { [key]: _, ...rest } = state.borderRadius;
|
|
112
|
+
return { borderRadius: rest };
|
|
113
|
+
}),
|
|
114
|
+
|
|
115
|
+
// Helper
|
|
116
|
+
getBaseColorById: (id) => {
|
|
117
|
+
return get().baseColors.find(c => c.id === id);
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
// Sync to CSS - sends new data model format
|
|
121
|
+
syncToCSS: async () => {
|
|
122
|
+
const state = get();
|
|
123
|
+
try {
|
|
124
|
+
const response = await fetch('/api/devtools/write-css', {
|
|
125
|
+
method: 'POST',
|
|
126
|
+
headers: { 'Content-Type': 'application/json' },
|
|
127
|
+
body: JSON.stringify({
|
|
128
|
+
baseColors: state.baseColors,
|
|
129
|
+
borderRadius: state.borderRadius,
|
|
130
|
+
colorModes: state.colorModes,
|
|
131
|
+
}),
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
if (!response.ok) {
|
|
135
|
+
const error = await response.json();
|
|
136
|
+
throw new Error(error.error || 'Failed to sync CSS');
|
|
137
|
+
}
|
|
138
|
+
} catch (error) {
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
// Load from CSS - parses existing CSS into new data model
|
|
144
|
+
loadFromCSS: async () => {
|
|
145
|
+
try {
|
|
146
|
+
const res = await fetch('/api/devtools/read-css');
|
|
147
|
+
if (!res.ok) {
|
|
148
|
+
throw new Error('Failed to fetch CSS');
|
|
149
|
+
}
|
|
150
|
+
const css = await res.text();
|
|
151
|
+
|
|
152
|
+
// Import parser dynamically to avoid SSR issues
|
|
153
|
+
const { parseGlobalsCSS, parsedCSSToStoreState } = await import('../../lib/cssParser');
|
|
154
|
+
|
|
155
|
+
const parsed = parseGlobalsCSS(css);
|
|
156
|
+
const state = parsedCSSToStoreState(parsed);
|
|
157
|
+
|
|
158
|
+
set({
|
|
159
|
+
baseColors: state.baseColors,
|
|
160
|
+
colorModes: state.colorModes,
|
|
161
|
+
borderRadius: state.borderRadius,
|
|
162
|
+
});
|
|
163
|
+
} catch (error) {
|
|
164
|
+
// Failed to load CSS
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
});
|