@sigx/lynx-safe-area 0.4.0 → 0.4.2

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/globals.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type EdgeInsets } from './types';
1
+ import { type EdgeInsets } from './types.js';
2
2
  /**
3
3
  * The key under `lynx.__globalProps` where the native publisher writes the
4
4
  * inset map. Kept as a constant so iOS/Android publishers, the JS reader, and
@@ -1 +1 @@
1
- {"version":3,"file":"globals.d.ts","sourceRoot":"","sources":["../src/globals.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAe,MAAM,SAAS,CAAC;AAEvD;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,aAAa,CAAC;AAE3C;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AASD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,IAAI,UAAU,CAe/C"}
1
+ {"version":3,"file":"globals.d.ts","sourceRoot":"","sources":["../src/globals.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAe,MAAM,YAAY,CAAC;AAE1D;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,aAAa,CAAC;AAE3C;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AASD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,IAAI,UAAU,CAe/C"}
@@ -0,0 +1,46 @@
1
+ import { ZERO_INSETS } from './types.js';
2
+ /**
3
+ * The key under `lynx.__globalProps` where the native publisher writes the
4
+ * inset map. Kept as a constant so iOS/Android publishers, the JS reader, and
5
+ * tests all agree on a single string.
6
+ */
7
+ export const GLOBAL_PROPS_KEY = 'safeArea';
8
+ /**
9
+ * Synchronously read the current safe-area insets from `lynx.__globalProps`.
10
+ *
11
+ * Returns `ZERO_INSETS` when the publisher hasn't populated yet, when the
12
+ * package is bundled into a non-Lynx host (web preview, SSR), or when the
13
+ * host runtime omits the global. All callers must be prepared for the
14
+ * zero-fallback — it's the natural state during cold start before the native
15
+ * publisher has fired its first `updateGlobalProps`.
16
+ *
17
+ * Safe to call from both the Background Thread (BG) and the Main Thread
18
+ * (MT), since `lynx.__globalProps` is mirrored across both. Sync read on MT
19
+ * is what gives us inset-aware first paint.
20
+ *
21
+ * The `lynx` symbol is a closure-injected identifier (provided by
22
+ * `@lynx-js/runtime-wrapper-webpack-plugin`'s `__init_card_bundle__`
23
+ * wrapper), NOT a property of `globalThis`. Access it as a bare identifier
24
+ * with a `typeof` guard — same pattern used by `lynx-runtime/src/bg-bridge.ts`.
25
+ */
26
+ export function readGlobalSafeArea() {
27
+ const lynxObj = typeof lynx !== 'undefined'
28
+ ? lynx
29
+ : undefined;
30
+ const raw = lynxObj?.__globalProps?.[GLOBAL_PROPS_KEY];
31
+ if (!raw || typeof raw !== 'object')
32
+ return ZERO_INSETS;
33
+ return {
34
+ top: numOr0(raw.top),
35
+ right: numOr0(raw.right),
36
+ bottom: numOr0(raw.bottom),
37
+ left: numOr0(raw.left),
38
+ keyboard: numOr0(raw.keyboard),
39
+ statusBar: numOr0(raw.statusBar),
40
+ navigationBar: numOr0(raw.navigationBar),
41
+ };
42
+ }
43
+ function numOr0(v) {
44
+ return typeof v === 'number' && Number.isFinite(v) ? v : 0;
45
+ }
46
+ //# sourceMappingURL=globals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"globals.js","sourceRoot":"","sources":["../src/globals.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,WAAW,EAAE,MAAM,YAAY,CAAC;AAE1D;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,UAAU,CAAC;AAyB3C;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,OAAO,GAA+B,OAAO,IAAI,KAAK,WAAW;QACrE,CAAC,CAAE,IAAkC;QACrC,CAAC,CAAC,SAAS,CAAC;IACd,MAAM,GAAG,GAAG,OAAO,EAAE,aAAa,EAAE,CAAC,gBAAgB,CAAiC,CAAC;IACvF,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,WAAW,CAAC;IACxD,OAAO;QACL,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC;QACpB,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC;QACxB,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC;QAC1B,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;QACtB,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC9B,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC;QAChC,aAAa,EAAE,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC;KACzC,CAAC;AACJ,CAAC;AAED,SAAS,MAAM,CAAC,CAAU;IACxB,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7D,CAAC"}
package/dist/hooks.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type Computed, type PrimitiveSignal } from '@sigx/reactivity';
2
- import { type EdgeInsets, type SafeAreaContextValue } from './types';
2
+ import { type EdgeInsets, type SafeAreaContextValue } from './types.js';
3
3
  type InsetsRead = PrimitiveSignal<EdgeInsets> | Computed<EdgeInsets>;
4
4
  /**
5
5
  * BG-side reactive read of current safe-area insets. Returns the live signal
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,QAAQ,EAAE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGjF,OAAO,EAAe,KAAK,UAAU,EAAE,KAAK,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAElF,KAAK,UAAU,GAAG,eAAe,CAAC,UAAU,CAAC,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;AAErE;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,IAAI,UAAU,CAI9C;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,IAAI,oBAAoB,CAAC,IAAI,CAAC,GAAG,IAAI,CAG3E;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,gBAAgB,CAC9B,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,GACrB,QAAQ,CAAC;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAWnE;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,IAAI,UAAU,CAEhD"}
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,QAAQ,EAAE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGjF,OAAO,EAAe,KAAK,UAAU,EAAE,KAAK,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAErF,KAAK,UAAU,GAAG,eAAe,CAAC,UAAU,CAAC,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;AAErE;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,IAAI,UAAU,CAI9C;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,IAAI,oBAAoB,CAAC,IAAI,CAAC,GAAG,IAAI,CAG3E;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,gBAAgB,CAC9B,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,GACrB,QAAQ,CAAC;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAWnE;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,IAAI,UAAU,CAEhD"}
package/dist/hooks.js ADDED
@@ -0,0 +1,97 @@
1
+ import { computed } from '@sigx/reactivity';
2
+ import { useSafeAreaContext } from './injectable.js';
3
+ import { readGlobalSafeArea } from './globals.js';
4
+ import { ZERO_INSETS } from './types.js';
5
+ /**
6
+ * BG-side reactive read of current safe-area insets. Returns the live signal
7
+ * — components calling this re-render on inset change (rotation, keyboard,
8
+ * split-view).
9
+ *
10
+ * If no `<SafeAreaProvider>` is in scope, returns a signal seeded with
11
+ * `ZERO_INSETS` and warns in dev. (We don't throw because mounting an app
12
+ * fragment for tests/storybook without a provider is convenient and the
13
+ * zero fallback degrades gracefully.)
14
+ *
15
+ * @example
16
+ * ```tsx
17
+ * const insets = useSafeAreaInsets();
18
+ * return () => <view style={{ paddingTop: insets.value.top }} />;
19
+ * ```
20
+ */
21
+ export function useSafeAreaInsets() {
22
+ const ctx = useSafeAreaContext();
23
+ if (!ctx)
24
+ return fallbackSignal();
25
+ return ctx.insets;
26
+ }
27
+ /**
28
+ * Per-edge SharedValues for MT-driven `useAnimatedStyle` bindings — the
29
+ * recommended path for `<SafeAreaView>`-style layouts that need padding to
30
+ * track insets without a BG re-render. See `safe-area-view.tsx` for the
31
+ * canonical consumer.
32
+ */
33
+ export function useSafeAreaSharedValues() {
34
+ const ctx = useSafeAreaContext();
35
+ return ctx?.sv ?? null;
36
+ }
37
+ /**
38
+ * Computed signal of the inner safe frame (origin + size) in dp/pt.
39
+ *
40
+ * Useful for absolute-positioned overlays, modal bounds, and layout math
41
+ * that needs to know "the visible content rect" rather than just the inset
42
+ * deltas. The frame size is computed from the host viewport — you must pass
43
+ * `viewportWidth`/`viewportHeight` (typically read once via
44
+ * `@sigx/lynx-device-info`) since the safe-area module deliberately avoids
45
+ * pulling that whole dependency.
46
+ *
47
+ * @example
48
+ * ```tsx
49
+ * const { screenWidth, screenHeight } = await DeviceInfo.getInfo();
50
+ * const frame = useSafeAreaFrame(screenWidth, screenHeight);
51
+ * return () => <view style={{
52
+ * position: 'absolute',
53
+ * top: frame.value.y, left: frame.value.x,
54
+ * width: frame.value.width, height: frame.value.height,
55
+ * }} />;
56
+ * ```
57
+ */
58
+ export function useSafeAreaFrame(viewportWidth, viewportHeight) {
59
+ const insets = useSafeAreaInsets();
60
+ return computed(() => {
61
+ const i = insets.value;
62
+ return {
63
+ x: i.left,
64
+ y: i.top,
65
+ width: Math.max(0, viewportWidth - i.left - i.right),
66
+ height: Math.max(0, viewportHeight - i.top - i.bottom - i.keyboard),
67
+ };
68
+ });
69
+ }
70
+ /**
71
+ * **MT-thread** synchronous read of the current safe-area insets. For use
72
+ * inside `'main thread'`-marked worklet bodies. Reads `lynx.__globalProps`
73
+ * directly — there's no signal subscription, so callers re-evaluate per
74
+ * worklet invocation rather than reactively.
75
+ *
76
+ * For declarative MT-driven layout (the common case), prefer
77
+ * `<SafeAreaView edges={…}>`, which composes `useSafeAreaSharedValues()`
78
+ * with `useAnimatedStyle` — that path is reactive and applies on flush.
79
+ */
80
+ export function useSafeAreaInsetsMT() {
81
+ return readGlobalSafeArea();
82
+ }
83
+ let _fallback;
84
+ function fallbackSignal() {
85
+ if (!_fallback) {
86
+ const env = globalThis
87
+ .process?.env?.['NODE_ENV'];
88
+ if (env !== 'production') {
89
+ console.warn('[sigx-safe-area] useSafeAreaInsets() called outside <SafeAreaProvider>. ' +
90
+ 'Returning ZERO_INSETS. Wrap your app in <SafeAreaProvider> to receive ' +
91
+ 'live device insets.');
92
+ }
93
+ _fallback = computed(() => ZERO_INSETS);
94
+ }
95
+ return _fallback;
96
+ }
97
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAuC,MAAM,kBAAkB,CAAC;AACjF,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,WAAW,EAA8C,MAAM,YAAY,CAAC;AAIrF;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,iBAAiB;IAC/B,MAAM,GAAG,GAAG,kBAAkB,EAAE,CAAC;IACjC,IAAI,CAAC,GAAG;QAAE,OAAO,cAAc,EAAE,CAAC;IAClC,OAAO,GAAG,CAAC,MAAM,CAAC;AACpB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB;IACrC,MAAM,GAAG,GAAG,kBAAkB,EAAE,CAAC;IACjC,OAAO,GAAG,EAAE,EAAE,IAAI,IAAI,CAAC;AACzB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,gBAAgB,CAC9B,aAAqB,EACrB,cAAsB;IAEtB,MAAM,MAAM,GAAG,iBAAiB,EAAE,CAAC;IACnC,OAAO,QAAQ,CAAC,GAAG,EAAE;QACnB,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC;QACvB,OAAO;YACL,CAAC,EAAE,CAAC,CAAC,IAAI;YACT,CAAC,EAAE,CAAC,CAAC,GAAG;YACR,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,aAAa,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC;YACpD,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,cAAc,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,QAAQ,CAAC;SACpE,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB;IACjC,OAAO,kBAAkB,EAAE,CAAC;AAC9B,CAAC;AAED,IAAI,SAA2C,CAAC;AAChD,SAAS,cAAc;IACrB,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,GAAG,GAAI,UAAyE;aACnF,OAAO,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,CAAC;QAC9B,IAAI,GAAG,KAAK,YAAY,EAAE,CAAC;YACzB,OAAO,CAAC,IAAI,CACV,0EAA0E;gBAC1E,wEAAwE;gBACxE,qBAAqB,CACtB,CAAC;QACJ,CAAC;QACD,SAAS,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,11 +1,11 @@
1
- export { SafeAreaProvider, SAFE_AREA_EVENT } from './provider';
2
- export type { SafeAreaProviderProps } from './provider';
3
- export { SafeAreaView } from './safe-area-view';
4
- export type { SafeAreaViewProps } from './safe-area-view';
5
- export { useSafeAreaInsets, useSafeAreaSharedValues, useSafeAreaFrame, useSafeAreaInsetsMT, } from './hooks';
6
- export { useSafeAreaContext } from './injectable';
7
- export { readGlobalSafeArea, GLOBAL_PROPS_KEY, } from './globals';
8
- export type { RawSafeAreaProps } from './globals';
9
- export { ZERO_INSETS } from './types';
10
- export type { EdgeInsets, Edge, SafeAreaMode, SafeAreaContextValue, } from './types';
1
+ export { SafeAreaProvider, SAFE_AREA_EVENT } from './provider.js';
2
+ export type { SafeAreaProviderProps } from './provider.js';
3
+ export { SafeAreaView } from './safe-area-view.js';
4
+ export type { SafeAreaViewProps } from './safe-area-view.js';
5
+ export { useSafeAreaInsets, useSafeAreaSharedValues, useSafeAreaFrame, useSafeAreaInsetsMT, } from './hooks.js';
6
+ export { useSafeAreaContext } from './injectable.js';
7
+ export { readGlobalSafeArea, GLOBAL_PROPS_KEY, } from './globals.js';
8
+ export type { RawSafeAreaProps } from './globals.js';
9
+ export { ZERO_INSETS } from './types.js';
10
+ export type { EdgeInsets, Edge, SafeAreaMode, SafeAreaContextValue, } from './types.js';
11
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC/D,YAAY,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAExD,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElD,OAAO,EACL,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAElD,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,YAAY,EACV,UAAU,EACV,IAAI,EACJ,YAAY,EACZ,oBAAoB,GACrB,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAClE,YAAY,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAE3D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,YAAY,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAE7D,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAErD,OAAO,EACL,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,YAAY,EACV,UAAU,EACV,IAAI,EACJ,YAAY,EACZ,oBAAoB,GACrB,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -1,167 +1,8 @@
1
- import { component as e, computed as t, defineInjectable as n, defineProvide as r, onMounted as i, onUnmounted as a, runOnMainThread as o, signal as s, useMainThreadRef as c, useSharedValue as l } from "@sigx/lynx";
2
- import { jsx as u } from "@sigx/lynx/jsx-runtime";
3
- import { computed as d } from "@sigx/reactivity";
4
- //#region src/injectable.ts
5
- var f = n(() => null), p = {
6
- top: 0,
7
- right: 0,
8
- bottom: 0,
9
- left: 0,
10
- keyboard: 0,
11
- statusBar: 0,
12
- navigationBar: 0
13
- }, m = "safeArea";
14
- function h() {
15
- let e = (typeof lynx < "u" ? lynx : void 0)?.__globalProps?.[m];
16
- return !e || typeof e != "object" ? p : {
17
- top: g(e.top),
18
- right: g(e.right),
19
- bottom: g(e.bottom),
20
- left: g(e.left),
21
- keyboard: g(e.keyboard),
22
- statusBar: g(e.statusBar),
23
- navigationBar: g(e.navigationBar)
24
- };
25
- }
26
- function g(e) {
27
- return typeof e == "number" && Number.isFinite(e) ? e : 0;
28
- }
29
- //#endregion
30
- //#region src/provider.tsx
31
- var _ = "safeAreaChanged", v = e(({ props: e, slots: n }) => {
32
- let d = h(), p = l(d.top), m = l(d.right), g = l(d.bottom), v = l(d.left), b = s({
33
- keyboard: d.keyboard,
34
- statusBar: d.statusBar,
35
- navigationBar: d.navigationBar
36
- }), S = t(() => ({
37
- top: p.value,
38
- right: m.value,
39
- bottom: g.value,
40
- left: v.value,
41
- keyboard: b.keyboard,
42
- statusBar: b.statusBar,
43
- navigationBar: b.navigationBar
44
- })), C = {
45
- insets: S,
46
- sv: {
47
- top: p,
48
- right: m,
49
- bottom: g,
50
- left: v
51
- }
52
- };
53
- r(f, () => C);
54
- let w = o((e, t, n, r) => {
55
- "main thread";
56
- p.current.value = e, m.current.value = t, g.current.value = n, v.current.value = r;
57
- }), T = c(null), E, D;
58
- return i(() => {
59
- D = (typeof lynx < "u" ? lynx : void 0)?.getJSModule?.("GlobalEventEmitter"), D && (E = (e) => {
60
- let t = y(e, S.value);
61
- b.$set({
62
- keyboard: t.keyboard,
63
- statusBar: t.statusBar,
64
- navigationBar: t.navigationBar
65
- }), w(t.top, t.right, t.bottom, t.left);
66
- }, D.addListener(_, E));
67
- }), a(() => {
68
- D && E && D.removeListener(_, E);
69
- }), () => /* @__PURE__ */ u("view", {
70
- class: e.class,
71
- "main-thread:ref": T,
72
- style: x(S.value, e.style),
73
- children: n.default?.()
74
- });
75
- });
76
- function y(e, t) {
77
- if (!e || typeof e != "object") return t;
78
- let n = e;
79
- return {
80
- top: b(n.top, t.top),
81
- right: b(n.right, t.right),
82
- bottom: b(n.bottom, t.bottom),
83
- left: b(n.left, t.left),
84
- keyboard: b(n.keyboard, t.keyboard),
85
- statusBar: b(n.statusBar, t.statusBar),
86
- navigationBar: b(n.navigationBar, t.navigationBar)
87
- };
88
- }
89
- function b(e, t) {
90
- return typeof e == "number" && Number.isFinite(e) ? e : t;
91
- }
92
- function x(e, t) {
93
- let n = {
94
- height: "100vh",
95
- display: "flex",
96
- flexDirection: "column",
97
- "--sat": `${e.top}px`,
98
- "--sar": `${e.right}px`,
99
- "--sab": `${e.bottom}px`,
100
- "--sal": `${e.left}px`,
101
- "--safe-area-keyboard": `${e.keyboard}px`
102
- };
103
- return t ? {
104
- ...n,
105
- ...t
106
- } : n;
107
- }
108
- //#endregion
109
- //#region src/hooks.ts
110
- function S() {
111
- let e = f();
112
- return e ? e.insets : D();
113
- }
114
- function C() {
115
- return f()?.sv ?? null;
116
- }
117
- function w(e, t) {
118
- let n = S();
119
- return d(() => {
120
- let r = n.value;
121
- return {
122
- x: r.left,
123
- y: r.top,
124
- width: Math.max(0, e - r.left - r.right),
125
- height: Math.max(0, t - r.top - r.bottom - r.keyboard)
126
- };
127
- });
128
- }
129
- function T() {
130
- return h();
131
- }
132
- var E;
133
- function D() {
134
- return E ||= (globalThis.process?.env?.NODE_ENV !== "production" && console.warn("[sigx-safe-area] useSafeAreaInsets() called outside <SafeAreaProvider>. Returning ZERO_INSETS. Wrap your app in <SafeAreaProvider> to receive live device insets."), d(() => p)), E;
135
- }
136
- //#endregion
137
- //#region src/safe-area-view.tsx
138
- var O = [
139
- "top",
140
- "right",
141
- "bottom",
142
- "left"
143
- ], k = e(({ props: e, slots: t }) => {
144
- let n = S(), r = e.edges ?? O, i = e.mode ?? "padding";
145
- return () => {
146
- let a = n.value, o = {
147
- flexGrow: 1,
148
- flexShrink: 1,
149
- flexBasis: 0,
150
- minHeight: 0,
151
- display: "flex",
152
- flexDirection: "column"
153
- };
154
- return r.includes("top") && (o[i === "padding" ? "paddingTop" : "marginTop"] = `${a.top}px`), r.includes("right") && (o[i === "padding" ? "paddingRight" : "marginRight"] = `${a.right}px`), r.includes("bottom") && (o[i === "padding" ? "paddingBottom" : "marginBottom"] = `${a.bottom}px`), r.includes("left") && (o[i === "padding" ? "paddingLeft" : "marginLeft"] = `${a.left}px`), /* @__PURE__ */ u("view", {
155
- class: e.class,
156
- style: e.style ? {
157
- ...o,
158
- ...e.style
159
- } : o,
160
- children: t.default?.()
161
- });
162
- };
163
- });
164
- //#endregion
165
- export { m as GLOBAL_PROPS_KEY, _ as SAFE_AREA_EVENT, v as SafeAreaProvider, k as SafeAreaView, p as ZERO_INSETS, h as readGlobalSafeArea, f as useSafeAreaContext, w as useSafeAreaFrame, S as useSafeAreaInsets, T as useSafeAreaInsetsMT, C as useSafeAreaSharedValues };
166
-
1
+ // Public API for @sigx/lynx-safe-area.
2
+ export { SafeAreaProvider, SAFE_AREA_EVENT } from './provider.js';
3
+ export { SafeAreaView } from './safe-area-view.js';
4
+ export { useSafeAreaInsets, useSafeAreaSharedValues, useSafeAreaFrame, useSafeAreaInsetsMT, } from './hooks.js';
5
+ export { useSafeAreaContext } from './injectable.js';
6
+ export { readGlobalSafeArea, GLOBAL_PROPS_KEY, } from './globals.js';
7
+ export { ZERO_INSETS } from './types.js';
167
8
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/injectable.ts","../src/types.ts","../src/globals.ts","../src/provider.tsx","../src/hooks.ts","../src/safe-area-view.tsx"],"sourcesContent":["import { defineInjectable } from '@sigx/lynx';\nimport type { SafeAreaContextValue } from './types';\n\n/**\n * The DI handle for the safe-area context.\n *\n * - Inside `<SafeAreaProvider>`'s setup we call `defineProvide(useSafeAreaContext, factory)`\n * to install a per-app instance.\n * - Anywhere downstream `useSafeAreaContext()` returns that instance, or\n * `null` if no provider is in scope. Hooks defined in `./hooks.ts` wrap\n * this with the null-check + signal subscription.\n *\n * The factory returns `null` at the global-singleton level so consumers\n * outside a `<SafeAreaProvider>` get a clear signal (vs. a phantom zero-\n * insets context that silently does nothing).\n */\nexport const useSafeAreaContext = defineInjectable<SafeAreaContextValue | null>(() => null);\n","/**\n * Per-edge inset values, in dp/pt (logical pixels). Top/right/bottom/left\n * follow CSS shorthand order. Keyboard, statusBar, navigationBar are\n * informational extras populated when the host platform exposes them — they\n * may be 0 if unknown.\n */\nexport interface EdgeInsets {\n top: number;\n right: number;\n bottom: number;\n left: number;\n /** IME (soft keyboard) height when visible, 0 when hidden. */\n keyboard: number;\n /** Status-bar height (top system bar). Often equal to `top`, but on\n * notched devices the safe-area top includes the notch and the status\n * bar is the smaller status-only inset. */\n statusBar: number;\n /** Navigation-bar height (Android gesture/3-button nav at bottom). */\n navigationBar: number;\n}\n\n/**\n * The four standard CSS edges. Subset to control which sides\n * `<SafeAreaView>` applies inset padding/margin to.\n */\nexport type Edge = 'top' | 'right' | 'bottom' | 'left';\n\n/** Whether `<SafeAreaView>` applies its insets as `padding` or `margin`. */\nexport type SafeAreaMode = 'padding' | 'margin';\n\n/**\n * The injectable shape exposed by `<SafeAreaProvider>`. Components that need\n * insets reactively read `insets.value` (BG signal) or, for MT-driven\n * layouts, subscribe to per-edge `SharedValue`s.\n */\nexport interface SafeAreaContextValue {\n /** BG-side reactive insets. Re-renders the consumer on change. */\n readonly insets: import('@sigx/reactivity').PrimitiveSignal<EdgeInsets>;\n /** Per-edge SharedValues for MT-driven `useAnimatedStyle` bindings. */\n readonly sv: {\n top: import('@sigx/lynx').SharedValue<number>;\n right: import('@sigx/lynx').SharedValue<number>;\n bottom: import('@sigx/lynx').SharedValue<number>;\n left: import('@sigx/lynx').SharedValue<number>;\n };\n}\n\nexport const ZERO_INSETS: EdgeInsets = {\n top: 0,\n right: 0,\n bottom: 0,\n left: 0,\n keyboard: 0,\n statusBar: 0,\n navigationBar: 0,\n};\n","import { type EdgeInsets, ZERO_INSETS } from './types';\n\n/**\n * The key under `lynx.__globalProps` where the native publisher writes the\n * inset map. Kept as a constant so iOS/Android publishers, the JS reader, and\n * tests all agree on a single string.\n */\nexport const GLOBAL_PROPS_KEY = 'safeArea';\n\n/**\n * Shape of the safe-area sub-object the native publishers write to\n * `lynx.__globalProps[GLOBAL_PROPS_KEY]`. Some fields may be absent on\n * platforms that don't expose them (e.g. Android pre-31 navigation-bar API);\n * the reader fills missing keys with 0.\n */\nexport interface RawSafeAreaProps {\n top?: number;\n right?: number;\n bottom?: number;\n left?: number;\n keyboard?: number;\n statusBar?: number;\n navigationBar?: number;\n}\n\ninterface LynxGlobalLike {\n __globalProps?: { [k: string]: unknown };\n}\n\n// Closure-injected identifier — see provider.tsx for context.\ndeclare const lynx: unknown | undefined;\n\n/**\n * Synchronously read the current safe-area insets from `lynx.__globalProps`.\n *\n * Returns `ZERO_INSETS` when the publisher hasn't populated yet, when the\n * package is bundled into a non-Lynx host (web preview, SSR), or when the\n * host runtime omits the global. All callers must be prepared for the\n * zero-fallback — it's the natural state during cold start before the native\n * publisher has fired its first `updateGlobalProps`.\n *\n * Safe to call from both the Background Thread (BG) and the Main Thread\n * (MT), since `lynx.__globalProps` is mirrored across both. Sync read on MT\n * is what gives us inset-aware first paint.\n *\n * The `lynx` symbol is a closure-injected identifier (provided by\n * `@lynx-js/runtime-wrapper-webpack-plugin`'s `__init_card_bundle__`\n * wrapper), NOT a property of `globalThis`. Access it as a bare identifier\n * with a `typeof` guard — same pattern used by `lynx-runtime/src/bg-bridge.ts`.\n */\nexport function readGlobalSafeArea(): EdgeInsets {\n const lynxObj: LynxGlobalLike | undefined = typeof lynx !== 'undefined'\n ? (lynx as unknown as LynxGlobalLike)\n : undefined;\n const raw = lynxObj?.__globalProps?.[GLOBAL_PROPS_KEY] as RawSafeAreaProps | undefined;\n if (!raw || typeof raw !== 'object') return ZERO_INSETS;\n return {\n top: numOr0(raw.top),\n right: numOr0(raw.right),\n bottom: numOr0(raw.bottom),\n left: numOr0(raw.left),\n keyboard: numOr0(raw.keyboard),\n statusBar: numOr0(raw.statusBar),\n navigationBar: numOr0(raw.navigationBar),\n };\n}\n\nfunction numOr0(v: unknown): number {\n return typeof v === 'number' && Number.isFinite(v) ? v : 0;\n}\n","import {\n component,\n defineProvide,\n computed,\n signal,\n onMounted,\n onUnmounted,\n useSharedValue,\n useMainThreadRef,\n runOnMainThread,\n type Define,\n type MainThread,\n type SharedValue,\n} from '@sigx/lynx';\nimport { useSafeAreaContext } from './injectable';\nimport { readGlobalSafeArea } from './globals';\nimport type { EdgeInsets, SafeAreaContextValue } from './types';\n\n/**\n * The native publisher (iOS `SafeAreaPublisher.swift`, Android\n * `SafeAreaPublisher.kt`) emits this event via `GlobalEventEmitter` every\n * time it republishes insets. Payload mirrors the same `RawSafeAreaProps`\n * shape stored under `lynx.__globalProps[GLOBAL_PROPS_KEY]`.\n *\n * We use a custom event rather than upstream's `onGlobalPropsChanged` so\n * the contract stays in our hands (upstream's event-name conventions have\n * churned across Lynx releases).\n */\nexport const SAFE_AREA_EVENT = 'safeAreaChanged';\n\ninterface GlobalEventEmitterLike {\n addListener: (name: string, fn: (...a: unknown[]) => void) => void;\n removeListener: (name: string, fn: (...a: unknown[]) => void) => void;\n}\n\ninterface LynxLike {\n getJSModule?: (name: string) => GlobalEventEmitterLike | undefined;\n}\n\n// Closure-injected identifier provided by\n// `@lynx-js/runtime-wrapper-webpack-plugin`. Same pattern as\n// `lynx-runtime/src/shims.d.ts`. Declared locally so this package doesn't\n// have to depend on lynx-runtime-internal just for the ambient.\ndeclare const lynx: unknown | undefined;\n\nexport type SafeAreaProviderProps =\n & Define.Prop<'class', string, false>\n & Define.Prop<'style', Record<string, string | number>, false>\n & Define.Slot<'default'>;\n\n/**\n * Mount once at the root of an app. Responsibilities:\n *\n * 1. **Seed insets synchronously** from `lynx.__globalProps[safeArea]`. The\n * native side populates this *before* the MT bundle evaluates, so the\n * seed is correct on first render — no flash of unsafe content.\n *\n * 2. **Provide a DI context** (`useSafeAreaContext`) holding:\n * - four per-edge `SharedValue<number>`s — the single source of truth,\n * writable on MT, observable from both threads.\n * - a derived BG `computed<EdgeInsets>` for re-render-driven consumers\n * (`useSafeAreaInsets()`).\n *\n * 3. **Subscribe to live updates** via `GlobalEventEmitter`. The native\n * publisher emits `'safeAreaChanged'` after each `updateGlobalProps`,\n * carrying the new inset map. We dispatch a `runOnMainThread` worklet\n * that writes the per-edge SVs on MT — the SharedValue diff/publish\n * bridge then propagates the new values back to the BG signal mirror,\n * which re-fires the `computed` and re-renders consumers.\n *\n * 4. **Apply CSS variables** (`--sat`, `--sar`, `--sab`, `--sal`,\n * `--safe-area-keyboard`) on the root `<view>` so utility-class\n * consumers can write `class=\"pt-[var(--sat)]\"` and have it work\n * uniformly across iOS and Android (upstream's\n * `env(safe-area-inset-*)` is iOS-only).\n */\nexport const SafeAreaProvider = component<SafeAreaProviderProps>(({ props, slots }) => {\n const initial = readGlobalSafeArea();\n\n const svTop = useSharedValue(initial.top);\n const svRight = useSharedValue(initial.right);\n const svBottom = useSharedValue(initial.bottom);\n const svLeft = useSharedValue(initial.left);\n\n // Reactive object signal for the non-SV extras (BG-only — keyboard,\n // statusBar, navigationBar don't drive MT-bound layout, so SV plumbing\n // isn't worth the cost). `signal({...})` returns a deeply reactive proxy;\n // access via `extras.keyboard` etc., replace via `extras.$set({...})`.\n const extras = signal<Extras>({\n keyboard: initial.keyboard,\n statusBar: initial.statusBar,\n navigationBar: initial.navigationBar,\n });\n\n // Single source of truth for BG consumers — derived reactively from the\n // four edge SVs (which live on MT) and the extras signal (which lives on\n // BG). Re-runs when MT publishes new SV values via the AvBridge OR when\n // the safeAreaChanged listener writes to `extras`.\n const insets = computed<EdgeInsets>(() => ({\n top: svTop.value,\n right: svRight.value,\n bottom: svBottom.value,\n left: svLeft.value,\n keyboard: extras.keyboard,\n statusBar: extras.statusBar,\n navigationBar: extras.navigationBar,\n }));\n\n const ctx: SafeAreaContextValue = {\n insets,\n sv: { top: svTop, right: svRight, bottom: svBottom, left: svLeft },\n };\n defineProvide(useSafeAreaContext, () => ctx);\n\n // Worklet that writes the four per-edge SVs on MT. Captured by `_c` at\n // build time — runOnMainThread ships the SV refs as `{_wvid, _initValue}`\n // placeholders that the MT runtime resolves to the live envelope.\n const writeOnMT = runOnMainThread((t: number, r: number, b: number, l: number) => {\n 'main thread';\n svTop.current.value = t;\n svRight.current.value = r;\n svBottom.current.value = b;\n svLeft.current.value = l;\n });\n\n // Hold the elRef purely so consumers can extend the provider's host view\n // via the published CSS variables. Not used internally for any MT writes.\n const elRef = useMainThreadRef<MainThread.Element | null>(null);\n\n let listener: ((...a: unknown[]) => void) | undefined;\n let emitter: GlobalEventEmitterLike | undefined;\n\n onMounted(() => {\n // `lynx` is a closure-injected identifier (provided by\n // `@lynx-js/runtime-wrapper-webpack-plugin`'s `__init_card_bundle__`\n // wrapper), NOT a property of `globalThis`. Access as a bare identifier\n // with `typeof` guard — same pattern as `lynx-runtime/src/bg-bridge.ts`.\n const lynxObj: LynxLike | undefined = typeof lynx !== 'undefined'\n ? (lynx as unknown as LynxLike)\n : undefined;\n emitter = lynxObj?.getJSModule?.('GlobalEventEmitter');\n if (!emitter) return;\n listener = (raw: unknown) => {\n const next = normaliseInsets(raw, insets.value);\n extras.$set({\n keyboard: next.keyboard,\n statusBar: next.statusBar,\n navigationBar: next.navigationBar,\n });\n void writeOnMT(next.top, next.right, next.bottom, next.left);\n };\n emitter.addListener(SAFE_AREA_EVENT, listener);\n });\n\n onUnmounted(() => {\n if (emitter && listener) emitter.removeListener(SAFE_AREA_EVENT, listener);\n });\n\n return () => (\n <view\n class={props.class}\n main-thread:ref={elRef}\n style={cssVarStyle(insets.value, props.style)}\n >\n {slots.default?.()}\n </view>\n );\n});\n\ninterface Extras {\n keyboard: number;\n statusBar: number;\n navigationBar: number;\n}\n\nfunction normaliseInsets(raw: unknown, fallback: EdgeInsets): EdgeInsets {\n if (!raw || typeof raw !== 'object') return fallback;\n const o = raw as Record<string, unknown>;\n return {\n top: numOr(o['top'], fallback.top),\n right: numOr(o['right'], fallback.right),\n bottom: numOr(o['bottom'], fallback.bottom),\n left: numOr(o['left'], fallback.left),\n keyboard: numOr(o['keyboard'], fallback.keyboard),\n statusBar: numOr(o['statusBar'], fallback.statusBar),\n navigationBar: numOr(o['navigationBar'], fallback.navigationBar),\n };\n}\n\nfunction numOr(v: unknown, fallback: number): number {\n return typeof v === 'number' && Number.isFinite(v) ? v : fallback;\n}\n\nfunction cssVarStyle(\n i: EdgeInsets,\n user: Record<string, string | number> | undefined,\n): Record<string, string | number> {\n // Defaults make the provider fill the device viewport and act as a\n // flex-column ancestor. Without these, every Lynx app re-rolls inline\n // `style={{ height: '100vh', display: 'flex', flexDirection: 'column' }}`\n // because `<view>` defaults to auto height and the lynx-tailwind\n // preset (as of 0.4.0) doesn't ship an `h-screen` rule. Consumers can\n // override any of these via `props.style`.\n const base: Record<string, string | number> = {\n height: '100vh',\n display: 'flex',\n flexDirection: 'column',\n '--sat': `${i.top}px`,\n '--sar': `${i.right}px`,\n '--sab': `${i.bottom}px`,\n '--sal': `${i.left}px`,\n '--safe-area-keyboard': `${i.keyboard}px`,\n };\n return user ? { ...base, ...user } : base;\n}\n\n// re-export so users only need `@sigx/lynx-safe-area`\nexport type { SharedValue };\n","import { computed, type Computed, type PrimitiveSignal } from '@sigx/reactivity';\nimport { useSafeAreaContext } from './injectable';\nimport { readGlobalSafeArea } from './globals';\nimport { ZERO_INSETS, type EdgeInsets, type SafeAreaContextValue } from './types';\n\ntype InsetsRead = PrimitiveSignal<EdgeInsets> | Computed<EdgeInsets>;\n\n/**\n * BG-side reactive read of current safe-area insets. Returns the live signal\n * — components calling this re-render on inset change (rotation, keyboard,\n * split-view).\n *\n * If no `<SafeAreaProvider>` is in scope, returns a signal seeded with\n * `ZERO_INSETS` and warns in dev. (We don't throw because mounting an app\n * fragment for tests/storybook without a provider is convenient and the\n * zero fallback degrades gracefully.)\n *\n * @example\n * ```tsx\n * const insets = useSafeAreaInsets();\n * return () => <view style={{ paddingTop: insets.value.top }} />;\n * ```\n */\nexport function useSafeAreaInsets(): InsetsRead {\n const ctx = useSafeAreaContext();\n if (!ctx) return fallbackSignal();\n return ctx.insets;\n}\n\n/**\n * Per-edge SharedValues for MT-driven `useAnimatedStyle` bindings — the\n * recommended path for `<SafeAreaView>`-style layouts that need padding to\n * track insets without a BG re-render. See `safe-area-view.tsx` for the\n * canonical consumer.\n */\nexport function useSafeAreaSharedValues(): SafeAreaContextValue['sv'] | null {\n const ctx = useSafeAreaContext();\n return ctx?.sv ?? null;\n}\n\n/**\n * Computed signal of the inner safe frame (origin + size) in dp/pt.\n *\n * Useful for absolute-positioned overlays, modal bounds, and layout math\n * that needs to know \"the visible content rect\" rather than just the inset\n * deltas. The frame size is computed from the host viewport — you must pass\n * `viewportWidth`/`viewportHeight` (typically read once via\n * `@sigx/lynx-device-info`) since the safe-area module deliberately avoids\n * pulling that whole dependency.\n *\n * @example\n * ```tsx\n * const { screenWidth, screenHeight } = await DeviceInfo.getInfo();\n * const frame = useSafeAreaFrame(screenWidth, screenHeight);\n * return () => <view style={{\n * position: 'absolute',\n * top: frame.value.y, left: frame.value.x,\n * width: frame.value.width, height: frame.value.height,\n * }} />;\n * ```\n */\nexport function useSafeAreaFrame(\n viewportWidth: number,\n viewportHeight: number,\n): Computed<{ x: number; y: number; width: number; height: number }> {\n const insets = useSafeAreaInsets();\n return computed(() => {\n const i = insets.value;\n return {\n x: i.left,\n y: i.top,\n width: Math.max(0, viewportWidth - i.left - i.right),\n height: Math.max(0, viewportHeight - i.top - i.bottom - i.keyboard),\n };\n });\n}\n\n/**\n * **MT-thread** synchronous read of the current safe-area insets. For use\n * inside `'main thread'`-marked worklet bodies. Reads `lynx.__globalProps`\n * directly — there's no signal subscription, so callers re-evaluate per\n * worklet invocation rather than reactively.\n *\n * For declarative MT-driven layout (the common case), prefer\n * `<SafeAreaView edges={…}>`, which composes `useSafeAreaSharedValues()`\n * with `useAnimatedStyle` — that path is reactive and applies on flush.\n */\nexport function useSafeAreaInsetsMT(): EdgeInsets {\n return readGlobalSafeArea();\n}\n\nlet _fallback: Computed<EdgeInsets> | undefined;\nfunction fallbackSignal(): Computed<EdgeInsets> {\n if (!_fallback) {\n const env = (globalThis as { process?: { env?: Record<string, string | undefined> } })\n .process?.env?.['NODE_ENV'];\n if (env !== 'production') {\n console.warn(\n '[sigx-safe-area] useSafeAreaInsets() called outside <SafeAreaProvider>. ' +\n 'Returning ZERO_INSETS. Wrap your app in <SafeAreaProvider> to receive ' +\n 'live device insets.',\n );\n }\n _fallback = computed(() => ZERO_INSETS);\n }\n return _fallback;\n}\n","import { component, type Define } from '@sigx/lynx';\nimport { useSafeAreaInsets } from './hooks';\nimport type { Edge, SafeAreaMode } from './types';\n\nexport type SafeAreaViewProps =\n & Define.Prop<'edges', Edge[], false>\n & Define.Prop<'mode', SafeAreaMode, false>\n & Define.Prop<'class', string, false>\n & Define.Prop<'style', Record<string, string | number>, false>\n & Define.Slot<'default'>;\n\nconst ALL_EDGES: Edge[] = ['top', 'right', 'bottom', 'left'];\n\n/**\n * Drop-in container that applies the current safe-area insets as padding\n * (default) or margin on the configured edges.\n *\n * Implementation: BG signal + inline style. Sigx auto-tracks `insets.value`\n * access in the render function, so the inset values land in the FIRST\n * layout pass and re-apply reactively on every `safeAreaChanged` event.\n *\n * The previous implementation used `useAnimatedStyle` to drive padding via\n * the MT bridge — but `setStyleProperties` writes that affect layout fire\n * AFTER the first layout pass, and child elements that have already laid\n * out (notably `<scroll-view>`, which captures its frame eagerly) don't\n * reflow. Inline style avoids that timing trap entirely.\n *\n * `edges` defaults to all four sides. Pass a subset (e.g. `['top']`) to\n * leave the unspecified sides unaffected.\n *\n * Must be a descendant of `<SafeAreaProvider>`. If no provider is in scope\n * (test/storybook), `useSafeAreaInsets()` returns `ZERO_INSETS` with a\n * dev-mode warning and SafeAreaView passes through unchanged.\n *\n * @example\n * ```tsx\n * <SafeAreaProvider>\n * <SafeAreaView edges={['top', 'bottom']} class=\"bg-base-100 flex-1\">\n * <PageContent />\n * </SafeAreaView>\n * </SafeAreaProvider>\n * ```\n */\nexport const SafeAreaView = component<SafeAreaViewProps>(({ props, slots }) => {\n const insets = useSafeAreaInsets();\n const edges = props.edges ?? ALL_EDGES;\n const mode = props.mode ?? 'padding';\n\n return () => {\n const i = insets.value;\n // Default to filling the parent (flex-grow + flex-column). Lynx (like\n // React Native) does NOT treat `flex: 1` shorthand the way browsers\n // do — its `flexBasis` resolves to `'auto'`, which sizes to content\n // and collapses the chain. The long-form `flexBasis: 0` is the only\n // reliable way to \"fill remaining space.\" Bottom chrome like\n // `<NavTabBar />` from `@sigx/lynx-daisyui` won't park itself at the\n // screen edge without this.\n const baseStyle: Record<string, string | number> = {\n flexGrow: 1,\n flexShrink: 1,\n flexBasis: 0,\n minHeight: 0,\n display: 'flex',\n flexDirection: 'column',\n };\n if (edges.includes('top')) {\n baseStyle[mode === 'padding' ? 'paddingTop' : 'marginTop'] = `${i.top}px`;\n }\n if (edges.includes('right')) {\n baseStyle[mode === 'padding' ? 'paddingRight' : 'marginRight'] = `${i.right}px`;\n }\n if (edges.includes('bottom')) {\n baseStyle[mode === 'padding' ? 'paddingBottom' : 'marginBottom'] = `${i.bottom}px`;\n }\n if (edges.includes('left')) {\n baseStyle[mode === 'padding' ? 'paddingLeft' : 'marginLeft'] = `${i.left}px`;\n }\n return (\n <view\n class={props.class}\n style={props.style ? { ...baseStyle, ...props.style } : baseStyle}\n >\n {slots.default?.()}\n </view>\n );\n };\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,uCAAuC;AAEvC,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAGlE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGnD,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAErD,OAAO,EACL,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
@@ -1,4 +1,4 @@
1
- import type { SafeAreaContextValue } from './types';
1
+ import type { SafeAreaContextValue } from './types.js';
2
2
  /**
3
3
  * The DI handle for the safe-area context.
4
4
  *
@@ -1 +1 @@
1
- {"version":3,"file":"injectable.d.ts","sourceRoot":"","sources":["../src/injectable.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAEpD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,kBAAkB,8EAA4D,CAAC"}
1
+ {"version":3,"file":"injectable.d.ts","sourceRoot":"","sources":["../src/injectable.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,kBAAkB,8EAA4D,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { defineInjectable } from '@sigx/lynx';
2
+ /**
3
+ * The DI handle for the safe-area context.
4
+ *
5
+ * - Inside `<SafeAreaProvider>`'s setup we call `defineProvide(useSafeAreaContext, factory)`
6
+ * to install a per-app instance.
7
+ * - Anywhere downstream `useSafeAreaContext()` returns that instance, or
8
+ * `null` if no provider is in scope. Hooks defined in `./hooks.ts` wrap
9
+ * this with the null-check + signal subscription.
10
+ *
11
+ * The factory returns `null` at the global-singleton level so consumers
12
+ * outside a `<SafeAreaProvider>` get a clear signal (vs. a phantom zero-
13
+ * insets context that silently does nothing).
14
+ */
15
+ export const useSafeAreaContext = defineInjectable(() => null);
16
+ //# sourceMappingURL=injectable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"injectable.js","sourceRoot":"","sources":["../src/injectable.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAG9C;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,gBAAgB,CAA8B,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC"}
@@ -0,0 +1,154 @@
1
+ import { jsx as _jsx } from "@sigx/lynx/jsx-runtime";
2
+ import { component, defineProvide, computed, signal, onMounted, onUnmounted, useSharedValue, useMainThreadRef, runOnMainThread, } from '@sigx/lynx';
3
+ import { useSafeAreaContext } from './injectable.js';
4
+ import { readGlobalSafeArea } from './globals.js';
5
+ /**
6
+ * The native publisher (iOS `SafeAreaPublisher.swift`, Android
7
+ * `SafeAreaPublisher.kt`) emits this event via `GlobalEventEmitter` every
8
+ * time it republishes insets. Payload mirrors the same `RawSafeAreaProps`
9
+ * shape stored under `lynx.__globalProps[GLOBAL_PROPS_KEY]`.
10
+ *
11
+ * We use a custom event rather than upstream's `onGlobalPropsChanged` so
12
+ * the contract stays in our hands (upstream's event-name conventions have
13
+ * churned across Lynx releases).
14
+ */
15
+ export const SAFE_AREA_EVENT = 'safeAreaChanged';
16
+ /**
17
+ * Mount once at the root of an app. Responsibilities:
18
+ *
19
+ * 1. **Seed insets synchronously** from `lynx.__globalProps[safeArea]`. The
20
+ * native side populates this *before* the MT bundle evaluates, so the
21
+ * seed is correct on first render — no flash of unsafe content.
22
+ *
23
+ * 2. **Provide a DI context** (`useSafeAreaContext`) holding:
24
+ * - four per-edge `SharedValue<number>`s — the single source of truth,
25
+ * writable on MT, observable from both threads.
26
+ * - a derived BG `computed<EdgeInsets>` for re-render-driven consumers
27
+ * (`useSafeAreaInsets()`).
28
+ *
29
+ * 3. **Subscribe to live updates** via `GlobalEventEmitter`. The native
30
+ * publisher emits `'safeAreaChanged'` after each `updateGlobalProps`,
31
+ * carrying the new inset map. We dispatch a `runOnMainThread` worklet
32
+ * that writes the per-edge SVs on MT — the SharedValue diff/publish
33
+ * bridge then propagates the new values back to the BG signal mirror,
34
+ * which re-fires the `computed` and re-renders consumers.
35
+ *
36
+ * 4. **Apply CSS variables** (`--sat`, `--sar`, `--sab`, `--sal`,
37
+ * `--safe-area-keyboard`) on the root `<view>` so utility-class
38
+ * consumers can write `class="pt-[var(--sat)]"` and have it work
39
+ * uniformly across iOS and Android (upstream's
40
+ * `env(safe-area-inset-*)` is iOS-only).
41
+ */
42
+ export const SafeAreaProvider = component(({ props, slots }) => {
43
+ const initial = readGlobalSafeArea();
44
+ const svTop = useSharedValue(initial.top);
45
+ const svRight = useSharedValue(initial.right);
46
+ const svBottom = useSharedValue(initial.bottom);
47
+ const svLeft = useSharedValue(initial.left);
48
+ // Reactive object signal for the non-SV extras (BG-only — keyboard,
49
+ // statusBar, navigationBar don't drive MT-bound layout, so SV plumbing
50
+ // isn't worth the cost). `signal({...})` returns a deeply reactive proxy;
51
+ // access via `extras.keyboard` etc., replace via `extras.$set({...})`.
52
+ const extras = signal({
53
+ keyboard: initial.keyboard,
54
+ statusBar: initial.statusBar,
55
+ navigationBar: initial.navigationBar,
56
+ });
57
+ // Single source of truth for BG consumers — derived reactively from the
58
+ // four edge SVs (which live on MT) and the extras signal (which lives on
59
+ // BG). Re-runs when MT publishes new SV values via the AvBridge OR when
60
+ // the safeAreaChanged listener writes to `extras`.
61
+ const insets = computed(() => ({
62
+ top: svTop.value,
63
+ right: svRight.value,
64
+ bottom: svBottom.value,
65
+ left: svLeft.value,
66
+ keyboard: extras.keyboard,
67
+ statusBar: extras.statusBar,
68
+ navigationBar: extras.navigationBar,
69
+ }));
70
+ const ctx = {
71
+ insets,
72
+ sv: { top: svTop, right: svRight, bottom: svBottom, left: svLeft },
73
+ };
74
+ defineProvide(useSafeAreaContext, () => ctx);
75
+ // Worklet that writes the four per-edge SVs on MT. Captured by `_c` at
76
+ // build time — runOnMainThread ships the SV refs as `{_wvid, _initValue}`
77
+ // placeholders that the MT runtime resolves to the live envelope.
78
+ const writeOnMT = runOnMainThread((t, r, b, l) => {
79
+ 'main thread';
80
+ svTop.current.value = t;
81
+ svRight.current.value = r;
82
+ svBottom.current.value = b;
83
+ svLeft.current.value = l;
84
+ });
85
+ // Hold the elRef purely so consumers can extend the provider's host view
86
+ // via the published CSS variables. Not used internally for any MT writes.
87
+ const elRef = useMainThreadRef(null);
88
+ let listener;
89
+ let emitter;
90
+ onMounted(() => {
91
+ // `lynx` is a closure-injected identifier (provided by
92
+ // `@lynx-js/runtime-wrapper-webpack-plugin`'s `__init_card_bundle__`
93
+ // wrapper), NOT a property of `globalThis`. Access as a bare identifier
94
+ // with `typeof` guard — same pattern as `lynx-runtime/src/bg-bridge.ts`.
95
+ const lynxObj = typeof lynx !== 'undefined'
96
+ ? lynx
97
+ : undefined;
98
+ emitter = lynxObj?.getJSModule?.('GlobalEventEmitter');
99
+ if (!emitter)
100
+ return;
101
+ listener = (raw) => {
102
+ const next = normaliseInsets(raw, insets.value);
103
+ extras.$set({
104
+ keyboard: next.keyboard,
105
+ statusBar: next.statusBar,
106
+ navigationBar: next.navigationBar,
107
+ });
108
+ void writeOnMT(next.top, next.right, next.bottom, next.left);
109
+ };
110
+ emitter.addListener(SAFE_AREA_EVENT, listener);
111
+ });
112
+ onUnmounted(() => {
113
+ if (emitter && listener)
114
+ emitter.removeListener(SAFE_AREA_EVENT, listener);
115
+ });
116
+ return () => (_jsx("view", { class: props.class, "main-thread:ref": elRef, style: cssVarStyle(insets.value, props.style), children: slots.default?.() }));
117
+ });
118
+ function normaliseInsets(raw, fallback) {
119
+ if (!raw || typeof raw !== 'object')
120
+ return fallback;
121
+ const o = raw;
122
+ return {
123
+ top: numOr(o['top'], fallback.top),
124
+ right: numOr(o['right'], fallback.right),
125
+ bottom: numOr(o['bottom'], fallback.bottom),
126
+ left: numOr(o['left'], fallback.left),
127
+ keyboard: numOr(o['keyboard'], fallback.keyboard),
128
+ statusBar: numOr(o['statusBar'], fallback.statusBar),
129
+ navigationBar: numOr(o['navigationBar'], fallback.navigationBar),
130
+ };
131
+ }
132
+ function numOr(v, fallback) {
133
+ return typeof v === 'number' && Number.isFinite(v) ? v : fallback;
134
+ }
135
+ function cssVarStyle(i, user) {
136
+ // Defaults make the provider fill the device viewport and act as a
137
+ // flex-column ancestor. Without these, every Lynx app re-rolls inline
138
+ // `style={{ height: '100vh', display: 'flex', flexDirection: 'column' }}`
139
+ // because `<view>` defaults to auto height and the lynx-tailwind
140
+ // preset (as of 0.4.0) doesn't ship an `h-screen` rule. Consumers can
141
+ // override any of these via `props.style`.
142
+ const base = {
143
+ height: '100vh',
144
+ display: 'flex',
145
+ flexDirection: 'column',
146
+ '--sat': `${i.top}px`,
147
+ '--sar': `${i.right}px`,
148
+ '--sab': `${i.bottom}px`,
149
+ '--sal': `${i.left}px`,
150
+ '--safe-area-keyboard': `${i.keyboard}px`,
151
+ };
152
+ return user ? { ...base, ...user } : base;
153
+ }
154
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { type Define } from '@sigx/lynx';
2
- import type { Edge, SafeAreaMode } from './types';
2
+ import type { Edge, SafeAreaMode } from './types.js';
3
3
  export type SafeAreaViewProps = Define.Prop<'edges', Edge[], false> & Define.Prop<'mode', SafeAreaMode, false> & Define.Prop<'class', string, false> & Define.Prop<'style', Record<string, string | number>, false> & Define.Slot<'default'>;
4
4
  /**
5
5
  * Drop-in container that applies the current safe-area insets as padding
@@ -1 +1 @@
1
- {"version":3,"file":"safe-area-view.d.ts","sourceRoot":"","sources":["../src/safe-area-view.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AAEpD,OAAO,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAElD,MAAM,MAAM,iBAAiB,GACzB,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,KAAK,CAAC,GACnC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,CAAC,GACxC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,GACnC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,EAAE,KAAK,CAAC,GAC5D,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;AAI3B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,YAAY;;EA2CvB,CAAC"}
1
+ {"version":3,"file":"safe-area-view.d.ts","sourceRoot":"","sources":["../src/safe-area-view.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AAEpD,OAAO,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAErD,MAAM,MAAM,iBAAiB,GACzB,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,KAAK,CAAC,GACnC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,CAAC,GACxC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,GACnC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,EAAE,KAAK,CAAC,GAC5D,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;AAI3B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,YAAY;;EA2CvB,CAAC"}
@@ -0,0 +1,71 @@
1
+ import { jsx as _jsx } from "@sigx/lynx/jsx-runtime";
2
+ import { component } from '@sigx/lynx';
3
+ import { useSafeAreaInsets } from './hooks.js';
4
+ const ALL_EDGES = ['top', 'right', 'bottom', 'left'];
5
+ /**
6
+ * Drop-in container that applies the current safe-area insets as padding
7
+ * (default) or margin on the configured edges.
8
+ *
9
+ * Implementation: BG signal + inline style. Sigx auto-tracks `insets.value`
10
+ * access in the render function, so the inset values land in the FIRST
11
+ * layout pass and re-apply reactively on every `safeAreaChanged` event.
12
+ *
13
+ * The previous implementation used `useAnimatedStyle` to drive padding via
14
+ * the MT bridge — but `setStyleProperties` writes that affect layout fire
15
+ * AFTER the first layout pass, and child elements that have already laid
16
+ * out (notably `<scroll-view>`, which captures its frame eagerly) don't
17
+ * reflow. Inline style avoids that timing trap entirely.
18
+ *
19
+ * `edges` defaults to all four sides. Pass a subset (e.g. `['top']`) to
20
+ * leave the unspecified sides unaffected.
21
+ *
22
+ * Must be a descendant of `<SafeAreaProvider>`. If no provider is in scope
23
+ * (test/storybook), `useSafeAreaInsets()` returns `ZERO_INSETS` with a
24
+ * dev-mode warning and SafeAreaView passes through unchanged.
25
+ *
26
+ * @example
27
+ * ```tsx
28
+ * <SafeAreaProvider>
29
+ * <SafeAreaView edges={['top', 'bottom']} class="bg-base-100 flex-1">
30
+ * <PageContent />
31
+ * </SafeAreaView>
32
+ * </SafeAreaProvider>
33
+ * ```
34
+ */
35
+ export const SafeAreaView = component(({ props, slots }) => {
36
+ const insets = useSafeAreaInsets();
37
+ const edges = props.edges ?? ALL_EDGES;
38
+ const mode = props.mode ?? 'padding';
39
+ return () => {
40
+ const i = insets.value;
41
+ // Default to filling the parent (flex-grow + flex-column). Lynx (like
42
+ // React Native) does NOT treat `flex: 1` shorthand the way browsers
43
+ // do — its `flexBasis` resolves to `'auto'`, which sizes to content
44
+ // and collapses the chain. The long-form `flexBasis: 0` is the only
45
+ // reliable way to "fill remaining space." Bottom chrome like
46
+ // `<NavTabBar />` from `@sigx/lynx-daisyui` won't park itself at the
47
+ // screen edge without this.
48
+ const baseStyle = {
49
+ flexGrow: 1,
50
+ flexShrink: 1,
51
+ flexBasis: 0,
52
+ minHeight: 0,
53
+ display: 'flex',
54
+ flexDirection: 'column',
55
+ };
56
+ if (edges.includes('top')) {
57
+ baseStyle[mode === 'padding' ? 'paddingTop' : 'marginTop'] = `${i.top}px`;
58
+ }
59
+ if (edges.includes('right')) {
60
+ baseStyle[mode === 'padding' ? 'paddingRight' : 'marginRight'] = `${i.right}px`;
61
+ }
62
+ if (edges.includes('bottom')) {
63
+ baseStyle[mode === 'padding' ? 'paddingBottom' : 'marginBottom'] = `${i.bottom}px`;
64
+ }
65
+ if (edges.includes('left')) {
66
+ baseStyle[mode === 'padding' ? 'paddingLeft' : 'marginLeft'] = `${i.left}px`;
67
+ }
68
+ return (_jsx("view", { class: props.class, style: props.style ? { ...baseStyle, ...props.style } : baseStyle, children: slots.default?.() }));
69
+ };
70
+ });
71
+ //# sourceMappingURL=safe-area-view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safe-area-view.js","sourceRoot":"","sources":["../src/safe-area-view.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAe,MAAM,YAAY,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAU/C,MAAM,SAAS,GAAW,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;AAE7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,SAAS,CAAoB,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE;IAC5E,MAAM,MAAM,GAAG,iBAAiB,EAAE,CAAC;IACnC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,SAAS,CAAC;IACvC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,SAAS,CAAC;IAErC,OAAO,GAAG,EAAE;QACV,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC;QACvB,sEAAsE;QACtE,oEAAoE;QACpE,oEAAoE;QACpE,oEAAoE;QACpE,6DAA6D;QAC7D,qEAAqE;QACrE,4BAA4B;QAC5B,MAAM,SAAS,GAAoC;YACjD,QAAQ,EAAE,CAAC;YACX,UAAU,EAAE,CAAC;YACb,SAAS,EAAE,CAAC;YACZ,SAAS,EAAE,CAAC;YACZ,OAAO,EAAE,MAAM;YACf,aAAa,EAAE,QAAQ;SACxB,CAAC;QACF,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,SAAS,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC;QAC5E,CAAC;QACD,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YAC5B,SAAS,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,aAAa,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC;QAClF,CAAC;QACD,IAAI,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,SAAS,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,cAAc,CAAC,GAAG,GAAG,CAAC,CAAC,MAAM,IAAI,CAAC;QACrF,CAAC;QACD,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,SAAS,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC;QAC/E,CAAC;QACD,OAAO,CACL,eACE,KAAK,EAAE,KAAK,CAAC,KAAK,EAClB,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,SAAS,EAAE,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,YAEhE,KAAK,CAAC,OAAO,EAAE,EAAE,GACb,CACR,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,10 @@
1
+ export const ZERO_INSETS = {
2
+ top: 0,
3
+ right: 0,
4
+ bottom: 0,
5
+ left: 0,
6
+ keyboard: 0,
7
+ statusBar: 0,
8
+ navigationBar: 0,
9
+ };
10
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AA+CA,MAAM,CAAC,MAAM,WAAW,GAAe;IACrC,GAAG,EAAE,CAAC;IACN,KAAK,EAAE,CAAC;IACR,MAAM,EAAE,CAAC;IACT,IAAI,EAAE,CAAC;IACP,QAAQ,EAAE,CAAC;IACX,SAAS,EAAE,CAAC;IACZ,aAAa,EAAE,CAAC;CACjB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sigx/lynx-safe-area",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Safe area insets (notch, home indicator, status bar, keyboard) for sigx-lynx",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -30,16 +30,16 @@
30
30
  "author": "Andreas Ekdahl",
31
31
  "license": "MIT",
32
32
  "dependencies": {
33
- "@sigx/reactivity": "^0.4.3",
34
- "@sigx/lynx": "^0.4.0",
35
- "@sigx/lynx-runtime-internal": "^0.4.0"
33
+ "@sigx/reactivity": "^0.4.8",
34
+ "@sigx/lynx-runtime-internal": "^0.4.2",
35
+ "@sigx/lynx": "^0.4.2"
36
36
  },
37
37
  "devDependencies": {
38
+ "@typescript/native-preview": "7.0.0-dev.20260521.1",
38
39
  "typescript": "^6.0.3",
39
- "vite": "^8.0.12",
40
- "@sigx/lynx-plugin": "^0.4.0",
41
- "@sigx/lynx-testing": "^0.4.0",
42
- "@sigx/lynx-runtime-main": "^0.4.0"
40
+ "@sigx/lynx-runtime-main": "^0.4.2",
41
+ "@sigx/lynx-testing": "^0.4.2",
42
+ "@sigx/lynx-plugin": "^0.4.2"
43
43
  },
44
44
  "repository": {
45
45
  "type": "git",
@@ -54,7 +54,8 @@
54
54
  "access": "public"
55
55
  },
56
56
  "scripts": {
57
- "build": "vite build && tsgo --emitDeclarationOnly",
58
- "dev": "vite build --watch"
57
+ "build": "node ../../scripts/clean.mjs dist && tsgo",
58
+ "dev": "tsgo --watch",
59
+ "clean": "node ../../scripts/clean.mjs dist .turbo"
59
60
  }
60
61
  }
package/src/globals.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type EdgeInsets, ZERO_INSETS } from './types';
1
+ import { type EdgeInsets, ZERO_INSETS } from './types.js';
2
2
 
3
3
  /**
4
4
  * The key under `lynx.__globalProps` where the native publisher writes the
package/src/hooks.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { computed, type Computed, type PrimitiveSignal } from '@sigx/reactivity';
2
- import { useSafeAreaContext } from './injectable';
3
- import { readGlobalSafeArea } from './globals';
4
- import { ZERO_INSETS, type EdgeInsets, type SafeAreaContextValue } from './types';
2
+ import { useSafeAreaContext } from './injectable.js';
3
+ import { readGlobalSafeArea } from './globals.js';
4
+ import { ZERO_INSETS, type EdgeInsets, type SafeAreaContextValue } from './types.js';
5
5
 
6
6
  type InsetsRead = PrimitiveSignal<EdgeInsets> | Computed<EdgeInsets>;
7
7
 
package/src/index.ts CHANGED
@@ -1,30 +1,30 @@
1
1
  // Public API for @sigx/lynx-safe-area.
2
2
 
3
- export { SafeAreaProvider, SAFE_AREA_EVENT } from './provider';
4
- export type { SafeAreaProviderProps } from './provider';
3
+ export { SafeAreaProvider, SAFE_AREA_EVENT } from './provider.js';
4
+ export type { SafeAreaProviderProps } from './provider.js';
5
5
 
6
- export { SafeAreaView } from './safe-area-view';
7
- export type { SafeAreaViewProps } from './safe-area-view';
6
+ export { SafeAreaView } from './safe-area-view.js';
7
+ export type { SafeAreaViewProps } from './safe-area-view.js';
8
8
 
9
9
  export {
10
10
  useSafeAreaInsets,
11
11
  useSafeAreaSharedValues,
12
12
  useSafeAreaFrame,
13
13
  useSafeAreaInsetsMT,
14
- } from './hooks';
14
+ } from './hooks.js';
15
15
 
16
- export { useSafeAreaContext } from './injectable';
16
+ export { useSafeAreaContext } from './injectable.js';
17
17
 
18
18
  export {
19
19
  readGlobalSafeArea,
20
20
  GLOBAL_PROPS_KEY,
21
- } from './globals';
22
- export type { RawSafeAreaProps } from './globals';
21
+ } from './globals.js';
22
+ export type { RawSafeAreaProps } from './globals.js';
23
23
 
24
- export { ZERO_INSETS } from './types';
24
+ export { ZERO_INSETS } from './types.js';
25
25
  export type {
26
26
  EdgeInsets,
27
27
  Edge,
28
28
  SafeAreaMode,
29
29
  SafeAreaContextValue,
30
- } from './types';
30
+ } from './types.js';
package/src/injectable.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { defineInjectable } from '@sigx/lynx';
2
- import type { SafeAreaContextValue } from './types';
2
+ import type { SafeAreaContextValue } from './types.js';
3
3
 
4
4
  /**
5
5
  * The DI handle for the safe-area context.
package/src/provider.tsx CHANGED
@@ -12,9 +12,9 @@ import {
12
12
  type MainThread,
13
13
  type SharedValue,
14
14
  } from '@sigx/lynx';
15
- import { useSafeAreaContext } from './injectable';
16
- import { readGlobalSafeArea } from './globals';
17
- import type { EdgeInsets, SafeAreaContextValue } from './types';
15
+ import { useSafeAreaContext } from './injectable.js';
16
+ import { readGlobalSafeArea } from './globals.js';
17
+ import type { EdgeInsets, SafeAreaContextValue } from './types.js';
18
18
 
19
19
  /**
20
20
  * The native publisher (iOS `SafeAreaPublisher.swift`, Android
@@ -1,6 +1,6 @@
1
1
  import { component, type Define } from '@sigx/lynx';
2
- import { useSafeAreaInsets } from './hooks';
3
- import type { Edge, SafeAreaMode } from './types';
2
+ import { useSafeAreaInsets } from './hooks.js';
3
+ import type { Edge, SafeAreaMode } from './types.js';
4
4
 
5
5
  export type SafeAreaViewProps =
6
6
  & Define.Prop<'edges', Edge[], false>