@felipe-software/react-native-gpui 0.0.0-placeholder → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/.reanimated-prebuilt/react-native-reanimated.mjs +29373 -0
  2. package/LICENSE +21 -0
  3. package/README.md +658 -1
  4. package/cli/bin.ts +379 -0
  5. package/cli/bundle.ts +32 -0
  6. package/cli/commands/do.ts +213 -0
  7. package/cli/commands/flow.ts +283 -0
  8. package/cli/commands/get.ts +584 -0
  9. package/cli/commands/shot.ts +312 -0
  10. package/cli/commands/trace.ts +293 -0
  11. package/cli/host.ts +714 -0
  12. package/cli/hotReload.ts +210 -0
  13. package/cli/run.ts +66 -0
  14. package/cli/selectors.ts +205 -0
  15. package/dist/Animated.d.ts +120 -0
  16. package/dist/ChatList.d.ts +28 -0
  17. package/dist/Dimensions.d.ts +31 -0
  18. package/dist/StyleSheet.d.ts +18 -0
  19. package/dist/Window.d.ts +33 -0
  20. package/dist/apis.d.ts +165 -0
  21. package/dist/app-state.d.ts +27 -0
  22. package/dist/chat-list-types.d.ts +136 -0
  23. package/dist/colors.d.ts +32 -0
  24. package/dist/commands.d.ts +230 -0
  25. package/dist/components.d.ts +419 -0
  26. package/dist/hotModules.d.ts +1 -0
  27. package/dist/index.d.ts +31 -0
  28. package/dist/index.development.js +14876 -0
  29. package/dist/index.js +12 -0
  30. package/dist/keyboard.d.ts +140 -0
  31. package/dist/native-menu.d.ts +7 -0
  32. package/dist/platform-driver.d.ts +32 -0
  33. package/dist/platform-services.d.ts +35 -0
  34. package/dist/portal.d.ts +43 -0
  35. package/dist/raf.d.ts +4 -0
  36. package/dist/reanimated/colors-processor-shim.d.ts +10 -0
  37. package/dist/reanimated/reanimated-host.d.ts +10 -0
  38. package/dist/reanimated/seam.d.ts +34 -0
  39. package/dist/reanimated/ui-entry.d.ts +2 -0
  40. package/dist/reanimated/worklet-channel.d.ts +10 -0
  41. package/dist/reanimated/worklet-runtime.d.ts +466 -0
  42. package/dist/reanimated/worklets.d.ts +199 -0
  43. package/dist/reconciler.d.ts +102 -0
  44. package/dist/refresh.d.ts +1 -0
  45. package/dist/render.d.ts +37 -0
  46. package/dist/runtime.d.ts +235 -0
  47. package/dist/surfaces.d.ts +51 -0
  48. package/dist/types.d.ts +276 -0
  49. package/dist/ui-runtime.js +42276 -0
  50. package/dist/window-context.d.ts +7 -0
  51. package/dist/window-host.d.ts +41 -0
  52. package/dist/wire-delta.d.ts +20 -0
  53. package/package.json +177 -1
  54. package/scripts/bundle-app.mjs +88 -0
  55. package/scripts/conformance-utils.mjs +293 -0
  56. package/scripts/hot-update-alias-plugin.mjs +88 -0
  57. package/scripts/native-package-exports-plugin.mjs +235 -0
  58. package/scripts/pixel.mjs +152 -0
  59. package/scripts/png.mjs +198 -0
  60. package/scripts/react-refresh-swc.mjs +35 -0
  61. package/scripts/reanimated-bun-plugin.mjs +188 -0
  62. package/scripts/service-binary.d.mts +27 -0
  63. package/scripts/service-binary.mjs +100 -0
  64. package/src/reanimated/colors-processor-shim.ts +119 -0
  65. package/src/reanimated/reanimated-host.ts +69 -0
  66. package/src/reanimated/rngpui-reanimated-prebuilt.d.ts +7 -0
  67. package/src/reanimated/seam.ts +566 -0
  68. package/src/reanimated/ui-entry.ts +80 -0
  69. package/src/reanimated/worklet-channel.ts +64 -0
  70. package/src/reanimated/worklet-runtime.ts +2569 -0
  71. package/src/reanimated/worklets.ts +1334 -0
  72. package/src/refresh.ts +33 -0
@@ -0,0 +1,566 @@
1
+ // react-native-reanimated — gpui's NATIVE-SEAM-ONLY override.
2
+ //
3
+ // react-native-reanimated is mostly pure JS: useSharedValue, useAnimatedStyle,
4
+ // useDerivedValue, withTiming/withSpring, createAnimatedComponent, the animation
5
+ // drivers — all of it resolves from node_modules unchanged. We replace ONLY the thin
6
+ // native seam: the turbomodule (`specs/NativeReanimatedModule`) that installs
7
+ // `__reanimatedModuleProxy` + `global._*` functions, and the
8
+ // `platformFunctions/{scrollTo,measure,setNativeProps,dispatchCommand,setGestureState}`
9
+ // (the redirect aliases in the bundler point upstream's imports here).
10
+ //
11
+ // The single fast path is `engineUpdateProps`: upstream calls `global._updateProps(ops)`
12
+ // every animation frame; we coalesce all ops within one rAF tick into ONE host crossing
13
+ // (`__rngpui_setNodeStyle`), which lands in Rust's animated-style overlay and re-renders
14
+ // WITHOUT a React re-commit. That is off-thread-style animation: the spring driver and
15
+ // the useAnimatedStyle mapper run in the UI JavaScriptCore runtime, and only the resulting
16
+ // style deltas cross to native.
17
+ //
18
+ // CRITICAL ordering: __RUNTIME_KIND=2 and RN$Bridgeless=true MUST be installed before
19
+ // reanimated first evaluates, or runtimeKind.ts defaults to ReactNative (1), defineAnimation
20
+ // returns the bare animation FUNCTION instead of calling it, and `withSpring(...)` leaks a
21
+ // function into a style value → "Invalid value function(){…} for setWidth". installReanimated
22
+ // NativeSeam() runs at module load (this file is evaluated by the bundler's seam redirect
23
+ // before upstream's core.ts imports resolve).
24
+
25
+ import { registerWorkletBuiltin } from './worklet-runtime'
26
+
27
+ interface MeasuredDimensions {
28
+ x: number
29
+ y: number
30
+ width: number
31
+ height: number
32
+ pageX: number
33
+ pageY: number
34
+ }
35
+
36
+ interface StyleProps {
37
+ [key: string]: unknown
38
+ }
39
+
40
+ interface SerializableLike {
41
+ __init?: () => unknown
42
+ }
43
+
44
+ type WorkletFn = (...args: unknown[]) => unknown
45
+
46
+ // =============================================================================
47
+ // viewTag → globalId registry. `createAnimatedComponent`'s ref resolves to the
48
+ // reconciler's `Instance` (getPublicInstance returns it), whose `.id` IS the
49
+ // `globalId` Rust keys on. Animated components register their host id here on mount
50
+ // (via the patched reanimated component below) so `_updateProps` can map an operation's
51
+ // shadowNodeWrapper/viewTag back to the globalId the overlay writes.
52
+ // =============================================================================
53
+
54
+ const VIEW_TAG_TO_GLOBAL_ID = new Map<number, number>()
55
+
56
+ /** Resolve an upstream `op.shadowNodeWrapper` / viewTag down to a numeric globalId. */
57
+ function resolveGlobalId(target: unknown): number | null {
58
+ if (typeof target === 'function') {
59
+ const resolved = (target as () => unknown)()
60
+ if (resolved !== target) return resolveGlobalId(resolved)
61
+ }
62
+ if (typeof target === 'number') {
63
+ return VIEW_TAG_TO_GLOBAL_ID.get(target) ?? target
64
+ }
65
+ if (target && typeof target === 'object') {
66
+ const candidate = target as {
67
+ __rngpuiGlobalId?: unknown
68
+ id?: unknown
69
+ __nativeTag?: unknown
70
+ _nativeTag?: unknown
71
+ __viewTag?: unknown
72
+ current?: unknown
73
+ getTag?: () => unknown
74
+ }
75
+ if (typeof candidate.__rngpuiGlobalId === 'number') return candidate.__rngpuiGlobalId
76
+ if (typeof candidate.id === 'number') return VIEW_TAG_TO_GLOBAL_ID.get(candidate.id) ?? candidate.id
77
+ const tag = candidate.__viewTag ?? candidate.__nativeTag ?? candidate._nativeTag
78
+ if (typeof tag === 'number') return VIEW_TAG_TO_GLOBAL_ID.get(tag) ?? tag
79
+ if (typeof candidate.getTag === 'function') {
80
+ const got = candidate.getTag()
81
+ if (typeof got === 'number') return VIEW_TAG_TO_GLOBAL_ID.get(got) ?? got
82
+ }
83
+ if (candidate.current != null && candidate.current !== target) {
84
+ return resolveGlobalId(candidate.current)
85
+ }
86
+ }
87
+ return null
88
+ }
89
+
90
+ /** Register a viewTag → globalId mapping for an animated component's lifetime. */
91
+ export function registerAnimatedViewTag(viewTag: number, globalId: number): void {
92
+ VIEW_TAG_TO_GLOBAL_ID.set(viewTag, globalId)
93
+ }
94
+
95
+ export function unregisterAnimatedViewTag(viewTag: number): void {
96
+ VIEW_TAG_TO_GLOBAL_ID.delete(viewTag)
97
+ }
98
+
99
+ declare const __rngpui_setNodeStyle: ((json: string) => void) | undefined
100
+ declare const __rngpui_scrollTo: ((json: string) => void) | undefined
101
+
102
+ // =============================================================================
103
+ // rAF coalescing. Every `_updateProps` op within one frame accumulates here; we
104
+ // flush ONCE per rAF tick, in ONE `__rngpui_setNodeStyle` host call (one cx.notify).
105
+ // =============================================================================
106
+
107
+ const PENDING_OPS = new Map<number, Record<string, unknown>>()
108
+ let flushScheduled = false
109
+
110
+ function scheduleFlush(): void {
111
+ if (flushScheduled) return
112
+ flushScheduled = true
113
+ const raf = (globalThis as { requestAnimationFrame?: typeof requestAnimationFrame }).requestAnimationFrame
114
+ if (typeof raf === 'function') {
115
+ raf.call(globalThis, flushUpdatedProps)
116
+ } else {
117
+ setTimeout(flushUpdatedProps, 16)
118
+ }
119
+ }
120
+
121
+ function flushUpdatedProps(): void {
122
+ flushScheduled = false
123
+ if (PENDING_OPS.size === 0) return
124
+ const ops: Array<[number, Record<string, unknown>]> = []
125
+ for (const [id, style] of PENDING_OPS) ops.push([id, style])
126
+ PENDING_OPS.clear()
127
+ if (typeof __rngpui_setNodeStyle === 'function') {
128
+ __rngpui_setNodeStyle(JSON.stringify(ops))
129
+ }
130
+ }
131
+
132
+ // =============================================================================
133
+ // __reanimatedModuleProxy + `global._*`. Upstream's NativeReanimatedModule installs
134
+ // these on real iOS; we wire the equivalents at module load so upstream's core.ts /
135
+ // mappers.ts see fully-wired globals by the time they import this seam.
136
+ // =============================================================================
137
+
138
+ let proxyInstalled = false
139
+
140
+ function installReanimatedNativeSeam(): void {
141
+ if (proxyInstalled) return
142
+ proxyInstalled = true
143
+
144
+ const g = globalThis as Record<string, unknown> & {
145
+ global?: typeof globalThis
146
+ _tagToJSPropNamesMapping?: Record<number, Record<string, boolean>>
147
+ _getAnimationTimestamp?: () => number
148
+ _updateProps?: (operations: Array<{ shadowNodeWrapper: unknown; updates: StyleProps }>) => void
149
+ _updatePropsFabric?: (operations: Array<{ shadowNodeWrapper: unknown; updates: StyleProps }>) => void
150
+ _updatePropsPaper?: (operations: Array<{ tag: unknown; name?: unknown; updates: StyleProps }>) => void
151
+ _maybeFlushUIUpdatesQueue?: () => void
152
+ _scrollTo?: (...args: unknown[]) => void
153
+ _scrollToPaper?: (...args: unknown[]) => void
154
+ _measure?: (node: unknown) => MeasuredDimensions | null
155
+ _measureFabric?: (node: unknown) => MeasuredDimensions | null
156
+ _measurePaper?: (node: unknown) => MeasuredDimensions | null
157
+ _dispatchCommand?: (...args: unknown[]) => void
158
+ _dispatchCommandFabric?: (...args: unknown[]) => void
159
+ _dispatchCommandPaper?: (...args: unknown[]) => void
160
+ _scheduleHostFunctionOnJS?: (fn: WorkletFn, args?: unknown[]) => void
161
+ _scheduleRemoteFunctionOnJS?: (fn: WorkletFn, args?: unknown[]) => void
162
+ _setGestureState?: (handlerTag: number, newState: number) => void
163
+ _notifyAboutProgress?: (tag: number, value: unknown) => void
164
+ _notifyAboutEnd?: (tag: number, removeView: boolean) => void
165
+ __reanimatedModuleProxy?: ReanimatedModuleProxy
166
+ }
167
+
168
+ // worklet bodies reference Node-style `global`; alias to globalThis so closure
169
+ // bindings resolve in either context.
170
+ if (typeof (globalThis as { global?: unknown }).global === 'undefined') {
171
+ ;(globalThis as { global?: unknown }).global = globalThis
172
+ }
173
+
174
+ // version-shim getters/setters: read undefined (passes assertSingleReanimatedInstance)
175
+ // and accept any write (records the active guest version, mirrored to _CPP so
176
+ // checkCppVersion sees a matching native side).
177
+ let activeReanimatedJsVersion: string | undefined
178
+ Object.defineProperty(globalThis, '_REANIMATED_VERSION_JS', {
179
+ get: () => undefined,
180
+ set: (version) => {
181
+ activeReanimatedJsVersion = typeof version === 'string' ? version : activeReanimatedJsVersion
182
+ },
183
+ configurable: true,
184
+ enumerable: false,
185
+ })
186
+ Object.defineProperty(globalThis, '_REANIMATED_VERSION_CPP', {
187
+ get: () => activeReanimatedJsVersion,
188
+ set: (version) => {
189
+ activeReanimatedJsVersion = typeof version === 'string' ? version : activeReanimatedJsVersion
190
+ },
191
+ configurable: true,
192
+ enumerable: false,
193
+ })
194
+
195
+ // Reanimated 4 requires the New Architecture; claim Bridgeless/Fabric.
196
+ ;(globalThis as { RN$Bridgeless?: boolean }).RN$Bridgeless ??= true
197
+
198
+ // __RUNTIME_KIND — MUST be UI (2) before reanimated evaluates (see header).
199
+ if ((globalThis as { __RUNTIME_KIND?: number }).__RUNTIME_KIND !== 2) {
200
+ ;(globalThis as { __RUNTIME_KIND?: number }).__RUNTIME_KIND = 2
201
+ }
202
+
203
+ g._tagToJSPropNamesMapping ??= {}
204
+ g._getAnimationTimestamp ??= () => performance.now()
205
+
206
+ // the fast path + the rest of the seam.
207
+ g._updateProps ??= (operations) => engineUpdateProps(operations)
208
+ g._updatePropsFabric ??= (operations) => engineUpdateProps(operations)
209
+ g._updatePropsPaper ??= (operations) =>
210
+ engineUpdateProps(operations.map((op) => ({ shadowNodeWrapper: op.tag, updates: op.updates })))
211
+ // reanimated's native renderer queues prop writes, then flushes that queue after
212
+ // each mapper pass. engineUpdateProps sends the write immediately, so there is no
213
+ // second queue to flush.
214
+ g._maybeFlushUIUpdatesQueue ??= () => {}
215
+
216
+ // measure / scroll / dispatchCommand — gpui's reconciler owns layout, not the
217
+ // worklet UI runtime; resolve against the live node graph through the host id.
218
+ g._measure ??= (node) => engineMeasure(node)
219
+ g._measureFabric ??= (node) => engineMeasure(node)
220
+ g._measurePaper ??= (node) => engineMeasure(node)
221
+ g._scrollTo ??= (node, x, y) => engineScrollTo(node, x, y)
222
+ g._scrollToPaper ??= (node, x, y) => engineScrollTo(node, x, y)
223
+ g._dispatchCommand ??= () => {}
224
+ g._dispatchCommandFabric ??= () => {}
225
+ g._dispatchCommandPaper ??= () => {}
226
+ g._scheduleHostFunctionOnJS ??= (fn, args = []) => {
227
+ queueMicrotask(() => {
228
+ if (typeof fn === 'function') fn(...args)
229
+ })
230
+ }
231
+ g._scheduleRemoteFunctionOnJS ??= (fn, args = []) => {
232
+ queueMicrotask(() => {
233
+ if (typeof fn === 'function') fn(...args)
234
+ })
235
+ }
236
+ g._setGestureState ??= () => {}
237
+ g._notifyAboutProgress ??= () => {}
238
+ g._notifyAboutEnd ??= () => {}
239
+
240
+ ensureFrameCallbackRegistry()
241
+
242
+ g.__reanimatedModuleProxy ??= createReanimatedModuleProxy()
243
+ }
244
+
245
+ function engineMeasure(_node: unknown): MeasuredDimensions | null {
246
+ // gpui resolves measure through the reconciler's measureInWindow path, not the
247
+ // worklet UI runtime — Tamagui's animation driver doesn't depend on this. Return
248
+ // null (upstream callers tolerate it) rather than guess a rect.
249
+ return null
250
+ }
251
+
252
+ function engineScrollTo(animatedRef: unknown, x: unknown, y: unknown): void {
253
+ const globalId = resolveGlobalId(animatedRef)
254
+ if (SEAM_DEBUG) {
255
+ console.error(
256
+ `[seam] scrollTo target=${String(globalId)} x=${String(x)} y=${String(y)} host=${typeof __rngpui_scrollTo}`,
257
+ )
258
+ }
259
+ if (
260
+ globalId == null ||
261
+ typeof x !== 'number' ||
262
+ !Number.isFinite(x) ||
263
+ typeof y !== 'number' ||
264
+ !Number.isFinite(y) ||
265
+ typeof __rngpui_scrollTo !== 'function'
266
+ ) {
267
+ return
268
+ }
269
+ __rngpui_scrollTo(JSON.stringify([globalId, x, y]))
270
+ }
271
+
272
+ // =============================================================================
273
+ // IReanimatedModule proxy — upstream delegates ~25 methods to it. Most are no-ops on
274
+ // gpui; the live ones are registerEventHandler / subscribeForKeyboardEvents (no-op
275
+ // here for now — Tamagui springs don't use them). A permissive Proxy returns a no-op
276
+ // for any unknown method so older bundles that call legacy methods don't crash.
277
+ // =============================================================================
278
+
279
+ interface ReanimatedModuleProxy {
280
+ [key: string]: (...args: unknown[]) => unknown
281
+ }
282
+
283
+ function createReanimatedModuleProxy(): ReanimatedModuleProxy {
284
+ const base: Record<string, (...args: unknown[]) => unknown> = {
285
+ configureLayoutAnimationBatch: () => {},
286
+ setShouldAnimateExitingForTag: () => {},
287
+ getStaticFeatureFlag: () => false,
288
+ setDynamicFeatureFlag: () => {},
289
+ registerSensor: () => -1,
290
+ unregisterSensor: () => {},
291
+ registerEventHandler: () => -1,
292
+ unregisterEventHandler: () => {},
293
+ subscribeForKeyboardEvents: () => -1,
294
+ unsubscribeFromKeyboardEvents: () => {},
295
+ getViewProp: (..._args: unknown[]) => {
296
+ const cb = _args[2]
297
+ if (typeof cb === 'function') (cb as (r: unknown) => void)(null)
298
+ return null
299
+ },
300
+ setViewStyle: () => {},
301
+ markNodeAsRemovable: () => {},
302
+ unmarkNodeAsRemovable: () => {},
303
+ registerCSSKeyframes: () => {},
304
+ unregisterCSSKeyframes: () => {},
305
+ applyCSSAnimations: () => {},
306
+ unregisterCSSAnimations: () => {},
307
+ runCSSTransition: () => {},
308
+ unregisterCSSTransition: () => {},
309
+ getSettledUpdates: () => [],
310
+ // legacy v3 methods some bundles call directly on the proxy
311
+ scheduleOnUI: (worklet: unknown) => {
312
+ const fn =
313
+ typeof worklet === 'function'
314
+ ? (worklet as WorkletFn)
315
+ : (worklet as { __init?: () => unknown })?.__init?.()
316
+ if (typeof fn === 'function') {
317
+ queueMicrotask(() => {
318
+ try {
319
+ ;(fn as WorkletFn).apply(fn, [])
320
+ } catch (err) {
321
+ console.error('[rngpui reanimated] scheduleOnUI threw:', err)
322
+ }
323
+ })
324
+ }
325
+ },
326
+ makeShareableClone: (value: unknown) => value,
327
+ makeShareableCloneOnUI: (value: unknown) => value,
328
+ installCoreFunctions: () => {},
329
+ installTurboModule: () => {},
330
+ enableLayoutAnimations: () => {},
331
+ }
332
+ return new Proxy(base as ReanimatedModuleProxy, {
333
+ get(target, prop) {
334
+ const value = (target as Record<PropertyKey, unknown>)[prop as PropertyKey]
335
+ if (value !== undefined) return value
336
+ if (typeof prop === 'string') return () => undefined
337
+ return undefined
338
+ },
339
+ })
340
+ }
341
+
342
+ // =============================================================================
343
+ // frame callback registry — upstream useFrameCallback drives off this. A minimal,
344
+ // rAF-backed registry (mirrors reanimated's own UI-runtime shape) so useFrameCallback
345
+ // works; the loop stops when no callbacks are active (idle → 0fps).
346
+ // =============================================================================
347
+
348
+ interface FrameInfo {
349
+ timestamp: number
350
+ timeSincePreviousFrame: number | null
351
+ timeSinceFirstFrame: number
352
+ }
353
+
354
+ function requestFrame(callback: (timestamp: number) => void): void {
355
+ const raf = (globalThis as { requestAnimationFrame?: typeof requestAnimationFrame }).requestAnimationFrame
356
+ if (typeof raf === 'function') {
357
+ raf.call(globalThis, callback)
358
+ return
359
+ }
360
+ setTimeout(() => callback(performance.now()), 16)
361
+ }
362
+
363
+ function ensureFrameCallbackRegistry(): void {
364
+ const g = globalThis as typeof globalThis & {
365
+ global?: typeof globalThis
366
+ _frameCallbackRegistry?: unknown
367
+ }
368
+ if (g._frameCallbackRegistry) return
369
+
370
+ const registry = {
371
+ frameCallbackRegistry: new Map<number, { callback: (f: FrameInfo) => void; startTime: number | null }>(),
372
+ activeFrameCallbacks: new Set<number>(),
373
+ previousFrameTimestamp: null as number | null,
374
+ nextCallId: 0,
375
+ runCallbacks(callId: number) {
376
+ const loop = (timestamp: number) => {
377
+ if (callId !== this.nextCallId) return
378
+ if (this.previousFrameTimestamp === null) this.previousFrameTimestamp = timestamp
379
+ const delta = timestamp - this.previousFrameTimestamp
380
+ this.activeFrameCallbacks.forEach((callbackId) => {
381
+ const details = this.frameCallbackRegistry.get(callbackId)
382
+ if (!details) return
383
+ if (details.startTime === null) {
384
+ details.startTime = timestamp
385
+ details.callback({ timestamp, timeSincePreviousFrame: null, timeSinceFirstFrame: 0 })
386
+ } else {
387
+ details.callback({
388
+ timestamp,
389
+ timeSincePreviousFrame: delta,
390
+ timeSinceFirstFrame: timestamp - details.startTime,
391
+ })
392
+ }
393
+ })
394
+ if (this.activeFrameCallbacks.size > 0) {
395
+ this.previousFrameTimestamp = timestamp
396
+ requestFrame(loop)
397
+ } else {
398
+ this.previousFrameTimestamp = null
399
+ }
400
+ }
401
+ if (this.activeFrameCallbacks.size === 1 && callId === this.nextCallId) requestFrame(loop)
402
+ },
403
+ registerFrameCallback(callback: (f: FrameInfo) => void, callbackId: number) {
404
+ this.frameCallbackRegistry.set(callbackId, { callback, startTime: null })
405
+ },
406
+ unregisterFrameCallback(callbackId: number) {
407
+ this.manageStateFrameCallback(callbackId, false)
408
+ this.frameCallbackRegistry.delete(callbackId)
409
+ },
410
+ manageStateFrameCallback(callbackId: number, state: boolean) {
411
+ if (callbackId === -1) return
412
+ if (state) {
413
+ if (!this.activeFrameCallbacks.has(callbackId)) {
414
+ this.activeFrameCallbacks.add(callbackId)
415
+ this.runCallbacks(this.nextCallId)
416
+ }
417
+ return
418
+ }
419
+ const cb = this.frameCallbackRegistry.get(callbackId)
420
+ if (cb) cb.startTime = null
421
+ this.activeFrameCallbacks.delete(callbackId)
422
+ if (this.activeFrameCallbacks.size === 0) this.nextCallId += 1
423
+ },
424
+ }
425
+
426
+ g._frameCallbackRegistry = registry
427
+ if (g.global && g.global !== g) {
428
+ ;(g.global as typeof globalThis & { _frameCallbackRegistry?: unknown })._frameCallbackRegistry = registry
429
+ }
430
+ }
431
+
432
+ // =============================================================================
433
+ // engineUpdateProps — THE fast path. Upstream's `_updateProps` is RN's "set view
434
+ // props without touching React state". On gpui that's the animated-style overlay:
435
+ // resolve the op's target → globalId, merge its style keys into the pending-ops map,
436
+ // and schedule a single coalesced rAF flush. Normalize the style values the SAME way
437
+ // the reconciler serializes them, so Rust's `from_json` reads them identically.
438
+ // =============================================================================
439
+
440
+ const SEAM_DEBUG =
441
+ typeof process !== 'undefined' && !!(process as { env?: Record<string, string> }).env?.RNGPUI_SEAM_DEBUG
442
+ let updatePropsCalls = 0
443
+
444
+ function engineUpdateProps(operations: Array<{ shadowNodeWrapper: unknown; updates: StyleProps }>): void {
445
+ let any = false
446
+ for (const op of operations) {
447
+ const globalId = resolveGlobalId(op.shadowNodeWrapper)
448
+ if (globalId == null) {
449
+ if (SEAM_DEBUG) {
450
+ console.error(
451
+ `[seam] _updateProps op dropped: unresolved target ${JSON.stringify(op.shadowNodeWrapper)} keys=${Object.keys(op.updates).join(',')}`,
452
+ )
453
+ }
454
+ continue
455
+ }
456
+ const prev = PENDING_OPS.get(globalId) ?? {}
457
+ for (const [key, value] of Object.entries(op.updates)) {
458
+ prev[key] = normalizeUpdatePropValue(key, value)
459
+ if (SEAM_DEBUG && prev[key] === undefined) {
460
+ // an undefined value silently VANISHES at the JSON host crossing — the
461
+ // key never reaches the overlay. always worth a loud line.
462
+ console.error(`[seam] _updateProps key dropped as undefined: ${key} (raw ${typeof value})`)
463
+ }
464
+ }
465
+ PENDING_OPS.set(globalId, prev)
466
+ any = true
467
+ }
468
+ if (SEAM_DEBUG) {
469
+ updatePropsCalls++
470
+ console.error(`[seam] _updateProps call #${updatePropsCalls} ops=${operations.length} any=${any}`)
471
+ }
472
+ if (any) scheduleFlush()
473
+ }
474
+
475
+ // reanimated emits transforms as `transform: [{translateX: n}, …]` and colors as
476
+ // processed ints; pass plain numbers/strings through. Drop the reanimated-internal
477
+ // `_requiresAnimatedComponent` marker. Colors arrive as RN-processed numbers from the
478
+ // worklet color interpolation — convert to a CSS rgba string the Rust color parser reads.
479
+ function normalizeUpdatePropValue(key: string, value: unknown): unknown {
480
+ if (value == null) return value
481
+ if (isColorKey(key) && typeof value === 'number') {
482
+ return processedColorToCss(value)
483
+ }
484
+ return value
485
+ }
486
+
487
+ function isColorKey(key: string): boolean {
488
+ return (
489
+ key === 'color' ||
490
+ key === 'backgroundColor' ||
491
+ key === 'borderColor' ||
492
+ key === 'borderTopColor' ||
493
+ key === 'borderRightColor' ||
494
+ key === 'borderBottomColor' ||
495
+ key === 'borderLeftColor' ||
496
+ key === 'tintColor' ||
497
+ key === 'shadowColor'
498
+ )
499
+ }
500
+
501
+ // RN processes colors to a 32-bit ARGB int on the worklet side (reanimated's color
502
+ // interpolation returns this form). Convert to `rgba(r,g,b,a)` so the Rust css parser
503
+ // reads it.
504
+ function processedColorToCss(argb: number): string {
505
+ const a = ((argb >> 24) & 0xff) / 255
506
+ const r = (argb >> 16) & 0xff
507
+ const g = (argb >> 8) & 0xff
508
+ const b = argb & 0xff
509
+ return `rgba(${r},${g},${b},${a})`
510
+ }
511
+
512
+ // =============================================================================
513
+ // install on import — upstream's core.ts/mappers.ts read globals like
514
+ // __reanimatedModuleProxy on load, so install before any upstream import resolves.
515
+ // =============================================================================
516
+
517
+ installReanimatedNativeSeam()
518
+
519
+ // default export — upstream's `specs/NativeReanimatedModule` is
520
+ // `export default TurboModuleRegistry.get('ReanimatedModule')`; the bundler redirect
521
+ // routes that import here.
522
+ export default {
523
+ installTurboModule(): boolean {
524
+ installReanimatedNativeSeam()
525
+ return true
526
+ },
527
+ }
528
+
529
+ // platform functions — upstream's `platformFunctions/*` redirect here. These run on
530
+ // the JS thread (single runtime). Mark each as a worklet builtin shape so upstream's
531
+ // areWorkletsEqual doesn't choke.
532
+ function markWorkletBuiltin<T extends (...args: never[]) => unknown>(name: string, fn: T): T {
533
+ Object.defineProperty(fn, '__workletHash', { value: 0, enumerable: false, configurable: true })
534
+ Object.defineProperty(fn, '__initData', {
535
+ value: { code: fn.toString(), location: name },
536
+ enumerable: false,
537
+ configurable: true,
538
+ })
539
+ Object.defineProperty(fn, '__closure', { value: {}, enumerable: false, configurable: true, writable: true })
540
+ registerWorkletBuiltin(name, fn)
541
+ return fn
542
+ }
543
+
544
+ function scrollToImpl(animatedRef: unknown, x: number, y: number, _animated: boolean): void {
545
+ engineScrollTo(animatedRef, x, y)
546
+ }
547
+ function measureImpl(animatedRef: unknown): MeasuredDimensions | null {
548
+ return engineMeasure(animatedRef)
549
+ }
550
+ function setNativePropsImpl(animatedRef: unknown, updates: StyleProps): void {
551
+ engineUpdateProps([{ shadowNodeWrapper: animatedRef, updates }])
552
+ }
553
+ function dispatchCommandImpl(): void {}
554
+ function setGestureStateImpl(): void {}
555
+
556
+ export const scrollTo = markWorkletBuiltin('scrollTo', scrollToImpl)
557
+ export const measure = markWorkletBuiltin('measure', measureImpl)
558
+ export const setNativeProps = markWorkletBuiltin('setNativeProps', setNativePropsImpl)
559
+ export const dispatchCommand = markWorkletBuiltin('dispatchCommand', dispatchCommandImpl)
560
+ export const setGestureState = markWorkletBuiltin('setGestureState', setGestureStateImpl)
561
+
562
+ // also expose the proxy install so a caller can re-run it (idempotent).
563
+ export { installReanimatedNativeSeam }
564
+
565
+ // satisfy the unused-binding lint for the optional keyboard handler shape.
566
+ export type { SerializableLike }
@@ -0,0 +1,80 @@
1
+ // reanimated worklet/UI runtime entry — bundled standalone as dist/ui-runtime.js
2
+ // and evaluated by the SECOND JavaScriptCore runtime (rust jsc::start_ui, thread
3
+ // "jsc-ui"). See plans/off-thread-reanimated.md.
4
+ //
5
+ // This bundle is app-independent library code. It provides everything a
6
+ // dispatched worklet needs to execute here:
7
+ // - the seam (KIND=2 globals: `_updateProps` → coalesced __rngpui_setNodeStyle,
8
+ // _getAnimationTimestamp, the frame-callback registry) — imported first so the
9
+ // globals exist before upstream reanimated evaluates,
10
+ // - vsync requestAnimationFrame (this runtime arms frame_clock bit 1),
11
+ // - upstream react-native-reanimated core: its valueSetter drives `sv.value =
12
+ // withSpring(...)` animations HERE, frame by frame, off the React thread,
13
+ // - the worklet runtime in 'ui' role (closure materialization, shared-value
14
+ // slots over __rngpui_svSlots, runOnJS bridging back to the React runtime),
15
+ // - the worklet builtin registry, so closures serialized as {kind:'builtin'}
16
+ // (withSpring, withTiming, scrollTo, …) bind to THIS runtime's implementations.
17
+ import './seam'
18
+ import '../raf'
19
+ import {
20
+ Easing,
21
+ withClamp,
22
+ withDecay,
23
+ withDelay,
24
+ withRepeat,
25
+ withSequence,
26
+ withSpring,
27
+ withTiming,
28
+ } from 'react-native-reanimated'
29
+ import { dispatchCommand, measure, scrollTo, setGestureState, setNativeProps } from './seam'
30
+ import { registerWorkletBuiltin } from './worklet-runtime'
31
+
32
+ // worklets.ts (imported by upstream's react-native-worklets redirect) installs the
33
+ // worklet runtime + channel at module load, role-detected from the host fns
34
+ // (__rngpui_jsPost ⇒ 'ui'). By the time this body runs, the runtime is live and
35
+ // __rngpui_peerRecv is wired; all that's left is populating the builtin registry.
36
+
37
+ registerWorkletBuiltin('withTiming', withTiming)
38
+ registerWorkletBuiltin('withSpring', withSpring)
39
+ registerWorkletBuiltin('withDecay', withDecay)
40
+ registerWorkletBuiltin('withDelay', withDelay)
41
+ registerWorkletBuiltin('withRepeat', withRepeat)
42
+ registerWorkletBuiltin('withSequence', withSequence)
43
+ registerWorkletBuiltin('withClamp', withClamp)
44
+ registerWorkletBuiltin('Easing', Easing)
45
+
46
+ // upstream's valueSetter (surfaced by the prebuild under an rngpui name): the
47
+ // shared-value proxies route writes through it so `sv.value = animation` drives
48
+ // the animation HERE — _animation cancel semantics, __frameTimestamp, the
49
+ // requestAnimationFrame step loop, per-frame `_value` writes that fan out to
50
+ // mapper listeners. without it the proxy's setter throws by design.
51
+ import * as upstreamInternals from 'react-native-reanimated'
52
+ ;(globalThis as Record<string, unknown>).__rngpuiReanimatedValueSetter = (
53
+ upstreamInternals as unknown as Record<string, unknown>
54
+ ).__rngpuiValueSetter
55
+
56
+ // the revive table for stamped animation requests shipped by a React-side
57
+ // `sv.value = withX(...)` write (reanimated-host.ts stamps; worklets.ts
58
+ // HOST_SET_SHARED_VALUE_WORKLET revives by `type` from this global and the
59
+ // animation drives HERE, off the React thread).
60
+ ;(globalThis as Record<string, unknown>).__rngpuiReanimatedAnimationFactories = {
61
+ withTiming,
62
+ withSpring,
63
+ withDecay,
64
+ withDelay,
65
+ withRepeat,
66
+ withSequence,
67
+ withClamp,
68
+ }
69
+ registerWorkletBuiltin('scrollTo', scrollTo)
70
+ registerWorkletBuiltin('measure', measure)
71
+ registerWorkletBuiltin('setNativeProps', setNativeProps)
72
+ registerWorkletBuiltin('dispatchCommand', dispatchCommand)
73
+ registerWorkletBuiltin('setGestureState', setGestureState)
74
+
75
+ // readiness beacon for conformance probes (RNGPUI_UI_TRACE) — proves this bundle
76
+ // evaluated on the worklet runtime, not the React one.
77
+ ;(globalThis as { __rngpuiUiRuntimeReady?: boolean }).__rngpuiUiRuntimeReady = true
78
+ if (typeof process !== 'undefined' && process.env?.RNGPUI_UI_TRACE) {
79
+ console.error('[ui-runtime] ready (worklet runtime evaluated)')
80
+ }