@kbach/react-native 1.0.0-beta.5 → 1.0.0-beta.51
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/AGENTS.md +361 -0
- package/README.md +150 -137
- package/android/src/main/jniLibs/arm64-v8a/libkbach_core_engine.so +0 -0
- package/android/src/main/jniLibs/x86_64/libkbach_core_engine.so +0 -0
- package/dist/chunk-CD5WFFIQ.mjs +40 -0
- package/dist/chunk-IJAXJVB4.mjs +566 -0
- package/dist/chunk-KHQ2BWYH.mjs +112 -0
- package/dist/clsx-BLLSnqAa.d.ts +277 -0
- package/dist/darkModeStore.d.ts +73 -0
- package/dist/darkModeStore.js +118 -0
- package/dist/dynamicTokens.d.ts +53 -0
- package/dist/dynamicTokens.js +75 -0
- package/dist/index.d.ts +92 -151
- package/dist/index.js +2119 -393
- package/dist/index.web.d.ts +101 -4
- package/dist/index.web.js +438 -401
- package/dist/index.web.mjs +268 -14
- package/dist/jsx-dev-runtime.js +2238 -332
- package/dist/jsx-dev-runtime.web.js +207 -361
- package/dist/jsx-dev-runtime.web.mjs +19 -7
- package/dist/jsx-runtime.js +2238 -332
- package/dist/jsx-runtime.web.js +191 -353
- package/dist/jsx-runtime.web.mjs +3 -6
- package/dist/theme.d.ts +79 -0
- package/dist/{chunk-AEVOM2NB.mjs → theme.js} +89 -62
- package/package.json +44 -14
- package/src/babel-plugin.js +144 -2
- package/dist/chunk-4KCCEMEN.mjs +0 -66
- package/dist/chunk-DLIXI5OW.mjs +0 -53
- package/dist/chunk-FG5E4NLJ.mjs +0 -18
- package/dist/chunk-KSDFO34H.mjs +0 -32
- package/dist/chunk-OJQAJLYT.mjs +0 -14
- package/dist/chunk-ZUE6SCTE.mjs +0 -14
- package/dist/index.d.mts +0 -161
- package/dist/index.mjs +0 -32
- package/dist/index.web.d.mts +0 -7
- package/dist/jsx-dev-runtime.d.mts +0 -10
- package/dist/jsx-dev-runtime.mjs +0 -17
- package/dist/jsx-dev-runtime.web.d.mts +0 -10
- package/dist/jsx-runtime.d.mts +0 -7
- package/dist/jsx-runtime.mjs +0 -14
- package/dist/jsx-runtime.web.d.mts +0 -7
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/dynamicTokens.ts
|
|
21
|
+
var dynamicTokens_exports = {};
|
|
22
|
+
__export(dynamicTokens_exports, {
|
|
23
|
+
_resetForTests: () => _resetForTests,
|
|
24
|
+
deleteDynamicToken: () => deleteDynamicToken,
|
|
25
|
+
getDynamicToken: () => getDynamicToken,
|
|
26
|
+
getDynamicTokensVersion: () => getDynamicTokensVersion,
|
|
27
|
+
setDynamicToken: () => setDynamicToken,
|
|
28
|
+
subscribeDynamicTokens: () => subscribeDynamicTokens
|
|
29
|
+
});
|
|
30
|
+
module.exports = __toCommonJS(dynamicTokens_exports);
|
|
31
|
+
var tokens = /* @__PURE__ */ new Map();
|
|
32
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
33
|
+
var version = 0;
|
|
34
|
+
function applyToDom(name, value) {
|
|
35
|
+
if (typeof document === "undefined") return;
|
|
36
|
+
document.documentElement.style.setProperty(`--${name}`, value);
|
|
37
|
+
}
|
|
38
|
+
function notify() {
|
|
39
|
+
version++;
|
|
40
|
+
for (const listener of listeners) listener();
|
|
41
|
+
}
|
|
42
|
+
function setDynamicToken(name, value) {
|
|
43
|
+
tokens.set(name, value);
|
|
44
|
+
applyToDom(name, value);
|
|
45
|
+
notify();
|
|
46
|
+
}
|
|
47
|
+
function getDynamicToken(name) {
|
|
48
|
+
return tokens.get(name);
|
|
49
|
+
}
|
|
50
|
+
function deleteDynamicToken(name) {
|
|
51
|
+
if (!tokens.delete(name)) return;
|
|
52
|
+
if (typeof document !== "undefined") document.documentElement.style.removeProperty(`--${name}`);
|
|
53
|
+
notify();
|
|
54
|
+
}
|
|
55
|
+
function subscribeDynamicTokens(listener) {
|
|
56
|
+
listeners.add(listener);
|
|
57
|
+
return () => listeners.delete(listener);
|
|
58
|
+
}
|
|
59
|
+
function getDynamicTokensVersion() {
|
|
60
|
+
return version;
|
|
61
|
+
}
|
|
62
|
+
function _resetForTests() {
|
|
63
|
+
tokens.clear();
|
|
64
|
+
listeners.clear();
|
|
65
|
+
version = 0;
|
|
66
|
+
}
|
|
67
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
68
|
+
0 && (module.exports = {
|
|
69
|
+
_resetForTests,
|
|
70
|
+
deleteDynamicToken,
|
|
71
|
+
getDynamicToken,
|
|
72
|
+
getDynamicTokensVersion,
|
|
73
|
+
setDynamicToken,
|
|
74
|
+
subscribeDynamicTokens
|
|
75
|
+
});
|
package/dist/index.d.ts
CHANGED
|
@@ -1,161 +1,102 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
colors: Record<string, string>;
|
|
7
|
-
spacing: Record<string, number>;
|
|
8
|
-
screens: Record<string, number>;
|
|
9
|
-
darkMode: 'attribute' | 'class' | 'media';
|
|
10
|
-
}
|
|
11
|
-
declare const defaultTheme: ThemeConfig;
|
|
12
|
-
declare function setTheme(theme: ThemeConfig): void;
|
|
13
|
-
declare function getTheme(): ThemeConfig;
|
|
1
|
+
export { ColorEntry, ContainerConfig, ThemeConfig, defaultTheme, getTheme, setTheme } from './theme';
|
|
2
|
+
export { C as ClassArray, a as ClassDictionary, b as ClassValue, c as ColorScale, d as ColorsAPI, K as KbachConfig, T as ThemeProvider, e as ThemeProviderProps, f as ThemeState, g as ThemeStorageAdapter, h as applyKbachConfig, i as clsx, r as resolveKbachConfig, u as useColors, j as useDynamicToken, k as useTheme } from './clsx-BLLSnqAa.js';
|
|
3
|
+
export { ThemeMode, getGlobalDarkMode, getGlobalThemeMode, setGlobalThemeMode, subscribeGlobalDarkMode, toggleGlobalDarkMode } from './darkModeStore';
|
|
4
|
+
export { deleteDynamicToken, getDynamicToken, setDynamicToken, subscribeDynamicTokens } from './dynamicTokens';
|
|
5
|
+
import 'react';
|
|
14
6
|
|
|
7
|
+
type StyleValue = string | number | {
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
} | Array<Record<string, string | number>>;
|
|
15
11
|
interface StyleObject {
|
|
16
|
-
[key: string]:
|
|
12
|
+
[key: string]: StyleValue;
|
|
17
13
|
}
|
|
18
|
-
/**
|
|
19
|
-
* Calls the Rust engine via the KbachModule TurboModule (see
|
|
20
|
-
* apps/native-sandbox/android/.../KbachModule.kt and
|
|
21
|
-
* packages/core-engine/src/resolve_style.rs) and returns a plain style
|
|
22
|
-
* object ready for RN's `style` prop. Synchronous — no async init step,
|
|
23
|
-
* unlike the web/WASM path, since JSI-backed TurboModule methods are
|
|
24
|
-
* available as soon as the module is loaded.
|
|
25
|
-
*
|
|
26
|
-
* Reads `getGlobalDarkMode()` (darkModeStore.ts) fresh on every call (no
|
|
27
|
-
* caching) so `dark:` resolves correctly whenever this runs. That store —
|
|
28
|
-
* not a direct `Appearance.getColorScheme()` read, which this function
|
|
29
|
-
* used prior to `<ThemeProvider>` support — is what makes an explicit
|
|
30
|
-
* `setGlobalThemeMode('dark')`/`toggle()` override actually affect
|
|
31
|
-
* rendering; the store itself falls back to tracking `Appearance` live
|
|
32
|
-
* whenever the mode is `'system'` (its own default), so nothing regresses
|
|
33
|
-
* for apps that never touch the override API at all. This function itself
|
|
34
|
-
* has no subscription of its own, so nothing re-renders just because the
|
|
35
|
-
* store's value changed — call `useGlobalDarkMode()` (or consume
|
|
36
|
-
* `useTheme()`'s `isDark`) somewhere in the tree for that, NOT React
|
|
37
|
-
* Native's own `useColorScheme()`, which only reflects the raw OS setting
|
|
38
|
-
* and would miss an explicit override entirely.
|
|
39
|
-
*
|
|
40
|
-
* Phase 11 originally shipped with live in-place theme switching unverified
|
|
41
|
-
* (a `uiMode` toggle triggered a re-render but `Appearance.getColorScheme()`
|
|
42
|
-
* kept returning a stale value). Root-caused in Phase 12 while debugging an
|
|
43
|
-
* unrelated `active:` failure: `apps/native-sandbox` has its own separate
|
|
44
|
-
* `node_modules/react-native` (excluded from the npm workspace since Phase
|
|
45
|
-
* 5), while `packages/react-native` resolved `react-native` hierarchically
|
|
46
|
-
* up to a *different* copy at the repo root — two physical module
|
|
47
|
-
* instances, so a `useColorScheme()` subscription and an
|
|
48
|
-
* `Appearance.getColorScheme()` read were silently talking to two separate
|
|
49
|
-
* copies of the Appearance module. Fixed by forcing a single instance via
|
|
50
|
-
* `resolver.resolveRequest` in apps/native-sandbox/metro.config.js; live
|
|
51
|
-
* theme switching (and `active:`, the bug that surfaced this) both verified
|
|
52
|
-
* working after the fix — see Phase 12. Still relevant here: darkModeStore.ts
|
|
53
|
-
* itself calls `Appearance.addChangeListener`, so the same dual-instance
|
|
54
|
-
* failure mode would silently break the 'system' fallback path too if that
|
|
55
|
-
* metro.config.js fix ever regressed.
|
|
56
|
-
*
|
|
57
|
-
* `pressed` gates the `active:` modifier — unlike `colorScheme`, there's no
|
|
58
|
-
* system-level getter for "is this element currently pressed"; only RN's
|
|
59
|
-
* `Pressable` ever knows this, so it's passed in directly by the caller
|
|
60
|
-
* (jsx-runtime, from Pressable's own style-function callback) rather than
|
|
61
|
-
* queried here. Defaults to `false` for every other element type.
|
|
62
|
-
*
|
|
63
|
-
* `width` gates `sm:`/`md:`/`lg:`/`xl:`/`2xl:` against the active theme's
|
|
64
|
-
* `screens` scale (resolve_style.rs's `native_modifier_state`) — read fresh
|
|
65
|
-
* from `Dimensions.get('window').width` on every call, the same "no
|
|
66
|
-
* caching, no store" choice `colorScheme`/`pressed` already make, and for
|
|
67
|
-
* the same reason: unlike dark mode there's no explicit-override concept to
|
|
68
|
-
* preserve here (nobody manually overrides their screen width), so a plain
|
|
69
|
-
* synchronous read is enough — no need for the subscribable store
|
|
70
|
-
* darkModeStore.ts is for. As with `colorScheme`, this function has no
|
|
71
|
-
* subscription of its own, so rotating/resizing the window doesn't
|
|
72
|
-
* re-render anything by itself — call RN's own `useWindowDimensions()`
|
|
73
|
-
* somewhere in the tree for that (same role `useColorScheme()` plays for
|
|
74
|
-
* `dark:` in apps/native-sandbox/App.tsx).
|
|
75
|
-
*/
|
|
76
14
|
declare function resolveStyle(classString: string, pressed?: boolean): StyleObject;
|
|
77
15
|
|
|
78
|
-
|
|
79
|
-
* Native port of @kbach/react's darkModeStore.ts — same architecture (a
|
|
80
|
-
* single module-level store is the real source of truth;
|
|
81
|
-
* ThemeProvider/useTheme are a thin, OPTIONAL wrapper around it; every
|
|
82
|
-
* function here works standalone with no provider mounted anywhere), with
|
|
83
|
-
* two real platform differences from the web version:
|
|
84
|
-
*
|
|
85
|
-
* 1. No persistence. There's no React-Native-builtin equivalent of
|
|
86
|
-
* localStorage — adding one would mean forcing a real native dependency
|
|
87
|
-
* (e.g. @react-native-async-storage/async-storage) on every consumer,
|
|
88
|
-
* even ones that don't want it. The mode always starts at 'system' on a
|
|
89
|
-
* fresh app launch. If you want persistence, read your own storage at
|
|
90
|
-
* startup (before your first render) and call setGlobalThemeMode() with
|
|
91
|
-
* the result — same "seed once, no built-in write-back" shape as
|
|
92
|
-
* seedDefaultMode() below, just driven by your own storage instead of
|
|
93
|
-
* one this package picks for you.
|
|
94
|
-
* 2. No DOM to "apply" the resolved mode to. On web, applyToDom() flips a
|
|
95
|
-
* class/attribute the generated CSS's `dark:` selectors match against.
|
|
96
|
-
* On native there's no CSS at all — nativeBridge.ts's resolveStyle()
|
|
97
|
-
* reads getGlobalDarkMode() directly on every call instead of querying
|
|
98
|
-
* Appearance.getColorScheme() itself, which is what actually makes an
|
|
99
|
-
* explicit override affect rendering (previously that function always
|
|
100
|
-
* read the raw OS setting, so it had no way to reflect a user choice —
|
|
101
|
-
* see nativeBridge.ts's own comment on this for why that's the one
|
|
102
|
-
* genuinely fixed bug this change introduces, not just a new feature).
|
|
103
|
-
*/
|
|
104
|
-
type ThemeMode = 'light' | 'dark' | 'system';
|
|
105
|
-
declare function getGlobalDarkMode(): boolean;
|
|
106
|
-
declare function getGlobalThemeMode(): ThemeMode;
|
|
107
|
-
/** Explicit user choice — takes priority over any later `<ThemeProvider defaultMode>` seed (see `seedDefaultMode` below). Not persisted; see this module's own doc comment. */
|
|
108
|
-
declare function setGlobalThemeMode(nextMode: ThemeMode): void;
|
|
109
|
-
declare function toggleGlobalDarkMode(): void;
|
|
110
|
-
declare function subscribeGlobalDarkMode(listener: () => void): () => void;
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Reactive read of the global dark-mode store — works with or without a
|
|
114
|
-
* `<ThemeProvider>` mounted anywhere in the tree, since it subscribes to
|
|
115
|
-
* `darkModeStore.ts` directly. No server-snapshot argument (unlike
|
|
116
|
-
* @kbach/react's web version) — React Native has no SSR concept at all,
|
|
117
|
-
* so there's no third argument for `useSyncExternalStore` to need.
|
|
118
|
-
*/
|
|
119
|
-
declare function useGlobalDarkMode(): boolean;
|
|
16
|
+
export { type StyleObject, resolveStyle };
|
|
120
17
|
|
|
121
|
-
interface ThemeProviderProps {
|
|
122
|
-
children: ReactNode;
|
|
123
|
-
/**
|
|
124
|
-
* Applied once, on this provider's first mount, but ONLY if nothing has
|
|
125
|
-
* made an explicit mode choice yet (see
|
|
126
|
-
* `darkModeStore.ts::seedDefaultMode`) — an explicit prior choice always
|
|
127
|
-
* wins over a component's own default. Unlike @kbach/react's web
|
|
128
|
-
* version, there's no persisted value to check here — see
|
|
129
|
-
* darkModeStore.ts's own doc comment on why this package doesn't persist
|
|
130
|
-
* the mode across app launches.
|
|
131
|
-
*/
|
|
132
|
-
defaultMode?: ThemeMode;
|
|
133
|
-
}
|
|
134
18
|
/**
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
* `
|
|
142
|
-
*
|
|
19
|
+
* Ambient module augmentation adding `className?: string` to React
|
|
20
|
+
* Native's own component prop types — without this, `<View
|
|
21
|
+
* className="...">` type-checks fine at RUNTIME (the babel plugin/
|
|
22
|
+
* jsx-runtime accept `className` on ANY host element regardless of its
|
|
23
|
+
* declared prop type — see jsxRuntimeCore.ts's own `processElement` doc
|
|
24
|
+
* comment) but fails `tsc`/editor type-checking, since `ViewProps`/
|
|
25
|
+
* `TextProps`/etc. (react-native's own types) have no `className` field at
|
|
26
|
+
* all. Confirmed as a real, reported gap: nothing in this package shipped
|
|
27
|
+
* this augmentation before, so a React Native CLI project running `tsc`
|
|
28
|
+
* (or just seeing red squiggles in-editor) hit "Property 'className' does
|
|
29
|
+
* not exist on type ... ViewProps ..." on every styled element.
|
|
30
|
+
*
|
|
31
|
+
* A plain `.d.ts` file has no effect on its own unless something actually
|
|
32
|
+
* pulls it into a consumer's type-checking. A side-effect `import
|
|
33
|
+
* './rnClassNameTypes'` from index.ts was the first thing tried here, but
|
|
34
|
+
* tsup's dts rollup (rollup-plugin-dts) tree-shakes a side-effect-only
|
|
35
|
+
* import with nothing it can trace a type reference through — confirmed by
|
|
36
|
+
* hand that this wasn't even consistent (survived one build, silently
|
|
37
|
+
* vanished from an immediately-following clean rebuild of the identical
|
|
38
|
+
* source). `scripts/append-classname-types.mjs` appends this file's own
|
|
39
|
+
* text onto the end of the built `dist/index.d.ts`/`dist/index.web.d.ts`
|
|
40
|
+
* directly as a deterministic post-build step instead — see that script's
|
|
41
|
+
* own doc comment. Anyone who imports ANYTHING from '@kbach/react-native'
|
|
42
|
+
* gets it with no extra tsconfig step needed either way. Same underlying
|
|
43
|
+
* problem (and the interface-merge half of this fix) nativewind — a React
|
|
44
|
+
* Native styling library solving this identical className-typing problem —
|
|
45
|
+
* also has to solve.
|
|
46
|
+
*
|
|
47
|
+
* Every interface below is `export interface XProps extends Readonly<...>
|
|
48
|
+
* {}` in react-native's own generated types (RN >= ~0.81's Flow-to-TS
|
|
49
|
+
* generation, `types_generated/`) — an otherwise-empty body, so merging a
|
|
50
|
+
* member in is exactly as safe as against a hand-written interface.
|
|
51
|
+
* Deliberately NOT listed here: `ImageProps`, `TouchableOpacityProps`,
|
|
52
|
+
* `ModalProps` — these are `export type X = Readonly<...>` TYPE ALIASES in
|
|
53
|
+
* the same generated types, which TypeScript cannot declaration-merge into
|
|
54
|
+
* at all (confirmed by hand: attempting it is a flat "Duplicate identifier"
|
|
55
|
+
* compile error, not a silent no-op). They don't need an entry anyway —
|
|
56
|
+
* confirmed against a real `tsc` run (RN 0.87) that `className` on
|
|
57
|
+
* `Image`/`TouchableOpacity`/`TouchableHighlight`/`Modal`/`SafeAreaView`
|
|
58
|
+
* ALL already type-check cleanly once `ViewProps` and
|
|
59
|
+
* `TouchableWithoutFeedbackProps` below are augmented — each of those
|
|
60
|
+
* components' real prop type composes one or the other somewhere in its
|
|
61
|
+
* own `Omit<...> & ...` chain (`SafeAreaView` in particular turned out to
|
|
62
|
+
* reuse `ViewProps` directly, with no separate props type of its own at
|
|
63
|
+
* all), so the merged `className` field flows through without needing its
|
|
64
|
+
* own dedicated entry.
|
|
143
65
|
*/
|
|
144
|
-
|
|
66
|
+
export {};
|
|
145
67
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
68
|
+
declare module 'react-native' {
|
|
69
|
+
interface ViewProps {
|
|
70
|
+
className?: string;
|
|
71
|
+
}
|
|
72
|
+
interface TextProps {
|
|
73
|
+
className?: string;
|
|
74
|
+
}
|
|
75
|
+
interface ScrollViewProps {
|
|
76
|
+
className?: string;
|
|
77
|
+
}
|
|
78
|
+
interface PressableProps {
|
|
79
|
+
className?: string;
|
|
80
|
+
}
|
|
81
|
+
interface TextInputProps {
|
|
82
|
+
className?: string;
|
|
83
|
+
}
|
|
84
|
+
interface TouchableHighlightProps {
|
|
85
|
+
className?: string;
|
|
86
|
+
}
|
|
87
|
+
interface TouchableWithoutFeedbackProps {
|
|
88
|
+
className?: string;
|
|
89
|
+
}
|
|
90
|
+
interface SwitchProps {
|
|
91
|
+
className?: string;
|
|
92
|
+
}
|
|
93
|
+
interface KeyboardAvoidingViewProps {
|
|
94
|
+
className?: string;
|
|
95
|
+
}
|
|
96
|
+
interface FlatListProps<ItemT> {
|
|
97
|
+
className?: string;
|
|
98
|
+
}
|
|
99
|
+
interface SectionListProps<ItemT, SectionT> {
|
|
100
|
+
className?: string;
|
|
101
|
+
}
|
|
151
102
|
}
|
|
152
|
-
/**
|
|
153
|
-
* Requires a `<ThemeProvider>` ancestor — throws otherwise, since a
|
|
154
|
-
* component that destructures `{ mode, setMode }` genuinely needs Context
|
|
155
|
-
* (unlike a component that just wants the dark/light boolean, which should
|
|
156
|
-
* reach for `useGlobalDarkMode()` instead and never needs a provider at
|
|
157
|
-
* all).
|
|
158
|
-
*/
|
|
159
|
-
declare function useTheme(): ThemeContextValue;
|
|
160
|
-
|
|
161
|
-
export { type StyleObject, type ThemeConfig, type ThemeContextValue, type ThemeMode, ThemeProvider, type ThemeProviderProps, defaultTheme, getGlobalDarkMode, getGlobalThemeMode, getTheme, resolveStyle, setGlobalThemeMode, setTheme, subscribeGlobalDarkMode, toggleGlobalDarkMode, useGlobalDarkMode, useTheme };
|