@symbiote-native/engine 1.2.0 → 1.3.1

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.
Files changed (110) hide show
  1. package/android/CMakeLists.txt +31 -1
  2. package/android/build.gradle +4 -1
  3. package/build/accessibility-info/index.android.js +21 -17
  4. package/build/accessibility-info/index.ios.js +2 -0
  5. package/build/accessibility-info/shared.d.ts +1 -1
  6. package/build/accessibility-props.d.ts +0 -11
  7. package/build/accessibility-props.js +30 -68
  8. package/build/alert/shared.d.ts +1 -1
  9. package/build/alert/shared.js +3 -7
  10. package/build/animated/graph.js +1 -1
  11. package/build/animated/leaf-lifecycle.js +2 -2
  12. package/build/app-state/index.d.ts +1 -1
  13. package/build/app-state/index.js +35 -27
  14. package/build/asset-source-resolver.d.ts +2 -0
  15. package/build/asset-source-resolver.js +13 -0
  16. package/build/back-handler/index.d.ts +7 -6
  17. package/build/back-handler/index.js +19 -16
  18. package/build/debug.js +8 -22
  19. package/build/dispatch.js +3 -9
  20. package/build/events/index.js +12 -3
  21. package/build/fabric-props.js +75 -180
  22. package/build/fabric.d.ts +2 -8
  23. package/build/fabric.js +27 -33
  24. package/build/host-access.d.ts +1 -128
  25. package/build/host-access.js +96 -205
  26. package/build/host-behavior.d.ts +1 -100
  27. package/build/host-behavior.js +125 -311
  28. package/build/image-loader.d.ts +4 -2
  29. package/build/image-loader.js +24 -48
  30. package/build/image-source-resolver.js +3 -7
  31. package/build/image-source-write.d.ts +1 -12
  32. package/build/image-source-write.js +28 -36
  33. package/build/imperative.d.ts +0 -28
  34. package/build/imperative.js +42 -92
  35. package/build/index.d.ts +3 -2
  36. package/build/index.js +30 -43
  37. package/build/invariant.d.ts +1 -0
  38. package/build/invariant.js +10 -0
  39. package/build/keyboard/index.js +11 -32
  40. package/build/linking/index.android.js +5 -3
  41. package/build/linking/shared.d.ts +1 -1
  42. package/build/linking/shared.js +16 -19
  43. package/build/mutation-buffer.d.ts +0 -177
  44. package/build/mutation-buffer.js +137 -308
  45. package/build/native-engine.d.ts +0 -102
  46. package/build/native-engine.js +38 -98
  47. package/build/native-events.d.ts +5 -0
  48. package/build/native-events.js +30 -18
  49. package/build/native-tree-host.d.ts +0 -21
  50. package/build/native-tree-host.js +14 -31
  51. package/build/node.d.ts +0 -212
  52. package/build/node.js +354 -774
  53. package/build/permissions-android/index.android.d.ts +59 -0
  54. package/build/permissions-android/index.android.js +47 -0
  55. package/build/permissions-android/index.d.ts +1 -115
  56. package/build/permissions-android/index.ios.d.ts +59 -0
  57. package/build/permissions-android/index.ios.js +31 -0
  58. package/build/permissions-android/index.js +3 -184
  59. package/build/permissions-android/shared.d.ts +63 -0
  60. package/build/permissions-android/shared.js +66 -0
  61. package/build/platform/index.android.js +3 -5
  62. package/build/platform/index.ios.js +3 -3
  63. package/build/platform/shared.d.ts +4 -0
  64. package/build/platform/shared.js +9 -0
  65. package/build/platform-color/index.android.d.ts +4 -0
  66. package/build/platform-color/index.android.js +7 -0
  67. package/build/platform-color/index.d.ts +2 -20
  68. package/build/platform-color/index.js +1 -45
  69. package/build/platform-color/shared.d.ts +21 -0
  70. package/build/platform-color/shared.js +41 -0
  71. package/build/post-commit.js +3 -8
  72. package/build/process-aspect-ratio.js +3 -7
  73. package/build/process-background-longhands.js +10 -19
  74. package/build/process-filter.js +11 -19
  75. package/build/process-font-variant.js +3 -7
  76. package/build/registry.d.ts +0 -33
  77. package/build/registry.js +22 -57
  78. package/build/report-error.js +4 -18
  79. package/build/settings/index.android.d.ts +6 -0
  80. package/build/settings/index.android.js +21 -0
  81. package/build/settings/index.d.ts +1 -8
  82. package/build/settings/index.ios.d.ts +8 -0
  83. package/build/settings/index.ios.js +122 -0
  84. package/build/settings/index.js +3 -122
  85. package/build/share/index.android.js +9 -32
  86. package/build/share/index.ios.js +14 -15
  87. package/build/share/shared.d.ts +4 -2
  88. package/build/share/shared.js +7 -10
  89. package/build/status-bar/index.android.js +1 -1
  90. package/build/status-bar/index.ios.js +4 -3
  91. package/build/structured-style.d.ts +0 -9
  92. package/build/structured-style.js +16 -31
  93. package/build/styles.js +3 -6
  94. package/build/surface.d.ts +0 -26
  95. package/build/surface.js +29 -76
  96. package/build/text-input-state.js +4 -8
  97. package/build/toast-android/index.android.d.ts +10 -0
  98. package/build/toast-android/index.android.js +108 -0
  99. package/build/toast-android/index.d.ts +1 -10
  100. package/build/toast-android/index.ios.d.ts +10 -0
  101. package/build/toast-android/index.ios.js +19 -0
  102. package/build/toast-android/index.js +3 -108
  103. package/build/touch-history.js +5 -11
  104. package/build/tree-host.d.ts +0 -270
  105. package/build/tree-host.js +63 -153
  106. package/build/view-config.js +17 -37
  107. package/cpp/SymbioteFabricProps.cpp +204 -120
  108. package/cpp/SymbioteFabricProps.h +5 -0
  109. package/cpp/SymbioteTree.cpp +20 -6
  110. package/package.json +2 -2
package/build/surface.js CHANGED
@@ -1,15 +1,5 @@
1
1
  // A surface is one mounted root: it owns the rootTag handed down by the native Fabric host and the
2
2
  // top-level nodes under it. Adapters mutate it and ask it to commit.
3
- //
4
- // A SURFACE IS ONE ORDINARY NODE, and that is the whole implementation rather than a trick. It holds
5
- // a real handle, its child ops are the ordinary append / insertBefore / removeChild, and `OP_COMMIT`
6
- // names that one handle — so the host needs no `rootTag -> node` map, which is what keeps it
7
- // stateless.
8
- //
9
- // The node is RN's AppContainer (`createSurfaceRoot` in `node.ts` — `flex: 1`, `box-none`), so what
10
- // reaches the root child set is one view with the app under it. An ANCHOR in the same position
11
- // hoists its children instead, and the host takes both through the same call, so nothing here is a
12
- // special case.
13
3
  import { dlog } from './debug.js';
14
4
  import { installEventHandler } from './events/index.js';
15
5
  import { detachAnimatedProps } from './animated/host-binding.js';
@@ -33,17 +23,10 @@ export class SymbioteSurface {
33
23
  this.rootTag = rootTag;
34
24
  this.node = createSurfaceRoot();
35
25
  }
36
- /**
37
- * Every OTHER live surface, so one commit names every root — see `commitSurfaceOps`.
38
- *
39
- * Static because it reads a sibling instance's private handle, which only a member of this class
40
- * may do. One surface — the universal case — allocates nothing.
41
- *
42
- * `self` may already be OUT of the registry: a teardown unregisters and then commits, and that
43
- * commit is the one carrying the removals. So the fast path cannot be a size check — with one
44
- * live surface left, `size === 1` means either "only me" or "only the other one", and taking the
45
- * shortcut on the second reading is how a surface loses the very batch that empties it.
46
- */
26
+ // Every other live surface, so one commit names every root — see commitSurfaceOps. Static
27
+ // because it reads a sibling instance's private handle, which only a member of this class may do.
28
+ // self may already be out of the registry: a teardown unregisters and then commits, carrying the
29
+ // removals — so the fast path can't be a size check, since size===1 could mean either surface.
47
30
  static others(self) {
48
31
  let out;
49
32
  for (const other of surfaces.values()) {
@@ -54,13 +37,8 @@ export class SymbioteSurface {
54
37
  }
55
38
  return out ?? NO_CO_COMMITTERS;
56
39
  }
57
- /**
58
- * The top-level nodes, asked of the host.
59
- *
60
- * A getter rather than an array this class maintains: a second copy of a child list is the thing
61
- * this design removes, and `nextSiblingOf` (host-access.ts) needs the same answer the host gives
62
- * for a parented node.
63
- */
40
+ // The top-level nodes, asked of the host. A getter rather than an array this class maintains: a
41
+ // second copy of a child list is the thing this design removes.
64
42
  get children() {
65
43
  return childrenOf(this.node);
66
44
  }
@@ -70,10 +48,8 @@ export class SymbioteSurface {
70
48
  insertBefore(child, beforeChild) {
71
49
  insertBefore(this.node, child, beforeChild);
72
50
  }
73
- // Nomination for teardown rides on `node.ts`'s `removeChild`, and only NOMINATES for the reason
74
- // stated there: a framework may spell a move as remove-then-reinsert, so the commit sweep
75
- // decides. The surface is one ordinary node, so it is not a second removal path that could miss
76
- // the sweep and leave a behavior — its timers included — outliving the surface.
51
+ // Nomination for teardown rides on node.ts's removeChild, and only nominates: a framework may
52
+ // spell a move as remove-then-reinsert, so the commit sweep decides.
77
53
  removeChild(child) {
78
54
  removeChild(this.node, child);
79
55
  }
@@ -81,63 +57,41 @@ export class SymbioteSurface {
81
57
  for (const child of this.children)
82
58
  removeChild(this.node, child);
83
59
  }
84
- /**
85
- * Release every host behavior still standing under this surface, at unmount.
86
- *
87
- * The sweep above cannot answer this: it only sees nodes a `removeChild` NOMINATED, and an
88
- * unmount removes nothing — the adapter drops the whole surface. Without it every node keeps its
89
- * `afterCommit` registration and its timers, and a restarted surface's commits drain the dead
90
- * one's hooks forever.
91
- */
60
+ // Release every host behavior still standing under this surface, at unmount. The sweep above
61
+ // can't answer this: it only sees nodes removeChild nominated, and an unmount removes nothing —
62
+ // the adapter drops the whole surface.
92
63
  teardown() {
93
- // The nominations first: an adapter that empties the surface and disposes it without a commit
94
- // in between (React's `clearContainer`) never reaches the commit sweep, and the walk below
95
- // cannot see those nodes either — they already left the tree.
64
+ // The nominations first: an adapter that empties and disposes the surface without a commit in
65
+ // between never reaches the commit sweep, and the walk below can't see those nodes either.
96
66
  sweepDetachedBehaviors(this.children, detachAnimatedProps);
97
67
  teardownSubtree(this.node, detachAnimatedProps);
98
68
  }
99
69
  // Synchronous commit: used by React's resetAfterCommit, which already batches per logical update.
100
70
  commit() {
101
- // A SUPERSEDED surface still flushes its ops and still names every other root — its teardown is
102
- // what carries the removals — but it must not complete a root another surface now owns. Fast
103
- // Refresh and the focus lifecycle re-mount the same rootTag, so the old surface's teardown
104
- // commit lands AFTER the new one's mount commit and would hand Fabric the emptied tree.
105
- // An UNREGISTERED surface is not superseded — nobody took the root, so its final emptied tree
106
- // is still the truth for it. Only a live OTHER owner suppresses the op.
107
- // The teardown half of the behavior lifecycle. It runs AFTER the ops are applied — it decides
108
- // what really left by asking the host for a parent, and the host does not know about a removal
109
- // it has not been handed — but BEFORE the root is completed, so a behavior's parting writes
110
- // (ScrollView taking its forced `scrollEventThrottle` back) ride this commit instead of owing
111
- // another one. `flushOps` is that split: apply, do not publish.
112
- //
113
- // `removeChild` only NOMINATES — a framework spells a move as remove-then-reinsert, so tearing
114
- // down at the call would kill a machine that comes back in the same batch.
71
+ // A superseded surface still flushes its ops and names every other root (its teardown carries
72
+ // the removals), but must not complete a root another surface now owns — Fast Refresh and the
73
+ // focus lifecycle can re-mount the same rootTag while an old teardown commit is still pending.
74
+ // The behavior-teardown half runs after ops are applied (asking the host for a parent needs the
75
+ // removal already handed over) but before the root completes, so a behavior's parting writes
76
+ // ride this commit instead of owing another one. flushOps is that split: apply, don't publish.
115
77
  flushOps();
116
- // GUARDED AT THE CALL SITE, not inside the sweep — `this.children` is a host read that builds
117
- // the whole top-level list, and it would run on every commit for a sweep that had nothing to do.
78
+ // Guarded at the call site, not inside the sweep — this.children is a host read that builds the
79
+ // whole top-level list, and it would run on every commit for a sweep with nothing to do.
118
80
  if (hasDetachCandidates()) {
119
81
  sweepDetachedBehaviors(this.children, detachAnimatedProps);
120
82
  }
121
83
  const owner = surfaces.get(this.rootTag);
122
84
  const superseded = owner !== undefined && owner !== this;
123
85
  commitSurfaceOps(superseded ? undefined : this.rootTag, this.node, SymbioteSurface.others(this));
124
- // Fresh Fabric handles are now assigned, so the three things that could not run before one
125
- // existed all drain here — this is the moment the old `commitChildren` drained them too.
126
- //
127
- // `notifyCommitted` releases the imperative waiters (`whenCommitted`); `runPostCommitHooks` the
128
- // consumers that needed a committed TAG and ran too early, which is the Animated native driver
129
- // binding a props node under an async-batched commit; `runDeferredAttaches` the half of a host
130
- // behavior whose setup needs a tag (a view command, an event attach). The predicate is passed in
131
- // rather than imported by `host-behavior.ts`, keeping that dependency one-directional — a cycle
132
- // there is a live hazard under Metro's `inlineRequires`.
86
+ // Fresh Fabric handles are now assigned, so the three things that couldn't run before one
87
+ // existed all drain here: notifyCommitted releases the imperative waiters, runPostCommitHooks
88
+ // the consumers that needed a committed tag, runDeferredAttaches the same for a host behavior.
133
89
  notifyCommitted();
134
90
  runPostCommitHooks();
135
91
  runDeferredAttaches(isNodeCommitted);
136
- // After the setup half, never before it: a node carrying both hooks has `attachAfterCommit`
137
- // seed the mirrors `afterCommit` then compares against. Unlike the two above it asks only
138
- // "props were published", so it is NOT gated on the commit having made native calls — a fold
139
- // that strips a prop makes its own commit byte-identical, and the hook that must react to the
140
- // flip would be the one the flip cannot wake.
92
+ // After the setup half, never before: a node carrying both hooks has attachAfterCommit seed the
93
+ // mirrors afterCommit then compares against. Unlike the two above, this asks only "props were
94
+ // published", so it isn't gated on the commit having made native calls.
141
95
  runCommittedHooks(isNodeCommitted);
142
96
  }
143
97
  // Coalesced commit: for reactive frameworks that emit many mutations per tick. Collapses to a
@@ -152,9 +106,8 @@ export class SymbioteSurface {
152
106
  });
153
107
  }
154
108
  }
155
- // Every live surface, so the microtask flush in `imperative.ts` can commit the one a queued write
156
- // named. Registered from here rather than imported there: a surface owns its own commit, and
157
- // reaching into it from the imperative half would put back the cycle that split exists to remove.
109
+ // Every live surface, so the microtask flush in imperative.ts can commit the one a queued write
110
+ // named. Registered from here rather than imported there, keeping that dependency one-directional.
158
111
  const surfaces = new Map();
159
112
  registerSurfaceCommit(rootTag => {
160
113
  surfaces.get(rootTag)?.commit();
@@ -1,7 +1,5 @@
1
- // Mirrors RN's TextInput/TextInputState: the single currently-focused input, tracked
2
- // JS-side because native exposes no focus getter. TextInput reports focus/blur here so
3
- // Keyboard.dismiss can blur whatever holds focus without a ref, exactly how RN's
4
- // dismissKeyboard() works (blurTextInput(currentlyFocusedInput())).
1
+ // Mirrors RN's TextInput/TextInputState: the single currently-focused input, tracked JS-side since
2
+ // native exposes no focus getter. Lets Keyboard.dismiss blur whatever holds focus without a ref.
5
3
  import { dispatchViewCommand, propOf } from './imperative.js';
6
4
  import { dlog } from './debug.js';
7
5
  let currentlyFocused = null;
@@ -18,10 +16,8 @@ export function setInputBlurred(node) {
18
16
  if (currentlyFocused === node)
19
17
  currentlyFocused = null;
20
18
  }
21
- // Imperative blur: drive the native `blur` view command and drop the tracked focus.
22
- // Used by TextInput.blur() and Keyboard.dismiss(). A no-op if this node isn't the
23
- // currently-focused one — mirrors RN's TextInputState.blurTextInput, which guards the
24
- // same way so blurring an already-unfocused input never reaches native.
19
+ // Imperative blur: drives the native `blur` command and drops tracked focus. A no-op if this
20
+ // node isn't the currently-focused one, mirroring RN's TextInputState.blurTextInput guard.
25
21
  export function blurTextInput(node) {
26
22
  if (node === null || currentlyFocused !== node)
27
23
  return;
@@ -0,0 +1,10 @@
1
+ export declare const ToastAndroid: {
2
+ SHORT: number;
3
+ LONG: number;
4
+ TOP: number;
5
+ BOTTOM: number;
6
+ CENTER: number;
7
+ show(message: string, duration: number): void;
8
+ showWithGravity(message: string, duration: number, gravity: number): void;
9
+ showWithGravityAndOffset(message: string, duration: number, gravity: number, xOffset: number, yOffset: number): void;
10
+ };
@@ -0,0 +1,108 @@
1
+ // ToastAndroid module: pops a brief Android system toast from JS. Mirrors RN's
2
+ // Libraries/Components/ToastAndroid/ToastAndroid.android.js: `show` takes a message
3
+ // and a duration; `showWithGravity` adds a layout gravity; `showWithGravityAndOffset`
4
+ // adds an x/y pixel offset. No Fabric view, pure JS->native.
5
+ //
6
+ // symbiote is iOS-first and this is an Android-only module. RN's iOS fallback
7
+ // (ToastAndroid.ios.js) throws "not supported"; we prefer a no-op + dlog so a
8
+ // cross-platform smoke never crashes when the native module is absent.
9
+ //
10
+ // The native contract is confirmed from RN's TurboModule spec at
11
+ // specs_DEPRECATED/modules/INativeToastAndroid.js:
12
+ // getConstants(): { SHORT, LONG, TOP, BOTTOM, CENTER }
13
+ // show(message: string, duration: number)
14
+ // showWithGravity(message: string, duration: number, gravity: number)
15
+ // showWithGravityAndOffset(message, duration, gravity, xOffset, yOffset)
16
+ import { dlog } from '../debug.js';
17
+ import { getNativeModule } from '../native-modules/index.js';
18
+ // The native module name RN registers this under. NOTE: this is the name the spec
19
+ // resolves via `TurboModuleRegistry.getEnforcing<Spec>('ToastAndroid')`, NOT the
20
+ // spec filename `INativeToastAndroid`. A module name is only provable on a real
21
+ // host (a headless fake answers to any name); this Android name is confirmed from
22
+ // RN source but not yet exercised on a device.
23
+ const TOAST_MODULE = 'ToastAndroid';
24
+ // Conventional RN values for SHORT/LONG/TOP/BOTTOM/CENTER. On a real device the
25
+ // numbers come from native getConstants() (resolved below); these are the fallbacks
26
+ // that keep the constants object populated headless and on a host without the module.
27
+ const FALLBACK_CONSTANTS = {
28
+ SHORT: 0,
29
+ LONG: 1,
30
+ TOP: 48,
31
+ BOTTOM: 80,
32
+ CENTER: 17,
33
+ };
34
+ // Lazily resolved so importing this module has no native side effect.
35
+ let toastModule;
36
+ function getModule() {
37
+ if (toastModule === undefined) {
38
+ toastModule = getNativeModule(TOAST_MODULE);
39
+ dlog(`ToastAndroid: ToastAndroid module ${toastModule ? 'resolved' : 'NOT resolved (null)'}`);
40
+ }
41
+ return toastModule;
42
+ }
43
+ // Read a single numeric constant from the native getConstants() payload, narrowing
44
+ // with a `typeof` guard (no cast) and falling back to the conventional RN value when
45
+ // the module or the key is absent.
46
+ function readConstant(raw, key) {
47
+ const value = raw[key];
48
+ return typeof value === 'number' ? value : FALLBACK_CONSTANTS[key];
49
+ }
50
+ // Resolve the constants once: real numbers from native on a device, fallbacks
51
+ // otherwise. Always returns a fully-populated object so the constants are never
52
+ // undefined, even headless.
53
+ function resolveConstants() {
54
+ const module = getModule();
55
+ if (module === null) {
56
+ dlog('ToastAndroid: constants from fallbacks (native module unavailable)');
57
+ return { ...FALLBACK_CONSTANTS };
58
+ }
59
+ const raw = module.getConstants();
60
+ return {
61
+ SHORT: readConstant(raw, 'SHORT'),
62
+ LONG: readConstant(raw, 'LONG'),
63
+ TOP: readConstant(raw, 'TOP'),
64
+ BOTTOM: readConstant(raw, 'BOTTOM'),
65
+ CENTER: readConstant(raw, 'CENTER'),
66
+ };
67
+ }
68
+ const constants = resolveConstants();
69
+ export const ToastAndroid = {
70
+ // Toast duration constants.
71
+ SHORT: constants.SHORT,
72
+ LONG: constants.LONG,
73
+ // Toast gravity constants.
74
+ TOP: constants.TOP,
75
+ BOTTOM: constants.BOTTOM,
76
+ CENTER: constants.CENTER,
77
+ // Show a toast with the given message and duration. Degrades to a no-op (logged)
78
+ // when the module is absent; a missing optional native module must never throw.
79
+ show(message, duration) {
80
+ const module = getModule();
81
+ if (module === null) {
82
+ dlog('ToastAndroid.show -> ToastAndroid native module unavailable, no-op');
83
+ return;
84
+ }
85
+ dlog(`ToastAndroid.show -> "${message}" (${duration})`);
86
+ module.show(message, duration);
87
+ },
88
+ // Show a toast at the given layout gravity (TOP / BOTTOM / CENTER).
89
+ showWithGravity(message, duration, gravity) {
90
+ const module = getModule();
91
+ if (module === null) {
92
+ dlog('ToastAndroid.showWithGravity -> ToastAndroid native module unavailable, no-op');
93
+ return;
94
+ }
95
+ dlog(`ToastAndroid.showWithGravity -> "${message}" (${duration}, gravity=${gravity})`);
96
+ module.showWithGravity(message, duration, gravity);
97
+ },
98
+ // Show a toast at the given gravity, offset by xOffset/yOffset pixels.
99
+ showWithGravityAndOffset(message, duration, gravity, xOffset, yOffset) {
100
+ const module = getModule();
101
+ if (module === null) {
102
+ dlog('ToastAndroid.showWithGravityAndOffset -> ToastAndroid native module unavailable, no-op');
103
+ return;
104
+ }
105
+ dlog(`ToastAndroid.showWithGravityAndOffset -> "${message}" (${duration}, gravity=${gravity}, ${xOffset}, ${yOffset})`);
106
+ module.showWithGravityAndOffset(message, duration, gravity, xOffset, yOffset);
107
+ },
108
+ };
@@ -1,10 +1 @@
1
- export declare const ToastAndroid: {
2
- SHORT: number;
3
- LONG: number;
4
- TOP: number;
5
- BOTTOM: number;
6
- CENTER: number;
7
- show(message: string, duration: number): void;
8
- showWithGravity(message: string, duration: number, gravity: number): void;
9
- showWithGravityAndOffset(message: string, duration: number, gravity: number, xOffset: number, yOffset: number): void;
10
- };
1
+ export * from './index.ios';
@@ -0,0 +1,10 @@
1
+ export declare const ToastAndroid: {
2
+ SHORT: number;
3
+ LONG: number;
4
+ TOP: number;
5
+ BOTTOM: number;
6
+ CENTER: number;
7
+ show(_message: string, _duration: number): void;
8
+ showWithGravity(_message: string, _duration: number, _gravity: number): void;
9
+ showWithGravityAndOffset(_message: string, _duration: number, _gravity: number, _xOffset: number, _yOffset: number): void;
10
+ };
@@ -0,0 +1,19 @@
1
+ // ToastAndroid off Android: RN's ToastAndroidFallback (ToastAndroid.ios.js). Zeroed constants and
2
+ // RN's own user-facing warning on every call; nothing reaches native.
3
+ const UNSUPPORTED = 'ToastAndroid is not supported on this platform.';
4
+ export const ToastAndroid = {
5
+ SHORT: 0,
6
+ LONG: 0,
7
+ TOP: 0,
8
+ BOTTOM: 0,
9
+ CENTER: 0,
10
+ show(_message, _duration) {
11
+ console.warn(UNSUPPORTED);
12
+ },
13
+ showWithGravity(_message, _duration, _gravity) {
14
+ console.warn(UNSUPPORTED);
15
+ },
16
+ showWithGravityAndOffset(_message, _duration, _gravity, _xOffset, _yOffset) {
17
+ console.warn(UNSUPPORTED);
18
+ },
19
+ };
@@ -1,108 +1,3 @@
1
- // ToastAndroid module: pops a brief Android system toast from JS. Mirrors RN's
2
- // Libraries/Components/ToastAndroid/ToastAndroid.android.js: `show` takes a message
3
- // and a duration; `showWithGravity` adds a layout gravity; `showWithGravityAndOffset`
4
- // adds an x/y pixel offset. No Fabric view, pure JS->native.
5
- //
6
- // symbiote is iOS-first and this is an Android-only module. RN's iOS fallback
7
- // (ToastAndroid.ios.js) throws "not supported"; we prefer a no-op + dlog so a
8
- // cross-platform smoke never crashes when the native module is absent.
9
- //
10
- // The native contract is confirmed from RN's TurboModule spec at
11
- // specs_DEPRECATED/modules/INativeToastAndroid.js:
12
- // getConstants(): { SHORT, LONG, TOP, BOTTOM, CENTER }
13
- // show(message: string, duration: number)
14
- // showWithGravity(message: string, duration: number, gravity: number)
15
- // showWithGravityAndOffset(message, duration, gravity, xOffset, yOffset)
16
- import { dlog } from '../debug.js';
17
- import { getNativeModule } from '../native-modules/index.js';
18
- // The native module name RN registers this under. NOTE: this is the name the spec
19
- // resolves via `TurboModuleRegistry.getEnforcing<Spec>('ToastAndroid')`, NOT the
20
- // spec filename `INativeToastAndroid`. A module name is only provable on a real
21
- // host (a headless fake answers to any name); this Android name is confirmed from
22
- // RN source but not yet exercised on a device.
23
- const TOAST_MODULE = 'ToastAndroid';
24
- // Conventional RN values for SHORT/LONG/TOP/BOTTOM/CENTER. On a real device the
25
- // numbers come from native getConstants() (resolved below); these are the fallbacks
26
- // that keep the constants object populated headless and on a host without the module.
27
- const FALLBACK_CONSTANTS = {
28
- SHORT: 0,
29
- LONG: 1,
30
- TOP: 48,
31
- BOTTOM: 80,
32
- CENTER: 17,
33
- };
34
- // Lazily resolved so importing this module has no native side effect.
35
- let toastModule;
36
- function getModule() {
37
- if (toastModule === undefined) {
38
- toastModule = getNativeModule(TOAST_MODULE);
39
- dlog(`ToastAndroid: ToastAndroid module ${toastModule ? 'resolved' : 'NOT resolved (null)'}`);
40
- }
41
- return toastModule;
42
- }
43
- // Read a single numeric constant from the native getConstants() payload, narrowing
44
- // with a `typeof` guard (no cast) and falling back to the conventional RN value when
45
- // the module or the key is absent.
46
- function readConstant(raw, key) {
47
- const value = raw[key];
48
- return typeof value === 'number' ? value : FALLBACK_CONSTANTS[key];
49
- }
50
- // Resolve the constants once: real numbers from native on a device, fallbacks
51
- // otherwise. Always returns a fully-populated object so the constants are never
52
- // undefined, even headless.
53
- function resolveConstants() {
54
- const module = getModule();
55
- if (module === null) {
56
- dlog('ToastAndroid: constants from fallbacks (native module unavailable)');
57
- return { ...FALLBACK_CONSTANTS };
58
- }
59
- const raw = module.getConstants();
60
- return {
61
- SHORT: readConstant(raw, 'SHORT'),
62
- LONG: readConstant(raw, 'LONG'),
63
- TOP: readConstant(raw, 'TOP'),
64
- BOTTOM: readConstant(raw, 'BOTTOM'),
65
- CENTER: readConstant(raw, 'CENTER'),
66
- };
67
- }
68
- const constants = resolveConstants();
69
- export const ToastAndroid = {
70
- // Toast duration constants.
71
- SHORT: constants.SHORT,
72
- LONG: constants.LONG,
73
- // Toast gravity constants.
74
- TOP: constants.TOP,
75
- BOTTOM: constants.BOTTOM,
76
- CENTER: constants.CENTER,
77
- // Show a toast with the given message and duration. Degrades to a no-op (logged)
78
- // when the module is absent; a missing optional native module must never throw.
79
- show(message, duration) {
80
- const module = getModule();
81
- if (module === null) {
82
- dlog('ToastAndroid.show -> ToastAndroid native module unavailable, no-op');
83
- return;
84
- }
85
- dlog(`ToastAndroid.show -> "${message}" (${duration})`);
86
- module.show(message, duration);
87
- },
88
- // Show a toast at the given layout gravity (TOP / BOTTOM / CENTER).
89
- showWithGravity(message, duration, gravity) {
90
- const module = getModule();
91
- if (module === null) {
92
- dlog('ToastAndroid.showWithGravity -> ToastAndroid native module unavailable, no-op');
93
- return;
94
- }
95
- dlog(`ToastAndroid.showWithGravity -> "${message}" (${duration}, gravity=${gravity})`);
96
- module.showWithGravity(message, duration, gravity);
97
- },
98
- // Show a toast at the given gravity, offset by xOffset/yOffset pixels.
99
- showWithGravityAndOffset(message, duration, gravity, xOffset, yOffset) {
100
- const module = getModule();
101
- if (module === null) {
102
- dlog('ToastAndroid.showWithGravityAndOffset -> ToastAndroid native module unavailable, no-op');
103
- return;
104
- }
105
- dlog(`ToastAndroid.showWithGravityAndOffset -> "${message}" (${duration}, gravity=${gravity}, ${xOffset}, ${yOffset})`);
106
- module.showWithGravityAndOffset(message, duration, gravity, xOffset, yOffset);
107
- },
108
- };
1
+ // ToastAndroid: base / headless build. Metro picks index.android.ts / index.ios.ts on a device; off
2
+ // those, RN's non-Android fallback is the default, as RN's own ToastAndroid.js is.
3
+ export * from './index.ios.js';
@@ -1,14 +1,8 @@
1
- // Per-touch position/time tracking, ported from RN's
2
- // react-native-renderer/.../legacy-events/ResponderTouchHistoryStore.js. PanResponder's
3
- // multitouch dx/vx math needs each touch's own previous->current delta (RN counts only
4
- // touches that moved since `_accountsForMovesUpTo`), which a grant-relative centroid of
5
- // ALL live touches cannot reconstruct. We maintain the bank as touches flow and ATTACH
6
- // `touchHistory` onto the nativeEvent reaching responder handlers, exactly how
7
- // ResponderEventPlugin.js sets `*.touchHistory`.
8
- //
9
- // events/index.ts consumes only this file's public surface (recordTouchTrack,
10
- // attachTouchHistory, resetTouchHistory, touchHistory); everything else here is a
11
- // private implementation detail of the bank.
1
+ // Per-touch position/time tracking, ported from RN's ResponderTouchHistoryStore. PanResponder's
2
+ // multitouch dx/vx math needs each touch's own previous->current delta, which a grant-relative
3
+ // centroid of all live touches can't reconstruct; the bank attaches to responder events.
4
+ // events/index.ts consumes only this file's public surface (recordTouchTrack, attachTouchHistory,
5
+ // resetTouchHistory, touchHistory); everything else here is a private implementation detail.
12
6
  import { isRecord } from './type-guards.js';
13
7
  // RN's bank is indexed by touch identifier and warns above 20; we never warn (headless
14
8
  // events may carry larger or absent ids), we just skip anything out of a sane range.