@symbiote-native/vue 0.1.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 (104) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +142 -0
  3. package/build/components/activity-indicator/index.android.d.ts +4 -0
  4. package/build/components/activity-indicator/index.android.js +10 -0
  5. package/build/components/activity-indicator/index.d.ts +1 -0
  6. package/build/components/activity-indicator/index.ios.d.ts +4 -0
  7. package/build/components/activity-indicator/index.ios.js +10 -0
  8. package/build/components/activity-indicator/index.js +4 -0
  9. package/build/components/activity-indicator/shared.d.ts +8 -0
  10. package/build/components/activity-indicator/shared.js +48 -0
  11. package/build/components/button.d.ts +14 -0
  12. package/build/components/button.js +59 -0
  13. package/build/components/drawer-layout-android/index.android.d.ts +8 -0
  14. package/build/components/drawer-layout-android/index.android.js +144 -0
  15. package/build/components/drawer-layout-android/index.d.ts +8 -0
  16. package/build/components/drawer-layout-android/index.js +36 -0
  17. package/build/components/drawer-layout-android/shared.d.ts +18 -0
  18. package/build/components/drawer-layout-android/shared.js +6 -0
  19. package/build/components/flat-list/index.d.ts +88 -0
  20. package/build/components/flat-list/index.js +189 -0
  21. package/build/components/image-background.d.ts +9 -0
  22. package/build/components/image-background.js +114 -0
  23. package/build/components/image.d.ts +9 -0
  24. package/build/components/image.js +99 -0
  25. package/build/components/input-accessory-view.d.ts +10 -0
  26. package/build/components/input-accessory-view.js +41 -0
  27. package/build/components/keyboard-avoiding-view.d.ts +18 -0
  28. package/build/components/keyboard-avoiding-view.js +121 -0
  29. package/build/components/modal.d.ts +29 -0
  30. package/build/components/modal.js +134 -0
  31. package/build/components/pressable.d.ts +58 -0
  32. package/build/components/pressable.js +221 -0
  33. package/build/components/refresh-control.d.ts +20 -0
  34. package/build/components/refresh-control.js +53 -0
  35. package/build/components/safe-area-view.d.ts +8 -0
  36. package/build/components/safe-area-view.js +33 -0
  37. package/build/components/scroll-view/index.android.d.ts +4 -0
  38. package/build/components/scroll-view/index.android.js +74 -0
  39. package/build/components/scroll-view/index.d.ts +1 -0
  40. package/build/components/scroll-view/index.ios.d.ts +4 -0
  41. package/build/components/scroll-view/index.ios.js +15 -0
  42. package/build/components/scroll-view/index.js +4 -0
  43. package/build/components/scroll-view/shared.d.ts +96 -0
  44. package/build/components/scroll-view/shared.js +297 -0
  45. package/build/components/scroll-view/sticky-header.d.ts +7 -0
  46. package/build/components/scroll-view/sticky-header.js +195 -0
  47. package/build/components/section-list/index.d.ts +39 -0
  48. package/build/components/section-list/index.js +77 -0
  49. package/build/components/switch/index.android.d.ts +12 -0
  50. package/build/components/switch/index.android.js +13 -0
  51. package/build/components/switch/index.d.ts +2 -0
  52. package/build/components/switch/index.ios.d.ts +12 -0
  53. package/build/components/switch/index.ios.js +11 -0
  54. package/build/components/switch/index.js +5 -0
  55. package/build/components/switch/shared.d.ts +23 -0
  56. package/build/components/switch/shared.js +129 -0
  57. package/build/components/text-input/index.d.ts +24 -0
  58. package/build/components/text-input/index.js +257 -0
  59. package/build/components/touchable-native-feedback.d.ts +24 -0
  60. package/build/components/touchable-native-feedback.js +75 -0
  61. package/build/components/touchable.d.ts +44 -0
  62. package/build/components/touchable.js +150 -0
  63. package/build/components/virtualized-list/index.d.ts +121 -0
  64. package/build/components/virtualized-list/index.js +687 -0
  65. package/build/components/virtualized-section-list/index.d.ts +96 -0
  66. package/build/components/virtualized-section-list/index.js +160 -0
  67. package/build/components.d.ts +51 -0
  68. package/build/components.js +26 -0
  69. package/build/composables/use-color-scheme.d.ts +3 -0
  70. package/build/composables/use-color-scheme.js +24 -0
  71. package/build/composables/use-window-dimensions.d.ts +3 -0
  72. package/build/composables/use-window-dimensions.js +34 -0
  73. package/build/create-tunnel.d.ts +9 -0
  74. package/build/create-tunnel.js +37 -0
  75. package/build/descriptor-to-vue.d.ts +3 -0
  76. package/build/descriptor-to-vue.js +14 -0
  77. package/build/host-instance/index.d.ts +2 -0
  78. package/build/host-instance/index.js +34 -0
  79. package/build/index.d.ts +67 -0
  80. package/build/index.js +90 -0
  81. package/build/modules/animated/create-animated-component.d.ts +4 -0
  82. package/build/modules/animated/create-animated-component.js +169 -0
  83. package/build/modules/animated/index.d.ts +55 -0
  84. package/build/modules/animated/index.js +66 -0
  85. package/build/modules/app-registry/index.d.ts +7 -0
  86. package/build/modules/app-registry/index.js +32 -0
  87. package/build/modules/status-bar.d.ts +17 -0
  88. package/build/modules/status-bar.js +65 -0
  89. package/build/render.d.ts +7 -0
  90. package/build/render.js +43 -0
  91. package/build/renderer.d.ts +4 -0
  92. package/build/renderer.js +129 -0
  93. package/build/runtime-helpers.d.ts +28 -0
  94. package/build/runtime-helpers.js +68 -0
  95. package/build/utils/component-helpers.d.ts +2 -0
  96. package/build/utils/component-helpers.js +13 -0
  97. package/build/utils/model-binding.d.ts +4 -0
  98. package/build/utils/model-binding.js +17 -0
  99. package/build/utils/normalize-attrs.d.ts +1 -0
  100. package/build/utils/normalize-attrs.js +30 -0
  101. package/build/utils/slots-to-render-props.d.ts +4 -0
  102. package/build/utils/slots-to-render-props.js +18 -0
  103. package/metro-css-parser.cjs +12 -0
  104. package/package.json +54 -0
package/build/index.js ADDED
@@ -0,0 +1,90 @@
1
+ // @symbiote-native/vue: a thin Vue 3 reconciler over @symbiote-native/engine. createRenderer maps
2
+ // each RendererOptions call onto the engine's mutation API; all Fabric clone-on-write
3
+ // lives in the engine, shared with every other adapter. App code names only @symbiote-native/vue.
4
+ export { mount, unmount } from './render';
5
+ // createTunnel: cross-surface content sharing (Teleport stays same-surface-only by design —
6
+ // see create-tunnel.ts and the vue-adapter-directives skill for why).
7
+ export { createTunnel } from './create-tunnel';
8
+ export { View, Text } from './components';
9
+ // findNodeHandle: RN's ref -> native reactTag lookup, the Vue twin of the React adapter's
10
+ // host-instance resolution. IHostInstance is the raw engine node (Vue host refs fall
11
+ // through to it; imperative methods live on each component's expose() handle).
12
+ export { findNodeHandle } from './host-instance';
13
+ // Image. Full parity with React: source/src/srcSet resolution, the width/height → style fold,
14
+ // alt → accessibility, and the Image statics (getSize / prefetch / queryCache / …) are shared
15
+ // verbatim from @symbiote-native/components via renderImage; Vue supplies only the functional bridge.
16
+ export { Image, setImageSourceResolver } from './components/image';
17
+ // First component off the shared @symbiote-native/components layer: render-only, drives the
18
+ // agnostic renderActivityIndicator through descriptorToVue. Proof the layer is reusable:
19
+ // the render fn is shared verbatim with React; Vue supplies only the bridge.
20
+ export { ActivityIndicator } from './components/activity-indicator';
21
+ // First component to bring the state half into Vue (the lastNativeReport reducer + the
22
+ // snap-back watch); render shared verbatim with React, Vue supplies the reactive lifecycle.
23
+ export { Switch } from './components/switch';
24
+ // ScrollView. Full parity (ADR 0024): vertical + horizontal, every pass-through prop, the
25
+ // synthesized onContentSizeChange, the imperative handle via expose() + shallowRef, RefreshControl
26
+ // (iOS sibling / Android wrap, Phase 2), and sticky headers (Phase 3: the scroll AnimatedValue,
27
+ // the headerLayoutYs cross-talk, and the per-header Animated.View wrap). The pure math (intrinsics,
28
+ // decelerationRate, content-size dedupe, the handle, the sticky interpolation) is shared verbatim
29
+ // with React from @symbiote-native/components.
30
+ export { ScrollView } from './components/scroll-view';
31
+ // Pressable family. Full parity with React (the 3-layer split): the press state machine + render
32
+ // decisions are shared in @symbiote-native/components; Vue supplies the reactivity + descriptor bridge.
33
+ export { Pressable } from './components/pressable';
34
+ export { TouchableOpacity, TouchableHighlight, TouchableWithoutFeedback, } from './components/touchable';
35
+ export { TouchableNativeFeedback } from './components/touchable-native-feedback';
36
+ export { Button } from './components/button';
37
+ // TextInput. Full parity with React (the 3-layer split): the value/selection folds + the
38
+ // controlled-write predicate live in @symbiote-native/components; Vue supplies the reactivity
39
+ // (shallowRef host node, post-flush watch) + the imperative handle via expose().
40
+ export { TextInput } from './components/text-input';
41
+ // VirtualizedList family. Full parity with React: the windowing math (visible range, cell
42
+ // keys, viewability token diffing, edge-reached, the list plan) is shared in
43
+ // @symbiote-native/components; Vue supplies the reactive lifecycle + the per-cell element.
44
+ export { VirtualizedList } from './components/virtualized-list';
45
+ export { FlatList } from './components/flat-list';
46
+ export { VirtualizedSectionList } from './components/virtualized-section-list';
47
+ export { SectionList } from './components/section-list';
48
+ // Phase 2 (ADR 0024): SafeAreaView + RefreshControl, wired into ScrollView with the iOS-sibling /
49
+ // Android-wrap platform split. RefreshControl hosts the wrapped scroll view via its default slot.
50
+ export { SafeAreaView } from './components/safe-area-view';
51
+ export { RefreshControl } from './components/refresh-control';
52
+ export { descriptorToVue } from './descriptor-to-vue';
53
+ // normalizeVueAttrs: the kebab→camel attr fold every component applies at entry. Exported so an
54
+ // external wrapper package (e.g. @symbiote-native/slider/vue over a third-party native view) can fold
55
+ // its incoming attrs through the SAME transform rather than reimplementing it.
56
+ export { normalizeVueAttrs } from './utils/normalize-attrs';
57
+ // resolveModelValue/emitModelUpdate: the v-model (Rule 6, vue-adapter-events skill) helper every
58
+ // controlled-value component uses. Exported for the same external-package reason as
59
+ // normalizeVueAttrs above (e.g. @symbiote-native/slider/vue).
60
+ export { resolveModelValue, emitModelUpdate } from './utils/model-binding';
61
+ export { createSymbioteRenderer } from './renderer';
62
+ // Animated (ADR 0024 Phase 3a): Animated.View/Text/Image + the lazy Animated.ScrollView over the
63
+ // Vue primitives, with the value graph / easing / drivers spread from @symbiote-native/engine. The wrap
64
+ // mechanism (createAnimatedComponent) is the Vue twin of React's; the pure leaves live in the
65
+ // engine.
66
+ export { Animated, createAnimatedComponent } from './modules/animated';
67
+ // Visual components off the shared @symbiote-native/components layer (wave: simple-visual).
68
+ export { ImageBackground } from './components/image-background';
69
+ export { InputAccessoryView } from './components/input-accessory-view';
70
+ export { Modal } from './components/modal';
71
+ // KeyboardAvoidingView: full parity via the shared inset math; Vue owns the Keyboard subscription.
72
+ export { KeyboardAvoidingView } from './components/keyboard-avoiding-view';
73
+ // StatusBar: declarative component + the shared imperative API.
74
+ export { StatusBar } from './modules/status-bar';
75
+ // AppRegistry: RN's app entry point over `mount`. setHostRegistrar wires RN's own
76
+ // registrar so the native Fabric host finds our runnable by app key.
77
+ export { AppRegistry, setHostRegistrar } from './modules/app-registry';
78
+ // Vue composables over the core device-state modules.
79
+ export { useColorScheme } from './composables/use-color-scheme';
80
+ export { useWindowDimensions } from './composables/use-window-dimensions';
81
+ // Imperative runtime modules: the SAME module both adapters share, re-exported from @symbiote-native/engine.
82
+ export { Alert, Share, ActionSheetIOS, Linking, Vibration, ToastAndroid, Settings, I18nManager, Dimensions, Appearance, AppState, Keyboard, KEYBOARD_EVENT, BackHandler, PermissionsAndroid, PERMISSIONS, RESULTS, AccessibilityInfo, LayoutAnimation, } from '@symbiote-native/engine';
83
+ // Re-export the framework-agnostic engine surface (pure utilities + diagnostics).
84
+ export { Platform, StyleSheet, processColor, setColorProcessor, dlog, isDebug, } from '@symbiote-native/engine';
85
+ // Wired once by the app entry on a real host (like setColorProcessor): hands the engine
86
+ // RN's ViewConfig registry so third-party Fabric views auto-derive their metadata.
87
+ export { setNativeViewConfigSource } from '@symbiote-native/engine';
88
+ // Pure utilities that moved to the engine (single source, both adapters re-export):
89
+ // PixelRatio + PanResponder, plus the color builders and the interaction scheduler.
90
+ export { PixelRatio, PanResponder, PlatformColor, DynamicColorIOS, InteractionManager, } from '@symbiote-native/engine';
@@ -0,0 +1,4 @@
1
+ import { type Component } from '@vue/runtime-core';
2
+ export declare function createAnimatedComponent(Component: Component): import("vue").DefineComponent<{}, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
3
+ [key: string]: any;
4
+ }>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
@@ -0,0 +1,169 @@
1
+ // createAnimatedComponent: the Vue twin of the React wrapper. It wraps a base component
2
+ // (View / Text / Image / ScrollView / any) so it accepts AnimatedNodes in its props.
3
+ // Same JS-driven path, NO native driver in the hot loop: a frame is the scoped commit
4
+ // setNativeProps drives from the AnimatedProps leaf (ADR 0016). The framework-agnostic
5
+ // pieces (reduceProps / readPassthroughStyle / resolveHostNode / isAnimatedNode + the
6
+ // AnimatedProps leaf itself) live in @symbiote-native/engine, shared verbatim with React; here
7
+ // Vue supplies only the lifecycle.
8
+ //
9
+ // React → hooks: useMemo(leaf) + three useEffects + a callback ref. Vue → reactivity:
10
+ // a render that rebuilds the leaf each pass (like React's per-render useMemo(rest)) + a
11
+ // post-commit reconcile (onMounted/onUpdated, the Vue twin of a post-commit useEffect) +
12
+ // a function ref. The per-frame path is unchanged and NEVER goes through Vue render:
13
+ // value.setValue / animation -> flushValue -> AnimatedProps.update() -> setNativeProps(node).
14
+ import { defineComponent, h, onBeforeUnmount, onMounted, onUpdated, shallowRef, } from '@vue/runtime-core';
15
+ import { AnimatedProps, attachNativeEventHandler, isNativeAnimatedAvailable, reduceProps, readPassthroughStyle, resolveHostNode, } from '@symbiote-native/engine';
16
+ import { normalizeVueAttrs } from '../../utils/normalize-attrs';
17
+ // RN's prop carrying explicit (already-rasterized) values that override the animated prop in
18
+ // the COMMITTED props (sticky-header passthrough). Named once here so render/reconcile agree.
19
+ const PASSTHROUGH_PROP = 'passthroughAnimatedPropExplicitValues';
20
+ // A base component is either a functional component (View / Text / Image, a function) or a
21
+ // stateful defineComponent (ScrollView, an object). Read its display name without a cast for
22
+ // the wrapper's devtools name.
23
+ function baseName(component) {
24
+ if (component === null || (typeof component !== 'function' && typeof component !== 'object')) {
25
+ return 'Anonymous';
26
+ }
27
+ const display = Reflect.get(component, 'displayName');
28
+ if (typeof display === 'string')
29
+ return display;
30
+ const name = Reflect.get(component, 'name');
31
+ if (typeof name === 'string')
32
+ return name;
33
+ return 'Anonymous';
34
+ }
35
+ export function createAnimatedComponent(Component) {
36
+ return defineComponent({
37
+ name: `Animated(${baseName(Component)})`,
38
+ inheritAttrs: false,
39
+ setup(_props, { attrs: rawAttrs, slots, expose }) {
40
+ // The committed host node, held by IDENTITY in a shallowRef (the reactivity rule: a deep
41
+ // ref() would run it through toReactive() and hand back a Proxy, so AnimatedProps.setNativeView
42
+ // / attachNativeEventHandler would miss the engine's WeakMap mirror, keyed on the raw node).
43
+ // See .claude/skills/vue-adapter-reactivity. Same rule as Switch / ScrollView host nodes.
44
+ const nodeRef = shallowRef(null);
45
+ // The base component's public instance (a ScrollView handle, or the host node for View).
46
+ // Forwarded to a parent ref via expose(). shallowRef: it may itself BE an engine node.
47
+ const instanceRef = shallowRef(null);
48
+ // The AnimatedProps leaf the next reconcile should wire in, rebuilt by render each pass
49
+ // (like React's per-render useMemo(rest)). Plain (non-reactive) so writing it in render
50
+ // triggers no reactive effect. `attached` is the leaf currently in the value graph.
51
+ let pendingLeaf = null;
52
+ let attached = null;
53
+ // The current non-passthrough props, kept so the post-commit event-attach can re-scan them.
54
+ let currentRest = {};
55
+ let wantsNative = false;
56
+ // Detachers for any Animated.event prop bound natively to the committed node (JS path: none).
57
+ let eventDetachers = [];
58
+ function detachEvents() {
59
+ for (const detach of eventDetachers)
60
+ detach();
61
+ eventDetachers = [];
62
+ }
63
+ // Native-attach any Animated.event prop (e.g. onScroll={Animated.event(…,{useNativeDriver:true})})
64
+ // to the committed node. attachNativeEventHandler no-ops (returns undefined) unless the prop is a
65
+ // native event handler with a committed tag, so the JS path stays the fallback. Rebound on every
66
+ // reconcile so a new inline event re-attaches; detached first to avoid leaking the prior binding.
67
+ function attachEvents() {
68
+ detachEvents();
69
+ const node = nodeRef.value;
70
+ if (node === null)
71
+ return;
72
+ for (const key of Object.keys(currentRest)) {
73
+ const attachment = attachNativeEventHandler(node, key, currentRest[key]);
74
+ if (attachment !== undefined)
75
+ eventDetachers.push(attachment.detach);
76
+ }
77
+ }
78
+ // Post-commit reconcile (onMounted first paint, onUpdated every re-render, the Vue twin of a
79
+ // post-commit useEffect). Attach the NEW leaf BEFORE detaching the OLD one: order is load-bearing.
80
+ // A shared Value self-detaches (and drops its native node) the instant its child count hits
81
+ // zero, so detaching first would kill a running native animation on any unrelated re-render
82
+ // (mirrors RN's AnimatedComponent._attachProps). Then bind the captured node, go native if wanted,
83
+ // and rebind native events.
84
+ function reconcile() {
85
+ const newLeaf = pendingLeaf;
86
+ if (newLeaf === null)
87
+ return;
88
+ newLeaf.__attach();
89
+ if (attached !== null && attached !== newLeaf)
90
+ attached.__detach();
91
+ attached = newLeaf;
92
+ const node = nodeRef.value;
93
+ if (node !== null)
94
+ newLeaf.setNativeView(node);
95
+ if (wantsNative)
96
+ newLeaf.__makeNative();
97
+ attachEvents();
98
+ }
99
+ onMounted(reconcile);
100
+ onUpdated(reconcile);
101
+ // Final teardown: detach the last-attached leaf and any native events on unmount.
102
+ onBeforeUnmount(() => {
103
+ detachEvents();
104
+ if (attached !== null) {
105
+ attached.__detach();
106
+ attached = null;
107
+ }
108
+ });
109
+ // Function ref (held stably, so Vue doesn't re-invoke it per patch). On mount the base hands
110
+ // back its public instance; resolve it to the underlying host node (unwrapping a scroll-container
111
+ // handle via getScrollNode), record THAT for the leaf binding / event attach, and keep the
112
+ // ORIGINAL instance for ref forwarding. Vue calls it with null on unmount, node clears.
113
+ const captureRef = (instance) => {
114
+ instanceRef.value = instance;
115
+ nodeRef.value = resolveHostNode(instance);
116
+ };
117
+ // Forward the caller's ref to the base instance: a parent ref on Animated.ScrollView still gets
118
+ // scrollTo (the exposed handle), on Animated.View the host node. A delegating proxy because the
119
+ // instance is captured async (after mount), so a snapshot exposed at setup time would be null.
120
+ expose(new Proxy(Object.create(null), {
121
+ get: (_target, key) => {
122
+ const instance = instanceRef.value;
123
+ if (instance === null || typeof instance !== 'object')
124
+ return undefined;
125
+ return Reflect.get(instance, key);
126
+ },
127
+ has: (_target, key) => {
128
+ const instance = instanceRef.value;
129
+ return instance !== null && typeof instance === 'object' && Reflect.has(instance, key);
130
+ },
131
+ }));
132
+ return () => {
133
+ // Fold kebab template props to camelCase before splitting (idempotent: the base component
134
+ // normalizes too, but keep `rest`/reduceProps on the RN contract here as well).
135
+ const attrs = normalizeVueAttrs(rawAttrs);
136
+ // Split the incoming attrs into the passthrough (consumed) and the rest (forwarded). The
137
+ // passthrough never reaches the base as a prop; it only overrides the committed style below.
138
+ const rest = {};
139
+ let passthrough;
140
+ for (const key of Object.keys(attrs)) {
141
+ if (key === PASSTHROUGH_PROP) {
142
+ passthrough = attrs[key];
143
+ continue;
144
+ }
145
+ rest[key] = attrs[key];
146
+ }
147
+ currentRest = rest;
148
+ // Native driving is opt-in per the passthrough prop AND requires a real native module;
149
+ // headless / unsupported hosts keep the JS flush path (and the JS smokes green).
150
+ wantsNative = passthrough != null && isNativeAnimatedAvailable();
151
+ // One AnimatedProps leaf per render pass (rebuilt like React's useMemo(rest)); the post-commit
152
+ // reconcile wires it into the graph and swaps the previous one out.
153
+ pendingLeaf = new AnimatedProps(rest);
154
+ // Reduced props are concrete (animated nodes replaced by their current values) so the first
155
+ // paint carries real values, exactly like React's first render.
156
+ const reduced = reduceProps(rest);
157
+ // Override the committed style with the explicit passthrough values (last wins via the style
158
+ // array, which the commit layer flattens) so the ShadowTree carries the current transform.
159
+ const passthroughStyle = readPassthroughStyle(passthrough);
160
+ if (passthroughStyle !== undefined) {
161
+ reduced.style =
162
+ reduced.style === undefined ? passthroughStyle : [reduced.style, passthroughStyle];
163
+ }
164
+ reduced.ref = captureRef;
165
+ return h(Component, reduced, slots.default !== undefined ? { default: slots.default } : undefined);
166
+ };
167
+ },
168
+ });
169
+ }
@@ -0,0 +1,55 @@
1
+ import { AnimatedValue, AnimatedValueXY, AnimatedColor, timing, spring, decay, parallel, sequence, stagger, loop, delay, add, subtract, multiply, divide, modulo, diffClamp, event, forkEvent, unforkEvent } from '@symbiote-native/engine';
2
+ import { createAnimatedComponent } from './create-animated-component';
3
+ export { createAnimatedComponent } from './create-animated-component';
4
+ export declare const Animated: {
5
+ Value: typeof AnimatedValue;
6
+ ValueXY: typeof AnimatedValueXY;
7
+ Color: typeof AnimatedColor;
8
+ Easing: {
9
+ step0(n: number): number;
10
+ step1(n: number): number;
11
+ linear(t: number): number;
12
+ ease(t: number): number;
13
+ quad(t: number): number;
14
+ cubic(t: number): number;
15
+ poly(n: number): import("@symbiote-native/engine").IEasingFunction;
16
+ sin(t: number): number;
17
+ circle(t: number): number;
18
+ exp(t: number): number;
19
+ elastic(bounciness?: number): import("@symbiote-native/engine").IEasingFunction;
20
+ back(s?: number): import("@symbiote-native/engine").IEasingFunction;
21
+ bounce(t: number): number;
22
+ bezier(x1: number, y1: number, x2: number, y2: number): import("@symbiote-native/engine").IEasingFunction;
23
+ in(easing: import("@symbiote-native/engine").IEasingFunction): import("@symbiote-native/engine").IEasingFunction;
24
+ out(easing: import("@symbiote-native/engine").IEasingFunction): import("@symbiote-native/engine").IEasingFunction;
25
+ inOut(easing: import("@symbiote-native/engine").IEasingFunction): import("@symbiote-native/engine").IEasingFunction;
26
+ };
27
+ timing: typeof timing;
28
+ spring: typeof spring;
29
+ decay: typeof decay;
30
+ parallel: typeof parallel;
31
+ sequence: typeof sequence;
32
+ stagger: typeof stagger;
33
+ loop: typeof loop;
34
+ delay: typeof delay;
35
+ add: typeof add;
36
+ subtract: typeof subtract;
37
+ multiply: typeof multiply;
38
+ divide: typeof divide;
39
+ modulo: typeof modulo;
40
+ diffClamp: typeof diffClamp;
41
+ event: typeof event;
42
+ forkEvent: typeof forkEvent;
43
+ unforkEvent: typeof unforkEvent;
44
+ View: import("vue").DefineComponent<{}, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
45
+ [key: string]: any;
46
+ }>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
47
+ Text: import("vue").DefineComponent<{}, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
48
+ [key: string]: any;
49
+ }>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
50
+ Image: import("vue").DefineComponent<{}, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
51
+ [key: string]: any;
52
+ }>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
53
+ ScrollView: ReturnType<typeof createAnimatedComponent>;
54
+ createAnimatedComponent: typeof createAnimatedComponent;
55
+ };
@@ -0,0 +1,66 @@
1
+ // The Animated namespace for @symbiote-native/vue: the Vue twin of adapters/react/src/animated/index.ts.
2
+ // createAnimatedComponent applied to the Vue primitives gives Animated.View / Text / Image; the
3
+ // value graph, easing and imperative drivers come from @symbiote-native/engine (framework-agnostic,
4
+ // JS-driven, ADR 0016), spread in verbatim. Both halves meet here so the familiar surface,
5
+ // `Animated.timing(new Animated.Value(0), …).start()`, works against the Vue-driven engine.
6
+ import { AnimatedValue, AnimatedValueXY, AnimatedColor, AnimatedMock, Easing, Platform, timing, spring, decay, parallel, sequence, stagger, loop, delay, add, subtract, multiply, divide, modulo, diffClamp, event, forkEvent, unforkEvent, } from '@symbiote-native/engine';
7
+ import { View, Text } from '../../components';
8
+ import { Image } from '../../components/image';
9
+ import { ScrollView } from '../../components/scroll-view';
10
+ import { createAnimatedComponent } from './create-animated-component';
11
+ export { createAnimatedComponent } from './create-animated-component';
12
+ // View/Text are pure host primitives; Image is the functional renderImage wrapper, and all expose
13
+ // their host node via ref fall-through, so wrap them eagerly.
14
+ const AnimatedView = createAnimatedComponent(View);
15
+ const AnimatedText = createAnimatedComponent(Text);
16
+ const AnimatedImage = createAnimatedComponent(Image);
17
+ // ScrollView is wrapped behind a LAZY, memoized getter, mirroring RN's `get ScrollView()` and the
18
+ // React adapter: ScrollView's module chain imports this Animated namespace back (sticky headers,
19
+ // Phase 3), so a static createAnimatedComponent(ScrollView) at init could read ScrollView inside
20
+ // its own TDZ. A memoized getter defers the wrap past module init, so the cycle never fires.
21
+ let animatedScrollView;
22
+ // FlatList / SectionList: DEFERRED. The Vue adapter has no FlatList/SectionList base component yet
23
+ // (the React adapter does), so there is nothing to wrap; Animated.FlatList / Animated.SectionList
24
+ // are intentionally OMITTED, not faked. They land once those base components exist (you cannot wrap
25
+ // a component that does not exist). This is a named gap, not a silent drop.
26
+ // The live, JS-driven driver namespace (real frames). RN's AnimatedImplementation.
27
+ const liveDrivers = {
28
+ Value: AnimatedValue,
29
+ ValueXY: AnimatedValueXY,
30
+ Color: AnimatedColor,
31
+ Easing,
32
+ timing,
33
+ spring,
34
+ decay,
35
+ parallel,
36
+ sequence,
37
+ stagger,
38
+ loop,
39
+ delay,
40
+ add,
41
+ subtract,
42
+ multiply,
43
+ divide,
44
+ modulo,
45
+ diffClamp,
46
+ event,
47
+ forkEvent,
48
+ unforkEvent,
49
+ };
50
+ // RN swaps the WHOLE driver namespace for the mock when the host reports isDisableAnimations
51
+ // (reduced motion / test env): the mock keeps the same surface but jumps each animation to its
52
+ // final value synchronously, no frames. The animated COMPONENTS (View/Text/Image + the lazy
53
+ // ScrollView getter) are live in both branches; only the drivers/value/operators/events half is
54
+ // swapped, exactly like RN spreading `...Animated` (impl or mock) over the same component getters.
55
+ const drivers = Platform.isDisableAnimations ? AnimatedMock : liveDrivers;
56
+ export const Animated = {
57
+ View: AnimatedView,
58
+ Text: AnimatedText,
59
+ Image: AnimatedImage,
60
+ get ScrollView() {
61
+ animatedScrollView ??= createAnimatedComponent(ScrollView);
62
+ return animatedScrollView;
63
+ },
64
+ createAnimatedComponent,
65
+ ...drivers,
66
+ };
@@ -0,0 +1,7 @@
1
+ import { type Component } from '@vue/runtime-core';
2
+ import { type IAppParameters } from '@symbiote-native/engine';
3
+ export type IComponentProvider = () => Component;
4
+ export type IWrapperComponentProvider = (appParameters: IAppParameters) => Component;
5
+ declare const AppRegistry: import("@symbiote-native/engine").IAppRegistry<IComponentProvider, IWrapperComponentProvider>, setHostRegistrar: (registrar: import("@symbiote-native/engine").IHostRegistrar) => void;
6
+ export { AppRegistry, setHostRegistrar };
7
+ export type { IAppParameters, IRunnable, IHostRegistrar, IRegistry, IHeadlessTask, ITaskProvider, ITaskCanceller, ITaskCancelProvider, } from '@symbiote-native/engine';
@@ -0,0 +1,32 @@
1
+ // AppRegistry: the JS entry point RN apps already use:
2
+ // AppRegistry.registerComponent(appKey, () => App)
3
+ // The registry bookkeeping (sections, host-registrar bridge, headless tasks) is
4
+ // framework-agnostic and lives in @symbiote-native/engine's createAppRegistry; this file supplies
5
+ // only the one Vue-specific seam — building a runnable from a component provider via a
6
+ // synthetic functional root (h() has no createElement-style prop/children spread of its
7
+ // own targets, so the wrap step is a render function rather than an object merge) — so
8
+ // examples/vue-sfc's own registerRunnable + mount call can move onto the same entry point
9
+ // React already has.
10
+ import { h } from '@vue/runtime-core';
11
+ import { createAppRegistry, dlog, } from '@symbiote-native/engine';
12
+ import { mount } from '../../render';
13
+ function runnableFor(componentProvider, getWrapperComponentProvider) {
14
+ return appParameters => {
15
+ dlog(`AppRegistry: mounting on rootTag ${String(appParameters.rootTag)}`);
16
+ const rootComponent = componentProvider();
17
+ const renderRoot = () => h(rootComponent, appParameters.initialProps);
18
+ // Wrap the root when a provider is set, mirroring RN's `registerComponent` passing
19
+ // `wrapperComponentProvider && wrapperComponentProvider(...)` into renderApplication
20
+ // (AppRegistryImpl.js:80) — Vue's equivalent of React's createElement(Wrapper, null,
21
+ // element) is a default-slot render, since Vue has no standalone element value to nest.
22
+ // Read live (not at registration time) so setWrapperComponentProvider affects every
23
+ // runnable's next run, not just future ones.
24
+ const wrapperComponentProvider = getWrapperComponentProvider();
25
+ const mounted = wrapperComponentProvider === undefined
26
+ ? renderRoot
27
+ : () => h(wrapperComponentProvider(appParameters), null, { default: renderRoot });
28
+ mount(appParameters.rootTag, mounted);
29
+ };
30
+ }
31
+ const { AppRegistry, setHostRegistrar } = createAppRegistry(runnableFor);
32
+ export { AppRegistry, setHostRegistrar };
@@ -0,0 +1,17 @@
1
+ export type { IStatusBarProps, IStatusBarStyle } from '@symbiote-native/engine';
2
+ declare const StatusBarWithStatics: {
3
+ new (...args: any[]): import("vue").CreateComponentPublicInstanceWithMixins<Readonly<{}> & Readonly<{}>, () => null, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, import("vue").PublicProps, {}, true, {}, {}, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, {}, any, import("vue").ComponentProvideOptions, {
4
+ P: {};
5
+ B: {};
6
+ D: {};
7
+ C: {};
8
+ M: {};
9
+ Defaults: {};
10
+ }, Readonly<{}> & Readonly<{}>, () => null, {}, {}, {}, {}>;
11
+ __isFragment?: never;
12
+ __isTeleport?: never;
13
+ __isSuspense?: never;
14
+ } & import("vue").ComponentOptionsBase<Readonly<{}> & Readonly<{}>, () => null, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, {}, {}, string, {}, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, import("vue").ComponentProvideOptions> & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps & import("@symbiote-native/engine").IStatusBarImperative;
15
+ export declare const StatusBar: typeof StatusBarWithStatics & {
16
+ readonly currentHeight?: number;
17
+ };
@@ -0,0 +1,65 @@
1
+ // StatusBar, the Vue lifecycle half. The native StatusBarManager driving (applyStatusBarProps),
2
+ // the imperative statics (statusBarImperative), and the Android bar-height constant all live in
3
+ // @symbiote-native/engine, shared verbatim with React; Metro selects the engine's status-bar.ios.ts /
4
+ // status-bar.android.ts per host, so the platform divergence never reaches this file. Vue supplies
5
+ // only the declarative shape: a component that renders NOTHING and re-applies the props through a
6
+ // watchEffect on mount + every prop change, with the imperative statics attached to the component
7
+ // object (RN's StatusBar: the value doubles as the imperative namespace, like the Vue Image).
8
+ //
9
+ // Renders null (no Fabric view), so it follows neither the descriptor split nor a host-node ref:
10
+ // the "else keep its current shape" path of the StatusBar component. Inputs arrive as attrs
11
+ // (untyped) and run through normalizeVueAttrs (kebab→camel) before each is narrowed by a guard.
12
+ import { defineComponent, watchEffect } from '@vue/runtime-core';
13
+ import { applyStatusBarProps, statusBarImperative, statusBarCurrentHeight, isOpaqueColorValue, } from '@symbiote-native/engine';
14
+ import { normalizeVueAttrs } from '../utils/normalize-attrs';
15
+ function asBoolean(value) {
16
+ return typeof value === 'boolean' ? value : undefined;
17
+ }
18
+ function asBarStyle(value) {
19
+ return value === 'default' || value === 'light-content' || value === 'dark-content'
20
+ ? value
21
+ : undefined;
22
+ }
23
+ // backgroundColor is a string / int / opaque PlatformColor; any other shape is dropped. A runtime
24
+ // guard at the untyped-attrs boundary, not a cast.
25
+ function asColorValue(value) {
26
+ if (typeof value === 'string')
27
+ return value;
28
+ if (isOpaqueColorValue(value))
29
+ return value;
30
+ return undefined;
31
+ }
32
+ function buildProps(attrs) {
33
+ return {
34
+ barStyle: asBarStyle(attrs.barStyle),
35
+ hidden: asBoolean(attrs.hidden),
36
+ animated: asBoolean(attrs.animated),
37
+ networkActivityIndicatorVisible: asBoolean(attrs.networkActivityIndicatorVisible),
38
+ backgroundColor: asColorValue(attrs.backgroundColor),
39
+ translucent: asBoolean(attrs.translucent),
40
+ };
41
+ }
42
+ const StatusBarComponent = defineComponent({
43
+ name: 'StatusBar',
44
+ inheritAttrs: false,
45
+ setup(_props, { attrs: rawAttrs }) {
46
+ // watchEffect tracks rawAttrs (reactive), so reading it here re-applies on every prop change:
47
+ // the Vue twin of React's useEffect over the prop deps. Resolution is lazy inside the engine
48
+ // (a missing StatusBarManager is a no-op), so this never crashes a render.
49
+ watchEffect(() => {
50
+ applyStatusBarProps(buildProps(normalizeVueAttrs(rawAttrs)));
51
+ });
52
+ // Renders nothing: StatusBar has no Fabric view, exactly like React's `return null`.
53
+ return () => null;
54
+ },
55
+ });
56
+ const StatusBarWithStatics = Object.assign(StatusBarComponent, statusBarImperative);
57
+ // Android exposes the bar height as a native constant; undefined on iOS / when absent. Read lazily
58
+ // (getter) so nothing touches native at import time; the Android engine impl resolves on access.
59
+ Object.defineProperty(StatusBarWithStatics, 'currentHeight', {
60
+ get: statusBarCurrentHeight,
61
+ enumerable: true,
62
+ });
63
+ // currentHeight is optional, so the defineProperty-added accessor doesn't need to appear on the
64
+ // runtime object's inferred type for this assignment to hold (no cast).
65
+ export const StatusBar = StatusBarWithStatics;
@@ -0,0 +1,7 @@
1
+ import { type IRootTag, type SymbioteSurface } from '@symbiote-native/engine';
2
+ import type { Component } from '@vue/runtime-core';
3
+ export declare function mount(rootTag: IRootTag, rootComponent: Component): SymbioteSurface;
4
+ export declare function unmount(rootTag: IRootTag): void;
5
+ declare global {
6
+ var RN$stopSurface: ((surfaceId: number) => void) | undefined;
7
+ }
@@ -0,0 +1,43 @@
1
+ // Mount a Vue app onto a Fabric surface. The native host hands us a rootTag; we create
2
+ // a surface for it and let the Vue renderer drive the engine, which commits into
3
+ // nativeFabricUIManager; RN's own renderer never in the path.
4
+ import { createSurface, disposeRoot, dlog, } from '@symbiote-native/engine';
5
+ import { createSymbioteRenderer } from './renderer';
6
+ // One Vue app per surface, so a surface can be torn down (unmount) or cleanly
7
+ // re-mounted on the same rootTag: the bridgeless host stops and restarts a surface on
8
+ // Fast Refresh and on lifecycle/focus changes, reusing the rootTag.
9
+ const apps = new Map();
10
+ function teardown(rootTag) {
11
+ const app = apps.get(rootTag);
12
+ if (app === undefined)
13
+ return;
14
+ app.unmount();
15
+ apps.delete(rootTag);
16
+ disposeRoot(rootTag);
17
+ }
18
+ export function mount(rootTag, rootComponent) {
19
+ // A re-mount on a live rootTag starts clean; otherwise the stale app double-drives
20
+ // the surface.
21
+ teardown(rootTag);
22
+ const surface = createSurface(rootTag);
23
+ const renderer = createSymbioteRenderer(surface);
24
+ const app = renderer.createApp(rootComponent);
25
+ apps.set(rootTag, app);
26
+ // The surface IS the Vue container: a top-level mutation routes to surface.appendChild
27
+ // (renderer.ts), and the engine wraps surface.children in its synthetic flex root.
28
+ app.mount(surface);
29
+ return surface;
30
+ }
31
+ // Tear down a surface by rootTag: the public pair of `mount`, and the JS half of the
32
+ // bridgeless `RN$stopSurface` contract (see installStopSurfaceGlobal).
33
+ export function unmount(rootTag) {
34
+ dlog(`unmount root=${rootTag}`);
35
+ teardown(rootTag);
36
+ }
37
+ function installStopSurfaceGlobal() {
38
+ globalThis.RN$stopSurface = (surfaceId) => {
39
+ unmount(surfaceId);
40
+ };
41
+ dlog('installed global.RN$stopSurface');
42
+ }
43
+ installStopSurfaceGlobal();
@@ -0,0 +1,4 @@
1
+ import { SymbioteSurface, type ISymbioteNode } from '@symbiote-native/engine';
2
+ type IHostElement = ISymbioteNode | SymbioteSurface;
3
+ export declare function createSymbioteRenderer(surface: SymbioteSurface): import("vue").Renderer<IHostElement>;
4
+ export {};