@fakhrirafiki/theme-engine 0.4.5 → 0.4.8

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/index.d.mts CHANGED
@@ -355,40 +355,27 @@ interface UnifiedThemeContextValue {
355
355
  /** Custom presets only */
356
356
  customPresets: Record<string, TweakCNThemePreset>;
357
357
  }
358
- type CustomPresetsRecord$1 = Record<string, TweakCNThemePreset>;
359
- type CustomPresetId$1<TCustomPresets> = TCustomPresets extends CustomPresetsRecord$1 ? string extends keyof TCustomPresets ? never : Extract<keyof TCustomPresets, string> : never;
360
- type PresetId<TCustomPresets> = BuiltInPresetId | CustomPresetId$1<TCustomPresets>;
358
+ type CustomPresetsRecord$2 = Record<string, TweakCNThemePreset>;
359
+ type CustomPresetId$2<TCustomPresets> = TCustomPresets extends CustomPresetsRecord$2 ? string extends keyof TCustomPresets ? never : Extract<keyof TCustomPresets, string> : never;
360
+ type PresetId<TCustomPresets> = BuiltInPresetId | CustomPresetId$2<TCustomPresets>;
361
361
  /**
362
362
  * Props for the UnifiedThemeProvider component.
363
363
  *
364
364
  * @public
365
365
  */
366
- interface UnifiedThemeProviderProps<TCustomPresets extends CustomPresetsRecord$1 | undefined = undefined> {
366
+ interface UnifiedThemeProviderProps<TCustomPresets extends CustomPresetsRecord$2 | undefined = undefined> {
367
367
  /** React children to wrap with theming context */
368
368
  children: react__default.ReactNode;
369
369
  /** Default appearance mode when no stored preference exists */
370
370
  defaultMode?: Mode;
371
371
  /** Default preset ID to use when no stored preset exists or when resetting */
372
372
  defaultPreset?: PresetId<TCustomPresets>;
373
- /** Enable smooth transitions between modes */
374
- enableTransitions?: boolean;
375
373
  /** localStorage key for appearance mode persistence */
376
374
  modeStorageKey?: string;
377
375
  /** localStorage key for color preset persistence */
378
376
  presetStorageKey?: string;
379
- /** Enable custom color preset functionality */
380
- enablePresets?: boolean;
381
377
  /** Custom presets to add to the available collection */
382
378
  customPresets?: TCustomPresets;
383
- /** Whether to include built-in TweakCN presets (default: true) */
384
- includeBuiltInPresets?: boolean;
385
- /** Disable injecting `ThemeScript` from inside `ThemeProvider` (useful if you render it in `<head>` yourself). */
386
- disableScript?: boolean;
387
- /**
388
- * When true, the provider renders `null` until persisted preset is loaded from storage.
389
- * Defaults to `true` when `disableScript` is enabled (backwards behavior), otherwise `false`.
390
- */
391
- deferRenderUntilReady?: boolean;
392
379
  }
393
380
  /**
394
381
  * Theme Provider - The heart of the elegant theming system.
@@ -410,7 +397,6 @@ interface UnifiedThemeProviderProps<TCustomPresets extends CustomPresetsRecord$1
410
397
  * defaultMode="system"
411
398
  * modeStorageKey="app-mode"
412
399
  * presetStorageKey="app-preset"
413
- * enablePresets={true}
414
400
  * >
415
401
  * <App />
416
402
  * </ThemeProvider>
@@ -418,7 +404,7 @@ interface UnifiedThemeProviderProps<TCustomPresets extends CustomPresetsRecord$1
418
404
  *
419
405
  * @public
420
406
  */
421
- declare function ThemeProvider<const TCustomPresets extends CustomPresetsRecord$1 | undefined = undefined>({ children, defaultMode, defaultPreset, enableTransitions, modeStorageKey, presetStorageKey, enablePresets, customPresets, includeBuiltInPresets, disableScript, deferRenderUntilReady, }: UnifiedThemeProviderProps<TCustomPresets>): react_jsx_runtime.JSX.Element | null;
407
+ declare function ThemeProvider<const TCustomPresets extends CustomPresetsRecord$2 | undefined = undefined>({ children, defaultMode, defaultPreset, modeStorageKey, presetStorageKey, customPresets, }: UnifiedThemeProviderProps<TCustomPresets>): react_jsx_runtime.JSX.Element;
422
408
  /**
423
409
  * Hook for accessing the unified theming system.
424
410
  *
@@ -492,10 +478,10 @@ declare const ThemeToggle: react.ForwardRefExoticComponent<ThemeToggleProps & re
492
478
  */
493
479
  declare const ThemePresetButtons: ({ animation: animationOverrides, layout: layoutOverrides, renderPreset, renderColorBox, className, categories, maxPresets, showBuiltIn, showCustom, }: ThemePresetButtonsProps) => react_jsx_runtime.JSX.Element;
494
480
 
495
- type CustomPresetsRecord = Record<string, TweakCNThemePreset>;
496
- type CustomPresetId<TCustomPresets> = TCustomPresets extends CustomPresetsRecord ? string extends keyof TCustomPresets ? never : Extract<keyof TCustomPresets, string> : never;
497
- type ThemePresetId<TCustomPresets extends CustomPresetsRecord | undefined = undefined> = BuiltInPresetId | CustomPresetId<TCustomPresets>;
498
- type LooseString = string & {};
481
+ type CustomPresetsRecord$1 = Record<string, TweakCNThemePreset>;
482
+ type CustomPresetId$1<TCustomPresets> = TCustomPresets extends CustomPresetsRecord$1 ? string extends keyof TCustomPresets ? never : Extract<keyof TCustomPresets, string> : never;
483
+ type ThemePresetId$1<TCustomPresets extends CustomPresetsRecord$1 | undefined = undefined> = BuiltInPresetId | CustomPresetId$1<TCustomPresets>;
484
+ type LooseString$1 = string & {};
499
485
  /**
500
486
  * Typed convenience wrapper around `useTheme()` that provides IntelliSense for preset IDs.
501
487
  *
@@ -504,8 +490,8 @@ type LooseString = string & {};
504
490
  *
505
491
  * The resulting `setThemePresetById()` still accepts any string, but VS Code will suggest known IDs first.
506
492
  */
507
- declare function useTypedTheme<const TCustomPresets extends CustomPresetsRecord | undefined = undefined>(customPresets?: TCustomPresets): {
508
- setThemePresetById: (presetId: LooseString | ThemePresetId<TCustomPresets>) => void;
493
+ declare function useTypedTheme<const TCustomPresets extends CustomPresetsRecord$1 | undefined = undefined>(customPresets?: TCustomPresets): {
494
+ setThemePresetById: (presetId: LooseString$1 | ThemePresetId$1<TCustomPresets>) => void;
509
495
  mode: Mode;
510
496
  resolvedMode: "light" | "dark";
511
497
  setMode: (mode: Mode) => void;
@@ -524,6 +510,59 @@ declare function useTypedTheme<const TCustomPresets extends CustomPresetsRecord
524
510
  customPresets: Record<string, TweakCNThemePreset>;
525
511
  };
526
512
 
513
+ type CustomPresetsRecord = Record<string, TweakCNThemePreset>;
514
+ type CustomPresetId<TCustomPresets> = TCustomPresets extends CustomPresetsRecord ? string extends keyof TCustomPresets ? never : Extract<keyof TCustomPresets, string> : never;
515
+ type ThemePresetId<TCustomPresets extends CustomPresetsRecord | undefined = undefined> = BuiltInPresetId | CustomPresetId<TCustomPresets>;
516
+ type LooseString = string & {};
517
+ /**
518
+ * Type helper to "register" your presets for autocomplete.
519
+ *
520
+ * Usage:
521
+ * `useThemeEngine<ThemePresets<typeof customPresets>>()`
522
+ */
523
+ type ThemePresets<T> = T extends CustomPresetsRecord ? T : never;
524
+ type ThemeEnginePresetId<TCustomPresets extends CustomPresetsRecord | undefined = undefined> = ThemePresetId<TCustomPresets>;
525
+ type ThemeId<TCustomPresets extends CustomPresetsRecord | undefined = undefined> = ThemeEnginePresetId<TCustomPresets> | LooseString;
526
+ /**
527
+ * DX-first hook for Theme Engine.
528
+ *
529
+ * Unifies:
530
+ * - mode controls (`light` | `dark` | `system`)
531
+ * - preset controls (apply/clear by ID)
532
+ *
533
+ * For typed preset ID autocomplete (built-in + your custom IDs):
534
+ * `useThemeEngine<ThemePresets<typeof customPresets>>()`
535
+ */
536
+ declare function useThemeEngine<const TCustomPresets extends CustomPresetsRecord | undefined = undefined>(): {
537
+ darkMode: boolean;
538
+ mode: Mode;
539
+ resolvedMode: "light" | "dark";
540
+ setDarkMode: (mode: Mode) => void;
541
+ setMode: (mode: Mode) => void;
542
+ toggleDarkMode: (coordinates?: Coordinates) => void;
543
+ toggleMode: (coordinates?: Coordinates) => void;
544
+ applyThemeById: (themeId: ThemeId<TCustomPresets>) => void;
545
+ applyPresetById: (themeId: ThemeId<TCustomPresets>) => void;
546
+ clearTheme: () => void;
547
+ clearPreset: () => void;
548
+ currentTheme: {
549
+ presetId: string;
550
+ presetName: string;
551
+ colors: ThemePreset["colors"];
552
+ appliedAt: number;
553
+ } | null;
554
+ currentPreset: {
555
+ presetId: string;
556
+ presetName: string;
557
+ colors: ThemePreset["colors"];
558
+ appliedAt: number;
559
+ } | null;
560
+ isUsingDefaultPreset: boolean;
561
+ availablePresets: Record<string, TweakCNThemePreset>;
562
+ builtInPresets: Record<string, TweakCNThemePreset>;
563
+ customPresets: Record<string, TweakCNThemePreset>;
564
+ };
565
+
527
566
  /**
528
567
  * Lightweight color utilities for theme-engine
529
568
  * Provides basic color manipulation without heavy dependencies
@@ -565,4 +604,4 @@ declare function validateCustomPresets(customPresets: Record<string, TweakCNThem
565
604
  */
566
605
  declare function logValidationResult(result: ValidationResult, context?: string): void;
567
606
 
568
- export { type BuiltInPresetId, type CompleteThemeSchema, type Coordinates, type Mode, type PresetProvider, type ThemePreset, ThemePresetButtons, type ThemePresetButtonsProps, type ThemePresetId, ThemeProvider, ThemeScript, ThemeToggle, type ThemeToggleProps, type TweakCNThemePreset, type ValidationResult, builtInPresetIds, formatColor, getPresetById, getPresetEntries, getPresetIds, getPresetLabels, getPresetsCount, logValidationResult, searchPresets, tweakcnPresets, useTheme, useTypedTheme, validateCustomPresets, validateTweakCNPreset, withAlpha };
607
+ export { type BuiltInPresetId, type CompleteThemeSchema, type Coordinates, type Mode, type PresetProvider, type ThemeEnginePresetId, type ThemeId, type ThemePreset, ThemePresetButtons, type ThemePresetButtonsProps, type ThemePresetId$1 as ThemePresetId, type ThemePresets, ThemeProvider, ThemeScript, ThemeToggle, type ThemeToggleProps, type TweakCNThemePreset, type ValidationResult, builtInPresetIds, formatColor, getPresetById, getPresetEntries, getPresetIds, getPresetLabels, getPresetsCount, logValidationResult, searchPresets, tweakcnPresets, useTheme, useThemeEngine, useTypedTheme, validateCustomPresets, validateTweakCNPreset, withAlpha };
package/dist/index.d.ts CHANGED
@@ -355,40 +355,27 @@ interface UnifiedThemeContextValue {
355
355
  /** Custom presets only */
356
356
  customPresets: Record<string, TweakCNThemePreset>;
357
357
  }
358
- type CustomPresetsRecord$1 = Record<string, TweakCNThemePreset>;
359
- type CustomPresetId$1<TCustomPresets> = TCustomPresets extends CustomPresetsRecord$1 ? string extends keyof TCustomPresets ? never : Extract<keyof TCustomPresets, string> : never;
360
- type PresetId<TCustomPresets> = BuiltInPresetId | CustomPresetId$1<TCustomPresets>;
358
+ type CustomPresetsRecord$2 = Record<string, TweakCNThemePreset>;
359
+ type CustomPresetId$2<TCustomPresets> = TCustomPresets extends CustomPresetsRecord$2 ? string extends keyof TCustomPresets ? never : Extract<keyof TCustomPresets, string> : never;
360
+ type PresetId<TCustomPresets> = BuiltInPresetId | CustomPresetId$2<TCustomPresets>;
361
361
  /**
362
362
  * Props for the UnifiedThemeProvider component.
363
363
  *
364
364
  * @public
365
365
  */
366
- interface UnifiedThemeProviderProps<TCustomPresets extends CustomPresetsRecord$1 | undefined = undefined> {
366
+ interface UnifiedThemeProviderProps<TCustomPresets extends CustomPresetsRecord$2 | undefined = undefined> {
367
367
  /** React children to wrap with theming context */
368
368
  children: react__default.ReactNode;
369
369
  /** Default appearance mode when no stored preference exists */
370
370
  defaultMode?: Mode;
371
371
  /** Default preset ID to use when no stored preset exists or when resetting */
372
372
  defaultPreset?: PresetId<TCustomPresets>;
373
- /** Enable smooth transitions between modes */
374
- enableTransitions?: boolean;
375
373
  /** localStorage key for appearance mode persistence */
376
374
  modeStorageKey?: string;
377
375
  /** localStorage key for color preset persistence */
378
376
  presetStorageKey?: string;
379
- /** Enable custom color preset functionality */
380
- enablePresets?: boolean;
381
377
  /** Custom presets to add to the available collection */
382
378
  customPresets?: TCustomPresets;
383
- /** Whether to include built-in TweakCN presets (default: true) */
384
- includeBuiltInPresets?: boolean;
385
- /** Disable injecting `ThemeScript` from inside `ThemeProvider` (useful if you render it in `<head>` yourself). */
386
- disableScript?: boolean;
387
- /**
388
- * When true, the provider renders `null` until persisted preset is loaded from storage.
389
- * Defaults to `true` when `disableScript` is enabled (backwards behavior), otherwise `false`.
390
- */
391
- deferRenderUntilReady?: boolean;
392
379
  }
393
380
  /**
394
381
  * Theme Provider - The heart of the elegant theming system.
@@ -410,7 +397,6 @@ interface UnifiedThemeProviderProps<TCustomPresets extends CustomPresetsRecord$1
410
397
  * defaultMode="system"
411
398
  * modeStorageKey="app-mode"
412
399
  * presetStorageKey="app-preset"
413
- * enablePresets={true}
414
400
  * >
415
401
  * <App />
416
402
  * </ThemeProvider>
@@ -418,7 +404,7 @@ interface UnifiedThemeProviderProps<TCustomPresets extends CustomPresetsRecord$1
418
404
  *
419
405
  * @public
420
406
  */
421
- declare function ThemeProvider<const TCustomPresets extends CustomPresetsRecord$1 | undefined = undefined>({ children, defaultMode, defaultPreset, enableTransitions, modeStorageKey, presetStorageKey, enablePresets, customPresets, includeBuiltInPresets, disableScript, deferRenderUntilReady, }: UnifiedThemeProviderProps<TCustomPresets>): react_jsx_runtime.JSX.Element | null;
407
+ declare function ThemeProvider<const TCustomPresets extends CustomPresetsRecord$2 | undefined = undefined>({ children, defaultMode, defaultPreset, modeStorageKey, presetStorageKey, customPresets, }: UnifiedThemeProviderProps<TCustomPresets>): react_jsx_runtime.JSX.Element;
422
408
  /**
423
409
  * Hook for accessing the unified theming system.
424
410
  *
@@ -492,10 +478,10 @@ declare const ThemeToggle: react.ForwardRefExoticComponent<ThemeToggleProps & re
492
478
  */
493
479
  declare const ThemePresetButtons: ({ animation: animationOverrides, layout: layoutOverrides, renderPreset, renderColorBox, className, categories, maxPresets, showBuiltIn, showCustom, }: ThemePresetButtonsProps) => react_jsx_runtime.JSX.Element;
494
480
 
495
- type CustomPresetsRecord = Record<string, TweakCNThemePreset>;
496
- type CustomPresetId<TCustomPresets> = TCustomPresets extends CustomPresetsRecord ? string extends keyof TCustomPresets ? never : Extract<keyof TCustomPresets, string> : never;
497
- type ThemePresetId<TCustomPresets extends CustomPresetsRecord | undefined = undefined> = BuiltInPresetId | CustomPresetId<TCustomPresets>;
498
- type LooseString = string & {};
481
+ type CustomPresetsRecord$1 = Record<string, TweakCNThemePreset>;
482
+ type CustomPresetId$1<TCustomPresets> = TCustomPresets extends CustomPresetsRecord$1 ? string extends keyof TCustomPresets ? never : Extract<keyof TCustomPresets, string> : never;
483
+ type ThemePresetId$1<TCustomPresets extends CustomPresetsRecord$1 | undefined = undefined> = BuiltInPresetId | CustomPresetId$1<TCustomPresets>;
484
+ type LooseString$1 = string & {};
499
485
  /**
500
486
  * Typed convenience wrapper around `useTheme()` that provides IntelliSense for preset IDs.
501
487
  *
@@ -504,8 +490,8 @@ type LooseString = string & {};
504
490
  *
505
491
  * The resulting `setThemePresetById()` still accepts any string, but VS Code will suggest known IDs first.
506
492
  */
507
- declare function useTypedTheme<const TCustomPresets extends CustomPresetsRecord | undefined = undefined>(customPresets?: TCustomPresets): {
508
- setThemePresetById: (presetId: LooseString | ThemePresetId<TCustomPresets>) => void;
493
+ declare function useTypedTheme<const TCustomPresets extends CustomPresetsRecord$1 | undefined = undefined>(customPresets?: TCustomPresets): {
494
+ setThemePresetById: (presetId: LooseString$1 | ThemePresetId$1<TCustomPresets>) => void;
509
495
  mode: Mode;
510
496
  resolvedMode: "light" | "dark";
511
497
  setMode: (mode: Mode) => void;
@@ -524,6 +510,59 @@ declare function useTypedTheme<const TCustomPresets extends CustomPresetsRecord
524
510
  customPresets: Record<string, TweakCNThemePreset>;
525
511
  };
526
512
 
513
+ type CustomPresetsRecord = Record<string, TweakCNThemePreset>;
514
+ type CustomPresetId<TCustomPresets> = TCustomPresets extends CustomPresetsRecord ? string extends keyof TCustomPresets ? never : Extract<keyof TCustomPresets, string> : never;
515
+ type ThemePresetId<TCustomPresets extends CustomPresetsRecord | undefined = undefined> = BuiltInPresetId | CustomPresetId<TCustomPresets>;
516
+ type LooseString = string & {};
517
+ /**
518
+ * Type helper to "register" your presets for autocomplete.
519
+ *
520
+ * Usage:
521
+ * `useThemeEngine<ThemePresets<typeof customPresets>>()`
522
+ */
523
+ type ThemePresets<T> = T extends CustomPresetsRecord ? T : never;
524
+ type ThemeEnginePresetId<TCustomPresets extends CustomPresetsRecord | undefined = undefined> = ThemePresetId<TCustomPresets>;
525
+ type ThemeId<TCustomPresets extends CustomPresetsRecord | undefined = undefined> = ThemeEnginePresetId<TCustomPresets> | LooseString;
526
+ /**
527
+ * DX-first hook for Theme Engine.
528
+ *
529
+ * Unifies:
530
+ * - mode controls (`light` | `dark` | `system`)
531
+ * - preset controls (apply/clear by ID)
532
+ *
533
+ * For typed preset ID autocomplete (built-in + your custom IDs):
534
+ * `useThemeEngine<ThemePresets<typeof customPresets>>()`
535
+ */
536
+ declare function useThemeEngine<const TCustomPresets extends CustomPresetsRecord | undefined = undefined>(): {
537
+ darkMode: boolean;
538
+ mode: Mode;
539
+ resolvedMode: "light" | "dark";
540
+ setDarkMode: (mode: Mode) => void;
541
+ setMode: (mode: Mode) => void;
542
+ toggleDarkMode: (coordinates?: Coordinates) => void;
543
+ toggleMode: (coordinates?: Coordinates) => void;
544
+ applyThemeById: (themeId: ThemeId<TCustomPresets>) => void;
545
+ applyPresetById: (themeId: ThemeId<TCustomPresets>) => void;
546
+ clearTheme: () => void;
547
+ clearPreset: () => void;
548
+ currentTheme: {
549
+ presetId: string;
550
+ presetName: string;
551
+ colors: ThemePreset["colors"];
552
+ appliedAt: number;
553
+ } | null;
554
+ currentPreset: {
555
+ presetId: string;
556
+ presetName: string;
557
+ colors: ThemePreset["colors"];
558
+ appliedAt: number;
559
+ } | null;
560
+ isUsingDefaultPreset: boolean;
561
+ availablePresets: Record<string, TweakCNThemePreset>;
562
+ builtInPresets: Record<string, TweakCNThemePreset>;
563
+ customPresets: Record<string, TweakCNThemePreset>;
564
+ };
565
+
527
566
  /**
528
567
  * Lightweight color utilities for theme-engine
529
568
  * Provides basic color manipulation without heavy dependencies
@@ -565,4 +604,4 @@ declare function validateCustomPresets(customPresets: Record<string, TweakCNThem
565
604
  */
566
605
  declare function logValidationResult(result: ValidationResult, context?: string): void;
567
606
 
568
- export { type BuiltInPresetId, type CompleteThemeSchema, type Coordinates, type Mode, type PresetProvider, type ThemePreset, ThemePresetButtons, type ThemePresetButtonsProps, type ThemePresetId, ThemeProvider, ThemeScript, ThemeToggle, type ThemeToggleProps, type TweakCNThemePreset, type ValidationResult, builtInPresetIds, formatColor, getPresetById, getPresetEntries, getPresetIds, getPresetLabels, getPresetsCount, logValidationResult, searchPresets, tweakcnPresets, useTheme, useTypedTheme, validateCustomPresets, validateTweakCNPreset, withAlpha };
607
+ export { type BuiltInPresetId, type CompleteThemeSchema, type Coordinates, type Mode, type PresetProvider, type ThemeEnginePresetId, type ThemeId, type ThemePreset, ThemePresetButtons, type ThemePresetButtonsProps, type ThemePresetId$1 as ThemePresetId, type ThemePresets, ThemeProvider, ThemeScript, ThemeToggle, type ThemeToggleProps, type TweakCNThemePreset, type ValidationResult, builtInPresetIds, formatColor, getPresetById, getPresetEntries, getPresetIds, getPresetLabels, getPresetsCount, logValidationResult, searchPresets, tweakcnPresets, useTheme, useThemeEngine, useTypedTheme, validateCustomPresets, validateTweakCNPreset, withAlpha };
package/dist/index.js CHANGED
@@ -36,6 +36,7 @@ __export(index_exports, {
36
36
  searchPresets: () => searchPresets,
37
37
  tweakcnPresets: () => tweakcnPresets,
38
38
  useTheme: () => useTheme,
39
+ useThemeEngine: () => useThemeEngine,
39
40
  useTypedTheme: () => useTypedTheme,
40
41
  validateCustomPresets: () => validateCustomPresets,
41
42
  validateTweakCNPreset: () => validateTweakCNPreset,
@@ -3983,6 +3984,29 @@ function ThemeScript({ presetStorageKey = "theme-preset", defaultPreset }) {
3983
3984
  if (rgb) return toTriplet(rgbToHsl(rgb.r, rgb.g, rgb.b));
3984
3985
  }
3985
3986
 
3987
+ // Any other CSS color (e.g. oklch(...), named colors) -> resolve via computed styles.
3988
+ try {
3989
+ const probe = document.createElement('span');
3990
+ probe.style.color = trimmed;
3991
+ probe.style.position = 'absolute';
3992
+ probe.style.left = '-9999px';
3993
+ probe.style.top = '-9999px';
3994
+ probe.style.visibility = 'hidden';
3995
+ document.documentElement.appendChild(probe);
3996
+ const computed = getComputedStyle(probe).color; // rgb(...) or rgba(...)
3997
+ probe.remove();
3998
+
3999
+ const match = computed && computed.match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/i);
4000
+ if (match) {
4001
+ const r = clamp(parseFloat(match[1]) || 0, 0, 255);
4002
+ const g = clamp(parseFloat(match[2]) || 0, 0, 255);
4003
+ const b = clamp(parseFloat(match[3]) || 0, 0, 255);
4004
+ return toTriplet(rgbToHsl(r, g, b));
4005
+ }
4006
+ } catch (e) {
4007
+ // ignore and fall through
4008
+ }
4009
+
3986
4010
  return value;
3987
4011
  }
3988
4012
 
@@ -4255,23 +4279,40 @@ function normalizeColorValueToHslTriplet(value) {
4255
4279
  if (parsedHsl) return formatHSL(parsedHsl, false);
4256
4280
  const parsedRgb = parseHex(trimmed);
4257
4281
  if (parsedRgb) return formatHSL(rgbToHsl(parsedRgb), false);
4258
- return value;
4282
+ if (typeof document !== "undefined") {
4283
+ try {
4284
+ if (trimmed.startsWith("var(")) return trimmed;
4285
+ const probe = document.createElement("span");
4286
+ probe.style.color = trimmed;
4287
+ probe.style.position = "absolute";
4288
+ probe.style.left = "-9999px";
4289
+ probe.style.top = "-9999px";
4290
+ probe.style.visibility = "hidden";
4291
+ document.documentElement.appendChild(probe);
4292
+ const computed = getComputedStyle(probe).color;
4293
+ probe.remove();
4294
+ const match = computed.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/i);
4295
+ if (match) {
4296
+ const r = Number(match[1]);
4297
+ const g = Number(match[2]);
4298
+ const b = Number(match[3]);
4299
+ if (Number.isFinite(r) && Number.isFinite(g) && Number.isFinite(b)) {
4300
+ return formatHSL(rgbToHsl({ r, g, b }), false);
4301
+ }
4302
+ }
4303
+ } catch {
4304
+ }
4305
+ }
4306
+ return trimmed;
4259
4307
  }
4260
4308
  function ThemeProvider({
4261
4309
  children,
4262
4310
  defaultMode = "system",
4263
4311
  defaultPreset,
4264
- enableTransitions = true,
4265
4312
  modeStorageKey = THEME_STORAGE_KEY,
4266
4313
  presetStorageKey = "theme-preset",
4267
- enablePresets = true,
4268
- customPresets,
4269
- includeBuiltInPresets = true,
4270
- disableScript = false,
4271
- deferRenderUntilReady
4314
+ customPresets
4272
4315
  }) {
4273
- const shouldInjectScript = !disableScript;
4274
- const shouldDeferRenderUntilReady = deferRenderUntilReady ?? disableScript;
4275
4316
  const normalizedCustomPresets = (0, import_react2.useMemo)(() => customPresets ?? {}, [customPresets]);
4276
4317
  const [mode, setMode] = (0, import_react2.useState)(() => {
4277
4318
  const stored = getStoredMode(modeStorageKey);
@@ -4285,9 +4326,7 @@ function ThemeProvider({
4285
4326
  });
4286
4327
  const availablePresets = (0, import_react2.useMemo)(() => {
4287
4328
  const merged = {};
4288
- if (includeBuiltInPresets) {
4289
- Object.assign(merged, tweakcnPresets);
4290
- }
4329
+ Object.assign(merged, tweakcnPresets);
4291
4330
  if (Object.keys(normalizedCustomPresets).length > 0) {
4292
4331
  const validationResult = validateCustomPresets(normalizedCustomPresets);
4293
4332
  const isDevelopment = typeof window !== "undefined" && window.location?.hostname === "localhost";
@@ -4303,8 +4342,8 @@ function ThemeProvider({
4303
4342
  }
4304
4343
  }
4305
4344
  return merged;
4306
- }, [includeBuiltInPresets, normalizedCustomPresets]);
4307
- const builtInPresets = (0, import_react2.useMemo)(() => includeBuiltInPresets ? tweakcnPresets : {}, [includeBuiltInPresets]);
4345
+ }, [normalizedCustomPresets]);
4346
+ const builtInPresets = tweakcnPresets;
4308
4347
  const getAvailablePresetById = (0, import_react2.useCallback)(
4309
4348
  (id) => {
4310
4349
  return availablePresets[id] || null;
@@ -4312,12 +4351,8 @@ function ThemeProvider({
4312
4351
  [availablePresets]
4313
4352
  );
4314
4353
  const [currentPreset, setCurrentPreset] = (0, import_react2.useState)(null);
4315
- const [isReady, setIsReady] = (0, import_react2.useState)(() => !shouldDeferRenderUntilReady);
4316
4354
  (0, import_react2.useEffect)(() => {
4317
- if (!enablePresets || typeof window === "undefined") {
4318
- setIsReady(true);
4319
- return;
4320
- }
4355
+ if (typeof window === "undefined") return;
4321
4356
  try {
4322
4357
  const stored = localStorage.getItem(presetStorageKey);
4323
4358
  const isDevelopment = typeof window !== "undefined" && window.location?.hostname === "localhost";
@@ -4370,10 +4405,8 @@ function ThemeProvider({
4370
4405
  if (isDevelopment) {
4371
4406
  console.warn("\u{1F3A8} UnifiedTheme: Failed to load preset from storage:", error);
4372
4407
  }
4373
- } finally {
4374
- setIsReady(true);
4375
4408
  }
4376
- }, [presetStorageKey, enablePresets, defaultPreset, getAvailablePresetById]);
4409
+ }, [presetStorageKey, defaultPreset, getAvailablePresetById]);
4377
4410
  (0, import_react2.useEffect)(() => {
4378
4411
  if (mode === "system") {
4379
4412
  const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
@@ -4452,7 +4485,7 @@ function ThemeProvider({
4452
4485
  (coordinates) => {
4453
4486
  const newMode = resolvedMode === "light" ? "dark" : "light";
4454
4487
  const prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
4455
- if (enableTransitions && !prefersReducedMotion && typeof document !== "undefined" && "startViewTransition" in document) {
4488
+ if (!prefersReducedMotion && typeof document !== "undefined" && "startViewTransition" in document) {
4456
4489
  const root = document.documentElement;
4457
4490
  if (coordinates) {
4458
4491
  root.style.setProperty("--x", `${coordinates.x}px`);
@@ -4465,7 +4498,7 @@ function ThemeProvider({
4465
4498
  handleModeChange(newMode);
4466
4499
  }
4467
4500
  },
4468
- [resolvedMode, enableTransitions, handleModeChange]
4501
+ [resolvedMode, handleModeChange]
4469
4502
  );
4470
4503
  (0, import_react2.useEffect)(() => {
4471
4504
  if (!currentPreset || typeof window === "undefined") return;
@@ -4480,7 +4513,7 @@ function ThemeProvider({
4480
4513
  appliedAt: Date.now()
4481
4514
  };
4482
4515
  setCurrentPreset(presetData);
4483
- if (enablePresets && typeof window !== "undefined") {
4516
+ if (typeof window !== "undefined") {
4484
4517
  try {
4485
4518
  localStorage.setItem(presetStorageKey, JSON.stringify(presetData));
4486
4519
  } catch (error) {
@@ -4491,7 +4524,7 @@ function ThemeProvider({
4491
4524
  applyPresetColors(preset.colors, resolvedMode);
4492
4525
  }
4493
4526
  },
4494
- [presetStorageKey, enablePresets, applyPresetColors, resolvedMode]
4527
+ [presetStorageKey, applyPresetColors, resolvedMode]
4495
4528
  );
4496
4529
  const setThemePresetById = (0, import_react2.useCallback)(
4497
4530
  (presetId) => {
@@ -4515,53 +4548,50 @@ function ThemeProvider({
4515
4548
  [getAvailablePresetById, applyPreset]
4516
4549
  );
4517
4550
  const clearPreset = (0, import_react2.useCallback)(() => {
4518
- if (enablePresets && typeof window !== "undefined") {
4551
+ if (typeof window !== "undefined") {
4519
4552
  try {
4520
4553
  localStorage.removeItem(presetStorageKey);
4521
4554
  } catch (error) {
4522
4555
  console.error("\u{1F3A8} UnifiedTheme: Failed to clear preset:", error);
4523
4556
  }
4524
- if (defaultPreset) {
4525
- const preset = getAvailablePresetById(defaultPreset);
4526
- if (preset) {
4527
- const presetData = {
4528
- presetId: defaultPreset,
4529
- presetName: preset.label,
4530
- colors: {
4531
- light: preset.styles.light,
4532
- dark: preset.styles.dark
4533
- },
4534
- appliedAt: Date.now()
4535
- };
4536
- setCurrentPreset(presetData);
4537
- applyPresetColors(presetData.colors, resolvedMode);
4538
- } else {
4539
- console.warn("\u{1F3A8} UnifiedTheme: Default preset not found:", defaultPreset);
4540
- setCurrentPreset(null);
4541
- }
4557
+ }
4558
+ if (defaultPreset) {
4559
+ const preset = getAvailablePresetById(defaultPreset);
4560
+ if (preset) {
4561
+ const presetData = {
4562
+ presetId: defaultPreset,
4563
+ presetName: preset.label,
4564
+ colors: {
4565
+ light: preset.styles.light,
4566
+ dark: preset.styles.dark
4567
+ },
4568
+ appliedAt: Date.now()
4569
+ };
4570
+ setCurrentPreset(presetData);
4571
+ applyPresetColors(presetData.colors, resolvedMode);
4542
4572
  } else {
4573
+ console.warn("\u{1F3A8} UnifiedTheme: Default preset not found:", defaultPreset);
4543
4574
  setCurrentPreset(null);
4544
- const root = document.documentElement;
4545
- const allProperties = [
4546
- ...CSS_PROPERTY_CATEGORIES.colors,
4547
- ...CSS_PROPERTY_CATEGORIES.typography,
4548
- ...CSS_PROPERTY_CATEGORIES.layout,
4549
- ...CSS_PROPERTY_CATEGORIES.shadows,
4550
- ...CSS_PROPERTY_CATEGORIES.spacing
4551
- ];
4552
- let clearedCount = 0;
4553
- allProperties.forEach((prop) => {
4554
- const cssVar = `--${prop}`;
4555
- root.style.removeProperty(cssVar);
4556
- clearedCount++;
4557
- });
4558
4575
  }
4576
+ } else {
4577
+ setCurrentPreset(null);
4578
+ const root = document.documentElement;
4579
+ const allProperties = [
4580
+ ...CSS_PROPERTY_CATEGORIES.colors,
4581
+ ...CSS_PROPERTY_CATEGORIES.typography,
4582
+ ...CSS_PROPERTY_CATEGORIES.layout,
4583
+ ...CSS_PROPERTY_CATEGORIES.shadows,
4584
+ ...CSS_PROPERTY_CATEGORIES.spacing
4585
+ ];
4586
+ let clearedCount = 0;
4587
+ allProperties.forEach((prop) => {
4588
+ const cssVar = `--${prop}`;
4589
+ root.style.removeProperty(cssVar);
4590
+ clearedCount++;
4591
+ });
4559
4592
  }
4560
- }, [presetStorageKey, enablePresets, defaultPreset, applyPresetColors, resolvedMode]);
4561
- const scriptElement = shouldInjectScript ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ThemeScript, { presetStorageKey, defaultPreset }) : null;
4562
- if (!isReady && shouldDeferRenderUntilReady) {
4563
- return scriptElement;
4564
- }
4593
+ }, [presetStorageKey, defaultPreset, getAvailablePresetById, applyPresetColors, resolvedMode]);
4594
+ const scriptElement = /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ThemeScript, { presetStorageKey, defaultPreset });
4565
4595
  const isUsingDefaultPreset = !!defaultPreset && currentPreset?.presetId === defaultPreset;
4566
4596
  const contextValue = {
4567
4597
  mode,
@@ -4817,7 +4847,7 @@ var AnimatedRow = ({
4817
4847
  }) => {
4818
4848
  if (presets.length === 0) return null;
4819
4849
  const duplicatedPresets = Array(animation.duplicationFactor).fill(presets).flat();
4820
- const totalWidth = presets.reduce((sum, preset) => sum + (Number(preset.metadata?.buttonWidth) || layout.buttonWidth), 0) + presets.length * layout.buttonGap;
4850
+ const totalWidth = presets.reduce((sum, preset) => sum + (Number(preset.metadata?.buttonWidth) || layout.buttonWidth), 0) + Math.max(0, presets.length - 1) * layout.buttonGap;
4821
4851
  const effectiveScrollSpeed = Math.max(0.1, animation.scrollSpeed || 1);
4822
4852
  const animationDuration = presets.length * animation.duration / effectiveScrollSpeed;
4823
4853
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
@@ -5014,6 +5044,42 @@ function useTypedTheme(customPresets) {
5014
5044
  setThemePresetById
5015
5045
  };
5016
5046
  }
5047
+
5048
+ // src/hooks/useThemeEngine.ts
5049
+ function useThemeEngine() {
5050
+ const theme = useTheme();
5051
+ const darkMode = theme.resolvedMode === "dark";
5052
+ const setDarkMode = (mode) => theme.setMode(mode);
5053
+ const toggleDarkMode = (coordinates) => theme.toggleMode(coordinates);
5054
+ const applyThemeById = (themeId) => theme.setThemePresetById(themeId);
5055
+ const applyPresetById = applyThemeById;
5056
+ const clearTheme = () => theme.clearPreset();
5057
+ const clearPreset = clearTheme;
5058
+ const currentTheme = theme.currentPreset;
5059
+ const currentPreset = theme.currentPreset;
5060
+ return {
5061
+ // Mode
5062
+ darkMode,
5063
+ mode: theme.mode,
5064
+ resolvedMode: theme.resolvedMode,
5065
+ setDarkMode,
5066
+ setMode: theme.setMode,
5067
+ toggleDarkMode,
5068
+ toggleMode: theme.toggleMode,
5069
+ // Presets (theme naming)
5070
+ applyThemeById,
5071
+ applyPresetById,
5072
+ clearTheme,
5073
+ clearPreset,
5074
+ currentTheme,
5075
+ currentPreset,
5076
+ // Advanced / diagnostics
5077
+ isUsingDefaultPreset: theme.isUsingDefaultPreset,
5078
+ availablePresets: theme.availablePresets,
5079
+ builtInPresets: theme.builtInPresets,
5080
+ customPresets: theme.customPresets
5081
+ };
5082
+ }
5017
5083
  // Annotate the CommonJS export names for ESM import in node:
5018
5084
  0 && (module.exports = {
5019
5085
  ThemePresetButtons,
@@ -5031,6 +5097,7 @@ function useTypedTheme(customPresets) {
5031
5097
  searchPresets,
5032
5098
  tweakcnPresets,
5033
5099
  useTheme,
5100
+ useThemeEngine,
5034
5101
  useTypedTheme,
5035
5102
  validateCustomPresets,
5036
5103
  validateTweakCNPreset,