@symbiote-native/engine 0.1.5 → 0.1.7
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/build/accessibility-info/index.android.js +1 -1
- package/build/accessibility-info/index.ios.js +1 -1
- package/build/action-sheet-ios/index.js +1 -1
- package/build/alert/index.android.js +1 -1
- package/build/alert/index.ios.js +1 -1
- package/build/animated/index.d.ts +1 -1
- package/build/animated/index.js +1 -1
- package/build/animated/native/native-animated.js +1 -1
- package/build/app-registry/index.js +1 -1
- package/build/app-state/index.js +1 -1
- package/build/appearance/index.js +1 -1
- package/build/back-handler/index.js +1 -1
- package/build/commit.js +18 -4
- package/build/dimensions/index.js +1 -1
- package/build/fabric-props.js +1 -1
- package/build/i18n-manager/index.js +1 -1
- package/build/image-loader.js +1 -1
- package/build/index.d.ts +1 -1
- package/build/index.js +3 -3
- package/build/keyboard/index.js +1 -1
- package/build/layout-animation/index.js +1 -1
- package/build/linking/shared.js +1 -1
- package/build/{native-modules.d.ts → native-modules/index.d.ts} +1 -1
- package/build/{native-modules.js → native-modules/index.js} +3 -3
- package/build/node.d.ts +1 -0
- package/build/node.js +27 -1
- package/build/permissions-android/index.js +1 -1
- package/build/platform/shared.js +1 -1
- package/build/process-background-image/index.js +1 -1
- package/build/process-box-shadow/index.js +1 -1
- package/build/process-filter.js +1 -1
- package/build/settings/index.js +1 -1
- package/build/share/index.android.js +1 -1
- package/build/share/index.ios.js +1 -1
- package/build/status-bar/index.android.js +2 -2
- package/build/status-bar/index.ios.js +1 -1
- package/build/style-registry/scope/index.d.ts +3 -0
- package/build/style-registry/scope/index.js +41 -0
- package/build/style-sheet/index.js +1 -1
- package/build/toast-android/index.js +1 -1
- package/build/touch-history/index.d.ts +23 -0
- package/build/touch-history/index.js +150 -0
- package/build/type-guards/index.d.ts +4 -0
- package/build/type-guards/index.js +15 -0
- package/build/vibration/shared.js +1 -1
- package/package.json +3 -2
- /package/build/animated/{animated-component-shared.d.ts → shared.d.ts} +0 -0
- /package/build/animated/{animated-component-shared.js → shared.js} +0 -0
- /package/build/{platform-color.d.ts → platform-color/index.d.ts} +0 -0
- /package/build/{platform-color.js → platform-color/index.js} +0 -0
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// device-event NAMES also differ from iOS (e.g. screen-reader is `touchExplorationDidChange`,
|
|
7
7
|
// reduce-motion is `reduceMotionDidChange`). Metro picks this on an Android host. Mirrors
|
|
8
8
|
// RN's AccessibilityInfo.js Android branches.
|
|
9
|
-
import { createDeviceEventModule } from '../native-modules.js';
|
|
9
|
+
import { createDeviceEventModule } from '../native-modules/index.js';
|
|
10
10
|
import {} from '../native-events.js';
|
|
11
11
|
import { dlog } from '../debug.js';
|
|
12
12
|
import { isBoolean, routeSendAccessibilityEvent, } from './shared.js';
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// NativeEventEmitter and re-broadcasts to JS listeners. Metro picks this on an iOS host;
|
|
7
7
|
// the bare accessibility-info.ts re-exports it as the default for tsc / tsx / headless.
|
|
8
8
|
// Mirrors RN's AccessibilityInfo.js iOS branches.
|
|
9
|
-
import { createDeviceEventModule } from '../native-modules.js';
|
|
9
|
+
import { createDeviceEventModule } from '../native-modules/index.js';
|
|
10
10
|
import {} from '../native-events.js';
|
|
11
11
|
import { dlog } from '../debug.js';
|
|
12
12
|
import { isBoolean, routeSendAccessibilityEvent, } from './shared.js';
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
// processing in @symbiote-native/engine, so we do NOT process colors here; options pass
|
|
18
18
|
// through untouched. The canary needs no colors; revisit when wiring real colors.
|
|
19
19
|
import { dlog } from '../debug.js';
|
|
20
|
-
import { getNativeModule } from '../native-modules.js';
|
|
20
|
+
import { getNativeModule } from '../native-modules/index.js';
|
|
21
21
|
export const ACTION_SHEET_MANAGER = 'ActionSheetManager';
|
|
22
22
|
// The static imperative API RN exposes, mirrored as a static-method object.
|
|
23
23
|
export const ActionSheetIOS = {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
//
|
|
18
18
|
// Non-throwing, like StatusBar: a missing native module is a no-op, never a crash.
|
|
19
19
|
import { dlog } from '../debug.js';
|
|
20
|
-
import { getNativeModule } from '../native-modules.js';
|
|
20
|
+
import { getNativeModule } from '../native-modules/index.js';
|
|
21
21
|
import { isRecord } from '../type-guards.js';
|
|
22
22
|
import { DEFAULT_POSITIVE_TEXT, normalizeButtons, } from './shared.js';
|
|
23
23
|
const DIALOG_MANAGER = 'DialogManagerAndroid';
|
package/build/alert/index.ios.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
// Non-throwing, like StatusBar: a missing native module is a no-op, never a crash (on a
|
|
13
13
|
// device the module may be absent).
|
|
14
14
|
import { dlog } from '../debug.js';
|
|
15
|
-
import { getNativeModule } from '../native-modules.js';
|
|
15
|
+
import { getNativeModule } from '../native-modules/index.js';
|
|
16
16
|
const ALERT_MANAGER = 'AlertManager';
|
|
17
17
|
// prompt builds the native args, assigns each button its array index as id, and dispatches
|
|
18
18
|
// the matching button's onPress when the native callback returns that id. Non-throwing: no
|
|
@@ -17,4 +17,4 @@ export { nativeAnimated, isNativeAnimatedAvailable, type INativeNodeConfig, type
|
|
|
17
17
|
export { AnimatedProps } from './props';
|
|
18
18
|
export { AnimatedStyle, AnimatedTransform } from './style';
|
|
19
19
|
export { AnimatedMock } from './mock';
|
|
20
|
-
export { reduceProps, isAnimatedNode, readPassthroughStyle, resolveHostNode
|
|
20
|
+
export { reduceProps, isAnimatedNode, readPassthroughStyle, resolveHostNode } from './shared';
|
package/build/animated/index.js
CHANGED
|
@@ -25,4 +25,4 @@ export { AnimatedProps } from './props.js';
|
|
|
25
25
|
export { AnimatedStyle, AnimatedTransform } from './style.js';
|
|
26
26
|
export { AnimatedMock } from './mock.js';
|
|
27
27
|
// Framework-agnostic createAnimatedComponent helpers. Both adapters import them.
|
|
28
|
-
export { reduceProps, isAnimatedNode, readPassthroughStyle, resolveHostNode
|
|
28
|
+
export { reduceProps, isAnimatedNode, readPassthroughStyle, resolveHostNode } from './shared.js';
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// module is resolved lazily on first use, so importing this file headless (no
|
|
11
11
|
// native host) is inert until a native-driven animation actually starts.
|
|
12
12
|
import { dlog } from '../../debug.js';
|
|
13
|
-
import { getNativeModule } from '../../native-modules.js';
|
|
13
|
+
import { getNativeModule } from '../../native-modules/index.js';
|
|
14
14
|
import { NativeEventEmitter } from '../../native-events.js';
|
|
15
15
|
import { isRecord } from '../../type-guards.js';
|
|
16
16
|
// iOS bridgeless registers the Turbo variant; the legacy name is the fallback.
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// registrar. Reached the same way shared reaches processColor: a dependency-injected seam
|
|
12
12
|
// (setHostRegistrar), so the core stays react-native-free and the app glue wires the host once
|
|
13
13
|
// at startup.
|
|
14
|
-
import { getNativeModule } from '../native-modules.js';
|
|
14
|
+
import { getNativeModule } from '../native-modules/index.js';
|
|
15
15
|
import { dlog } from '../debug.js';
|
|
16
16
|
const HEADLESS_TASK_MODULE = 'HeadlessJsTaskSupport';
|
|
17
17
|
// Builds one adapter's AppRegistry. `runnableFor` is the sole framework-specific seam: given a
|
package/build/app-state/index.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// NativeEventEmitter bound to the AppState native module and maps them onto the
|
|
6
6
|
// public 'change'/'memoryWarning'/'focus'/'blur' listeners, mirroring RN's
|
|
7
7
|
// Libraries/AppState/AppState.js.
|
|
8
|
-
import { createDeviceEventModule } from '../native-modules.js';
|
|
8
|
+
import { createDeviceEventModule } from '../native-modules/index.js';
|
|
9
9
|
import {} from '../native-events.js';
|
|
10
10
|
import { dlog } from '../debug.js';
|
|
11
11
|
// The native module name RN registers AppState under, confirmed from its spec
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// native module (which RN keys its appearance events off of) and re-broadcasts to
|
|
5
5
|
// JS listeners as a plain `{ colorScheme }` payload. Mirrors RN's
|
|
6
6
|
// Libraries/Utilities/Appearance.js, slimmed to the parts we need.
|
|
7
|
-
import { createDeviceEventModule } from '../native-modules.js';
|
|
7
|
+
import { createDeviceEventModule } from '../native-modules/index.js';
|
|
8
8
|
import {} from '../native-events.js';
|
|
9
9
|
import { dlog } from '../debug.js';
|
|
10
10
|
// The native module name RN registers the appearance module under, confirmed from
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// Android-only by intent: iOS has no hardware back button, so the whole module
|
|
7
7
|
// degrades to a no-op when the native module is absent, matching RN's
|
|
8
8
|
// BackHandler.ios.js stub. Mirrors RN's Libraries/Utilities/BackHandler.android.js.
|
|
9
|
-
import { createDeviceEventModule } from '../native-modules.js';
|
|
9
|
+
import { createDeviceEventModule } from '../native-modules/index.js';
|
|
10
10
|
import {} from '../native-events.js';
|
|
11
11
|
import { dlog } from '../debug.js';
|
|
12
12
|
// The native module name RN registers this under. NOTE: this is the name the
|
package/build/commit.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// references to specific child handles. That bubble is inherent to a persistent
|
|
14
14
|
// tree and is exactly what React's own Fabric renderer does.
|
|
15
15
|
import { getSlot, } from './fabric.js';
|
|
16
|
-
import { createElement, isAnchor, VIRTUAL_TEXT_COMPONENT } from './node.js';
|
|
16
|
+
import { createElement, debugNodeId, isAnchor, VIRTUAL_TEXT_COMPONENT, } from './node.js';
|
|
17
17
|
import { dlog, isDebug } from './debug.js';
|
|
18
18
|
import { flattenStyle } from './style/index.js';
|
|
19
19
|
import { nextTag } from './tags.js';
|
|
@@ -23,7 +23,7 @@ import { isRecord } from './type-guards.js';
|
|
|
23
23
|
// processColor/setColorProcessor now live in ./platform-color (the stable color-processing
|
|
24
24
|
// leaf every color-touching module imports from); re-exported here so nothing outside this
|
|
25
25
|
// module needs to change its import path.
|
|
26
|
-
export { processColor, setColorProcessor } from './platform-color.js';
|
|
26
|
+
export { processColor, setColorProcessor } from './platform-color/index.js';
|
|
27
27
|
// Per-commit work counters, surfaced via dlog so a device run can prove the
|
|
28
28
|
// engine is incremental (created=0 with clones after the first mount).
|
|
29
29
|
const stats = { created: 0, cloneProps: 0, cloneChildren: 0, reused: 0 };
|
|
@@ -210,6 +210,13 @@ function reconcile(slot, node, rootTag, hasTextAncestor, renderableParent, force
|
|
|
210
210
|
slot.appendChild(handle, reconcile(slot, child, rootTag, childInText, node, true).handle);
|
|
211
211
|
}
|
|
212
212
|
logScrollChildren(node, viewName, tag);
|
|
213
|
+
// Investigation instrumentation (search-bar-ref "node not committed" bug): scoped to RNS* so
|
|
214
|
+
// it can be directly compared against the ref-attach log in stack.ts and the dispatch-miss
|
|
215
|
+
// log below — same debugNodeId on both sides proves/disproves an identity mismatch. Kept
|
|
216
|
+
// behind DEBUG per <keep_logs_gate_behind_DEBUG>, never removed.
|
|
217
|
+
if (viewName.startsWith('RNS')) {
|
|
218
|
+
dlog(`mirror.set (create) node=${debugNodeId(node)} tag=${tag} view=${viewName}`);
|
|
219
|
+
}
|
|
213
220
|
mirror.set(node, {
|
|
214
221
|
handle,
|
|
215
222
|
tag,
|
|
@@ -259,6 +266,11 @@ function reconcile(slot, node, rootTag, hasTextAncestor, renderableParent, force
|
|
|
259
266
|
guardSerializable(propsDiff, viewName, committed.tag);
|
|
260
267
|
handle = slot.cloneNodeWithNewProps(committed.handle, propsDiff);
|
|
261
268
|
}
|
|
269
|
+
// Investigation instrumentation (search-bar-ref "node not committed" bug): see the create-path
|
|
270
|
+
// dlog above. Kept behind DEBUG per <keep_logs_gate_behind_DEBUG>, never removed.
|
|
271
|
+
if (viewName.startsWith('RNS')) {
|
|
272
|
+
dlog(`mirror.set (update) node=${debugNodeId(node)} tag=${committed.tag} view=${viewName}`);
|
|
273
|
+
}
|
|
262
274
|
// The clone keeps the node's family, so its reactTag is unchanged; carry it.
|
|
263
275
|
mirror.set(node, {
|
|
264
276
|
handle,
|
|
@@ -428,10 +440,12 @@ export function getNativeNode(node) {
|
|
|
428
440
|
export function dispatchViewCommand(node, commandName, args) {
|
|
429
441
|
const record = mirror.get(node);
|
|
430
442
|
if (record === undefined) {
|
|
431
|
-
|
|
443
|
+
// node=... compares directly against the mirror.set logs above (same debugNodeId scheme) to
|
|
444
|
+
// prove/disprove a node-identity mismatch — see the search-bar-ref investigation note there.
|
|
445
|
+
dlog(`dispatchViewCommand "${commandName}" skipped: node not committed (node=${debugNodeId(node)} component=${node.component})`);
|
|
432
446
|
return;
|
|
433
447
|
}
|
|
434
|
-
dlog(`dispatchViewCommand "${commandName}"`);
|
|
448
|
+
dlog(`dispatchViewCommand "${commandName}" (node=${debugNodeId(node)})`);
|
|
435
449
|
getSlot().dispatchCommand(record.handle, commandName, args);
|
|
436
450
|
}
|
|
437
451
|
// Emit an accessibility event (focus/click/viewHoverEnter/windowStateChange) at a node's
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// We resolve it through the same generic native-module bridge as Platform
|
|
12
12
|
// (getNativeModule), so this module stays importable headless before a fake proxy
|
|
13
13
|
// is installed.
|
|
14
|
-
import { createDeviceEventModule } from '../native-modules.js';
|
|
14
|
+
import { createDeviceEventModule } from '../native-modules/index.js';
|
|
15
15
|
import {} from '../native-events.js';
|
|
16
16
|
import { dlog } from '../debug.js';
|
|
17
17
|
import { isRecord } from '../type-guards.js';
|
package/build/fabric-props.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { RAW_TEXT_COMPONENT } from './node.js';
|
|
8
8
|
import { flattenStyle } from './style/index.js';
|
|
9
9
|
import { registeredProcessor } from './registry.js';
|
|
10
|
-
import { isProcessableColor, processColor } from './platform-color.js';
|
|
10
|
+
import { isProcessableColor, processColor } from './platform-color/index.js';
|
|
11
11
|
import { processBoxShadow } from './process-box-shadow/index.js';
|
|
12
12
|
import { processFilter } from './process-filter.js';
|
|
13
13
|
import { processTransformOrigin } from './process-transform-origin/index.js';
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// forceRTL(forceRTL: boolean)
|
|
14
14
|
// swapLeftAndRightInRTL(flipStyles: boolean)
|
|
15
15
|
import { dlog } from '../debug.js';
|
|
16
|
-
import { getNativeModule } from '../native-modules.js';
|
|
16
|
+
import { getNativeModule } from '../native-modules/index.js';
|
|
17
17
|
import { isBoolean } from '../type-guards.js';
|
|
18
18
|
// The iOS native module name RN registers this under (the same name on both
|
|
19
19
|
// platforms). A module name is only provable on a real host (a headless fake
|
package/build/image-loader.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
// native bridge).
|
|
16
16
|
import { dlog } from './debug.js';
|
|
17
17
|
import { resolveImageSource } from './image-source-resolver.js';
|
|
18
|
-
import { getNativeModule } from './native-modules.js';
|
|
18
|
+
import { getNativeModule } from './native-modules/index.js';
|
|
19
19
|
import { Platform } from './platform';
|
|
20
20
|
import { isNumber } from './type-guards.js';
|
|
21
21
|
// The iOS native module name RN registers this under (NativeImageLoaderIOS.js resolves
|
package/build/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { createElement, createRawText, createAnchor, isAnchor, appendChild, insertBefore, removeChild, setProp, setEventListener, routeProp, getExplicitStyle, setText, isSymbioteNode, isSymbioteEvent, RAW_TEXT_COMPONENT, } from './node';
|
|
1
|
+
export { createElement, createRawText, createAnchor, isAnchor, appendChild, insertBefore, removeChild, setProp, setEventListener, routeProp, getExplicitStyle, setText, isSymbioteNode, isSymbioteEvent, RAW_TEXT_COMPONENT, debugNodeId, } from './node';
|
|
2
2
|
export { isEventFor } from './view-config';
|
|
3
3
|
export { registerComponent, setNativeViewConfigSource } from './registry';
|
|
4
4
|
export { isRecord } from './type-guards';
|
package/build/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Every framework adapter drives this tiny mutation API; all Fabric-specific
|
|
3
3
|
// logic (tag allocation, view-name resolution, clone-on-write, event
|
|
4
4
|
// normalization) lives behind it, in one place.
|
|
5
|
-
export { createElement, createRawText, createAnchor, isAnchor, appendChild, insertBefore, removeChild, setProp, setEventListener, routeProp, getExplicitStyle, setText, isSymbioteNode, isSymbioteEvent, RAW_TEXT_COMPONENT, } from './node.js';
|
|
5
|
+
export { createElement, createRawText, createAnchor, isAnchor, appendChild, insertBefore, removeChild, setProp, setEventListener, routeProp, getExplicitStyle, setText, isSymbioteNode, isSymbioteEvent, RAW_TEXT_COMPONENT, debugNodeId, } from './node.js';
|
|
6
6
|
export { isEventFor } from './view-config.js';
|
|
7
7
|
export { registerComponent, setNativeViewConfigSource } from './registry.js';
|
|
8
8
|
// Real cross-package consumer: core/components' KeyboardAvoidingView render narrows
|
|
@@ -21,7 +21,7 @@ export { setColorProcessor, processColor, dispatchViewCommand, sendAccessibility
|
|
|
21
21
|
// the Vue renderer's createElement): the imperative measure/setNativeProps/focus API. Lives
|
|
22
22
|
// here because it depends only on engine internals, so all adapters inherit it identically.
|
|
23
23
|
export { toPublicInstance } from './host-instance/index.js';
|
|
24
|
-
export { PlatformColor, DynamicColorIOS, isOpaqueColorValue } from './platform-color.js';
|
|
24
|
+
export { PlatformColor, DynamicColorIOS, isOpaqueColorValue } from './platform-color/index.js';
|
|
25
25
|
// CSS-style processors (boxShadow/filter): RN parses these in JS before native because
|
|
26
26
|
// enableNativeCSSParsing() defaults to false. Exported so an adapter / test can reuse them.
|
|
27
27
|
export { processBoxShadow } from './process-box-shadow/index.js';
|
|
@@ -37,7 +37,7 @@ export { registerStyles, resolveClassName, clearGlobalStyles, isClassNameValue,
|
|
|
37
37
|
export { scopeClassName } from './style-registry/scope.js';
|
|
38
38
|
export { Platform } from './platform';
|
|
39
39
|
export { dlog, isDebug } from './debug.js';
|
|
40
|
-
export { getNativeModule, getEnforcingNativeModule } from './native-modules.js';
|
|
40
|
+
export { getNativeModule, getEnforcingNativeModule } from './native-modules/index.js';
|
|
41
41
|
export { installDeviceEventHub, NativeEventEmitter, setDeviceEventSource } from './native-events.js';
|
|
42
42
|
export { AnimatedNode, AnimatedWithChildren, AnimatedValue, AnimatedValueXY, AnimatedColor, AnimatedInterpolation, AnimatedAddition, AnimatedSubtraction, AnimatedMultiplication, AnimatedDivision, AnimatedModulo, AnimatedDiffClamp, AnimatedEvent, add, subtract, multiply, divide, modulo, diffClamp, event, forkEvent, unforkEvent, attachNativeEvent, attachNativeEventHandler, flushValue, Easing, createNumericInterpolation, checkValidRanges, TimingAnimation, SpringAnimation, DecayAnimation, AnimatedTracking, timing, spring, decay, parallel, sequence, stagger, loop, delay, nativeAnimated, isNativeAnimatedAvailable, AnimatedProps, AnimatedStyle, AnimatedTransform, AnimatedMock, reduceProps, isAnimatedNode, readPassthroughStyle, resolveHostNode, } from './animated/index.js';
|
|
43
43
|
export { getSlot } from './fabric.js';
|
package/build/keyboard/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// subscribes through a NativeEventEmitter bound to the KeyboardObserver native
|
|
4
4
|
// module, which RN keys its keyboard events off of. Mirrors RN's
|
|
5
5
|
// Libraries/Components/Keyboard/Keyboard.js, slimmed to the parts we need.
|
|
6
|
-
import { createDeviceEventModule } from '../native-modules.js';
|
|
6
|
+
import { createDeviceEventModule } from '../native-modules/index.js';
|
|
7
7
|
import {} from '../native-events.js';
|
|
8
8
|
import { dlog } from '../debug.js';
|
|
9
9
|
import { blurTextInput, currentlyFocusedInput } from '../text-input-state.js';
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
//
|
|
8
8
|
// It is a native-bridge consumer (RN's `Platform`/`StyleSheet` purity split puts
|
|
9
9
|
// these in the adapter, not in shared, like Keyboard / StatusBar).
|
|
10
|
-
import { getNativeModule } from '../native-modules.js';
|
|
10
|
+
import { getNativeModule } from '../native-modules/index.js';
|
|
11
11
|
import { dlog } from '../debug.js';
|
|
12
12
|
import { isRecord } from '../type-guards.js';
|
|
13
13
|
// ---- native module routing ----------------------------------------------
|
package/build/linking/shared.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// the base linking.ts re-exports the iOS build for web/headless. There is no runtime
|
|
9
9
|
// `Platform.OS` read; the filename is the selector.
|
|
10
10
|
import { installDeviceEventHub, NativeEventEmitter, } from '../native-events.js';
|
|
11
|
-
import { getNativeModule } from '../native-modules.js';
|
|
11
|
+
import { getNativeModule } from '../native-modules/index.js';
|
|
12
12
|
import { dlog } from '../debug.js';
|
|
13
13
|
// The one event symbiote observes: an incoming deep link. RN's LinkingEventDefinitions.
|
|
14
14
|
const URL_EVENT = 'url';
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
// KeyboardObserver, ...). Third-party RN packages import `TurboModuleRegistry` from
|
|
9
9
|
// `'react-native'` and read the same global themselves; they do not go through
|
|
10
10
|
// here.
|
|
11
|
-
import { dlog } from '
|
|
12
|
-
import { installDeviceEventHub, NativeEventEmitter, } from '
|
|
13
|
-
import { isRecord } from '
|
|
11
|
+
import { dlog } from '../debug.js';
|
|
12
|
+
import { installDeviceEventHub, NativeEventEmitter, } from '../native-events.js';
|
|
13
|
+
import { isRecord } from '../type-guards.js';
|
|
14
14
|
// The native module value crosses from an untyped HostObject into our types here;
|
|
15
15
|
// the caller vouches for its shape via T (the single trust-boundary narrowing, no
|
|
16
16
|
// per-call `as`). Native modules are always non-null objects.
|
package/build/node.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ export interface ISymbioteNode {
|
|
|
23
23
|
export declare function createElement(component: string, isText?: boolean): ISymbioteNode;
|
|
24
24
|
export declare function createRawText(text: string): ISymbioteNode;
|
|
25
25
|
export declare function isSymbioteNode(value: unknown): value is ISymbioteNode;
|
|
26
|
+
export declare function debugNodeId(node: ISymbioteNode): number;
|
|
26
27
|
export declare const ANCHOR_COMPONENT = "#anchor";
|
|
27
28
|
export declare function createAnchor(): ISymbioteNode;
|
|
28
29
|
export declare function isAnchor(node: ISymbioteNode): boolean;
|
package/build/node.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
// lives here in shared so no adapter re-implements it.
|
|
7
7
|
import { isEventFor } from './view-config.js';
|
|
8
8
|
import { isClassNameValue, resolveClassName } from './style-registry/index.js';
|
|
9
|
+
import { dlog } from './debug.js';
|
|
9
10
|
const BRAND = Symbol('symbiote.node');
|
|
10
11
|
// A node carries the Fabric view name directly, so adding a primitive (Image,
|
|
11
12
|
// ScrollView, TextInput) is just a new string from the adapter, no core change.
|
|
@@ -52,6 +53,21 @@ export function createRawText(text) {
|
|
|
52
53
|
export function isSymbioteNode(value) {
|
|
53
54
|
return typeof value === 'object' && value !== null && BRAND in value;
|
|
54
55
|
}
|
|
56
|
+
// Investigation instrumentation (HeaderOptionsScreen search-bar-ref "node not committed" bug):
|
|
57
|
+
// a WeakMap can't be logged, so this gives every node a small human-readable id, assigned lazily
|
|
58
|
+
// on first call — lets a dlog at ref-attach time and a dlog at commit/dispatch time be compared
|
|
59
|
+
// directly to prove whether they're the SAME node object or two different ones. Kept behind
|
|
60
|
+
// DEBUG per <keep_logs_gate_behind_DEBUG>, never removed.
|
|
61
|
+
const debugIds = new WeakMap();
|
|
62
|
+
let nextDebugId = 1;
|
|
63
|
+
export function debugNodeId(node) {
|
|
64
|
+
let id = debugIds.get(node);
|
|
65
|
+
if (id === undefined) {
|
|
66
|
+
id = nextDebugId++;
|
|
67
|
+
debugIds.set(node, id);
|
|
68
|
+
}
|
|
69
|
+
return id;
|
|
70
|
+
}
|
|
55
71
|
// Vue's runtime-core needs comment/anchor nodes (fragments, v-if, v-for) to track
|
|
56
72
|
// sibling order; Fabric has no such concept. An anchor is a real retained node so
|
|
57
73
|
// insert/nextSibling/parentNode ordering stays correct, but the commit walk SKIPS it
|
|
@@ -166,7 +182,17 @@ export function routeProp(node, key, value) {
|
|
|
166
182
|
}
|
|
167
183
|
if (ON_PREFIX.test(key)) {
|
|
168
184
|
const name = listenerName(key);
|
|
169
|
-
|
|
185
|
+
const isRegisteredEvent = RESPONDER_EVENTS.has(name) || isEventFor(node.component, name);
|
|
186
|
+
// Investigation instrumentation (HeaderOptionsScreen unresponsive-buttons bug): RNS* views
|
|
187
|
+
// derive their events from react-native-screens' own codegen ViewConfig (registry.ts), so an
|
|
188
|
+
// unregistered event silently falls through to setProp below — a dead prop Fabric ignores,
|
|
189
|
+
// indistinguishable from "the button did nothing" at the UI. Scoped to RNS* to avoid noise
|
|
190
|
+
// from the rest of the app. Kept behind DEBUG per <keep_logs_gate_behind_DEBUG>, never removed.
|
|
191
|
+
if (node.component.startsWith('RNS')) {
|
|
192
|
+
dlog(`routeProp: ${node.component} "${key}" -> listener "${name}" ` +
|
|
193
|
+
`registered=${isRegisteredEvent} at t=${Date.now()}`);
|
|
194
|
+
}
|
|
195
|
+
if (isRegisteredEvent) {
|
|
170
196
|
setEventListener(node, name, value);
|
|
171
197
|
return;
|
|
172
198
|
}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
// requestPermission(permission): Promise<string>
|
|
18
18
|
// shouldShowRequestPermissionRationale(permission): Promise<boolean>
|
|
19
19
|
// requestMultiplePermissions(permissions): Promise<{[permission]: string}>
|
|
20
|
-
import { getNativeModule } from '../native-modules.js';
|
|
20
|
+
import { getNativeModule } from '../native-modules/index.js';
|
|
21
21
|
import { dlog } from '../debug.js';
|
|
22
22
|
import { isBoolean } from '../type-guards.js';
|
|
23
23
|
// The native module name RN registers this TurboModule under
|
package/build/platform/shared.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// select precedence, and the device-class getters. The filename is the selector (Metro
|
|
6
6
|
// picks platform.ios / platform.android by host); no Platform.OS read lives here.
|
|
7
7
|
import { dlog } from '../debug.js';
|
|
8
|
-
import { getNativeModule } from '../native-modules.js';
|
|
8
|
+
import { getNativeModule } from '../native-modules/index.js';
|
|
9
9
|
// The native module name RN registers PlatformConstants under, identical on both
|
|
10
10
|
// platforms; only getConstants()'s SHAPE differs, which each platform file guards.
|
|
11
11
|
export const PLATFORM_CONSTANTS = 'PlatformConstants';
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// enableNativeCSSParsing(), which DEFAULTS TO FALSE, so RN's stock path parses the CSS gradient
|
|
4
4
|
// string / structured array in JS and sends only the processed array to native - Fabric's C++
|
|
5
5
|
// never sees the raw string. This restores that missing JS parse.
|
|
6
|
-
import { isOpaqueColorValue, processColor } from '../platform-color.js';
|
|
6
|
+
import { isOpaqueColorValue, processColor } from '../platform-color/index.js';
|
|
7
7
|
import { dlog } from '../debug.js';
|
|
8
8
|
import { isRecord } from '../type-guards.js';
|
|
9
9
|
// RN processBackgroundImage.js: pre-compiled patterns.
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// processed array to native; Fabric's C++ never sees the raw string. symbiote was
|
|
5
5
|
// forwarding the raw value, which native (CSS parsing off) silently ignores: shadow
|
|
6
6
|
// rendered nothing. This restores the missing JS parse + per-color processColor.
|
|
7
|
-
import { isOpaqueColorValue, processColor } from '../platform-color.js';
|
|
7
|
+
import { isOpaqueColorValue, processColor } from '../platform-color/index.js';
|
|
8
8
|
import { dlog } from '../debug.js';
|
|
9
9
|
// RN processBoxShadow.js:16-19: split args only on the delimiters that are NOT inside
|
|
10
10
|
// a parenthesized color like rgba(0,0,0,1).
|
package/build/process-filter.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// structured result. symbiote forwarded the raw value; the array form already worked
|
|
5
5
|
// (Fabric accepts it raw, `filter:[{brightness:0.5}]` was device-verified), but the
|
|
6
6
|
// CSS-string form and drop-shadow color processing were missing. This restores them.
|
|
7
|
-
import { isOpaqueColorValue, processColor } from './platform-color.js';
|
|
7
|
+
import { isOpaqueColorValue, processColor } from './platform-color/index.js';
|
|
8
8
|
import { dlog } from './debug.js';
|
|
9
9
|
import { isRecord } from './type-guards.js';
|
|
10
10
|
// RN processFilter.js:19-24: pre-compiled patterns.
|
package/build/settings/index.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// device event `settingsUpdated`, which feeds the same change->fire path. Mirrors
|
|
7
7
|
// RN's Libraries/Settings/Settings.ios.js, iOS surface only.
|
|
8
8
|
import { installDeviceEventHub, NativeEventEmitter, } from '../native-events.js';
|
|
9
|
-
import { getNativeModule } from '../native-modules.js';
|
|
9
|
+
import { getNativeModule } from '../native-modules/index.js';
|
|
10
10
|
import { dlog } from '../debug.js';
|
|
11
11
|
import { isRecord } from '../type-guards.js';
|
|
12
12
|
// The iOS native module name RN registers this under. NOTE: this is the name the
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// TurboModuleRegistry.get('ShareModule') from RN source, but headless fakes resolve any
|
|
9
9
|
// name, so it is only proven on a real Android host (a bridgeless resolution log there).
|
|
10
10
|
import { dlog } from '../debug.js';
|
|
11
|
-
import { getNativeModule } from '../native-modules.js';
|
|
11
|
+
import { getNativeModule } from '../native-modules/index.js';
|
|
12
12
|
import { validateContent, shareActions, SHARED_ACTION, DISMISSED_ACTION } from './shared.js';
|
|
13
13
|
const SHARE_MODULE = 'ShareModule';
|
|
14
14
|
// ShareModule.share resolves an untyped value at the native boundary; narrow it before
|
package/build/share/index.ios.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// callback. Metro picks this file on an iOS host; the base share.ts re-exports it for
|
|
6
6
|
// web/headless.
|
|
7
7
|
import { dlog } from '../debug.js';
|
|
8
|
-
import { getNativeModule } from '../native-modules.js';
|
|
8
|
+
import { getNativeModule } from '../native-modules/index.js';
|
|
9
9
|
import { ACTION_SHEET_MANAGER, } from '../action-sheet-ios/index.js';
|
|
10
10
|
import { validateContent, shareActions, SHARED_ACTION, DISMISSED_ACTION } from './shared.js';
|
|
11
11
|
export const Share = {
|
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
// replace. Now that render.ts installs RN$stopSurface and tears surfaces down cleanly, the
|
|
11
11
|
// relayout survives and the bar updates without blanking (verified on device: show/hide +
|
|
12
12
|
// light/dark text). See render.ts's installStopSurfaceGlobal.
|
|
13
|
-
import { getNativeModule } from '../native-modules.js';
|
|
13
|
+
import { getNativeModule } from '../native-modules/index.js';
|
|
14
14
|
import { dlog } from '../debug.js';
|
|
15
|
-
import { processColor } from '../platform-color.js';
|
|
15
|
+
import { processColor } from '../platform-color/index.js';
|
|
16
16
|
import { STATUS_BAR_MANAGER } from './shared.js';
|
|
17
17
|
// processColor returns `unknown` (its result is platform-dependent); narrow to the
|
|
18
18
|
// number Fabric/native expects, like RN's invariant before setColor. A non-number
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// boundary getNativeModule<T> resolves against. Metro picks this on an iOS host; the base
|
|
9
9
|
// status-bar.ts re-exports it for tsc / tsx / headless. Each adapter wraps these with its own
|
|
10
10
|
// declarative component.
|
|
11
|
-
import { getNativeModule } from '../native-modules.js';
|
|
11
|
+
import { getNativeModule } from '../native-modules/index.js';
|
|
12
12
|
import { dlog } from '../debug.js';
|
|
13
13
|
import { STATUS_BAR_MANAGER, STATIC_HIDE_TRANSITION, hideTransition, } from './shared.js';
|
|
14
14
|
// Apply a StatusBar component's props to the native module. The adapter calls this from its
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export type IClassToggleMap = Record<string, boolean | undefined>;
|
|
2
|
+
export type IScopableClassValue = string | IClassToggleMap | Array<string | IClassToggleMap> | undefined | null;
|
|
3
|
+
export declare function scopeClassName(value: IScopableClassValue, localNames: ReadonlySet<string>, scopeId: string): IScopableClassValue;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Vue `<style scoped>` class-name rewriter. Distinct responsibility from the sibling
|
|
2
|
+
// ./index.ts (the CSS class -> style registry): this module does pure NAME rewriting,
|
|
3
|
+
// no registry lookup, no CSS parsing. It runs at the compiled call site of a Vue SFC's
|
|
4
|
+
// scoped-style template - `adapters/vue/metro-vue-transformer.cjs` emits calls to
|
|
5
|
+
// scopeClassName (imported there as `__scopeClass`) - BEFORE Vue's own normalizeClass()
|
|
6
|
+
// collapses string/object/array `class` values to a final string, so it must pre-process
|
|
7
|
+
// all three shapes normalizeClass understands. resolveClassName in ./index.ts still does
|
|
8
|
+
// the actual style lookup, unchanged, against the rewritten (possibly suffixed) name.
|
|
9
|
+
import { kebabToCamel } from '../index.js';
|
|
10
|
+
// Suffixes every class token that this file's scoped block locally defines with
|
|
11
|
+
// `__${scopeId}`, leaving unrecognized tokens (globals, external classes) untouched.
|
|
12
|
+
export function scopeClassName(value, localNames, scopeId) {
|
|
13
|
+
if (value === undefined || value === null)
|
|
14
|
+
return value;
|
|
15
|
+
if (Array.isArray(value)) {
|
|
16
|
+
return value.map(item => scopeClassEntry(item, localNames, scopeId));
|
|
17
|
+
}
|
|
18
|
+
return scopeClassEntry(value, localNames, scopeId);
|
|
19
|
+
}
|
|
20
|
+
// A token arrives as either the camelCase registry key (`sectionLabel`) or its kebab-case
|
|
21
|
+
// authoring form (`section-label`) - normalize to camelCase FIRST, then decide scoping, so
|
|
22
|
+
// `localNames` (always camelCase, built from the css-parser's registered keys) recognizes a
|
|
23
|
+
// kebab-written token. The emitted (possibly suffixed) name is always the camelCase form.
|
|
24
|
+
function scopeToken(token, localNames, scopeId) {
|
|
25
|
+
const camelToken = kebabToCamel(token);
|
|
26
|
+
return localNames.has(camelToken) ? `${camelToken}__${scopeId}` : camelToken;
|
|
27
|
+
}
|
|
28
|
+
function scopeClassEntry(value, localNames, scopeId) {
|
|
29
|
+
if (typeof value === 'object') {
|
|
30
|
+
const scoped = {};
|
|
31
|
+
for (const [name, enabled] of Object.entries(value)) {
|
|
32
|
+
scoped[scopeToken(name, localNames, scopeId)] = enabled;
|
|
33
|
+
}
|
|
34
|
+
return scoped;
|
|
35
|
+
}
|
|
36
|
+
return value
|
|
37
|
+
.split(/\s+/)
|
|
38
|
+
.filter(Boolean)
|
|
39
|
+
.map(token => scopeToken(token, localNames, scopeId))
|
|
40
|
+
.join(' ');
|
|
41
|
+
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// constants. The typed ViewStyle/TextStyle live next door in styles.ts, so `create`
|
|
6
6
|
// is typed against them like RN's, not left generic over a plain Record.
|
|
7
7
|
import { dlog } from '../debug.js';
|
|
8
|
-
import { getNativeModule } from '../native-modules.js';
|
|
8
|
+
import { getNativeModule } from '../native-modules/index.js';
|
|
9
9
|
import { flattenStyle } from '../style/index.js';
|
|
10
10
|
// RN's hairline factor: a "1 physical pixel" line is ~0.4 logical px, rounded to the
|
|
11
11
|
// nearest device pixel. Named so the 0.4 isn't a bare magic number (RN source uses
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
// showWithGravity(message: string, duration: number, gravity: number)
|
|
15
15
|
// showWithGravityAndOffset(message, duration, gravity, xOffset, yOffset)
|
|
16
16
|
import { dlog } from '../debug.js';
|
|
17
|
-
import { getNativeModule } from '../native-modules.js';
|
|
17
|
+
import { getNativeModule } from '../native-modules/index.js';
|
|
18
18
|
// The native module name RN registers this under. NOTE: this is the name the spec
|
|
19
19
|
// resolves via `TurboModuleRegistry.getEnforcing<Spec>('ToastAndroid')`, NOT the
|
|
20
20
|
// spec filename `INativeToastAndroid`. A module name is only provable on a real
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
interface ITouchRecord {
|
|
2
|
+
touchActive: boolean;
|
|
3
|
+
startPageX: number;
|
|
4
|
+
startPageY: number;
|
|
5
|
+
startTimeStamp: number;
|
|
6
|
+
currentPageX: number;
|
|
7
|
+
currentPageY: number;
|
|
8
|
+
currentTimeStamp: number;
|
|
9
|
+
previousPageX: number;
|
|
10
|
+
previousPageY: number;
|
|
11
|
+
previousTimeStamp: number;
|
|
12
|
+
}
|
|
13
|
+
interface ITouchHistory {
|
|
14
|
+
touchBank: ITouchRecord[];
|
|
15
|
+
numberActiveTouches: number;
|
|
16
|
+
indexOfSingleActiveTouch: number;
|
|
17
|
+
mostRecentTimeStamp: number;
|
|
18
|
+
}
|
|
19
|
+
export declare const touchHistory: ITouchHistory;
|
|
20
|
+
export declare function recordTouchTrack(kind: 'start' | 'move' | 'end', nativeEvent: Record<string, unknown>): void;
|
|
21
|
+
export declare function resetTouchHistory(): void;
|
|
22
|
+
export declare function attachTouchHistory(nativeEvent: Record<string, unknown>): void;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,150 @@
|
|
|
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.
|
|
12
|
+
import { isRecord } from '../type-guards.js';
|
|
13
|
+
// RN's bank is indexed by touch identifier and warns above 20; we never warn (headless
|
|
14
|
+
// events may carry larger or absent ids), we just skip anything out of a sane range.
|
|
15
|
+
const MAX_TOUCH_BANK = 20;
|
|
16
|
+
const touchBank = [];
|
|
17
|
+
export const touchHistory = {
|
|
18
|
+
touchBank,
|
|
19
|
+
numberActiveTouches: 0,
|
|
20
|
+
indexOfSingleActiveTouch: -1,
|
|
21
|
+
mostRecentTimeStamp: 0,
|
|
22
|
+
};
|
|
23
|
+
function toFiniteNumber(value) {
|
|
24
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
25
|
+
}
|
|
26
|
+
// Pull a recordable touch out of an untyped entry. RN's getTouchIdentifier throws on
|
|
27
|
+
// a null id; we skip instead, so events without touch geometry leave the bank untouched.
|
|
28
|
+
function normalizeTouch(raw) {
|
|
29
|
+
if (!isRecord(raw))
|
|
30
|
+
return undefined;
|
|
31
|
+
const identifier = toFiniteNumber(raw.identifier);
|
|
32
|
+
const pageX = toFiniteNumber(raw.pageX);
|
|
33
|
+
const pageY = toFiniteNumber(raw.pageY);
|
|
34
|
+
if (identifier === undefined || pageX === undefined || pageY === undefined)
|
|
35
|
+
return undefined;
|
|
36
|
+
if (identifier < 0 || identifier > MAX_TOUCH_BANK)
|
|
37
|
+
return undefined;
|
|
38
|
+
return { identifier, pageX, pageY, timestamp: toFiniteNumber(raw.timestamp) ?? 0 };
|
|
39
|
+
}
|
|
40
|
+
// The changed touches for this frame (start/move/end), defensively read.
|
|
41
|
+
function changedTouchesOf(nativeEvent) {
|
|
42
|
+
const raw = nativeEvent.changedTouches;
|
|
43
|
+
if (!Array.isArray(raw))
|
|
44
|
+
return [];
|
|
45
|
+
const out = [];
|
|
46
|
+
for (const entry of raw) {
|
|
47
|
+
const touch = normalizeTouch(entry);
|
|
48
|
+
if (touch !== undefined)
|
|
49
|
+
out.push(touch);
|
|
50
|
+
}
|
|
51
|
+
return out;
|
|
52
|
+
}
|
|
53
|
+
// Count of all touches still down (RN reads nativeEvent.touches.length directly).
|
|
54
|
+
function activeTouchCount(nativeEvent) {
|
|
55
|
+
const raw = nativeEvent.touches;
|
|
56
|
+
return Array.isArray(raw) ? raw.length : 0;
|
|
57
|
+
}
|
|
58
|
+
function recordTouchStart(touch) {
|
|
59
|
+
const record = touchBank[touch.identifier];
|
|
60
|
+
if (record) {
|
|
61
|
+
record.touchActive = true;
|
|
62
|
+
record.startPageX = touch.pageX;
|
|
63
|
+
record.startPageY = touch.pageY;
|
|
64
|
+
record.startTimeStamp = touch.timestamp;
|
|
65
|
+
record.currentPageX = touch.pageX;
|
|
66
|
+
record.currentPageY = touch.pageY;
|
|
67
|
+
record.currentTimeStamp = touch.timestamp;
|
|
68
|
+
record.previousPageX = touch.pageX;
|
|
69
|
+
record.previousPageY = touch.pageY;
|
|
70
|
+
record.previousTimeStamp = touch.timestamp;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
touchBank[touch.identifier] = {
|
|
74
|
+
touchActive: true,
|
|
75
|
+
startPageX: touch.pageX,
|
|
76
|
+
startPageY: touch.pageY,
|
|
77
|
+
startTimeStamp: touch.timestamp,
|
|
78
|
+
currentPageX: touch.pageX,
|
|
79
|
+
currentPageY: touch.pageY,
|
|
80
|
+
currentTimeStamp: touch.timestamp,
|
|
81
|
+
previousPageX: touch.pageX,
|
|
82
|
+
previousPageY: touch.pageY,
|
|
83
|
+
previousTimeStamp: touch.timestamp,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
touchHistory.mostRecentTimeStamp = touch.timestamp;
|
|
87
|
+
}
|
|
88
|
+
// Move and end share the previous<-current shift; only `touchActive` differs.
|
|
89
|
+
function shiftTouchRecord(touch, active) {
|
|
90
|
+
const record = touchBank[touch.identifier];
|
|
91
|
+
if (!record)
|
|
92
|
+
return;
|
|
93
|
+
record.touchActive = active;
|
|
94
|
+
record.previousPageX = record.currentPageX;
|
|
95
|
+
record.previousPageY = record.currentPageY;
|
|
96
|
+
record.previousTimeStamp = record.currentTimeStamp;
|
|
97
|
+
record.currentPageX = touch.pageX;
|
|
98
|
+
record.currentPageY = touch.pageY;
|
|
99
|
+
record.currentTimeStamp = touch.timestamp;
|
|
100
|
+
touchHistory.mostRecentTimeStamp = touch.timestamp;
|
|
101
|
+
}
|
|
102
|
+
function arrayFirst(value) {
|
|
103
|
+
return Array.isArray(value) ? value[0] : undefined;
|
|
104
|
+
}
|
|
105
|
+
// Maintain the bank as a touch frame flows. Mirrors RN's recordTouchTrack: moveish
|
|
106
|
+
// shifts records, startish records + recomputes numberActiveTouches, endish marks the
|
|
107
|
+
// record inactive + rescans for the single remaining touch. `kind` is the touch phase.
|
|
108
|
+
export function recordTouchTrack(kind, nativeEvent) {
|
|
109
|
+
if (kind === 'move') {
|
|
110
|
+
for (const touch of changedTouchesOf(nativeEvent))
|
|
111
|
+
shiftTouchRecord(touch, true);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (kind === 'start') {
|
|
115
|
+
for (const touch of changedTouchesOf(nativeEvent))
|
|
116
|
+
recordTouchStart(touch);
|
|
117
|
+
touchHistory.numberActiveTouches = activeTouchCount(nativeEvent);
|
|
118
|
+
if (touchHistory.numberActiveTouches === 1) {
|
|
119
|
+
const first = normalizeTouch(arrayFirst(nativeEvent.touches));
|
|
120
|
+
touchHistory.indexOfSingleActiveTouch = first?.identifier ?? -1;
|
|
121
|
+
}
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
for (const touch of changedTouchesOf(nativeEvent))
|
|
125
|
+
shiftTouchRecord(touch, false);
|
|
126
|
+
touchHistory.numberActiveTouches = activeTouchCount(nativeEvent);
|
|
127
|
+
if (touchHistory.numberActiveTouches === 1) {
|
|
128
|
+
for (let i = 0; i < touchBank.length; i++) {
|
|
129
|
+
const record = touchBank[i];
|
|
130
|
+
if (record !== undefined && record.touchActive) {
|
|
131
|
+
touchHistory.indexOfSingleActiveTouch = i;
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// Drop all touch state. Called on a fully-released / cancelled gesture so a stale bank
|
|
138
|
+
// never leaks geometry into the next gesture's first frame.
|
|
139
|
+
export function resetTouchHistory() {
|
|
140
|
+
touchBank.length = 0;
|
|
141
|
+
touchHistory.numberActiveTouches = 0;
|
|
142
|
+
touchHistory.indexOfSingleActiveTouch = -1;
|
|
143
|
+
touchHistory.mostRecentTimeStamp = 0;
|
|
144
|
+
}
|
|
145
|
+
// Attach the live touch history onto the event the responder handlers receive, matching
|
|
146
|
+
// ResponderEventPlugin.js (`grantEvent.touchHistory = ...`, etc.). PanResponder reads
|
|
147
|
+
// it for the per-touch dx/vx math; handlers that ignore it are unaffected.
|
|
148
|
+
export function attachTouchHistory(nativeEvent) {
|
|
149
|
+
nativeEvent.touchHistory = touchHistory;
|
|
150
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
2
|
+
export declare function isBoolean(value: unknown): value is boolean;
|
|
3
|
+
export declare function isNumber(value: unknown): value is number;
|
|
4
|
+
export declare function isString(value: unknown): value is string;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Runtime guards to narrow `unknown` at trust boundaries (native payloads, ViewConfig
|
|
2
|
+
// attributes, style values) without an `as` cast. `isRecord` excludes arrays - most
|
|
3
|
+
// call sites mean "a native payload keyed by string," never a list.
|
|
4
|
+
export function isRecord(value) {
|
|
5
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
6
|
+
}
|
|
7
|
+
export function isBoolean(value) {
|
|
8
|
+
return typeof value === 'boolean';
|
|
9
|
+
}
|
|
10
|
+
export function isNumber(value) {
|
|
11
|
+
return typeof value === 'number';
|
|
12
|
+
}
|
|
13
|
+
export function isString(value) {
|
|
14
|
+
return typeof value === 'string';
|
|
15
|
+
}
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// the base vibration.ts re-exports the iOS build for web/headless. There is no runtime
|
|
17
17
|
// `Platform.OS` read; the filename is the selector. No Fabric view, pure JS->native.
|
|
18
18
|
import { dlog } from '../debug.js';
|
|
19
|
-
import { getNativeModule } from '../native-modules.js';
|
|
19
|
+
import { getNativeModule } from '../native-modules/index.js';
|
|
20
20
|
const VIBRATION_MODULE = 'Vibration';
|
|
21
21
|
// RN's _default_vibration_length: the single-buzz duration when no pattern is given,
|
|
22
22
|
// and the per-segment buzz length the iOS scheduler emits between waits.
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@symbiote-native/engine",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "SymbioteNative's retained shadow-tree engine — clone-on-write commit path + event normalization over React Native Fabric, shared by every framework adapter.",
|
|
5
|
+
"license": "MIT",
|
|
5
6
|
"repository": {
|
|
6
7
|
"type": "git",
|
|
7
8
|
"url": "git+https://github.com/OneEyed1366/symbiote-native.git",
|
|
@@ -32,7 +33,7 @@
|
|
|
32
33
|
"react-native": ">=0.86"
|
|
33
34
|
},
|
|
34
35
|
"devDependencies": {
|
|
35
|
-
"@symbiote-native/test-utils": "0.1.
|
|
36
|
+
"@symbiote-native/test-utils": "0.1.6"
|
|
36
37
|
},
|
|
37
38
|
"scripts": {
|
|
38
39
|
"typecheck": "tsc --build",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|