@sigx/lynx-runtime 0.2.4 → 0.4.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.
@@ -12,5 +12,5 @@
12
12
  *
13
13
  * The old names continue to work via these re-exports for one minor cycle.
14
14
  */
15
- export { SharedValue as AnimatedValue, useSharedValue as useAnimatedValue, } from './shared-value.js';
16
- export type { SharedValueState as AnimatedValueState } from './shared-value.js';
15
+ export { SharedValue as AnimatedValue, useSharedValue as useAnimatedValue, } from './shared-value';
16
+ export type { SharedValueState as AnimatedValueState } from './shared-value';
@@ -1,5 +1,5 @@
1
1
  import type { PrimitiveSignal } from '@sigx/reactivity';
2
- import { MainThreadRef } from '../main-thread-ref.js';
2
+ import { MainThreadRef } from '../main-thread-ref';
3
3
  /**
4
4
  * Internal envelope shape stored under `MainThreadRef.current`. Wrapping the
5
5
  * value in `{ value: T }` (rather than a bare `T`) lets MT worklets mutate
@@ -1,7 +1,7 @@
1
- import type { MainThreadRef } from '../main-thread-ref.js';
2
- import type { MainThread } from '../jsx.js';
1
+ import type { MainThreadRef } from '../main-thread-ref';
2
+ import type { MainThread } from '../jsx';
3
3
  import type { BuiltinMapperName, MapperParams } from '@sigx/lynx-runtime-internal';
4
- import type { SharedValue } from './shared-value.js';
4
+ import type { SharedValue } from './shared-value';
5
5
  export type { BuiltinMapperName, MapperParams };
6
6
  /** Reset hook for tests. */
7
7
  export declare function resetAnimatedStyleBindingIds(): void;
package/dist/flush.d.ts CHANGED
@@ -5,4 +5,4 @@
5
5
  * directly. Instead, ops are batched in a queue and flushed to the Main Thread
6
6
  * via sigxPatchUpdate. This file exists for backwards-compatible imports.
7
7
  */
8
- export { scheduleFlush, flushNow, resetOpQueue as resetFlushState } from './op-queue.js';
8
+ export { scheduleFlush, flushNow, resetOpQueue as resetFlushState } from './op-queue';
package/dist/hmr.d.ts CHANGED
@@ -19,14 +19,22 @@
19
19
  * the per-module index, `component()` fires `onDefine`, and existing
20
20
  * instances are patched in-place (property-ops only — no crash).
21
21
  */
22
+ import type { ComponentSetupContext } from '@sigx/runtime-core/internals';
22
23
  type RegisterFn = (plugin: {
23
24
  onDefine?: (name: string | undefined, factory: any, setup: Function) => void;
24
25
  }) => void;
26
+ type SetCurrentInstanceFn = (ctx: ComponentSetupContext | null) => ComponentSetupContext | null;
25
27
  /**
26
28
  * Initialise the HMR plugin using the *app-side* registerComponentPlugin.
27
29
  * Called once by the loader-injected preamble. Idempotent.
30
+ *
31
+ * `setCurrentInstanceFn` is the renderer's instance-stack push/pop helper
32
+ * (re-exported from `@sigx/lynx` as `__setCurrentInstanceForHMR`). Without
33
+ * it, re-running a screen's setup during HMR throws on hooks that depend on
34
+ * provide/inject (`useNav`, etc.) because the renderer's currentInstance is
35
+ * `null` when called outside the normal mount path.
28
36
  */
29
- export declare function initHMR(registerComponentPlugin: RegisterFn): void;
37
+ export declare function initHMR(registerComponentPlugin: RegisterFn, setCurrentInstanceFn?: SetCurrentInstanceFn): void;
30
38
  /**
31
39
  * Register the current module for HMR tracking.
32
40
  * Called at the top of each transformed module by the HMR loader.
package/dist/hmr.js CHANGED
@@ -1,38 +1,42 @@
1
1
  //#region src/hmr.ts
2
- var e = /* @__PURE__ */ new Map(), t = /* @__PURE__ */ new Map(), n = null, r = !1;
3
- function i(t) {
4
- r || (r = !0, t({ onDefine(t, n, r) {
5
- let i = o();
6
- if (!i) return;
7
- n.__hmrId = i;
8
- let a = e.get(i);
9
- a && a.size > 0 && a.forEach((e) => {
2
+ var e = /* @__PURE__ */ new Map(), t = /* @__PURE__ */ new Map(), n = null, r = null, i = !1;
3
+ function a(t, n) {
4
+ i || (i = !0, n && (r = n), t({ onDefine(t, n, i) {
5
+ let a = s();
6
+ if (!a) return;
7
+ n.__hmrId = a;
8
+ let o = e.get(a);
9
+ o && o.size > 0 && o.forEach((e) => {
10
+ let n = r ? r(e.ctx) : null;
10
11
  try {
11
- let t = r(e.ctx);
12
+ let t = i(e.ctx);
12
13
  e.ctx.renderFn = t, e.ctx.update();
13
14
  } catch (e) {
14
- console.error(`[sigx-hmr] Failed to update ${t || "component"}:`, e);
15
+ let n = e?.message ?? String(e), r = e?.stack ?? "<no stack>";
16
+ console.error(`[sigx-hmr] Failed to update ${t || "component"}: ${n}\n${r}`);
17
+ } finally {
18
+ r && r(n);
15
19
  }
16
20
  });
17
- let s = r;
21
+ let c = i;
18
22
  n.__setup = (t) => {
19
- let n = s(t), r = { ctx: t }, a = e.get(i);
20
- return a || (a = /* @__PURE__ */ new Set(), e.set(i, a)), a.add(r), t.onUnmounted(() => {
21
- let t = e.get(i);
23
+ let n = c(t), r = { ctx: t }, i = e.get(a);
24
+ return i || (i = /* @__PURE__ */ new Set(), e.set(a, i)), i.add(r), t.onUnmounted(() => {
25
+ let t = e.get(a);
22
26
  t && t.delete(r);
23
27
  }), n;
24
28
  };
25
29
  } }));
26
30
  }
27
- function a(e) {
31
+ function o(e) {
28
32
  n = e, t.set(e, 0);
29
33
  }
30
- function o() {
34
+ function s() {
31
35
  if (!n) return null;
32
36
  let e = t.get(n) || 0;
33
37
  return t.set(n, e + 1), `${n}:${e}`;
34
38
  }
35
39
  //#endregion
36
- export { i as initHMR, a as registerHMRModule };
40
+ export { a as initHMR, o as registerHMRModule };
37
41
 
38
42
  //# sourceMappingURL=hmr.js.map
package/dist/hmr.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"hmr.js","names":[],"sources":["../src/hmr.ts"],"sourcesContent":["/**\n * HMR runtime for sigx-lynx (rspack/rsbuild).\n *\n * The `@sigx/lynx` meta package inlines runtime-core into a single bundle,\n * so `@sigx/runtime-core/internals` would resolve to a *different* copy of\n * the plugin registry than the one used by `component()`. To work around\n * this the HMR loader injects a call to `initHMR(registerComponentPlugin)`\n * where `registerComponentPlugin` is imported from `@sigx/lynx` (the same\n * bundle the app uses), ensuring a single shared plugin array.\n *\n * Flow:\n * 1. Loader prepends:\n * import { __registerComponentPlugin } from '@sigx/lynx';\n * import { initHMR, registerHMRModule } from '@sigx/lynx-runtime/hmr';\n * initHMR(__registerComponentPlugin);\n * registerHMRModule('<moduleId>');\n * 2. On first call, `initHMR` installs the onDefine plugin.\n * 3. On HMR update the module re-executes, `registerHMRModule` resets\n * the per-module index, `component()` fires `onDefine`, and existing\n * instances are patched in-place (property-ops only — no crash).\n */\n\n// Type-only import — NOT a runtime import from runtime-core\nimport type { ComponentSetupContext } from '@sigx/runtime-core/internals';\n\ntype RegisterFn = (plugin: { onDefine?: (name: string | undefined, factory: any, setup: Function) => void }) => void;\n\ninterface InstanceEntry {\n ctx: ComponentSetupContext;\n}\n\n// Track instances by component ID (moduleId:index)\nconst instancesByComponentId = new Map<string, Set<InstanceEntry>>();\n\n// Track component definition order within each module\nconst moduleComponentIndex = new Map<string, number>();\n\n// Current module being registered\nlet currentModuleId: string | null = null;\n\nlet installed = false;\n\n/**\n * Initialise the HMR plugin using the *app-side* registerComponentPlugin.\n * Called once by the loader-injected preamble. Idempotent.\n */\nexport function initHMR(registerComponentPlugin: RegisterFn): void {\n if (installed) return;\n installed = true;\n\n registerComponentPlugin({\n onDefine(name: string | undefined, factory: any, setup: Function) {\n const componentId = getNextComponentId();\n if (!componentId) return;\n\n factory.__hmrId = componentId;\n\n const existingInstances = instancesByComponentId.get(componentId);\n\n if (existingInstances && existingInstances.size > 0) {\n // HMR update: patch all existing instances with the new setup\n existingInstances.forEach(instance => {\n try {\n const newRenderFn = setup(instance.ctx);\n instance.ctx.renderFn = newRenderFn;\n instance.ctx.update();\n } catch (e) {\n console.error(`[sigx-hmr] Failed to update ${name || 'component'}:`, e);\n }\n });\n }\n\n // Wrap setup to track future instances\n const originalSetup = setup;\n\n factory.__setup = (ctx: ComponentSetupContext) => {\n const renderFn = originalSetup(ctx);\n\n const instance: InstanceEntry = { ctx };\n\n let instances = instancesByComponentId.get(componentId);\n if (!instances) {\n instances = new Set();\n instancesByComponentId.set(componentId, instances);\n }\n instances.add(instance);\n\n ctx.onUnmounted(() => {\n const instances = instancesByComponentId.get(componentId);\n if (instances) instances.delete(instance);\n });\n\n return renderFn;\n };\n }\n });\n}\n\n/**\n * Register the current module for HMR tracking.\n * Called at the top of each transformed module by the HMR loader.\n */\nexport function registerHMRModule(moduleId: string): void {\n currentModuleId = moduleId;\n moduleComponentIndex.set(moduleId, 0);\n}\n\n/**\n * Get the next component ID for the current module.\n */\nfunction getNextComponentId(): string | null {\n if (!currentModuleId) return null;\n\n const index = moduleComponentIndex.get(currentModuleId) || 0;\n moduleComponentIndex.set(currentModuleId, index + 1);\n\n return `${currentModuleId}:${index}`;\n}\n"],"mappings":";AAgCA,IAAM,oBAAyB,IAAI,KAAiC,EAG9D,oBAAuB,IAAI,KAAqB,EAGlD,IAAiC,MAEjC,IAAY;AAMhB,SAAgB,EAAQ,GAA2C;CAC3D,MACJ,IAAY,IAEZ,EAAwB,EACpB,SAAS,GAA0B,GAAc,GAAiB;EAC9D,IAAM,IAAc,GAAoB;EACxC,IAAI,CAAC,GAAa;EAElB,EAAQ,UAAU;EAElB,IAAM,IAAoB,EAAuB,IAAI,EAAY;EAEjE,AAAI,KAAqB,EAAkB,OAAO,KAE9C,EAAkB,SAAQ,MAAY;GAClC,IAAI;IACA,IAAM,IAAc,EAAM,EAAS,IAAI;IAEvC,AADA,EAAS,IAAI,WAAW,GACxB,EAAS,IAAI,QAAQ;YAChB,GAAG;IACR,QAAQ,MAAM,+BAA+B,KAAQ,YAAY,IAAI,EAAE;;IAE7E;EAIN,IAAM,IAAgB;EAEtB,EAAQ,WAAW,MAA+B;GAC9C,IAAM,IAAW,EAAc,EAAI,EAE7B,IAA0B,EAAE,QAAK,EAEnC,IAAY,EAAuB,IAAI,EAAY;GAYvD,OAXK,MACD,oBAAY,IAAI,KAAK,EACrB,EAAuB,IAAI,GAAa,EAAU,GAEtD,EAAU,IAAI,EAAS,EAEvB,EAAI,kBAAkB;IAClB,IAAM,IAAY,EAAuB,IAAI,EAAY;IACzD,AAAI,KAAW,EAAU,OAAO,EAAS;KAC3C,EAEK;;IAGlB,CAAC;;AAON,SAAgB,EAAkB,GAAwB;CAEtD,AADA,IAAkB,GAClB,EAAqB,IAAI,GAAU,EAAE;;AAMzC,SAAS,IAAoC;CACzC,IAAI,CAAC,GAAiB,OAAO;CAE7B,IAAM,IAAQ,EAAqB,IAAI,EAAgB,IAAI;CAG3D,OAFA,EAAqB,IAAI,GAAiB,IAAQ,EAAE,EAE7C,GAAG,EAAgB,GAAG"}
1
+ {"version":3,"file":"hmr.js","names":[],"sources":["../src/hmr.ts"],"sourcesContent":["/**\n * HMR runtime for sigx-lynx (rspack/rsbuild).\n *\n * The `@sigx/lynx` meta package inlines runtime-core into a single bundle,\n * so `@sigx/runtime-core/internals` would resolve to a *different* copy of\n * the plugin registry than the one used by `component()`. To work around\n * this the HMR loader injects a call to `initHMR(registerComponentPlugin)`\n * where `registerComponentPlugin` is imported from `@sigx/lynx` (the same\n * bundle the app uses), ensuring a single shared plugin array.\n *\n * Flow:\n * 1. Loader prepends:\n * import { __registerComponentPlugin } from '@sigx/lynx';\n * import { initHMR, registerHMRModule } from '@sigx/lynx-runtime/hmr';\n * initHMR(__registerComponentPlugin);\n * registerHMRModule('<moduleId>');\n * 2. On first call, `initHMR` installs the onDefine plugin.\n * 3. On HMR update the module re-executes, `registerHMRModule` resets\n * the per-module index, `component()` fires `onDefine`, and existing\n * instances are patched in-place (property-ops only — no crash).\n */\n\n// Type-only import — NOT a runtime import from runtime-core\nimport type { ComponentSetupContext } from '@sigx/runtime-core/internals';\n\ntype RegisterFn = (plugin: { onDefine?: (name: string | undefined, factory: any, setup: Function) => void }) => void;\ntype SetCurrentInstanceFn = (ctx: ComponentSetupContext | null) => ComponentSetupContext | null;\n\ninterface InstanceEntry {\n ctx: ComponentSetupContext;\n}\n\n// Track instances by component ID (moduleId:index)\nconst instancesByComponentId = new Map<string, Set<InstanceEntry>>();\n\n// Track component definition order within each module\nconst moduleComponentIndex = new Map<string, number>();\n\n// Current module being registered\nlet currentModuleId: string | null = null;\n\n// The renderer's currentInstance setter — captured from the app-side bundle\n// so push/pop targets the SAME instance stack the renderer reads from. If\n// missing (older app version that doesn't inject it), HMR patches skip the\n// push/pop and rely on the caller having no context-dependent hooks.\nlet setCurrentInstance: SetCurrentInstanceFn | null = null;\n\nlet installed = false;\n\n/**\n * Initialise the HMR plugin using the *app-side* registerComponentPlugin.\n * Called once by the loader-injected preamble. Idempotent.\n *\n * `setCurrentInstanceFn` is the renderer's instance-stack push/pop helper\n * (re-exported from `@sigx/lynx` as `__setCurrentInstanceForHMR`). Without\n * it, re-running a screen's setup during HMR throws on hooks that depend on\n * provide/inject (`useNav`, etc.) because the renderer's currentInstance is\n * `null` when called outside the normal mount path.\n */\nexport function initHMR(\n registerComponentPlugin: RegisterFn,\n setCurrentInstanceFn?: SetCurrentInstanceFn,\n): void {\n if (installed) return;\n installed = true;\n\n if (setCurrentInstanceFn) {\n setCurrentInstance = setCurrentInstanceFn;\n }\n\n registerComponentPlugin({\n onDefine(name: string | undefined, factory: any, setup: Function) {\n const componentId = getNextComponentId();\n if (!componentId) return;\n\n factory.__hmrId = componentId;\n\n const existingInstances = instancesByComponentId.get(componentId);\n\n if (existingInstances && existingInstances.size > 0) {\n // HMR update: patch all existing instances with the new setup.\n // The renderer pushes the active instance onto a stack before\n // calling setup so that hooks like `useNav()` can resolve\n // provide/inject up the parent chain. We're calling setup\n // *outside* the renderer's mount path here, so we mirror the\n // push/pop ourselves — otherwise context-dependent hooks\n // throw with messages like \"no <NavigationRoot> is mounted\".\n existingInstances.forEach(instance => {\n const prev = setCurrentInstance ? setCurrentInstance(instance.ctx) : null;\n try {\n const newRenderFn = setup(instance.ctx);\n instance.ctx.renderFn = newRenderFn;\n instance.ctx.update();\n } catch (e: any) {\n const msg = e?.message ?? String(e);\n const stack = e?.stack ?? '<no stack>';\n console.error(\n `[sigx-hmr] Failed to update ${name || 'component'}: ${msg}\\n${stack}`,\n );\n } finally {\n if (setCurrentInstance) setCurrentInstance(prev);\n }\n });\n }\n\n // Wrap setup to track future instances\n const originalSetup = setup;\n\n factory.__setup = (ctx: ComponentSetupContext) => {\n const renderFn = originalSetup(ctx);\n\n const instance: InstanceEntry = { ctx };\n\n let instances = instancesByComponentId.get(componentId);\n if (!instances) {\n instances = new Set();\n instancesByComponentId.set(componentId, instances);\n }\n instances.add(instance);\n\n ctx.onUnmounted(() => {\n const instances = instancesByComponentId.get(componentId);\n if (instances) instances.delete(instance);\n });\n\n return renderFn;\n };\n }\n });\n}\n\n/**\n * Register the current module for HMR tracking.\n * Called at the top of each transformed module by the HMR loader.\n */\nexport function registerHMRModule(moduleId: string): void {\n currentModuleId = moduleId;\n moduleComponentIndex.set(moduleId, 0);\n}\n\n/**\n * Get the next component ID for the current module.\n */\nfunction getNextComponentId(): string | null {\n if (!currentModuleId) return null;\n\n const index = moduleComponentIndex.get(currentModuleId) || 0;\n moduleComponentIndex.set(currentModuleId, index + 1);\n\n return `${currentModuleId}:${index}`;\n}\n"],"mappings":";AAiCA,IAAM,oBAAyB,IAAI,KAAiC,EAG9D,oBAAuB,IAAI,KAAqB,EAGlD,IAAiC,MAMjC,IAAkD,MAElD,IAAY;AAYhB,SAAgB,EACZ,GACA,GACI;CACA,MACJ,IAAY,IAER,MACA,IAAqB,IAGzB,EAAwB,EACpB,SAAS,GAA0B,GAAc,GAAiB;EAC9D,IAAM,IAAc,GAAoB;EACxC,IAAI,CAAC,GAAa;EAElB,EAAQ,UAAU;EAElB,IAAM,IAAoB,EAAuB,IAAI,EAAY;EAEjE,AAAI,KAAqB,EAAkB,OAAO,KAQ9C,EAAkB,SAAQ,MAAY;GAClC,IAAM,IAAO,IAAqB,EAAmB,EAAS,IAAI,GAAG;GACrE,IAAI;IACA,IAAM,IAAc,EAAM,EAAS,IAAI;IAEvC,AADA,EAAS,IAAI,WAAW,GACxB,EAAS,IAAI,QAAQ;YAChB,GAAQ;IACb,IAAM,IAAM,GAAG,WAAW,OAAO,EAAE,EAC7B,IAAQ,GAAG,SAAS;IAC1B,QAAQ,MACJ,+BAA+B,KAAQ,YAAY,IAAI,EAAI,IAAI,IAClE;aACK;IACN,AAAI,KAAoB,EAAmB,EAAK;;IAEtD;EAIN,IAAM,IAAgB;EAEtB,EAAQ,WAAW,MAA+B;GAC9C,IAAM,IAAW,EAAc,EAAI,EAE7B,IAA0B,EAAE,QAAK,EAEnC,IAAY,EAAuB,IAAI,EAAY;GAYvD,OAXK,MACD,oBAAY,IAAI,KAAK,EACrB,EAAuB,IAAI,GAAa,EAAU,GAEtD,EAAU,IAAI,EAAS,EAEvB,EAAI,kBAAkB;IAClB,IAAM,IAAY,EAAuB,IAAI,EAAY;IACzD,AAAI,KAAW,EAAU,OAAO,EAAS;KAC3C,EAEK;;IAGlB,CAAC;;AAON,SAAgB,EAAkB,GAAwB;CAEtD,AADA,IAAkB,GAClB,EAAqB,IAAI,GAAU,EAAE;;AAMzC,SAAS,IAAoC;CACzC,IAAI,CAAC,GAAiB,OAAO;CAE7B,IAAM,IAAQ,EAAqB,IAAI,EAAgB,IAAI;CAG3D,OAFA,EAAqB,IAAI,GAAiB,IAAQ,EAAE,EAE7C,GAAG,EAAgB,GAAG"}
package/dist/index.d.ts CHANGED
@@ -1,24 +1,24 @@
1
- import './jsx.js';
2
- import './types.js';
3
- import './model-processor.js';
4
- import './bg-bridge.js';
5
- import './run-on-background.js';
6
- export { render, lynxMount } from './render.js';
7
- export { nodeOps } from './nodeOps.js';
8
- export type { LynxNode, LynxElement } from './nodeOps.js';
9
- export { ShadowElement, createPageRoot, resetShadowState } from './shadow-element.js';
10
- export { pushOp, takeOps, scheduleFlush, flushNow, resetOpQueue } from './op-queue.js';
1
+ import './jsx';
2
+ import './types';
3
+ import './model-processor';
4
+ import './bg-bridge';
5
+ import './run-on-background';
6
+ export { render, lynxMount } from './render';
7
+ export { nodeOps } from './nodeOps';
8
+ export type { LynxNode, LynxElement } from './nodeOps';
9
+ export { ShadowElement, createPageRoot, resetShadowState } from './shadow-element';
10
+ export { pushOp, takeOps, scheduleFlush, flushNow, resetOpQueue } from './op-queue';
11
11
  export { OP } from '@sigx/lynx-runtime-internal';
12
12
  export type { OpCode, MapperParams, RangeParams, BuiltinMapperName, AnimatedStyleMapper, } from '@sigx/lynx-runtime-internal';
13
- export { register, updateHandler, unregister, getHandler, publishEvent, resetRegistry, } from './event-registry.js';
14
- export { MainThreadRef, useMainThreadRef, resetWvidCounter, } from './main-thread-ref.js';
15
- export { registerBgSink, unregisterBgSink, ingestAvPublishes, resetBgAvBridge, bgAvSinkCount, } from './animated-bridge.js';
16
- export { useSharedValue, SharedValue, } from './animated/shared-value.js';
17
- export type { SharedValueState } from './animated/shared-value.js';
18
- export { useAnimatedStyle, resetAnimatedStyleBindingIds, } from './animated/use-animated-style.js';
19
- export { useAnimatedValue, AnimatedValue, } from './animated/animated-value.js';
20
- export type { AnimatedValueState } from './animated/animated-value.js';
21
- export { runOnMainThread, runOnBackground, resetThreading, transformToWorklet, resetRunOnBackgroundState, } from './threading.js';
22
- export { Gesture, GestureType, useGestureDetector, resetGestureIdCounter, } from './native/index.js';
23
- export type { GestureTypeValue, GestureWorklet, GestureCallback, BaseGesture, ComposedGesture, AnyGesture, } from './native/index.js';
24
- export type { LynxEventHandler, LynxCommonAttributes, ViewAttributes, TextAttributes, ImageAttributes, ScrollViewAttributes, ListAttributes, ListItemAttributes, InputAttributes, TextAreaAttributes, PageAttributes, SvgAttributes, FilterImageAttributes, MainThread, } from './jsx.js';
13
+ export { register, updateHandler, unregister, getHandler, publishEvent, resetRegistry, } from './event-registry';
14
+ export { MainThreadRef, useMainThreadRef, resetWvidCounter, } from './main-thread-ref';
15
+ export { registerBgSink, unregisterBgSink, ingestAvPublishes, resetBgAvBridge, bgAvSinkCount, } from './animated-bridge';
16
+ export { useSharedValue, SharedValue, } from './animated/shared-value';
17
+ export type { SharedValueState } from './animated/shared-value';
18
+ export { useAnimatedStyle, resetAnimatedStyleBindingIds, } from './animated/use-animated-style';
19
+ export { useAnimatedValue, AnimatedValue, } from './animated/animated-value';
20
+ export type { AnimatedValueState } from './animated/animated-value';
21
+ export { runOnMainThread, runOnBackground, resetThreading, transformToWorklet, resetRunOnBackgroundState, } from './threading';
22
+ export { Gesture, GestureType, useGestureDetector, resetGestureIdCounter, } from './native/index';
23
+ export type { GestureTypeValue, GestureWorklet, GestureCallback, BaseGesture, ComposedGesture, AnyGesture, } from './native/index';
24
+ export type { LynxEventHandler, LynxCommonAttributes, ViewAttributes, TextAttributes, ImageAttributes, ScrollViewAttributes, ListAttributes, ListItemAttributes, InputAttributes, TextAreaAttributes, PageAttributes, SvgAttributes, FilterImageAttributes, MainThread, } from './jsx';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/model-processor.ts","../src/event-registry.ts","../src/animated-bridge.ts","../src/bg-bridge.ts","../src/run-on-background.ts","../src/op-queue.ts","../src/shadow-element.ts","../src/main-thread-ref.ts","../src/nodeOps.ts","../src/render.ts","../src/animated/shared-value.ts","../src/animated/use-animated-style.ts","../src/threading.ts","../src/native/gesture-detector.ts"],"sourcesContent":["/**\n * Platform-specific model processor for Lynx form elements.\n *\n * Wires sigx's `model={() => state.field}` two-way binding directive to\n * Lynx's <input> and <textarea> elements. The processor runs at JSX\n * creation time (called from runtime-core/jsx-runtime.ts when an\n * intrinsic element has a `model` prop) and rewrites the props to:\n *\n * 1. Set the initial `value` from the bound state\n * 2. Install a `bindinput` handler that pushes the new value back into state\n *\n * Lynx differs from DOM in two important ways:\n *\n * - Lynx <input> fires `bindinput` with `event.detail.value` containing\n * the new text. There is no DOM-style `target.value` property — the\n * value lives on the event detail object.\n * - Lynx Go (and most current Lynx hosts) have no native checkbox or\n * radio elements. We only handle text-style <input> and <textarea>\n * here. Adding checkbox/radio later is straightforward — mirror the\n * branches in packages/runtime-dom/src/model-processor.ts.\n *\n * Mirrors packages/runtime-dom/src/model-processor.ts.\n */\n\nimport { setPlatformModelProcessor } from '@sigx/runtime-core/internals';\n\ninterface LynxInputEvent {\n detail?: { value?: unknown };\n}\n\nsetPlatformModelProcessor((type, props, [stateObj, key], _originalProps) => {\n // Helper to set value — uses onUpdate handler if available (for props\n // model forwarding through component boundaries).\n const setValue = (v: unknown): void => {\n const updateHandler = stateObj[`onUpdate:${key}`];\n if (typeof updateHandler === 'function') {\n updateHandler(v);\n } else {\n stateObj[key] = v;\n }\n };\n\n // Text <input>\n if (type === 'input') {\n props.value = stateObj[key] ?? '';\n const existingBindInput = props.bindinput as\n | ((e: LynxInputEvent) => void)\n | undefined;\n props.bindinput = (e: LynxInputEvent) => {\n const v = e?.detail?.value ?? '';\n setValue(v);\n if (existingBindInput) existingBindInput(e);\n };\n const existingHandler = props['onUpdate:modelValue'];\n props['onUpdate:modelValue'] = (v: unknown) => {\n setValue(v);\n if (existingHandler) existingHandler(v);\n };\n return true;\n }\n\n // <textarea>\n if (type === 'textarea') {\n props.value = stateObj[key] ?? '';\n const existingBindInput = props.bindinput as\n | ((e: LynxInputEvent) => void)\n | undefined;\n props.bindinput = (e: LynxInputEvent) => {\n const v = e?.detail?.value ?? '';\n setValue(v);\n if (existingBindInput) existingBindInput(e);\n };\n const existingHandler = props['onUpdate:modelValue'];\n props['onUpdate:modelValue'] = (v: unknown) => {\n setValue(v);\n if (existingHandler) existingHandler(v);\n };\n return true;\n }\n\n // Not handled — fall back to the generic modelValue/onUpdate:modelValue\n // pair so component-level model forwarding still works.\n return false;\n});\n","/**\n * Sign-based event handler registry for the Background Thread.\n *\n * When patchProp registers an event handler, it gets a unique sign string.\n * The Main Thread stores this sign with __AddEvent(). When Native fires an\n * event it calls publishEvent(sign, data) on the BG Thread, which looks up\n * the handler and executes it directly — no cross-thread round-trip.\n *\n * sigx uses signal closures, so event handlers are plain functions captured by the component's\n * render closure.\n */\n\ninterface RegistryState {\n signCounter: number;\n handlers: Map<string, (data: unknown) => void>;\n}\n\n// The consumer bundle can materialise multiple event-registry module instances\n// (for example via separate entry-background/runtime graphs). Store the state\n// on globalThis so register() and publishEvent() still see the same handlers.\nconst REGISTRY_STATE_KEY = '__SIGX_LYNX_EVENT_REGISTRY__';\n\ntype RegistryGlobal = typeof globalThis & {\n __SIGX_LYNX_EVENT_REGISTRY__?: RegistryState;\n};\n\nfunction getRegistryState(): RegistryState {\n const g = globalThis as RegistryGlobal;\n let state = g[REGISTRY_STATE_KEY];\n if (!state) {\n state = {\n signCounter: 0,\n handlers: new Map<string, (data: unknown) => void>(),\n };\n Object.defineProperty(g, REGISTRY_STATE_KEY, {\n value: state,\n configurable: true,\n enumerable: false,\n writable: true,\n });\n }\n return state;\n}\n\n/**\n * Register a handler and return a unique sign string.\n * The sign is passed to __AddEvent so that the MTS can route events back.\n */\nexport function register(handler: (data: unknown) => void): string {\n const state = getRegistryState();\n const sign = `sigx:${state.signCounter++}`;\n state.handlers.set(sign, handler);\n return sign;\n}\n\n/**\n * Update the handler for an existing sign without changing the sign.\n * Used on re-renders: keeps the same sign registered on the Main Thread\n * while pointing it to the freshest handler closure.\n */\nexport function updateHandler(\n sign: string,\n handler: (data: unknown) => void,\n): void {\n getRegistryState().handlers.set(sign, handler);\n}\n\n/**\n * Get the current handler for a sign.\n */\nexport function getHandler(sign: string): ((data: unknown) => void) | undefined {\n return getRegistryState().handlers.get(sign);\n}\n\n/**\n * Unregister a handler by its sign.\n */\nexport function unregister(sign: string): void {\n getRegistryState().handlers.delete(sign);\n}\n\n/**\n * Called by Lynx Native when an event fires on the BG Thread.\n * Looks up the handler by sign and invokes it with the event data.\n */\nexport function publishEvent(sign: string, data: unknown): void {\n getRegistryState().handlers.get(sign)?.(data);\n}\n\n/** Reset all state — for testing only. */\nexport function resetRegistry(): void {\n const state = getRegistryState();\n state.signCounter = 0;\n state.handlers.clear();\n}\n","/**\n * BG-side SharedValue bridge — receives MT-thread publishes.\n *\n * Maintains a `wvid → signal<T>` registry. The MT-side bridge in\n * `@sigx/lynx-runtime-main/animated-bridge-mt.ts` dispatches batched\n * `Lynx.Sigx.AvPublish` events with `[wvid, value]` tuples; `bg-bridge.ts`\n * routes them through `ingestAvPublishes()` here, which writes each new\n * value into the corresponding signal. Sigx reactivity tracking then re-runs\n * any `effect` that read `sv.value` on BG.\n *\n * Producer: the `useSharedValue` hook in `@sigx/lynx` allocates the\n * BG-side signal via `registerBgSink(wvid, initial)` and tears it down via\n * `unregisterBgSink(wvid)` on component unmount.\n *\n * Naming note: the wire-format ops (`REGISTER_AV_BRIDGE`, `UNREGISTER_AV_BRIDGE`,\n * `Lynx.Sigx.AvPublish`) keep their original `Av` prefix as infrastructure\n * constants. The user-facing primitive renamed from `AnimatedValue` to\n * `SharedValue` in Phase 2.8.\n */\n\nimport { signal, type PrimitiveSignal } from '@sigx/reactivity';\n\nconst bgRegistry = new Map<number, PrimitiveSignal<unknown>>();\n\n/**\n * Allocate a BG-side signal mirror for the given wvid. Returns the signal so\n * the caller can read it via `.value` — sigx tracking applies as usual.\n *\n * Idempotent on the wvid: if a signal is already registered, returns the\n * existing one without resetting its value (avoids losing in-flight publishes\n * during HMR-triggered re-registration).\n */\nexport function registerBgSink<T>(wvid: number, initial: T): PrimitiveSignal<T> {\n const existing = bgRegistry.get(wvid);\n if (existing) return existing as unknown as PrimitiveSignal<T>;\n // sigx's `signal` has Primitive vs object overloads; SharedValues hold\n // primitives in practice (numbers, strings) and the bridge only ever\n // writes through `.value`, so a single-overload selection via cast keeps\n // the unconstrained T usable here without forcing SharedValue consumers\n // to type-narrow at every call site.\n const s = signal(initial as unknown as number);\n bgRegistry.set(wvid, s as unknown as PrimitiveSignal<unknown>);\n return s as unknown as PrimitiveSignal<T>;\n}\n\n/**\n * Drop the BG-side signal for this wvid. Called on component unmount.\n * Subsequent `ingestPublish` entries for this wvid become no-ops.\n */\nexport function unregisterBgSink(wvid: number): void {\n bgRegistry.delete(wvid);\n}\n\n/**\n * Ingest a batch of `[wvid, value]` tuples from the MT bridge. Writes each\n * into the corresponding signal so any `effect` that read it re-runs on the\n * sigx scheduler's next tick. Tuples for unregistered wvids (race with\n * unmount) are silently dropped.\n */\nexport function ingestAvPublishes(updates: Array<[number, unknown]>): void {\n for (const [wvid, value] of updates) {\n const s = bgRegistry.get(wvid);\n if (!s) continue;\n (s as unknown as { value: unknown }).value = value;\n }\n}\n\n/** Reset hook — for testing. Drops every registered sink. */\nexport function resetBgAvBridge(): void {\n bgRegistry.clear();\n}\n\n/** Test hook — number of currently registered sinks. */\nexport function bgAvSinkCount(): number {\n return bgRegistry.size;\n}\n","/**\n * BG-side listener for the MT→BG `Lynx.Sigx.PublishEvent` channel.\n *\n * The MT-side hybrid worklet (`lynx-runtime-main/src/hybrid-worklet.ts`) calls\n * `lynx.getJSContext().dispatchEvent({ type: 'Lynx.Sigx.PublishEvent', data })`\n * to fire the BG handler whose sign is captured in the hybrid ctx. We listen\n * for that event here and route through the existing event-registry's\n * `publishEvent` — the same dispatcher Lynx native calls when a normal\n * `bindtap` fires on BG. The user's BG handler runs in the same call-stack\n * shape it always has, so signal updates / `count.value++` etc. work without\n * any awareness that the trigger came from MT.\n *\n * Side-effect import from `index.ts` so the listener is wired before any\n * user code runs.\n *\n * CROSS-THREAD ASYMMETRY (per @lynx-js/react/runtime/lib/worklet/call/runOnBackground.js):\n * - MT → BG dispatch: MT calls `lynx.getJSContext().dispatchEvent(...)`,\n * BG listens via `lynx.getCoreContext().addEventListener(...)`.\n * - BG → MT dispatch: BG calls `lynx.getCoreContext().dispatchEvent(...)`,\n * MT listens via `lynx.getJSContext().addEventListener(...)`.\n * Each side calls a DIFFERENT method to reach the other thread — they're not\n * symmetric. Listening on `lynx.getJSContext()` from BG just listens on BG's\n * own context (no cross-thread events arrive).\n *\n * `lynx` is closure-injected by RuntimeWrapperWebpackPlugin (declared in\n * shims.d.ts). It is NOT available as `globalThis.lynx` — use the free\n * identifier directly.\n */\n\nimport { publishEvent } from './event-registry.js';\nimport { ingestAvPublishes } from './animated-bridge.js';\n\ninterface JSContextLike {\n addEventListener?: (\n type: string,\n listener: (e: { data: string }) => void,\n ) => void;\n}\n\nconst lynxObj: { getCoreContext?: () => JSContextLike } | undefined =\n typeof lynx !== 'undefined'\n ? (lynx as unknown as { getCoreContext?: () => JSContextLike })\n : undefined;\nconst ctx: JSContextLike | undefined = lynxObj?.getCoreContext?.();\n\nif (ctx?.addEventListener) {\n ctx.addEventListener('Lynx.Sigx.PublishEvent', (e: { data: string }) => {\n let payload: { sign?: string; event?: unknown };\n try {\n payload = JSON.parse(e.data) as { sign?: string; event?: unknown };\n } catch {\n return; // malformed bridge message — drop\n }\n if (typeof payload.sign === 'string') {\n publishEvent(payload.sign, payload.event);\n }\n });\n\n // SharedValue bridge: each event payload is an array of\n // `[wvid, value]` tuples coalesced from one MT flush window. See\n // `animated-bridge.ts` and `@sigx/lynx-runtime-main/animated-bridge-mt.ts`.\n ctx.addEventListener('Lynx.Sigx.AvPublish', (e: { data: string }) => {\n let updates: Array<[number, unknown]>;\n try {\n updates = JSON.parse(e.data) as Array<[number, unknown]>;\n } catch {\n return;\n }\n if (Array.isArray(updates)) {\n ingestAvPublishes(updates);\n }\n });\n}\n\nexport {};\n","/**\n * runOnBackground — BG-side wiring for the MT→BG cross-thread call channel.\n *\n * Two responsibilities:\n * 1. `transformToWorklet(fn)` — wraps a BG function as a `JsFnHandle`\n * `{ _jsFnId, _fn }` so the SWC transform can serialise it into the\n * `_jsFn` slot of a worklet ctx. The BG worklet-loader emits inline\n * `transformToWorklet(...)` calls when the user writes `runOnBackground(fn)`\n * inside a `'main thread'` body.\n * 2. `Lynx.Sigx.RunOnBackground` listener — when the MT-side dispatcher\n * fires, finds the matching JsFnHandle by `(execId, fnId)` from the\n * registered worklet ctxs, runs `_fn(...params)`, dispatches\n * `Lynx.Sigx.FunctionCallRet` back with `{resolveId, returnValue}`.\n *\n * Mirrors @lynx-js/react/runtime/lib/worklet/call/runOnBackground +\n * vue-lynx's run-on-background.ts (same protocol shape, sigx-namespaced\n * event types).\n */\n\nconst RUN_ON_BACKGROUND = 'Lynx.Sigx.RunOnBackground';\nconst FUNCTION_CALL_RET = 'Lynx.Sigx.FunctionCallRet';\n\n// ---------------------------------------------------------------------------\n// JsFnHandle shape — serialisable representation of a BG function\n// ---------------------------------------------------------------------------\n\nexport interface JsFnHandle {\n _jsFnId?: number;\n _execId?: number;\n _fn?: (...args: unknown[]) => unknown;\n _isFirstScreen?: boolean;\n _error?: string;\n}\n\ninterface WorkletCtx {\n _wkltId: string;\n _execId?: number;\n _c?: Record<string, unknown>;\n _jsFn?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\n// ---------------------------------------------------------------------------\n// transformToWorklet — mint a JsFnHandle for cross-thread dispatch\n//\n// The SWC JS pass emits inline `transformToWorklet(fn)` calls in the BG bundle\n// when it sees `runOnBackground(fn)` inside a `'main thread'` body. The handle\n// flows into the worklet ctx's `_jsFn` slot; MT extracts it and dispatches via\n// `runOnBackground(handle)(...args)`.\n// ---------------------------------------------------------------------------\n\nlet lastJsFnId = 0;\n\nexport function transformToWorklet(\n fn: (...args: unknown[]) => unknown,\n): JsFnHandle {\n const id = ++lastJsFnId;\n if (typeof fn !== 'function') {\n return {\n _jsFnId: id,\n _error:\n `Argument of runOnBackground should be a function, got [${typeof fn}]`,\n };\n }\n // Stamp toJSON so JSON.stringify of the worklet ctx replaces the function\n // body with a placeholder string — MT only needs `_jsFnId`/`_execId`.\n (fn as unknown as { toJSON?: () => string }).toJSON ??= () =>\n '[BackgroundFunction]';\n return { _jsFnId: id, _fn: fn };\n}\n\n// ---------------------------------------------------------------------------\n// IndexMap — auto-incrementing Map (worklet exec-id allocator)\n// ---------------------------------------------------------------------------\n\nclass IndexMap<T> {\n private lastIndex = 0;\n private map = new Map<number, T>();\n\n add(value: T): number {\n const id = ++this.lastIndex;\n this.map.set(id, value);\n return id;\n }\n\n get(index: number): T | undefined {\n return this.map.get(index);\n }\n\n remove(index: number): void {\n this.map.delete(index);\n }\n}\n\nclass WorkletExecIdMap extends IndexMap<WorkletCtx> {\n override add(worklet: WorkletCtx): number {\n const execId = super.add(worklet);\n worklet._execId = execId;\n return execId;\n }\n\n findJsFnHandle(execId: number, fnId: number): JsFnHandle | undefined {\n const worklet = this.get(execId);\n if (!worklet) return undefined;\n\n const visited = new Set<object>();\n const search = (value: unknown): JsFnHandle | undefined => {\n if (value === null || typeof value !== 'object') return undefined;\n const obj = value as Record<string, unknown>;\n if (visited.has(obj)) return undefined;\n visited.add(obj);\n if ('_jsFnId' in obj && obj['_jsFnId'] === fnId) {\n return obj as JsFnHandle;\n }\n for (const key in obj) {\n const result = search(obj[key]);\n if (result) return result;\n }\n return undefined;\n };\n\n return search(worklet);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Module state — lazy-init so SSR / tests don't pay for the listener wiring\n// ---------------------------------------------------------------------------\n\nlet execIdMap: WorkletExecIdMap | undefined;\n\ninterface CoreContextLike {\n addEventListener?: (type: string, listener: (e: { data?: unknown }) => void) => void;\n dispatchEvent?: (e: { type: string; data: string }) => void;\n}\n\nfunction getCoreContext(): CoreContextLike | undefined {\n if (typeof lynx === 'undefined') return undefined;\n const obj = lynx as unknown as { getCoreContext?: () => CoreContextLike };\n return typeof obj.getCoreContext === 'function' ? obj.getCoreContext() : undefined;\n}\n\nfunction init(): void {\n execIdMap = new WorkletExecIdMap();\n const ctx = getCoreContext();\n ctx?.addEventListener?.(RUN_ON_BACKGROUND, runJSFunction);\n}\n\n// ---------------------------------------------------------------------------\n// registerWorkletCtx — stamp _execId on outgoing worklet ctxs\n//\n// Called from nodeOps.patchProp (SET_WORKLET_EVENT path) and from\n// runOnMainThread before shipping a ctx across threads. Must run BEFORE\n// JSON.stringify so the ctx carries `_execId` to MT.\n// ---------------------------------------------------------------------------\n\nexport function registerWorkletCtx(ctx: WorkletCtx): void {\n if (!execIdMap) init();\n execIdMap!.add(ctx);\n}\n\n// ---------------------------------------------------------------------------\n// runJSFunction — handles MT → BG dispatch\n// ---------------------------------------------------------------------------\n\ninterface RunOnBackgroundData {\n obj: { _jsFnId: number; _execId: number };\n params: unknown[];\n resolveId: number;\n}\n\nfunction runJSFunction(event: { data?: unknown }): void {\n let data: RunOnBackgroundData;\n try {\n data = JSON.parse(event.data as string) as RunOnBackgroundData;\n } catch {\n return; // malformed bridge message — drop\n }\n const handle = execIdMap?.findJsFnHandle(data.obj._execId, data.obj._jsFnId);\n if (!handle?._fn) {\n // Fn is gone — likely the owning worklet ctx was unregistered. Resolve\n // with undefined so the MT promise doesn't hang.\n dispatchReturn(data.resolveId, undefined);\n return;\n }\n let returnValue: unknown;\n try {\n returnValue = handle._fn(...data.params);\n } catch (e) {\n dispatchReturn(data.resolveId, undefined);\n throw e;\n }\n // Promise return values are not transferable across the JSON bridge — caller\n // must await on the BG fn body itself if they need async results.\n dispatchReturn(data.resolveId, returnValue);\n}\n\nfunction dispatchReturn(resolveId: number, returnValue: unknown): void {\n const ctx = getCoreContext();\n ctx?.dispatchEvent?.({\n type: FUNCTION_CALL_RET,\n data: JSON.stringify({ resolveId, returnValue }),\n });\n}\n\n// ---------------------------------------------------------------------------\n// User-facing stub — replaced by SWC at every `runOnBackground(fn)` call site\n// inside a `'main thread'` body. If you reach this code path at runtime, the\n// build transform did not run on this file.\n// ---------------------------------------------------------------------------\n\nexport function runOnBackground<R, Fn extends (...args: never[]) => R>(\n _fn: Fn,\n): (...args: Parameters<Fn>) => Promise<R> {\n throw new Error(\n 'runOnBackground() can only be used inside \\'main thread\\' functions. '\n + 'The SWC worklet transform should replace this call at build time — '\n + 'verify @sigx/lynx-plugin\\'s worklet-loader is wired into your bundler.',\n );\n}\n\n// ---------------------------------------------------------------------------\n// Reset — for testing only\n// ---------------------------------------------------------------------------\n\nexport function resetRunOnBackgroundState(): void {\n execIdMap = undefined;\n lastJsFnId = 0;\n}\n","/**\n * Op queue — accumulates renderer ops on the Background Thread and flushes\n * them to the Main Thread via the Lynx host bridge.\n *\n * The bridge identifiers `lynx` and `lynxCoreInject` are NOT on globalThis.\n * They are injected as closure parameters by RuntimeWrapperWebpackPlugin\n * (peer dep `@lynx-js/runtime-wrapper-webpack-plugin`), which wraps the BG\n * bundle in `__init_card_bundle__(lynxCoreInject, lynx, ...)`. Once wrapped,\n * any module in the bundle can reference them as bare identifiers.\n *\n */\n\nexport { OP } from '@sigx/lynx-runtime-internal';\n\n// ---------------------------------------------------------------------------\n// Ambient declarations for the closure-injected host bridge identifiers\n// ---------------------------------------------------------------------------\n\n// `lynx` and `lynxCoreInject` are declared in src/shims.d.ts as the\n// single source of truth for closure-injected identifiers from\n// runtime-wrapper-webpack-plugin. Both are typed as `any` since their\n// shape varies by host — call sites guard with typeof checks.\n\n// ---------------------------------------------------------------------------\n// Op buffer\n// ---------------------------------------------------------------------------\n\nlet buffer: unknown[] = [];\n\n/**\n * Push one op (opcode + arguments) into the buffer as a flat sequence.\n * Example: pushOp(OP.CREATE, id, type) → buffer gets [0, id, type].\n */\nexport function pushOp(...args: unknown[]): void {\n for (const arg of args) {\n buffer.push(arg);\n }\n}\n\n/** Take all buffered ops and reset the buffer. */\nexport function takeOps(): unknown[] {\n const b = buffer;\n buffer = [];\n return b;\n}\n\n// ---------------------------------------------------------------------------\n// Scheduling\n// ---------------------------------------------------------------------------\n\nlet scheduled = false;\n\n/**\n * Schedule a flush of the ops buffer at the end of the current microtask.\n * Multiple calls within one tick are coalesced into one cross-thread call.\n */\nexport function scheduleFlush(): void {\n if (scheduled) return;\n scheduled = true;\n Promise.resolve().then(doFlush);\n}\n\n/**\n * Immediately flush all buffered ops — used on initial mount so the first\n * frame is committed synchronously.\n */\nexport function flushNow(): void {\n scheduled = false;\n const ops = takeOps();\n if (ops.length === 0) return;\n sendOps(ops);\n}\n\n/** Reset module state — for testing only. */\nexport function resetOpQueue(): void {\n buffer = [];\n scheduled = false;\n pendingAckResolve = null;\n pendingAckPromise = null;\n}\n\nfunction doFlush(): void {\n scheduled = false;\n const ops = takeOps();\n if (ops.length === 0) return;\n sendOps(ops);\n}\n\n// ---------------------------------------------------------------------------\n// Main-thread ack tracking\n//\n// callLepusMethod is asynchronous: by the time the BG flush cycle finishes,\n// the MT has not yet applied the ops. Track a promise that resolves when the\n// MT acks via the callback so callers can `await waitForFlush()` if they need\n// to coordinate with the next-tick UI state.\n// ---------------------------------------------------------------------------\n\nlet pendingAckResolve: (() => void) | null = null;\nlet pendingAckPromise: Promise<void> | null = null;\n\n/**\n * Resolves once the most recent ops batch has been applied on the main\n * thread. If no ops are in flight, resolves immediately.\n */\nexport function waitForFlush(): Promise<void> {\n return pendingAckPromise ?? Promise.resolve();\n}\n\n// ---------------------------------------------------------------------------\n// Transport (BG → MT)\n// ---------------------------------------------------------------------------\n\nfunction sendOps(ops: unknown[]): void {\n const data = JSON.stringify(ops);\n\n // Create the ack promise BEFORE sending so any waitForFlush() chained\n // immediately after this call observes the in-flight batch.\n pendingAckPromise = new Promise<void>((resolve) => {\n pendingAckResolve = resolve;\n });\n\n // Primary path: closure-injected `lynx` from RuntimeWrapperWebpackPlugin.\n if (typeof lynx !== 'undefined') {\n const app = lynx?.getNativeApp?.();\n if (app && typeof app.callLepusMethod === 'function') {\n app.callLepusMethod('sigxPatchUpdate', { data }, () => {\n pendingAckResolve?.();\n pendingAckResolve = null;\n pendingAckPromise = null;\n });\n return;\n }\n }\n\n // Same-thread fallback for unit tests where BG and MT share globalThis.\n const g = globalThis as Record<string, unknown>;\n if (typeof g['sigxPatchUpdate'] === 'function') {\n (g['sigxPatchUpdate'] as Function)({ data });\n pendingAckResolve?.();\n pendingAckResolve = null;\n pendingAckPromise = null;\n return;\n }\n\n // No bridge available — drop and resolve so callers don't hang. This path\n // indicates the bundle wasn't wrapped by RuntimeWrapperWebpackPlugin.\n console.log(\n '[sigx-bg] sendOps: no `lynx` global injected — bundle is missing RuntimeWrapperWebpackPlugin',\n );\n pendingAckResolve?.();\n pendingAckResolve = null;\n pendingAckPromise = null;\n}\n\n// ---------------------------------------------------------------------------\n// Hot reload signal (BG → MT)\n//\n// Sent before a webpack HMR update replaces the BG module, so the MT resets\n// its element registry and page root before the new ops batch arrives.\n// ---------------------------------------------------------------------------\n\n/**\n * Tell the Main Thread to reset its element tree in preparation for a hot\n * reload. The MT handler (`sigxHotReload`) calls `resetMainThreadState()`,\n * re-creates the page root, and flushes — so the next `sigxPatchUpdate`\n * batch builds on a clean tree.\n *\n * This is fire-and-forget: callLepusMethod messages are ordered, so\n * sigxHotReload will be processed before any subsequent sigxPatchUpdate.\n */\nexport function sendHotReloadSignal(): void {\n // Primary path: closure-injected `lynx` from RuntimeWrapperWebpackPlugin.\n if (typeof lynx !== 'undefined') {\n const app = lynx?.getNativeApp?.();\n if (app && typeof app.callLepusMethod === 'function') {\n app.callLepusMethod('sigxHotReload', {}, () => {});\n return;\n }\n }\n\n // Same-thread fallback for testing where BG and MT share globalThis.\n const g = globalThis as Record<string, unknown>;\n if (typeof g['sigxHotReload'] === 'function') {\n (g['sigxHotReload'] as Function)();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Event dispatch (MT → BG)\n//\n// The Lynx host calls `lynxCoreInject.tt.publishEvent(sign, data)` (and\n// `publicComponentEvent(cid, sign, data)`) when an event fires on the\n// Main Thread. We install our dispatcher there once at module load.\n// ---------------------------------------------------------------------------\n\n/**\n * Look up a sign in __SIGX_LYNX_EVENT_REGISTRY__ and invoke the registered\n * handler. The registry is populated by lynx-runtime's patchProp branch\n * whenever the renderer sees a `bindtap` / `onTap` / etc. prop.\n */\nfunction dispatchEvent(sign: unknown, evt: unknown): void {\n try {\n const registry = (globalThis as any).__SIGX_LYNX_EVENT_REGISTRY__;\n if (!registry?.handlers || sign == null) return;\n const handlers = registry.handlers;\n const fn = handlers instanceof Map\n ? handlers.get(String(sign))\n : handlers[String(sign)];\n if (typeof fn === 'function') fn(evt);\n } catch (e) {\n console.log('[sigx-bg] event dispatch threw:', String(e));\n }\n}\n\n/**\n * Install our event dispatcher on `lynxCoreInject.tt` — the official place\n * the Lynx host calls when it forwards Main Thread events to the BG.\n *\n * Idempotent. Called from render.ts on module load and from lynxMount() as\n * a defensive re-install in case the host swaps the tt namespace between\n * card loads.\n */\nexport function installEventPublisher(): void {\n // Primary install path — the canonical Lynx integration point.\n if (typeof lynxCoreInject !== 'undefined' && lynxCoreInject?.tt) {\n lynxCoreInject.tt.publishEvent = dispatchEvent;\n lynxCoreInject.tt.publicComponentEvent = (\n _cid: string,\n sign: string,\n data: unknown,\n ) => dispatchEvent(sign, data);\n }\n\n // Fallback for older Lynx SDKs that look at globalThis.publishEvent.\n const g = globalThis as Record<string, unknown>;\n if (typeof g['publishEvent'] !== 'function') {\n g['publishEvent'] = dispatchEvent as (...args: unknown[]) => void;\n }\n if (typeof g['publicComponentEvent'] !== 'function') {\n g['publicComponentEvent'] = ((_cid: string, sign: string, data: unknown) =>\n dispatchEvent(sign, data)) as (...args: unknown[]) => void;\n }\n}\n","/**\n * ShadowElement: a lightweight doubly-linked tree node that lives entirely in\n * the Background Thread. It lets the renderer call parentNode() / nextSibling()\n * synchronously, while the real Lynx elements exist only on the Main Thread.\n *\n * id=1 is reserved for the page root (created via __CreatePage on Main Thread).\n * Regular elements start from id=2.\n */\n\nexport class ShadowElement {\n static nextId = 2; // 1 is reserved for the page root\n\n id: number;\n type: string;\n parent: ShadowElement | null = null;\n firstChild: ShadowElement | null = null;\n lastChild: ShadowElement | null = null;\n prev: ShadowElement | null = null;\n next: ShadowElement | null = null;\n\n // Cached style object (last value passed to patchProp 'style').\n // Used by vShow to merge display:none without losing the original styles.\n _style: Record<string, unknown> = {};\n // Set to true by vShow when the element should be hidden.\n _vShowHidden = false;\n\n // Class management for Transition support.\n _baseClass = '';\n _transitionClasses: Set<string> = new Set();\n\n constructor(type: string, forceId?: number) {\n this.id = forceId !== undefined ? forceId : ShadowElement.nextId++;\n this.type = type;\n }\n\n insertBefore(child: ShadowElement, anchor: ShadowElement | null): void {\n // Detach from current parent first\n if (child.parent) {\n child.parent.removeChild(child);\n }\n child.parent = this;\n\n if (anchor) {\n // Insert before anchor\n const prev = anchor.prev;\n child.next = anchor;\n child.prev = prev;\n anchor.prev = child;\n if (prev) {\n prev.next = child;\n } else {\n this.firstChild = child;\n }\n } else {\n // Append at end\n if (this.lastChild) {\n this.lastChild.next = child;\n child.prev = this.lastChild;\n } else {\n this.firstChild = child;\n child.prev = null;\n }\n this.lastChild = child;\n child.next = null;\n }\n }\n\n removeChild(child: ShadowElement): void {\n const prev = child.prev;\n const next = child.next;\n if (prev) {\n prev.next = next;\n } else {\n this.firstChild = next;\n }\n if (next) {\n next.prev = prev;\n } else {\n this.lastChild = prev;\n }\n child.parent = null;\n child.prev = null;\n child.next = null;\n }\n}\n\nexport const PAGE_ROOT_ID = 1;\n\n/** Create the page root shadow element with the reserved id=1. */\nexport function createPageRoot(): ShadowElement {\n return new ShadowElement('page', PAGE_ROOT_ID);\n}\n\n/** Reset the ID counter — for testing only. */\nexport function resetShadowState(): void {\n ShadowElement.nextId = 2;\n}\n","/**\n * MainThreadRef — a ref whose `.current` value lives on the Main Thread.\n *\n * On the Background Thread, `.current` is always the initial value (typically\n * `null`). On the Main Thread, `.current` is set to the real Lynx element\n * handle when the ref is bound via `main-thread:ref={ref}`.\n *\n * This is the sigx equivalent of react-lynx's `useMainThreadRef()` and\n * vue-lynx's `useMainThreadRef()`. It enables zero-latency style updates\n * and animations by giving main-thread event handlers synchronous access\n * to native elements.\n *\n * Architecture:\n * BG: useMainThreadRef(init) → MainThreadRef { wvid, current: init }\n * → pushOp(INIT_MT_REF, wvid, init)\n * BG: patchProp('main-thread:ref', ref) → pushOp(SET_MT_REF, elId, wvid)\n * MT: INIT_MT_REF → workletRefs.set(wvid, { current: init })\n * MT: SET_MT_REF → workletRefs.get(wvid).current = elements.get(elId)\n */\n\nimport { onUnmounted } from '@sigx/runtime-core';\nimport { OP, pushOp, scheduleFlush } from './op-queue.js';\n\n// ---------------------------------------------------------------------------\n// Worklet variable ID generator\n// ---------------------------------------------------------------------------\n\nlet nextWvid = 1;\n\nexport function resetWvidCounter(): void {\n nextWvid = 1;\n}\n\n// ---------------------------------------------------------------------------\n// MainThreadRef class\n// ---------------------------------------------------------------------------\n\n/**\n * A ref whose `.current` property is managed on the Main Thread.\n *\n * On the BG thread, `.current` returns the `initValue` and is read-only\n * (setting it has no effect — the real value lives on MT).\n *\n * In main-thread event handlers and `runOnMainThread` callbacks, `.current`\n * is the real Lynx element handle with methods like `setStyleProperties()`,\n * `getComputedStyleProperty()`, and `animate()`.\n */\nexport class MainThreadRef<T = unknown> {\n /**\n * Worklet variable ID — uniquely identifies this ref across threads.\n * Underscored to match the field name `transformWorklet` walks for\n * in @lynx-js/react/worklet-runtime when expanding `_c` captures.\n */\n readonly _wvid: number;\n\n /**\n * Initial value snapshot — sent to MT in INIT_MT_REF and used by\n * the worklet-runtime to seed the firstScreen ref map.\n */\n readonly _initValue: T;\n\n /**\n * On BG: the init value (read-only snapshot).\n * On MT: the real element handle (set by SET_MT_REF op).\n */\n current: T;\n\n constructor(initValue: T) {\n this._wvid = nextWvid++;\n this._initValue = initValue;\n this.current = initValue;\n }\n}\n\n/**\n * Walk a captured `_c` map and serialize MainThreadRef instances to plain\n * `{ _wvid, _initValue }` objects so they survive the JSON round-trip across\n * the BG→MT bridge. Upstream's worklet-runtime walks `_c` looking for `_wvid`\n * to recognize ref captures and resolve them via\n * `lynxWorkletImpl._refImpl._workletRefMap`.\n *\n * Used by both the SET_WORKLET_EVENT path (`nodeOps.patchProp`) and the\n * SET_GESTURE_DETECTOR path (`native/gesture-detector.ts`).\n */\nexport function sanitizeCaptured(\n captured: Record<string, unknown>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const k in captured) {\n const v = captured[k];\n if (v instanceof MainThreadRef) {\n out[k] = { _wvid: v._wvid, _initValue: v._initValue };\n } else {\n out[k] = v;\n }\n }\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// useMainThreadRef composable\n// ---------------------------------------------------------------------------\n\n/**\n * Create a ref that provides synchronous access to a native element on the\n * Main Thread. Bind it to an element via `main-thread:ref={ref}`.\n *\n * @example\n * ```tsx\n * const elRef = useMainThreadRef<MainThread.Element>(null);\n *\n * function handleScroll(e: ScrollEvent) {\n * 'main thread';\n * const offset = e.detail.scrollTop;\n * elRef.current?.setStyleProperties({\n * transform: `translateY(${-offset}px)`,\n * });\n * }\n *\n * return (\n * <scroll-view\n * main-thread-bindscroll={handleScroll}\n * >\n * <view main-thread:ref={elRef}>\n * <text>Sticky header</text>\n * </view>\n * </scroll-view>\n * );\n * ```\n */\nexport function useMainThreadRef<T = unknown>(\n initValue: T,\n): MainThreadRef<T> {\n const ref = new MainThreadRef<T>(initValue);\n // Tell the MT to create a worklet ref holder with this ID and initial value.\n pushOp(OP.INIT_MT_REF, ref._wvid, initValue);\n scheduleFlush();\n // Release the holder when the owning component unmounts. Without this, the\n // MT-side `_workletRefMap` grows monotonically across mount/unmount cycles\n // (router-driven apps with frequent navigation hit this fastest).\n // `onUnmounted` no-ops if called outside a component setup; callers that\n // construct refs ad-hoc (e.g. tests) just won't get a release op, which is\n // the same as today's behaviour.\n onUnmounted(() => {\n pushOp(OP.RELEASE_MT_REF, ref._wvid);\n scheduleFlush();\n });\n return ref;\n}\n","/**\n * Lynx renderer node operations (Background Thread).\n *\n * Builds a ShadowElement tree and pushes ops into the op queue. NO Lynx PAPI\n * globals (__CreateElement, __AppendElement, etc.) are referenced here — those\n * only exist on the Main Thread. The op queue is flushed to MT via\n * sigxPatchUpdate, where ops-apply.ts dispatches them to real PAPI calls.\n */\nimport type { RendererOptions } from '@sigx/runtime-core/internals';\nimport { OP, pushOp, scheduleFlush } from './op-queue.js';\nimport { register, unregister } from './event-registry.js';\nimport { ShadowElement } from './shadow-element.js';\nimport { registerWorkletCtx } from './run-on-background.js';\n\n// ---------------------------------------------------------------------------\n// Re-export ShadowElement as the HostNode / HostElement type\n// ---------------------------------------------------------------------------\n\nexport type LynxNode = ShadowElement;\nexport type LynxElement = ShadowElement;\n\n// ---------------------------------------------------------------------------\n// Event prop classification\n// ---------------------------------------------------------------------------\n\ninterface EventSpec {\n type: string;\n name: string;\n /** When true, this event runs on the Main Thread (zero-latency). */\n mainThread?: boolean;\n}\n\nfunction parseEventProp(key: string): EventSpec | null {\n // Main-thread event prefixes: main-thread-bind*, main-thread-catch*\n if (key.startsWith('main-thread-bind')) {\n return { type: 'bindEvent', name: key.slice('main-thread-bind'.length), mainThread: true };\n }\n if (key.startsWith('main-thread-catch')) {\n return { type: 'catchEvent', name: key.slice('main-thread-catch'.length), mainThread: true };\n }\n // Alternative syntax: main-thread:bind*, main-thread:catch*\n if (key.startsWith('main-thread:bind')) {\n return { type: 'bindEvent', name: key.slice('main-thread:bind'.length), mainThread: true };\n }\n if (key.startsWith('main-thread:catch')) {\n return { type: 'catchEvent', name: key.slice('main-thread:catch'.length), mainThread: true };\n }\n if (key.startsWith('global-bind')) {\n return { type: 'bindGlobalEvent', name: key.slice('global-bind'.length) };\n }\n if (key.startsWith('global-catch')) {\n return { type: 'catchGlobalEvent', name: key.slice('global-catch'.length) };\n }\n if (key.startsWith('catch')) {\n return { type: 'catchEvent', name: key.slice('catch'.length) };\n }\n if (/^bind(?!ingx)/.test(key)) {\n return { type: 'bindEvent', name: key.slice('bind'.length) };\n }\n if (/^on[A-Z]/.test(key)) {\n // onTap → { type: 'bindEvent', name: 'tap' }\n const name = key.slice(2, 3).toLowerCase() + key.slice(3);\n return { type: 'bindEvent', name };\n }\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Worklet placeholder detection — @lynx-js/react/transform replaces\n// 'main thread' functions with { _wkltId, _c? } placeholders in the BG bundle.\n// ---------------------------------------------------------------------------\n\nimport { MainThreadRef, sanitizeCaptured } from './main-thread-ref.js';\n\ninterface WorkletPlaceholder {\n _wkltId: string;\n _c?: Record<string, unknown>;\n _jsFn?: Record<string, unknown>;\n}\n\nfunction isWorkletPlaceholder(v: unknown): v is WorkletPlaceholder {\n return typeof v === 'object' && v !== null && '_wkltId' in (v as object);\n}\n\n// Track sent worklet ids per (elementId, propKey) to skip redundant ops on re-render.\nconst sentWorklets = new Map<number, Map<string, string>>();\n\n// Track the sign registered for each (element, propKey) so we can unregister\n// on prop removal / update.\nconst elementEventSigns = new Map<number, Map<string, string>>();\n\n// Track which sign owns the native event slot per (elementId, 'eventType:eventName').\n// Lynx only supports one __AddEvent per (element, eventType, eventName). When multiple\n// props resolve to the same native event (e.g. main-thread-bindtap + bindtap both map\n// to bindEvent:tap), we keep one sign in __AddEvent and dispatch to all handlers from\n// a multi-handler wrapper in the BG registry.\nconst nativeEventSlots = new Map<number, Map<string, { sign: string; handlers: Map<string, (data: unknown) => void> }>>();\n\n// ---------------------------------------------------------------------------\n// Style normalisation — numeric values → 'Npx' (Lynx requires units)\n// ---------------------------------------------------------------------------\n\nconst DIMENSIONLESS = new Set([\n 'flex',\n 'flexGrow',\n 'flexShrink',\n 'flexOrder',\n 'order',\n 'opacity',\n 'zIndex',\n 'aspectRatio',\n 'fontWeight',\n 'lineClamp',\n]);\n\nfunction normalizeStyle(\n style: Record<string, unknown>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(style)) {\n const val = style[key];\n if (typeof val === 'number' && !DIMENSIONLESS.has(key) && val !== 0) {\n out[key] = `${val}px`;\n } else {\n out[key] = val;\n }\n }\n return out;\n}\n\nfunction shallowEqual(\n a: Record<string, unknown>,\n b: Record<string, unknown>,\n): boolean {\n const ka = Object.keys(a);\n const kb = Object.keys(b);\n if (ka.length !== kb.length) return false;\n for (const k of ka) {\n if (a[k] !== b[k]) return false;\n }\n return true;\n}\n\n// ---------------------------------------------------------------------------\n// Class resolution — merges user :class with transition classes\n// ---------------------------------------------------------------------------\n\nexport function resolveClass(el: ShadowElement): string {\n if (el._transitionClasses.size === 0) return el._baseClass;\n const parts: string[] = [];\n if (el._baseClass) parts.push(el._baseClass);\n for (const cls of el._transitionClasses) parts.push(cls);\n return parts.join(' ');\n}\n\n// ---------------------------------------------------------------------------\n// RendererOptions implementation\n// ---------------------------------------------------------------------------\n\nexport const nodeOps: RendererOptions<ShadowElement, ShadowElement> = {\n createElement(type: string): ShadowElement {\n const el = new ShadowElement(type);\n pushOp(OP.CREATE, el.id, type);\n scheduleFlush();\n return el;\n },\n\n createText(text: string): ShadowElement {\n const el = new ShadowElement('#text');\n pushOp(OP.CREATE_TEXT, el.id);\n if (text) pushOp(OP.SET_TEXT, el.id, text);\n scheduleFlush();\n return el;\n },\n\n // Comment nodes are used as position anchors for conditionals / Fragment.\n // Materialised as invisible placeholder elements on the Main Thread.\n createComment(_text: string): ShadowElement {\n const el = new ShadowElement('#comment');\n pushOp(OP.CREATE, el.id, '__comment');\n scheduleFlush();\n return el;\n },\n\n setText(node: ShadowElement, text: string): void {\n pushOp(OP.SET_TEXT, node.id, text);\n scheduleFlush();\n },\n\n setElementText(el: ShadowElement, text: string): void {\n // Remove all children from shadow tree\n while (el.firstChild) {\n const child = el.firstChild;\n el.removeChild(child);\n pushOp(OP.REMOVE, el.id, child.id);\n }\n // Set text content directly on the element\n pushOp(OP.SET_TEXT, el.id, text);\n scheduleFlush();\n },\n\n insert(\n child: ShadowElement,\n parent: ShadowElement,\n anchor?: ShadowElement | null,\n ): void {\n // Always update the shadow tree (the core renderer needs sync tree queries).\n parent.insertBefore(child, anchor ?? null);\n\n // Lynx's native <list> only accepts <list-item> children.\n // Skip comment/text anchors to avoid NSInvalidArgumentException.\n if (\n parent.type === 'list'\n && (child.type === '#comment' || child.type === '#text')\n ) {\n return;\n }\n\n // If the anchor is a comment node inside a <list>, walk forward to find\n // the next real sibling so the MT __InsertElementBefore has a valid ref.\n let resolvedAnchor: ShadowElement | null = anchor ?? null;\n if (parent.type === 'list') {\n while (\n resolvedAnchor\n && (resolvedAnchor.type === '#comment'\n || resolvedAnchor.type === '#text')\n ) {\n resolvedAnchor = resolvedAnchor.next;\n }\n }\n\n const anchorId = resolvedAnchor ? resolvedAnchor.id : -1;\n pushOp(OP.INSERT, parent.id, child.id, anchorId);\n scheduleFlush();\n },\n\n remove(child: ShadowElement): void {\n if (child.parent) {\n const parentId = child.parent.id;\n child.parent.removeChild(child);\n pushOp(OP.REMOVE, parentId, child.id);\n scheduleFlush();\n }\n },\n\n patchProp(\n el: ShadowElement,\n key: string,\n _prevValue: unknown,\n nextValue: unknown,\n ): void {\n // Handle main-thread:ref — bind a MainThreadRef to this element\n if (key === 'main-thread:ref') {\n if (nextValue != null) {\n const mtRef = nextValue as MainThreadRef;\n pushOp(OP.SET_MT_REF, el.id, mtRef._wvid);\n }\n scheduleFlush();\n return;\n }\n\n const event = parseEventProp(key);\n\n if (event) {\n // Worklet placeholders ({ _wkltId, _c? }) emitted by @lynx-js/react/transform\n // bypass the BG event-registry path entirely — the MT side dispatches.\n if (event.mainThread && nextValue != null && isWorkletPlaceholder(nextValue)) {\n let elWorklets = sentWorklets.get(el.id);\n const prevId = elWorklets?.get(key);\n if (prevId !== nextValue._wkltId) {\n if (!elWorklets) {\n elWorklets = new Map();\n sentWorklets.set(el.id, elWorklets);\n }\n elWorklets.set(key, nextValue._wkltId);\n const wireCtx: {\n _wkltId: string;\n _c?: Record<string, unknown>;\n _jsFn?: Record<string, unknown>;\n _execId?: number;\n } = {\n _wkltId: nextValue._wkltId,\n };\n if (nextValue._c) wireCtx._c = sanitizeCaptured(nextValue._c);\n if (nextValue._jsFn) wireCtx._jsFn = nextValue._jsFn;\n // Stamp _execId so MT can route runOnBackground dispatches back\n // to the JsFnHandles inside _jsFn / _c.\n registerWorkletCtx(wireCtx);\n pushOp(OP.SET_WORKLET_EVENT, el.id, event.type, event.name, wireCtx);\n scheduleFlush();\n }\n return;\n }\n\n let propSigns = elementEventSigns.get(el.id);\n const nativeKey = `${event.type}:${event.name}`;\n\n if (nextValue != null) {\n const handler = nextValue as (data: unknown) => void;\n\n // Get or create the native event slot for this (element, eventType, eventName).\n let elSlots = nativeEventSlots.get(el.id);\n if (!elSlots) {\n elSlots = new Map();\n nativeEventSlots.set(el.id, elSlots);\n }\n\n let slot = elSlots.get(nativeKey);\n if (!slot) {\n // First handler for this native event — register with Lynx.\n const sign = register((data: unknown) => {\n // Dispatch to all handlers registered for this slot.\n const s = elSlots!.get(nativeKey);\n if (s) {\n for (const h of s.handlers.values()) h(data);\n }\n });\n slot = { sign, handlers: new Map() };\n elSlots.set(nativeKey, slot);\n pushOp(OP.SET_EVENT, el.id, event.type, event.name, sign);\n }\n\n // Add/update this prop's handler in the slot.\n slot.handlers.set(key, handler);\n\n // Track prop→sign for re-render updates.\n if (!propSigns) {\n propSigns = new Map<string, string>();\n elementEventSigns.set(el.id, propSigns);\n }\n propSigns.set(key, slot.sign);\n } else {\n // Handler removed.\n const elSlots = nativeEventSlots.get(el.id);\n const slot = elSlots?.get(nativeKey);\n if (slot) {\n slot.handlers.delete(key);\n if (slot.handlers.size === 0) {\n // No more handlers — unregister from Lynx.\n unregister(slot.sign);\n elSlots!.delete(nativeKey);\n pushOp(OP.REMOVE_EVENT, el.id, event.type, event.name);\n }\n }\n propSigns?.delete(key);\n }\n } else if (key === 'style') {\n const style = nextValue != null && typeof nextValue === 'object'\n ? normalizeStyle(nextValue as Record<string, unknown>)\n : {};\n const effective = el._vShowHidden ? { ...style, display: 'none' } : style;\n // Skip SET_STYLE when structurally unchanged. JSX inline `style={{...}}`\n // creates a fresh object every render but its keys/values typically don't\n // change between renders. Re-emitting SET_STYLE would overwrite any MT\n // worklet's setStyleProperties calls (pink/scale on tap, etc.) on every\n // unrelated signal change. Shallow-equal previous _style suffices since\n // sigx normalises everything to a flat string→string|number map.\n const prev = el._style;\n const sameStyle = prev != null && shallowEqual(prev, effective);\n el._style = style;\n if (!sameStyle) {\n pushOp(OP.SET_STYLE, el.id, effective);\n }\n } else if (key === 'class') {\n el._baseClass = (nextValue as string) ?? '';\n const finalClass = resolveClass(el);\n pushOp(OP.SET_CLASS, el.id, finalClass);\n } else if (key === 'id') {\n pushOp(OP.SET_ID, el.id, nextValue);\n } else {\n pushOp(OP.SET_PROP, el.id, key, nextValue);\n }\n scheduleFlush();\n },\n\n parentNode(node: ShadowElement): ShadowElement | null {\n return node.parent;\n },\n\n nextSibling(node: ShadowElement): ShadowElement | null {\n return node.next;\n },\n\n cloneNode(node: ShadowElement): ShadowElement {\n // Lynx has no native clone — create a new shadow element of the same type\n const el = new ShadowElement(node.type);\n if (node.type === '#text') {\n pushOp(OP.CREATE_TEXT, el.id);\n } else {\n pushOp(OP.CREATE, el.id, node.type);\n }\n scheduleFlush();\n return el;\n },\n};\n","/**\n * Lynx renderer entry -- creates the renderer, defines lynxMount, and\n * registers it as the default mount via setDefaultMount.\n *\n * Ported pattern from packages/runtime-terminal/src/index.ts\n */\nimport { createRenderer, setDefaultMount } from '@sigx/runtime-core/internals';\nimport type { MountFn, AppContext } from '@sigx/runtime-core';\nimport { nodeOps } from './nodeOps.js';\nimport { flushNow } from './flush.js';\nimport { installEventPublisher } from './op-queue.js';\nimport { createPageRoot, type ShadowElement } from './shadow-element.js';\n\n// Install host-required event stubs (publishEvent / publicComponentEvent)\n// before sigx mounts anything so the first MT → BG dispatch doesn't crash.\ninstallEventPublisher();\n\n// ---------------------------------------------------------------------------\n// Renderer\n// ---------------------------------------------------------------------------\n\nconst renderer = createRenderer<ShadowElement, ShadowElement>(nodeOps);\nexport const { render } = renderer;\n\n// ---------------------------------------------------------------------------\n// lynxMount -- MountFn for Lynx environments\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// HMR mount state — tracked so hot reloads can tear down and re-mount\n// ---------------------------------------------------------------------------\n\nlet _hmrMounted = false;\nlet _hmrRoot: ShadowElement | null = null;\nlet _hmrAppContext: AppContext | undefined = undefined;\n\n/**\n * Mount function for Lynx environments.\n *\n * The page root is a ShadowElement with id=1 — the Main Thread creates the\n * real page element in renderPage() before the BG thread runs. All subsequent\n * ops reference this root by id so the MT can resolve it.\n *\n * On subsequent calls (hot reload), the previous tree is torn down and the\n * Main Thread is signalled to reset before the new tree is mounted.\n *\n * @example\n * ```tsx\n * import '@sigx/lynx-runtime'; // side-effect: registers lynxMount\n * import { defineApp } from '@sigx/sigx';\n *\n * defineApp(<App />).mount();\n * ```\n */\nexport const lynxMount: MountFn = (element, _container, appContext) => {\n // Re-install in case the per-card app instance only became available\n // after this module's top-level executed (timing varies by Lynx host).\n installEventPublisher();\n\n // Hot-reload fallback: if lynxMount is called again (e.g., main.tsx\n // re-executed because a non-component module change bubbled up), the\n // Lynx native engine cannot handle structural tree mutations. Fall\n // back to a full card reload. With component-level HMR active, this\n // path is only reached for non-component changes.\n // See docs/hmr-investigation.md for details.\n if (_hmrMounted && _hmrRoot) {\n console.log('[sigx-hmr] Non-component change — triggering full card reload');\n triggerLiveReload();\n return () => {};\n }\n\n _hmrMounted = true;\n const root = createPageRoot();\n _hmrRoot = root;\n _hmrAppContext = appContext;\n render(element, root, appContext);\n flushNow();\n return () => {\n render(null, root, appContext);\n flushNow();\n _hmrMounted = false;\n _hmrRoot = null;\n };\n};\n\n// ---------------------------------------------------------------------------\n// Register as the default mount -- activated only when this module is imported\n// ---------------------------------------------------------------------------\n\nsetDefaultMount(lynxMount);\n\n// ---------------------------------------------------------------------------\n// Live-reload fallback\n//\n// When a webpack HMR update cannot be applied (module shape changed too\n// drastically), fall back to reloading the entire card bundle — the Lynx\n// equivalent of a browser full-page refresh.\n// ---------------------------------------------------------------------------\n\n/**\n * Trigger a full card reload via the Lynx host. Tries several host APIs\n * in order of preference:\n * 1. `lynxCoreInject.tt.reloadCard()` — standard Lynx host reload\n * 2. `lynx.getNativeApp().callLepusMethod('sigxReloadCard', ...)` — custom\n * reload signal the host can implement\n * If none are available, logs a warning — the developer must manually reload.\n */\nfunction triggerLiveReload(): void {\n try {\n // Option 1: lynxCoreInject.tt.reloadCard (available in some Lynx hosts)\n if (typeof lynxCoreInject !== 'undefined' && lynxCoreInject?.tt) {\n const reloadCard = lynxCoreInject.tt['reloadCard'];\n if (typeof reloadCard === 'function') {\n (reloadCard as () => void)();\n return;\n }\n }\n\n // Option 2: signal via callLepusMethod so the MT can trigger a reload\n if (typeof lynx !== 'undefined') {\n const app = lynx?.getNativeApp?.();\n if (app && typeof app.callLepusMethod === 'function') {\n app.callLepusMethod('sigxReloadCard', {}, () => {});\n return;\n }\n }\n\n console.log(\n '[sigx-hmr] No reload API available. Please manually reload the card.',\n );\n } catch (e) {\n console.log('[sigx-hmr] triggerLiveReload error:', String(e));\n }\n}\n\n// ---------------------------------------------------------------------------\n// Webpack HMR acceptance\n//\n// Accept hot updates to this module. With component-level HMR, most changes\n// are handled by the HMR loader (self-accepting component modules). This\n// catch-all only fires for changes to render.ts itself or its direct deps.\n// ---------------------------------------------------------------------------\n\n// `lynx` and `lynxCoreInject` are declared in src/shims.d.ts.\n\ndeclare var module: { hot?: {\n accept(cb?: (err?: Error) => void): void;\n} } | undefined;\n\nif (typeof module !== 'undefined' && module?.hot) {\n module.hot.accept((err?: Error) => {\n if (err) {\n console.log(\n '[sigx-hmr] Hot update failed, falling back to live reload:',\n String(err),\n );\n triggerLiveReload();\n }\n });\n}\n","import { onUnmounted } from '@sigx/runtime-core';\nimport type { PrimitiveSignal } from '@sigx/reactivity';\n\nimport { MainThreadRef } from '../main-thread-ref.js';\nimport { pushOp, scheduleFlush } from '../op-queue.js';\nimport { registerBgSink, unregisterBgSink } from '../animated-bridge.js';\nimport { OP } from '@sigx/lynx-runtime-internal';\n\n/**\n * Internal envelope shape stored under `MainThreadRef.current`. Wrapping the\n * value in `{ value: T }` (rather than a bare `T`) lets MT worklets mutate\n * the value without breaking ref identity, and leaves room to extend with\n * derived state later (e.g. velocity) without changing the public type.\n */\nexport interface SharedValueState<T> {\n value: T;\n}\n\n/**\n * MT-writeable, BG-readable cross-thread value with sigx reactive tracking.\n *\n * **Not animation-specific.** Animation is one customer (see `@sigx/motion`),\n * gestures is another (`<Draggable translateX={sv} />`), scroll is another\n * (`useScrollViewOffset`). Anywhere fast-or-frequent state lives natively on\n * MT and you want BG to observe it reactively, this is the primitive.\n *\n * **MT side** — inside `'main thread'` worklets, mutate via `sv.current.value\n * = newValue`. The MainThreadRef machinery makes the mutation stick across\n * worklet invocations on the same wvid; the bridge picks up the new value on\n * the next `__FlushElementTree` boundary.\n *\n * **BG side** — read via `sv.value` to get the latest published snapshot.\n * Sigx tracking applies, so an `effect(() => console.log(sv.value))` re-runs\n * whenever an MT publish ingests a new value. Writes from BG are read-only;\n * dev-mode warns to point users back at the MT path.\n *\n * The class extends `MainThreadRef<{value:T}>` so that BG-side serialization\n * (`nodeOps.ts:sanitizeCaptured`) recognizes it as a worklet ref and ships\n * `{_wvid, _initValue}` over the wire to the MT bundle.\n *\n * @example\n * ```tsx\n * const tx = useSharedValue(0);\n *\n * <Draggable translateX={tx} />\n * <text>x = {tx.value}</text> // BG-reactive, updates per drag frame\n * ```\n */\nexport class SharedValue<T = number> extends MainThreadRef<SharedValueState<T>> {\n /** @internal — populated by `useSharedValue`. Reads here back the BG signal. */\n private _bgSignal!: PrimitiveSignal<T>;\n\n constructor(initial: T) {\n super({ value: initial });\n }\n\n /** @internal */\n _bind(sig: PrimitiveSignal<T>): void {\n this._bgSignal = sig;\n }\n\n /**\n * BG-side reactive read. Returns the latest snapshot published by the MT\n * bridge (or the initial value before any publish has occurred).\n */\n get value(): T {\n return this._bgSignal.value as T;\n }\n\n set value(_v: T) {\n // Reach for process via globalThis so the package doesn't pull in\n // @types/node just for a dev-mode warning. NODE_ENV defaults to a\n // non-production string, so the warning fires unless the bundler has\n // explicitly substituted it to 'production'.\n const env = (globalThis as { process?: { env?: Record<string, string | undefined> } })\n .process?.env?.['NODE_ENV'];\n if (env !== 'production') {\n console.warn(\n '[sigx] SharedValue.value is read-only on the BG thread. ' +\n 'Mutate via the MT worklet (sv.current.value = v).',\n );\n }\n }\n}\n\n/**\n * Allocate a `SharedValue<T>` whose MT-side mutations are observable on the\n * BG thread via sigx reactivity. Bind it to a component prop or read its\n * `.value` from a BG `effect`.\n *\n * Lifecycle:\n * - On allocate: pushes `INIT_MT_REF` (creates the MT-side refMap holder)\n * and `REGISTER_AV_BRIDGE` (tells MT to track this wvid in its diff/\n * publish pass). Allocates a BG-side signal mirror keyed by wvid.\n * - On the owning component's unmount: pushes `UNREGISTER_AV_BRIDGE` and\n * `RELEASE_MT_REF`, drops the BG signal.\n */\nexport function useSharedValue<T = number>(initial: T): SharedValue<T> {\n const sv = new SharedValue<T>(initial);\n\n // BG: register the signal mirror under the wvid allocated in super().\n sv._bind(registerBgSink(sv._wvid, initial));\n\n // MT: create the refMap entry (envelope shape) and begin tracking this\n // wvid in the diff/publish pass.\n pushOp(OP.INIT_MT_REF, sv._wvid, sv._initValue);\n pushOp(OP.REGISTER_AV_BRIDGE, sv._wvid, initial);\n scheduleFlush();\n\n onUnmounted(() => {\n pushOp(OP.UNREGISTER_AV_BRIDGE, sv._wvid);\n pushOp(OP.RELEASE_MT_REF, sv._wvid);\n scheduleFlush();\n unregisterBgSink(sv._wvid);\n });\n\n return sv;\n}\n","import { onUnmounted } from '@sigx/runtime-core';\n\nimport { pushOp, scheduleFlush } from '../op-queue.js';\nimport type { MainThreadRef } from '../main-thread-ref.js';\nimport type { MainThread } from '../jsx.js';\nimport { OP } from '@sigx/lynx-runtime-internal';\nimport type { BuiltinMapperName, MapperParams } from '@sigx/lynx-runtime-internal';\n\nimport type { SharedValue } from './shared-value.js';\n\n// Re-export the public types so users can write\n// import type { BuiltinMapperName } from '@sigx/lynx'\n// without reaching into lynx-runtime-internal directly.\nexport type { BuiltinMapperName, MapperParams };\n\nlet nextBindingId = 1;\n\n/** Reset hook for tests. */\nexport function resetAnimatedStyleBindingIds(): void {\n nextBindingId = 1;\n}\n\n/**\n * Returns the next binding id without incrementing — internal helper used by\n * the `useAnimatedStyle` overloads to share allocation. Not exported.\n */\nfunction allocBindingId(): number {\n return nextBindingId++;\n}\n\n/**\n * Bind a `MainThreadRef`'s element style to a `SharedValue` via a named\n * mapper. The MT-side runtime applies the mapper's output via\n * `setStyleProperties` on every flush boundary where the SharedValue's value changed.\n *\n * The mapper runs on the **Main Thread** with no thread crossing per frame —\n * the only inputs are the SharedValue's value (already on MT) and the `params` shipped\n * once in the registration op. Because mappers are looked up by *name*, the\n * SWC worklet transform doesn't have to capture a function reference (which\n * it can't), so this fits the existing worklet pipeline cleanly.\n *\n * Multiple bindings on the same element compose into a single\n * `setStyleProperties` call per flush. `transform` outputs concatenate in\n * registration order (e.g. `translateX(50px) translateY(20px)`); other style\n * keys merge by last-write-wins. When ANY binding on an element is dirty,\n * ALL of that element's bindings re-run so partial outputs don't drop the\n * unchanged-axis contribution.\n *\n * @example Ghost follower at 0.5×\n * ```tsx\n * const tx = useSharedValue(0);\n * const ghostRef = useMainThreadRef<MainThread.Element | null>(null);\n *\n * useAnimatedStyle(ghostRef, tx, 'translateX', { factor: 0.5 });\n *\n * <Draggable translateX={tx} />\n * <view main-thread:ref={ghostRef} style={...} />\n * ```\n */\nexport function useAnimatedStyle<N extends BuiltinMapperName>(\n elRef: MainThreadRef<MainThread.Element | null>,\n sv: SharedValue<unknown>,\n mapperName: N,\n params?: MapperParams[N],\n): void {\n const bindingId = allocBindingId();\n\n pushOp(\n OP.REGISTER_AV_STYLE_BINDING,\n bindingId,\n elRef._wvid,\n sv._wvid,\n mapperName,\n params ?? null,\n );\n scheduleFlush();\n\n onUnmounted(() => {\n pushOp(OP.UNREGISTER_AV_STYLE_BINDING, bindingId);\n scheduleFlush();\n });\n}\n","/**\n * Cross-thread function invocation primitives for Lynx MTS.\n *\n * - runOnMainThread(fn): BG → MT — invoke a function on the Main Thread\n * - runOnBackground(fn): MT → BG — invoke a function on the Background Thread\n *\n * ## Worklet placeholder semantics (Phase 1b)\n *\n * The `'main thread'` directive inside the function passed to runOnMainThread\n * tells the build pipeline (`@lynx-js/react/transform`) to:\n * 1. Replace the function with a `{ _wkltId }` placeholder in the BG bundle.\n * 2. Emit a `registerWorkletInternal(\"main-thread\", \"<wkltId>\", function(...) { ... })`\n * call into the MT bundle that registers the function in the MT-side worklet\n * registry.\n *\n * At runtime, runOnMainThread receives the placeholder, returns a callable, and\n * each invocation ships `{ wkltId, args }` over `callLepusMethod('sigxRunOnMT')`\n * to the MT bridge installed in `entry-main.ts`.\n *\n * ## Usage\n *\n * ```tsx\n * import { runOnMainThread, runOnBackground } from '@sigx/lynx-runtime';\n *\n * const updateOffset = runOnMainThread((offset: number) => {\n * 'main thread';\n * ref.current?.setStyleProperties({ transform: `translateX(${offset}px)` });\n * });\n * updateOffset(100);\n * ```\n *\n * The `(offset) => { 'main thread'; ... }` source is what the user writes; after\n * the build transform it becomes `{ _wkltId: '...' }` at this call site.\n */\n\n// `lynx` and `lynxCoreInject` are closure-injected — see shims.d.ts\n\nimport { MainThreadRef } from './main-thread-ref.js';\n\n// ---------------------------------------------------------------------------\n// Worklet placeholder shape (post-transform)\n// ---------------------------------------------------------------------------\n\ninterface WorkletPlaceholder {\n _wkltId: string;\n _c?: Record<string, unknown>;\n}\n\nfunction isWorkletPlaceholder(v: unknown): v is WorkletPlaceholder {\n return typeof v === 'object' && v !== null && '_wkltId' in (v as object);\n}\n\n/**\n * Convert MainThreadRef instances inside `_c` to plain `{ _wvid, _initValue }`\n * placeholders so they survive JSON serialization to MT. Upstream's worklet\n * runtime (`I()` walker in `runtime/worklet-runtime/main.js`) recognizes the\n * shape and resolves it via `lynxWorkletImpl._refImpl._workletRefMap`.\n *\n * Same logic as `nodeOps.ts:sanitizeCaptured`. Kept local to avoid pulling\n * the renderer into this module.\n */\nfunction sanitizeCaptured(captured: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const k in captured) {\n const v = captured[k];\n if (v instanceof MainThreadRef) {\n out[k] = { _wvid: v._wvid, _initValue: v._initValue };\n } else {\n out[k] = v;\n }\n }\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// runOnMainThread — BG → MT\n// ---------------------------------------------------------------------------\n\nexport function runOnMainThread<TArgs extends unknown[]>(\n worklet: WorkletPlaceholder | ((...args: TArgs) => unknown),\n): (...args: TArgs) => Promise<unknown> {\n // The build-time transform replaces the user's function with a placeholder.\n // If we receive an actual function here it means the transform didn't run\n // (test / dev fallback) — we can't ship a function across threads, so we\n // run it locally on BG.\n if (typeof worklet === 'function') {\n return (...args: TArgs): Promise<unknown> => {\n try {\n return Promise.resolve(worklet(...args));\n } catch (e) {\n return Promise.reject(e);\n }\n };\n }\n\n if (!isWorkletPlaceholder(worklet)) {\n throw new Error('runOnMainThread: argument is not a worklet placeholder. Did the build transform run?');\n }\n\n const wkltId = worklet._wkltId;\n // Snapshot _c at placeholder-construction time. Captures are resolved to\n // their identifier values at SWC transform time, so this is the right\n // moment — the user calls runOnMainThread once at setup with the\n // already-captured placeholder.\n const captured = worklet._c ? sanitizeCaptured(worklet._c) : undefined;\n\n return (...args: TArgs): Promise<unknown> => {\n return new Promise<unknown>((resolve) => {\n if (typeof lynx !== 'undefined') {\n const app = lynx?.getNativeApp?.();\n if (app && typeof app.callLepusMethod === 'function') {\n app.callLepusMethod(\n 'sigxRunOnMT',\n { wkltId, args, captured },\n (result: unknown) => resolve(result),\n );\n return;\n }\n }\n\n // Same-thread fallback (testing only)\n const g = globalThis as Record<string, unknown>;\n if (typeof g['sigxRunOnMT'] === 'function') {\n let result: unknown;\n (g['sigxRunOnMT'] as Function)(\n { wkltId, args, captured },\n (r: unknown) => { result = r; },\n );\n resolve(result);\n return;\n }\n\n resolve(undefined);\n });\n };\n}\n\n// ---------------------------------------------------------------------------\n// runOnBackground — MT → BG\n//\n// Delegated to ./run-on-background.ts. The exported `runOnBackground` from\n// that module is a throwing stub: the SWC build transform replaces every\n// `runOnBackground(fn)` call inside a `'main thread'` body with a bridged\n// dispatch (BG: `transformToWorklet(fn)` → JsFnHandle in the worklet ctx;\n// MT: bare `runOnBackground(handle)` resolved against globalThis). If the\n// stub is hit at runtime, the build transform did not run.\n// ---------------------------------------------------------------------------\n\nexport {\n runOnBackground,\n transformToWorklet,\n resetRunOnBackgroundState,\n} from './run-on-background.js';\n\nexport function resetThreading(): void {\n // Kept for API compatibility with tests that previously reset the local\n // stub. The runOnBackground module now exposes its own reset.\n}\n","/**\n * Native gesture detector — BG-side wrapper around Lynx's\n * `__SetGestureDetector(dom, id, type, config, relationMap)` PAPI.\n *\n * Mirrors the contract from upstream `@lynx-js/react/runtime/lib/gesture/`:\n * - GestureType enum values match `GestureTypeInner`.\n * - relationMap keys: `waitFor`, `simultaneous`, `continueWith`.\n * - COMPOSED gestures are walked client-side and each base is registered\n * with `__SetGestureDetector` separately (the platform receives bases).\n *\n * Public surface:\n * - `Gesture.Pan() / .Tap() / .LongPress() / ...` — chainable builders.\n * - `Gesture.Race(...) / .Simultaneous(...) / .Exclusive(...)` — composers.\n * - `useGestureDetector(elRef, gesture)` — attaches the gesture to the\n * element pointed at by elRef. Op-emit is deferred to `onMounted` so the\n * SET_MT_REF op (pushed during the first JSX render) is applied before\n * the SET_GESTURE_DETECTOR op tries to resolve the workletRefMap entry.\n */\n\nimport { onMounted, onUnmounted } from '@sigx/runtime-core';\nimport { OP, pushOp, scheduleFlush } from '../op-queue.js';\nimport { registerWorkletCtx } from '../run-on-background.js';\nimport { MainThreadRef, sanitizeCaptured } from '../main-thread-ref.js';\n\n// ---------------------------------------------------------------------------\n// Gesture type enum\n// ---------------------------------------------------------------------------\n\nexport const GestureType = {\n COMPOSED: -1,\n PAN: 0,\n FLING: 1,\n DEFAULT: 2,\n TAP: 3,\n LONGPRESS: 4,\n ROTATION: 5,\n PINCH: 6,\n NATIVE: 7,\n} as const;\n\nexport type GestureTypeValue = (typeof GestureType)[keyof typeof GestureType];\n\n// ---------------------------------------------------------------------------\n// Worklet placeholder shape (emitted by @lynx-js/react/transform)\n// ---------------------------------------------------------------------------\n\nexport interface GestureWorklet {\n _wkltId: string;\n _c?: Record<string, unknown>;\n _jsFn?: Record<string, unknown>;\n _execId?: number;\n _workletType?: string;\n}\n\n/**\n * What users write at the source level — a `'main thread'` arrow function.\n * The SWC LEPUS transform replaces it with a `GestureWorklet` placeholder\n * before the BG bundle ships, so by the time the runtime sees the value\n * it's already in placeholder shape. The union lets TypeScript accept the\n * source-level function while the runtime branch treats it as a placeholder.\n */\nexport type GestureCallback =\n | GestureWorklet\n | ((event: never) => void);\n\n// ---------------------------------------------------------------------------\n// Gesture descriptor shapes\n// ---------------------------------------------------------------------------\n\nexport interface BaseGesture {\n __isSerialized: true;\n type: number;\n id: number;\n callbacks: Record<string, GestureWorklet>;\n waitFor: BaseGesture[];\n simultaneousWith: BaseGesture[];\n continueWith: BaseGesture[];\n config?: Record<string, unknown>;\n}\n\nexport interface ComposedGesture {\n __isSerialized: true;\n type: -1;\n gestures: AnyGesture[];\n}\n\nexport type AnyGesture = BaseGesture | ComposedGesture;\n\n// ---------------------------------------------------------------------------\n// Gesture id allocator (global counter — relations refer across components)\n// ---------------------------------------------------------------------------\n\nlet nextGestureId = 1;\n\nexport function resetGestureIdCounter(): void {\n nextGestureId = 1;\n}\n\nfunction allocGestureId(): number {\n return nextGestureId++;\n}\n\n// ---------------------------------------------------------------------------\n// Builder base — uses polymorphic `this` so subclass-specific config setters\n// (`PanBuilder.axis`, `LongPressBuilder.duration`, …) chain off shared\n// callback / relation methods without losing the concrete type.\n// ---------------------------------------------------------------------------\n\nclass GestureBuilderBase {\n protected gesture: BaseGesture;\n\n constructor(type: number) {\n this.gesture = {\n __isSerialized: true,\n type,\n id: allocGestureId(),\n callbacks: {},\n waitFor: [],\n simultaneousWith: [],\n continueWith: [],\n };\n }\n\n protected setConfigKey(key: string, value: unknown): this {\n if (!this.gesture.config) this.gesture.config = {};\n this.gesture.config[key] = value;\n return this;\n }\n\n onBegin(cb: GestureCallback): this {\n this.gesture.callbacks['onBegin'] = cb as GestureWorklet;\n return this;\n }\n onStart(cb: GestureCallback): this {\n this.gesture.callbacks['onStart'] = cb as GestureWorklet;\n return this;\n }\n onUpdate(cb: GestureCallback): this {\n this.gesture.callbacks['onUpdate'] = cb as GestureWorklet;\n return this;\n }\n onEnd(cb: GestureCallback): this {\n this.gesture.callbacks['onEnd'] = cb as GestureWorklet;\n return this;\n }\n onFinalize(cb: GestureCallback): this {\n this.gesture.callbacks['onFinalize'] = cb as GestureWorklet;\n return this;\n }\n\n waitFor(...gestures: BaseGesture[]): this {\n this.gesture.waitFor.push(...gestures);\n return this;\n }\n simultaneousWith(...gestures: BaseGesture[]): this {\n this.gesture.simultaneousWith.push(...gestures);\n return this;\n }\n continueWith(...gestures: BaseGesture[]): this {\n this.gesture.continueWith.push(...gestures);\n return this;\n }\n\n build(): BaseGesture {\n return this.gesture;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Per-type builders\n// ---------------------------------------------------------------------------\n\nclass PanBuilder extends GestureBuilderBase {\n constructor() {\n super(GestureType.PAN);\n }\n axis(a: 'x' | 'y' | 'xy'): this {\n return this.setConfigKey('axis', a);\n }\n minDistance(n: number): this {\n return this.setConfigKey('minDistance', n);\n }\n}\n\nclass FlingBuilder extends GestureBuilderBase {\n constructor() {\n super(GestureType.FLING);\n }\n minVelocity(n: number): this {\n return this.setConfigKey('minVelocity', n);\n }\n direction(d: 'left' | 'right' | 'up' | 'down'): this {\n return this.setConfigKey('direction', d);\n }\n}\n\nclass TapBuilder extends GestureBuilderBase {\n constructor() {\n super(GestureType.TAP);\n }\n numberOfTaps(n: number): this {\n return this.setConfigKey('numberOfTaps', n);\n }\n maxDistance(n: number): this {\n return this.setConfigKey('maxDistance', n);\n }\n maxDuration(ms: number): this {\n return this.setConfigKey('maxDuration', ms);\n }\n}\n\nclass LongPressBuilder extends GestureBuilderBase {\n constructor() {\n super(GestureType.LONGPRESS);\n }\n /**\n * Minimum hold duration in ms before the gesture activates and `onStart`\n * fires. Native iOS handler (`LynxLongPressGestureHandler`) reads the\n * `minDuration` config key — defaults to 500 ms if not set.\n */\n minDuration(ms: number): this {\n return this.setConfigKey('minDuration', ms);\n }\n /**\n * @deprecated alias for `minDuration` kept for source compatibility. The\n * native handler only honours `minDuration`; this method now writes both\n * keys so older call sites keep working until they migrate.\n */\n duration(ms: number): this {\n this.setConfigKey('duration', ms);\n return this.setConfigKey('minDuration', ms);\n }\n maxDistance(n: number): this {\n return this.setConfigKey('maxDistance', n);\n }\n}\n\nclass PinchBuilder extends GestureBuilderBase {\n constructor() {\n super(GestureType.PINCH);\n }\n}\n\nclass RotationBuilder extends GestureBuilderBase {\n constructor() {\n super(GestureType.ROTATION);\n }\n}\n\nclass NativeBuilder extends GestureBuilderBase {\n constructor() {\n super(GestureType.NATIVE);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Compose helpers\n// ---------------------------------------------------------------------------\n\nfunction asBaseGestures(g: AnyGesture | { build(): BaseGesture }): BaseGesture[] {\n const resolved = resolveGesture(g);\n const out: BaseGesture[] = [];\n collectBases(resolved, out);\n return out;\n}\n\nfunction collectBases(g: AnyGesture, out: BaseGesture[]): void {\n if (g.type === GestureType.COMPOSED) {\n for (const sub of (g as ComposedGesture).gestures) collectBases(sub, out);\n return;\n }\n out.push(g as BaseGesture);\n}\n\nfunction resolveGesture(\n g: AnyGesture | { build(): BaseGesture },\n): AnyGesture {\n if (g && typeof (g as { build?: () => BaseGesture }).build === 'function') {\n if ((g as Partial<BaseGesture>).__isSerialized !== true) {\n return (g as { build(): BaseGesture }).build();\n }\n }\n return g as AnyGesture;\n}\n\nfunction makeComposed(gestures: AnyGesture[]): ComposedGesture {\n return {\n __isSerialized: true,\n type: GestureType.COMPOSED,\n gestures,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Public Gesture namespace\n// ---------------------------------------------------------------------------\n\nexport const Gesture = {\n Pan: () => new PanBuilder(),\n Fling: () => new FlingBuilder(),\n Tap: () => new TapBuilder(),\n LongPress: () => new LongPressBuilder(),\n Pinch: () => new PinchBuilder(),\n Rotation: () => new RotationBuilder(),\n Native: () => new NativeBuilder(),\n\n /**\n * Race — first recognizer to claim wins. Sibling bases mutually waitFor\n * each other so the platform's gesture arena resolves the priority.\n */\n Race(...gs: (AnyGesture | { build(): BaseGesture })[]): ComposedGesture {\n const resolved = gs.map(resolveGesture);\n const composed = makeComposed(resolved);\n const bases = asBaseGestures(composed);\n for (const a of bases) {\n for (const b of bases) {\n if (a !== b) a.waitFor.push(b);\n }\n }\n return composed;\n },\n\n /**\n * Simultaneous — all recognizers can fire at once. Sibling bases declare\n * mutual `simultaneousWith`.\n */\n Simultaneous(\n ...gs: (AnyGesture | { build(): BaseGesture })[]\n ): ComposedGesture {\n const resolved = gs.map(resolveGesture);\n const composed = makeComposed(resolved);\n const bases = asBaseGestures(composed);\n for (const a of bases) {\n for (const b of bases) {\n if (a !== b) a.simultaneousWith.push(b);\n }\n }\n return composed;\n },\n\n /**\n * Exclusive — sequential. Later items waitFor all earlier items.\n */\n Exclusive(\n ...gs: (AnyGesture | { build(): BaseGesture })[]\n ): ComposedGesture {\n const resolved = gs.map(resolveGesture);\n const composed = makeComposed(resolved);\n for (let i = 1; i < resolved.length; i++) {\n const laterBases: BaseGesture[] = [];\n collectBases(resolved[i]!, laterBases);\n const earlierBases: BaseGesture[] = [];\n for (let j = 0; j < i; j++) collectBases(resolved[j]!, earlierBases);\n for (const later of laterBases) later.waitFor.push(...earlierBases);\n }\n return composed;\n },\n};\n\n// ---------------------------------------------------------------------------\n// useGestureDetector — attach a gesture (or composed gesture) to an element\n// ---------------------------------------------------------------------------\n\ninterface SerializedRelationMap {\n waitFor: number[];\n simultaneous: number[];\n continueWith: number[];\n}\n\ninterface SerializedConfig {\n callbacks: { name: string; callback: GestureWorklet }[];\n config?: Record<string, unknown>;\n}\n\nfunction appendUniqueBases(\n g: AnyGesture,\n out: BaseGesture[],\n seen: Set<number>,\n): void {\n if (g.type === GestureType.COMPOSED) {\n for (const sub of (g as ComposedGesture).gestures) {\n appendUniqueBases(sub, out, seen);\n }\n return;\n }\n const base = g as BaseGesture;\n if (seen.has(base.id)) return;\n seen.add(base.id);\n out.push(base);\n}\n\nfunction buildSerializedConfig(base: BaseGesture): SerializedConfig {\n const callbacks: { name: string; callback: GestureWorklet }[] = [];\n for (const name in base.callbacks) {\n const cb = base.callbacks[name]!;\n // Preserve every field the SWC transform emits — the platform's gesture\n // arena may rely on `_workletType: 'main-thread'` (and any other markers)\n // being present at __SetGestureDetector time. We had previously stripped\n // to `{_wkltId,_c,_jsFn}`; that lost `_workletType`, and the gesture\n // arena silently ignored the registration on-device. Spread first, then\n // sanitize `_c` for wire-safety (MainThreadRef instances → wire shape).\n const wireCtx: GestureWorklet = { ...cb };\n if (cb._c) wireCtx._c = sanitizeCaptured(cb._c);\n // Stamp _execId via registerWorkletCtx so runOnBackground inside the\n // gesture callback can route MT→BG dispatches back through the same\n // pipeline used by SET_WORKLET_EVENT.\n registerWorkletCtx(wireCtx as Parameters<typeof registerWorkletCtx>[0]);\n callbacks.push({ name, callback: wireCtx });\n }\n const out: SerializedConfig = { callbacks };\n if (base.config) out.config = base.config;\n return out;\n}\n\nexport function useGestureDetector(\n elRef: MainThreadRef<unknown>,\n gesture: AnyGesture | { build(): BaseGesture },\n): void {\n const resolved = resolveGesture(gesture);\n const bases: BaseGesture[] = [];\n appendUniqueBases(resolved, bases, new Set());\n\n if (bases.length === 0) return;\n\n onMounted(() => {\n for (const base of bases) {\n const config = buildSerializedConfig(base);\n const relationMap: SerializedRelationMap = {\n waitFor: base.waitFor.map((g) => g.id),\n simultaneous: base.simultaneousWith.map((g) => g.id),\n continueWith: base.continueWith.map((g) => g.id),\n };\n pushOp(\n OP.SET_GESTURE_DETECTOR,\n elRef._wvid,\n base.id,\n base.type,\n config,\n relationMap,\n );\n }\n scheduleFlush();\n });\n\n onUnmounted(() => {\n for (const base of bases) {\n pushOp(OP.REMOVE_GESTURE_DETECTOR, elRef._wvid, base.id);\n }\n scheduleFlush();\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/model-processor.ts","../src/event-registry.ts","../src/animated-bridge.ts","../src/bg-bridge.ts","../src/run-on-background.ts","../src/op-queue.ts","../src/shadow-element.ts","../src/main-thread-ref.ts","../src/nodeOps.ts","../src/render.ts","../src/animated/shared-value.ts","../src/animated/use-animated-style.ts","../src/threading.ts","../src/native/gesture-detector.ts"],"sourcesContent":["/**\n * Platform-specific model processor for Lynx form elements.\n *\n * Wires sigx's `model={() => state.field}` two-way binding directive to\n * Lynx's <input> and <textarea> elements. The processor runs at JSX\n * creation time (called from runtime-core/jsx-runtime.ts when an\n * intrinsic element has a `model` prop) and rewrites the props to:\n *\n * 1. Set the initial `value` from the bound state\n * 2. Install a `bindinput` handler that pushes the new value back into state\n *\n * Lynx differs from DOM in two important ways:\n *\n * - Lynx <input> fires `bindinput` with `event.detail.value` containing\n * the new text. There is no DOM-style `target.value` property — the\n * value lives on the event detail object.\n * - Lynx Go (and most current Lynx hosts) have no native checkbox or\n * radio elements. We only handle text-style <input> and <textarea>\n * here. Adding checkbox/radio later is straightforward — mirror the\n * branches in packages/runtime-dom/src/model-processor.ts.\n *\n * Mirrors packages/runtime-dom/src/model-processor.ts.\n */\n\nimport { setPlatformModelProcessor } from '@sigx/runtime-core/internals';\n\ninterface LynxInputEvent {\n detail?: { value?: unknown };\n}\n\nsetPlatformModelProcessor((type, props, [stateObj, key], _originalProps) => {\n // Helper to set value — uses onUpdate handler if available (for props\n // model forwarding through component boundaries).\n const setValue = (v: unknown): void => {\n const updateHandler = stateObj[`onUpdate:${key}`];\n if (typeof updateHandler === 'function') {\n updateHandler(v);\n } else {\n stateObj[key] = v;\n }\n };\n\n // Text <input>\n if (type === 'input') {\n props.value = stateObj[key] ?? '';\n const existingBindInput = props.bindinput as\n | ((e: LynxInputEvent) => void)\n | undefined;\n props.bindinput = (e: LynxInputEvent) => {\n const v = e?.detail?.value ?? '';\n setValue(v);\n if (existingBindInput) existingBindInput(e);\n };\n const existingHandler = props['onUpdate:modelValue'];\n props['onUpdate:modelValue'] = (v: unknown) => {\n setValue(v);\n if (existingHandler) existingHandler(v);\n };\n return true;\n }\n\n // <textarea>\n if (type === 'textarea') {\n props.value = stateObj[key] ?? '';\n const existingBindInput = props.bindinput as\n | ((e: LynxInputEvent) => void)\n | undefined;\n props.bindinput = (e: LynxInputEvent) => {\n const v = e?.detail?.value ?? '';\n setValue(v);\n if (existingBindInput) existingBindInput(e);\n };\n const existingHandler = props['onUpdate:modelValue'];\n props['onUpdate:modelValue'] = (v: unknown) => {\n setValue(v);\n if (existingHandler) existingHandler(v);\n };\n return true;\n }\n\n // Not handled — fall back to the generic modelValue/onUpdate:modelValue\n // pair so component-level model forwarding still works.\n return false;\n});\n","/**\n * Sign-based event handler registry for the Background Thread.\n *\n * When patchProp registers an event handler, it gets a unique sign string.\n * The Main Thread stores this sign with __AddEvent(). When Native fires an\n * event it calls publishEvent(sign, data) on the BG Thread, which looks up\n * the handler and executes it directly — no cross-thread round-trip.\n *\n * sigx uses signal closures, so event handlers are plain functions captured by the component's\n * render closure.\n */\n\ninterface RegistryState {\n signCounter: number;\n handlers: Map<string, (data: unknown) => void>;\n}\n\n// The consumer bundle can materialise multiple event-registry module instances\n// (for example via separate entry-background/runtime graphs). Store the state\n// on globalThis so register() and publishEvent() still see the same handlers.\nconst REGISTRY_STATE_KEY = '__SIGX_LYNX_EVENT_REGISTRY__';\n\ntype RegistryGlobal = typeof globalThis & {\n __SIGX_LYNX_EVENT_REGISTRY__?: RegistryState;\n};\n\nfunction getRegistryState(): RegistryState {\n const g = globalThis as RegistryGlobal;\n let state = g[REGISTRY_STATE_KEY];\n if (!state) {\n state = {\n signCounter: 0,\n handlers: new Map<string, (data: unknown) => void>(),\n };\n Object.defineProperty(g, REGISTRY_STATE_KEY, {\n value: state,\n configurable: true,\n enumerable: false,\n writable: true,\n });\n }\n return state;\n}\n\n/**\n * Register a handler and return a unique sign string.\n * The sign is passed to __AddEvent so that the MTS can route events back.\n */\nexport function register(handler: (data: unknown) => void): string {\n const state = getRegistryState();\n const sign = `sigx:${state.signCounter++}`;\n state.handlers.set(sign, handler);\n return sign;\n}\n\n/**\n * Update the handler for an existing sign without changing the sign.\n * Used on re-renders: keeps the same sign registered on the Main Thread\n * while pointing it to the freshest handler closure.\n */\nexport function updateHandler(\n sign: string,\n handler: (data: unknown) => void,\n): void {\n getRegistryState().handlers.set(sign, handler);\n}\n\n/**\n * Get the current handler for a sign.\n */\nexport function getHandler(sign: string): ((data: unknown) => void) | undefined {\n return getRegistryState().handlers.get(sign);\n}\n\n/**\n * Unregister a handler by its sign.\n */\nexport function unregister(sign: string): void {\n getRegistryState().handlers.delete(sign);\n}\n\n/**\n * Called by Lynx Native when an event fires on the BG Thread.\n * Looks up the handler by sign and invokes it with the event data.\n */\nexport function publishEvent(sign: string, data: unknown): void {\n getRegistryState().handlers.get(sign)?.(data);\n}\n\n/** Reset all state — for testing only. */\nexport function resetRegistry(): void {\n const state = getRegistryState();\n state.signCounter = 0;\n state.handlers.clear();\n}\n","/**\n * BG-side SharedValue bridge — receives MT-thread publishes.\n *\n * Maintains a `wvid → signal<T>` registry. The MT-side bridge in\n * `@sigx/lynx-runtime-main/animated-bridge-mt.ts` dispatches batched\n * `Lynx.Sigx.AvPublish` events with `[wvid, value]` tuples; `bg-bridge.ts`\n * routes them through `ingestAvPublishes()` here, which writes each new\n * value into the corresponding signal. Sigx reactivity tracking then re-runs\n * any `effect` that read `sv.value` on BG.\n *\n * Producer: the `useSharedValue` hook in `@sigx/lynx` allocates the\n * BG-side signal via `registerBgSink(wvid, initial)` and tears it down via\n * `unregisterBgSink(wvid)` on component unmount.\n *\n * Naming note: the wire-format ops (`REGISTER_AV_BRIDGE`, `UNREGISTER_AV_BRIDGE`,\n * `Lynx.Sigx.AvPublish`) keep their original `Av` prefix as infrastructure\n * constants. The user-facing primitive renamed from `AnimatedValue` to\n * `SharedValue` in Phase 2.8.\n */\n\nimport { signal, type PrimitiveSignal } from '@sigx/reactivity';\n\nconst bgRegistry = new Map<number, PrimitiveSignal<unknown>>();\n\n/**\n * Allocate a BG-side signal mirror for the given wvid. Returns the signal so\n * the caller can read it via `.value` — sigx tracking applies as usual.\n *\n * Idempotent on the wvid: if a signal is already registered, returns the\n * existing one without resetting its value (avoids losing in-flight publishes\n * during HMR-triggered re-registration).\n */\nexport function registerBgSink<T>(wvid: number, initial: T): PrimitiveSignal<T> {\n const existing = bgRegistry.get(wvid);\n if (existing) return existing as unknown as PrimitiveSignal<T>;\n // sigx's `signal` has Primitive vs object overloads; SharedValues hold\n // primitives in practice (numbers, strings) and the bridge only ever\n // writes through `.value`, so a single-overload selection via cast keeps\n // the unconstrained T usable here without forcing SharedValue consumers\n // to type-narrow at every call site.\n const s = signal(initial as unknown as number);\n bgRegistry.set(wvid, s as unknown as PrimitiveSignal<unknown>);\n return s as unknown as PrimitiveSignal<T>;\n}\n\n/**\n * Drop the BG-side signal for this wvid. Called on component unmount.\n * Subsequent `ingestPublish` entries for this wvid become no-ops.\n */\nexport function unregisterBgSink(wvid: number): void {\n bgRegistry.delete(wvid);\n}\n\n/**\n * Ingest a batch of `[wvid, value]` tuples from the MT bridge. Writes each\n * into the corresponding signal so any `effect` that read it re-runs on the\n * sigx scheduler's next tick. Tuples for unregistered wvids (race with\n * unmount) are silently dropped.\n */\nexport function ingestAvPublishes(updates: Array<[number, unknown]>): void {\n for (const [wvid, value] of updates) {\n const s = bgRegistry.get(wvid);\n if (!s) continue;\n (s as unknown as { value: unknown }).value = value;\n }\n}\n\n/** Reset hook — for testing. Drops every registered sink. */\nexport function resetBgAvBridge(): void {\n bgRegistry.clear();\n}\n\n/** Test hook — number of currently registered sinks. */\nexport function bgAvSinkCount(): number {\n return bgRegistry.size;\n}\n","/**\n * BG-side listener for the MT→BG `Lynx.Sigx.PublishEvent` channel.\n *\n * The MT-side hybrid worklet (`lynx-runtime-main/src/hybrid-worklet.ts`) calls\n * `lynx.getJSContext().dispatchEvent({ type: 'Lynx.Sigx.PublishEvent', data })`\n * to fire the BG handler whose sign is captured in the hybrid ctx. We listen\n * for that event here and route through the existing event-registry's\n * `publishEvent` — the same dispatcher Lynx native calls when a normal\n * `bindtap` fires on BG. The user's BG handler runs in the same call-stack\n * shape it always has, so signal updates / `count.value++` etc. work without\n * any awareness that the trigger came from MT.\n *\n * Side-effect import from `index.ts` so the listener is wired before any\n * user code runs.\n *\n * CROSS-THREAD ASYMMETRY (per @lynx-js/react/runtime/lib/worklet/call/runOnBackground.js):\n * - MT → BG dispatch: MT calls `lynx.getJSContext().dispatchEvent(...)`,\n * BG listens via `lynx.getCoreContext().addEventListener(...)`.\n * - BG → MT dispatch: BG calls `lynx.getCoreContext().dispatchEvent(...)`,\n * MT listens via `lynx.getJSContext().addEventListener(...)`.\n * Each side calls a DIFFERENT method to reach the other thread — they're not\n * symmetric. Listening on `lynx.getJSContext()` from BG just listens on BG's\n * own context (no cross-thread events arrive).\n *\n * `lynx` is closure-injected by RuntimeWrapperWebpackPlugin (declared in\n * shims.d.ts). It is NOT available as `globalThis.lynx` — use the free\n * identifier directly.\n */\n\nimport { publishEvent } from './event-registry';\nimport { ingestAvPublishes } from './animated-bridge';\n\ninterface JSContextLike {\n addEventListener?: (\n type: string,\n listener: (e: { data: string }) => void,\n ) => void;\n}\n\nconst lynxObj: { getCoreContext?: () => JSContextLike } | undefined =\n typeof lynx !== 'undefined'\n ? (lynx as unknown as { getCoreContext?: () => JSContextLike })\n : undefined;\nconst ctx: JSContextLike | undefined = lynxObj?.getCoreContext?.();\n\nif (ctx?.addEventListener) {\n ctx.addEventListener('Lynx.Sigx.PublishEvent', (e: { data: string }) => {\n let payload: { sign?: string; event?: unknown };\n try {\n payload = JSON.parse(e.data) as { sign?: string; event?: unknown };\n } catch {\n return; // malformed bridge message — drop\n }\n if (typeof payload.sign === 'string') {\n publishEvent(payload.sign, payload.event);\n }\n });\n\n // SharedValue bridge: each event payload is an array of\n // `[wvid, value]` tuples coalesced from one MT flush window. See\n // `animated-bridge.ts` and `@sigx/lynx-runtime-main/animated-bridge-mt.ts`.\n ctx.addEventListener('Lynx.Sigx.AvPublish', (e: { data: string }) => {\n let updates: Array<[number, unknown]>;\n try {\n updates = JSON.parse(e.data) as Array<[number, unknown]>;\n } catch {\n return;\n }\n if (Array.isArray(updates)) {\n ingestAvPublishes(updates);\n }\n });\n}\n\nexport {};\n","/**\n * runOnBackground — BG-side wiring for the MT→BG cross-thread call channel.\n *\n * Two responsibilities:\n * 1. `transformToWorklet(fn)` — wraps a BG function as a `JsFnHandle`\n * `{ _jsFnId, _fn }` so the SWC transform can serialise it into the\n * `_jsFn` slot of a worklet ctx. The BG worklet-loader emits inline\n * `transformToWorklet(...)` calls when the user writes `runOnBackground(fn)`\n * inside a `'main thread'` body.\n * 2. `Lynx.Sigx.RunOnBackground` listener — when the MT-side dispatcher\n * fires, finds the matching JsFnHandle by `(execId, fnId)` from the\n * registered worklet ctxs, runs `_fn(...params)`, dispatches\n * `Lynx.Sigx.FunctionCallRet` back with `{resolveId, returnValue}`.\n *\n * Mirrors @lynx-js/react/runtime/lib/worklet/call/runOnBackground +\n * vue-lynx's run-on-background.ts (same protocol shape, sigx-namespaced\n * event types).\n */\n\nconst RUN_ON_BACKGROUND = 'Lynx.Sigx.RunOnBackground';\nconst FUNCTION_CALL_RET = 'Lynx.Sigx.FunctionCallRet';\n\n// ---------------------------------------------------------------------------\n// JsFnHandle shape — serialisable representation of a BG function\n// ---------------------------------------------------------------------------\n\nexport interface JsFnHandle {\n _jsFnId?: number;\n _execId?: number;\n _fn?: (...args: unknown[]) => unknown;\n _isFirstScreen?: boolean;\n _error?: string;\n}\n\ninterface WorkletCtx {\n _wkltId: string;\n _execId?: number;\n _c?: Record<string, unknown>;\n _jsFn?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\n// ---------------------------------------------------------------------------\n// transformToWorklet — mint a JsFnHandle for cross-thread dispatch\n//\n// The SWC JS pass emits inline `transformToWorklet(fn)` calls in the BG bundle\n// when it sees `runOnBackground(fn)` inside a `'main thread'` body. The handle\n// flows into the worklet ctx's `_jsFn` slot; MT extracts it and dispatches via\n// `runOnBackground(handle)(...args)`.\n// ---------------------------------------------------------------------------\n\nlet lastJsFnId = 0;\n\nexport function transformToWorklet(\n fn: (...args: unknown[]) => unknown,\n): JsFnHandle {\n const id = ++lastJsFnId;\n if (typeof fn !== 'function') {\n return {\n _jsFnId: id,\n _error:\n `Argument of runOnBackground should be a function, got [${typeof fn}]`,\n };\n }\n // Stamp toJSON so JSON.stringify of the worklet ctx replaces the function\n // body with a placeholder string — MT only needs `_jsFnId`/`_execId`.\n (fn as unknown as { toJSON?: () => string }).toJSON ??= () =>\n '[BackgroundFunction]';\n return { _jsFnId: id, _fn: fn };\n}\n\n// ---------------------------------------------------------------------------\n// IndexMap — auto-incrementing Map (worklet exec-id allocator)\n// ---------------------------------------------------------------------------\n\nclass IndexMap<T> {\n private lastIndex = 0;\n private map = new Map<number, T>();\n\n add(value: T): number {\n const id = ++this.lastIndex;\n this.map.set(id, value);\n return id;\n }\n\n get(index: number): T | undefined {\n return this.map.get(index);\n }\n\n remove(index: number): void {\n this.map.delete(index);\n }\n}\n\nclass WorkletExecIdMap extends IndexMap<WorkletCtx> {\n override add(worklet: WorkletCtx): number {\n const execId = super.add(worklet);\n worklet._execId = execId;\n return execId;\n }\n\n findJsFnHandle(execId: number, fnId: number): JsFnHandle | undefined {\n const worklet = this.get(execId);\n if (!worklet) return undefined;\n\n const visited = new Set<object>();\n const search = (value: unknown): JsFnHandle | undefined => {\n if (value === null || typeof value !== 'object') return undefined;\n const obj = value as Record<string, unknown>;\n if (visited.has(obj)) return undefined;\n visited.add(obj);\n if ('_jsFnId' in obj && obj['_jsFnId'] === fnId) {\n return obj as JsFnHandle;\n }\n for (const key in obj) {\n const result = search(obj[key]);\n if (result) return result;\n }\n return undefined;\n };\n\n return search(worklet);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Module state — lazy-init so SSR / tests don't pay for the listener wiring\n// ---------------------------------------------------------------------------\n\nlet execIdMap: WorkletExecIdMap | undefined;\n\ninterface CoreContextLike {\n addEventListener?: (type: string, listener: (e: { data?: unknown }) => void) => void;\n dispatchEvent?: (e: { type: string; data: string }) => void;\n}\n\nfunction getCoreContext(): CoreContextLike | undefined {\n if (typeof lynx === 'undefined') return undefined;\n const obj = lynx as unknown as { getCoreContext?: () => CoreContextLike };\n return typeof obj.getCoreContext === 'function' ? obj.getCoreContext() : undefined;\n}\n\nfunction init(): void {\n execIdMap = new WorkletExecIdMap();\n const ctx = getCoreContext();\n ctx?.addEventListener?.(RUN_ON_BACKGROUND, runJSFunction);\n}\n\n// ---------------------------------------------------------------------------\n// registerWorkletCtx — stamp _execId on outgoing worklet ctxs\n//\n// Called from nodeOps.patchProp (SET_WORKLET_EVENT path) and from\n// runOnMainThread before shipping a ctx across threads. Must run BEFORE\n// JSON.stringify so the ctx carries `_execId` to MT.\n// ---------------------------------------------------------------------------\n\nexport function registerWorkletCtx(ctx: WorkletCtx): void {\n if (!execIdMap) init();\n execIdMap!.add(ctx);\n}\n\n// ---------------------------------------------------------------------------\n// runJSFunction — handles MT → BG dispatch\n// ---------------------------------------------------------------------------\n\ninterface RunOnBackgroundData {\n obj: { _jsFnId: number; _execId: number };\n params: unknown[];\n resolveId: number;\n}\n\nfunction runJSFunction(event: { data?: unknown }): void {\n let data: RunOnBackgroundData;\n try {\n data = JSON.parse(event.data as string) as RunOnBackgroundData;\n } catch {\n return; // malformed bridge message — drop\n }\n const handle = execIdMap?.findJsFnHandle(data.obj._execId, data.obj._jsFnId);\n if (!handle?._fn) {\n // Fn is gone — likely the owning worklet ctx was unregistered. Resolve\n // with undefined so the MT promise doesn't hang.\n dispatchReturn(data.resolveId, undefined);\n return;\n }\n let returnValue: unknown;\n try {\n returnValue = handle._fn(...data.params);\n } catch (e) {\n dispatchReturn(data.resolveId, undefined);\n throw e;\n }\n // Promise return values are not transferable across the JSON bridge — caller\n // must await on the BG fn body itself if they need async results.\n dispatchReturn(data.resolveId, returnValue);\n}\n\nfunction dispatchReturn(resolveId: number, returnValue: unknown): void {\n const ctx = getCoreContext();\n ctx?.dispatchEvent?.({\n type: FUNCTION_CALL_RET,\n data: JSON.stringify({ resolveId, returnValue }),\n });\n}\n\n// ---------------------------------------------------------------------------\n// User-facing stub — replaced by SWC at every `runOnBackground(fn)` call site\n// inside a `'main thread'` body. If you reach this code path at runtime, the\n// build transform did not run on this file.\n// ---------------------------------------------------------------------------\n\nexport function runOnBackground<R, Fn extends (...args: never[]) => R>(\n _fn: Fn,\n): (...args: Parameters<Fn>) => Promise<R> {\n throw new Error(\n 'runOnBackground() can only be used inside \\'main thread\\' functions. '\n + 'The SWC worklet transform should replace this call at build time — '\n + 'verify @sigx/lynx-plugin\\'s worklet-loader is wired into your bundler.',\n );\n}\n\n// ---------------------------------------------------------------------------\n// Reset — for testing only\n// ---------------------------------------------------------------------------\n\nexport function resetRunOnBackgroundState(): void {\n execIdMap = undefined;\n lastJsFnId = 0;\n}\n","/**\n * Op queue — accumulates renderer ops on the Background Thread and flushes\n * them to the Main Thread via the Lynx host bridge.\n *\n * The bridge identifiers `lynx` and `lynxCoreInject` are NOT on globalThis.\n * They are injected as closure parameters by RuntimeWrapperWebpackPlugin\n * (peer dep `@lynx-js/runtime-wrapper-webpack-plugin`), which wraps the BG\n * bundle in `__init_card_bundle__(lynxCoreInject, lynx, ...)`. Once wrapped,\n * any module in the bundle can reference them as bare identifiers.\n *\n */\n\nexport { OP } from '@sigx/lynx-runtime-internal';\n\n// ---------------------------------------------------------------------------\n// Ambient declarations for the closure-injected host bridge identifiers\n// ---------------------------------------------------------------------------\n\n// `lynx` and `lynxCoreInject` are declared in src/shims.d.ts as the\n// single source of truth for closure-injected identifiers from\n// runtime-wrapper-webpack-plugin. Both are typed as `any` since their\n// shape varies by host — call sites guard with typeof checks.\n\n// ---------------------------------------------------------------------------\n// Op buffer\n// ---------------------------------------------------------------------------\n\nlet buffer: unknown[] = [];\n\n/**\n * Push one op (opcode + arguments) into the buffer as a flat sequence.\n * Example: pushOp(OP.CREATE, id, type) → buffer gets [0, id, type].\n */\nexport function pushOp(...args: unknown[]): void {\n for (const arg of args) {\n buffer.push(arg);\n }\n}\n\n/** Take all buffered ops and reset the buffer. */\nexport function takeOps(): unknown[] {\n const b = buffer;\n buffer = [];\n return b;\n}\n\n// ---------------------------------------------------------------------------\n// Scheduling\n// ---------------------------------------------------------------------------\n\nlet scheduled = false;\n\n/**\n * Schedule a flush of the ops buffer at the end of the current microtask.\n * Multiple calls within one tick are coalesced into one cross-thread call.\n */\nexport function scheduleFlush(): void {\n if (scheduled) return;\n scheduled = true;\n Promise.resolve().then(doFlush);\n}\n\n/**\n * Immediately flush all buffered ops — used on initial mount so the first\n * frame is committed synchronously.\n */\nexport function flushNow(): void {\n scheduled = false;\n const ops = takeOps();\n if (ops.length === 0) return;\n sendOps(ops);\n}\n\n/** Reset module state — for testing only. */\nexport function resetOpQueue(): void {\n buffer = [];\n scheduled = false;\n pendingAckResolve = null;\n pendingAckPromise = null;\n}\n\nfunction doFlush(): void {\n scheduled = false;\n const ops = takeOps();\n if (ops.length === 0) return;\n sendOps(ops);\n}\n\n// ---------------------------------------------------------------------------\n// Main-thread ack tracking\n//\n// callLepusMethod is asynchronous: by the time the BG flush cycle finishes,\n// the MT has not yet applied the ops. Track a promise that resolves when the\n// MT acks via the callback so callers can `await waitForFlush()` if they need\n// to coordinate with the next-tick UI state.\n// ---------------------------------------------------------------------------\n\nlet pendingAckResolve: (() => void) | null = null;\nlet pendingAckPromise: Promise<void> | null = null;\n\n/**\n * Resolves once the most recent ops batch has been applied on the main\n * thread. If no ops are in flight, resolves immediately.\n */\nexport function waitForFlush(): Promise<void> {\n return pendingAckPromise ?? Promise.resolve();\n}\n\n// ---------------------------------------------------------------------------\n// Transport (BG → MT)\n// ---------------------------------------------------------------------------\n\nfunction sendOps(ops: unknown[]): void {\n const data = JSON.stringify(ops);\n\n // Create the ack promise BEFORE sending so any waitForFlush() chained\n // immediately after this call observes the in-flight batch.\n pendingAckPromise = new Promise<void>((resolve) => {\n pendingAckResolve = resolve;\n });\n\n // Primary path: closure-injected `lynx` from RuntimeWrapperWebpackPlugin.\n if (typeof lynx !== 'undefined') {\n const app = lynx?.getNativeApp?.();\n if (app && typeof app.callLepusMethod === 'function') {\n app.callLepusMethod('sigxPatchUpdate', { data }, () => {\n pendingAckResolve?.();\n pendingAckResolve = null;\n pendingAckPromise = null;\n });\n return;\n }\n }\n\n // Same-thread fallback for unit tests where BG and MT share globalThis.\n const g = globalThis as Record<string, unknown>;\n if (typeof g['sigxPatchUpdate'] === 'function') {\n (g['sigxPatchUpdate'] as Function)({ data });\n pendingAckResolve?.();\n pendingAckResolve = null;\n pendingAckPromise = null;\n return;\n }\n\n // No bridge available — drop and resolve so callers don't hang. This path\n // indicates the bundle wasn't wrapped by RuntimeWrapperWebpackPlugin.\n console.log(\n '[sigx-bg] sendOps: no `lynx` global injected — bundle is missing RuntimeWrapperWebpackPlugin',\n );\n pendingAckResolve?.();\n pendingAckResolve = null;\n pendingAckPromise = null;\n}\n\n// ---------------------------------------------------------------------------\n// Hot reload signal (BG → MT)\n//\n// Sent before a webpack HMR update replaces the BG module, so the MT resets\n// its element registry and page root before the new ops batch arrives.\n// ---------------------------------------------------------------------------\n\n/**\n * Tell the Main Thread to reset its element tree in preparation for a hot\n * reload. The MT handler (`sigxHotReload`) calls `resetMainThreadState()`,\n * re-creates the page root, and flushes — so the next `sigxPatchUpdate`\n * batch builds on a clean tree.\n *\n * This is fire-and-forget: callLepusMethod messages are ordered, so\n * sigxHotReload will be processed before any subsequent sigxPatchUpdate.\n */\nexport function sendHotReloadSignal(): void {\n // Primary path: closure-injected `lynx` from RuntimeWrapperWebpackPlugin.\n if (typeof lynx !== 'undefined') {\n const app = lynx?.getNativeApp?.();\n if (app && typeof app.callLepusMethod === 'function') {\n app.callLepusMethod('sigxHotReload', {}, () => {});\n return;\n }\n }\n\n // Same-thread fallback for testing where BG and MT share globalThis.\n const g = globalThis as Record<string, unknown>;\n if (typeof g['sigxHotReload'] === 'function') {\n (g['sigxHotReload'] as Function)();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Event dispatch (MT → BG)\n//\n// The Lynx host calls `lynxCoreInject.tt.publishEvent(sign, data)` (and\n// `publicComponentEvent(cid, sign, data)`) when an event fires on the\n// Main Thread. We install our dispatcher there once at module load.\n// ---------------------------------------------------------------------------\n\n/**\n * Look up a sign in __SIGX_LYNX_EVENT_REGISTRY__ and invoke the registered\n * handler. The registry is populated by lynx-runtime's patchProp branch\n * whenever the renderer sees a `bindtap` / `onTap` / etc. prop.\n */\nfunction dispatchEvent(sign: unknown, evt: unknown): void {\n try {\n const registry = (globalThis as any).__SIGX_LYNX_EVENT_REGISTRY__;\n if (!registry?.handlers || sign == null) return;\n const handlers = registry.handlers;\n const fn = handlers instanceof Map\n ? handlers.get(String(sign))\n : handlers[String(sign)];\n if (typeof fn === 'function') fn(evt);\n } catch (e) {\n console.log('[sigx-bg] event dispatch threw:', String(e));\n }\n}\n\n/**\n * Install our event dispatcher on `lynxCoreInject.tt` — the official place\n * the Lynx host calls when it forwards Main Thread events to the BG.\n *\n * Idempotent. Called from render.ts on module load and from lynxMount() as\n * a defensive re-install in case the host swaps the tt namespace between\n * card loads.\n */\nexport function installEventPublisher(): void {\n // Primary install path — the canonical Lynx integration point.\n if (typeof lynxCoreInject !== 'undefined' && lynxCoreInject?.tt) {\n lynxCoreInject.tt.publishEvent = dispatchEvent;\n lynxCoreInject.tt.publicComponentEvent = (\n _cid: string,\n sign: string,\n data: unknown,\n ) => dispatchEvent(sign, data);\n }\n\n // Fallback for older Lynx SDKs that look at globalThis.publishEvent.\n const g = globalThis as Record<string, unknown>;\n if (typeof g['publishEvent'] !== 'function') {\n g['publishEvent'] = dispatchEvent as (...args: unknown[]) => void;\n }\n if (typeof g['publicComponentEvent'] !== 'function') {\n g['publicComponentEvent'] = ((_cid: string, sign: string, data: unknown) =>\n dispatchEvent(sign, data)) as (...args: unknown[]) => void;\n }\n}\n","/**\n * ShadowElement: a lightweight doubly-linked tree node that lives entirely in\n * the Background Thread. It lets the renderer call parentNode() / nextSibling()\n * synchronously, while the real Lynx elements exist only on the Main Thread.\n *\n * id=1 is reserved for the page root (created via __CreatePage on Main Thread).\n * Regular elements start from id=2.\n */\n\nexport class ShadowElement {\n static nextId = 2; // 1 is reserved for the page root\n\n id: number;\n type: string;\n parent: ShadowElement | null = null;\n firstChild: ShadowElement | null = null;\n lastChild: ShadowElement | null = null;\n prev: ShadowElement | null = null;\n next: ShadowElement | null = null;\n\n // Cached style object (last value passed to patchProp 'style').\n // Used by vShow to merge display:none without losing the original styles.\n _style: Record<string, unknown> = {};\n // Set to true by vShow when the element should be hidden.\n _vShowHidden = false;\n\n // Class management for Transition support.\n _baseClass = '';\n _transitionClasses: Set<string> = new Set();\n\n constructor(type: string, forceId?: number) {\n this.id = forceId !== undefined ? forceId : ShadowElement.nextId++;\n this.type = type;\n }\n\n insertBefore(child: ShadowElement, anchor: ShadowElement | null): void {\n // Detach from current parent first\n if (child.parent) {\n child.parent.removeChild(child);\n }\n child.parent = this;\n\n if (anchor) {\n // Insert before anchor\n const prev = anchor.prev;\n child.next = anchor;\n child.prev = prev;\n anchor.prev = child;\n if (prev) {\n prev.next = child;\n } else {\n this.firstChild = child;\n }\n } else {\n // Append at end\n if (this.lastChild) {\n this.lastChild.next = child;\n child.prev = this.lastChild;\n } else {\n this.firstChild = child;\n child.prev = null;\n }\n this.lastChild = child;\n child.next = null;\n }\n }\n\n removeChild(child: ShadowElement): void {\n const prev = child.prev;\n const next = child.next;\n if (prev) {\n prev.next = next;\n } else {\n this.firstChild = next;\n }\n if (next) {\n next.prev = prev;\n } else {\n this.lastChild = prev;\n }\n child.parent = null;\n child.prev = null;\n child.next = null;\n }\n}\n\nexport const PAGE_ROOT_ID = 1;\n\n/** Create the page root shadow element with the reserved id=1. */\nexport function createPageRoot(): ShadowElement {\n return new ShadowElement('page', PAGE_ROOT_ID);\n}\n\n/** Reset the ID counter — for testing only. */\nexport function resetShadowState(): void {\n ShadowElement.nextId = 2;\n}\n","/**\n * MainThreadRef — a ref whose `.current` value lives on the Main Thread.\n *\n * On the Background Thread, `.current` is always the initial value (typically\n * `null`). On the Main Thread, `.current` is set to the real Lynx element\n * handle when the ref is bound via `main-thread:ref={ref}`.\n *\n * This is the sigx equivalent of react-lynx's `useMainThreadRef()` and\n * vue-lynx's `useMainThreadRef()`. It enables zero-latency style updates\n * and animations by giving main-thread event handlers synchronous access\n * to native elements.\n *\n * Architecture:\n * BG: useMainThreadRef(init) → MainThreadRef { wvid, current: init }\n * → pushOp(INIT_MT_REF, wvid, init)\n * BG: patchProp('main-thread:ref', ref) → pushOp(SET_MT_REF, elId, wvid)\n * MT: INIT_MT_REF → workletRefs.set(wvid, { current: init })\n * MT: SET_MT_REF → workletRefs.get(wvid).current = elements.get(elId)\n */\n\nimport { onUnmounted } from '@sigx/runtime-core';\nimport { OP, pushOp, scheduleFlush } from './op-queue';\n\n// ---------------------------------------------------------------------------\n// Worklet variable ID generator\n// ---------------------------------------------------------------------------\n\nlet nextWvid = 1;\n\nexport function resetWvidCounter(): void {\n nextWvid = 1;\n}\n\n// ---------------------------------------------------------------------------\n// MainThreadRef class\n// ---------------------------------------------------------------------------\n\n/**\n * A ref whose `.current` property is managed on the Main Thread.\n *\n * On the BG thread, `.current` returns the `initValue` and is read-only\n * (setting it has no effect — the real value lives on MT).\n *\n * In main-thread event handlers and `runOnMainThread` callbacks, `.current`\n * is the real Lynx element handle with methods like `setStyleProperties()`,\n * `getComputedStyleProperty()`, and `animate()`.\n */\nexport class MainThreadRef<T = unknown> {\n /**\n * Worklet variable ID — uniquely identifies this ref across threads.\n * Underscored to match the field name `transformWorklet` walks for\n * in @lynx-js/react/worklet-runtime when expanding `_c` captures.\n */\n readonly _wvid: number;\n\n /**\n * Initial value snapshot — sent to MT in INIT_MT_REF and used by\n * the worklet-runtime to seed the firstScreen ref map.\n */\n readonly _initValue: T;\n\n /**\n * On BG: the init value (read-only snapshot).\n * On MT: the real element handle (set by SET_MT_REF op).\n */\n current: T;\n\n constructor(initValue: T) {\n this._wvid = nextWvid++;\n this._initValue = initValue;\n this.current = initValue;\n }\n}\n\n/**\n * Walk a captured `_c` map and serialize MainThreadRef instances to plain\n * `{ _wvid, _initValue }` objects so they survive the JSON round-trip across\n * the BG→MT bridge. Upstream's worklet-runtime walks `_c` looking for `_wvid`\n * to recognize ref captures and resolve them via\n * `lynxWorkletImpl._refImpl._workletRefMap`.\n *\n * Used by both the SET_WORKLET_EVENT path (`nodeOps.patchProp`) and the\n * SET_GESTURE_DETECTOR path (`native/gesture-detector.ts`).\n */\nexport function sanitizeCaptured(\n captured: Record<string, unknown>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const k in captured) {\n const v = captured[k];\n if (v instanceof MainThreadRef) {\n out[k] = { _wvid: v._wvid, _initValue: v._initValue };\n } else {\n out[k] = v;\n }\n }\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// useMainThreadRef composable\n// ---------------------------------------------------------------------------\n\n/**\n * Create a ref that provides synchronous access to a native element on the\n * Main Thread. Bind it to an element via `main-thread:ref={ref}`.\n *\n * @example\n * ```tsx\n * const elRef = useMainThreadRef<MainThread.Element>(null);\n *\n * function handleScroll(e: ScrollEvent) {\n * 'main thread';\n * const offset = e.detail.scrollTop;\n * elRef.current?.setStyleProperties({\n * transform: `translateY(${-offset}px)`,\n * });\n * }\n *\n * return (\n * <scroll-view\n * main-thread-bindscroll={handleScroll}\n * >\n * <view main-thread:ref={elRef}>\n * <text>Sticky header</text>\n * </view>\n * </scroll-view>\n * );\n * ```\n */\nexport function useMainThreadRef<T = unknown>(\n initValue: T,\n): MainThreadRef<T> {\n const ref = new MainThreadRef<T>(initValue);\n // Tell the MT to create a worklet ref holder with this ID and initial value.\n pushOp(OP.INIT_MT_REF, ref._wvid, initValue);\n scheduleFlush();\n // Release the holder when the owning component unmounts. Without this, the\n // MT-side `_workletRefMap` grows monotonically across mount/unmount cycles\n // (router-driven apps with frequent navigation hit this fastest).\n // `onUnmounted` no-ops if called outside a component setup; callers that\n // construct refs ad-hoc (e.g. tests) just won't get a release op, which is\n // the same as today's behaviour.\n onUnmounted(() => {\n pushOp(OP.RELEASE_MT_REF, ref._wvid);\n scheduleFlush();\n });\n return ref;\n}\n","/**\n * Lynx renderer node operations (Background Thread).\n *\n * Builds a ShadowElement tree and pushes ops into the op queue. NO Lynx PAPI\n * globals (__CreateElement, __AppendElement, etc.) are referenced here — those\n * only exist on the Main Thread. The op queue is flushed to MT via\n * sigxPatchUpdate, where ops-apply.ts dispatches them to real PAPI calls.\n */\nimport type { RendererOptions } from '@sigx/runtime-core/internals';\nimport { OP, pushOp, scheduleFlush } from './op-queue';\nimport { register, unregister } from './event-registry';\nimport { ShadowElement } from './shadow-element';\nimport { registerWorkletCtx } from './run-on-background';\n\n// ---------------------------------------------------------------------------\n// Re-export ShadowElement as the HostNode / HostElement type\n// ---------------------------------------------------------------------------\n\nexport type LynxNode = ShadowElement;\nexport type LynxElement = ShadowElement;\n\n// ---------------------------------------------------------------------------\n// Event prop classification\n// ---------------------------------------------------------------------------\n\ninterface EventSpec {\n type: string;\n name: string;\n /** When true, this event runs on the Main Thread (zero-latency). */\n mainThread?: boolean;\n}\n\nfunction parseEventProp(key: string): EventSpec | null {\n // Main-thread event prefixes: main-thread-bind*, main-thread-catch*\n if (key.startsWith('main-thread-bind')) {\n return { type: 'bindEvent', name: key.slice('main-thread-bind'.length), mainThread: true };\n }\n if (key.startsWith('main-thread-catch')) {\n return { type: 'catchEvent', name: key.slice('main-thread-catch'.length), mainThread: true };\n }\n // Alternative syntax: main-thread:bind*, main-thread:catch*\n if (key.startsWith('main-thread:bind')) {\n return { type: 'bindEvent', name: key.slice('main-thread:bind'.length), mainThread: true };\n }\n if (key.startsWith('main-thread:catch')) {\n return { type: 'catchEvent', name: key.slice('main-thread:catch'.length), mainThread: true };\n }\n if (key.startsWith('global-bind')) {\n return { type: 'bindGlobalEvent', name: key.slice('global-bind'.length) };\n }\n if (key.startsWith('global-catch')) {\n return { type: 'catchGlobalEvent', name: key.slice('global-catch'.length) };\n }\n if (key.startsWith('catch')) {\n return { type: 'catchEvent', name: key.slice('catch'.length) };\n }\n if (/^bind(?!ingx)/.test(key)) {\n return { type: 'bindEvent', name: key.slice('bind'.length) };\n }\n if (/^on[A-Z]/.test(key)) {\n // onTap → { type: 'bindEvent', name: 'tap' }\n const name = key.slice(2, 3).toLowerCase() + key.slice(3);\n return { type: 'bindEvent', name };\n }\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Worklet placeholder detection — @lynx-js/react/transform replaces\n// 'main thread' functions with { _wkltId, _c? } placeholders in the BG bundle.\n// ---------------------------------------------------------------------------\n\nimport { MainThreadRef, sanitizeCaptured } from './main-thread-ref';\n\ninterface WorkletPlaceholder {\n _wkltId: string;\n _c?: Record<string, unknown>;\n _jsFn?: Record<string, unknown>;\n}\n\nfunction isWorkletPlaceholder(v: unknown): v is WorkletPlaceholder {\n return typeof v === 'object' && v !== null && '_wkltId' in (v as object);\n}\n\n// Track sent worklet ids per (elementId, propKey) to skip redundant ops on re-render.\nconst sentWorklets = new Map<number, Map<string, string>>();\n\n// Track the sign registered for each (element, propKey) so we can unregister\n// on prop removal / update.\nconst elementEventSigns = new Map<number, Map<string, string>>();\n\n// Track which sign owns the native event slot per (elementId, 'eventType:eventName').\n// Lynx only supports one __AddEvent per (element, eventType, eventName). When multiple\n// props resolve to the same native event (e.g. main-thread-bindtap + bindtap both map\n// to bindEvent:tap), we keep one sign in __AddEvent and dispatch to all handlers from\n// a multi-handler wrapper in the BG registry.\nconst nativeEventSlots = new Map<number, Map<string, { sign: string; handlers: Map<string, (data: unknown) => void> }>>();\n\n// ---------------------------------------------------------------------------\n// Style normalisation — numeric values → 'Npx' (Lynx requires units)\n// ---------------------------------------------------------------------------\n\nconst DIMENSIONLESS = new Set([\n 'flex',\n 'flexGrow',\n 'flexShrink',\n 'flexOrder',\n 'order',\n 'opacity',\n 'zIndex',\n 'aspectRatio',\n 'fontWeight',\n 'lineClamp',\n]);\n\nfunction normalizeStyle(\n style: Record<string, unknown>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(style)) {\n const val = style[key];\n if (typeof val === 'number' && !DIMENSIONLESS.has(key) && val !== 0) {\n out[key] = `${val}px`;\n } else {\n out[key] = val;\n }\n }\n return out;\n}\n\nfunction shallowEqual(\n a: Record<string, unknown>,\n b: Record<string, unknown>,\n): boolean {\n const ka = Object.keys(a);\n const kb = Object.keys(b);\n if (ka.length !== kb.length) return false;\n for (const k of ka) {\n if (a[k] !== b[k]) return false;\n }\n return true;\n}\n\n// ---------------------------------------------------------------------------\n// Class resolution — merges user :class with transition classes\n// ---------------------------------------------------------------------------\n\nexport function resolveClass(el: ShadowElement): string {\n if (el._transitionClasses.size === 0) return el._baseClass;\n const parts: string[] = [];\n if (el._baseClass) parts.push(el._baseClass);\n for (const cls of el._transitionClasses) parts.push(cls);\n return parts.join(' ');\n}\n\n// ---------------------------------------------------------------------------\n// RendererOptions implementation\n// ---------------------------------------------------------------------------\n\nexport const nodeOps: RendererOptions<ShadowElement, ShadowElement> = {\n createElement(type: string): ShadowElement {\n const el = new ShadowElement(type);\n pushOp(OP.CREATE, el.id, type);\n scheduleFlush();\n return el;\n },\n\n createText(text: string): ShadowElement {\n const el = new ShadowElement('#text');\n pushOp(OP.CREATE_TEXT, el.id);\n if (text) pushOp(OP.SET_TEXT, el.id, text);\n scheduleFlush();\n return el;\n },\n\n // Comment nodes are used as position anchors for conditionals / Fragment.\n // Materialised as invisible placeholder elements on the Main Thread.\n createComment(_text: string): ShadowElement {\n const el = new ShadowElement('#comment');\n pushOp(OP.CREATE, el.id, '__comment');\n scheduleFlush();\n return el;\n },\n\n setText(node: ShadowElement, text: string): void {\n pushOp(OP.SET_TEXT, node.id, text);\n scheduleFlush();\n },\n\n setElementText(el: ShadowElement, text: string): void {\n // Remove all children from shadow tree\n while (el.firstChild) {\n const child = el.firstChild;\n el.removeChild(child);\n pushOp(OP.REMOVE, el.id, child.id);\n }\n // Set text content directly on the element\n pushOp(OP.SET_TEXT, el.id, text);\n scheduleFlush();\n },\n\n insert(\n child: ShadowElement,\n parent: ShadowElement,\n anchor?: ShadowElement | null,\n ): void {\n // Always update the shadow tree (the core renderer needs sync tree queries).\n parent.insertBefore(child, anchor ?? null);\n\n // Lynx's native <list> only accepts <list-item> children.\n // Skip comment/text anchors to avoid NSInvalidArgumentException.\n if (\n parent.type === 'list'\n && (child.type === '#comment' || child.type === '#text')\n ) {\n return;\n }\n\n // If the anchor is a comment node inside a <list>, walk forward to find\n // the next real sibling so the MT __InsertElementBefore has a valid ref.\n let resolvedAnchor: ShadowElement | null = anchor ?? null;\n if (parent.type === 'list') {\n while (\n resolvedAnchor\n && (resolvedAnchor.type === '#comment'\n || resolvedAnchor.type === '#text')\n ) {\n resolvedAnchor = resolvedAnchor.next;\n }\n }\n\n const anchorId = resolvedAnchor ? resolvedAnchor.id : -1;\n pushOp(OP.INSERT, parent.id, child.id, anchorId);\n scheduleFlush();\n },\n\n remove(child: ShadowElement): void {\n if (child.parent) {\n const parentId = child.parent.id;\n child.parent.removeChild(child);\n pushOp(OP.REMOVE, parentId, child.id);\n scheduleFlush();\n }\n },\n\n patchProp(\n el: ShadowElement,\n key: string,\n _prevValue: unknown,\n nextValue: unknown,\n ): void {\n // Handle main-thread:ref — bind a MainThreadRef to this element\n if (key === 'main-thread:ref') {\n if (nextValue != null) {\n const mtRef = nextValue as MainThreadRef;\n pushOp(OP.SET_MT_REF, el.id, mtRef._wvid);\n }\n scheduleFlush();\n return;\n }\n\n const event = parseEventProp(key);\n\n if (event) {\n // Worklet placeholders ({ _wkltId, _c? }) emitted by @lynx-js/react/transform\n // bypass the BG event-registry path entirely — the MT side dispatches.\n if (event.mainThread && nextValue != null && isWorkletPlaceholder(nextValue)) {\n let elWorklets = sentWorklets.get(el.id);\n const prevId = elWorklets?.get(key);\n if (prevId !== nextValue._wkltId) {\n if (!elWorklets) {\n elWorklets = new Map();\n sentWorklets.set(el.id, elWorklets);\n }\n elWorklets.set(key, nextValue._wkltId);\n const wireCtx: {\n _wkltId: string;\n _c?: Record<string, unknown>;\n _jsFn?: Record<string, unknown>;\n _execId?: number;\n } = {\n _wkltId: nextValue._wkltId,\n };\n if (nextValue._c) wireCtx._c = sanitizeCaptured(nextValue._c);\n if (nextValue._jsFn) wireCtx._jsFn = nextValue._jsFn;\n // Stamp _execId so MT can route runOnBackground dispatches back\n // to the JsFnHandles inside _jsFn / _c.\n registerWorkletCtx(wireCtx);\n pushOp(OP.SET_WORKLET_EVENT, el.id, event.type, event.name, wireCtx);\n scheduleFlush();\n }\n return;\n }\n\n let propSigns = elementEventSigns.get(el.id);\n const nativeKey = `${event.type}:${event.name}`;\n\n if (nextValue != null) {\n const handler = nextValue as (data: unknown) => void;\n\n // Get or create the native event slot for this (element, eventType, eventName).\n let elSlots = nativeEventSlots.get(el.id);\n if (!elSlots) {\n elSlots = new Map();\n nativeEventSlots.set(el.id, elSlots);\n }\n\n let slot = elSlots.get(nativeKey);\n if (!slot) {\n // First handler for this native event — register with Lynx.\n const sign = register((data: unknown) => {\n // Dispatch to all handlers registered for this slot.\n const s = elSlots!.get(nativeKey);\n if (s) {\n for (const h of s.handlers.values()) h(data);\n }\n });\n slot = { sign, handlers: new Map() };\n elSlots.set(nativeKey, slot);\n pushOp(OP.SET_EVENT, el.id, event.type, event.name, sign);\n }\n\n // Add/update this prop's handler in the slot.\n slot.handlers.set(key, handler);\n\n // Track prop→sign for re-render updates.\n if (!propSigns) {\n propSigns = new Map<string, string>();\n elementEventSigns.set(el.id, propSigns);\n }\n propSigns.set(key, slot.sign);\n } else {\n // Handler removed.\n const elSlots = nativeEventSlots.get(el.id);\n const slot = elSlots?.get(nativeKey);\n if (slot) {\n slot.handlers.delete(key);\n if (slot.handlers.size === 0) {\n // No more handlers — unregister from Lynx.\n unregister(slot.sign);\n elSlots!.delete(nativeKey);\n pushOp(OP.REMOVE_EVENT, el.id, event.type, event.name);\n }\n }\n propSigns?.delete(key);\n }\n } else if (key === 'style') {\n const style = nextValue != null && typeof nextValue === 'object'\n ? normalizeStyle(nextValue as Record<string, unknown>)\n : {};\n const effective = el._vShowHidden ? { ...style, display: 'none' } : style;\n // Skip SET_STYLE when structurally unchanged. JSX inline `style={{...}}`\n // creates a fresh object every render but its keys/values typically don't\n // change between renders. Re-emitting SET_STYLE would overwrite any MT\n // worklet's setStyleProperties calls (pink/scale on tap, etc.) on every\n // unrelated signal change. Shallow-equal previous _style suffices since\n // sigx normalises everything to a flat string→string|number map.\n const prev = el._style;\n const sameStyle = prev != null && shallowEqual(prev, effective);\n el._style = style;\n if (!sameStyle) {\n pushOp(OP.SET_STYLE, el.id, effective);\n }\n } else if (key === 'class') {\n el._baseClass = (nextValue as string) ?? '';\n const finalClass = resolveClass(el);\n pushOp(OP.SET_CLASS, el.id, finalClass);\n } else if (key === 'id') {\n pushOp(OP.SET_ID, el.id, nextValue);\n } else {\n pushOp(OP.SET_PROP, el.id, key, nextValue);\n }\n scheduleFlush();\n },\n\n parentNode(node: ShadowElement): ShadowElement | null {\n return node.parent;\n },\n\n nextSibling(node: ShadowElement): ShadowElement | null {\n return node.next;\n },\n\n cloneNode(node: ShadowElement): ShadowElement {\n // Lynx has no native clone — create a new shadow element of the same type\n const el = new ShadowElement(node.type);\n if (node.type === '#text') {\n pushOp(OP.CREATE_TEXT, el.id);\n } else {\n pushOp(OP.CREATE, el.id, node.type);\n }\n scheduleFlush();\n return el;\n },\n};\n","/**\n * Lynx renderer entry -- creates the renderer, defines lynxMount, and\n * registers it as the default mount via setDefaultMount.\n *\n * Ported pattern from packages/runtime-terminal/src/index.ts\n */\nimport { createRenderer, setDefaultMount } from '@sigx/runtime-core/internals';\nimport type { MountFn, AppContext } from '@sigx/runtime-core';\nimport { nodeOps } from './nodeOps';\nimport { flushNow } from './flush';\nimport { installEventPublisher } from './op-queue';\nimport { createPageRoot, type ShadowElement } from './shadow-element';\n\n// Install host-required event stubs (publishEvent / publicComponentEvent)\n// before sigx mounts anything so the first MT → BG dispatch doesn't crash.\ninstallEventPublisher();\n\n// ---------------------------------------------------------------------------\n// Renderer\n// ---------------------------------------------------------------------------\n\nconst renderer = createRenderer<ShadowElement, ShadowElement>(nodeOps);\nexport const { render } = renderer;\n\n// ---------------------------------------------------------------------------\n// lynxMount -- MountFn for Lynx environments\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// HMR mount state — tracked so hot reloads can tear down and re-mount\n// ---------------------------------------------------------------------------\n\nlet _hmrMounted = false;\nlet _hmrRoot: ShadowElement | null = null;\nlet _hmrAppContext: AppContext | undefined = undefined;\n\n/**\n * Mount function for Lynx environments.\n *\n * The page root is a ShadowElement with id=1 — the Main Thread creates the\n * real page element in renderPage() before the BG thread runs. All subsequent\n * ops reference this root by id so the MT can resolve it.\n *\n * On subsequent calls (hot reload), the previous tree is torn down and the\n * Main Thread is signalled to reset before the new tree is mounted.\n *\n * @example\n * ```tsx\n * import '@sigx/lynx-runtime'; // side-effect: registers lynxMount\n * import { defineApp } from '@sigx/sigx';\n *\n * defineApp(<App />).mount();\n * ```\n */\nexport const lynxMount: MountFn = (element, _container, appContext) => {\n // Re-install in case the per-card app instance only became available\n // after this module's top-level executed (timing varies by Lynx host).\n installEventPublisher();\n\n // Hot-reload fallback: if lynxMount is called again (e.g., main.tsx\n // re-executed because a non-component module change bubbled up), the\n // Lynx native engine cannot handle structural tree mutations. Fall\n // back to a full card reload. With component-level HMR active, this\n // path is only reached for non-component changes.\n // See docs/hmr-investigation.md for details.\n if (_hmrMounted && _hmrRoot) {\n console.log('[sigx-hmr] Non-component change — triggering full card reload');\n triggerLiveReload();\n return () => {};\n }\n\n _hmrMounted = true;\n const root = createPageRoot();\n _hmrRoot = root;\n _hmrAppContext = appContext;\n render(element, root, appContext);\n flushNow();\n return () => {\n render(null, root, appContext);\n flushNow();\n _hmrMounted = false;\n _hmrRoot = null;\n };\n};\n\n// ---------------------------------------------------------------------------\n// Register as the default mount -- activated only when this module is imported\n// ---------------------------------------------------------------------------\n\nsetDefaultMount(lynxMount);\n\n// ---------------------------------------------------------------------------\n// Live-reload fallback\n//\n// When a webpack HMR update cannot be applied (module shape changed too\n// drastically), fall back to reloading the entire card bundle — the Lynx\n// equivalent of a browser full-page refresh.\n// ---------------------------------------------------------------------------\n\n/**\n * Trigger a full card reload via the Lynx host. Tries several host APIs\n * in order of preference:\n * 1. `lynxCoreInject.tt.reloadCard()` — standard Lynx host reload\n * 2. `lynx.getNativeApp().callLepusMethod('sigxReloadCard', ...)` — custom\n * reload signal the host can implement\n * If none are available, logs a warning — the developer must manually reload.\n */\nfunction triggerLiveReload(): void {\n try {\n // Option 1: lynxCoreInject.tt.reloadCard (available in some Lynx hosts)\n if (typeof lynxCoreInject !== 'undefined' && lynxCoreInject?.tt) {\n const reloadCard = lynxCoreInject.tt['reloadCard'];\n if (typeof reloadCard === 'function') {\n (reloadCard as () => void)();\n return;\n }\n }\n\n // Option 2: signal via callLepusMethod so the MT can trigger a reload\n if (typeof lynx !== 'undefined') {\n const app = lynx?.getNativeApp?.();\n if (app && typeof app.callLepusMethod === 'function') {\n app.callLepusMethod('sigxReloadCard', {}, () => {});\n return;\n }\n }\n\n console.log(\n '[sigx-hmr] No reload API available. Please manually reload the card.',\n );\n } catch (e) {\n console.log('[sigx-hmr] triggerLiveReload error:', String(e));\n }\n}\n\n// ---------------------------------------------------------------------------\n// Webpack HMR acceptance\n//\n// Accept hot updates to this module. With component-level HMR, most changes\n// are handled by the HMR loader (self-accepting component modules). This\n// catch-all only fires for changes to render.ts itself or its direct deps.\n// ---------------------------------------------------------------------------\n\n// `lynx` and `lynxCoreInject` are declared in src/shims.d.ts.\n\ndeclare var module: { hot?: {\n accept(cb?: (err?: Error) => void): void;\n} } | undefined;\n\nif (typeof module !== 'undefined' && module?.hot) {\n module.hot.accept((err?: Error) => {\n if (err) {\n console.log(\n '[sigx-hmr] Hot update failed, falling back to live reload:',\n String(err),\n );\n triggerLiveReload();\n }\n });\n}\n","import { onUnmounted } from '@sigx/runtime-core';\nimport type { PrimitiveSignal } from '@sigx/reactivity';\n\nimport { MainThreadRef } from '../main-thread-ref';\nimport { pushOp, scheduleFlush } from '../op-queue';\nimport { registerBgSink, unregisterBgSink } from '../animated-bridge';\nimport { OP } from '@sigx/lynx-runtime-internal';\n\n/**\n * Internal envelope shape stored under `MainThreadRef.current`. Wrapping the\n * value in `{ value: T }` (rather than a bare `T`) lets MT worklets mutate\n * the value without breaking ref identity, and leaves room to extend with\n * derived state later (e.g. velocity) without changing the public type.\n */\nexport interface SharedValueState<T> {\n value: T;\n}\n\n/**\n * MT-writeable, BG-readable cross-thread value with sigx reactive tracking.\n *\n * **Not animation-specific.** Animation is one customer (see `@sigx/motion`),\n * gestures is another (`<Draggable translateX={sv} />`), scroll is another\n * (`useScrollViewOffset`). Anywhere fast-or-frequent state lives natively on\n * MT and you want BG to observe it reactively, this is the primitive.\n *\n * **MT side** — inside `'main thread'` worklets, mutate via `sv.current.value\n * = newValue`. The MainThreadRef machinery makes the mutation stick across\n * worklet invocations on the same wvid; the bridge picks up the new value on\n * the next `__FlushElementTree` boundary.\n *\n * **BG side** — read via `sv.value` to get the latest published snapshot.\n * Sigx tracking applies, so an `effect(() => console.log(sv.value))` re-runs\n * whenever an MT publish ingests a new value. Writes from BG are read-only;\n * dev-mode warns to point users back at the MT path.\n *\n * The class extends `MainThreadRef<{value:T}>` so that BG-side serialization\n * (`nodeOps.ts:sanitizeCaptured`) recognizes it as a worklet ref and ships\n * `{_wvid, _initValue}` over the wire to the MT bundle.\n *\n * @example\n * ```tsx\n * const tx = useSharedValue(0);\n *\n * <Draggable translateX={tx} />\n * <text>x = {tx.value}</text> // BG-reactive, updates per drag frame\n * ```\n */\nexport class SharedValue<T = number> extends MainThreadRef<SharedValueState<T>> {\n /** @internal — populated by `useSharedValue`. Reads here back the BG signal. */\n private _bgSignal!: PrimitiveSignal<T>;\n\n constructor(initial: T) {\n super({ value: initial });\n }\n\n /** @internal */\n _bind(sig: PrimitiveSignal<T>): void {\n this._bgSignal = sig;\n }\n\n /**\n * BG-side reactive read. Returns the latest snapshot published by the MT\n * bridge (or the initial value before any publish has occurred).\n */\n get value(): T {\n return this._bgSignal.value as T;\n }\n\n set value(_v: T) {\n // Reach for process via globalThis so the package doesn't pull in\n // @types/node just for a dev-mode warning. NODE_ENV defaults to a\n // non-production string, so the warning fires unless the bundler has\n // explicitly substituted it to 'production'.\n const env = (globalThis as { process?: { env?: Record<string, string | undefined> } })\n .process?.env?.['NODE_ENV'];\n if (env !== 'production') {\n console.warn(\n '[sigx] SharedValue.value is read-only on the BG thread. ' +\n 'Mutate via the MT worklet (sv.current.value = v).',\n );\n }\n }\n}\n\n/**\n * Allocate a `SharedValue<T>` whose MT-side mutations are observable on the\n * BG thread via sigx reactivity. Bind it to a component prop or read its\n * `.value` from a BG `effect`.\n *\n * Lifecycle:\n * - On allocate: pushes `INIT_MT_REF` (creates the MT-side refMap holder)\n * and `REGISTER_AV_BRIDGE` (tells MT to track this wvid in its diff/\n * publish pass). Allocates a BG-side signal mirror keyed by wvid.\n * - On the owning component's unmount: pushes `UNREGISTER_AV_BRIDGE` and\n * `RELEASE_MT_REF`, drops the BG signal.\n */\nexport function useSharedValue<T = number>(initial: T): SharedValue<T> {\n const sv = new SharedValue<T>(initial);\n\n // BG: register the signal mirror under the wvid allocated in super().\n sv._bind(registerBgSink(sv._wvid, initial));\n\n // MT: create the refMap entry (envelope shape) and begin tracking this\n // wvid in the diff/publish pass.\n pushOp(OP.INIT_MT_REF, sv._wvid, sv._initValue);\n pushOp(OP.REGISTER_AV_BRIDGE, sv._wvid, initial);\n scheduleFlush();\n\n onUnmounted(() => {\n pushOp(OP.UNREGISTER_AV_BRIDGE, sv._wvid);\n pushOp(OP.RELEASE_MT_REF, sv._wvid);\n scheduleFlush();\n unregisterBgSink(sv._wvid);\n });\n\n return sv;\n}\n","import { onUnmounted } from '@sigx/runtime-core';\n\nimport { pushOp, scheduleFlush } from '../op-queue';\nimport type { MainThreadRef } from '../main-thread-ref';\nimport type { MainThread } from '../jsx';\nimport { OP } from '@sigx/lynx-runtime-internal';\nimport type { BuiltinMapperName, MapperParams } from '@sigx/lynx-runtime-internal';\n\nimport type { SharedValue } from './shared-value';\n\n// Re-export the public types so users can write\n// import type { BuiltinMapperName } from '@sigx/lynx'\n// without reaching into lynx-runtime-internal directly.\nexport type { BuiltinMapperName, MapperParams };\n\nlet nextBindingId = 1;\n\n/** Reset hook for tests. */\nexport function resetAnimatedStyleBindingIds(): void {\n nextBindingId = 1;\n}\n\n/**\n * Returns the next binding id without incrementing — internal helper used by\n * the `useAnimatedStyle` overloads to share allocation. Not exported.\n */\nfunction allocBindingId(): number {\n return nextBindingId++;\n}\n\n/**\n * Bind a `MainThreadRef`'s element style to a `SharedValue` via a named\n * mapper. The MT-side runtime applies the mapper's output via\n * `setStyleProperties` on every flush boundary where the SharedValue's value changed.\n *\n * The mapper runs on the **Main Thread** with no thread crossing per frame —\n * the only inputs are the SharedValue's value (already on MT) and the `params` shipped\n * once in the registration op. Because mappers are looked up by *name*, the\n * SWC worklet transform doesn't have to capture a function reference (which\n * it can't), so this fits the existing worklet pipeline cleanly.\n *\n * Multiple bindings on the same element compose into a single\n * `setStyleProperties` call per flush. `transform` outputs concatenate in\n * registration order (e.g. `translateX(50px) translateY(20px)`); other style\n * keys merge by last-write-wins. When ANY binding on an element is dirty,\n * ALL of that element's bindings re-run so partial outputs don't drop the\n * unchanged-axis contribution.\n *\n * @example Ghost follower at 0.5×\n * ```tsx\n * const tx = useSharedValue(0);\n * const ghostRef = useMainThreadRef<MainThread.Element | null>(null);\n *\n * useAnimatedStyle(ghostRef, tx, 'translateX', { factor: 0.5 });\n *\n * <Draggable translateX={tx} />\n * <view main-thread:ref={ghostRef} style={...} />\n * ```\n */\nexport function useAnimatedStyle<N extends BuiltinMapperName>(\n elRef: MainThreadRef<MainThread.Element | null>,\n sv: SharedValue<unknown>,\n mapperName: N,\n params?: MapperParams[N],\n): void {\n const bindingId = allocBindingId();\n\n pushOp(\n OP.REGISTER_AV_STYLE_BINDING,\n bindingId,\n elRef._wvid,\n sv._wvid,\n mapperName,\n params ?? null,\n );\n scheduleFlush();\n\n onUnmounted(() => {\n pushOp(OP.UNREGISTER_AV_STYLE_BINDING, bindingId);\n scheduleFlush();\n });\n}\n","/**\n * Cross-thread function invocation primitives for Lynx MTS.\n *\n * - runOnMainThread(fn): BG → MT — invoke a function on the Main Thread\n * - runOnBackground(fn): MT → BG — invoke a function on the Background Thread\n *\n * ## Worklet placeholder semantics (Phase 1b)\n *\n * The `'main thread'` directive inside the function passed to runOnMainThread\n * tells the build pipeline (`@lynx-js/react/transform`) to:\n * 1. Replace the function with a `{ _wkltId }` placeholder in the BG bundle.\n * 2. Emit a `registerWorkletInternal(\"main-thread\", \"<wkltId>\", function(...) { ... })`\n * call into the MT bundle that registers the function in the MT-side worklet\n * registry.\n *\n * At runtime, runOnMainThread receives the placeholder, returns a callable, and\n * each invocation ships `{ wkltId, args }` over `callLepusMethod('sigxRunOnMT')`\n * to the MT bridge installed in `entry-main.ts`.\n *\n * ## Usage\n *\n * ```tsx\n * import { runOnMainThread, runOnBackground } from '@sigx/lynx-runtime';\n *\n * const updateOffset = runOnMainThread((offset: number) => {\n * 'main thread';\n * ref.current?.setStyleProperties({ transform: `translateX(${offset}px)` });\n * });\n * updateOffset(100);\n * ```\n *\n * The `(offset) => { 'main thread'; ... }` source is what the user writes; after\n * the build transform it becomes `{ _wkltId: '...' }` at this call site.\n */\n\n// `lynx` and `lynxCoreInject` are closure-injected — see shims.d.ts\n\nimport { MainThreadRef } from './main-thread-ref';\n\n// ---------------------------------------------------------------------------\n// Worklet placeholder shape (post-transform)\n// ---------------------------------------------------------------------------\n\ninterface WorkletPlaceholder {\n _wkltId: string;\n _c?: Record<string, unknown>;\n}\n\nfunction isWorkletPlaceholder(v: unknown): v is WorkletPlaceholder {\n return typeof v === 'object' && v !== null && '_wkltId' in (v as object);\n}\n\n/**\n * Convert MainThreadRef instances inside `_c` to plain `{ _wvid, _initValue }`\n * placeholders so they survive JSON serialization to MT. Upstream's worklet\n * runtime (`I()` walker in `runtime/worklet-runtime/main.js`) recognizes the\n * shape and resolves it via `lynxWorkletImpl._refImpl._workletRefMap`.\n *\n * Same logic as `nodeOps.ts:sanitizeCaptured`. Kept local to avoid pulling\n * the renderer into this module.\n */\nfunction sanitizeCaptured(captured: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const k in captured) {\n const v = captured[k];\n if (v instanceof MainThreadRef) {\n out[k] = { _wvid: v._wvid, _initValue: v._initValue };\n } else {\n out[k] = v;\n }\n }\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// runOnMainThread — BG → MT\n// ---------------------------------------------------------------------------\n\nexport function runOnMainThread<TArgs extends unknown[]>(\n worklet: WorkletPlaceholder | ((...args: TArgs) => unknown),\n): (...args: TArgs) => Promise<unknown> {\n // The build-time transform replaces the user's function with a placeholder.\n // If we receive an actual function here it means the transform didn't run\n // (test / dev fallback) — we can't ship a function across threads, so we\n // run it locally on BG.\n if (typeof worklet === 'function') {\n return (...args: TArgs): Promise<unknown> => {\n try {\n return Promise.resolve(worklet(...args));\n } catch (e) {\n return Promise.reject(e);\n }\n };\n }\n\n if (!isWorkletPlaceholder(worklet)) {\n throw new Error('runOnMainThread: argument is not a worklet placeholder. Did the build transform run?');\n }\n\n const wkltId = worklet._wkltId;\n // Snapshot _c at placeholder-construction time. Captures are resolved to\n // their identifier values at SWC transform time, so this is the right\n // moment — the user calls runOnMainThread once at setup with the\n // already-captured placeholder.\n const captured = worklet._c ? sanitizeCaptured(worklet._c) : undefined;\n\n return (...args: TArgs): Promise<unknown> => {\n return new Promise<unknown>((resolve) => {\n if (typeof lynx !== 'undefined') {\n const app = lynx?.getNativeApp?.();\n if (app && typeof app.callLepusMethod === 'function') {\n app.callLepusMethod(\n 'sigxRunOnMT',\n { wkltId, args, captured },\n (result: unknown) => resolve(result),\n );\n return;\n }\n }\n\n // Same-thread fallback (testing only)\n const g = globalThis as Record<string, unknown>;\n if (typeof g['sigxRunOnMT'] === 'function') {\n let result: unknown;\n (g['sigxRunOnMT'] as Function)(\n { wkltId, args, captured },\n (r: unknown) => { result = r; },\n );\n resolve(result);\n return;\n }\n\n resolve(undefined);\n });\n };\n}\n\n// ---------------------------------------------------------------------------\n// runOnBackground — MT → BG\n//\n// Delegated to ./run-on-background.ts. The exported `runOnBackground` from\n// that module is a throwing stub: the SWC build transform replaces every\n// `runOnBackground(fn)` call inside a `'main thread'` body with a bridged\n// dispatch (BG: `transformToWorklet(fn)` → JsFnHandle in the worklet ctx;\n// MT: bare `runOnBackground(handle)` resolved against globalThis). If the\n// stub is hit at runtime, the build transform did not run.\n// ---------------------------------------------------------------------------\n\nexport {\n runOnBackground,\n transformToWorklet,\n resetRunOnBackgroundState,\n} from './run-on-background';\n\nexport function resetThreading(): void {\n // Kept for API compatibility with tests that previously reset the local\n // stub. The runOnBackground module now exposes its own reset.\n}\n","/**\n * Native gesture detector — BG-side wrapper around Lynx's\n * `__SetGestureDetector(dom, id, type, config, relationMap)` PAPI.\n *\n * Mirrors the contract from upstream `@lynx-js/react/runtime/lib/gesture/`:\n * - GestureType enum values match `GestureTypeInner`.\n * - relationMap keys: `waitFor`, `simultaneous`, `continueWith`.\n * - COMPOSED gestures are walked client-side and each base is registered\n * with `__SetGestureDetector` separately (the platform receives bases).\n *\n * Public surface:\n * - `Gesture.Pan() / .Tap() / .LongPress() / ...` — chainable builders.\n * - `Gesture.Race(...) / .Simultaneous(...) / .Exclusive(...)` — composers.\n * - `useGestureDetector(elRef, gesture)` — attaches the gesture to the\n * element pointed at by elRef. Op-emit is deferred to `onMounted` so the\n * SET_MT_REF op (pushed during the first JSX render) is applied before\n * the SET_GESTURE_DETECTOR op tries to resolve the workletRefMap entry.\n */\n\nimport { onMounted, onUnmounted } from '@sigx/runtime-core';\nimport { OP, pushOp, scheduleFlush } from '../op-queue';\nimport { registerWorkletCtx } from '../run-on-background';\nimport { MainThreadRef, sanitizeCaptured } from '../main-thread-ref';\n\n// ---------------------------------------------------------------------------\n// Gesture type enum\n// ---------------------------------------------------------------------------\n\nexport const GestureType = {\n COMPOSED: -1,\n PAN: 0,\n FLING: 1,\n DEFAULT: 2,\n TAP: 3,\n LONGPRESS: 4,\n ROTATION: 5,\n PINCH: 6,\n NATIVE: 7,\n} as const;\n\nexport type GestureTypeValue = (typeof GestureType)[keyof typeof GestureType];\n\n// ---------------------------------------------------------------------------\n// Worklet placeholder shape (emitted by @lynx-js/react/transform)\n// ---------------------------------------------------------------------------\n\nexport interface GestureWorklet {\n _wkltId: string;\n _c?: Record<string, unknown>;\n _jsFn?: Record<string, unknown>;\n _execId?: number;\n _workletType?: string;\n}\n\n/**\n * What users write at the source level — a `'main thread'` arrow function.\n * The SWC LEPUS transform replaces it with a `GestureWorklet` placeholder\n * before the BG bundle ships, so by the time the runtime sees the value\n * it's already in placeholder shape. The union lets TypeScript accept the\n * source-level function while the runtime branch treats it as a placeholder.\n */\nexport type GestureCallback =\n | GestureWorklet\n | ((event: never) => void);\n\n// ---------------------------------------------------------------------------\n// Gesture descriptor shapes\n// ---------------------------------------------------------------------------\n\nexport interface BaseGesture {\n __isSerialized: true;\n type: number;\n id: number;\n callbacks: Record<string, GestureWorklet>;\n waitFor: BaseGesture[];\n simultaneousWith: BaseGesture[];\n continueWith: BaseGesture[];\n config?: Record<string, unknown>;\n}\n\nexport interface ComposedGesture {\n __isSerialized: true;\n type: -1;\n gestures: AnyGesture[];\n}\n\nexport type AnyGesture = BaseGesture | ComposedGesture;\n\n// ---------------------------------------------------------------------------\n// Gesture id allocator (global counter — relations refer across components)\n// ---------------------------------------------------------------------------\n\nlet nextGestureId = 1;\n\nexport function resetGestureIdCounter(): void {\n nextGestureId = 1;\n}\n\nfunction allocGestureId(): number {\n return nextGestureId++;\n}\n\n// ---------------------------------------------------------------------------\n// Builder base — uses polymorphic `this` so subclass-specific config setters\n// (`PanBuilder.axis`, `LongPressBuilder.duration`, …) chain off shared\n// callback / relation methods without losing the concrete type.\n// ---------------------------------------------------------------------------\n\nclass GestureBuilderBase {\n protected gesture: BaseGesture;\n\n constructor(type: number) {\n this.gesture = {\n __isSerialized: true,\n type,\n id: allocGestureId(),\n callbacks: {},\n waitFor: [],\n simultaneousWith: [],\n continueWith: [],\n };\n }\n\n protected setConfigKey(key: string, value: unknown): this {\n if (!this.gesture.config) this.gesture.config = {};\n this.gesture.config[key] = value;\n return this;\n }\n\n onBegin(cb: GestureCallback): this {\n this.gesture.callbacks['onBegin'] = cb as GestureWorklet;\n return this;\n }\n onStart(cb: GestureCallback): this {\n this.gesture.callbacks['onStart'] = cb as GestureWorklet;\n return this;\n }\n onUpdate(cb: GestureCallback): this {\n this.gesture.callbacks['onUpdate'] = cb as GestureWorklet;\n return this;\n }\n onEnd(cb: GestureCallback): this {\n this.gesture.callbacks['onEnd'] = cb as GestureWorklet;\n return this;\n }\n onFinalize(cb: GestureCallback): this {\n this.gesture.callbacks['onFinalize'] = cb as GestureWorklet;\n return this;\n }\n\n waitFor(...gestures: BaseGesture[]): this {\n this.gesture.waitFor.push(...gestures);\n return this;\n }\n simultaneousWith(...gestures: BaseGesture[]): this {\n this.gesture.simultaneousWith.push(...gestures);\n return this;\n }\n continueWith(...gestures: BaseGesture[]): this {\n this.gesture.continueWith.push(...gestures);\n return this;\n }\n\n build(): BaseGesture {\n return this.gesture;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Per-type builders\n// ---------------------------------------------------------------------------\n\nclass PanBuilder extends GestureBuilderBase {\n constructor() {\n super(GestureType.PAN);\n }\n axis(a: 'x' | 'y' | 'xy'): this {\n return this.setConfigKey('axis', a);\n }\n minDistance(n: number): this {\n return this.setConfigKey('minDistance', n);\n }\n}\n\nclass FlingBuilder extends GestureBuilderBase {\n constructor() {\n super(GestureType.FLING);\n }\n minVelocity(n: number): this {\n return this.setConfigKey('minVelocity', n);\n }\n direction(d: 'left' | 'right' | 'up' | 'down'): this {\n return this.setConfigKey('direction', d);\n }\n}\n\nclass TapBuilder extends GestureBuilderBase {\n constructor() {\n super(GestureType.TAP);\n }\n numberOfTaps(n: number): this {\n return this.setConfigKey('numberOfTaps', n);\n }\n maxDistance(n: number): this {\n return this.setConfigKey('maxDistance', n);\n }\n maxDuration(ms: number): this {\n return this.setConfigKey('maxDuration', ms);\n }\n}\n\nclass LongPressBuilder extends GestureBuilderBase {\n constructor() {\n super(GestureType.LONGPRESS);\n }\n /**\n * Minimum hold duration in ms before the gesture activates and `onStart`\n * fires. Native iOS handler (`LynxLongPressGestureHandler`) reads the\n * `minDuration` config key — defaults to 500 ms if not set.\n */\n minDuration(ms: number): this {\n return this.setConfigKey('minDuration', ms);\n }\n /**\n * @deprecated alias for `minDuration` kept for source compatibility. The\n * native handler only honours `minDuration`; this method now writes both\n * keys so older call sites keep working until they migrate.\n */\n duration(ms: number): this {\n this.setConfigKey('duration', ms);\n return this.setConfigKey('minDuration', ms);\n }\n maxDistance(n: number): this {\n return this.setConfigKey('maxDistance', n);\n }\n}\n\nclass PinchBuilder extends GestureBuilderBase {\n constructor() {\n super(GestureType.PINCH);\n }\n}\n\nclass RotationBuilder extends GestureBuilderBase {\n constructor() {\n super(GestureType.ROTATION);\n }\n}\n\nclass NativeBuilder extends GestureBuilderBase {\n constructor() {\n super(GestureType.NATIVE);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Compose helpers\n// ---------------------------------------------------------------------------\n\nfunction asBaseGestures(g: AnyGesture | { build(): BaseGesture }): BaseGesture[] {\n const resolved = resolveGesture(g);\n const out: BaseGesture[] = [];\n collectBases(resolved, out);\n return out;\n}\n\nfunction collectBases(g: AnyGesture, out: BaseGesture[]): void {\n if (g.type === GestureType.COMPOSED) {\n for (const sub of (g as ComposedGesture).gestures) collectBases(sub, out);\n return;\n }\n out.push(g as BaseGesture);\n}\n\nfunction resolveGesture(\n g: AnyGesture | { build(): BaseGesture },\n): AnyGesture {\n if (g && typeof (g as { build?: () => BaseGesture }).build === 'function') {\n if ((g as Partial<BaseGesture>).__isSerialized !== true) {\n return (g as { build(): BaseGesture }).build();\n }\n }\n return g as AnyGesture;\n}\n\nfunction makeComposed(gestures: AnyGesture[]): ComposedGesture {\n return {\n __isSerialized: true,\n type: GestureType.COMPOSED,\n gestures,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Public Gesture namespace\n// ---------------------------------------------------------------------------\n\nexport const Gesture = {\n Pan: () => new PanBuilder(),\n Fling: () => new FlingBuilder(),\n Tap: () => new TapBuilder(),\n LongPress: () => new LongPressBuilder(),\n Pinch: () => new PinchBuilder(),\n Rotation: () => new RotationBuilder(),\n Native: () => new NativeBuilder(),\n\n /**\n * Race — first recognizer to claim wins. Sibling bases mutually waitFor\n * each other so the platform's gesture arena resolves the priority.\n */\n Race(...gs: (AnyGesture | { build(): BaseGesture })[]): ComposedGesture {\n const resolved = gs.map(resolveGesture);\n const composed = makeComposed(resolved);\n const bases = asBaseGestures(composed);\n for (const a of bases) {\n for (const b of bases) {\n if (a !== b) a.waitFor.push(b);\n }\n }\n return composed;\n },\n\n /**\n * Simultaneous — all recognizers can fire at once. Sibling bases declare\n * mutual `simultaneousWith`.\n */\n Simultaneous(\n ...gs: (AnyGesture | { build(): BaseGesture })[]\n ): ComposedGesture {\n const resolved = gs.map(resolveGesture);\n const composed = makeComposed(resolved);\n const bases = asBaseGestures(composed);\n for (const a of bases) {\n for (const b of bases) {\n if (a !== b) a.simultaneousWith.push(b);\n }\n }\n return composed;\n },\n\n /**\n * Exclusive — sequential. Later items waitFor all earlier items.\n */\n Exclusive(\n ...gs: (AnyGesture | { build(): BaseGesture })[]\n ): ComposedGesture {\n const resolved = gs.map(resolveGesture);\n const composed = makeComposed(resolved);\n for (let i = 1; i < resolved.length; i++) {\n const laterBases: BaseGesture[] = [];\n collectBases(resolved[i]!, laterBases);\n const earlierBases: BaseGesture[] = [];\n for (let j = 0; j < i; j++) collectBases(resolved[j]!, earlierBases);\n for (const later of laterBases) later.waitFor.push(...earlierBases);\n }\n return composed;\n },\n};\n\n// ---------------------------------------------------------------------------\n// useGestureDetector — attach a gesture (or composed gesture) to an element\n// ---------------------------------------------------------------------------\n\ninterface SerializedRelationMap {\n waitFor: number[];\n simultaneous: number[];\n continueWith: number[];\n}\n\ninterface SerializedConfig {\n callbacks: { name: string; callback: GestureWorklet }[];\n config?: Record<string, unknown>;\n}\n\nfunction appendUniqueBases(\n g: AnyGesture,\n out: BaseGesture[],\n seen: Set<number>,\n): void {\n if (g.type === GestureType.COMPOSED) {\n for (const sub of (g as ComposedGesture).gestures) {\n appendUniqueBases(sub, out, seen);\n }\n return;\n }\n const base = g as BaseGesture;\n if (seen.has(base.id)) return;\n seen.add(base.id);\n out.push(base);\n}\n\nfunction buildSerializedConfig(base: BaseGesture): SerializedConfig {\n const callbacks: { name: string; callback: GestureWorklet }[] = [];\n for (const name in base.callbacks) {\n const cb = base.callbacks[name]!;\n // Preserve every field the SWC transform emits — the platform's gesture\n // arena may rely on `_workletType: 'main-thread'` (and any other markers)\n // being present at __SetGestureDetector time. We had previously stripped\n // to `{_wkltId,_c,_jsFn}`; that lost `_workletType`, and the gesture\n // arena silently ignored the registration on-device. Spread first, then\n // sanitize `_c` for wire-safety (MainThreadRef instances → wire shape).\n const wireCtx: GestureWorklet = { ...cb };\n if (cb._c) wireCtx._c = sanitizeCaptured(cb._c);\n // Stamp _execId via registerWorkletCtx so runOnBackground inside the\n // gesture callback can route MT→BG dispatches back through the same\n // pipeline used by SET_WORKLET_EVENT.\n registerWorkletCtx(wireCtx as Parameters<typeof registerWorkletCtx>[0]);\n callbacks.push({ name, callback: wireCtx });\n }\n const out: SerializedConfig = { callbacks };\n if (base.config) out.config = base.config;\n return out;\n}\n\nexport function useGestureDetector(\n elRef: MainThreadRef<unknown>,\n gesture: AnyGesture | { build(): BaseGesture },\n): void {\n const resolved = resolveGesture(gesture);\n const bases: BaseGesture[] = [];\n appendUniqueBases(resolved, bases, new Set());\n\n if (bases.length === 0) return;\n\n onMounted(() => {\n for (const base of bases) {\n const config = buildSerializedConfig(base);\n const relationMap: SerializedRelationMap = {\n waitFor: base.waitFor.map((g) => g.id),\n simultaneous: base.simultaneousWith.map((g) => g.id),\n continueWith: base.continueWith.map((g) => g.id),\n };\n pushOp(\n OP.SET_GESTURE_DETECTOR,\n elRef._wvid,\n base.id,\n base.type,\n config,\n relationMap,\n );\n }\n scheduleFlush();\n });\n\n onUnmounted(() => {\n for (const base of bases) {\n pushOp(OP.REMOVE_GESTURE_DETECTOR, elRef._wvid, base.id);\n }\n scheduleFlush();\n });\n}\n"],"mappings":"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"}
package/dist/jsx.d.ts CHANGED
@@ -17,7 +17,7 @@
17
17
  * the imports — see lynx-runtime README for details.
18
18
  */
19
19
  import type { Model } from '@sigx/runtime-core';
20
- import type { MainThreadRef } from './main-thread-ref.js';
20
+ import type { MainThreadRef } from './main-thread-ref';
21
21
  export type LynxEventHandler<E = any> = (event: E) => void;
22
22
  export declare namespace MainThread {
23
23
  /** Element handle available in main-thread event handlers via MainThreadRef.current. */
@@ -280,12 +280,26 @@ export interface PageAttributes extends LynxCommonAttributes {
280
280
  children?: any;
281
281
  }
282
282
  export interface SvgAttributes extends LynxCommonAttributes {
283
- children?: any;
284
- /** SVG width */
283
+ /**
284
+ * Raw SVG markup as a string — Lynx's `<svg>` element renders this
285
+ * natively (the engine parses the inline XML). Per the Lynx docs the
286
+ * element does NOT accept JSX/React children; pass everything via
287
+ * `content` or `src`.
288
+ *
289
+ * @example
290
+ * ```tsx
291
+ * <svg content='<svg viewBox="0 0 24 24"><path d="…" fill="currentColor"/></svg>'
292
+ * style={{ width: 24, height: 24 }} />
293
+ * ```
294
+ */
295
+ content?: string;
296
+ /** URL to an external SVG resource (alternative to `content`). */
297
+ src?: string;
298
+ /** SVG width — usually preferable to control sizing via `style`. */
285
299
  width?: number | string;
286
- /** SVG height */
300
+ /** SVG height — usually preferable to control sizing via `style`. */
287
301
  height?: number | string;
288
- /** SVG viewBox */
302
+ /** SVG viewBox — only relevant when `content` is omitted (otherwise the inline `<svg>` carries its own viewBox). */
289
303
  viewBox?: string;
290
304
  }
291
305
  export interface FilterImageAttributes extends LynxCommonAttributes {
@@ -16,7 +16,7 @@
16
16
  * SET_MT_REF op (pushed during the first JSX render) is applied before
17
17
  * the SET_GESTURE_DETECTOR op tries to resolve the workletRefMap entry.
18
18
  */
19
- import { MainThreadRef } from '../main-thread-ref.js';
19
+ import { MainThreadRef } from '../main-thread-ref';
20
20
  export declare const GestureType: {
21
21
  readonly COMPOSED: -1;
22
22
  readonly PAN: 0;
@@ -1,2 +1,2 @@
1
- export { Gesture, GestureType, useGestureDetector, resetGestureIdCounter, } from './gesture-detector.js';
2
- export type { GestureTypeValue, GestureWorklet, GestureCallback, BaseGesture, ComposedGesture, AnyGesture, } from './gesture-detector.js';
1
+ export { Gesture, GestureType, useGestureDetector, resetGestureIdCounter, } from './gesture-detector';
2
+ export type { GestureTypeValue, GestureWorklet, GestureCallback, BaseGesture, ComposedGesture, AnyGesture, } from './gesture-detector';
package/dist/nodeOps.d.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  * sigxPatchUpdate, where ops-apply.ts dispatches them to real PAPI calls.
8
8
  */
9
9
  import type { RendererOptions } from '@sigx/runtime-core/internals';
10
- import { ShadowElement } from './shadow-element.js';
10
+ import { ShadowElement } from './shadow-element';
11
11
  export type LynxNode = ShadowElement;
12
12
  export type LynxElement = ShadowElement;
13
13
  export declare function resolveClass(el: ShadowElement): string;
package/dist/render.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { MountFn } from '@sigx/runtime-core';
2
- import { type ShadowElement } from './shadow-element.js';
2
+ import { type ShadowElement } from './shadow-element';
3
3
  export declare const render: import("@sigx/runtime-core/internals").RootRenderFunction<ShadowElement, ShadowElement>;
4
4
  /**
5
5
  * Mount function for Lynx environments.
@@ -37,5 +37,5 @@ interface WorkletPlaceholder {
37
37
  _c?: Record<string, unknown>;
38
38
  }
39
39
  export declare function runOnMainThread<TArgs extends unknown[]>(worklet: WorkletPlaceholder | ((...args: TArgs) => unknown)): (...args: TArgs) => Promise<unknown>;
40
- export { runOnBackground, transformToWorklet, resetRunOnBackgroundState, } from './run-on-background.js';
40
+ export { runOnBackground, transformToWorklet, resetRunOnBackgroundState, } from './run-on-background';
41
41
  export declare function resetThreading(): void;
package/dist/types.d.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * Mirrors packages/runtime-terminal/src/types.ts exactly.
9
9
  */
10
- import type { ShadowElement } from './shadow-element.js';
10
+ import type { ShadowElement } from './shadow-element';
11
11
  declare module '@sigx/runtime-core' {
12
12
  /** Lynx platform sets ShadowElement as the default element type */
13
13
  interface PlatformTypes {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sigx/lynx-runtime",
3
- "version": "0.2.4",
3
+ "version": "0.4.0",
4
4
  "description": "Lynx renderer for SignalX (background thread)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -41,7 +41,7 @@
41
41
  "dependencies": {
42
42
  "@sigx/runtime-core": "^0.4.3",
43
43
  "@sigx/reactivity": "^0.4.3",
44
- "@sigx/lynx-runtime-internal": "^0.2.4"
44
+ "@sigx/lynx-runtime-internal": "^0.4.0"
45
45
  },
46
46
  "peerDependencies": {
47
47
  "@lynx-js/types": "*"