@thesage/ui 0.0.9
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/fontThemes-Bwf7_lFg.d.mts +842 -0
- package/dist/fontThemes-Bwf7_lFg.d.ts +842 -0
- package/dist/hooks-C8PrmIXy.d.mts +225 -0
- package/dist/hooks-Ct9RBhg-.d.ts +225 -0
- package/dist/hooks.d.mts +3 -0
- package/dist/hooks.d.ts +3 -0
- package/dist/hooks.js +1342 -0
- package/dist/hooks.js.map +1 -0
- package/dist/hooks.mjs +1314 -0
- package/dist/hooks.mjs.map +1 -0
- package/dist/index-CsnncHSm.d.mts +23 -0
- package/dist/index-CsnncHSm.d.ts +23 -0
- package/dist/index.d.mts +2830 -0
- package/dist/index.d.ts +2830 -0
- package/dist/index.js +12637 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +12319 -0
- package/dist/index.mjs.map +1 -0
- package/dist/providers-Dv3LFGtJ.d.mts +17 -0
- package/dist/providers-Dv3LFGtJ.d.ts +17 -0
- package/dist/providers.d.mts +2 -0
- package/dist/providers.d.ts +2 -0
- package/dist/providers.js +1944 -0
- package/dist/providers.js.map +1 -0
- package/dist/providers.mjs +1918 -0
- package/dist/providers.mjs.map +1 -0
- package/dist/tokens.d.mts +831 -0
- package/dist/tokens.d.ts +831 -0
- package/dist/tokens.js +2399 -0
- package/dist/tokens.js.map +1 -0
- package/dist/tokens.mjs +2319 -0
- package/dist/tokens.mjs.map +1 -0
- package/dist/utils-DlJKRVzQ.d.mts +986 -0
- package/dist/utils-xrpHqxXR.d.ts +986 -0
- package/dist/utils.d.mts +4 -0
- package/dist/utils.d.ts +4 -0
- package/dist/utils.js +873 -0
- package/dist/utils.js.map +1 -0
- package/dist/utils.mjs +805 -0
- package/dist/utils.mjs.map +1 -0
- package/dist/validation-Bj1ye-v_.d.mts +114 -0
- package/dist/validation-Bj1ye-v_.d.ts +114 -0
- package/package.json +117 -0
package/dist/hooks.mjs
ADDED
|
@@ -0,0 +1,1314 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/lib/store/theme.ts
|
|
4
|
+
import { create } from "zustand";
|
|
5
|
+
import { persist } from "zustand/middleware";
|
|
6
|
+
var useThemeStore = create()(
|
|
7
|
+
persist(
|
|
8
|
+
(set, get) => ({
|
|
9
|
+
// Defaults
|
|
10
|
+
theme: "volt",
|
|
11
|
+
mode: "dark",
|
|
12
|
+
// Actions
|
|
13
|
+
setTheme: (theme) => set({ theme }),
|
|
14
|
+
setMode: (mode) => set({ mode }),
|
|
15
|
+
toggleMode: () => set((state) => ({ mode: state.mode === "light" ? "dark" : "light" })),
|
|
16
|
+
// Computed
|
|
17
|
+
get themeConfig() {
|
|
18
|
+
const state = get();
|
|
19
|
+
return { name: state.theme, mode: state.mode };
|
|
20
|
+
}
|
|
21
|
+
}),
|
|
22
|
+
{
|
|
23
|
+
name: "ecosystem-theme",
|
|
24
|
+
// Only persist theme and mode
|
|
25
|
+
partialize: (state) => ({
|
|
26
|
+
theme: state.theme,
|
|
27
|
+
mode: state.mode
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
)
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
// src/hooks/useTheme.ts
|
|
34
|
+
function useTheme() {
|
|
35
|
+
return useThemeStore();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// src/hooks/useMotionPreference.ts
|
|
39
|
+
import { useEffect } from "react";
|
|
40
|
+
|
|
41
|
+
// src/lib/store/customizer.ts
|
|
42
|
+
import { create as create2 } from "zustand";
|
|
43
|
+
import { persist as persist2 } from "zustand/middleware";
|
|
44
|
+
|
|
45
|
+
// ../tokens/src/base.ts
|
|
46
|
+
var baseTokens = {
|
|
47
|
+
/**
|
|
48
|
+
* Spacing scale (based on 4px grid)
|
|
49
|
+
*/
|
|
50
|
+
spacing: {
|
|
51
|
+
"0": "0",
|
|
52
|
+
"0.5": "0.125rem",
|
|
53
|
+
// 2px
|
|
54
|
+
"1": "0.25rem",
|
|
55
|
+
// 4px
|
|
56
|
+
"2": "0.5rem",
|
|
57
|
+
// 8px
|
|
58
|
+
"3": "0.75rem",
|
|
59
|
+
// 12px
|
|
60
|
+
"4": "1rem",
|
|
61
|
+
// 16px
|
|
62
|
+
"5": "1.25rem",
|
|
63
|
+
// 20px
|
|
64
|
+
"6": "1.5rem",
|
|
65
|
+
// 24px
|
|
66
|
+
"8": "2rem",
|
|
67
|
+
// 32px
|
|
68
|
+
"10": "2.5rem",
|
|
69
|
+
// 40px
|
|
70
|
+
"12": "3rem",
|
|
71
|
+
// 48px
|
|
72
|
+
"16": "4rem",
|
|
73
|
+
// 64px
|
|
74
|
+
"20": "5rem",
|
|
75
|
+
// 80px
|
|
76
|
+
"24": "6rem",
|
|
77
|
+
// 96px
|
|
78
|
+
"32": "8rem"
|
|
79
|
+
// 128px
|
|
80
|
+
},
|
|
81
|
+
/**
|
|
82
|
+
* Typography scales
|
|
83
|
+
*/
|
|
84
|
+
fontSize: {
|
|
85
|
+
"xs": "0.75rem",
|
|
86
|
+
// 12px
|
|
87
|
+
"sm": "0.875rem",
|
|
88
|
+
// 14px
|
|
89
|
+
"base": "1rem",
|
|
90
|
+
// 16px
|
|
91
|
+
"lg": "1.125rem",
|
|
92
|
+
// 18px
|
|
93
|
+
"xl": "1.25rem",
|
|
94
|
+
// 20px
|
|
95
|
+
"2xl": "1.5rem",
|
|
96
|
+
// 24px
|
|
97
|
+
"3xl": "1.875rem",
|
|
98
|
+
// 30px
|
|
99
|
+
"4xl": "2.25rem",
|
|
100
|
+
// 36px
|
|
101
|
+
"5xl": "3rem",
|
|
102
|
+
// 48px
|
|
103
|
+
"6xl": "3.75rem",
|
|
104
|
+
// 60px
|
|
105
|
+
"7xl": "4.5rem",
|
|
106
|
+
// 72px
|
|
107
|
+
"8xl": "6rem"
|
|
108
|
+
// 96px
|
|
109
|
+
},
|
|
110
|
+
fontWeight: {
|
|
111
|
+
light: "300",
|
|
112
|
+
normal: "400",
|
|
113
|
+
medium: "500",
|
|
114
|
+
semibold: "600",
|
|
115
|
+
bold: "700",
|
|
116
|
+
extrabold: "800",
|
|
117
|
+
black: "900"
|
|
118
|
+
},
|
|
119
|
+
lineHeight: {
|
|
120
|
+
none: "1",
|
|
121
|
+
tight: "1.25",
|
|
122
|
+
snug: "1.375",
|
|
123
|
+
normal: "1.5",
|
|
124
|
+
relaxed: "1.625",
|
|
125
|
+
loose: "2"
|
|
126
|
+
},
|
|
127
|
+
/**
|
|
128
|
+
* Border radius
|
|
129
|
+
*/
|
|
130
|
+
borderRadius: {
|
|
131
|
+
none: "0",
|
|
132
|
+
sm: "0.125rem",
|
|
133
|
+
// 2px
|
|
134
|
+
DEFAULT: "0.25rem",
|
|
135
|
+
// 4px
|
|
136
|
+
md: "0.375rem",
|
|
137
|
+
// 6px
|
|
138
|
+
lg: "0.5rem",
|
|
139
|
+
// 8px
|
|
140
|
+
xl: "0.75rem",
|
|
141
|
+
// 12px
|
|
142
|
+
"2xl": "1rem",
|
|
143
|
+
// 16px
|
|
144
|
+
"3xl": "1.5rem",
|
|
145
|
+
// 24px
|
|
146
|
+
full: "9999px"
|
|
147
|
+
},
|
|
148
|
+
/**
|
|
149
|
+
* Motion durations (base values - themes can override)
|
|
150
|
+
*/
|
|
151
|
+
duration: {
|
|
152
|
+
instant: "0ms",
|
|
153
|
+
fast: "150ms",
|
|
154
|
+
normal: "300ms",
|
|
155
|
+
slow: "500ms",
|
|
156
|
+
slower: "800ms"
|
|
157
|
+
},
|
|
158
|
+
/**
|
|
159
|
+
* Easing curves (base values - themes can override)
|
|
160
|
+
*/
|
|
161
|
+
ease: {
|
|
162
|
+
linear: "linear",
|
|
163
|
+
in: "cubic-bezier(0.4, 0, 1, 1)",
|
|
164
|
+
out: "cubic-bezier(0, 0, 0.2, 1)",
|
|
165
|
+
inOut: "cubic-bezier(0.4, 0, 0.2, 1)"
|
|
166
|
+
},
|
|
167
|
+
/**
|
|
168
|
+
* Z-index scale
|
|
169
|
+
*/
|
|
170
|
+
zIndex: {
|
|
171
|
+
"auto": "auto",
|
|
172
|
+
"0": "0",
|
|
173
|
+
"10": "10",
|
|
174
|
+
"20": "20",
|
|
175
|
+
"30": "30",
|
|
176
|
+
"40": "40",
|
|
177
|
+
"50": "50",
|
|
178
|
+
dropdown: "1000",
|
|
179
|
+
sticky: "1020",
|
|
180
|
+
fixed: "1030",
|
|
181
|
+
modalBackdrop: "1040",
|
|
182
|
+
modal: "1050",
|
|
183
|
+
popover: "1060",
|
|
184
|
+
tooltip: "1070"
|
|
185
|
+
},
|
|
186
|
+
/**
|
|
187
|
+
* Focus ring configuration
|
|
188
|
+
*/
|
|
189
|
+
focus: {
|
|
190
|
+
width: "2px",
|
|
191
|
+
offset: "2px",
|
|
192
|
+
style: "solid"
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
var spacing = {
|
|
196
|
+
xs: baseTokens.spacing["1"],
|
|
197
|
+
// 4px — Tight internal padding
|
|
198
|
+
sm: baseTokens.spacing["2"],
|
|
199
|
+
// 8px — Default gap
|
|
200
|
+
md: baseTokens.spacing["4"],
|
|
201
|
+
// 16px — Section padding
|
|
202
|
+
lg: baseTokens.spacing["6"],
|
|
203
|
+
// 24px — Card padding
|
|
204
|
+
xl: baseTokens.spacing["8"],
|
|
205
|
+
// 32px — Section margins
|
|
206
|
+
"2xl": baseTokens.spacing["12"],
|
|
207
|
+
// 48px — Page sections
|
|
208
|
+
"3xl": baseTokens.spacing["16"]
|
|
209
|
+
// 64px — Major divisions
|
|
210
|
+
};
|
|
211
|
+
var typography = {
|
|
212
|
+
fonts: {
|
|
213
|
+
sans: "var(--font-body)",
|
|
214
|
+
serif: "var(--font-heading)",
|
|
215
|
+
mono: "var(--font-mono)"
|
|
216
|
+
},
|
|
217
|
+
sizes: {
|
|
218
|
+
xs: baseTokens.fontSize.xs,
|
|
219
|
+
// 12px — Fine print
|
|
220
|
+
sm: baseTokens.fontSize.sm,
|
|
221
|
+
// 14px — Secondary text
|
|
222
|
+
base: baseTokens.fontSize.base,
|
|
223
|
+
// 16px — Body text
|
|
224
|
+
lg: baseTokens.fontSize.lg,
|
|
225
|
+
// 18px — Lead paragraphs
|
|
226
|
+
xl: baseTokens.fontSize.xl,
|
|
227
|
+
// 20px — Section headers
|
|
228
|
+
"2xl": baseTokens.fontSize["2xl"],
|
|
229
|
+
// 24px — Page headers
|
|
230
|
+
"3xl": baseTokens.fontSize["3xl"]
|
|
231
|
+
// 30px — Hero text
|
|
232
|
+
},
|
|
233
|
+
weights: {
|
|
234
|
+
normal: baseTokens.fontWeight.normal,
|
|
235
|
+
// 400
|
|
236
|
+
medium: baseTokens.fontWeight.medium,
|
|
237
|
+
// 500
|
|
238
|
+
semibold: baseTokens.fontWeight.semibold,
|
|
239
|
+
// 600
|
|
240
|
+
bold: baseTokens.fontWeight.bold
|
|
241
|
+
// 700
|
|
242
|
+
},
|
|
243
|
+
leading: {
|
|
244
|
+
tight: baseTokens.lineHeight.tight,
|
|
245
|
+
// 1.25 — Headings
|
|
246
|
+
normal: baseTokens.lineHeight.normal,
|
|
247
|
+
// 1.5 — Body
|
|
248
|
+
relaxed: baseTokens.lineHeight.relaxed
|
|
249
|
+
// 1.625 — Spacious reading
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
var motion = {
|
|
253
|
+
duration: baseTokens.duration,
|
|
254
|
+
easing: {
|
|
255
|
+
default: baseTokens.ease.out,
|
|
256
|
+
// Most transitions
|
|
257
|
+
spring: "cubic-bezier(0.16, 1, 0.3, 1)",
|
|
258
|
+
// Playful interactions
|
|
259
|
+
linear: baseTokens.ease.linear
|
|
260
|
+
// Progress indicators
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
// ../tokens/src/typography.ts
|
|
265
|
+
var fontSizes = {
|
|
266
|
+
// Body text scale
|
|
267
|
+
xs: { base: "0.75rem", mobile: "0.75rem" },
|
|
268
|
+
// 12px
|
|
269
|
+
sm: { base: "0.875rem", mobile: "0.875rem" },
|
|
270
|
+
// 14px
|
|
271
|
+
base: { base: "1rem", mobile: "1rem" },
|
|
272
|
+
// 16px
|
|
273
|
+
lg: { base: "1.125rem", mobile: "1rem" },
|
|
274
|
+
// 18px / 16px mobile
|
|
275
|
+
xl: { base: "1.25rem", mobile: "1.125rem" },
|
|
276
|
+
// 20px / 18px mobile
|
|
277
|
+
"2xl": { base: "1.5rem", mobile: "1.25rem" },
|
|
278
|
+
// 24px / 20px mobile
|
|
279
|
+
"3xl": { base: "1.875rem", mobile: "1.5rem" },
|
|
280
|
+
// 30px / 24px mobile
|
|
281
|
+
// Heading scale (h6 → h1)
|
|
282
|
+
"4xl": { base: "2.25rem", mobile: "1.875rem" },
|
|
283
|
+
// 36px / 30px - h3
|
|
284
|
+
"5xl": { base: "3rem", mobile: "2.25rem" },
|
|
285
|
+
// 48px / 36px - h2
|
|
286
|
+
"6xl": { base: "3.75rem", mobile: "2.5rem" },
|
|
287
|
+
// 60px / 40px - h1
|
|
288
|
+
"7xl": { base: "4.5rem", mobile: "3rem" },
|
|
289
|
+
// 72px / 48px - Display
|
|
290
|
+
"8xl": { base: "6rem", mobile: "3.75rem" },
|
|
291
|
+
// 96px / 60px - Hero
|
|
292
|
+
"9xl": { base: "8rem", mobile: "4.5rem" }
|
|
293
|
+
// 128px / 72px - Ultra
|
|
294
|
+
};
|
|
295
|
+
var fontWeights = {
|
|
296
|
+
thin: "100",
|
|
297
|
+
extralight: "200",
|
|
298
|
+
light: "300",
|
|
299
|
+
normal: "400",
|
|
300
|
+
medium: "500",
|
|
301
|
+
semibold: "600",
|
|
302
|
+
bold: "700",
|
|
303
|
+
extrabold: "800",
|
|
304
|
+
black: "900"
|
|
305
|
+
};
|
|
306
|
+
var lineHeights = {
|
|
307
|
+
none: "1",
|
|
308
|
+
tight: "1.25",
|
|
309
|
+
snug: "1.375",
|
|
310
|
+
normal: "1.5",
|
|
311
|
+
relaxed: "1.625",
|
|
312
|
+
loose: "1.75",
|
|
313
|
+
extraloose: "2"
|
|
314
|
+
};
|
|
315
|
+
var letterSpacing = {
|
|
316
|
+
tighter: "-0.05em",
|
|
317
|
+
tight: "-0.025em",
|
|
318
|
+
normal: "0",
|
|
319
|
+
wide: "0.025em",
|
|
320
|
+
wider: "0.05em",
|
|
321
|
+
widest: "0.1em"
|
|
322
|
+
};
|
|
323
|
+
var typePresets = {
|
|
324
|
+
"display-large": {
|
|
325
|
+
size: fontSizes["8xl"],
|
|
326
|
+
weight: fontWeights.bold,
|
|
327
|
+
lineHeight: lineHeights.none,
|
|
328
|
+
letterSpacing: letterSpacing.tighter,
|
|
329
|
+
description: "Large hero text, landing pages"
|
|
330
|
+
},
|
|
331
|
+
"display": {
|
|
332
|
+
size: fontSizes["7xl"],
|
|
333
|
+
weight: fontWeights.bold,
|
|
334
|
+
lineHeight: lineHeights.tight,
|
|
335
|
+
letterSpacing: letterSpacing.tight,
|
|
336
|
+
description: "Hero sections, major headings"
|
|
337
|
+
},
|
|
338
|
+
"heading-1": {
|
|
339
|
+
size: fontSizes["6xl"],
|
|
340
|
+
weight: fontWeights.bold,
|
|
341
|
+
lineHeight: lineHeights.tight,
|
|
342
|
+
letterSpacing: letterSpacing.tight,
|
|
343
|
+
description: "Page titles, h1"
|
|
344
|
+
},
|
|
345
|
+
"heading-2": {
|
|
346
|
+
size: fontSizes["5xl"],
|
|
347
|
+
weight: fontWeights.bold,
|
|
348
|
+
lineHeight: lineHeights.tight,
|
|
349
|
+
letterSpacing: letterSpacing.normal,
|
|
350
|
+
description: "Section titles, h2"
|
|
351
|
+
},
|
|
352
|
+
"heading-3": {
|
|
353
|
+
size: fontSizes["4xl"],
|
|
354
|
+
weight: fontWeights.semibold,
|
|
355
|
+
lineHeight: lineHeights.snug,
|
|
356
|
+
letterSpacing: letterSpacing.normal,
|
|
357
|
+
description: "Subsection titles, h3"
|
|
358
|
+
},
|
|
359
|
+
"heading-4": {
|
|
360
|
+
size: fontSizes["2xl"],
|
|
361
|
+
weight: fontWeights.semibold,
|
|
362
|
+
lineHeight: lineHeights.snug,
|
|
363
|
+
letterSpacing: letterSpacing.normal,
|
|
364
|
+
description: "Component titles, h4"
|
|
365
|
+
},
|
|
366
|
+
"heading-5": {
|
|
367
|
+
size: fontSizes.xl,
|
|
368
|
+
weight: fontWeights.medium,
|
|
369
|
+
lineHeight: lineHeights.normal,
|
|
370
|
+
letterSpacing: letterSpacing.normal,
|
|
371
|
+
description: "Small headings, h5"
|
|
372
|
+
},
|
|
373
|
+
"heading-6": {
|
|
374
|
+
size: fontSizes.lg,
|
|
375
|
+
weight: fontWeights.medium,
|
|
376
|
+
lineHeight: lineHeights.normal,
|
|
377
|
+
letterSpacing: letterSpacing.normal,
|
|
378
|
+
description: "Tiny headings, h6"
|
|
379
|
+
},
|
|
380
|
+
"body-large": {
|
|
381
|
+
size: fontSizes.lg,
|
|
382
|
+
weight: fontWeights.normal,
|
|
383
|
+
lineHeight: lineHeights.relaxed,
|
|
384
|
+
letterSpacing: letterSpacing.normal,
|
|
385
|
+
description: "Lead paragraphs, intro text"
|
|
386
|
+
},
|
|
387
|
+
"body": {
|
|
388
|
+
size: fontSizes.base,
|
|
389
|
+
weight: fontWeights.normal,
|
|
390
|
+
lineHeight: lineHeights.normal,
|
|
391
|
+
letterSpacing: letterSpacing.normal,
|
|
392
|
+
description: "Default body text"
|
|
393
|
+
},
|
|
394
|
+
"body-small": {
|
|
395
|
+
size: fontSizes.sm,
|
|
396
|
+
weight: fontWeights.normal,
|
|
397
|
+
lineHeight: lineHeights.normal,
|
|
398
|
+
letterSpacing: letterSpacing.normal,
|
|
399
|
+
description: "Small body text, fine print"
|
|
400
|
+
},
|
|
401
|
+
"caption": {
|
|
402
|
+
size: fontSizes.xs,
|
|
403
|
+
weight: fontWeights.normal,
|
|
404
|
+
lineHeight: lineHeights.snug,
|
|
405
|
+
letterSpacing: letterSpacing.wide,
|
|
406
|
+
description: "Captions, labels, metadata"
|
|
407
|
+
},
|
|
408
|
+
"overline": {
|
|
409
|
+
size: fontSizes.xs,
|
|
410
|
+
weight: fontWeights.semibold,
|
|
411
|
+
lineHeight: lineHeights.normal,
|
|
412
|
+
letterSpacing: letterSpacing.widest,
|
|
413
|
+
description: "Eyebrows, categories, all-caps labels"
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
// ../tokens/src/color-utils.ts
|
|
418
|
+
function hexToRgb(hex) {
|
|
419
|
+
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
420
|
+
return result ? {
|
|
421
|
+
r: parseInt(result[1], 16),
|
|
422
|
+
g: parseInt(result[2], 16),
|
|
423
|
+
b: parseInt(result[3], 16)
|
|
424
|
+
} : null;
|
|
425
|
+
}
|
|
426
|
+
function hexToHSL(hex) {
|
|
427
|
+
const rgb = hexToRgb(hex);
|
|
428
|
+
if (!rgb) return { h: 0, s: 0, l: 0 };
|
|
429
|
+
const r = rgb.r / 255;
|
|
430
|
+
const g = rgb.g / 255;
|
|
431
|
+
const b = rgb.b / 255;
|
|
432
|
+
const max = Math.max(r, g, b);
|
|
433
|
+
const min = Math.min(r, g, b);
|
|
434
|
+
let h = 0, s = 0, l = (max + min) / 2;
|
|
435
|
+
if (max !== min) {
|
|
436
|
+
const d = max - min;
|
|
437
|
+
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
438
|
+
switch (max) {
|
|
439
|
+
case r:
|
|
440
|
+
h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
|
|
441
|
+
break;
|
|
442
|
+
case g:
|
|
443
|
+
h = ((b - r) / d + 2) / 6;
|
|
444
|
+
break;
|
|
445
|
+
case b:
|
|
446
|
+
h = ((r - g) / d + 4) / 6;
|
|
447
|
+
break;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
return {
|
|
451
|
+
h: Math.round(h * 360),
|
|
452
|
+
s: Math.round(s * 100),
|
|
453
|
+
l: Math.round(l * 100)
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
function hslToHex(h, s, l) {
|
|
457
|
+
h = h / 360;
|
|
458
|
+
s = s / 100;
|
|
459
|
+
l = l / 100;
|
|
460
|
+
let r, g, b;
|
|
461
|
+
if (s === 0) {
|
|
462
|
+
r = g = b = l;
|
|
463
|
+
} else {
|
|
464
|
+
const hue2rgb = (p2, q2, t) => {
|
|
465
|
+
if (t < 0) t += 1;
|
|
466
|
+
if (t > 1) t -= 1;
|
|
467
|
+
if (t < 1 / 6) return p2 + (q2 - p2) * 6 * t;
|
|
468
|
+
if (t < 1 / 2) return q2;
|
|
469
|
+
if (t < 2 / 3) return p2 + (q2 - p2) * (2 / 3 - t) * 6;
|
|
470
|
+
return p2;
|
|
471
|
+
};
|
|
472
|
+
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
473
|
+
const p = 2 * l - q;
|
|
474
|
+
r = hue2rgb(p, q, h + 1 / 3);
|
|
475
|
+
g = hue2rgb(p, q, h);
|
|
476
|
+
b = hue2rgb(p, q, h - 1 / 3);
|
|
477
|
+
}
|
|
478
|
+
const toHex = (x) => {
|
|
479
|
+
const hex = Math.round(x * 255).toString(16);
|
|
480
|
+
return hex.length === 1 ? "0" + hex : hex;
|
|
481
|
+
};
|
|
482
|
+
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
483
|
+
}
|
|
484
|
+
function adjustLightness(hex, percent) {
|
|
485
|
+
const hsl = hexToHSL(hex);
|
|
486
|
+
const newL = Math.max(0, Math.min(100, hsl.l + percent));
|
|
487
|
+
return hslToHex(hsl.h, hsl.s, newL);
|
|
488
|
+
}
|
|
489
|
+
function adjustSaturation(hex, percent) {
|
|
490
|
+
const hsl = hexToHSL(hex);
|
|
491
|
+
const newS = Math.max(0, Math.min(100, hsl.s + percent));
|
|
492
|
+
return hslToHex(hsl.h, newS, hsl.l);
|
|
493
|
+
}
|
|
494
|
+
function rotateHue(hex, degrees) {
|
|
495
|
+
const hsl = hexToHSL(hex);
|
|
496
|
+
const newH = (hsl.h + degrees) % 360;
|
|
497
|
+
return hslToHex(newH, hsl.s, hsl.l);
|
|
498
|
+
}
|
|
499
|
+
function adjustOpacity(hex, opacity) {
|
|
500
|
+
const rgb = hexToRgb(hex);
|
|
501
|
+
if (!rgb) return hex;
|
|
502
|
+
return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${opacity})`;
|
|
503
|
+
}
|
|
504
|
+
function getLuminance(r, g, b) {
|
|
505
|
+
const [rs, gs, bs] = [r, g, b].map((c) => {
|
|
506
|
+
const srgb = c / 255;
|
|
507
|
+
return srgb <= 0.03928 ? srgb / 12.92 : Math.pow((srgb + 0.055) / 1.055, 2.4);
|
|
508
|
+
});
|
|
509
|
+
return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
|
|
510
|
+
}
|
|
511
|
+
function getContrastRatio(hex1, hex2) {
|
|
512
|
+
const rgb1 = hexToRgb(hex1);
|
|
513
|
+
const rgb2 = hexToRgb(hex2);
|
|
514
|
+
if (!rgb1 || !rgb2) return 0;
|
|
515
|
+
const lum1 = getLuminance(rgb1.r, rgb1.g, rgb1.b);
|
|
516
|
+
const lum2 = getLuminance(rgb2.r, rgb2.g, rgb2.b);
|
|
517
|
+
const lighter = Math.max(lum1, lum2);
|
|
518
|
+
const darker = Math.min(lum1, lum2);
|
|
519
|
+
return (lighter + 0.05) / (darker + 0.05);
|
|
520
|
+
}
|
|
521
|
+
function getOptimalForeground(bgHex, whiteHex = "#ffffff", blackHex = "#000000") {
|
|
522
|
+
const whiteRatio = getContrastRatio(bgHex, whiteHex);
|
|
523
|
+
const blackRatio = getContrastRatio(bgHex, blackHex);
|
|
524
|
+
return whiteRatio > blackRatio ? whiteHex : blackHex;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// ../tokens/src/token-graph.ts
|
|
528
|
+
var primaryColorDerivations = {
|
|
529
|
+
// Links use primary color
|
|
530
|
+
"--color-link": {
|
|
531
|
+
source: "--color-primary",
|
|
532
|
+
transform: (primary) => primary,
|
|
533
|
+
description: "Links inherit primary brand color"
|
|
534
|
+
},
|
|
535
|
+
// Focus ring uses primary color
|
|
536
|
+
"--color-ring": {
|
|
537
|
+
source: "--color-primary",
|
|
538
|
+
transform: (primary) => primary,
|
|
539
|
+
description: "Focus rings use primary for brand consistency"
|
|
540
|
+
},
|
|
541
|
+
// Link hover is slightly darker primary
|
|
542
|
+
"--color-link-hover": {
|
|
543
|
+
source: "--color-primary",
|
|
544
|
+
transform: (primary) => adjustLightness(primary, -10),
|
|
545
|
+
description: "Link hover is 10% darker for visual feedback"
|
|
546
|
+
},
|
|
547
|
+
// Chart primary series
|
|
548
|
+
"--chart-1": {
|
|
549
|
+
source: "--color-primary",
|
|
550
|
+
transform: (primary) => primary,
|
|
551
|
+
description: "First chart series uses primary"
|
|
552
|
+
},
|
|
553
|
+
// Chart secondary series (lighter tint)
|
|
554
|
+
"--chart-2": {
|
|
555
|
+
source: "--color-primary",
|
|
556
|
+
transform: (primary) => adjustLightness(primary, 20),
|
|
557
|
+
description: "Second chart series is lighter tint of primary"
|
|
558
|
+
},
|
|
559
|
+
// Chart tertiary series (darker shade)
|
|
560
|
+
"--chart-3": {
|
|
561
|
+
source: "--color-primary",
|
|
562
|
+
transform: (primary) => adjustLightness(primary, -15),
|
|
563
|
+
description: "Third chart series is darker shade of primary"
|
|
564
|
+
},
|
|
565
|
+
// Chart quaternary (desaturated primary)
|
|
566
|
+
"--chart-4": {
|
|
567
|
+
source: "--color-primary",
|
|
568
|
+
transform: (primary) => adjustSaturation(primary, -30),
|
|
569
|
+
description: "Fourth chart series is muted primary"
|
|
570
|
+
},
|
|
571
|
+
// Chart quinary (complementary color)
|
|
572
|
+
"--chart-5": {
|
|
573
|
+
source: "--color-primary",
|
|
574
|
+
transform: (primary) => rotateHue(primary, 180),
|
|
575
|
+
description: "Fifth chart series is complementary to primary"
|
|
576
|
+
}
|
|
577
|
+
};
|
|
578
|
+
var secondaryColorDerivations = {
|
|
579
|
+
// Hover states
|
|
580
|
+
"--color-hover": {
|
|
581
|
+
source: "--color-secondary",
|
|
582
|
+
transform: (secondary) => secondary,
|
|
583
|
+
description: "Hover backgrounds use secondary"
|
|
584
|
+
},
|
|
585
|
+
// Active states
|
|
586
|
+
"--color-active": {
|
|
587
|
+
source: "--color-secondary",
|
|
588
|
+
transform: (secondary) => adjustLightness(secondary, -5),
|
|
589
|
+
description: "Active state is slightly darker secondary"
|
|
590
|
+
},
|
|
591
|
+
// Muted backgrounds
|
|
592
|
+
"--color-muted": {
|
|
593
|
+
source: "--color-secondary",
|
|
594
|
+
transform: (secondary) => secondary,
|
|
595
|
+
description: "Muted sections use secondary color"
|
|
596
|
+
}
|
|
597
|
+
};
|
|
598
|
+
var accentColorDerivations = {
|
|
599
|
+
// Info semantic color uses accent
|
|
600
|
+
"--color-info": {
|
|
601
|
+
source: "--color-accent",
|
|
602
|
+
transform: (accent) => accent,
|
|
603
|
+
description: "Info semantic color uses accent"
|
|
604
|
+
},
|
|
605
|
+
// Info foreground calculated for contrast
|
|
606
|
+
"--color-info-foreground": {
|
|
607
|
+
source: "--color-accent",
|
|
608
|
+
transform: (accent) => getOptimalForeground(accent),
|
|
609
|
+
description: "Info foreground calculated for contrast"
|
|
610
|
+
}
|
|
611
|
+
};
|
|
612
|
+
var modeSpecificDerivations = {
|
|
613
|
+
"--color-primary-muted": {
|
|
614
|
+
light: {
|
|
615
|
+
source: "--color-primary",
|
|
616
|
+
transform: (primary) => adjustLightness(primary, 40),
|
|
617
|
+
description: "Muted primary for light backgrounds"
|
|
618
|
+
},
|
|
619
|
+
dark: {
|
|
620
|
+
source: "--color-primary",
|
|
621
|
+
transform: (primary) => adjustLightness(primary, -20),
|
|
622
|
+
description: "Muted primary for dark backgrounds"
|
|
623
|
+
}
|
|
624
|
+
},
|
|
625
|
+
"--color-primary-subtle": {
|
|
626
|
+
light: {
|
|
627
|
+
source: "--color-primary",
|
|
628
|
+
transform: (primary) => adjustOpacity(primary, 0.1),
|
|
629
|
+
description: "Subtle primary background for light mode"
|
|
630
|
+
},
|
|
631
|
+
dark: {
|
|
632
|
+
source: "--color-primary",
|
|
633
|
+
transform: (primary) => adjustOpacity(primary, 0.2),
|
|
634
|
+
description: "Subtle primary background for dark mode"
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
function computeDerivedTokens(sourceToken, sourceValue, mode) {
|
|
639
|
+
const derived = {};
|
|
640
|
+
Object.entries(primaryColorDerivations).forEach(([token, config]) => {
|
|
641
|
+
if (config.source === sourceToken) {
|
|
642
|
+
derived[token] = config.transform(sourceValue);
|
|
643
|
+
}
|
|
644
|
+
});
|
|
645
|
+
Object.entries(secondaryColorDerivations).forEach(([token, config]) => {
|
|
646
|
+
if (config.source === sourceToken) {
|
|
647
|
+
derived[token] = config.transform(sourceValue);
|
|
648
|
+
}
|
|
649
|
+
});
|
|
650
|
+
Object.entries(accentColorDerivations).forEach(([token, config]) => {
|
|
651
|
+
if (config.source === sourceToken) {
|
|
652
|
+
derived[token] = config.transform(sourceValue);
|
|
653
|
+
}
|
|
654
|
+
});
|
|
655
|
+
Object.entries(modeSpecificDerivations).forEach(([token, configs]) => {
|
|
656
|
+
const config = configs[mode];
|
|
657
|
+
if (config.source === sourceToken) {
|
|
658
|
+
derived[token] = config.transform(sourceValue);
|
|
659
|
+
}
|
|
660
|
+
});
|
|
661
|
+
return derived;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// src/lib/colors.ts
|
|
665
|
+
var colorTokens = {
|
|
666
|
+
// Background colors
|
|
667
|
+
background: "var(--color-background)",
|
|
668
|
+
backgroundSecondary: "var(--color-background-secondary)",
|
|
669
|
+
backgroundTertiary: "var(--color-background-tertiary)",
|
|
670
|
+
surface: "var(--color-surface)",
|
|
671
|
+
// Foreground/Text colors
|
|
672
|
+
foreground: "var(--color-foreground)",
|
|
673
|
+
foregroundSecondary: "var(--color-foreground-secondary)",
|
|
674
|
+
foregroundTertiary: "var(--color-foreground-tertiary)",
|
|
675
|
+
textPrimary: "var(--color-text-primary)",
|
|
676
|
+
textSecondary: "var(--color-text-secondary)",
|
|
677
|
+
textMuted: "var(--color-text-muted)",
|
|
678
|
+
// Brand colors
|
|
679
|
+
primary: "var(--color-primary)",
|
|
680
|
+
primaryForeground: "var(--color-primary-foreground)",
|
|
681
|
+
secondary: "var(--color-secondary)",
|
|
682
|
+
secondaryForeground: "var(--color-secondary-foreground)",
|
|
683
|
+
accent: "var(--color-accent)",
|
|
684
|
+
accentForeground: "var(--color-accent-foreground)",
|
|
685
|
+
// Semantic colors
|
|
686
|
+
success: "var(--color-success)",
|
|
687
|
+
successForeground: "var(--color-success-foreground)",
|
|
688
|
+
warning: "var(--color-warning)",
|
|
689
|
+
warningForeground: "var(--color-warning-foreground)",
|
|
690
|
+
error: "var(--color-error)",
|
|
691
|
+
errorForeground: "var(--color-error-foreground)",
|
|
692
|
+
info: "var(--color-info)",
|
|
693
|
+
infoForeground: "var(--color-info-foreground)",
|
|
694
|
+
// Borders
|
|
695
|
+
border: "var(--color-border)",
|
|
696
|
+
borderSubtle: "var(--color-border-subtle)",
|
|
697
|
+
// Interactive states
|
|
698
|
+
hover: "var(--color-hover)",
|
|
699
|
+
active: "var(--color-active)",
|
|
700
|
+
focus: "var(--color-focus)",
|
|
701
|
+
// Links
|
|
702
|
+
link: "var(--color-link)",
|
|
703
|
+
linkHover: "var(--color-link-hover)",
|
|
704
|
+
linkHoverForeground: "var(--color-link-hover-foreground)"
|
|
705
|
+
};
|
|
706
|
+
var semanticColors = {
|
|
707
|
+
/**
|
|
708
|
+
* Status colors for indicating states
|
|
709
|
+
*/
|
|
710
|
+
status: {
|
|
711
|
+
success: {
|
|
712
|
+
bg: colorTokens.success,
|
|
713
|
+
fg: colorTokens.successForeground
|
|
714
|
+
},
|
|
715
|
+
warning: {
|
|
716
|
+
bg: colorTokens.warning,
|
|
717
|
+
fg: colorTokens.warningForeground
|
|
718
|
+
},
|
|
719
|
+
error: {
|
|
720
|
+
bg: colorTokens.error,
|
|
721
|
+
fg: colorTokens.errorForeground
|
|
722
|
+
},
|
|
723
|
+
info: {
|
|
724
|
+
bg: colorTokens.info,
|
|
725
|
+
fg: colorTokens.infoForeground
|
|
726
|
+
}
|
|
727
|
+
},
|
|
728
|
+
/**
|
|
729
|
+
* Brand colors for primary UI elements
|
|
730
|
+
*/
|
|
731
|
+
brand: {
|
|
732
|
+
primary: {
|
|
733
|
+
bg: colorTokens.primary,
|
|
734
|
+
fg: colorTokens.primaryForeground
|
|
735
|
+
},
|
|
736
|
+
secondary: {
|
|
737
|
+
bg: colorTokens.secondary,
|
|
738
|
+
fg: colorTokens.secondaryForeground
|
|
739
|
+
},
|
|
740
|
+
accent: {
|
|
741
|
+
bg: colorTokens.accent,
|
|
742
|
+
fg: colorTokens.accentForeground
|
|
743
|
+
}
|
|
744
|
+
},
|
|
745
|
+
/**
|
|
746
|
+
* Interactive state colors
|
|
747
|
+
*/
|
|
748
|
+
interactive: {
|
|
749
|
+
default: {
|
|
750
|
+
bg: colorTokens.background,
|
|
751
|
+
fg: colorTokens.foreground
|
|
752
|
+
},
|
|
753
|
+
hover: {
|
|
754
|
+
bg: colorTokens.hover,
|
|
755
|
+
fg: colorTokens.foreground
|
|
756
|
+
},
|
|
757
|
+
active: {
|
|
758
|
+
bg: colorTokens.active,
|
|
759
|
+
fg: colorTokens.foreground
|
|
760
|
+
},
|
|
761
|
+
focus: {
|
|
762
|
+
border: colorTokens.focus
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
};
|
|
766
|
+
function hexToRgb2(hex) {
|
|
767
|
+
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
768
|
+
return result ? {
|
|
769
|
+
r: parseInt(result[1], 16),
|
|
770
|
+
g: parseInt(result[2], 16),
|
|
771
|
+
b: parseInt(result[3], 16)
|
|
772
|
+
} : null;
|
|
773
|
+
}
|
|
774
|
+
function getLuminance2(r, g, b) {
|
|
775
|
+
const [rs, gs, bs] = [r, g, b].map((c) => {
|
|
776
|
+
const srgb = c / 255;
|
|
777
|
+
return srgb <= 0.03928 ? srgb / 12.92 : Math.pow((srgb + 0.055) / 1.055, 2.4);
|
|
778
|
+
});
|
|
779
|
+
return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
|
|
780
|
+
}
|
|
781
|
+
function getContrastRatio2(hex1, hex2) {
|
|
782
|
+
const rgb1 = hexToRgb2(hex1);
|
|
783
|
+
const rgb2 = hexToRgb2(hex2);
|
|
784
|
+
if (!rgb1 || !rgb2) return 0;
|
|
785
|
+
const lum1 = getLuminance2(rgb1.r, rgb1.g, rgb1.b);
|
|
786
|
+
const lum2 = getLuminance2(rgb2.r, rgb2.g, rgb2.b);
|
|
787
|
+
const lighter = Math.max(lum1, lum2);
|
|
788
|
+
const darker = Math.min(lum1, lum2);
|
|
789
|
+
return (lighter + 0.05) / (darker + 0.05);
|
|
790
|
+
}
|
|
791
|
+
function hexToHSL2(hex) {
|
|
792
|
+
const rgb = hexToRgb2(hex);
|
|
793
|
+
if (!rgb) return { h: 0, s: 0, l: 0 };
|
|
794
|
+
const r = rgb.r / 255;
|
|
795
|
+
const g = rgb.g / 255;
|
|
796
|
+
const b = rgb.b / 255;
|
|
797
|
+
const max = Math.max(r, g, b);
|
|
798
|
+
const min = Math.min(r, g, b);
|
|
799
|
+
let h = 0, s = 0, l = (max + min) / 2;
|
|
800
|
+
if (max !== min) {
|
|
801
|
+
const d = max - min;
|
|
802
|
+
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
803
|
+
switch (max) {
|
|
804
|
+
case r:
|
|
805
|
+
h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
|
|
806
|
+
break;
|
|
807
|
+
case g:
|
|
808
|
+
h = ((b - r) / d + 2) / 6;
|
|
809
|
+
break;
|
|
810
|
+
case b:
|
|
811
|
+
h = ((r - g) / d + 4) / 6;
|
|
812
|
+
break;
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
return {
|
|
816
|
+
h: Math.round(h * 360),
|
|
817
|
+
s: Math.round(s * 100),
|
|
818
|
+
l: Math.round(l * 100)
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
function hslToHex2(h, s, l) {
|
|
822
|
+
h = h / 360;
|
|
823
|
+
s = s / 100;
|
|
824
|
+
l = l / 100;
|
|
825
|
+
let r, g, b;
|
|
826
|
+
if (s === 0) {
|
|
827
|
+
r = g = b = l;
|
|
828
|
+
} else {
|
|
829
|
+
const hue2rgb = (p2, q2, t) => {
|
|
830
|
+
if (t < 0) t += 1;
|
|
831
|
+
if (t > 1) t -= 1;
|
|
832
|
+
if (t < 1 / 6) return p2 + (q2 - p2) * 6 * t;
|
|
833
|
+
if (t < 1 / 2) return q2;
|
|
834
|
+
if (t < 2 / 3) return p2 + (q2 - p2) * (2 / 3 - t) * 6;
|
|
835
|
+
return p2;
|
|
836
|
+
};
|
|
837
|
+
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
838
|
+
const p = 2 * l - q;
|
|
839
|
+
r = hue2rgb(p, q, h + 1 / 3);
|
|
840
|
+
g = hue2rgb(p, q, h);
|
|
841
|
+
b = hue2rgb(p, q, h - 1 / 3);
|
|
842
|
+
}
|
|
843
|
+
const toHex = (x) => {
|
|
844
|
+
const hex = Math.round(x * 255).toString(16);
|
|
845
|
+
return hex.length === 1 ? "0" + hex : hex;
|
|
846
|
+
};
|
|
847
|
+
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
848
|
+
}
|
|
849
|
+
function getOptimalForeground2(bgHex, whiteHex = "#ffffff", blackHex = "#000000") {
|
|
850
|
+
const whiteRatio = getContrastRatio2(bgHex, whiteHex);
|
|
851
|
+
const blackRatio = getContrastRatio2(bgHex, blackHex);
|
|
852
|
+
return whiteRatio > blackRatio ? whiteHex : blackHex;
|
|
853
|
+
}
|
|
854
|
+
function generateColorScale(baseHex) {
|
|
855
|
+
const hsl = hexToHSL2(baseHex);
|
|
856
|
+
return {
|
|
857
|
+
50: hslToHex2(hsl.h, Math.max(hsl.s - 10, 20), 95),
|
|
858
|
+
100: hslToHex2(hsl.h, Math.max(hsl.s - 5, 30), 90),
|
|
859
|
+
200: hslToHex2(hsl.h, hsl.s, 80),
|
|
860
|
+
300: hslToHex2(hsl.h, hsl.s, 70),
|
|
861
|
+
400: hslToHex2(hsl.h, hsl.s, 60),
|
|
862
|
+
500: baseHex,
|
|
863
|
+
// Base color
|
|
864
|
+
600: hslToHex2(hsl.h, Math.min(hsl.s + 5, 100), 45),
|
|
865
|
+
700: hslToHex2(hsl.h, Math.min(hsl.s + 10, 100), 35),
|
|
866
|
+
800: hslToHex2(hsl.h, Math.min(hsl.s + 15, 100), 25),
|
|
867
|
+
900: hslToHex2(hsl.h, Math.min(hsl.s + 20, 100), 15)
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
// src/lib/store/customizer.ts
|
|
872
|
+
var useCustomizer = create2()(
|
|
873
|
+
persist2(
|
|
874
|
+
(set, get) => ({
|
|
875
|
+
motion: 5,
|
|
876
|
+
prefersReducedMotion: false,
|
|
877
|
+
customizationMode: "simple",
|
|
878
|
+
customColors: {},
|
|
879
|
+
savedPalettes: [],
|
|
880
|
+
customFontThemes: {},
|
|
881
|
+
savedFontThemes: [],
|
|
882
|
+
setMotion: (level) => set({ motion: level }),
|
|
883
|
+
setPrefersReducedMotion: (value) => set({ prefersReducedMotion: value }),
|
|
884
|
+
setCustomizationMode: (mode) => set({ customizationMode: mode }),
|
|
885
|
+
setCustomPrimaryColor: (theme, mode, hexColor) => {
|
|
886
|
+
const state = get();
|
|
887
|
+
const currentPalette = state.customColors[theme]?.[mode];
|
|
888
|
+
const scale = generateColorScale(hexColor);
|
|
889
|
+
const primaryForeground = getOptimalForeground2(hexColor);
|
|
890
|
+
const derivedTokens = computeDerivedTokens("--color-primary", hexColor, mode);
|
|
891
|
+
const isSimple = state.customizationMode === "simple";
|
|
892
|
+
const palette = {
|
|
893
|
+
primary: hexColor,
|
|
894
|
+
primaryForeground,
|
|
895
|
+
secondary: isSimple ? void 0 : currentPalette?.secondary,
|
|
896
|
+
secondaryForeground: isSimple ? void 0 : currentPalette?.secondaryForeground,
|
|
897
|
+
accent: isSimple ? void 0 : currentPalette?.accent,
|
|
898
|
+
accentForeground: isSimple ? void 0 : currentPalette?.accentForeground,
|
|
899
|
+
scale,
|
|
900
|
+
derivedTokens
|
|
901
|
+
};
|
|
902
|
+
set((state2) => ({
|
|
903
|
+
customColors: {
|
|
904
|
+
...state2.customColors,
|
|
905
|
+
[theme]: {
|
|
906
|
+
...state2.customColors[theme],
|
|
907
|
+
[mode]: palette
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
}));
|
|
911
|
+
},
|
|
912
|
+
setCustomSecondaryColor: (theme, mode, hexColor) => {
|
|
913
|
+
const state = get();
|
|
914
|
+
const currentPalette = state.customColors[theme]?.[mode];
|
|
915
|
+
if (!currentPalette) return;
|
|
916
|
+
const secondaryForeground = getOptimalForeground2(hexColor);
|
|
917
|
+
const derivedTokens = computeDerivedTokens("--color-secondary", hexColor, mode);
|
|
918
|
+
set((state2) => ({
|
|
919
|
+
customColors: {
|
|
920
|
+
...state2.customColors,
|
|
921
|
+
[theme]: {
|
|
922
|
+
...state2.customColors[theme],
|
|
923
|
+
[mode]: {
|
|
924
|
+
...currentPalette,
|
|
925
|
+
secondary: hexColor,
|
|
926
|
+
secondaryForeground,
|
|
927
|
+
derivedTokens: {
|
|
928
|
+
...currentPalette.derivedTokens,
|
|
929
|
+
...derivedTokens
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
}));
|
|
935
|
+
},
|
|
936
|
+
setCustomAccentColor: (theme, mode, hexColor) => {
|
|
937
|
+
const state = get();
|
|
938
|
+
const currentPalette = state.customColors[theme]?.[mode];
|
|
939
|
+
if (!currentPalette) return;
|
|
940
|
+
const accentForeground = getOptimalForeground2(hexColor);
|
|
941
|
+
const derivedTokens = computeDerivedTokens("--color-accent", hexColor, mode);
|
|
942
|
+
set((state2) => ({
|
|
943
|
+
customColors: {
|
|
944
|
+
...state2.customColors,
|
|
945
|
+
[theme]: {
|
|
946
|
+
...state2.customColors[theme],
|
|
947
|
+
[mode]: {
|
|
948
|
+
...currentPalette,
|
|
949
|
+
accent: hexColor,
|
|
950
|
+
accentForeground,
|
|
951
|
+
derivedTokens: {
|
|
952
|
+
...currentPalette.derivedTokens,
|
|
953
|
+
...derivedTokens
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
}));
|
|
959
|
+
},
|
|
960
|
+
applyColorPalette: (theme, mode, colors) => {
|
|
961
|
+
const scale = generateColorScale(colors.primary);
|
|
962
|
+
const primaryForeground = getOptimalForeground2(colors.primary);
|
|
963
|
+
let derivedTokens = computeDerivedTokens("--color-primary", colors.primary, mode);
|
|
964
|
+
let secondary = colors.secondary;
|
|
965
|
+
let secondaryForeground = secondary ? getOptimalForeground2(secondary) : void 0;
|
|
966
|
+
if (secondary) {
|
|
967
|
+
const secondaryDerived = computeDerivedTokens("--color-secondary", secondary, mode);
|
|
968
|
+
derivedTokens = { ...derivedTokens, ...secondaryDerived };
|
|
969
|
+
}
|
|
970
|
+
let accent = colors.accent;
|
|
971
|
+
let accentForeground = accent ? getOptimalForeground2(accent) : void 0;
|
|
972
|
+
if (accent) {
|
|
973
|
+
const accentDerived = computeDerivedTokens("--color-accent", accent, mode);
|
|
974
|
+
derivedTokens = { ...derivedTokens, ...accentDerived };
|
|
975
|
+
}
|
|
976
|
+
const palette = {
|
|
977
|
+
name: colors.name,
|
|
978
|
+
description: colors.description,
|
|
979
|
+
primary: colors.primary,
|
|
980
|
+
primaryForeground,
|
|
981
|
+
secondary,
|
|
982
|
+
secondaryForeground,
|
|
983
|
+
accent,
|
|
984
|
+
accentForeground,
|
|
985
|
+
scale,
|
|
986
|
+
derivedTokens
|
|
987
|
+
};
|
|
988
|
+
set((state) => ({
|
|
989
|
+
customColors: {
|
|
990
|
+
...state.customColors,
|
|
991
|
+
[theme]: {
|
|
992
|
+
...state.customColors[theme],
|
|
993
|
+
[mode]: palette
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
}));
|
|
997
|
+
},
|
|
998
|
+
resetCustomColors: (theme, mode) => {
|
|
999
|
+
if (mode) {
|
|
1000
|
+
set((state) => ({
|
|
1001
|
+
customColors: {
|
|
1002
|
+
...state.customColors,
|
|
1003
|
+
[theme]: {
|
|
1004
|
+
...state.customColors[theme],
|
|
1005
|
+
[mode]: void 0
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
}));
|
|
1009
|
+
} else {
|
|
1010
|
+
set((state) => {
|
|
1011
|
+
const { [theme]: _, ...rest } = state.customColors;
|
|
1012
|
+
return { customColors: rest };
|
|
1013
|
+
});
|
|
1014
|
+
}
|
|
1015
|
+
},
|
|
1016
|
+
getActiveColorPalette: (theme, mode) => {
|
|
1017
|
+
return get().customColors[theme]?.[mode] || null;
|
|
1018
|
+
},
|
|
1019
|
+
// Saved palette management
|
|
1020
|
+
savePalette: (paletteData) => {
|
|
1021
|
+
const id = `custom-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
1022
|
+
const newPalette = {
|
|
1023
|
+
...paletteData,
|
|
1024
|
+
id,
|
|
1025
|
+
category: "custom",
|
|
1026
|
+
createdAt: Date.now()
|
|
1027
|
+
};
|
|
1028
|
+
set((state) => ({
|
|
1029
|
+
savedPalettes: [...state.savedPalettes, newPalette]
|
|
1030
|
+
}));
|
|
1031
|
+
},
|
|
1032
|
+
updatePalette: (id, updates) => {
|
|
1033
|
+
set((state) => ({
|
|
1034
|
+
savedPalettes: state.savedPalettes.map(
|
|
1035
|
+
(p) => p.id === id ? { ...p, ...updates } : p
|
|
1036
|
+
)
|
|
1037
|
+
}));
|
|
1038
|
+
},
|
|
1039
|
+
renamePalette: (id, newName) => {
|
|
1040
|
+
set((state) => ({
|
|
1041
|
+
savedPalettes: state.savedPalettes.map(
|
|
1042
|
+
(p) => p.id === id ? { ...p, name: newName } : p
|
|
1043
|
+
)
|
|
1044
|
+
}));
|
|
1045
|
+
},
|
|
1046
|
+
deletePalette: (id) => {
|
|
1047
|
+
set((state) => ({
|
|
1048
|
+
savedPalettes: state.savedPalettes.filter((p) => p.id !== id)
|
|
1049
|
+
}));
|
|
1050
|
+
},
|
|
1051
|
+
reorderPalettes: (palettes) => {
|
|
1052
|
+
set({ savedPalettes: palettes });
|
|
1053
|
+
},
|
|
1054
|
+
getSavedPalettes: () => {
|
|
1055
|
+
return get().savedPalettes;
|
|
1056
|
+
},
|
|
1057
|
+
// Font theme management
|
|
1058
|
+
applyFontTheme: (theme, mode, fontTheme) => {
|
|
1059
|
+
set((state) => ({
|
|
1060
|
+
customFontThemes: {
|
|
1061
|
+
...state.customFontThemes,
|
|
1062
|
+
[theme]: {
|
|
1063
|
+
...state.customFontThemes[theme],
|
|
1064
|
+
[mode]: fontTheme
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
}));
|
|
1068
|
+
},
|
|
1069
|
+
resetCustomFonts: (theme, mode) => {
|
|
1070
|
+
if (mode) {
|
|
1071
|
+
set((state) => ({
|
|
1072
|
+
customFontThemes: {
|
|
1073
|
+
...state.customFontThemes,
|
|
1074
|
+
[theme]: {
|
|
1075
|
+
...state.customFontThemes[theme],
|
|
1076
|
+
[mode]: void 0
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
}));
|
|
1080
|
+
} else {
|
|
1081
|
+
set((state) => {
|
|
1082
|
+
const { [theme]: _, ...rest } = state.customFontThemes;
|
|
1083
|
+
return { customFontThemes: rest };
|
|
1084
|
+
});
|
|
1085
|
+
}
|
|
1086
|
+
},
|
|
1087
|
+
getActiveFontTheme: (theme, mode) => {
|
|
1088
|
+
return get().customFontThemes[theme]?.[mode] || null;
|
|
1089
|
+
},
|
|
1090
|
+
// Saved font theme management
|
|
1091
|
+
saveFontTheme: (fontThemeData) => {
|
|
1092
|
+
const id = `font-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
1093
|
+
const newFontTheme = {
|
|
1094
|
+
...fontThemeData,
|
|
1095
|
+
id,
|
|
1096
|
+
category: "custom",
|
|
1097
|
+
createdAt: Date.now(),
|
|
1098
|
+
isCustom: true
|
|
1099
|
+
};
|
|
1100
|
+
set((state) => ({
|
|
1101
|
+
savedFontThemes: [...state.savedFontThemes, newFontTheme]
|
|
1102
|
+
}));
|
|
1103
|
+
},
|
|
1104
|
+
updateFontTheme: (id, updates) => {
|
|
1105
|
+
set((state) => ({
|
|
1106
|
+
savedFontThemes: state.savedFontThemes.map(
|
|
1107
|
+
(ft) => ft.id === id ? { ...ft, ...updates } : ft
|
|
1108
|
+
)
|
|
1109
|
+
}));
|
|
1110
|
+
},
|
|
1111
|
+
renameFontTheme: (id, newName) => {
|
|
1112
|
+
set((state) => ({
|
|
1113
|
+
savedFontThemes: state.savedFontThemes.map(
|
|
1114
|
+
(ft) => ft.id === id ? { ...ft, name: newName } : ft
|
|
1115
|
+
)
|
|
1116
|
+
}));
|
|
1117
|
+
},
|
|
1118
|
+
deleteFontTheme: (id) => {
|
|
1119
|
+
set((state) => ({
|
|
1120
|
+
savedFontThemes: state.savedFontThemes.filter((ft) => ft.id !== id)
|
|
1121
|
+
}));
|
|
1122
|
+
},
|
|
1123
|
+
reorderFontThemes: (fontThemes) => {
|
|
1124
|
+
set({ savedFontThemes: fontThemes });
|
|
1125
|
+
},
|
|
1126
|
+
getSavedFontThemes: () => {
|
|
1127
|
+
return get().savedFontThemes;
|
|
1128
|
+
}
|
|
1129
|
+
}),
|
|
1130
|
+
{
|
|
1131
|
+
name: "ecosystem-customizer",
|
|
1132
|
+
version: 4,
|
|
1133
|
+
partialize: (state) => ({
|
|
1134
|
+
motion: state.motion,
|
|
1135
|
+
prefersReducedMotion: state.prefersReducedMotion,
|
|
1136
|
+
customizationMode: state.customizationMode,
|
|
1137
|
+
customColors: state.customColors,
|
|
1138
|
+
savedPalettes: state.savedPalettes,
|
|
1139
|
+
customFontThemes: state.customFontThemes,
|
|
1140
|
+
savedFontThemes: state.savedFontThemes
|
|
1141
|
+
})
|
|
1142
|
+
}
|
|
1143
|
+
)
|
|
1144
|
+
);
|
|
1145
|
+
|
|
1146
|
+
// src/hooks/useMotionPreference.ts
|
|
1147
|
+
function useMotionPreference() {
|
|
1148
|
+
const { motion: motion2, prefersReducedMotion, setPrefersReducedMotion } = useCustomizer();
|
|
1149
|
+
useEffect(() => {
|
|
1150
|
+
if (typeof window === "undefined") return;
|
|
1151
|
+
const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
1152
|
+
setPrefersReducedMotion(mediaQuery.matches);
|
|
1153
|
+
const handleChange = (e) => {
|
|
1154
|
+
setPrefersReducedMotion(e.matches);
|
|
1155
|
+
};
|
|
1156
|
+
mediaQuery.addEventListener("change", handleChange);
|
|
1157
|
+
return () => mediaQuery.removeEventListener("change", handleChange);
|
|
1158
|
+
}, [setPrefersReducedMotion]);
|
|
1159
|
+
return {
|
|
1160
|
+
scale: motion2,
|
|
1161
|
+
shouldAnimate: motion2 > 0 && !prefersReducedMotion,
|
|
1162
|
+
prefersReducedMotion
|
|
1163
|
+
};
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
// src/hooks/useForm.ts
|
|
1167
|
+
import { useState, useCallback } from "react";
|
|
1168
|
+
|
|
1169
|
+
// src/lib/validation.ts
|
|
1170
|
+
function validateField(value, rules) {
|
|
1171
|
+
if (rules.required) {
|
|
1172
|
+
const isEmpty = value === void 0 || value === null || value === "" || Array.isArray(value) && value.length === 0;
|
|
1173
|
+
if (isEmpty) {
|
|
1174
|
+
return typeof rules.required === "string" ? rules.required : "This field is required";
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
if (!value && !rules.required) {
|
|
1178
|
+
return void 0;
|
|
1179
|
+
}
|
|
1180
|
+
if (rules.minLength && value.length < rules.minLength.value) {
|
|
1181
|
+
return rules.minLength.message;
|
|
1182
|
+
}
|
|
1183
|
+
if (rules.maxLength && value.length > rules.maxLength.value) {
|
|
1184
|
+
return rules.maxLength.message;
|
|
1185
|
+
}
|
|
1186
|
+
if (rules.pattern && !rules.pattern.value.test(value)) {
|
|
1187
|
+
return rules.pattern.message;
|
|
1188
|
+
}
|
|
1189
|
+
if (rules.custom) {
|
|
1190
|
+
for (const rule of rules.custom) {
|
|
1191
|
+
if (!rule.validate(value)) {
|
|
1192
|
+
return rule.message;
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
return void 0;
|
|
1197
|
+
}
|
|
1198
|
+
function validateForm(values, validations) {
|
|
1199
|
+
const errors = {};
|
|
1200
|
+
for (const [field, rules] of Object.entries(validations)) {
|
|
1201
|
+
const error = validateField(values[field], rules);
|
|
1202
|
+
if (error) {
|
|
1203
|
+
errors[field] = error;
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
return errors;
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
// src/hooks/useForm.ts
|
|
1210
|
+
function useForm({
|
|
1211
|
+
initialValues,
|
|
1212
|
+
validations = {},
|
|
1213
|
+
onSubmit,
|
|
1214
|
+
validateOn = "onBlur"
|
|
1215
|
+
}) {
|
|
1216
|
+
const [values, setValues] = useState(initialValues);
|
|
1217
|
+
const [errors, setErrors] = useState({});
|
|
1218
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
1219
|
+
const [isDirty, setIsDirty] = useState(false);
|
|
1220
|
+
const setValue = useCallback((name, value) => {
|
|
1221
|
+
setValues((prev) => ({ ...prev, [name]: value }));
|
|
1222
|
+
setIsDirty(true);
|
|
1223
|
+
}, []);
|
|
1224
|
+
const setError = useCallback((name, error) => {
|
|
1225
|
+
setErrors((prev) => ({ ...prev, [name]: error }));
|
|
1226
|
+
}, []);
|
|
1227
|
+
const validateFieldByName = useCallback(
|
|
1228
|
+
(name) => {
|
|
1229
|
+
const fieldRules = validations[name];
|
|
1230
|
+
if (!fieldRules) return;
|
|
1231
|
+
const error = validateField(values[name], fieldRules);
|
|
1232
|
+
setError(name, error);
|
|
1233
|
+
return !error;
|
|
1234
|
+
},
|
|
1235
|
+
[values, validations, setError]
|
|
1236
|
+
);
|
|
1237
|
+
const handleChange = useCallback(
|
|
1238
|
+
(e) => {
|
|
1239
|
+
const { name, value, type } = e.target;
|
|
1240
|
+
const fieldValue = type === "checkbox" ? e.target.checked : value;
|
|
1241
|
+
setValue(name, fieldValue);
|
|
1242
|
+
if (validateOn === "onChange") {
|
|
1243
|
+
validateFieldByName(name);
|
|
1244
|
+
}
|
|
1245
|
+
},
|
|
1246
|
+
[setValue, validateOn, validateFieldByName]
|
|
1247
|
+
);
|
|
1248
|
+
const handleBlur = useCallback(
|
|
1249
|
+
(e) => {
|
|
1250
|
+
const { name } = e.target;
|
|
1251
|
+
if (validateOn === "onBlur") {
|
|
1252
|
+
validateFieldByName(name);
|
|
1253
|
+
}
|
|
1254
|
+
},
|
|
1255
|
+
[validateOn, validateFieldByName]
|
|
1256
|
+
);
|
|
1257
|
+
const validate = useCallback(() => {
|
|
1258
|
+
const formErrors = validateForm(values, validations);
|
|
1259
|
+
setErrors(formErrors);
|
|
1260
|
+
return Object.keys(formErrors).length === 0;
|
|
1261
|
+
}, [values, validations]);
|
|
1262
|
+
const handleSubmit = useCallback(
|
|
1263
|
+
async (e) => {
|
|
1264
|
+
e?.preventDefault();
|
|
1265
|
+
const isValid = validate();
|
|
1266
|
+
if (!isValid) return;
|
|
1267
|
+
if (onSubmit) {
|
|
1268
|
+
setIsSubmitting(true);
|
|
1269
|
+
try {
|
|
1270
|
+
await onSubmit(values);
|
|
1271
|
+
} finally {
|
|
1272
|
+
setIsSubmitting(false);
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
},
|
|
1276
|
+
[validate, onSubmit, values]
|
|
1277
|
+
);
|
|
1278
|
+
const reset = useCallback(() => {
|
|
1279
|
+
setValues(initialValues);
|
|
1280
|
+
setErrors({});
|
|
1281
|
+
setIsDirty(false);
|
|
1282
|
+
setIsSubmitting(false);
|
|
1283
|
+
}, [initialValues]);
|
|
1284
|
+
const getFieldProps = useCallback(
|
|
1285
|
+
(name) => ({
|
|
1286
|
+
name,
|
|
1287
|
+
value: values[name] ?? "",
|
|
1288
|
+
onChange: handleChange,
|
|
1289
|
+
onBlur: handleBlur,
|
|
1290
|
+
error: !!errors[name]
|
|
1291
|
+
}),
|
|
1292
|
+
[values, errors, handleChange, handleBlur]
|
|
1293
|
+
);
|
|
1294
|
+
return {
|
|
1295
|
+
values,
|
|
1296
|
+
errors,
|
|
1297
|
+
isSubmitting,
|
|
1298
|
+
isDirty,
|
|
1299
|
+
setValue,
|
|
1300
|
+
setError,
|
|
1301
|
+
handleChange,
|
|
1302
|
+
handleBlur,
|
|
1303
|
+
handleSubmit,
|
|
1304
|
+
reset,
|
|
1305
|
+
validate,
|
|
1306
|
+
getFieldProps
|
|
1307
|
+
};
|
|
1308
|
+
}
|
|
1309
|
+
export {
|
|
1310
|
+
useForm,
|
|
1311
|
+
useMotionPreference,
|
|
1312
|
+
useTheme
|
|
1313
|
+
};
|
|
1314
|
+
//# sourceMappingURL=hooks.mjs.map
|