@kbach/react-native 1.0.0-beta.24 → 1.0.0-beta.26

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.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  resolveClassName
3
- } from "./chunk-AG7ZQ2K4.mjs";
3
+ } from "./chunk-BEXNFWW6.mjs";
4
4
 
5
5
  // src/jsxRuntimeCoreWeb.ts
6
6
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getTheme
3
- } from "./chunk-AG7ZQ2K4.mjs";
3
+ } from "./chunk-BEXNFWW6.mjs";
4
4
 
5
5
  // src/darkModeStore.ts
6
6
  import { Appearance } from "react-native";
@@ -35,6 +35,9 @@ var subscription = Appearance.addChangeListener(({ colorScheme }) => {
35
35
  function notify() {
36
36
  for (const listener of listeners) listener();
37
37
  }
38
+ function resyncDomWithActiveTheme() {
39
+ applyToDom(isDark);
40
+ }
38
41
  function getGlobalDarkMode() {
39
42
  return isDark;
40
43
  }
@@ -94,6 +97,7 @@ function subscribeDynamicTokens(listener) {
94
97
  }
95
98
 
96
99
  export {
100
+ resyncDomWithActiveTheme,
97
101
  getGlobalDarkMode,
98
102
  getGlobalThemeMode,
99
103
  setGlobalThemeMode,
@@ -34,6 +34,21 @@
34
34
  * browser DOM where it's needed.
35
35
  */
36
36
  type ThemeMode = 'light' | 'dark' | 'system';
37
+ /**
38
+ * Re-applies the CURRENT `isDark` value to the DOM (Expo Web only — a
39
+ * genuine no-op on real native, same as `applyToDom` itself) under whatever
40
+ * `getTheme().darkMode` strategy is active right now, with no change to
41
+ * `mode`/`isDark` themselves, so it deliberately does NOT `notify()`.
42
+ * Mirrors @kbach/react's identical `resyncDomWithActiveTheme` — see that
43
+ * one's own doc comment for the full race it fixes: this module's initial
44
+ * `applyToDom(isDark)` call (above) runs at IMPORT time against whichever
45
+ * `darkMode` strategy `defaultTheme` had at that moment, which on Expo Web
46
+ * is BEFORE an app's own `applyKbachConfig({darkMode: 'attribute'})` call
47
+ * (in config.ts) has had a chance to switch it — without this, that config
48
+ * call would silently never get its `dark:` classes to apply on initial
49
+ * load, even though `useTheme().isDark` already reads correctly.
50
+ */
51
+ declare function resyncDomWithActiveTheme(): void;
37
52
  declare function getGlobalDarkMode(): boolean;
38
53
  declare function getGlobalThemeMode(): ThemeMode;
39
54
  /** Explicit user choice — takes priority over any later `<ThemeProvider defaultMode>` seed (see `seedDefaultMode` below). Not persisted; see this module's own doc comment. */
@@ -55,4 +70,4 @@ declare function _resetForTests(): void;
55
70
  /** Exported for tests only — removes the Appearance change subscription so a test's mock doesn't leak into the next one. */
56
71
  declare function _unsubscribeForTests(): void;
57
72
 
58
- export { type ThemeMode, _resetForTests, _unsubscribeForTests, getGlobalDarkMode, getGlobalThemeMode, seedDefaultMode, setGlobalThemeMode, subscribeGlobalDarkMode, toggleGlobalDarkMode };
73
+ export { type ThemeMode, _resetForTests, _unsubscribeForTests, getGlobalDarkMode, getGlobalThemeMode, resyncDomWithActiveTheme, seedDefaultMode, setGlobalThemeMode, subscribeGlobalDarkMode, toggleGlobalDarkMode };
@@ -24,6 +24,7 @@ __export(darkModeStore_exports, {
24
24
  _unsubscribeForTests: () => _unsubscribeForTests,
25
25
  getGlobalDarkMode: () => getGlobalDarkMode,
26
26
  getGlobalThemeMode: () => getGlobalThemeMode,
27
+ resyncDomWithActiveTheme: () => resyncDomWithActiveTheme,
27
28
  seedDefaultMode: () => seedDefaultMode,
28
29
  setGlobalThemeMode: () => setGlobalThemeMode,
29
30
  subscribeGlobalDarkMode: () => subscribeGlobalDarkMode,
@@ -63,6 +64,9 @@ var subscription = import_react_native.Appearance.addChangeListener(({ colorSche
63
64
  function notify() {
64
65
  for (const listener of listeners) listener();
65
66
  }
67
+ function resyncDomWithActiveTheme() {
68
+ applyToDom(isDark);
69
+ }
66
70
  function getGlobalDarkMode() {
67
71
  return isDark;
68
72
  }
@@ -105,6 +109,7 @@ function _unsubscribeForTests() {
105
109
  _unsubscribeForTests,
106
110
  getGlobalDarkMode,
107
111
  getGlobalThemeMode,
112
+ resyncDomWithActiveTheme,
108
113
  seedDefaultMode,
109
114
  setGlobalThemeMode,
110
115
  subscribeGlobalDarkMode,
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { ColorEntry, ContainerConfig, ThemeConfig, defaultTheme, getTheme, setTheme } from './theme';
2
- export { C as ColorScale, a as ColorsAPI, K as KbachConfig, T as ThemeProvider, b as ThemeProviderProps, c as ThemeState, d as ThemeStorageAdapter, e as applyKbachConfig, r as resolveKbachConfig, u as useColors, f as useDynamicToken, g as useTheme } from './useDynamicToken-DAHQob1u.js';
2
+ export { C as ColorScale, a as ColorsAPI, K as KbachConfig, T as ThemeProvider, b as ThemeProviderProps, c as ThemeState, d as ThemeStorageAdapter, e as applyKbachConfig, r as resolveKbachConfig, u as useColors, f as useDynamicToken, g as useTheme } from './useDynamicToken-C8T9r6Cl.js';
3
3
  export { ThemeMode, getGlobalDarkMode, getGlobalThemeMode, setGlobalThemeMode, subscribeGlobalDarkMode, toggleGlobalDarkMode } from './darkModeStore';
4
4
  export { deleteDynamicToken, getDynamicToken, setDynamicToken, subscribeDynamicTokens } from './dynamicTokens';
5
5
  import 'react';
@@ -11,89 +11,6 @@ type StyleValue = string | number | {
11
11
  interface StyleObject {
12
12
  [key: string]: StyleValue;
13
13
  }
14
- /**
15
- * Calls the Rust engine via the KbachModule TurboModule (see
16
- * apps/native-sandbox/android/.../KbachModule.kt and
17
- * packages/core-engine/src/resolve_style.rs) and returns a plain style
18
- * object ready for RN's `style` prop. Synchronous — no async init step,
19
- * unlike the web/WASM path, since JSI-backed TurboModule methods are
20
- * available as soon as the module is loaded.
21
- *
22
- * Reads `getGlobalDarkMode()` (darkModeStore.ts) fresh on every call (no
23
- * caching) so `dark:` resolves correctly whenever this runs. That store —
24
- * not a direct `Appearance.getColorScheme()` read, which this function
25
- * used prior to `<ThemeProvider>` support — is what makes an explicit
26
- * `setGlobalThemeMode('dark')`/`toggle()` override actually affect
27
- * rendering; the store itself falls back to tracking `Appearance` live
28
- * whenever the mode is `'system'` (its own default), so nothing regresses
29
- * for apps that never touch the override API at all. This function itself
30
- * has no subscription of its own, so nothing re-renders just because the
31
- * store's value changed — call `useTheme()` (its `isDark`) somewhere in
32
- * the tree for that, NOT React Native's own `useColorScheme()`, which only
33
- * reflects the raw OS setting and would miss an explicit override entirely.
34
- *
35
- * Phase 11 originally shipped with live in-place theme switching unverified
36
- * (a `uiMode` toggle triggered a re-render but `Appearance.getColorScheme()`
37
- * kept returning a stale value). Root-caused in Phase 12 while debugging an
38
- * unrelated `active:` failure: `apps/native-sandbox` has its own separate
39
- * `node_modules/react-native` (excluded from the npm workspace since Phase
40
- * 5), while `packages/react-native` resolved `react-native` hierarchically
41
- * up to a *different* copy at the repo root — two physical module
42
- * instances, so a `useColorScheme()` subscription and an
43
- * `Appearance.getColorScheme()` read were silently talking to two separate
44
- * copies of the Appearance module. Fixed by forcing a single instance via
45
- * `resolver.resolveRequest` in apps/native-sandbox/metro.config.js; live
46
- * theme switching (and `active:`, the bug that surfaced this) both verified
47
- * working after the fix — see Phase 12. Still relevant here: darkModeStore.ts
48
- * itself calls `Appearance.addChangeListener`, so the same dual-instance
49
- * failure mode would silently break the 'system' fallback path too if that
50
- * metro.config.js fix ever regressed.
51
- *
52
- * `pressed` gates the `active:` modifier — unlike `colorScheme`, there's no
53
- * system-level getter for "is this element currently pressed"; only RN's
54
- * `Pressable` ever knows this, so it's passed in directly by the caller
55
- * (jsx-runtime, from Pressable's own style-function callback) rather than
56
- * queried here. Defaults to `false` for every other element type.
57
- *
58
- * `width` gates `sm:`/`md:`/`lg:`/`xl:`/`2xl:` against the active theme's
59
- * `screens` scale (resolve_style.rs's `native_modifier_state`) — read fresh
60
- * from `Dimensions.get('window').width` on every call, the same "no
61
- * caching, no store" choice `colorScheme`/`pressed` already make, and for
62
- * the same reason: unlike dark mode there's no explicit-override concept to
63
- * preserve here (nobody manually overrides their screen width), so a plain
64
- * synchronous read is enough — no need for the subscribable store
65
- * darkModeStore.ts is for. As with `colorScheme`, this function has no
66
- * subscription of its own, so rotating/resizing the window doesn't
67
- * re-render anything by itself — call RN's own `useWindowDimensions()`
68
- * somewhere in the tree for that (same role `useColorScheme()` plays for
69
- * `dark:` in apps/native-sandbox/App.tsx).
70
- *
71
- * Falls back to `./jsEngine` (a pure-JS, no-WASM/no-native reimplementation
72
- * of this same resolution logic — see that directory's own doc comments)
73
- * when `KbachModule` isn't registered at all — Expo Go, which can never
74
- * load ANY custom native module (not even Expo's own Modules API), so the
75
- * TurboModule this file otherwise depends on simply doesn't exist there.
76
- * `TurboModuleRegistry.get` (not `.getEnforcing`, which throws) is what
77
- * makes this detectable at all — `get` returns `null`/`undefined` instead.
78
- * A real dev-client/CLI build always has the module registered, so this
79
- * fallback only ever activates in Expo Go itself; everywhere else behaves
80
- * exactly as before. The JS engine covers a strict subset of what the
81
- * native/WASM engines resolve (see jsEngine/resolveUtilityNative.ts) — an
82
- * intentional, documented parity gap, not a bug.
83
- *
84
- * An arbitrary value that couldn't be resolved to something RN's style
85
- * system can actually use (an unreduced percentage/viewport-relative
86
- * `calc()`, a raw `var(...)`, ...) is dropped from the returned style —
87
- * never passed through as an invalid string — and surfaced via
88
- * `console.warn` here (dev builds only, mirroring RN's own convention for
89
- * this kind of non-fatal correctness warning) rather than failing
90
- * silently. The native/JNI path embeds these under a `"__kbachWarnings"`
91
- * key in the same JSON `resolve_style_json` already returns (see that
92
- * function's own doc comment for why — reusing the one JSON round-trip
93
- * rather than adding a second FFI call); the jsEngine fallback returns
94
- * them directly via `resolveStyleJsWithWarnings`. Both paths converge here
95
- * so call sites never need to know which engine actually resolved anything.
96
- */
97
14
  declare function resolveStyle(classString: string, pressed?: boolean): StyleObject;
98
15
 
99
16
  export { type StyleObject, resolveStyle };
package/dist/index.js CHANGED
@@ -25,17 +25,17 @@ __export(src_exports, {
25
25
  defaultTheme: () => import_theme4.defaultTheme,
26
26
  deleteDynamicToken: () => import_dynamicTokens2.deleteDynamicToken,
27
27
  getDynamicToken: () => import_dynamicTokens2.getDynamicToken,
28
- getGlobalDarkMode: () => import_darkModeStore4.getGlobalDarkMode,
29
- getGlobalThemeMode: () => import_darkModeStore4.getGlobalThemeMode,
28
+ getGlobalDarkMode: () => import_darkModeStore5.getGlobalDarkMode,
29
+ getGlobalThemeMode: () => import_darkModeStore5.getGlobalThemeMode,
30
30
  getTheme: () => import_theme4.getTheme,
31
31
  resolveKbachConfig: () => resolveKbachConfig,
32
32
  resolveStyle: () => resolveStyle,
33
33
  setDynamicToken: () => import_dynamicTokens2.setDynamicToken,
34
- setGlobalThemeMode: () => import_darkModeStore4.setGlobalThemeMode,
34
+ setGlobalThemeMode: () => import_darkModeStore5.setGlobalThemeMode,
35
35
  setTheme: () => import_theme4.setTheme,
36
36
  subscribeDynamicTokens: () => import_dynamicTokens2.subscribeDynamicTokens,
37
- subscribeGlobalDarkMode: () => import_darkModeStore4.subscribeGlobalDarkMode,
38
- toggleGlobalDarkMode: () => import_darkModeStore4.toggleGlobalDarkMode,
37
+ subscribeGlobalDarkMode: () => import_darkModeStore5.subscribeGlobalDarkMode,
38
+ toggleGlobalDarkMode: () => import_darkModeStore5.toggleGlobalDarkMode,
39
39
  useColors: () => useColors,
40
40
  useDynamicToken: () => useDynamicToken,
41
41
  useTheme: () => useTheme
@@ -341,6 +341,27 @@ function resolveNegatableLength(theme, parsed) {
341
341
  if (px === 0) return "0px";
342
342
  return `-${px}px`;
343
343
  }
344
+ function fractionPercent(value) {
345
+ const slashIdx = value.indexOf("/");
346
+ if (slashIdx === -1) return null;
347
+ const numStr = value.slice(0, slashIdx);
348
+ const denStr = value.slice(slashIdx + 1);
349
+ const num = Number(numStr);
350
+ const den = Number(denStr);
351
+ if (!Number.isFinite(num) || !Number.isFinite(den) || numStr.trim() === "" || denStr.trim() === "") return null;
352
+ if (den === 0) return null;
353
+ const pct = num / den * 100;
354
+ const formatted = pct.toFixed(6);
355
+ const trimmed = formatted.replace(/0+$/, "").replace(/\.$/, "");
356
+ return `${trimmed}%`;
357
+ }
358
+ function resolveNegatableSize(theme, parsed) {
359
+ if (!parsed.isArbitrary && parsed.value !== null) {
360
+ const pct = fractionPercent(parsed.value);
361
+ if (pct !== null) return parsed.negative ? `-${pct}` : pct;
362
+ }
363
+ return resolveNegatableLength(theme, parsed);
364
+ }
344
365
  function resolvePercent(parsed) {
345
366
  if (parsed.negative) return null;
346
367
  const value = parsed.value;
@@ -494,43 +515,44 @@ function resolve(parsed, theme) {
494
515
  case "shrink":
495
516
  return [decl("flex-shrink", parsed.value ?? "1")];
496
517
  // top/right/bottom/left/inset support real Tailwind's negative-value
497
- // convention ("-top-4") — see resolveNegatableLength's own doc comment.
518
+ // convention ("-top-4") AND its percentage-fraction scale ("top-1/2",
519
+ // "-inset-1/3") — see resolveNegatableSize's own doc comment.
498
520
  case "top": {
499
- const v = resolveNegatableLength(theme, parsed);
521
+ const v = resolveNegatableSize(theme, parsed);
500
522
  return v === null ? null : [decl("top", v)];
501
523
  }
502
524
  case "right": {
503
- const v = resolveNegatableLength(theme, parsed);
525
+ const v = resolveNegatableSize(theme, parsed);
504
526
  return v === null ? null : [decl("right", v)];
505
527
  }
506
528
  case "bottom": {
507
- const v = resolveNegatableLength(theme, parsed);
529
+ const v = resolveNegatableSize(theme, parsed);
508
530
  return v === null ? null : [decl("bottom", v)];
509
531
  }
510
532
  case "left": {
511
- const v = resolveNegatableLength(theme, parsed);
533
+ const v = resolveNegatableSize(theme, parsed);
512
534
  return v === null ? null : [decl("left", v)];
513
535
  }
514
536
  case "inset": {
515
- const v = resolveNegatableLength(theme, parsed);
537
+ const v = resolveNegatableSize(theme, parsed);
516
538
  return v === null ? null : [decl("inset", v)];
517
539
  }
518
540
  case "inset-x": {
519
- const v = resolveNegatableLength(theme, parsed);
541
+ const v = resolveNegatableSize(theme, parsed);
520
542
  return v === null ? null : [decl("left", v), decl("right", v)];
521
543
  }
522
544
  case "inset-y": {
523
- const v = resolveNegatableLength(theme, parsed);
545
+ const v = resolveNegatableSize(theme, parsed);
524
546
  return v === null ? null : [decl("top", v), decl("bottom", v)];
525
547
  }
526
548
  // Logical inset — just a different CSS property name, same reasoning
527
549
  // as Rust's layout.rs (no direction-tracking logic needed here).
528
550
  case "start": {
529
- const v = resolveNegatableLength(theme, parsed);
551
+ const v = resolveNegatableSize(theme, parsed);
530
552
  return v === null ? null : [decl("inset-inline-start", v)];
531
553
  }
532
554
  case "end": {
533
- const v = resolveNegatableLength(theme, parsed);
555
+ const v = resolveNegatableSize(theme, parsed);
534
556
  return v === null ? null : [decl("inset-inline-end", v)];
535
557
  }
536
558
  // "aspect-" is a registered VALUE_PREFIXES entry (parser.ts), so
@@ -628,20 +650,6 @@ function namedSize(key) {
628
650
  return null;
629
651
  }
630
652
  }
631
- function fractionPercent(value) {
632
- const slashIdx = value.indexOf("/");
633
- if (slashIdx === -1) return null;
634
- const numStr = value.slice(0, slashIdx);
635
- const denStr = value.slice(slashIdx + 1);
636
- const num = Number(numStr);
637
- const den = Number(denStr);
638
- if (!Number.isFinite(num) || !Number.isFinite(den) || numStr.trim() === "" || denStr.trim() === "") return null;
639
- if (den === 0) return null;
640
- const pct = num / den * 100;
641
- const formatted = pct.toFixed(6);
642
- const trimmed = formatted.replace(/0+$/, "").replace(/\.$/, "");
643
- return `${trimmed}%`;
644
- }
645
653
  function cssSizeKeyword(key) {
646
654
  switch (key) {
647
655
  case "min":
@@ -1689,16 +1697,33 @@ function resolveStyleJsWithWarnings(classString, theme, colorScheme, pressed, wi
1689
1697
  }
1690
1698
 
1691
1699
  // src/nativeBridge.ts
1700
+ var RESOLVE_STYLE_CACHE_MAX = 500;
1701
+ var resolveStyleCache = /* @__PURE__ */ new Map();
1702
+ var cachedForThemeJson = null;
1692
1703
  function resolveStyle(classString, pressed = false) {
1693
1704
  const colorScheme = (0, import_darkModeStore.getGlobalDarkMode)() ? "dark" : "light";
1694
1705
  const width = import_react_native.Dimensions.get("window").width;
1706
+ const themeJson = (0, import_theme.getThemeJson)();
1707
+ if (themeJson !== cachedForThemeJson) {
1708
+ resolveStyleCache.clear();
1709
+ cachedForThemeJson = themeJson;
1710
+ }
1711
+ const cacheKey = `${classString}\0${pressed}\0${colorScheme}\0${width}`;
1712
+ const cached = resolveStyleCache.get(cacheKey);
1713
+ if (cached !== void 0) return { ...cached };
1714
+ const style = resolveStyleUncached(classString, pressed, colorScheme, width, themeJson);
1715
+ if (resolveStyleCache.size >= RESOLVE_STYLE_CACHE_MAX) resolveStyleCache.clear();
1716
+ resolveStyleCache.set(cacheKey, style);
1717
+ return { ...style };
1718
+ }
1719
+ function resolveStyleUncached(classString, pressed, colorScheme, width, themeJson) {
1695
1720
  const KbachModule = import_react_native.TurboModuleRegistry.get("KbachModule");
1696
1721
  if (!KbachModule) {
1697
1722
  const { style: style2, warnings: warnings2 } = resolveStyleJsWithWarnings(classString, (0, import_theme.getTheme)(), colorScheme, pressed, width);
1698
1723
  warnIfDev(classString, warnings2);
1699
1724
  return style2;
1700
1725
  }
1701
- const json = KbachModule.resolveStyle(classString, (0, import_theme.getThemeJson)(), colorScheme, pressed, width);
1726
+ const json = KbachModule.resolveStyle(classString, themeJson, colorScheme, pressed, width);
1702
1727
  const style = JSON.parse(json);
1703
1728
  const warnings = style.__kbachWarnings;
1704
1729
  if (warnings !== void 0) {
@@ -1721,6 +1746,7 @@ function warnIfDev(classString, warnings) {
1721
1746
 
1722
1747
  // src/config.ts
1723
1748
  var import_theme2 = require("./theme");
1749
+ var import_darkModeStore2 = require("./darkModeStore");
1724
1750
  function parseHexRgb(hex) {
1725
1751
  const m = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(hex.trim());
1726
1752
  if (!m) return null;
@@ -1780,24 +1806,25 @@ function resolveKbachConfig(config) {
1780
1806
  function applyKbachConfig(config) {
1781
1807
  const theme = resolveKbachConfig(config);
1782
1808
  (0, import_theme2.setTheme)(theme);
1809
+ (0, import_darkModeStore2.resyncDomWithActiveTheme)();
1783
1810
  return theme;
1784
1811
  }
1785
1812
 
1786
1813
  // src/useTheme.ts
1787
1814
  var import_react = require("react");
1788
- var import_darkModeStore2 = require("./darkModeStore");
1815
+ var import_darkModeStore3 = require("./darkModeStore");
1789
1816
  function useTheme() {
1790
- const isDark = (0, import_react.useSyncExternalStore)(import_darkModeStore2.subscribeGlobalDarkMode, import_darkModeStore2.getGlobalDarkMode);
1791
- const mode = (0, import_react.useSyncExternalStore)(import_darkModeStore2.subscribeGlobalDarkMode, import_darkModeStore2.getGlobalThemeMode);
1817
+ const isDark = (0, import_react.useSyncExternalStore)(import_darkModeStore3.subscribeGlobalDarkMode, import_darkModeStore3.getGlobalDarkMode);
1818
+ const mode = (0, import_react.useSyncExternalStore)(import_darkModeStore3.subscribeGlobalDarkMode, import_darkModeStore3.getGlobalThemeMode);
1792
1819
  return (0, import_react.useMemo)(
1793
- () => ({ mode, isDark, setMode: import_darkModeStore2.setGlobalThemeMode, toggle: import_darkModeStore2.toggleGlobalDarkMode }),
1820
+ () => ({ mode, isDark, setMode: import_darkModeStore3.setGlobalThemeMode, toggle: import_darkModeStore3.toggleGlobalDarkMode }),
1794
1821
  [mode, isDark]
1795
1822
  );
1796
1823
  }
1797
1824
 
1798
1825
  // src/ThemeProvider.tsx
1799
1826
  var import_react2 = require("react");
1800
- var import_darkModeStore3 = require("./darkModeStore");
1827
+ var import_darkModeStore4 = require("./darkModeStore");
1801
1828
  var import_jsx_runtime = require("react/jsx-runtime");
1802
1829
  var PERSIST_STORAGE_KEY = "kbach-theme";
1803
1830
  function isThemeMode(value) {
@@ -1823,16 +1850,16 @@ function ThemeProvider({ children, defaultMode = "system", persist = false }) {
1823
1850
  if (seeded.current) return;
1824
1851
  seeded.current = true;
1825
1852
  if (!persist) {
1826
- (0, import_darkModeStore3.seedDefaultMode)(defaultMode);
1853
+ (0, import_darkModeStore4.seedDefaultMode)(defaultMode);
1827
1854
  return;
1828
1855
  }
1829
1856
  let cancelled = false;
1830
1857
  persist.getItem(PERSIST_STORAGE_KEY).then((stored) => {
1831
1858
  if (cancelled) return;
1832
- if (isThemeMode(stored)) (0, import_darkModeStore3.seedDefaultMode)(stored);
1833
- else (0, import_darkModeStore3.seedDefaultMode)(defaultMode);
1859
+ if (isThemeMode(stored)) (0, import_darkModeStore4.seedDefaultMode)(stored);
1860
+ else (0, import_darkModeStore4.seedDefaultMode)(defaultMode);
1834
1861
  }).catch(() => {
1835
- if (!cancelled) (0, import_darkModeStore3.seedDefaultMode)(defaultMode);
1862
+ if (!cancelled) (0, import_darkModeStore4.seedDefaultMode)(defaultMode);
1836
1863
  });
1837
1864
  return () => {
1838
1865
  cancelled = true;
@@ -1840,8 +1867,8 @@ function ThemeProvider({ children, defaultMode = "system", persist = false }) {
1840
1867
  }, []);
1841
1868
  (0, import_react2.useEffect)(() => {
1842
1869
  if (!persist) return;
1843
- return (0, import_darkModeStore3.subscribeGlobalDarkMode)(() => {
1844
- persist.setItem(PERSIST_STORAGE_KEY, (0, import_darkModeStore3.getGlobalThemeMode)()).catch(() => {
1870
+ return (0, import_darkModeStore4.subscribeGlobalDarkMode)(() => {
1871
+ persist.setItem(PERSIST_STORAGE_KEY, (0, import_darkModeStore4.getGlobalThemeMode)()).catch(() => {
1845
1872
  });
1846
1873
  });
1847
1874
  }, [persist]);
@@ -1849,9 +1876,10 @@ function ThemeProvider({ children, defaultMode = "system", persist = false }) {
1849
1876
  }
1850
1877
 
1851
1878
  // src/index.ts
1852
- var import_darkModeStore4 = require("./darkModeStore");
1879
+ var import_darkModeStore5 = require("./darkModeStore");
1853
1880
 
1854
1881
  // src/useColors.ts
1882
+ var import_react3 = require("react");
1855
1883
  var import_theme3 = require("./theme");
1856
1884
  function parseHexRgb2(hex) {
1857
1885
  const m = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(hex.trim());
@@ -1913,17 +1941,18 @@ function makeColorsProxy(theme, isDark) {
1913
1941
  }
1914
1942
  function useColors() {
1915
1943
  const { isDark } = useTheme();
1916
- return makeColorsProxy((0, import_theme3.getTheme)(), isDark);
1944
+ const theme = (0, import_theme3.getTheme)();
1945
+ return (0, import_react3.useMemo)(() => makeColorsProxy(theme, isDark), [theme, isDark]);
1917
1946
  }
1918
1947
 
1919
1948
  // src/index.ts
1920
1949
  var import_dynamicTokens2 = require("./dynamicTokens");
1921
1950
 
1922
1951
  // src/useDynamicToken.ts
1923
- var import_react3 = require("react");
1952
+ var import_react4 = require("react");
1924
1953
  var import_dynamicTokens = require("./dynamicTokens");
1925
1954
  function useDynamicToken(name) {
1926
- return (0, import_react3.useSyncExternalStore)(import_dynamicTokens.subscribeDynamicTokens, () => (0, import_dynamicTokens.getDynamicToken)(name));
1955
+ return (0, import_react4.useSyncExternalStore)(import_dynamicTokens.subscribeDynamicTokens, () => (0, import_dynamicTokens.getDynamicToken)(name));
1927
1956
  }
1928
1957
  // Annotate the CommonJS export names for ESM import in node:
1929
1958
  0 && (module.exports = {
@@ -1,5 +1,5 @@
1
1
  export { ColorEntry, ContainerConfig, ThemeConfig, defaultTheme, getTheme, setTheme } from './theme';
2
- export { C as ColorScale, a as ColorsAPI, K as KbachConfig, T as ThemeProvider, b as ThemeProviderProps, c as ThemeState, e as applyKbachConfig, r as resolveKbachConfig, u as useColors, f as useDynamicToken, g as useTheme } from './useDynamicToken-DAHQob1u.js';
2
+ export { C as ColorScale, a as ColorsAPI, K as KbachConfig, T as ThemeProvider, b as ThemeProviderProps, c as ThemeState, e as applyKbachConfig, r as resolveKbachConfig, u as useColors, f as useDynamicToken, g as useTheme } from './useDynamicToken-C8T9r6Cl.js';
3
3
  export { ThemeMode, getGlobalDarkMode, getGlobalThemeMode, setGlobalThemeMode, subscribeGlobalDarkMode, toggleGlobalDarkMode } from './darkModeStore';
4
4
  export { deleteDynamicToken, getDynamicToken, setDynamicToken, subscribeDynamicTokens } from './dynamicTokens';
5
5
  import 'react';