@sigx/lynx-zero 0.4.9 → 0.5.0

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,104 +1,104 @@
1
- /**
2
- * `<StatusBarSync />` — keeps the device status-bar (and Android's
3
- * navigation-bar) tint legible against the active theme.
4
- *
5
- * Reads the current theme via `useTheme()`, looks up its variant in the
6
- * theme registry, and pushes the appropriate tint to the OS via
7
- * `@sigx/lynx-appearance`:
8
- *
9
- * light theme → dark status-bar icons (legible against light bg)
10
- * dark theme → light status-bar icons (legible against dark bg)
11
- *
12
- * Mount once, inside `<ThemeProvider>`:
13
- *
14
- * ```tsx
15
- * <ThemeProvider>
16
- * <StatusBarSync />
17
- * <App />
18
- * </ThemeProvider>
19
- * ```
20
- *
21
- * Renders nothing — it's a side-effect-only component that drives a
22
- * reactive `effect()` reading `theme.name`. The `matchBackground` prop is
23
- * reserved for a follow-up that pushes the active theme's
24
- * `--color-base-100` as the Android system-bar background; today it's a
25
- * declared no-op so the API surface is stable across the rev that wires
26
- * CSS-var resolution.
27
- */
28
- import { component, effect, onMounted, onUnmounted, type Define } from '@sigx/lynx';
29
- import { isAvailable, setSystemBarsStyle } from '@sigx/lynx-appearance';
30
- import type { SystemBarStyle } from '@sigx/lynx-appearance';
31
- import { themeController } from './theme-state.js';
32
- import { variantOf } from './registry.js';
33
-
34
- export type StatusBarSyncProps =
35
- /**
36
- * Reserved — will (in a follow-up) push the active theme's
37
- * `--color-base-100` as the Android status- and navigation-bar
38
- * background. Currently a no-op; the prop ships so consumers can opt
39
- * in without an API break later. iOS and Android 15+ ignore the
40
- * background regardless (no equivalent on iOS; edge-to-edge on
41
- * Android 15+).
42
- */
43
- & Define.Prop<'matchBackground', boolean, false>;
44
-
45
- export const StatusBarSync = component<StatusBarSyncProps>(({ props }) => {
46
- // Bind to the *global* theme — not `useTheme()` — so the OS bars always
47
- // track the app/screen theme and can't be hijacked by a content sub-scope
48
- // (a nested `<ThemeProvider>` recolors its subtree but leaves the bars put).
49
- const theme = themeController;
50
- let lastApplied: string | null = null;
51
- let runner: { stop: () => void } | undefined;
52
-
53
- function apply(name: string): void {
54
- if (name === lastApplied) return;
55
- lastApplied = name;
56
- const variant = variantOf(name);
57
- // For unregistered themes we can't infer a variant — leave the
58
- // system bars alone. Consumers can register their custom theme via
59
- // `registerTheme()` to opt in.
60
- if (!variant) return;
61
- const style: SystemBarStyle = variant === 'dark' ? 'light' : 'dark';
62
- // Fire-and-forget: `setSystemBarsStyle` is non-throwing (it
63
- // resolves `{ ok: false, reason: 'unsupported' }` when the native
64
- // module isn't registered, and silently filters per-leg
65
- // `unsupported` results — e.g. nav-bar on iOS — so an aggregate
66
- // `ok: true` is still reachable on partial platforms). Either way,
67
- // void-discarding the promise here can't surface as an unhandled
68
- // rejection.
69
- void setSystemBarsStyle({
70
- statusBar: style,
71
- navigationBar: { style },
72
- });
73
- }
74
-
75
- onMounted(() => {
76
- if (!isAvailable()) return;
77
- // A reactive effect that reads `theme.name` so the effect re-runs
78
- // whenever the theme controller's underlying signal changes —
79
- // including the live system-flip path inside ThemeProvider. No
80
- // side effects in render; nothing to subscribe/unsubscribe by
81
- // hand.
82
- runner = effect(() => {
83
- apply(theme.name);
84
- });
85
- });
86
-
87
- onUnmounted(() => {
88
- runner?.stop();
89
- runner = undefined;
90
- });
91
-
92
- // Reference the prop so the type checker doesn't flag it as unused
93
- // while it's still reserved. Drop this when the matchBackground
94
- // implementation lands.
95
- void props.matchBackground;
96
-
97
- // Zero-size, out-of-flow placeholder. Avoids `display: none` —
98
- // Lynx can leak unstyled text paint through display:none overlays in
99
- // some builds (see lynx-display-none caveat); zero-size + absolute is
100
- // the safer shape.
101
- return () => (
102
- <view style={{ position: 'absolute', width: '0px', height: '0px', opacity: 0 }} />
103
- );
104
- });
1
+ /**
2
+ * `<StatusBarSync />` — keeps the device status-bar (and Android's
3
+ * navigation-bar) tint legible against the active theme.
4
+ *
5
+ * Reads the current theme via `useTheme()`, looks up its variant in the
6
+ * theme registry, and pushes the appropriate tint to the OS via
7
+ * `@sigx/lynx-appearance`:
8
+ *
9
+ * light theme → dark status-bar icons (legible against light bg)
10
+ * dark theme → light status-bar icons (legible against dark bg)
11
+ *
12
+ * Mount once, inside `<ThemeProvider>`:
13
+ *
14
+ * ```tsx
15
+ * <ThemeProvider>
16
+ * <StatusBarSync />
17
+ * <App />
18
+ * </ThemeProvider>
19
+ * ```
20
+ *
21
+ * Renders nothing — it's a side-effect-only component that drives a
22
+ * reactive `effect()` reading `theme.name`. The `matchBackground` prop is
23
+ * reserved for a follow-up that pushes the active theme's
24
+ * `--color-base-100` as the Android system-bar background; today it's a
25
+ * declared no-op so the API surface is stable across the rev that wires
26
+ * CSS-var resolution.
27
+ */
28
+ import { component, effect, onMounted, onUnmounted, type Define } from '@sigx/lynx';
29
+ import { isAvailable, setSystemBarsStyle } from '@sigx/lynx-appearance';
30
+ import type { SystemBarStyle } from '@sigx/lynx-appearance';
31
+ import { themeController } from './theme-state.js';
32
+ import { variantOf } from './registry.js';
33
+
34
+ export type StatusBarSyncProps =
35
+ /**
36
+ * Reserved — will (in a follow-up) push the active theme's
37
+ * `--color-base-100` as the Android status- and navigation-bar
38
+ * background. Currently a no-op; the prop ships so consumers can opt
39
+ * in without an API break later. iOS and Android 15+ ignore the
40
+ * background regardless (no equivalent on iOS; edge-to-edge on
41
+ * Android 15+).
42
+ */
43
+ & Define.Prop<'matchBackground', boolean, false>;
44
+
45
+ export const StatusBarSync = component<StatusBarSyncProps>(({ props }) => {
46
+ // Bind to the *global* theme — not `useTheme()` — so the OS bars always
47
+ // track the app/screen theme and can't be hijacked by a content sub-scope
48
+ // (a nested `<ThemeProvider>` recolors its subtree but leaves the bars put).
49
+ const theme = themeController;
50
+ let lastApplied: string | null = null;
51
+ let runner: { stop: () => void } | undefined;
52
+
53
+ function apply(name: string): void {
54
+ if (name === lastApplied) return;
55
+ lastApplied = name;
56
+ const variant = variantOf(name);
57
+ // For unregistered themes we can't infer a variant — leave the
58
+ // system bars alone. Consumers can register their custom theme via
59
+ // `registerTheme()` to opt in.
60
+ if (!variant) return;
61
+ const style: SystemBarStyle = variant === 'dark' ? 'light' : 'dark';
62
+ // Fire-and-forget: `setSystemBarsStyle` is non-throwing (it
63
+ // resolves `{ ok: false, reason: 'unsupported' }` when the native
64
+ // module isn't registered, and silently filters per-leg
65
+ // `unsupported` results — e.g. nav-bar on iOS — so an aggregate
66
+ // `ok: true` is still reachable on partial platforms). Either way,
67
+ // void-discarding the promise here can't surface as an unhandled
68
+ // rejection.
69
+ void setSystemBarsStyle({
70
+ statusBar: style,
71
+ navigationBar: { style },
72
+ });
73
+ }
74
+
75
+ onMounted(() => {
76
+ if (!isAvailable()) return;
77
+ // A reactive effect that reads `theme.name` so the effect re-runs
78
+ // whenever the theme controller's underlying signal changes —
79
+ // including the live system-flip path inside ThemeProvider. No
80
+ // side effects in render; nothing to subscribe/unsubscribe by
81
+ // hand.
82
+ runner = effect(() => {
83
+ apply(theme.name);
84
+ });
85
+ });
86
+
87
+ onUnmounted(() => {
88
+ runner?.stop();
89
+ runner = undefined;
90
+ });
91
+
92
+ // Reference the prop so the type checker doesn't flag it as unused
93
+ // while it's still reserved. Drop this when the matchBackground
94
+ // implementation lands.
95
+ void props.matchBackground;
96
+
97
+ // Zero-size, out-of-flow placeholder. Avoids `display: none` —
98
+ // Lynx can leak unstyled text paint through display:none overlays in
99
+ // some builds (see lynx-display-none caveat); zero-size + absolute is
100
+ // the safer shape.
101
+ return () => (
102
+ <view style={{ position: 'absolute', width: '0px', height: '0px', opacity: 0 }} />
103
+ );
104
+ });