@symbiote-native/engine 0.4.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/README.md +39 -14
  2. package/android/CMakeLists.txt +51 -0
  3. package/android/build.gradle +90 -0
  4. package/android/src/main/AndroidManifest.xml +1 -0
  5. package/android/src/main/cpp/SymbioteEngineJni.cpp +72 -0
  6. package/android/src/main/java/dev/symbiotenative/engine/SymbioteEngineModule.kt +43 -0
  7. package/android/src/main/java/dev/symbiotenative/engine/SymbioteEnginePackage.kt +35 -0
  8. package/build/accessibility-info/shared.js +1 -1
  9. package/build/accessibility-props.d.ts +1 -7
  10. package/build/accessibility-props.js +22 -21
  11. package/build/animated/animations/composition.d.ts +1 -1
  12. package/build/animated/animations/composition.js +18 -4
  13. package/build/animated/easing.d.ts +3 -2
  14. package/build/animated/easing.js +17 -88
  15. package/build/animated/event.js +6 -1
  16. package/build/animated/graph.d.ts +2 -0
  17. package/build/animated/graph.js +14 -0
  18. package/build/animated/host-binding.d.ts +39 -0
  19. package/build/animated/host-binding.js +278 -0
  20. package/build/animated/index.d.ts +1 -1
  21. package/build/animated/leaf-lifecycle.js +10 -22
  22. package/build/animated/mock.d.ts +1 -19
  23. package/build/animated/props.js +1 -1
  24. package/build/animated/rgba.js +16 -50
  25. package/build/events/index.js +123 -33
  26. package/build/fabric-props.d.ts +1 -1
  27. package/build/fabric-props.js +129 -182
  28. package/build/fabric.d.ts +10 -0
  29. package/build/fabric.js +40 -0
  30. package/build/host-access.d.ts +125 -0
  31. package/build/host-access.js +280 -0
  32. package/build/host-behavior.d.ts +107 -7
  33. package/build/host-behavior.js +309 -31
  34. package/build/image-source-write.d.ts +16 -0
  35. package/build/image-source-write.js +65 -0
  36. package/build/imperative.d.ts +49 -0
  37. package/build/imperative.js +258 -0
  38. package/build/index.d.ts +18 -10
  39. package/build/index.js +65 -10
  40. package/build/mutation-buffer.d.ts +222 -0
  41. package/build/mutation-buffer.js +491 -0
  42. package/build/native-engine.d.ts +182 -0
  43. package/build/native-engine.js +178 -0
  44. package/build/native-tree-host.d.ts +25 -0
  45. package/build/native-tree-host.js +66 -0
  46. package/build/node.d.ts +191 -60
  47. package/build/node.js +1034 -327
  48. package/build/pan-responder/index.d.ts +2 -2
  49. package/build/pan-responder/index.js +37 -56
  50. package/build/platform-color/index.d.ts +1 -1
  51. package/build/platform-color/index.js +11 -4
  52. package/build/process-background-image/index.js +30 -566
  53. package/build/process-background-longhands.d.ts +4 -0
  54. package/build/process-background-longhands.js +44 -0
  55. package/build/process-box-shadow/index.js +23 -187
  56. package/build/process-filter.js +27 -300
  57. package/build/process-transform/index.d.ts +1 -1
  58. package/build/process-transform/index.js +25 -107
  59. package/build/process-transform-origin/index.d.ts +1 -1
  60. package/build/process-transform-origin/index.js +29 -102
  61. package/build/registry.d.ts +36 -0
  62. package/build/registry.js +73 -0
  63. package/build/sound-manager/index.d.ts +3 -0
  64. package/build/sound-manager/index.js +36 -0
  65. package/build/structured-style.d.ts +10 -0
  66. package/build/structured-style.js +180 -0
  67. package/build/style-registry/index.d.ts +14 -0
  68. package/build/style-registry/index.js +60 -11
  69. package/build/styles.d.ts +5 -1
  70. package/build/surface.d.ts +31 -2
  71. package/build/surface.js +138 -42
  72. package/build/text-input-state.d.ts +1 -0
  73. package/build/text-input-state.js +17 -3
  74. package/build/tree-host.d.ts +307 -0
  75. package/build/tree-host.js +211 -0
  76. package/build/view-config.js +4 -4
  77. package/codegen-specs/NativeSymbioteEngine.ts +27 -0
  78. package/cpp/SymbioteDebug.cpp +51 -0
  79. package/cpp/SymbioteDebug.h +54 -0
  80. package/cpp/SymbioteEngineBindings.cpp +232 -0
  81. package/cpp/SymbioteEngineBindings.h +59 -0
  82. package/cpp/SymbioteFabricProps.cpp +2619 -0
  83. package/cpp/SymbioteFabricProps.h +223 -0
  84. package/cpp/SymbioteTree.cpp +2478 -0
  85. package/cpp/SymbioteTree.h +257 -0
  86. package/ios/SymbioteEngineModule.h +25 -0
  87. package/ios/SymbioteEngineModule.mm +44 -0
  88. package/package.json +31 -3
  89. package/react-native.config.cjs +23 -0
  90. package/symbiote-engine.podspec +42 -0
  91. package/build/animated/bezier.d.ts +0 -1
  92. package/build/animated/bezier.js +0 -102
  93. package/build/commit.d.ts +0 -49
  94. package/build/commit.js +0 -1030
  95. package/build/tags.d.ts +0 -2
  96. package/build/tags.js +0 -40
@@ -29,7 +29,7 @@ export interface IPanResponderCallbacks {
29
29
  onPanResponderTerminationRequest?: IActiveCallback;
30
30
  onShouldBlockNativeResponder?: IActiveCallback;
31
31
  }
32
- export interface IGestureResponderHandlers {
32
+ export type IGestureResponderHandlers = {
33
33
  onStartShouldSetResponder: (event: ISymbioteEvent) => boolean;
34
34
  onStartShouldSetResponderCapture: (event: ISymbioteEvent) => boolean;
35
35
  onMoveShouldSetResponder: (event: ISymbioteEvent) => boolean;
@@ -42,7 +42,7 @@ export interface IGestureResponderHandlers {
42
42
  onResponderRelease: (event: ISymbioteEvent) => void;
43
43
  onResponderTerminate: (event: ISymbioteEvent) => void;
44
44
  onResponderTerminationRequest: (event: ISymbioteEvent) => boolean;
45
- }
45
+ };
46
46
  export interface IPanResponderInstance {
47
47
  panHandlers: IGestureResponderHandlers;
48
48
  getInteractionHandle: () => number | null;
@@ -12,6 +12,8 @@
12
12
  // changed ones on `changedTouches`), so the centroid/velocity math here reads
13
13
  // those directly while keeping RN's accumulate-deltas-over-time behavior.
14
14
  import { dlog } from '../debug.js';
15
+ // @ts-expect-error - untyped Flow source. Metro compiles it; vitest.config.ts strips the types.
16
+ import TouchHistoryMath from 'react-native/Libraries/Interaction/TouchHistoryMath';
15
17
  import { isRecord } from '../type-guards.js';
16
18
  const SINGLE_TOUCH_COUNT = 1;
17
19
  // onShouldBlockNativeResponder defaults to true (RN: block native by default).
@@ -81,15 +83,6 @@ function mostRecentTimestamp(touches) {
81
83
  // that moved this frame, via each touch's own previous->current delta. Headless callers
82
84
  // that invoke the handlers directly (no shared, no store) carry no touchHistory and fall
83
85
  // back to the centroid path below, which keeps single-touch behavior.
84
- function isTouchRecord(value) {
85
- return (isRecord(value) &&
86
- typeof value.touchActive === 'boolean' &&
87
- typeof value.currentPageX === 'number' &&
88
- typeof value.currentPageY === 'number' &&
89
- typeof value.currentTimeStamp === 'number' &&
90
- typeof value.previousPageX === 'number' &&
91
- typeof value.previousPageY === 'number');
92
- }
93
86
  function isTouchHistory(value) {
94
87
  return (isRecord(value) &&
95
88
  Array.isArray(value.touchBank) &&
@@ -101,58 +94,31 @@ function touchHistoryOf(event) {
101
94
  const raw = event.nativeEvent.touchHistory;
102
95
  return isTouchHistory(raw) ? raw : undefined;
103
96
  }
104
- // Ported from RN Interaction/TouchHistoryMath.js:centroidDimension (lines 30-85). Mean
105
- // of one coordinate over the touches that moved after `touchesChangedAfter`, taking each
106
- // touch's current or previous position. The single-active-touch fast path uses a strict
107
- // `>`; the multi-touch scan uses `>=`, both kept from RN.
108
- function centroidDimension(touchHistory, touchesChangedAfter, isXAxis, ofCurrent) {
109
- const { touchBank } = touchHistory;
110
- let total = 0;
111
- let count = 0;
112
- const single = touchHistory.numberActiveTouches === SINGLE_TOUCH_COUNT
113
- ? touchBank[touchHistory.indexOfSingleActiveTouch]
114
- : undefined;
115
- if (isTouchRecord(single)) {
116
- if (single.touchActive && single.currentTimeStamp > touchesChangedAfter) {
117
- total += dimensionOf(single, isXAxis, ofCurrent);
118
- count = 1;
119
- }
120
- }
121
- else {
122
- for (const record of touchBank) {
123
- if (isTouchRecord(record) &&
124
- record.touchActive &&
125
- record.currentTimeStamp >= touchesChangedAfter) {
126
- total += dimensionOf(record, isXAxis, ofCurrent);
127
- count++;
128
- }
129
- }
130
- }
131
- return count > 0 ? total / count : NO_CENTROID;
132
- }
133
- const NO_CENTROID = -1;
134
- function dimensionOf(record, isXAxis, ofCurrent) {
135
- if (ofCurrent)
136
- return isXAxis ? record.currentPageX : record.currentPageY;
137
- return isXAxis ? record.previousPageX : record.previousPageY;
138
- }
97
+ // The six readings PanResponder needs, under this file's shorter names. Upstream's own module is
98
+ // imported rather than re-derived: it has no dependencies, no native access, and its two scans -
99
+ // a strict `>` on the single-active-touch fast path, `>=` on the multi-touch loop - are
100
+ // load-bearing and easy to reproduce subtly wrong.
101
+ //
102
+ // The per-record runtime guard that used to sit here went with it. It was forced by `nativeEvent`
103
+ // being a Record<string, unknown>; `touchHistoryOf` above already validates the bank at the
104
+ // boundary, and the records inside it are written by our own touch-history.ts.
139
105
  function currentCentroidXOfChanged(touchHistory, after) {
140
- return centroidDimension(touchHistory, after, true, true);
106
+ return TouchHistoryMath.currentCentroidXOfTouchesChangedAfter(touchHistory, after);
141
107
  }
142
108
  function currentCentroidYOfChanged(touchHistory, after) {
143
- return centroidDimension(touchHistory, after, false, true);
109
+ return TouchHistoryMath.currentCentroidYOfTouchesChangedAfter(touchHistory, after);
144
110
  }
145
111
  function previousCentroidXOfChanged(touchHistory, after) {
146
- return centroidDimension(touchHistory, after, true, false);
112
+ return TouchHistoryMath.previousCentroidXOfTouchesChangedAfter(touchHistory, after);
147
113
  }
148
114
  function previousCentroidYOfChanged(touchHistory, after) {
149
- return centroidDimension(touchHistory, after, false, false);
115
+ return TouchHistoryMath.previousCentroidYOfTouchesChangedAfter(touchHistory, after);
150
116
  }
151
117
  function currentCentroidXAll(touchHistory) {
152
- return centroidDimension(touchHistory, 0, true, true);
118
+ return TouchHistoryMath.currentCentroidX(touchHistory);
153
119
  }
154
120
  function currentCentroidYAll(touchHistory) {
155
- return centroidDimension(touchHistory, 0, false, true);
121
+ return TouchHistoryMath.currentCentroidY(touchHistory);
156
122
  }
157
123
  // RN PanResponder._updateGestureStateOnMove (Interaction/PanResponder.js lines 330-366):
158
124
  // accumulate the centroid change of touches that moved after `_accountsForMovesUpTo`,
@@ -250,9 +216,11 @@ const PanResponder = {
250
216
  };
251
217
  const panHandlers = {
252
218
  onStartShouldSetResponder(event) {
253
- return config.onStartShouldSetPanResponder === undefined
219
+ const wants = config.onStartShouldSetPanResponder === undefined
254
220
  ? false
255
221
  : config.onStartShouldSetPanResponder(event, gestureState);
222
+ dlog(`PanResponder startShouldSet -> ${wants}`);
223
+ return wants;
256
224
  },
257
225
  onMoveShouldSetResponder(event) {
258
226
  return config.onMoveShouldSetPanResponder === undefined
@@ -299,8 +267,17 @@ const PanResponder = {
299
267
  : centroidY(touches);
300
268
  gestureState.dx = 0;
301
269
  gestureState.dy = 0;
302
- // The grant frame is already accounted for, so the first move's velocity
303
- // is measured from here, not from time 0.
270
+ // Two deliberate divergences from upstream's onResponderGrant (PanResponder.js:459),
271
+ // which sets x0/y0/dx/dy and nothing else. Both are decisions, not drift - recorded here
272
+ // because the first one spent months living in a comment nobody audited against upstream.
273
+ //
274
+ // 1. The grant frame is already accounted for, so the first move's velocity is measured
275
+ // from here, not from time 0. packages/navigation's drawer reads vx through
276
+ // resolveSwipeIntent with a 0.5 flick threshold, and upstream's version makes the first
277
+ // move look instantaneous.
278
+ // 2. numberActiveTouches is refreshed at grant. Upstream leaves whatever the previous
279
+ // gesture's onResponderEnd left behind - usually 0 - so an app reading it inside
280
+ // onPanResponderGrant gets a stale count there and a correct one everywhere else.
304
281
  gestureState._accountsForMovesUpTo = frameTimestampOf(event, touches);
305
282
  gestureState.numberActiveTouches =
306
283
  touchHistory?.numberActiveTouches ?? touches.length;
@@ -320,12 +297,16 @@ const PanResponder = {
320
297
  },
321
298
  onResponderMove(event) {
322
299
  const touches = readTouches(event);
323
- // Same duplicate-frame guard as the capture path.
324
- if (gestureState._accountsForMovesUpTo ===
325
- frameTimestampOf(event, touches)) {
300
+ const frame = frameTimestampOf(event, touches);
301
+ // Same duplicate-frame guard as the capture path. Logged on BOTH sides: a frame
302
+ // stamp that never advances swallows every move while the handlers themselves keep
303
+ // firing, so the app looks inert with nothing to see from the outside.
304
+ if (gestureState._accountsForMovesUpTo === frame) {
305
+ dlog(`PanResponder move SWALLOWED frame=${frame} touches=${touches.length} history=${touchHistoryOf(event) === undefined ? 'none' : 'yes'}`);
326
306
  return;
327
307
  }
328
308
  updateGestureStateOnMove(gestureState, event, touches);
309
+ dlog(`PanResponder move frame=${frame} dx=${gestureState.dx} dy=${gestureState.dy}`);
329
310
  config.onPanResponderMove?.(event, gestureState);
330
311
  },
331
312
  onResponderEnd(event) {
@@ -13,7 +13,7 @@ export interface IOpaqueColorValue {
13
13
  readonly highContrastDark?: IColorValue;
14
14
  };
15
15
  }
16
- export type IColorValue = string | IOpaqueColorValue;
16
+ export type IColorValue = string | number | IOpaqueColorValue;
17
17
  export declare function PlatformColor(...names: string[]): IOpaqueColorValue;
18
18
  export declare function DynamicColorIOS(tuple: IDynamicColorIOSTuple): IOpaqueColorValue;
19
19
  export declare function isOpaqueColorValue(value: unknown): value is IOpaqueColorValue;
@@ -43,9 +43,16 @@ export function setColorProcessor(process) {
43
43
  export function processColor(color) {
44
44
  return colorProcessor(color);
45
45
  }
46
- // A color-keyed value the platform processor must convert before Fabric: a CSS string, or an
47
- // opaque PlatformColor / DynamicColorIOS object. Numbers (already platform ints) and undefined
48
- // are left untouched.
46
+ // A color-keyed value the platform processor must convert before Fabric: a CSS string, a numeric
47
+ // literal, or an opaque PlatformColor / DynamicColorIOS object. `undefined` is left untouched.
48
+ //
49
+ // A number used to be excluded here on the reasoning that it was "already a resolved platform
50
+ // int". Nothing in the pipeline produces one: the CSS parser emits hex STRINGS, and fabricProps
51
+ // builds a fresh payload object instead of writing the resolved value back into node.props, so a
52
+ // number reaching this guard is always the author's own literal. Excluding it committed
53
+ // `color: 0xff0000ff` (opaque red, rrggbbaa) unrotated, and native reads that as aarrggbb: blue.
49
54
  export function isProcessableColor(value) {
50
- return typeof value === 'string' || isOpaqueColorValue(value);
55
+ return (typeof value === 'string' ||
56
+ typeof value === 'number' ||
57
+ isOpaqueColorValue(value));
51
58
  }