@almadar/ui 5.127.0 → 5.129.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/dist/{UserContext-BKckAUv7.d.cts → UserContext-g_LcDiGN.d.cts} +6 -22
- package/dist/{UserContext-BKckAUv7.d.ts → UserContext-g_LcDiGN.d.ts} +6 -22
- package/dist/avl/index.cjs +297 -6
- package/dist/avl/index.js +297 -6
- package/dist/components/index.cjs +300 -6
- package/dist/components/index.d.cts +119 -1
- package/dist/components/index.d.ts +119 -1
- package/dist/components/index.js +301 -7
- package/dist/context/index.d.cts +2 -2
- package/dist/context/index.d.ts +2 -2
- package/dist/providers/index.cjs +394 -107
- package/dist/providers/index.d.cts +15 -4
- package/dist/providers/index.d.ts +15 -4
- package/dist/providers/index.js +393 -108
- package/dist/runtime/index.cjs +297 -6
- package/dist/runtime/index.js +297 -6
- package/package.json +7 -6
- package/scripts/audit-tailwind-safelist.ts +237 -0
- package/scripts/export-static.js +61 -0
- package/scripts/generate-design-system.ts +344 -0
- package/scripts/generate-theme-from-schema.ts +480 -0
- package/scripts/generate.ts +835 -0
- package/scripts/strip-base-tokens-from-themes.mjs +179 -0
- package/scripts/suggest-components.ts +508 -0
- package/scripts/theme-contrast-audit.mjs +132 -0
- package/scripts/types.ts +282 -0
- package/themes/_contract.md +1 -1
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme Generator from OrbitalSchema
|
|
3
|
+
*
|
|
4
|
+
* Analyzes an .orb schema file and generates a client-specific theme CSS file.
|
|
5
|
+
* The theme is derived from the domain, entity names, and any explicit style hints.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as fs from 'fs';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
|
|
11
|
+
// Domain-to-theme mappings
|
|
12
|
+
const DOMAIN_THEMES: Record<string, ThemeConfig> = {
|
|
13
|
+
// Agriculture / Garden domains
|
|
14
|
+
garden: {
|
|
15
|
+
primary: '#15803d',
|
|
16
|
+
primaryHover: '#166534',
|
|
17
|
+
secondary: '#fef7ed',
|
|
18
|
+
accent: '#ca8a04',
|
|
19
|
+
muted: '#ecfccb',
|
|
20
|
+
background: '#fefefe',
|
|
21
|
+
foreground: '#1a2e05',
|
|
22
|
+
border: '#bbf7d0',
|
|
23
|
+
metaphor: 'organic growth',
|
|
24
|
+
shadows: 'soft-organic',
|
|
25
|
+
radius: 'organic',
|
|
26
|
+
},
|
|
27
|
+
agriculture: {
|
|
28
|
+
primary: '#15803d',
|
|
29
|
+
primaryHover: '#166534',
|
|
30
|
+
secondary: '#fef7ed',
|
|
31
|
+
accent: '#ca8a04',
|
|
32
|
+
muted: '#ecfccb',
|
|
33
|
+
background: '#fefefe',
|
|
34
|
+
foreground: '#1a2e05',
|
|
35
|
+
border: '#bbf7d0',
|
|
36
|
+
metaphor: 'organic growth',
|
|
37
|
+
shadows: 'soft-organic',
|
|
38
|
+
radius: 'organic',
|
|
39
|
+
},
|
|
40
|
+
// Healthcare domains
|
|
41
|
+
healthcare: {
|
|
42
|
+
primary: '#0891b2',
|
|
43
|
+
primaryHover: '#0e7490',
|
|
44
|
+
secondary: '#f0f9ff',
|
|
45
|
+
accent: '#06b6d4',
|
|
46
|
+
muted: '#e0f2fe',
|
|
47
|
+
background: '#ffffff',
|
|
48
|
+
foreground: '#0c4a6e',
|
|
49
|
+
border: '#bae6fd',
|
|
50
|
+
metaphor: 'clinical trust',
|
|
51
|
+
shadows: 'minimal',
|
|
52
|
+
radius: 'gentle',
|
|
53
|
+
},
|
|
54
|
+
medical: {
|
|
55
|
+
primary: '#0891b2',
|
|
56
|
+
primaryHover: '#0e7490',
|
|
57
|
+
secondary: '#f0f9ff',
|
|
58
|
+
accent: '#06b6d4',
|
|
59
|
+
muted: '#e0f2fe',
|
|
60
|
+
background: '#ffffff',
|
|
61
|
+
foreground: '#0c4a6e',
|
|
62
|
+
border: '#bae6fd',
|
|
63
|
+
metaphor: 'clinical trust',
|
|
64
|
+
shadows: 'minimal',
|
|
65
|
+
radius: 'gentle',
|
|
66
|
+
},
|
|
67
|
+
// Finance domains
|
|
68
|
+
finance: {
|
|
69
|
+
primary: '#1e3a5f',
|
|
70
|
+
primaryHover: '#0f172a',
|
|
71
|
+
secondary: '#f8fafc',
|
|
72
|
+
accent: '#eab308',
|
|
73
|
+
muted: '#f1f5f9',
|
|
74
|
+
background: '#ffffff',
|
|
75
|
+
foreground: '#0f172a',
|
|
76
|
+
border: '#cbd5e1',
|
|
77
|
+
metaphor: 'stability and trust',
|
|
78
|
+
shadows: 'corporate',
|
|
79
|
+
radius: 'sharp',
|
|
80
|
+
},
|
|
81
|
+
banking: {
|
|
82
|
+
primary: '#1e3a5f',
|
|
83
|
+
primaryHover: '#0f172a',
|
|
84
|
+
secondary: '#f8fafc',
|
|
85
|
+
accent: '#eab308',
|
|
86
|
+
muted: '#f1f5f9',
|
|
87
|
+
background: '#ffffff',
|
|
88
|
+
foreground: '#0f172a',
|
|
89
|
+
border: '#cbd5e1',
|
|
90
|
+
metaphor: 'stability and trust',
|
|
91
|
+
shadows: 'corporate',
|
|
92
|
+
radius: 'sharp',
|
|
93
|
+
},
|
|
94
|
+
// E-commerce domains
|
|
95
|
+
ecommerce: {
|
|
96
|
+
primary: '#7c3aed',
|
|
97
|
+
primaryHover: '#6d28d9',
|
|
98
|
+
secondary: '#faf5ff',
|
|
99
|
+
accent: '#f97316',
|
|
100
|
+
muted: '#f3e8ff',
|
|
101
|
+
background: '#ffffff',
|
|
102
|
+
foreground: '#1e1b4b',
|
|
103
|
+
border: '#e9d5ff',
|
|
104
|
+
metaphor: 'vibrant marketplace',
|
|
105
|
+
shadows: 'playful',
|
|
106
|
+
radius: 'rounded',
|
|
107
|
+
},
|
|
108
|
+
retail: {
|
|
109
|
+
primary: '#7c3aed',
|
|
110
|
+
primaryHover: '#6d28d9',
|
|
111
|
+
secondary: '#faf5ff',
|
|
112
|
+
accent: '#f97316',
|
|
113
|
+
muted: '#f3e8ff',
|
|
114
|
+
background: '#ffffff',
|
|
115
|
+
foreground: '#1e1b4b',
|
|
116
|
+
border: '#e9d5ff',
|
|
117
|
+
metaphor: 'vibrant marketplace',
|
|
118
|
+
shadows: 'playful',
|
|
119
|
+
radius: 'rounded',
|
|
120
|
+
},
|
|
121
|
+
// Game domains
|
|
122
|
+
game: {
|
|
123
|
+
primary: '#dc2626',
|
|
124
|
+
primaryHover: '#b91c1c',
|
|
125
|
+
secondary: '#18181b',
|
|
126
|
+
accent: '#facc15',
|
|
127
|
+
muted: '#27272a',
|
|
128
|
+
background: '#09090b',
|
|
129
|
+
foreground: '#fafafa',
|
|
130
|
+
border: '#3f3f46',
|
|
131
|
+
metaphor: 'energetic action',
|
|
132
|
+
shadows: 'dramatic',
|
|
133
|
+
radius: 'sharp',
|
|
134
|
+
},
|
|
135
|
+
gaming: {
|
|
136
|
+
primary: '#dc2626',
|
|
137
|
+
primaryHover: '#b91c1c',
|
|
138
|
+
secondary: '#18181b',
|
|
139
|
+
accent: '#facc15',
|
|
140
|
+
muted: '#27272a',
|
|
141
|
+
background: '#09090b',
|
|
142
|
+
foreground: '#fafafa',
|
|
143
|
+
border: '#3f3f46',
|
|
144
|
+
metaphor: 'energetic action',
|
|
145
|
+
shadows: 'dramatic',
|
|
146
|
+
radius: 'sharp',
|
|
147
|
+
},
|
|
148
|
+
// Education domains
|
|
149
|
+
education: {
|
|
150
|
+
primary: '#2563eb',
|
|
151
|
+
primaryHover: '#1d4ed8',
|
|
152
|
+
secondary: '#eff6ff',
|
|
153
|
+
accent: '#10b981',
|
|
154
|
+
muted: '#dbeafe',
|
|
155
|
+
background: '#ffffff',
|
|
156
|
+
foreground: '#1e3a8a',
|
|
157
|
+
border: '#bfdbfe',
|
|
158
|
+
metaphor: 'clarity and learning',
|
|
159
|
+
shadows: 'soft',
|
|
160
|
+
radius: 'friendly',
|
|
161
|
+
},
|
|
162
|
+
learning: {
|
|
163
|
+
primary: '#2563eb',
|
|
164
|
+
primaryHover: '#1d4ed8',
|
|
165
|
+
secondary: '#eff6ff',
|
|
166
|
+
accent: '#10b981',
|
|
167
|
+
muted: '#dbeafe',
|
|
168
|
+
background: '#ffffff',
|
|
169
|
+
foreground: '#1e3a8a',
|
|
170
|
+
border: '#bfdbfe',
|
|
171
|
+
metaphor: 'clarity and learning',
|
|
172
|
+
shadows: 'soft',
|
|
173
|
+
radius: 'friendly',
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
// Shadow presets
|
|
178
|
+
const SHADOW_PRESETS: Record<string, ShadowConfig> = {
|
|
179
|
+
'soft-organic': {
|
|
180
|
+
main: '0 4px 12px -2px rgba(21, 128, 61, 0.15), 0 2px 6px -2px rgba(21, 128, 61, 0.1)',
|
|
181
|
+
sm: '0 2px 4px -1px rgba(21, 128, 61, 0.1)',
|
|
182
|
+
lg: '0 16px 32px -8px rgba(21, 128, 61, 0.2), 0 8px 16px -4px rgba(21, 128, 61, 0.1)',
|
|
183
|
+
hover: '0 12px 24px -6px rgba(21, 128, 61, 0.25)',
|
|
184
|
+
},
|
|
185
|
+
minimal: {
|
|
186
|
+
main: '0 1px 3px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.06)',
|
|
187
|
+
sm: '0 1px 2px rgba(0, 0, 0, 0.04)',
|
|
188
|
+
lg: '0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1)',
|
|
189
|
+
hover: '0 10px 40px -10px rgba(0, 0, 0, 0.15)',
|
|
190
|
+
},
|
|
191
|
+
corporate: {
|
|
192
|
+
main: '0 2px 8px -2px rgba(15, 23, 42, 0.12)',
|
|
193
|
+
sm: '0 1px 3px rgba(15, 23, 42, 0.08)',
|
|
194
|
+
lg: '0 12px 28px -6px rgba(15, 23, 42, 0.18)',
|
|
195
|
+
hover: '0 16px 36px -8px rgba(15, 23, 42, 0.22)',
|
|
196
|
+
},
|
|
197
|
+
playful: {
|
|
198
|
+
main: '0 4px 14px -3px rgba(124, 58, 237, 0.2)',
|
|
199
|
+
sm: '0 2px 6px rgba(124, 58, 237, 0.12)',
|
|
200
|
+
lg: '0 18px 36px -10px rgba(124, 58, 237, 0.25)',
|
|
201
|
+
hover: '0 20px 40px -12px rgba(124, 58, 237, 0.3)',
|
|
202
|
+
},
|
|
203
|
+
dramatic: {
|
|
204
|
+
main: '0 4px 20px -4px rgba(0, 0, 0, 0.5)',
|
|
205
|
+
sm: '0 2px 8px rgba(0, 0, 0, 0.4)',
|
|
206
|
+
lg: '0 20px 50px -12px rgba(0, 0, 0, 0.6)',
|
|
207
|
+
hover: '0 24px 60px -16px rgba(0, 0, 0, 0.7)',
|
|
208
|
+
},
|
|
209
|
+
soft: {
|
|
210
|
+
main: '0 2px 8px -2px rgba(37, 99, 235, 0.15)',
|
|
211
|
+
sm: '0 1px 4px rgba(37, 99, 235, 0.1)',
|
|
212
|
+
lg: '0 12px 28px -6px rgba(37, 99, 235, 0.2)',
|
|
213
|
+
hover: '0 16px 36px -8px rgba(37, 99, 235, 0.25)',
|
|
214
|
+
},
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
// Radius presets
|
|
218
|
+
const RADIUS_PRESETS: Record<string, RadiusConfig> = {
|
|
219
|
+
organic: { sm: '6px', md: '12px', lg: '16px', xl: '24px' },
|
|
220
|
+
gentle: { sm: '4px', md: '8px', lg: '12px', xl: '16px' },
|
|
221
|
+
sharp: { sm: '2px', md: '4px', lg: '6px', xl: '8px' },
|
|
222
|
+
rounded: { sm: '8px', md: '16px', lg: '24px', xl: '32px' },
|
|
223
|
+
friendly: { sm: '6px', md: '10px', lg: '14px', xl: '20px' },
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
interface ThemeConfig {
|
|
227
|
+
primary: string;
|
|
228
|
+
primaryHover: string;
|
|
229
|
+
secondary: string;
|
|
230
|
+
accent: string;
|
|
231
|
+
muted: string;
|
|
232
|
+
background: string;
|
|
233
|
+
foreground: string;
|
|
234
|
+
border: string;
|
|
235
|
+
metaphor: string;
|
|
236
|
+
shadows: string;
|
|
237
|
+
radius: string;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
interface ShadowConfig {
|
|
241
|
+
main: string;
|
|
242
|
+
sm: string;
|
|
243
|
+
lg: string;
|
|
244
|
+
hover: string;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
interface RadiusConfig {
|
|
248
|
+
sm: string;
|
|
249
|
+
md: string;
|
|
250
|
+
lg: string;
|
|
251
|
+
xl: string;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
interface OrbitalSchema {
|
|
255
|
+
name: string;
|
|
256
|
+
version?: string;
|
|
257
|
+
description?: string;
|
|
258
|
+
orbitals?: Array<{
|
|
259
|
+
name: string;
|
|
260
|
+
entity?: {
|
|
261
|
+
name: string;
|
|
262
|
+
fields?: Array<{ name: string; type: string }>;
|
|
263
|
+
};
|
|
264
|
+
}>;
|
|
265
|
+
traits?: Array<{ name: string; category?: string }>;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Detect domain from schema content
|
|
270
|
+
*/
|
|
271
|
+
function detectDomain(schema: OrbitalSchema): string {
|
|
272
|
+
const schemaName = schema.name.toLowerCase();
|
|
273
|
+
const description = (schema.description || '').toLowerCase();
|
|
274
|
+
|
|
275
|
+
// Check schema name first
|
|
276
|
+
for (const domain of Object.keys(DOMAIN_THEMES)) {
|
|
277
|
+
if (schemaName.includes(domain)) {
|
|
278
|
+
return domain;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Check description
|
|
283
|
+
for (const domain of Object.keys(DOMAIN_THEMES)) {
|
|
284
|
+
if (description.includes(domain)) {
|
|
285
|
+
return domain;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Check entity names
|
|
290
|
+
const entityNames = (schema.orbitals || [])
|
|
291
|
+
.map(o => (o.entity?.name || '').toLowerCase())
|
|
292
|
+
.join(' ');
|
|
293
|
+
|
|
294
|
+
// Domain keywords
|
|
295
|
+
const domainKeywords: Record<string, string[]> = {
|
|
296
|
+
garden: ['plant', 'seed', 'garden', 'grow', 'harvest', 'cultivate', 'soil', 'water', 'farm'],
|
|
297
|
+
healthcare: ['patient', 'doctor', 'hospital', 'medical', 'health', 'diagnosis', 'treatment'],
|
|
298
|
+
finance: ['account', 'transaction', 'payment', 'balance', 'transfer', 'bank', 'money'],
|
|
299
|
+
ecommerce: ['product', 'cart', 'order', 'checkout', 'inventory', 'shop', 'store'],
|
|
300
|
+
game: ['player', 'score', 'level', 'enemy', 'character', 'game', 'sprite'],
|
|
301
|
+
education: ['course', 'student', 'lesson', 'quiz', 'learning', 'teacher', 'class'],
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
for (const [domain, keywords] of Object.entries(domainKeywords)) {
|
|
305
|
+
for (const keyword of keywords) {
|
|
306
|
+
if (entityNames.includes(keyword) || schemaName.includes(keyword) || description.includes(keyword)) {
|
|
307
|
+
return domain;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Default to minimalist if no domain detected
|
|
313
|
+
return 'default';
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Generate theme CSS from config
|
|
318
|
+
*/
|
|
319
|
+
function generateThemeCSS(themeName: string, config: ThemeConfig): string {
|
|
320
|
+
const shadows = SHADOW_PRESETS[config.shadows] || SHADOW_PRESETS.minimal;
|
|
321
|
+
const radius = RADIUS_PRESETS[config.radius] || RADIUS_PRESETS.gentle;
|
|
322
|
+
|
|
323
|
+
return `/**
|
|
324
|
+
* ${themeName} Theme
|
|
325
|
+
*
|
|
326
|
+
* Auto-generated theme based on domain: ${config.metaphor}
|
|
327
|
+
* Generated at: ${new Date().toISOString()}
|
|
328
|
+
*/
|
|
329
|
+
|
|
330
|
+
[data-design-theme="${themeName}"] {
|
|
331
|
+
/* === Color Palette === */
|
|
332
|
+
--color-primary: ${config.primary};
|
|
333
|
+
--color-primary-hover: ${config.primaryHover};
|
|
334
|
+
--color-primary-foreground: #ffffff;
|
|
335
|
+
|
|
336
|
+
--color-secondary: ${config.secondary};
|
|
337
|
+
--color-secondary-hover: ${adjustBrightness(config.secondary, -5)};
|
|
338
|
+
--color-secondary-foreground: ${config.foreground};
|
|
339
|
+
|
|
340
|
+
--color-accent: ${config.accent};
|
|
341
|
+
--color-accent-foreground: #ffffff;
|
|
342
|
+
|
|
343
|
+
--color-muted: ${config.muted};
|
|
344
|
+
--color-muted-foreground: ${adjustBrightness(config.foreground, 30)};
|
|
345
|
+
|
|
346
|
+
--color-background: ${config.background};
|
|
347
|
+
--color-foreground: ${config.foreground};
|
|
348
|
+
--color-card: #ffffff;
|
|
349
|
+
--color-card-foreground: ${config.foreground};
|
|
350
|
+
--color-border: ${config.border};
|
|
351
|
+
--color-input: ${config.border};
|
|
352
|
+
--color-ring: ${config.primary};
|
|
353
|
+
|
|
354
|
+
/* === Shadows === */
|
|
355
|
+
--shadow-main: ${shadows.main};
|
|
356
|
+
--shadow-sm: ${shadows.sm};
|
|
357
|
+
--shadow-lg: ${shadows.lg};
|
|
358
|
+
--shadow-inner: inset 0 2px 4px rgba(0, 0, 0, 0.05);
|
|
359
|
+
--shadow-none: none;
|
|
360
|
+
--shadow-hover: ${shadows.hover};
|
|
361
|
+
--shadow-active: ${shadows.sm};
|
|
362
|
+
|
|
363
|
+
/* === Border Radius === */
|
|
364
|
+
--radius-none: 0px;
|
|
365
|
+
--radius-sm: ${radius.sm};
|
|
366
|
+
--radius-md: ${radius.md};
|
|
367
|
+
--radius-lg: ${radius.lg};
|
|
368
|
+
--radius-xl: ${radius.xl};
|
|
369
|
+
--radius-full: 9999px;
|
|
370
|
+
|
|
371
|
+
/* === Border Width === */
|
|
372
|
+
--border-width: 1px;
|
|
373
|
+
--border-width-thin: 1px;
|
|
374
|
+
--border-width-thick: 2px;
|
|
375
|
+
|
|
376
|
+
/* === Typography === */
|
|
377
|
+
--font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
378
|
+
--font-weight-normal: 400;
|
|
379
|
+
--font-weight-medium: 500;
|
|
380
|
+
--font-weight-bold: 600;
|
|
381
|
+
--letter-spacing: -0.01em;
|
|
382
|
+
--line-height: 1.6;
|
|
383
|
+
|
|
384
|
+
/* === Transitions === */
|
|
385
|
+
--transition-fast: 150ms;
|
|
386
|
+
--transition-normal: 250ms;
|
|
387
|
+
--transition-slow: 400ms;
|
|
388
|
+
--transition-timing: cubic-bezier(0.4, 0, 0.2, 1);
|
|
389
|
+
|
|
390
|
+
/* === Hover/Active Transforms === */
|
|
391
|
+
--hover-scale: 1.02;
|
|
392
|
+
--hover-translate-y: -2px;
|
|
393
|
+
--active-scale: 0.98;
|
|
394
|
+
|
|
395
|
+
/* === Focus Ring === */
|
|
396
|
+
--focus-ring-width: 2px;
|
|
397
|
+
--focus-ring-offset: 2px;
|
|
398
|
+
--focus-ring-color: ${config.primary};
|
|
399
|
+
|
|
400
|
+
/* === Card Specific === */
|
|
401
|
+
--card-hover-shadow: ${shadows.hover};
|
|
402
|
+
--card-hover-transform: translateY(-2px) scale(1.01);
|
|
403
|
+
|
|
404
|
+
/* === Button Specific === */
|
|
405
|
+
--button-active-transform: scale(0.98);
|
|
406
|
+
}
|
|
407
|
+
`;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Adjust color brightness (simple implementation)
|
|
412
|
+
*/
|
|
413
|
+
function adjustBrightness(hex: string, percent: number): string {
|
|
414
|
+
// Simple brightness adjustment - in production use a proper color library
|
|
415
|
+
const num = parseInt(hex.replace('#', ''), 16);
|
|
416
|
+
const amt = Math.round(2.55 * percent);
|
|
417
|
+
const R = Math.max(0, Math.min(255, (num >> 16) + amt));
|
|
418
|
+
const G = Math.max(0, Math.min(255, ((num >> 8) & 0x00ff) + amt));
|
|
419
|
+
const B = Math.max(0, Math.min(255, (num & 0x0000ff) + amt));
|
|
420
|
+
return `#${(0x1000000 + R * 0x10000 + G * 0x100 + B).toString(16).slice(1)}`;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Main function to generate theme from schema
|
|
425
|
+
*/
|
|
426
|
+
export function generateThemeFromSchema(schemaPath: string, outputPath?: string): string {
|
|
427
|
+
// Read and parse schema
|
|
428
|
+
const schemaContent = fs.readFileSync(schemaPath, 'utf-8');
|
|
429
|
+
const schema: OrbitalSchema = JSON.parse(schemaContent);
|
|
430
|
+
|
|
431
|
+
// Detect domain
|
|
432
|
+
const domain = detectDomain(schema);
|
|
433
|
+
console.log(`Detected domain: ${domain}`);
|
|
434
|
+
|
|
435
|
+
// Get theme config
|
|
436
|
+
const themeConfig = DOMAIN_THEMES[domain] || {
|
|
437
|
+
primary: '#18181b',
|
|
438
|
+
primaryHover: '#27272a',
|
|
439
|
+
secondary: '#fafafa',
|
|
440
|
+
accent: '#18181b',
|
|
441
|
+
muted: '#f4f4f5',
|
|
442
|
+
background: '#ffffff',
|
|
443
|
+
foreground: '#18181b',
|
|
444
|
+
border: '#e4e4e7',
|
|
445
|
+
metaphor: 'neutral professional',
|
|
446
|
+
shadows: 'minimal',
|
|
447
|
+
radius: 'gentle',
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
// Generate theme name from schema name
|
|
451
|
+
const themeName = schema.name.toLowerCase().replace(/[^a-z0-9]+/g, '-');
|
|
452
|
+
|
|
453
|
+
// Generate CSS
|
|
454
|
+
const css = generateThemeCSS(themeName, themeConfig);
|
|
455
|
+
|
|
456
|
+
// Write to file if output path provided
|
|
457
|
+
if (outputPath) {
|
|
458
|
+
fs.writeFileSync(outputPath, css);
|
|
459
|
+
console.log(`Theme generated: ${outputPath}`);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return css;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// CLI usage
|
|
466
|
+
if (require.main === module) {
|
|
467
|
+
const args = process.argv.slice(2);
|
|
468
|
+
if (args.length < 1) {
|
|
469
|
+
console.error('Usage: npx tsx generate-theme-from-schema.ts <schema.orb> [output.css]');
|
|
470
|
+
process.exit(1);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const schemaPath = args[0];
|
|
474
|
+
const outputPath = args[1] || path.join(
|
|
475
|
+
path.dirname(schemaPath),
|
|
476
|
+
`${path.basename(schemaPath, '.orb')}-theme.css`
|
|
477
|
+
);
|
|
478
|
+
|
|
479
|
+
generateThemeFromSchema(schemaPath, outputPath);
|
|
480
|
+
}
|