@symbiote-native/react 0.2.8 → 0.3.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 (55) hide show
  1. package/build/bootstrap.js +4 -5
  2. package/build/components/activity-indicator/index.android.js +5 -7
  3. package/build/components/keyboard-avoiding-view/index.js +1 -1
  4. package/build/components/modal/index.d.ts +3 -2
  5. package/build/components/scroll-view/index.android.js +0 -2
  6. package/build/components/scroll-view/index.ios.js +10 -10
  7. package/build/components/scroll-view/shared.js +2 -2
  8. package/build/components/scroll-view/sticky-header.js +7 -9
  9. package/build/components/section-list/index.js +4 -6
  10. package/build/components/text-input/index.js +1 -1
  11. package/build/components/virtualized-list/index.js +25 -0
  12. package/build/create-tunnel/index.js +14 -20
  13. package/build/hooks/use-color-scheme.d.ts +1 -1
  14. package/build/hooks/use-color-scheme.js +1 -1
  15. package/build/hooks/use-window-dimensions.d.ts +1 -1
  16. package/build/hooks/use-window-dimensions.js +1 -1
  17. package/build/host-instance.js +5 -6
  18. package/build/index.d.ts +6 -31
  19. package/build/index.js +15 -25
  20. package/build/modules/animated/create-animated-component.js +24 -65
  21. package/build/render.js +20 -2
  22. package/metro-css-parser.cjs +7 -12
  23. package/package.json +4 -4
  24. package/build/modules/action-sheet-ios.d.ts +0 -2
  25. package/build/modules/action-sheet-ios.js +0 -4
  26. package/build/modules/alert.d.ts +0 -2
  27. package/build/modules/alert.js +0 -5
  28. package/build/modules/app-state.d.ts +0 -2
  29. package/build/modules/app-state.js +0 -3
  30. package/build/modules/appearance.d.ts +0 -2
  31. package/build/modules/appearance.js +0 -3
  32. package/build/modules/back-handler.d.ts +0 -2
  33. package/build/modules/back-handler.js +0 -3
  34. package/build/modules/dimensions.d.ts +0 -2
  35. package/build/modules/dimensions.js +0 -3
  36. package/build/modules/i18n-manager.d.ts +0 -2
  37. package/build/modules/i18n-manager.js +0 -4
  38. package/build/modules/keyboard.d.ts +0 -2
  39. package/build/modules/keyboard.js +0 -3
  40. package/build/modules/layout-animation.d.ts +0 -2
  41. package/build/modules/layout-animation.js +0 -3
  42. package/build/modules/linking.d.ts +0 -2
  43. package/build/modules/linking.js +0 -4
  44. package/build/modules/permissions-android.d.ts +0 -2
  45. package/build/modules/permissions-android.js +0 -3
  46. package/build/modules/settings.d.ts +0 -1
  47. package/build/modules/settings.js +0 -4
  48. package/build/modules/share.d.ts +0 -2
  49. package/build/modules/share.js +0 -4
  50. package/build/modules/text-input-state.d.ts +0 -1
  51. package/build/modules/text-input-state.js +0 -3
  52. package/build/modules/toast-android.d.ts +0 -1
  53. package/build/modules/toast-android.js +0 -4
  54. package/build/modules/vibration.d.ts +0 -1
  55. package/build/modules/vibration.js +0 -4
@@ -1,9 +1,8 @@
1
- // Zero-config app entry: wires the four RN-backed host seams (via @symbiote-native/components'
1
+ // Zero-config app entry: wires the four RN-backed host seams (via @symbiote-native/components's
2
2
  // bootstrapHost) plus this adapter's own AppRegistry host-registrar bridge, then registers the
3
- // root component — collapsing the canary's manual sequence into one call. Lives OUTSIDE the
4
- // package's main barrel (see package.json's "./bootstrap" export): it imports react-native
5
- // directly, which Vitest's Flow-unaware transform can't parse (see
6
- // @symbiote-native/components/bootstrap for the full reason).
3
+ // root component in one call. Lives OUTSIDE the package's main barrel (package.json's
4
+ // "./bootstrap" export) because it imports react-native directly, which Vitest's Flow-unaware
5
+ // transform can't parse (see @symbiote-native/components/bootstrap for the full reason).
7
6
  import { AppRegistry as RNAppRegistry } from 'react-native';
8
7
  import { bootstrapHost } from '@symbiote-native/components/bootstrap';
9
8
  import { AppRegistry, setHostRegistrar } from './modules/app-registry/index.js';
@@ -1,10 +1,8 @@
1
- // ActivityIndicator on Android: AndroidProgressBar needs `styleAttr` (it drives the
2
- // ProgressBar's setStyle(); without it the view throws "setStyle() not called") and
3
- // `indeterminate: true`, and its default color is the theme (null), per RN's
4
- // ActivityIndicator.js android branch ({styleAttr:'Normal', indeterminate:true}). Metro
5
- // picks this on an Android host; no Platform.OS read.
6
- // device-verify-pending: prop names mirror RN's AndroidProgressBar, proven on a real
7
- // host by the absence of the "setStyle() not called" red box.
1
+ // AndroidProgressBar needs `styleAttr` (drives ProgressBar.setStyle(); missing it throws
2
+ // "setStyle() not called") and `indeterminate: true`; default color is the theme (null) —
3
+ // mirrors RN's ActivityIndicator.js android branch. Metro selects this file on an Android
4
+ // host, no Platform.OS read needed.
5
+ // device-verify-pending: confirmed by the absence of the setStyle() red box on a real host.
8
6
  import { descriptorToReact } from '../../descriptor-to-react/index.js';
9
7
  import { useActivityIndicatorLogic } from './shared.js';
10
8
  const PLATFORM = {
@@ -11,7 +11,7 @@ import { createElement, useEffect, useRef, useState, } from 'react';
11
11
  import { dlog } from '@symbiote-native/engine';
12
12
  import { computeInset, readKeyboardFrame, readLayoutFrame, resolveKeyboardAvoidingLayout, DEFAULT_VERTICAL_OFFSET, } from '@symbiote-native/components';
13
13
  import { View } from '../../components.js';
14
- import { Keyboard, KEYBOARD_EVENT } from '../../modules/keyboard.js';
14
+ import { Keyboard, KEYBOARD_EVENT } from '@symbiote-native/engine';
15
15
  export const KeyboardAvoidingView = props => {
16
16
  const { behavior, enabled = true, keyboardVerticalOffset = DEFAULT_VERTICAL_OFFSET, contentContainerStyle, style, children, onLayout,
17
17
  // The wrapper is the View FC, which runs resolveAccessibilityProps itself, so
@@ -1,5 +1,6 @@
1
1
  import { type FC, type ReactNode } from 'react';
2
- import { type IAccessibilityProps, type IAriaProps, type IModalAnimationType, type IModalOrientation, type IModalOrientationChangeEvent, type IModalPresentationStyle } from '@symbiote-native/components';
2
+ import { type ISymbioteEvent } from '@symbiote-native/engine';
3
+ import { type IAccessibilityProps, type IAriaProps, type IModalAnimationType, type IModalOrientation, type IModalPresentationStyle } from '@symbiote-native/components';
3
4
  import type { IStyleProp, IViewStyle } from '../../utils/styles';
4
5
  export type { IModalAnimationType, IModalPresentationStyle, IModalOrientation, IModalOrientationChangeEvent, } from '@symbiote-native/components';
5
6
  export interface IModalProps extends IAccessibilityProps, IAriaProps {
@@ -16,7 +17,7 @@ export interface IModalProps extends IAccessibilityProps, IAriaProps {
16
17
  onShow?: () => void;
17
18
  onDismiss?: () => void;
18
19
  onRequestClose?: () => void;
19
- onOrientationChange?: (event: IModalOrientationChangeEvent) => void;
20
+ onOrientationChange?: (event: ISymbioteEvent) => void;
20
21
  style?: IStyleProp<IViewStyle>;
21
22
  className?: string;
22
23
  children?: ReactNode;
@@ -6,8 +6,6 @@
6
6
  // android branch (cloneElement(refreshControl, {style}, <ScrollView nestedScrollEnabled
7
7
  // style={flex:1}>{content}</ScrollView>)). Metro picks this on an Android host; no
8
8
  // Platform.OS read.
9
- // device-verify-pending: the wrap shape mirrors RN, proven on a real host by the absence
10
- // of the "addViewAt: failed to insert" crash.
11
9
  import { cloneElement, createElement, forwardRef, useImperativeHandle, useRef } from 'react';
12
10
  import { dlog } from '@symbiote-native/engine';
13
11
  import { buildScrollViewHandle, splitLayoutProps } from '@symbiote-native/components';
@@ -1,21 +1,21 @@
1
- // ScrollView on iOS: the RefreshControl is a CHILD of the scroll view, rendered as a
2
- // sibling BEFORE the content container (RN ScrollView.js: {refreshControl}{contentContainer}).
3
- // Also the base (scroll-view.ts re-exports it) for headless / web.
1
+ // ScrollView (iOS): RefreshControl renders as a sibling BEFORE the content container, matching
2
+ // RN's ScrollView.js ({refreshControl}{contentContainer}). Also the base export
3
+ // (scroll-view.ts re-exports it) for headless / web.
4
4
  import { createElement, forwardRef, useImperativeHandle, useRef } from 'react';
5
5
  import { buildScrollViewHandle } from '@symbiote-native/components';
6
6
  import { usePreparedScrollView, useNativeStickyScrollAttach, } from './shared.js';
7
7
  export const ScrollView = forwardRef((props, forwardedRef) => {
8
8
  const { scrollViewIntrinsic, scrollViewBaseStyle, outerProps, style, content, refreshControl, scrollAnimatedValue, nativeStickyAvailable, } = usePreparedScrollView(props);
9
- // The node ref backs the imperative handle; it attaches to the scroll-view element below
10
- // (passing `ref` through createElement props binds it to the SymbioteNode, as TextInput does).
9
+ // Backs the imperative handle; passing `ref` through createElement props binds it to the
10
+ // SymbioteNode below, as TextInput does.
11
11
  const ref = useRef(null);
12
- // Lazy getter, not the ref itself: the node is null until the element commits, so the handle
13
- // must read ref.current on each command, an eager capture would freeze null.
12
+ // Lazy getter: the node is null until commit, so the handle reads ref.current per call
13
+ // instead of capturing eagerly, which would freeze null.
14
14
  useImperativeHandle(forwardedRef, () => buildScrollViewHandle(() => ref.current), []);
15
- // Drive the sticky scroll value on the native UI thread (RN attachNativeEvent). No-op on a
16
- // host without the native animated module. The JS sticky path stays in effect.
15
+ // Drives sticky scroll on the native UI thread (RN attachNativeEvent); no-ops without the
16
+ // native animated module, falling back to the JS sticky path.
17
17
  useNativeStickyScrollAttach(ref, scrollAnimatedValue, nativeStickyAvailable);
18
- // Base style under user style so an explicit user value wins; undefined base (vertical)
18
+ // Base style stays under user style so an explicit value wins; undefined base (vertical)
19
19
  // passes the user style through unchanged.
20
20
  const scrollStyle = scrollViewBaseStyle ? [scrollViewBaseStyle, style] : style;
21
21
  const scrollProps = { ...outerProps, style: scrollStyle, ref };
@@ -144,8 +144,8 @@ export function usePreparedScrollView(rawProps) {
144
144
  onContentSizeChange(width, height);
145
145
  };
146
146
  }
147
- // Sticky headers are a pure-JS layer (the native scroll view ignores stickyHeaderIndices);
148
- // wrap the flagged children so they pin to the scroll offset. No-op when none are flagged.
147
+ // Wrap the flagged children so they pin to the scroll offset (see the sticky-headers note
148
+ // above); no-op when none are flagged.
149
149
  const contentChildren = hasStickyHeaders
150
150
  ? wrapStickyHeaders(children, stickyHeaderIndices, scrollAnimatedValue, invertStickyHeaders, viewportHeight, StickyHeaderComponent, headerLayoutYs, onHeaderLayoutY)
151
151
  : children;
@@ -1,14 +1,12 @@
1
1
  // Sticky headers: the JS layer RN implements in ScrollView.js / ScrollViewStickyHeader.js.
2
2
  //
3
- // VERDICT (source-based): RN does stickiness PURELY IN JS. ScrollView.js (render, ~line
4
- // 1690) wraps each child whose index is in `stickyHeaderIndices` in a ScrollViewStickyHeader,
5
- // fed by a single `_scrollAnimatedValue` an Animated.event drives from `onScroll`
6
- // (ScrollView.js ~line 1095). The native Fabric scroll view does NOT honor the index array on
7
- // its own. Forwarding `stickyHeaderIndices` to native is a silent no-op. So we replicate the
8
- // JS layer: subscribe each flagged child to the scroll offset and translate it to stay pinned.
9
- // The interpolation mirrors ScrollViewStickyHeader.js (non-inverted + inverted branches) and now
10
- // lives, framework-agnostic, in @symbiote-native/components (computeStickyInterpolation); this
11
- // file holds the React component shell, the layout state, and the child-wrapping.
3
+ // RN does stickiness purely in JS: ScrollView.js (~line 1690) wraps each flagged child in a
4
+ // ScrollViewStickyHeader, fed by a single `_scrollAnimatedValue` that Animated.event drives
5
+ // from `onScroll` (~line 1095). The native Fabric scroll view does not honor the index array,
6
+ // so forwarding `stickyHeaderIndices` to native would be a silent no-op - we replicate the JS
7
+ // layer instead. The interpolation mirrors ScrollViewStickyHeader.js and now lives,
8
+ // framework-agnostic, in @symbiote-native/components (computeStickyInterpolation); this file
9
+ // holds only the React component shell, layout state, and child-wrapping.
12
10
  import { Children, createElement, isValidElement, useCallback, useEffect, useReducer, useRef, useState, } from 'react';
13
11
  import { AnimatedInterpolation, AnimatedValue, Platform, dlog, } from '@symbiote-native/engine';
14
12
  import { createInitialStickyState, nextStickyHeaderY, readLayoutNumber, reduceSticky, STICKY_HEADER_Z_INDEX, } from '@symbiote-native/components';
@@ -1,10 +1,8 @@
1
- // SectionList: the public, high-level list-of-sections component. It is a thin
2
- // wrapper over VirtualizedSectionList, mirroring RN's layering
3
- // (SectionList -> VirtualizedSectionList -> VirtualizedList). All the
1
+ // SectionList: public wrapper over VirtualizedSectionList, mirroring RN's
2
+ // SectionList -> VirtualizedSectionList -> VirtualizedList layering. All
4
3
  // section-flattening / windowing / imperative-scroll logic lives in
5
- // VirtualizedSectionList; this layer only re-exposes the same surface under the
6
- // SectionList name so existing consumers and the canary keep importing it from
7
- // here, threading the imperative ref straight through.
4
+ // VirtualizedSectionList; this re-exposes the same surface under the
5
+ // SectionList name, threading the imperative ref straight through.
8
6
  import { createElement } from 'react';
9
7
  import { VirtualizedSectionList, } from '../virtualized-section-list/index.js';
10
8
  export function SectionList(props) {
@@ -10,7 +10,7 @@
10
10
  // (setTextAndSelection), never a plain prop re-push. See @symbiote-native/components/state/text-input.
11
11
  import { forwardRef, useCallback, useEffect, useImperativeHandle, useLayoutEffect, useRef, useState, } from 'react';
12
12
  import { dispatchViewCommand, dlog, } from '@symbiote-native/engine';
13
- import { blurTextInput, setInputBlurred, setInputFocused } from '../../modules/text-input-state.js';
13
+ import { blurTextInput, setInputBlurred, setInputFocused } from '@symbiote-native/engine';
14
14
  import { resolveAccessibilityProps, resolveTextInputProps, renderTextInput, foldText, textFromChange, eventCountFromChange, shouldCommandText, INITIAL_EVENT_COUNT, SELECTION_NONE, } from '@symbiote-native/components';
15
15
  import { descriptorToReact } from '../../descriptor-to-react/index.js';
16
16
  export const TextInput = forwardRef((rawProps, forwardedRef) => {
@@ -346,6 +346,31 @@ export function VirtualizedList(props) {
346
346
  style: horizontal ? { width: plan.leadingExtent } : { height: plan.leadingExtent },
347
347
  }));
348
348
  }
349
+ // The force-mounted sticky cell (RN _ensureClosestStickyHeader): the nearest sticky index
350
+ // below the window, kept alive outside [first,last] so it never gets destroyed/recreated
351
+ // as the window slides back over its origin index. Rendered exactly like an in-window
352
+ // cell — same measuring wrapper, same key format (so a transition between forced and
353
+ // in-window rendering of the SAME index keeps the same React key and never remounts).
354
+ if (plan.forcedStickyCell !== undefined) {
355
+ const forcedIndex = plan.forcedStickyCell.index;
356
+ const forcedItem = getItem(data, forcedIndex);
357
+ const forcedCell = renderItem({
358
+ item: forcedItem,
359
+ index: forcedIndex,
360
+ separators: makeSeparators(forcedIndex),
361
+ });
362
+ children.push(createElement('symbiote-view', {
363
+ key: `cell-${plan.forcedStickyCell.key}`,
364
+ onLayout: makeCellMeasure(forcedIndex),
365
+ style: inverted ? (horizontal ? INVERTED_X_STYLE : INVERTED_Y_STYLE) : undefined,
366
+ }, forcedCell));
367
+ }
368
+ if (plan.gapExtent > EMPTY_OFFSET) {
369
+ children.push(createElement('symbiote-view', {
370
+ key: 'spacer-gap',
371
+ style: horizontal ? { width: plan.gapExtent } : { height: plan.gapExtent },
372
+ }));
373
+ }
349
374
  for (const planCell of plan.cells) {
350
375
  const item = getItem(data, planCell.index);
351
376
  // renderItem gets a separators handle so a row can highlight/update its own dividers.
@@ -1,26 +1,20 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- // createTunnel — the cross-surface answer to createPortal's same-surface-only scope.
3
- // Prior art: pmndrs/tunnel-rat exists precisely because a real
4
- // createPortal cannot reach across two genuinely separate reconciler roots — see
5
- // github.com/facebook/react/issues/17147 ("react-reconciler & portals: missing root
6
- // instance"). The fix is the one the ecosystem already settled on: don't reach into a
7
- // foreign surface's commit machinery — let that surface commit ITSELF, normally, by reading
2
+ // createTunnel - the cross-surface answer to createPortal's same-surface-only scope. A real
3
+ // createPortal cannot reach across two separate reconciler roots (see
4
+ // github.com/facebook/react/issues/17147); the fix, same as pmndrs/tunnel-rat, is to not
5
+ // reach into a foreign surface's commit machinery - let that surface commit itself, reading
8
6
  // from a store it's already subscribed to.
9
7
  //
10
- // Both In and Out are COMPONENTS (the Vue twin's shape, and the same pattern as
11
- // Context.Provider/Context.Consumer) — NOT hooks. This isn't just nicer call-site ergonomics:
12
- // it's what makes the cascade in the first place structurally impossible. An earlier
13
- // hook-based version (`useTunnelIn`/`useTunnelOut`, called directly inside one component)
14
- // produced a genuine infinite render loop — a silent white screen on device, no thrown error
15
- // — because notify() forces a re-render of whichever component's useSyncExternalStore call
16
- // subscribed, and when THAT was the SAME component that also called useTunnelIn, its own
17
- // effect re-ran and notified again, forever (this custom renderer's synchronous commit loop
18
- // has no "Maximum update depth exceeded" guard to catch it). As separate components, notify()
19
- // only forces Out's OWN render scope — never In's, even when both are children of the same
20
- // parent — so the update has nowhere to bounce back to. `Out` lives inside whichever surface
21
- // should PAINT the content (e.g. a persistent overlay-host surface); `In` lives ANYWHERE, same
22
- // surface or a totally different one, and never touches a Fabric node directly, so there is
23
- // no "target must already be mounted" guard to satisfy at all.
8
+ // In and Out are COMPONENTS (Context.Provider/Consumer shape), never hooks - this is
9
+ // load-bearing, not just ergonomics. An earlier hook-based version caused a real infinite
10
+ // render loop on device (silent white screen, no thrown error): notify() re-renders
11
+ // whichever component's useSyncExternalStore subscription fired, and when that was the same
12
+ // component calling useTunnelIn, its own effect re-notified itself forever - this custom
13
+ // renderer's synchronous commit loop has no "Maximum update depth exceeded" guard. As
14
+ // separate components, notify() only forces Out's own render scope, so the update has
15
+ // nowhere to bounce back to. `Out` lives in whichever surface should PAINT the content; `In`
16
+ // lives anywhere - same surface or a different one - and never touches a Fabric node
17
+ // directly, so there's no "target must already be mounted" guard to satisfy.
24
18
  import { Fragment, useEffect, useId, useSyncExternalStore } from 'react';
25
19
  export function createTunnel() {
26
20
  const items = new Map();
@@ -1,2 +1,2 @@
1
- import { type IColorSchemeName } from '../modules/appearance';
1
+ import { type IColorSchemeName } from '@symbiote-native/engine';
2
2
  export declare function useColorScheme(): IColorSchemeName | null;
@@ -2,7 +2,7 @@
2
2
  // on change. Mirrors RN's Libraries/Utilities/useColorScheme.js: a thin
3
3
  // useSyncExternalStore over Appearance's change event + current scheme read.
4
4
  import { useSyncExternalStore } from 'react';
5
- import { Appearance } from '../modules/appearance.js';
5
+ import { Appearance } from '@symbiote-native/engine';
6
6
  const subscribe = (onStoreChange) => {
7
7
  const subscription = Appearance.addChangeListener(onStoreChange);
8
8
  return () => subscription.remove();
@@ -1,2 +1,2 @@
1
- import { type IDisplayMetrics } from '../modules/dimensions';
1
+ import { type IDisplayMetrics } from '@symbiote-native/engine';
2
2
  export declare function useWindowDimensions(): IDisplayMetrics;
@@ -4,7 +4,7 @@
4
4
  // subscribing to close the gap between the render-time get and the effect-time
5
5
  // listener. Only the window metrics changing triggers a re-render.
6
6
  import { useEffect, useState } from 'react';
7
- import { Dimensions } from '../modules/dimensions.js';
7
+ import { Dimensions } from '@symbiote-native/engine';
8
8
  export function useWindowDimensions() {
9
9
  const [dimensions, setDimensions] = useState(() => Dimensions.get('window'));
10
10
  useEffect(() => {
@@ -1,9 +1,8 @@
1
- // The public instance a host ref hands back and the ref -> tag lookup. toPublicInstance and
2
- // IHostInstance now live in @symbiote-native/engine (they depend only on engine internals, so every
3
- // adapter inherits the SAME public instance); this module re-exports them so the React surface
4
- // (`@symbiote-native/react` exports findNodeHandle + IHostInstance, host-config grafts via
5
- // toPublicInstance) is unchanged. findNodeHandle stays here: it is the React-shaped "ref ->
6
- // native tag" lookup over the engine's getNativeTag.
1
+ // The public instance a host ref hands back, and the ref -> tag lookup. toPublicInstance and
2
+ // IHostInstance live in @symbiote-native/engine (every adapter inherits the same public instance);
3
+ // this module re-exports them so the React surface (findNodeHandle + IHostInstance, host-config's
4
+ // toPublicInstance graft) is unchanged. findNodeHandle stays React-specific: it is the
5
+ // RN-shaped "ref -> native tag" lookup over the engine's getNativeTag.
7
6
  import { getNativeTag, isSymbioteNode, toPublicInstance, } from '@symbiote-native/engine';
8
7
  export { toPublicInstance };
9
8
  // The committed native tag of a host ref (or a bare tag, idempotent). RN's
package/build/index.d.ts CHANGED
@@ -12,8 +12,6 @@ export { TextInput } from './components/text-input';
12
12
  export type { ITextInputProps, ITextInputHandle } from './components/text-input';
13
13
  export { InputAccessoryView } from './components/input-accessory-view';
14
14
  export type { IInputAccessoryViewProps } from './components/input-accessory-view';
15
- export { Keyboard, KEYBOARD_EVENT } from './modules/keyboard';
16
- export type { IKeyboardEventName } from './modules/keyboard';
17
15
  export { KeyboardAvoidingView } from './components/keyboard-avoiding-view';
18
16
  export type { IKeyboardAvoidingViewProps, IKeyboardAvoidingBehavior, } from './components/keyboard-avoiding-view';
19
17
  export { StatusBar } from './modules/status-bar';
@@ -55,44 +53,21 @@ export { Animated, createAnimatedComponent } from './modules/animated';
55
53
  export { Platform, StyleSheet } from '@symbiote-native/engine';
56
54
  export { PlatformColor, DynamicColorIOS, processColor } from '@symbiote-native/engine';
57
55
  export type { IColorValue, IOpaqueColorValue, IDynamicColorIOSTuple, } from '@symbiote-native/engine';
58
- export { setNativeViewConfigSource } from '@symbiote-native/engine';
56
+ export { setNativeViewConfigSource, setColorProcessor, setDeviceEventSource, } from '@symbiote-native/engine';
57
+ export { dlog, isDebug } from '@symbiote-native/engine';
59
58
  export type { INativeViewConfig, INativeViewConfigSource } from '@symbiote-native/engine';
60
59
  export type { IPlatformStatic, IPlatformOSType, IPlatformConstantsIOS, IPlatformConstantsAndroid, IPlatformSelectSpec, } from '@symbiote-native/engine';
61
- export { Dimensions } from './modules/dimensions';
62
- export type { IDisplayMetrics, IDisplayMetricsAndroid, IDimensionsPayload, IDimensionsSet, IDimensionsKey, IDimensionsChangeListener, IDimensionsStatic, } from './modules/dimensions';
63
- export { PixelRatio } from '@symbiote-native/engine';
64
- export type { IPixelRatioStatic } from '@symbiote-native/engine';
60
+ export { Alert, Share, ActionSheetIOS, Linking, Vibration, ToastAndroid, Settings, I18nManager, Dimensions, Appearance, AppState, Keyboard, KEYBOARD_EVENT, BackHandler, PermissionsAndroid, PERMISSIONS, RESULTS, LayoutAnimation, PixelRatio, } from '@symbiote-native/engine';
61
+ export type { IAlertType, IAlertButtonStyle, IAlertButton, IAlertButtons, IAlertOptions, IShareContent, IShareOptions, IShareAction, IActionSheetIOSOptions, IShareActionSheetIOSOptions, IShareActionSheetError, IUrlEvent, II18nManagerConstants, IDisplayMetrics, IDisplayMetricsAndroid, IDimensionsPayload, IDimensionsSet, IDimensionsKey, IDimensionsChangeListener, IDimensionsStatic, IColorSchemeName, IColorSchemePreference, IAppStateStatus, IAppStateEvent, IKeyboardEventName, IKeyboardEvent, IKeyboardMetrics, IBackPressEventName, IBackPressHandler, IPermission, IPermissionStatus, IRationale, ILayoutAnimationType, ILayoutAnimationProperty, ILayoutAnimationConfig, ILayoutAnimationAnim, IPixelRatioStatic, } from '@symbiote-native/engine';
65
62
  export { useWindowDimensions } from './hooks/use-window-dimensions';
66
- export { Appearance } from './modules/appearance';
67
- export type { IColorSchemeName, IColorSchemePreference } from './modules/appearance';
68
63
  export { useColorScheme } from './hooks/use-color-scheme';
69
- export { AppState } from './modules/app-state';
70
- export type { IAppStateStatus, IAppStateEvent } from './modules/app-state';
71
- export { Alert } from './modules/alert';
72
- export type { IAlertType, IAlertButtonStyle, IAlertButton, IAlertButtons, IAlertOptions, } from './modules/alert';
73
- export { ActionSheetIOS } from './modules/action-sheet-ios';
74
- export type { IActionSheetIOSOptions, IShareActionSheetIOSOptions, IShareActionSheetError, } from './modules/action-sheet-ios';
75
- export { Linking } from './modules/linking';
76
- export type { IUrlEvent } from './modules/linking';
77
- export { Vibration } from './modules/vibration';
78
- export { Share } from './modules/share';
79
- export type { IShareContent, IShareOptions, IShareAction } from './modules/share';
80
64
  export { AccessibilityInfo } from './modules/accessibility-info';
81
65
  export type { IAccessibilityChangeEvent, IAccessibilityChangeEventName, IAccessibilityChangeEventHandler, IAccessibilityAnnouncementFinishedEvent, IAnnounceForAccessibilityOptions, IAccessibilityEventType, } from './modules/accessibility-info';
82
- export { I18nManager } from './modules/i18n-manager';
83
- export type { II18nManagerConstants } from './modules/i18n-manager';
84
- export { Settings } from './modules/settings';
85
66
  export { PanResponder } from '@symbiote-native/engine';
86
67
  export type { IPanResponderGestureState, IPanResponderCallbacks, IGestureResponderHandlers, IPanResponderInstance, } from '@symbiote-native/engine';
87
- export { LayoutAnimation } from './modules/layout-animation';
88
- export type { ILayoutAnimationType, ILayoutAnimationProperty, ILayoutAnimationConfig, ILayoutAnimationAnim, } from './modules/layout-animation';
89
68
  export { InteractionManager } from '@symbiote-native/engine';
90
69
  export type { IInteractionEvent, ISimpleTask, IPromiseTask, ITask, IHandle, } from '@symbiote-native/engine';
91
- export { BackHandler } from './modules/back-handler';
92
- export type { IBackPressEventName, IBackPressHandler } from './modules/back-handler';
93
- export { ToastAndroid } from './modules/toast-android';
94
- export { PermissionsAndroid, PERMISSIONS, RESULTS } from './modules/permissions-android';
95
- export type { IPermission, IPermissionStatus, IRationale } from './modules/permissions-android';
96
70
  export { TouchableNativeFeedback } from './components/touchable-native-feedback';
97
71
  export type { ITouchableNativeFeedbackProps, INativeFeedbackBackground, IThemeAttrBackground, IRippleBackground, } from './components/touchable-native-feedback';
98
- export type { ISymbioteEvent } from '@symbiote-native/engine';
72
+ export type { ISymbioteEvent, ISymbioteNode, IRootTag } from '@symbiote-native/engine';
73
+ export type { ICellLayout, ISeparatorProps, ISeparators, IModalOrientationChangeEvent, IPressableAndroidRippleConfig, IEnterKeyHint, IInputMode, ISubmitBehavior, ITextInputSelection, IImageStatics, } from '@symbiote-native/components';
package/build/index.js CHANGED
@@ -7,7 +7,6 @@ export { ImageBackground } from './components/image-background/index.js';
7
7
  export { ScrollView } from './components/scroll-view';
8
8
  export { TextInput } from './components/text-input/index.js';
9
9
  export { InputAccessoryView } from './components/input-accessory-view/index.js';
10
- export { Keyboard, KEYBOARD_EVENT } from './modules/keyboard.js';
11
10
  export { KeyboardAvoidingView } from './components/keyboard-avoiding-view/index.js';
12
11
  export { StatusBar } from './modules/status-bar';
13
12
  export { Switch } from './components/switch';
@@ -51,37 +50,28 @@ export { Platform, StyleSheet } from '@symbiote-native/engine';
51
50
  // processColor runs a color through the injected platform processor. All pure /
52
51
  // seam-backed, so they live in shared and the adapter re-exports them.
53
52
  export { PlatformColor, DynamicColorIOS, processColor } from '@symbiote-native/engine';
54
- // Wired once by the app entry on a real host (like setColorProcessor): hands shared
55
- // RN's ViewConfig registry so third-party Fabric views auto-derive their metadata:
53
+ // The three app-entry seams, wired once on a real host, so the barrel exposes them together.
54
+ // setNativeViewConfigSource hands the engine RN's ViewConfig registry, which is how third-party
55
+ // Fabric views auto-derive their metadata:
56
56
  // setNativeViewConfigSource(name => ReactNativeViewConfigRegistry.get(name))
57
- export { setNativeViewConfigSource } from '@symbiote-native/engine';
58
- // Runtime modules: native-bridge consumers, same shape as Keyboard/StatusBar:
59
- // thin JS over getNativeModule + device events, no Fabric component of their own.
60
- export { Dimensions } from './modules/dimensions.js';
61
- export { PixelRatio } from '@symbiote-native/engine';
57
+ export { setNativeViewConfigSource, setColorProcessor, setDeviceEventSource, } from '@symbiote-native/engine';
58
+ // Diagnostics, gated by DEBUG (<keep_logs_gate_behind_DEBUG>): app code logs through the same
59
+ // seam the engine does instead of a bare console.log.
60
+ export { dlog, isDebug } from '@symbiote-native/engine';
61
+ // Imperative runtime modules: the SAME module every adapter shares, re-exported straight from
62
+ // @symbiote-native/engine so app code names only @symbiote-native/react (RN's single import
63
+ // root). Thin JS over getNativeModule + device events, no Fabric component of their own.
64
+ export { Alert, Share, ActionSheetIOS, Linking, Vibration, ToastAndroid, Settings, I18nManager, Dimensions, Appearance, AppState, Keyboard, KEYBOARD_EVENT, BackHandler, PermissionsAndroid, PERMISSIONS, RESULTS, LayoutAnimation, PixelRatio, } from '@symbiote-native/engine';
65
+ // React lifecycle over those core device-state modules.
62
66
  export { useWindowDimensions } from './hooks/use-window-dimensions.js';
63
- export { Appearance } from './modules/appearance.js';
64
67
  export { useColorScheme } from './hooks/use-color-scheme.js';
65
- export { AppState } from './modules/app-state.js';
66
- export { Alert } from './modules/alert.js';
67
- export { ActionSheetIOS } from './modules/action-sheet-ios.js';
68
- export { Linking } from './modules/linking.js';
69
- export { Vibration } from './modules/vibration.js';
70
- export { Share } from './modules/share.js';
71
68
  export { AccessibilityInfo } from './modules/accessibility-info';
72
- export { I18nManager } from './modules/i18n-manager.js';
73
- export { Settings } from './modules/settings.js';
74
69
  // Interaction subsystems: gestures, deferred work, and layout transitions.
75
70
  export { PanResponder } from '@symbiote-native/engine';
76
- export { LayoutAnimation } from './modules/layout-animation.js';
77
71
  // InteractionManager is pure JS, so it lives in shared; re-exported here so app code
78
72
  // names only @symbiote-native/react (RN's single import root).
79
73
  export { InteractionManager } from '@symbiote-native/engine';
80
- // Android-only surface (the second-platform pass). Each is a thin JS shim over an
81
- // Android native module / Fabric view, inert on iOS (no module → graceful no-op,
82
- // no native view → degrade to a plain container). Native module name correctness
83
- // can only be confirmed on a real device/simulator, not headless tests.
84
- export { BackHandler } from './modules/back-handler.js';
85
- export { ToastAndroid } from './modules/toast-android.js';
86
- export { PermissionsAndroid, PERMISSIONS, RESULTS } from './modules/permissions-android.js';
74
+ // Android-only surface (the second-platform pass): a thin JS shim over an Android Fabric view,
75
+ // inert on iOS (no native view -> degrades to a plain container). The Android-only MODULES
76
+ // (ToastAndroid, PermissionsAndroid, BackHandler) sit in the engine block above.
87
77
  export { TouchableNativeFeedback } from './components/touchable-native-feedback/index.js';
@@ -13,8 +13,8 @@
13
13
  // and detaches on unmount / when the leaf identity changes. The per-frame path is
14
14
  // then: value.setValue / animation -> flushValue -> AnimatedProps.update() ->
15
15
  // setNativeProps(node, partial).
16
- import { createElement, useEffect, useMemo, useRef, } from 'react';
17
- import { AnimatedProps, attachNativeEventHandler, isNativeAnimatedAvailable, reduceProps, readPassthroughStyle, resolveHostNode, } from '@symbiote-native/engine';
16
+ import { createElement, useEffect, useRef, } from 'react';
17
+ import { createAnimatedLeafLifecycle, isSymbioteNode, isNativeAnimatedAvailable, reduceProps, readPassthroughStyle, resolveHostNode, } from '@symbiote-native/engine';
18
18
  // A ref can be a function or a `.current` object; assign through both forms without
19
19
  // casting so a forwarded ref from the caller still receives the instance. Framework-
20
20
  // ref-specific, so it stays per-adapter (the rest of the wrap helpers are shared).
@@ -33,77 +33,36 @@ export function createAnimatedComponent(Component) {
33
33
  // Native driving is opt-in per the passthrough prop AND requires a real native module;
34
34
  // headless / unsupported hosts keep the JS flush path (and the existing JS smokes green).
35
35
  const wantsNative = passthrough != null && isNativeAnimatedAvailable();
36
- // One AnimatedProps leaf per distinct props object. Rebuilt when props change,
37
- // so a newly-added animated value joins the graph; the effect below swaps the
38
- // graph attachment to match.
39
- const animatedProps = useMemo(() => new AnimatedProps(rest), [rest]);
40
- // The leaf currently wired into the value graph. Tracked across renders so a swap
41
- // can attach the new leaf BEFORE detaching the old one.
42
- const attached = useRef(null);
43
- // The committed host node, captured by the ref below. Needed in the event-attach
44
- // effect (a native event binds to the node's tag, not the AnimatedProps leaf).
36
+ // The leaf lifecycle - build/swap/bind/detach, native event rebinding, and the
37
+ // rebuild-vs-skip decision - is the engine's, shared by every adapter
38
+ // (core/engine/src/animated/leaf-lifecycle.ts). React owns only WHEN to run it.
39
+ //
40
+ // This used to be `useMemo(() => new AnimatedProps(rest), [rest])`, which reads like the same
41
+ // guard but never was one: `rest` comes out of a rest-destructure, so it is a fresh object on
42
+ // every render and the memo's dependency always differed. The real content check now lives in
43
+ // the shared lifecycle.
44
+ const lifecycleRef = useRef(null);
45
+ lifecycleRef.current ??= createAnimatedLeafLifecycle('react');
46
+ const lifecycle = lifecycleRef.current;
47
+ // The committed host node, captured by the ref below - a native event binds to the node's
48
+ // tag, not the AnimatedProps leaf.
45
49
  const nodeRef = useRef(null);
46
- // Swap the graph attachment to the current leaf, attaching the new one FIRST and
47
- // detaching the previous one SECOND. Order is load-bearing: a shared Value node
48
- // self-detaches (and drops its native animation node) the instant its child count
49
- // hits zero, so detaching the old leaf before the new one is attached would kill a
50
- // running native-driven animation on any unrelated re-render. Detaching here (not
51
- // in a cleanup, which React runs BEFORE the next setup) keeps the new-before-old
52
- // order, mirroring RN's AnimatedComponent._attachProps.
50
+ // Reconcile after every commit. No dependency array on purpose: the props object is rebuilt
51
+ // by every render anyway, so a dependency list could only ever say "always" - the real
52
+ // rebuild-vs-skip decision is the lifecycle's, and it compares CONTENT by key identity.
53
53
  useEffect(() => {
54
- animatedProps.__attach();
55
- const previous = attached.current;
56
- attached.current = animatedProps;
57
- if (previous !== null && previous !== animatedProps) {
58
- previous.__detach();
59
- }
60
- }, [animatedProps]);
61
- // Native-driver trigger. Runs after attach: push the leaf -> style -> transform
62
- // -> interpolation -> value chain native so the props animate on the UI thread (no JS lag).
63
- // Cascades down to the source value; the scroll event attaches to that same value
64
- // (idempotent). __makeNative is idempotent, so re-firing on a leaf swap is safe.
54
+ lifecycle.reconcile(rest, isSymbioteNode(nodeRef.current) ? nodeRef.current : null, wantsNative);
55
+ });
56
+ // Final teardown: detach the last-attached leaf and any native event bindings on unmount.
65
57
  useEffect(() => {
66
- if (wantsNative)
67
- animatedProps.__makeNative();
68
- }, [animatedProps, wantsNative]);
69
- // Final teardown: detach the last-attached leaf when the component unmounts.
70
- useEffect(() => {
71
- return () => {
72
- if (attached.current !== null) {
73
- attached.current.__detach();
74
- attached.current = null;
75
- }
76
- };
77
- }, []);
78
- // Native-attach any Animated.event prop, e.g. onScroll={Animated.event(…,
79
- // {useNativeDriver:true})}, to the committed node, so the event drives its values on
80
- // the UI thread. attachNativeEventHandler no-ops (returns undefined) unless the prop is
81
- // a native event handler with a committed tag, so the JS path stays the fallback. The
82
- // __makeNative cascade then carries the bound interpolations/props native too. Keyed on
83
- // animatedProps (rebuilt with rest) so a new inline event re-attaches; cleanup detaches.
84
- useEffect(() => {
85
- const node = nodeRef.current;
86
- if (node === null)
87
- return;
88
- const detachers = [];
89
- for (const key of Object.keys(rest)) {
90
- const attachment = attachNativeEventHandler(node, key, rest[key]);
91
- if (attachment !== undefined)
92
- detachers.push(attachment.detach);
93
- }
94
- return () => {
95
- for (const detach of detachers)
96
- detach();
97
- };
98
- }, [animatedProps]);
58
+ return () => lifecycle.teardown();
59
+ }, [lifecycle]);
99
60
  // Callback ref: when the base component mounts, capture its public instance, resolve
100
61
  // it to the underlying host node (unwrapping a scroll-container handle), record THAT
101
62
  // for the event-attach effect and bind it to the leaf, but forward the ORIGINAL
102
63
  // instance to the caller, who expects the component's public handle (scrollTo, …).
103
64
  const captureRef = (instance) => {
104
- const node = resolveHostNode(instance);
105
- nodeRef.current = node;
106
- animatedProps.setNativeView(node);
65
+ nodeRef.current = resolveHostNode(instance);
107
66
  assignRef(forwardedRef, instance);
108
67
  };
109
68
  // Reduced props are P-shaped (animated nodes already replaced by values); add the
package/build/render.js CHANGED
@@ -1,10 +1,24 @@
1
1
  // Mount a React element tree onto a Fabric surface. The native host hands us a
2
2
  // rootTag (via AppRegistry.registerRunnable); we create a surface for it and let
3
3
  // the reconciler drive shared, which commits into nativeFabricUIManager.
4
- import { createSurface, disposeRoot, setEventDispatcher, dlog, } from '@symbiote-native/engine';
4
+ import { createSurface, disposeRoot, setEventDispatcher, dlog, reportUncaughtError, } from '@symbiote-native/engine';
5
5
  import reconciler, { withDiscretePriority } from './host-config.js';
6
6
  import { LegacyRoot } from './reconciler-constants.js';
7
7
  const noop = () => { };
8
+ function errorReporter(origin) {
9
+ return (error, info) => {
10
+ reportUncaughtError(error, { origin, componentStack: info.componentStack });
11
+ };
12
+ }
13
+ const onUncaughtError = errorReporter('react render (no error boundary)');
14
+ // A boundary handled it, and it is still reported - the boundary decides what the USER sees, not
15
+ // whether the developer hears about it. Upstream's nativeOnCaughtError calls the same
16
+ // showErrorDialog as the uncaught path.
17
+ const onCaughtError = errorReporter('react render (caught by error boundary)');
18
+ // React recovered on its own (a concurrent render retried and succeeded), so this is not fatal -
19
+ // but it is still a real error the app swallowed a first render over. RN keeps React's
20
+ // defaultOnRecoverableError here, which reports it globally.
21
+ const onRecoverableError = errorReporter('react render (recovered)');
8
22
  // A native event runs the listener (which may call setState) outside React's
9
23
  // loop. Run it at discrete priority so the update takes the sync lane, then
10
24
  // flush that work synchronously to paint the result.
@@ -41,7 +55,11 @@ export function mount(rootTag, element) {
41
55
  // first) starts clean. Otherwise the stale container double-drives the surface.
42
56
  teardown(rootTag);
43
57
  const surface = createSurface(rootTag);
44
- const container = reconciler.createContainer(surface, LegacyRoot, null, false, null, 'symbiote', noop, noop, noop, noop, null);
58
+ const container = reconciler.createContainer(surface, LegacyRoot, null, false, null, 'symbiote', onUncaughtError, onCaughtError, onRecoverableError,
59
+ // onDefaultTransitionIndicator - the ONE that is genuinely a no-op. RN's own renderer says
60
+ // so in as many words: "Native doesn't have a default indicator" (ReactFabric.js's
61
+ // nativeOnDefaultTransitionIndicator).
62
+ noop, null);
45
63
  containers.set(rootTag, container);
46
64
  // react-reconciler 0.33 exposes updateContainerSync + flushSyncWork for an
47
65
  // immediate render/commit; @types 0.32 still lists the older updateContainer /
@@ -1,13 +1,8 @@
1
- // Re-exports @symbiote-native/css-parser verbatim from INSIDE this package, so a consuming app's own
2
- // require('@symbiote-native/react/metro-css-parser') resolves css-parser relative to THIS file's
3
- // location (adapters/react/node_modules, where css-parser IS a real dependency — see
4
- // package.json), not relative to the app's own node_modules, which does not and should not
5
- // need to declare @symbiote-native/css-parser itself. Node resolves each require() relative to the
6
- // requiring FILE's own location, so this indirection is what actually removes the extra install
7
- // step, not pnpm hoisting (which does not propagate this transitively across workspace packages
8
- // the way a flat classic node_modules would). .cjs, not .js: this package is "type": "module",
9
- // and Metro's babelTransformerPath loading expects a require()-able module.
10
- // createCssMetroTransformer is a factory, not a ready transformer — Metro's
11
- // babelTransformerPath needs the actual {transform, getCacheKey} object it returns,
12
- // not the css-parser package barrel.
1
+ // Re-exported from THIS file's own location so a consuming app's
2
+ // require('@symbiote-native/react/metro-css-parser') resolves css-parser as a dependency of THIS
3
+ // package, not the app's node_modules (Node resolves require() relative to the requiring file,
4
+ // not via pnpm hoisting) — the app never needs to declare @symbiote-native/css-parser itself.
5
+ // .cjs, not .js: this package is "type": "module", but Metro's babelTransformerPath loading needs
6
+ // a require()-able file. createCssMetroTransformer() is a factory — Metro needs the actual
7
+ // {transform, getCacheKey} object it returns, not the css-parser package barrel.
13
8
  module.exports = require('@symbiote-native/css-parser').createCssMetroTransformer();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@symbiote-native/react",
3
- "version": "0.2.8",
3
+ "version": "0.3.0",
4
4
  "description": "SymbioteNative's React adapter — a react-reconciler host config (mutation mode) driving real native iOS/Android views through stock React Native Fabric.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -37,8 +37,8 @@
37
37
  },
38
38
  "dependencies": {
39
39
  "react-reconciler": "^0.33.0",
40
- "@symbiote-native/components": "0.3.0",
41
- "@symbiote-native/css-parser": "0.2.3"
40
+ "@symbiote-native/components": "0.4.0",
41
+ "@symbiote-native/css-parser": "0.3.0"
42
42
  },
43
43
  "peerDependencies": {
44
44
  "@symbiote-native/engine": ">=0.1.7",
@@ -49,7 +49,7 @@
49
49
  "@types/react": "^19.2.0",
50
50
  "@types/react-reconciler": "^0.32.2",
51
51
  "react": "19.2.3",
52
- "@symbiote-native/engine": "0.1.7",
52
+ "@symbiote-native/engine": "0.2.0",
53
53
  "@symbiote-native/test-utils": "0.1.6"
54
54
  },
55
55
  "scripts": {
@@ -1,2 +0,0 @@
1
- export { ActionSheetIOS } from '@symbiote-native/engine';
2
- export type { IActionSheetIOSOptions, IShareActionSheetIOSOptions, IShareActionSheetError, } from '@symbiote-native/engine';
@@ -1,4 +0,0 @@
1
- // ActionSheetIOS lives framework-agnostic in @symbiote-native/engine (imperative
2
- // native-bridge module, no visual, no lifecycle). The React adapter re-exports it
3
- // verbatim so the public surface is unchanged.
4
- export { ActionSheetIOS } from '@symbiote-native/engine';
@@ -1,2 +0,0 @@
1
- export { Alert } from '@symbiote-native/engine';
2
- export type { IAlertType, IAlertButtonStyle, IAlertButton, IAlertButtons, IAlertOptions, } from '@symbiote-native/engine';
@@ -1,5 +0,0 @@
1
- // Alert lives framework-agnostic in @symbiote-native/engine (imperative native-bridge
2
- // module, no visual, no lifecycle). The React adapter re-exports it verbatim so the
3
- // public surface is unchanged; the platform split (alert.ios/alert.android) lives
4
- // inside engine.
5
- export { Alert } from '@symbiote-native/engine';
@@ -1,2 +0,0 @@
1
- export { AppState } from '@symbiote-native/engine';
2
- export type { IAppStateStatus, IAppStateEvent } from '@symbiote-native/engine';
@@ -1,3 +0,0 @@
1
- // AppState moved to @symbiote-native/engine (a framework-agnostic device-state module re-exported
2
- // by every adapter). This thin re-export keeps @symbiote-native/react's public surface identical.
3
- export { AppState } from '@symbiote-native/engine';
@@ -1,2 +0,0 @@
1
- export { Appearance } from '@symbiote-native/engine';
2
- export type { IColorSchemeName, IColorSchemePreference } from '@symbiote-native/engine';
@@ -1,3 +0,0 @@
1
- // Appearance moved to @symbiote-native/engine (a framework-agnostic device-state module re-exported
2
- // by every adapter). This thin re-export keeps @symbiote-native/react's public surface identical.
3
- export { Appearance } from '@symbiote-native/engine';
@@ -1,2 +0,0 @@
1
- export { BackHandler } from '@symbiote-native/engine';
2
- export type { IBackPressEventName, IBackPressHandler } from '@symbiote-native/engine';
@@ -1,3 +0,0 @@
1
- // BackHandler moved to @symbiote-native/engine (a framework-agnostic device-state module re-exported
2
- // by every adapter). This thin re-export keeps @symbiote-native/react's public surface identical.
3
- export { BackHandler } from '@symbiote-native/engine';
@@ -1,2 +0,0 @@
1
- export { Dimensions } from '@symbiote-native/engine';
2
- export type { IDisplayMetrics, IDisplayMetricsAndroid, IDimensionsPayload, IDimensionsSet, IDimensionsKey, IDimensionsChangeListener, IDimensionsStatic, } from '@symbiote-native/engine';
@@ -1,3 +0,0 @@
1
- // Dimensions moved to @symbiote-native/engine (a framework-agnostic device-state module re-exported
2
- // by every adapter). This thin re-export keeps @symbiote-native/react's public surface identical.
3
- export { Dimensions } from '@symbiote-native/engine';
@@ -1,2 +0,0 @@
1
- export { I18nManager } from '@symbiote-native/engine';
2
- export type { II18nManagerConstants } from '@symbiote-native/engine';
@@ -1,4 +0,0 @@
1
- // I18nManager now lives framework-agnostic in @symbiote-native/engine (imperative
2
- // native-bridge module, no visual, no lifecycle). The React adapter re-exports it
3
- // verbatim so the public surface is unchanged.
4
- export { I18nManager } from '@symbiote-native/engine';
@@ -1,2 +0,0 @@
1
- export { Keyboard, KEYBOARD_EVENT } from '@symbiote-native/engine';
2
- export type { IKeyboardEventName, IKeyboardEvent, IKeyboardMetrics } from '@symbiote-native/engine';
@@ -1,3 +0,0 @@
1
- // Keyboard moved to @symbiote-native/engine (a framework-agnostic device-state module re-exported
2
- // by every adapter). This thin re-export keeps @symbiote-native/react's public surface identical.
3
- export { Keyboard, KEYBOARD_EVENT } from '@symbiote-native/engine';
@@ -1,2 +0,0 @@
1
- export { LayoutAnimation } from '@symbiote-native/engine';
2
- export type { ILayoutAnimationType, ILayoutAnimationProperty, ILayoutAnimationConfig, ILayoutAnimationAnim, ILayoutAnimationTypes, ILayoutAnimationProperties, } from '@symbiote-native/engine';
@@ -1,3 +0,0 @@
1
- // LayoutAnimation moved to @symbiote-native/engine (a framework-agnostic native-bridge consumer
2
- // re-exported by every adapter). This thin re-export keeps @symbiote-native/react's surface identical.
3
- export { LayoutAnimation } from '@symbiote-native/engine';
@@ -1,2 +0,0 @@
1
- export { Linking } from '@symbiote-native/engine';
2
- export type { IUrlEvent } from '@symbiote-native/engine';
@@ -1,4 +0,0 @@
1
- // Linking now lives framework-agnostic in @symbiote-native/engine (imperative native-bridge
2
- // module, no visual, no lifecycle). The React adapter re-exports it verbatim; the
3
- // platform split (linking.ios/linking.android) lives inside engine.
4
- export { Linking } from '@symbiote-native/engine';
@@ -1,2 +0,0 @@
1
- export { PermissionsAndroid, PERMISSIONS, RESULTS } from '@symbiote-native/engine';
2
- export type { IPermission, IPermissionStatus, IRationale } from '@symbiote-native/engine';
@@ -1,3 +0,0 @@
1
- // PermissionsAndroid moved to @symbiote-native/engine (a framework-agnostic device-state module
2
- // re-exported by every adapter). This thin re-export keeps @symbiote-native/react's surface identical.
3
- export { PermissionsAndroid, PERMISSIONS, RESULTS } from '@symbiote-native/engine';
@@ -1 +0,0 @@
1
- export { Settings } from '@symbiote-native/engine';
@@ -1,4 +0,0 @@
1
- // Settings now lives framework-agnostic in @symbiote-native/engine (imperative native-bridge
2
- // module, no visual, no lifecycle). The React adapter re-exports it verbatim so the
3
- // public surface is unchanged.
4
- export { Settings } from '@symbiote-native/engine';
@@ -1,2 +0,0 @@
1
- export { Share } from '@symbiote-native/engine';
2
- export type { IShareContent, IShareOptions, IShareAction } from '@symbiote-native/engine';
@@ -1,4 +0,0 @@
1
- // Share now lives framework-agnostic in @symbiote-native/engine (imperative native-bridge
2
- // module, no visual, no lifecycle). The React adapter re-exports it verbatim; the
3
- // platform split (share.ios/share.android) lives inside engine.
4
- export { Share } from '@symbiote-native/engine';
@@ -1 +0,0 @@
1
- export { currentlyFocusedInput, setInputFocused, setInputBlurred, blurTextInput, } from '@symbiote-native/engine';
@@ -1,3 +0,0 @@
1
- // TextInputState moved to @symbiote-native/engine (a framework-agnostic module: Keyboard.dismiss
2
- // and TextInput both reach it). This thin re-export keeps the adapter's import path stable.
3
- export { currentlyFocusedInput, setInputFocused, setInputBlurred, blurTextInput, } from '@symbiote-native/engine';
@@ -1 +0,0 @@
1
- export { ToastAndroid } from '@symbiote-native/engine';
@@ -1,4 +0,0 @@
1
- // ToastAndroid lives framework-agnostic in @symbiote-native/engine (imperative
2
- // native-bridge module, no visual, no lifecycle). The React adapter re-exports it
3
- // verbatim so the public surface is unchanged.
4
- export { ToastAndroid } from '@symbiote-native/engine';
@@ -1 +0,0 @@
1
- export { Vibration } from '@symbiote-native/engine';
@@ -1,4 +0,0 @@
1
- // Vibration lives framework-agnostic in @symbiote-native/engine (imperative native-bridge
2
- // module, no visual, no lifecycle). The React adapter re-exports it verbatim; the
3
- // platform split (vibration.ios/vibration.android) lives inside the engine.
4
- export { Vibration } from '@symbiote-native/engine';