@hanzogui/theme-builder 2.0.0-rc.41-hanzoai.5

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.
Files changed (124) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cjs/ThemeBuilder.cjs +248 -0
  3. package/dist/cjs/ThemeBuilder.native.js +338 -0
  4. package/dist/cjs/ThemeBuilder.native.js.map +1 -0
  5. package/dist/cjs/createStudioThemes.cjs +42 -0
  6. package/dist/cjs/createStudioThemes.native.js +45 -0
  7. package/dist/cjs/createStudioThemes.native.js.map +1 -0
  8. package/dist/cjs/createThemes.cjs +395 -0
  9. package/dist/cjs/createThemes.native.js +405 -0
  10. package/dist/cjs/createThemes.native.js.map +1 -0
  11. package/dist/cjs/defaultComponentThemes.cjs +86 -0
  12. package/dist/cjs/defaultComponentThemes.native.js +89 -0
  13. package/dist/cjs/defaultComponentThemes.native.js.map +1 -0
  14. package/dist/cjs/defaultTemplates.cjs +154 -0
  15. package/dist/cjs/defaultTemplates.native.js +162 -0
  16. package/dist/cjs/defaultTemplates.native.js.map +1 -0
  17. package/dist/cjs/defaultTemplatesStronger.cjs +152 -0
  18. package/dist/cjs/defaultTemplatesStronger.native.js +160 -0
  19. package/dist/cjs/defaultTemplatesStronger.native.js.map +1 -0
  20. package/dist/cjs/defaultTemplatesStrongest.cjs +152 -0
  21. package/dist/cjs/defaultTemplatesStrongest.native.js +160 -0
  22. package/dist/cjs/defaultTemplatesStrongest.native.js.map +1 -0
  23. package/dist/cjs/getThemeSuitePalettes.cjs +92 -0
  24. package/dist/cjs/getThemeSuitePalettes.native.js +117 -0
  25. package/dist/cjs/getThemeSuitePalettes.native.js.map +1 -0
  26. package/dist/cjs/helpers.cjs +36 -0
  27. package/dist/cjs/helpers.native.js +41 -0
  28. package/dist/cjs/helpers.native.js.map +1 -0
  29. package/dist/cjs/index.cjs +45 -0
  30. package/dist/cjs/index.native.js +48 -0
  31. package/dist/cjs/index.native.js.map +1 -0
  32. package/dist/cjs/masks.cjs +117 -0
  33. package/dist/cjs/masks.native.js +120 -0
  34. package/dist/cjs/masks.native.js.map +1 -0
  35. package/dist/cjs/types.cjs +18 -0
  36. package/dist/cjs/types.native.js +21 -0
  37. package/dist/cjs/types.native.js.map +1 -0
  38. package/dist/esm/ThemeBuilder.mjs +222 -0
  39. package/dist/esm/ThemeBuilder.mjs.map +1 -0
  40. package/dist/esm/ThemeBuilder.native.js +309 -0
  41. package/dist/esm/ThemeBuilder.native.js.map +1 -0
  42. package/dist/esm/createStudioThemes.mjs +17 -0
  43. package/dist/esm/createStudioThemes.mjs.map +1 -0
  44. package/dist/esm/createStudioThemes.native.js +17 -0
  45. package/dist/esm/createStudioThemes.native.js.map +1 -0
  46. package/dist/esm/createThemes.mjs +364 -0
  47. package/dist/esm/createThemes.mjs.map +1 -0
  48. package/dist/esm/createThemes.native.js +371 -0
  49. package/dist/esm/createThemes.native.js.map +1 -0
  50. package/dist/esm/defaultComponentThemes.mjs +61 -0
  51. package/dist/esm/defaultComponentThemes.mjs.map +1 -0
  52. package/dist/esm/defaultComponentThemes.native.js +61 -0
  53. package/dist/esm/defaultComponentThemes.native.js.map +1 -0
  54. package/dist/esm/defaultTemplates.mjs +129 -0
  55. package/dist/esm/defaultTemplates.mjs.map +1 -0
  56. package/dist/esm/defaultTemplates.native.js +134 -0
  57. package/dist/esm/defaultTemplates.native.js.map +1 -0
  58. package/dist/esm/defaultTemplatesStronger.mjs +127 -0
  59. package/dist/esm/defaultTemplatesStronger.mjs.map +1 -0
  60. package/dist/esm/defaultTemplatesStronger.native.js +132 -0
  61. package/dist/esm/defaultTemplatesStronger.native.js.map +1 -0
  62. package/dist/esm/defaultTemplatesStrongest.mjs +127 -0
  63. package/dist/esm/defaultTemplatesStrongest.mjs.map +1 -0
  64. package/dist/esm/defaultTemplatesStrongest.native.js +132 -0
  65. package/dist/esm/defaultTemplatesStrongest.native.js.map +1 -0
  66. package/dist/esm/getThemeSuitePalettes.mjs +66 -0
  67. package/dist/esm/getThemeSuitePalettes.mjs.map +1 -0
  68. package/dist/esm/getThemeSuitePalettes.native.js +88 -0
  69. package/dist/esm/getThemeSuitePalettes.native.js.map +1 -0
  70. package/dist/esm/helpers.mjs +9 -0
  71. package/dist/esm/helpers.mjs.map +1 -0
  72. package/dist/esm/helpers.native.js +11 -0
  73. package/dist/esm/helpers.native.js.map +1 -0
  74. package/dist/esm/index.js +10 -0
  75. package/dist/esm/index.js.map +1 -0
  76. package/dist/esm/index.mjs +10 -0
  77. package/dist/esm/index.mjs.map +1 -0
  78. package/dist/esm/index.native.js +10 -0
  79. package/dist/esm/index.native.js.map +1 -0
  80. package/dist/esm/masks.mjs +92 -0
  81. package/dist/esm/masks.mjs.map +1 -0
  82. package/dist/esm/masks.native.js +92 -0
  83. package/dist/esm/masks.native.js.map +1 -0
  84. package/dist/esm/types.mjs +2 -0
  85. package/dist/esm/types.mjs.map +1 -0
  86. package/dist/esm/types.native.js +2 -0
  87. package/dist/esm/types.native.js.map +1 -0
  88. package/package.json +48 -0
  89. package/src/ThemeBuilder.ts +514 -0
  90. package/src/createStudioThemes.ts +28 -0
  91. package/src/createThemes.ts +794 -0
  92. package/src/defaultComponentThemes.ts +26 -0
  93. package/src/defaultTemplates.ts +157 -0
  94. package/src/defaultTemplatesStronger.ts +152 -0
  95. package/src/defaultTemplatesStrongest.ts +152 -0
  96. package/src/getThemeSuitePalettes.ts +101 -0
  97. package/src/helpers.ts +44 -0
  98. package/src/index.ts +22 -0
  99. package/src/masks.tsx +83 -0
  100. package/src/types.ts +97 -0
  101. package/types/ThemeBuilder.d.ts +80 -0
  102. package/types/ThemeBuilder.d.ts.map +1 -0
  103. package/types/createStudioThemes.d.ts +8 -0
  104. package/types/createStudioThemes.d.ts.map +1 -0
  105. package/types/createThemes.d.ts +141 -0
  106. package/types/createThemes.d.ts.map +1 -0
  107. package/types/defaultComponentThemes.d.ts +63 -0
  108. package/types/defaultComponentThemes.d.ts.map +1 -0
  109. package/types/defaultTemplates.d.ts +42 -0
  110. package/types/defaultTemplates.d.ts.map +1 -0
  111. package/types/defaultTemplatesStronger.d.ts +42 -0
  112. package/types/defaultTemplatesStronger.d.ts.map +1 -0
  113. package/types/defaultTemplatesStrongest.d.ts +42 -0
  114. package/types/defaultTemplatesStrongest.d.ts.map +1 -0
  115. package/types/getThemeSuitePalettes.d.ts +7 -0
  116. package/types/getThemeSuitePalettes.d.ts.map +1 -0
  117. package/types/helpers.d.ts +24 -0
  118. package/types/helpers.d.ts.map +1 -0
  119. package/types/index.d.ts +11 -0
  120. package/types/index.d.ts.map +1 -0
  121. package/types/masks.d.ts +18 -0
  122. package/types/masks.d.ts.map +1 -0
  123. package/types/types.d.ts +76 -0
  124. package/types/types.d.ts.map +1 -0
@@ -0,0 +1,794 @@
1
+ import { parseToHsla } from 'color2k'
2
+ import type { Template } from '@hanzogui/create-theme'
3
+ import { defaultComponentThemes } from './defaultComponentThemes'
4
+ import { defaultTemplates } from './defaultTemplates'
5
+ import { getThemeSuitePalettes, PALETTE_BACKGROUND_OFFSET } from './getThemeSuitePalettes'
6
+ import { createThemeBuilder, type ThemeBuilder } from './ThemeBuilder'
7
+ import type { BuildPalettes, BuildTemplates } from './types'
8
+
9
+ /**
10
+ * GrandChildren theme nesting logic implementation:
11
+ * - IF palette is set: treat as palette theme (don't nest into accent family)
12
+ * - IF only template: nest into color children, but avoid base themes (light, dark)
13
+ * to prevent conflicts with top-level accent theme
14
+ */
15
+
16
+ type ExtraThemeValues = Record<string, string>
17
+ type ExtraThemeValuesByScheme<Values extends ExtraThemeValues = ExtraThemeValues> = {
18
+ dark: Values
19
+ light: Values
20
+ }
21
+
22
+ type SimpleThemeDefinition = { palette?: Palette; template?: string }
23
+ type BaseThemeDefinition<Extra extends ExtraThemeValuesByScheme> = {
24
+ palette: Palette
25
+ template?: string
26
+ extra?: Extra
27
+ }
28
+
29
+ export type SimpleThemesDefinition = Record<string, SimpleThemeDefinition>
30
+ type SimplePaletteDefinitions = Record<string, string[]>
31
+
32
+ type SinglePalette = string[]
33
+ type SchemePalette = { light: SinglePalette; dark: SinglePalette }
34
+ type Palette = SinglePalette | SchemePalette
35
+
36
+ /** Props for getTheme callback */
37
+ export type GetThemeProps = {
38
+ name: string
39
+ theme: Record<string, string>
40
+ scheme?: 'light' | 'dark'
41
+ parentName: string
42
+ parentNames: string[]
43
+ level: number
44
+ palette?: string[]
45
+ template?: Template
46
+ }
47
+
48
+ export type CreateThemesProps<
49
+ Accent extends BaseThemeDefinition<Extra> | undefined = undefined,
50
+ GrandChildrenThemes extends SimpleThemesDefinition | undefined = undefined,
51
+ Extra extends ExtraThemeValuesByScheme = ExtraThemeValuesByScheme,
52
+ ChildrenThemes extends SimpleThemesDefinition = SimpleThemesDefinition,
53
+ ComponentThemes extends SimpleThemesDefinition | false = SimpleThemesDefinition,
54
+ Templates extends BuildTemplates = typeof defaultTemplates,
55
+ GetThemeReturn extends Record<string, string | number> = Record<string, string>,
56
+ > = {
57
+ base: BaseThemeDefinition<Extra>
58
+ accent?: Accent
59
+ childrenThemes?: ChildrenThemes
60
+ grandChildrenThemes?: GrandChildrenThemes
61
+ templates?: Templates
62
+ /**
63
+ * @deprecated component themes are no longer recommended - configure component styles directly via themes or component defaultProps instead
64
+ */
65
+ componentThemes?: ComponentThemes
66
+ getTheme?: (props: GetThemeProps) => GetThemeReturn
67
+ }
68
+
69
+ // Overload 1: With getTheme callback - infers return type
70
+ export function createThemes<
71
+ Extra extends ExtraThemeValuesByScheme,
72
+ SubThemes extends SimpleThemesDefinition,
73
+ ComponentThemes extends SimpleThemesDefinition | false,
74
+ GrandChildrenThemes extends SimpleThemesDefinition | undefined,
75
+ Accent extends BaseThemeDefinition<Extra> | undefined,
76
+ Templates extends BuildTemplates,
77
+ GetThemeReturn extends Record<string, string | number>,
78
+ >(
79
+ props: CreateThemesProps<
80
+ Accent,
81
+ GrandChildrenThemes,
82
+ Extra,
83
+ SubThemes,
84
+ ComponentThemes,
85
+ Templates,
86
+ GetThemeReturn
87
+ > & {
88
+ getTheme: (props: GetThemeProps) => GetThemeReturn
89
+ }
90
+ ): ReturnType<
91
+ typeof createSimpleThemeBuilder<
92
+ Extra,
93
+ typeof defaultTemplates,
94
+ SimplePaletteDefinitions,
95
+ { [K in keyof SubThemes]: { template: string; palette?: string } },
96
+ GrandChildrenThemes extends undefined
97
+ ? undefined
98
+ : Record<keyof GrandChildrenThemes, any>,
99
+ Accent extends undefined ? false : true,
100
+ ComponentThemes,
101
+ GetThemeReturn
102
+ >
103
+ >['themes']
104
+
105
+ // Overload 2: Without getTheme callback - standard return type
106
+ export function createThemes<
107
+ Extra extends ExtraThemeValuesByScheme = ExtraThemeValuesByScheme,
108
+ SubThemes extends SimpleThemesDefinition = SimpleThemesDefinition,
109
+ ComponentThemes extends SimpleThemesDefinition | false = SimpleThemesDefinition,
110
+ GrandChildrenThemes extends SimpleThemesDefinition | undefined = undefined,
111
+ Accent extends BaseThemeDefinition<Extra> | undefined = undefined,
112
+ Templates extends BuildTemplates = typeof defaultTemplates,
113
+ >(
114
+ props: CreateThemesProps<
115
+ Accent,
116
+ GrandChildrenThemes,
117
+ Extra,
118
+ SubThemes,
119
+ ComponentThemes,
120
+ Templates
121
+ > & {
122
+ getTheme?: undefined
123
+ }
124
+ ): ReturnType<
125
+ typeof createSimpleThemeBuilder<
126
+ Extra,
127
+ typeof defaultTemplates,
128
+ SimplePaletteDefinitions,
129
+ { [K in keyof SubThemes]: { template: string; palette?: string } },
130
+ GrandChildrenThemes extends undefined
131
+ ? undefined
132
+ : Record<keyof GrandChildrenThemes, any>,
133
+ Accent extends undefined ? false : true,
134
+ ComponentThemes,
135
+ Record<string, string>
136
+ >
137
+ >['themes']
138
+
139
+ // Implementation
140
+ export function createThemes<
141
+ Extra extends ExtraThemeValuesByScheme = ExtraThemeValuesByScheme,
142
+ SubThemes extends SimpleThemesDefinition = SimpleThemesDefinition,
143
+ ComponentThemes extends SimpleThemesDefinition | false = SimpleThemesDefinition,
144
+ GrandChildrenThemes extends SimpleThemesDefinition | undefined = undefined,
145
+ Accent extends BaseThemeDefinition<Extra> | undefined = undefined,
146
+ Templates extends BuildTemplates = typeof defaultTemplates,
147
+ GetThemeReturn extends Record<string, string | number> = Record<string, string>,
148
+ >(
149
+ props: CreateThemesProps<
150
+ Accent,
151
+ GrandChildrenThemes,
152
+ Extra,
153
+ SubThemes,
154
+ ComponentThemes,
155
+ Templates,
156
+ GetThemeReturn
157
+ >
158
+ ) {
159
+ const {
160
+ accent,
161
+ childrenThemes,
162
+ grandChildrenThemes,
163
+ templates = defaultTemplates,
164
+ componentThemes,
165
+ getTheme,
166
+ } = props
167
+
168
+ const builder = createSimpleThemeBuilder<
169
+ Extra,
170
+ typeof defaultTemplates,
171
+ SimplePaletteDefinitions,
172
+ { [K in keyof SubThemes]: { template: string; palette?: string } },
173
+ GrandChildrenThemes extends undefined
174
+ ? undefined
175
+ : Record<keyof GrandChildrenThemes, any>,
176
+ Accent extends undefined ? false : true,
177
+ ComponentThemes,
178
+ GetThemeReturn
179
+ >({
180
+ extra: props.base.extra,
181
+ accentExtra: accent?.extra,
182
+ componentThemes,
183
+ palettes: createPalettes(getThemesPalettes(props as any)),
184
+ templates: templates as typeof defaultTemplates,
185
+ accentTheme: !!accent as Accent extends undefined ? false : true,
186
+ childrenThemes: normalizeSubThemes(childrenThemes),
187
+ grandChildrenThemes: (grandChildrenThemes
188
+ ? normalizeSubThemes(grandChildrenThemes)
189
+ : undefined) as GrandChildrenThemes extends undefined
190
+ ? undefined
191
+ : Record<keyof GrandChildrenThemes, any>,
192
+ getTheme: getTheme as any,
193
+ })
194
+
195
+ lastBuilder = builder.themeBuilder
196
+
197
+ return builder.themes
198
+ }
199
+
200
+ let lastBuilder: ThemeBuilder | null = null
201
+
202
+ export const getLastBuilder = () => lastBuilder
203
+
204
+ /**
205
+ * V4 version of createThemes - uses v4 theme ordering for backwards compatibility.
206
+ * Use this for v4 themes (like v4-hanzogui.ts).
207
+ */
208
+ export function createV4Themes<
209
+ Extra extends ExtraThemeValuesByScheme = ExtraThemeValuesByScheme,
210
+ SubThemes extends SimpleThemesDefinition = SimpleThemesDefinition,
211
+ ComponentThemes extends SimpleThemesDefinition | false = SimpleThemesDefinition,
212
+ GrandChildrenThemes extends SimpleThemesDefinition | undefined = undefined,
213
+ Accent extends BaseThemeDefinition<Extra> | undefined = undefined,
214
+ Templates extends BuildTemplates = typeof defaultTemplates,
215
+ GetThemeReturn extends Record<string, string | number> = Record<string, string>,
216
+ >(
217
+ props: CreateThemesProps<
218
+ Accent,
219
+ GrandChildrenThemes,
220
+ Extra,
221
+ SubThemes,
222
+ ComponentThemes,
223
+ Templates,
224
+ GetThemeReturn
225
+ >
226
+ ) {
227
+ const {
228
+ accent,
229
+ childrenThemes,
230
+ grandChildrenThemes,
231
+ templates = defaultTemplates,
232
+ componentThemes,
233
+ getTheme,
234
+ } = props
235
+
236
+ const builder = createV4ThemeBuilder<
237
+ Extra,
238
+ typeof defaultTemplates,
239
+ SimplePaletteDefinitions,
240
+ { [K in keyof SubThemes]: { template: string; palette?: string } },
241
+ GrandChildrenThemes extends undefined
242
+ ? undefined
243
+ : Record<keyof GrandChildrenThemes, any>,
244
+ Accent extends undefined ? false : true,
245
+ ComponentThemes,
246
+ GetThemeReturn
247
+ >({
248
+ extra: props.base.extra,
249
+ accentExtra: accent?.extra,
250
+ componentThemes,
251
+ palettes: createPalettes(getThemesPalettes(props as any)),
252
+ templates: templates as typeof defaultTemplates,
253
+ accentTheme: !!accent as Accent extends undefined ? false : true,
254
+ childrenThemes: normalizeSubThemes(childrenThemes),
255
+ grandChildrenThemes: (grandChildrenThemes
256
+ ? normalizeSubThemes(grandChildrenThemes)
257
+ : undefined) as GrandChildrenThemes extends undefined
258
+ ? undefined
259
+ : Record<keyof GrandChildrenThemes, any>,
260
+ getTheme: getTheme as any,
261
+ })
262
+
263
+ lastBuilder = builder.themeBuilder
264
+
265
+ return builder.themes
266
+ }
267
+
268
+ function normalizeSubThemes<A extends SimpleThemesDefinition>(defs?: A) {
269
+ return Object.fromEntries(
270
+ Object.entries(defs || {}).map(([name, value]) => {
271
+ const hasPalette = value.palette !== undefined
272
+
273
+ return [
274
+ name,
275
+ {
276
+ // Only add palette if the definition has one, otherwise theme is template-only
277
+ ...(hasPalette ? { palette: name } : {}),
278
+ template: value.template || 'base',
279
+ },
280
+ ]
281
+ })
282
+ ) as Record<keyof A, any>
283
+ }
284
+
285
+ const defaultPalettes: SimplePaletteDefinitions = createPalettes(
286
+ getThemesPalettes({
287
+ base: {
288
+ palette: ['#fff', '#000'],
289
+ },
290
+ accent: {
291
+ palette: ['#ff0000', '#ff9999'],
292
+ },
293
+ })
294
+ )
295
+
296
+ type NamesWithChildrenNames<ParentNames extends string, ChildNames> =
297
+ | ParentNames
298
+ | (ChildNames extends string ? `${ParentNames}_${ChildNames}` : never)
299
+
300
+ // a simpler API surface
301
+ export function createSimpleThemeBuilder<
302
+ Extra extends ExtraThemeValuesByScheme,
303
+ Templates extends BuildTemplates,
304
+ Palettes extends SimplePaletteDefinitions,
305
+ ChildrenThemes extends Record<
306
+ string,
307
+ {
308
+ template: string
309
+ palette?: string
310
+ }
311
+ >,
312
+ GrandChildrenThemes extends
313
+ | undefined
314
+ | Record<
315
+ string,
316
+ {
317
+ template: string
318
+ palette?: string
319
+ }
320
+ >,
321
+ HasAccent extends boolean = false,
322
+ ComponentThemes extends SimpleThemesDefinition | false = false,
323
+ GetThemeReturn extends Record<string, string | number> = Record<string, string>,
324
+ FullTheme extends Record<string, string | number> = {
325
+ [ThemeKey in
326
+ | keyof Templates['light_base']
327
+ | keyof Extra['dark']
328
+ | (HasAccent extends true
329
+ ? `accent${0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12}`
330
+ : never)]: string
331
+ },
332
+ ThemeNames extends string =
333
+ | 'light'
334
+ | 'dark'
335
+ | (HasAccent extends true ? 'light_accent' | 'dark_accent' : never)
336
+ | (keyof ChildrenThemes extends string
337
+ ? `${'light' | 'dark'}_${GrandChildrenThemes extends undefined
338
+ ? keyof ChildrenThemes
339
+ : NamesWithChildrenNames<keyof ChildrenThemes, keyof GrandChildrenThemes>}`
340
+ : never),
341
+ >(props: {
342
+ palettes?: Palettes
343
+ accentTheme?: HasAccent
344
+ templates?: Templates
345
+ childrenThemes?: ChildrenThemes
346
+ grandChildrenThemes?: GrandChildrenThemes
347
+ /** @deprecated component themes are no longer recommended */
348
+ componentThemes?: ComponentThemes
349
+ extra?: Extra
350
+ accentExtra?: Extra
351
+ getTheme?: (props: GetThemeProps) => GetThemeReturn
352
+ }): {
353
+ themeBuilder: ThemeBuilder<any>
354
+ themes: Record<ThemeNames, FullTheme & GetThemeReturn>
355
+ } {
356
+ const {
357
+ getTheme,
358
+ extra,
359
+ accentExtra,
360
+ childrenThemes = null as unknown as ChildrenThemes,
361
+ grandChildrenThemes = null as unknown as GrandChildrenThemes,
362
+ templates = defaultTemplates as unknown as Templates,
363
+ palettes = defaultPalettes as unknown as Palettes,
364
+ accentTheme,
365
+ componentThemes = templates === (defaultTemplates as any)
366
+ ? (defaultComponentThemes as unknown as ComponentThemes)
367
+ : undefined,
368
+ } = props
369
+
370
+ // start theme-builder
371
+ let themeBuilder = createThemeBuilder()
372
+ .addPalettes(palettes)
373
+ .addTemplates(templates)
374
+ .addThemes({
375
+ light: {
376
+ template: 'base',
377
+ palette: 'light',
378
+ nonInheritedValues: {
379
+ ...extra?.light,
380
+ ...(accentTheme &&
381
+ palettes.light_accent && {
382
+ accent1: palettes.light_accent[PALETTE_BACKGROUND_OFFSET + 0],
383
+ accent2: palettes.light_accent[PALETTE_BACKGROUND_OFFSET + 1],
384
+ accent3: palettes.light_accent[PALETTE_BACKGROUND_OFFSET + 2],
385
+ accent4: palettes.light_accent[PALETTE_BACKGROUND_OFFSET + 3],
386
+ accent5: palettes.light_accent[PALETTE_BACKGROUND_OFFSET + 4],
387
+ accent6: palettes.light_accent[PALETTE_BACKGROUND_OFFSET + 5],
388
+ accent7: palettes.light_accent[PALETTE_BACKGROUND_OFFSET + 6],
389
+ accent8: palettes.light_accent[PALETTE_BACKGROUND_OFFSET + 7],
390
+ accent9: palettes.light_accent[PALETTE_BACKGROUND_OFFSET + 8],
391
+ accent10: palettes.light_accent[PALETTE_BACKGROUND_OFFSET + 9],
392
+ accent11: palettes.light_accent[PALETTE_BACKGROUND_OFFSET + 10],
393
+ accent12: palettes.light_accent[PALETTE_BACKGROUND_OFFSET + 11],
394
+ }),
395
+ },
396
+ },
397
+ dark: {
398
+ template: 'base',
399
+ palette: 'dark',
400
+ nonInheritedValues: {
401
+ ...extra?.dark,
402
+ ...(accentTheme &&
403
+ palettes.dark_accent && {
404
+ accent1: palettes.dark_accent[PALETTE_BACKGROUND_OFFSET + 0],
405
+ accent2: palettes.dark_accent[PALETTE_BACKGROUND_OFFSET + 1],
406
+ accent3: palettes.dark_accent[PALETTE_BACKGROUND_OFFSET + 2],
407
+ accent4: palettes.dark_accent[PALETTE_BACKGROUND_OFFSET + 3],
408
+ accent5: palettes.dark_accent[PALETTE_BACKGROUND_OFFSET + 4],
409
+ accent6: palettes.dark_accent[PALETTE_BACKGROUND_OFFSET + 5],
410
+ accent7: palettes.dark_accent[PALETTE_BACKGROUND_OFFSET + 6],
411
+ accent8: palettes.dark_accent[PALETTE_BACKGROUND_OFFSET + 7],
412
+ accent9: palettes.dark_accent[PALETTE_BACKGROUND_OFFSET + 8],
413
+ accent10: palettes.dark_accent[PALETTE_BACKGROUND_OFFSET + 9],
414
+ accent11: palettes.dark_accent[PALETTE_BACKGROUND_OFFSET + 10],
415
+ accent12: palettes.dark_accent[PALETTE_BACKGROUND_OFFSET + 11],
416
+ }),
417
+ },
418
+ },
419
+ })
420
+
421
+ // v5 behavior: add accent FIRST, without avoidNestingWithin
422
+ if (palettes.light_accent) {
423
+ themeBuilder = themeBuilder.addChildThemes({
424
+ accent: [
425
+ {
426
+ parent: 'light',
427
+ template: 'base',
428
+ palette: 'light_accent',
429
+ nonInheritedValues: accentExtra?.light,
430
+ },
431
+ {
432
+ parent: 'dark',
433
+ template: 'base',
434
+ palette: 'dark_accent',
435
+ nonInheritedValues: accentExtra?.dark,
436
+ },
437
+ ],
438
+ }) as any
439
+ }
440
+
441
+ // then add children and grandChildren
442
+ if (childrenThemes) {
443
+ themeBuilder = themeBuilder.addChildThemes(childrenThemes, {
444
+ avoidNestingWithin: ['accent'],
445
+ }) as any
446
+ }
447
+
448
+ if (grandChildrenThemes) {
449
+ themeBuilder = themeBuilder.addChildThemes(grandChildrenThemes, {
450
+ avoidNestingWithin: ['accent'],
451
+ }) as any
452
+ }
453
+
454
+ if (componentThemes) {
455
+ themeBuilder = themeBuilder.addComponentThemes(getComponentThemes(componentThemes), {
456
+ avoidNestingWithin: Object.keys(grandChildrenThemes || {}),
457
+ })
458
+ }
459
+
460
+ if (getTheme) {
461
+ themeBuilder = themeBuilder.getTheme(getTheme as any) as any
462
+ }
463
+
464
+ return {
465
+ themeBuilder: themeBuilder as any,
466
+ themes: themeBuilder.build() as any,
467
+ }
468
+ }
469
+
470
+ function getSchemePalette(colors: SinglePalette): SchemePalette {
471
+ return {
472
+ light: colors,
473
+ dark: [...colors].reverse(),
474
+ }
475
+ }
476
+
477
+ function getAnchors(palette: SchemePalette) {
478
+ const maxIndex = 11
479
+ const numItems = palette.light.length
480
+
481
+ const anchors = palette.light.map((lcolor, index) => {
482
+ const dcolor = palette.dark[index]
483
+ const [lhue, lsat, llum, lalpha] = parseToHsla(lcolor)
484
+ const [dhue, dsat, dlum, dalpha] = parseToHsla(dcolor)
485
+
486
+ return {
487
+ index: spreadIndex(maxIndex, numItems, index),
488
+ hue: { light: lhue, dark: dhue },
489
+ sat: { light: lsat, dark: dsat },
490
+ lum: { light: llum, dark: dlum },
491
+ alpha: { light: lalpha, dark: dalpha },
492
+ } as const
493
+ })
494
+
495
+ return anchors
496
+ }
497
+
498
+ function spreadIndex(maxIndex: number, numItems: number, index: number) {
499
+ return Math.round((index / (numItems - 1)) * maxIndex)
500
+ }
501
+
502
+ function coerceSimplePaletteToSchemePalette(def: Palette) {
503
+ return Array.isArray(def) ? getSchemePalette(def) : def
504
+ }
505
+
506
+ function getThemesPalettes(props: CreateThemesProps<any, any>): BuildPalettes {
507
+ const base = coerceSimplePaletteToSchemePalette(props.base.palette)
508
+ const accent = props.accent
509
+ ? coerceSimplePaletteToSchemePalette(props.accent.palette)
510
+ : null
511
+
512
+ const baseAnchors = getAnchors(base)
513
+
514
+ function getSubThemesPalettes(defs: SimpleThemesDefinition, isGrandChildren = false) {
515
+ return Object.fromEntries(
516
+ Object.entries(defs)
517
+ .map(([key, value]) => {
518
+ // For grandChildren accent without custom palette: skip it entirely
519
+ // It will inherit from parent in the theme builder
520
+ if (isGrandChildren && key === 'accent' && !value.palette) {
521
+ return null
522
+ }
523
+
524
+ return [
525
+ key,
526
+ {
527
+ name: key,
528
+ anchors: value.palette
529
+ ? getAnchors(coerceSimplePaletteToSchemePalette(value.palette))
530
+ : baseAnchors,
531
+ },
532
+ ]
533
+ })
534
+ .filter(Boolean) as [string, any][]
535
+ )
536
+ }
537
+
538
+ return {
539
+ base: {
540
+ name: 'base',
541
+ anchors: baseAnchors,
542
+ },
543
+ ...(accent && {
544
+ accent: {
545
+ name: 'accent',
546
+ anchors: getAnchors(accent),
547
+ },
548
+ }),
549
+ ...(props.childrenThemes && getSubThemesPalettes(props.childrenThemes, false)),
550
+ ...(props.grandChildrenThemes &&
551
+ getSubThemesPalettes(props.grandChildrenThemes, true)),
552
+ }
553
+ }
554
+
555
+ export const getComponentThemes = (components: SimpleThemesDefinition) => {
556
+ return Object.fromEntries(
557
+ Object.entries(components).map(([componentName, { template }]) => {
558
+ return [
559
+ componentName,
560
+ {
561
+ parent: '',
562
+ template: template || 'base',
563
+ },
564
+ ]
565
+ })
566
+ )
567
+ }
568
+
569
+ export function createPalettes(palettes: BuildPalettes): SimplePaletteDefinitions {
570
+ const accentPalettes = palettes.accent ? getThemeSuitePalettes(palettes.accent) : null
571
+ const basePalettes = getThemeSuitePalettes(palettes.base)
572
+
573
+ const next = Object.fromEntries(
574
+ Object.entries(palettes).flatMap(([name, palette]) => {
575
+ const palettes = getThemeSuitePalettes(palette)
576
+ const isAccent = name.startsWith('accent')
577
+ const oppositePalettes = isAccent ? basePalettes : accentPalettes || basePalettes
578
+
579
+ if (!oppositePalettes) {
580
+ return []
581
+ }
582
+
583
+ const oppositeLight = oppositePalettes!.light
584
+ const oppositeDark = oppositePalettes!.dark
585
+
586
+ const bgOffset = 7
587
+
588
+ const out = [
589
+ [
590
+ name === 'base' ? 'light' : `light_${name}`,
591
+ [
592
+ oppositeLight[bgOffset],
593
+ ...palettes.light,
594
+ oppositeLight[oppositeLight.length - bgOffset - 1],
595
+ ],
596
+ ],
597
+ [
598
+ name === 'base' ? 'dark' : `dark_${name}`,
599
+ [
600
+ oppositeDark[oppositeDark.length - bgOffset - 1],
601
+ ...palettes.dark,
602
+ oppositeDark[bgOffset],
603
+ ],
604
+ ],
605
+ ] as const
606
+
607
+ return out
608
+ })
609
+ )
610
+
611
+ return next as any
612
+ }
613
+
614
+ /**
615
+ * V4 theme builder - preserves v4 ordering for backwards compatibility:
616
+ * - Children and grandChildren themes are added FIRST
617
+ * - Accent theme is added LAST with avoidNestingWithin for children themes
618
+ *
619
+ * Use this for v4 themes (like v4-hanzogui.ts). The default createSimpleThemeBuilder
620
+ * now uses v5 ordering.
621
+ */
622
+ export function createV4ThemeBuilder<
623
+ Extra extends ExtraThemeValuesByScheme,
624
+ Templates extends BuildTemplates,
625
+ Palettes extends SimplePaletteDefinitions,
626
+ ChildrenThemes extends Record<
627
+ string,
628
+ {
629
+ template: string
630
+ palette?: string
631
+ }
632
+ >,
633
+ GrandChildrenThemes extends
634
+ | undefined
635
+ | Record<
636
+ string,
637
+ {
638
+ template: string
639
+ palette?: string
640
+ }
641
+ >,
642
+ HasAccent extends boolean = false,
643
+ ComponentThemes extends SimpleThemesDefinition | false = false,
644
+ GetThemeReturn extends Record<string, string | number> = Record<string, string>,
645
+ FullTheme extends Record<string, string | number> = {
646
+ [ThemeKey in
647
+ | keyof Templates['light_base']
648
+ | keyof Extra['dark']
649
+ | (HasAccent extends true
650
+ ? `accent${0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12}`
651
+ : never)]: string
652
+ },
653
+ ThemeNames extends string =
654
+ | 'light'
655
+ | 'dark'
656
+ | (HasAccent extends true ? 'light_accent' | 'dark_accent' : never)
657
+ | (keyof ChildrenThemes extends string
658
+ ? `${'light' | 'dark'}_${GrandChildrenThemes extends undefined
659
+ ? keyof ChildrenThemes
660
+ : NamesWithChildrenNames<keyof ChildrenThemes, keyof GrandChildrenThemes>}`
661
+ : never),
662
+ >(props: {
663
+ palettes?: Palettes
664
+ accentTheme?: HasAccent
665
+ templates?: Templates
666
+ childrenThemes?: ChildrenThemes
667
+ grandChildrenThemes?: GrandChildrenThemes
668
+ /** @deprecated component themes are no longer recommended */
669
+ componentThemes?: ComponentThemes
670
+ extra?: Extra
671
+ accentExtra?: Extra
672
+ getTheme?: (props: GetThemeProps) => GetThemeReturn
673
+ }): {
674
+ themeBuilder: ThemeBuilder<any>
675
+ themes: Record<ThemeNames, FullTheme & GetThemeReturn>
676
+ } {
677
+ const {
678
+ getTheme,
679
+ extra,
680
+ accentExtra,
681
+ childrenThemes = null as unknown as ChildrenThemes,
682
+ grandChildrenThemes = null as unknown as GrandChildrenThemes,
683
+ templates = defaultTemplates as unknown as Templates,
684
+ palettes = defaultPalettes as unknown as Palettes,
685
+ accentTheme,
686
+ componentThemes = templates === (defaultTemplates as any)
687
+ ? (defaultComponentThemes as unknown as ComponentThemes)
688
+ : undefined,
689
+ } = props
690
+
691
+ // start theme-builder
692
+ let themeBuilder = createThemeBuilder()
693
+ .addPalettes(palettes)
694
+ .addTemplates(templates)
695
+ .addThemes({
696
+ light: {
697
+ template: 'base',
698
+ palette: 'light',
699
+ nonInheritedValues: {
700
+ ...extra?.light,
701
+ ...(accentTheme &&
702
+ palettes.light_accent && {
703
+ accent1: palettes.light_accent[PALETTE_BACKGROUND_OFFSET + 0],
704
+ accent2: palettes.light_accent[PALETTE_BACKGROUND_OFFSET + 1],
705
+ accent3: palettes.light_accent[PALETTE_BACKGROUND_OFFSET + 2],
706
+ accent4: palettes.light_accent[PALETTE_BACKGROUND_OFFSET + 3],
707
+ accent5: palettes.light_accent[PALETTE_BACKGROUND_OFFSET + 4],
708
+ accent6: palettes.light_accent[PALETTE_BACKGROUND_OFFSET + 5],
709
+ accent7: palettes.light_accent[PALETTE_BACKGROUND_OFFSET + 6],
710
+ accent8: palettes.light_accent[PALETTE_BACKGROUND_OFFSET + 7],
711
+ accent9: palettes.light_accent[PALETTE_BACKGROUND_OFFSET + 8],
712
+ accent10: palettes.light_accent[PALETTE_BACKGROUND_OFFSET + 9],
713
+ accent11: palettes.light_accent[PALETTE_BACKGROUND_OFFSET + 10],
714
+ accent12: palettes.light_accent[PALETTE_BACKGROUND_OFFSET + 11],
715
+ }),
716
+ },
717
+ },
718
+ dark: {
719
+ template: 'base',
720
+ palette: 'dark',
721
+ nonInheritedValues: {
722
+ ...extra?.dark,
723
+ ...(accentTheme &&
724
+ palettes.dark_accent && {
725
+ accent1: palettes.dark_accent[PALETTE_BACKGROUND_OFFSET + 0],
726
+ accent2: palettes.dark_accent[PALETTE_BACKGROUND_OFFSET + 1],
727
+ accent3: palettes.dark_accent[PALETTE_BACKGROUND_OFFSET + 2],
728
+ accent4: palettes.dark_accent[PALETTE_BACKGROUND_OFFSET + 3],
729
+ accent5: palettes.dark_accent[PALETTE_BACKGROUND_OFFSET + 4],
730
+ accent6: palettes.dark_accent[PALETTE_BACKGROUND_OFFSET + 5],
731
+ accent7: palettes.dark_accent[PALETTE_BACKGROUND_OFFSET + 6],
732
+ accent8: palettes.dark_accent[PALETTE_BACKGROUND_OFFSET + 7],
733
+ accent9: palettes.dark_accent[PALETTE_BACKGROUND_OFFSET + 8],
734
+ accent10: palettes.dark_accent[PALETTE_BACKGROUND_OFFSET + 9],
735
+ accent11: palettes.dark_accent[PALETTE_BACKGROUND_OFFSET + 10],
736
+ accent12: palettes.dark_accent[PALETTE_BACKGROUND_OFFSET + 11],
737
+ }),
738
+ },
739
+ },
740
+ })
741
+
742
+ // v4 behavior: add children and grandChildren FIRST
743
+ if (childrenThemes) {
744
+ themeBuilder = themeBuilder.addChildThemes(childrenThemes, {
745
+ avoidNestingWithin: ['accent'],
746
+ }) as any
747
+ }
748
+
749
+ if (grandChildrenThemes) {
750
+ themeBuilder = themeBuilder.addChildThemes(grandChildrenThemes, {
751
+ avoidNestingWithin: ['accent'],
752
+ }) as any
753
+ }
754
+
755
+ // then add accent LAST with avoidNestingWithin
756
+ if (palettes.light_accent) {
757
+ themeBuilder = themeBuilder.addChildThemes(
758
+ {
759
+ accent: [
760
+ {
761
+ parent: 'light',
762
+ template: 'base',
763
+ palette: 'light_accent',
764
+ nonInheritedValues: accentExtra?.light,
765
+ },
766
+ {
767
+ parent: 'dark',
768
+ template: 'base',
769
+ palette: 'dark_accent',
770
+ nonInheritedValues: accentExtra?.dark,
771
+ },
772
+ ],
773
+ },
774
+ {
775
+ avoidNestingWithin: Object.keys(childrenThemes || {}),
776
+ }
777
+ ) as any
778
+ }
779
+
780
+ if (componentThemes) {
781
+ themeBuilder = themeBuilder.addComponentThemes(getComponentThemes(componentThemes), {
782
+ avoidNestingWithin: Object.keys(grandChildrenThemes || {}),
783
+ })
784
+ }
785
+
786
+ if (getTheme) {
787
+ themeBuilder = themeBuilder.getTheme(getTheme as any) as any
788
+ }
789
+
790
+ return {
791
+ themeBuilder: themeBuilder as any,
792
+ themes: themeBuilder.build() as any,
793
+ }
794
+ }