react-native-gtkx 0.3.0-alpha.1 → 0.4.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/README.md +35 -96
  2. package/dist/adw/index.d.ts +3 -2
  3. package/dist/adw/index.js +24 -6
  4. package/dist/adw/index.js.map +1 -1
  5. package/dist/adw/widgets.generated.d.ts +1 -1
  6. package/dist/adw/widgets.generated.js +2 -2
  7. package/dist/adw/widgets.generated.js.map +1 -1
  8. package/dist/animated/create-animated.d.ts +2 -0
  9. package/dist/animated/create-animated.js +2 -0
  10. package/dist/animated/create-animated.js.map +1 -1
  11. package/dist/animated/event.d.ts +15 -0
  12. package/dist/animated/event.js +80 -0
  13. package/dist/animated/event.js.map +1 -0
  14. package/dist/animated/index.d.ts +4 -0
  15. package/dist/animated/index.js +8 -0
  16. package/dist/animated/index.js.map +1 -1
  17. package/dist/animated/spring.d.ts +10 -0
  18. package/dist/animated/spring.js +20 -6
  19. package/dist/animated/spring.js.map +1 -1
  20. package/dist/animated/timing.d.ts +10 -0
  21. package/dist/animated/timing.js +21 -13
  22. package/dist/animated/timing.js.map +1 -1
  23. package/dist/apis/host.gtkx.js +70 -11
  24. package/dist/apis/host.gtkx.js.map +1 -1
  25. package/dist/common/navigation-stack.d.ts +6 -3
  26. package/dist/common/navigation-stack.js +8 -1
  27. package/dist/common/navigation-stack.js.map +1 -1
  28. package/dist/common/widget.js +2 -11
  29. package/dist/common/widget.js.map +1 -1
  30. package/dist/components/animated.d.ts +1 -0
  31. package/dist/components/animated.js +28 -4
  32. package/dist/components/animated.js.map +1 -1
  33. package/dist/components/app-registry.js +32 -8
  34. package/dist/components/app-registry.js.map +1 -1
  35. package/dist/components/request-animation-frame.d.ts +33 -0
  36. package/dist/components/request-animation-frame.js +83 -0
  37. package/dist/components/request-animation-frame.js.map +1 -0
  38. package/dist/components/scroll-view.js +60 -2
  39. package/dist/components/scroll-view.js.map +1 -1
  40. package/dist/components/virtualized-list.js +1 -4
  41. package/dist/components/virtualized-list.js.map +1 -1
  42. package/dist/dnd/autoscroll.d.ts +44 -0
  43. package/dist/dnd/autoscroll.js +176 -0
  44. package/dist/dnd/autoscroll.js.map +1 -0
  45. package/dist/dnd/grid-order.d.ts +45 -0
  46. package/dist/dnd/grid-order.js +151 -0
  47. package/dist/dnd/grid-order.js.map +1 -0
  48. package/dist/dnd/grid.d.ts +44 -0
  49. package/dist/dnd/grid.js +397 -0
  50. package/dist/dnd/grid.js.map +1 -0
  51. package/dist/dnd/gtk-controllers.d.ts +16 -2
  52. package/dist/dnd/gtk-controllers.js +24 -4
  53. package/dist/dnd/gtk-controllers.js.map +1 -1
  54. package/dist/dnd/index.d.ts +4 -2
  55. package/dist/dnd/index.js +9 -2
  56. package/dist/dnd/index.js.map +1 -1
  57. package/dist/dnd/order-state.d.ts +14 -0
  58. package/dist/dnd/order-state.js +55 -0
  59. package/dist/dnd/order-state.js.map +1 -0
  60. package/dist/dnd/order.d.ts +19 -0
  61. package/dist/dnd/order.js +19 -0
  62. package/dist/dnd/order.js.map +1 -1
  63. package/dist/dnd/sortable.d.ts +29 -4
  64. package/dist/dnd/sortable.js +308 -115
  65. package/dist/dnd/sortable.js.map +1 -1
  66. package/dist/dnd/types.d.ts +236 -14
  67. package/dist/dnd/types.js +30 -0
  68. package/dist/dnd/types.js.map +1 -1
  69. package/dist/gesture-handler-compat/attach-context.d.ts +29 -0
  70. package/dist/gesture-handler-compat/attach-context.js +90 -0
  71. package/dist/gesture-handler-compat/attach-context.js.map +1 -0
  72. package/dist/gesture-handler-compat/detector-runtime.d.ts +14 -3
  73. package/dist/gesture-handler-compat/detector-runtime.js +15 -5
  74. package/dist/gesture-handler-compat/detector-runtime.js.map +1 -1
  75. package/dist/gesture-handler-compat/detector.js +57 -2
  76. package/dist/gesture-handler-compat/detector.js.map +1 -1
  77. package/dist/gesture-handler-compat/gesture-state-manager.d.ts +5 -0
  78. package/dist/gesture-handler-compat/gesture-state-manager.js +77 -0
  79. package/dist/gesture-handler-compat/gesture-state-manager.js.map +1 -0
  80. package/dist/gesture-handler-compat/index.d.ts +2 -2
  81. package/dist/gesture-handler-compat/index.js +38 -33
  82. package/dist/gesture-handler-compat/index.js.map +1 -1
  83. package/dist/gesture-handler-compat/recognizer.d.ts +12 -1
  84. package/dist/gesture-handler-compat/recognizer.js +23 -9
  85. package/dist/gesture-handler-compat/recognizer.js.map +1 -1
  86. package/dist/gesture-handler-compat/tag-registry.d.ts +14 -0
  87. package/dist/gesture-handler-compat/tag-registry.js +19 -0
  88. package/dist/gesture-handler-compat/tag-registry.js.map +1 -0
  89. package/dist/globals/index.d.ts +47 -0
  90. package/dist/globals/index.js +181 -0
  91. package/dist/globals/index.js.map +1 -0
  92. package/dist/globals/install.d.ts +1 -0
  93. package/dist/globals/install.js +14 -0
  94. package/dist/globals/install.js.map +1 -0
  95. package/dist/gtk/index.js +4 -3
  96. package/dist/gtk/index.js.map +1 -1
  97. package/dist/gtkx/bridge/adw-namespace.d.ts +2 -0
  98. package/dist/gtkx/bridge/adw-namespace.js +26 -0
  99. package/dist/gtkx/bridge/adw-namespace.js.map +1 -0
  100. package/dist/gtkx/bridge/adw.d.ts +23 -0
  101. package/dist/gtkx/bridge/adw.js +93 -0
  102. package/dist/gtkx/bridge/adw.js.map +1 -0
  103. package/dist/gtkx/bridge/color-scheme-parse.d.ts +11 -0
  104. package/dist/gtkx/bridge/color-scheme-parse.js +42 -0
  105. package/dist/gtkx/bridge/color-scheme-parse.js.map +1 -0
  106. package/dist/gtkx/bridge/color-scheme-portal.d.ts +24 -0
  107. package/dist/gtkx/bridge/color-scheme-portal.js +172 -0
  108. package/dist/gtkx/bridge/color-scheme-portal.js.map +1 -0
  109. package/dist/gtkx/bridge/core.d.ts +25 -0
  110. package/dist/gtkx/bridge/core.js +69 -0
  111. package/dist/gtkx/bridge/core.js.map +1 -0
  112. package/dist/gtkx/bridge/drag-layer.d.ts +27 -0
  113. package/dist/gtkx/bridge/drag-layer.js +177 -0
  114. package/dist/gtkx/bridge/drag-layer.js.map +1 -0
  115. package/dist/gtkx/bridge/index.d.ts +1 -27
  116. package/dist/gtkx/bridge/index.js +12 -71
  117. package/dist/gtkx/bridge/index.js.map +1 -1
  118. package/dist/gtkx/bridge/view-box.js +1 -1
  119. package/dist/gtkx/bridge/view-box.js.map +1 -1
  120. package/dist/gtkx/bridge/widgets.generated.adw.d.ts +1 -0
  121. package/dist/gtkx/bridge/widgets.generated.adw.js +16 -0
  122. package/dist/gtkx/bridge/widgets.generated.adw.js.map +1 -0
  123. package/dist/gtkx/bridge/widgets.generated.d.ts +0 -1
  124. package/dist/gtkx/bridge/widgets.generated.js +3 -3
  125. package/dist/gtkx/bridge/widgets.generated.js.map +1 -1
  126. package/dist/index.js +20 -0
  127. package/dist/index.js.map +1 -1
  128. package/dist/mcp/bin.js +1 -1
  129. package/dist/mcp/bin.js.map +1 -1
  130. package/dist/mcp/data/generated.d.ts +815 -481
  131. package/dist/mcp/data/generated.js +933 -513
  132. package/dist/mcp/data/generated.js.map +1 -1
  133. package/dist/mcp/resolve.d.ts +2 -2
  134. package/dist/mcp/resolve.js +2 -2
  135. package/dist/mcp/resolve.js.map +1 -1
  136. package/dist/mcp/server.js +2 -2
  137. package/dist/mcp/server.js.map +1 -1
  138. package/dist/metro/index.d.ts +1 -0
  139. package/dist/metro/index.js +12 -0
  140. package/dist/metro/index.js.map +1 -1
  141. package/dist/navigation/sidebar.js +2 -45
  142. package/dist/navigation/sidebar.js.map +1 -1
  143. package/dist/reanimated-compat/animatable-value.d.ts +158 -0
  144. package/dist/reanimated-compat/animatable-value.js +316 -0
  145. package/dist/reanimated-compat/animatable-value.js.map +1 -0
  146. package/dist/reanimated-compat/animation.d.ts +34 -13
  147. package/dist/reanimated-compat/animation.js +97 -25
  148. package/dist/reanimated-compat/animation.js.map +1 -1
  149. package/dist/reanimated-compat/mutable.js +6 -6
  150. package/dist/reanimated-compat/mutable.js.map +1 -1
  151. package/dist/reanimated-compat/updater-animations.js +32 -12
  152. package/dist/reanimated-compat/updater-animations.js.map +1 -1
  153. package/dist/runner/host-dev.js +5 -1
  154. package/dist/runner/host-dev.js.map +1 -1
  155. package/dist/runner/host.js +9 -1
  156. package/dist/runner/host.js.map +1 -1
  157. package/dist/sea/bundle.js +1 -1
  158. package/dist/sea/bundle.js.map +1 -1
  159. package/dist/sea/native-shim.js +3 -2
  160. package/dist/sea/native-shim.js.map +1 -1
  161. package/dist/vite/index.js +130 -0
  162. package/dist/vite/index.js.map +1 -1
  163. package/dist/vitest/index.d.ts +1 -1
  164. package/dist/vitest/index.js +1 -1
  165. package/dist/vitest/index.js.map +1 -1
  166. package/package.json +1 -1
  167. package/dist/gtkx/bridge/theme.d.ts +0 -4
  168. package/dist/gtkx/bridge/theme.js +0 -5
  169. package/dist/gtkx/bridge/theme.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"app-registry.js","sourceRoot":"","sources":["../../src/components/app-registry.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useLayoutEffect, type ComponentType, type ReactNode } from \"react\"\nimport {\n AdwApplicationWindow,\n createRoot,\n Gtk,\n GtkApplication,\n GtkApplicationWindow,\n quit,\n} from \"../gtkx/bridge/index\"\nimport { Root } from \"./root\"\n\ntype ComponentProvider = () => ComponentType<Record<string, unknown>>\n\nconst registry = new Map<string, ComponentProvider>()\n\n// The chrome the running app was started with — read by the navigation\n// dev-mode hint (a HeaderBar page under \"system\" chrome renders a doubled\n// titlebar). null until runApplication ran (test harnesses never set it).\nlet activeChrome: \"system\" | \"content\" | null = null\nexport const getActiveChrome = (): \"system\" | \"content\" | null => activeChrome\n\n// One accelerator binding on the GApplication — the shape GtkApplication's\n// own `actionAccels` prop takes, restated locally so this file needs\n// nothing from @gtkx/react/internal (the \"internal\" subpath is where the\n// real type lives, but nothing else in this package reaches for it).\nexport type ActionAccel = {\n detailedActionName: string\n accels: string[]\n}\n\nexport type RunApplicationParams = {\n initialProps?: Record<string, unknown>\n title?: string\n width?: number\n height?: number\n // Window chrome. \"system\" (default): a GtkApplicationWindow with its own\n // titlebar. \"content\": an AdwApplicationWindow with NO window titlebar —\n // the app's content provides HeaderBars (navigation apps: the page\n // HeaderBar becomes the titlebar, with the window controls in it).\n chrome?: \"system\" | \"content\"\n /**\n * GSimpleAction elements registered on the GApplication itself\n * (\"app.<name>\" — reachable from anywhere, including a Gio.Notification's\n * action buttons, which can only ever target an app-level action).\n *\n * @deprecated Render `<ApplicationActions>` (react-native-gtkx/gtk) inside\n * the app instead. Options render as SIBLINGS of the app tree, so no\n * context of the app's can reach them and the set is fixed for the\n * process's lifetime. Still supported; see docs/api.md.\n */\n applicationActions?: ReactNode\n // Keyboard accelerators bound to actions, at the application level —\n // GtkApplication's own actionAccels prop, unchanged. NOT deprecated with\n // the three ReactNode options below: this is a flat name→keys table with\n // no children and nothing to read from context, and it is deliberately\n // process-wide (an accelerator for an action that is not currently\n // registered simply does nothing). A shortcut that should come and go\n // with a screen is a GtkShortcutController in <WindowControllers>.\n actionAccels?: ActionAccel[]\n /**\n * GSimpleAction elements registered on the window (\"win.<name>\" — what a\n * HeaderBar button's actionName or a GMenu item usually targets).\n *\n * @deprecated Render `<WindowActions>` (react-native-gtkx/gtk) inside the\n * app instead — it can read the app's context and registers/unregisters\n * with the component that declares it. Still supported; see docs/api.md.\n */\n windowActions?: ReactNode\n /**\n * Event controllers attached to the window itself — a\n * GtkShortcutController scoped to the whole window is the common case.\n *\n * @deprecated Render `<WindowControllers>` (react-native-gtkx/gtk) inside\n * the app instead, for the same reasons as `windowActions`. Still\n * supported; see docs/api.md.\n */\n windowControllers?: ReactNode\n // AdwBreakpoint elements, evaluated against the window's own allocated\n // size. Only meaningful under chrome: \"content\" (AdwApplicationWindow) —\n // GtkApplicationWindow has no such concept, so this is ignored (with a\n // dev warning) under the default \"system\" chrome.\n breakpoints?: ReactNode\n}\n\n// RN parity: system \"reduce animations\" hints never auto-stop RN animations\n// (ActivityIndicator keeps spinning; honoring reduce-motion is an explicit\n// opt-in via AccessibilityInfo). Our Animated runs on GLib timers and already\n// ignores the hint, so GTK-internal animations (GtkSpinner, switch slides)\n// must behave the same — otherwise e.g. GNOME under software rendering\n// reports enable-animations=false through the settings portal and spinners\n// freeze while Animated keeps moving.\nlet animationsForced = false\nconst forceEnableAnimations = (): void => {\n if (animationsForced) {\n return\n }\n const settings = Gtk.Settings.getDefault()\n if (!settings) {\n return\n }\n animationsForced = true\n // Application-set GtkSettings values outrank the desktop backend, so a\n // later portal update cannot flip this back.\n settings.gtkEnableAnimations = true\n}\n\n// chrome \"content\": the app component IS the window content (a navigation\n// container whose pages host their own NestedRoots) — no window-level Yoga\n// root in between: the layout root would not allocate foreign GTK children.\nconst ContentChrome = ({\n App,\n initialProps,\n}: {\n App: ComponentType<Record<string, unknown>>\n initialProps: Record<string, unknown>\n}) => {\n useLayoutEffect(() => {\n forceEnableAnimations()\n }, [])\n return <App {...initialProps} />\n}\n\nconst WindowContent = ({\n App,\n initialProps,\n initialWidth,\n initialHeight,\n}: {\n App: ComponentType<Record<string, unknown>>\n initialProps: Record<string, unknown>\n initialWidth: number\n initialHeight: number\n}) => {\n // Settings need an initialized display, and writing them mid-render is\n // unsafe — apply after the window mounts.\n useLayoutEffect(() => {\n forceEnableAnimations()\n }, [])\n\n // The Root is the window's direct child: RnGtkxLayout reports a zero\n // minimum (the window shrinks freely — no ratchet) and adopts the actual\n // content-area allocation as the layout viewport. No scrollable wrapper\n // means a window can never scroll its own root — RN semantics, scrolling\n // stays opt-in via <ScrollView>.\n return (\n <Root\n width={initialWidth}\n height={initialHeight}\n followAllocation\n >\n <App {...initialProps} />\n </Root>\n )\n}\n\nexport const AppRegistry = {\n registerComponent(appKey: string, provider: ComponentProvider): string {\n registry.set(appKey, provider)\n return appKey\n },\n\n getAppKeys(): string[] {\n return [...registry.keys()]\n },\n\n runApplication(appKey: string, params: RunApplicationParams = {}): void {\n const provider = registry.get(appKey)\n if (!provider) {\n throw new Error(\n `AppRegistry: no component registered for \"${appKey}\". ` +\n `Registered: ${[...registry.keys()].join(\", \") || \"(none)\"}`,\n )\n }\n const App = provider()\n const width = params.width ?? 800\n const height = params.height ?? 600\n\n const contentChrome = params.chrome === \"content\"\n activeChrome = contentChrome ? \"content\" : \"system\"\n\n if (\n process.env.NODE_ENV !== \"production\" &&\n params.breakpoints &&\n !contentChrome\n ) {\n console.warn(\n '[react-native-gtkx] breakpoints was passed to runApplication without chrome: \"content\" — GtkApplicationWindow (the \"system\" chrome default) has no breakpoints concept, so it is ignored.',\n )\n }\n\n const content = contentChrome ? (\n <ContentChrome\n App={App}\n initialProps={params.initialProps ?? {}}\n />\n ) : (\n <WindowContent\n App={App}\n initialProps={params.initialProps ?? {}}\n initialWidth={width}\n initialHeight={height}\n />\n )\n\n const AppWindow = () =>\n contentChrome ? (\n <AdwApplicationWindow\n title={params.title ?? appKey}\n defaultWidth={width}\n defaultHeight={height}\n onCloseRequest={quit}\n actions={params.windowActions}\n controllers={params.windowControllers}\n breakpoints={params.breakpoints}\n >\n {content}\n </AdwApplicationWindow>\n ) : (\n <GtkApplicationWindow\n title={params.title ?? appKey}\n defaultWidth={width}\n defaultHeight={height}\n onCloseRequest={quit}\n actions={params.windowActions}\n controllers={params.windowControllers}\n >\n {content}\n </GtkApplicationWindow>\n )\n\n createRoot().render(\n <GtkApplication\n actions={params.applicationActions}\n actionAccels={params.actionAccels}\n >\n <AppWindow />\n </GtkApplication>,\n )\n },\n}\n"]}
1
+ {"version":3,"file":"app-registry.js","sourceRoot":"","sources":["../../src/components/app-registry.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useLayoutEffect, type ComponentType, type ReactNode } from \"react\"\nimport { warnOnce } from \"../style/dev-warning\"\nimport { adwAvailable, requireAdwJsx } from \"../gtkx/bridge/adw\"\nimport {\n createRoot,\n Gtk,\n GtkApplication,\n GtkApplicationWindow,\n quit,\n} from \"../gtkx/bridge/index\"\nimport { Root } from \"./root\"\n\ntype ComponentProvider = () => ComponentType<Record<string, unknown>>\n\nconst registry = new Map<string, ComponentProvider>()\n\n// The chrome the running app was started with — read by the navigation\n// dev-mode hint (a HeaderBar page under \"system\" chrome renders a doubled\n// titlebar). null until runApplication ran (test harnesses never set it).\nlet activeChrome: \"system\" | \"content\" | null = null\nexport const getActiveChrome = (): \"system\" | \"content\" | null => activeChrome\n\n// One accelerator binding on the GApplication — the shape GtkApplication's\n// own `actionAccels` prop takes, restated locally so this file needs\n// nothing from @gtkx/react/internal (the \"internal\" subpath is where the\n// real type lives, but nothing else in this package reaches for it).\nexport type ActionAccel = {\n detailedActionName: string\n accels: string[]\n}\n\nexport type RunApplicationParams = {\n initialProps?: Record<string, unknown>\n title?: string\n width?: number\n height?: number\n // Window chrome. \"system\" (default): a GtkApplicationWindow with its own\n // titlebar. \"content\": an AdwApplicationWindow with NO window titlebar —\n // the app's content provides HeaderBars (navigation apps: the page\n // HeaderBar becomes the titlebar, with the window controls in it).\n // Plain-GTK profile (no \"Adw-1\" in this app's gtkx.config.ts): \"content\"\n // falls back to the same GtkApplicationWindow \"system\" uses — Adw is what\n // makes chrome: \"content\" work, not just this option — see adwAvailable()\n // in .claude/epics/adw-optional/002.md.\n chrome?: \"system\" | \"content\"\n /**\n * GSimpleAction elements registered on the GApplication itself\n * (\"app.<name>\" — reachable from anywhere, including a Gio.Notification's\n * action buttons, which can only ever target an app-level action).\n *\n * @deprecated Render `<ApplicationActions>` (react-native-gtkx/gtk) inside\n * the app instead. Options render as SIBLINGS of the app tree, so no\n * context of the app's can reach them and the set is fixed for the\n * process's lifetime. Still supported; see docs/api.md.\n */\n applicationActions?: ReactNode\n // Keyboard accelerators bound to actions, at the application level —\n // GtkApplication's own actionAccels prop, unchanged. NOT deprecated with\n // the three ReactNode options below: this is a flat name→keys table with\n // no children and nothing to read from context, and it is deliberately\n // process-wide (an accelerator for an action that is not currently\n // registered simply does nothing). A shortcut that should come and go\n // with a screen is a GtkShortcutController in <WindowControllers>.\n actionAccels?: ActionAccel[]\n /**\n * GSimpleAction elements registered on the window (\"win.<name>\" — what a\n * HeaderBar button's actionName or a GMenu item usually targets).\n *\n * @deprecated Render `<WindowActions>` (react-native-gtkx/gtk) inside the\n * app instead — it can read the app's context and registers/unregisters\n * with the component that declares it. Still supported; see docs/api.md.\n */\n windowActions?: ReactNode\n /**\n * Event controllers attached to the window itself — a\n * GtkShortcutController scoped to the whole window is the common case.\n *\n * @deprecated Render `<WindowControllers>` (react-native-gtkx/gtk) inside\n * the app instead, for the same reasons as `windowActions`. Still\n * supported; see docs/api.md.\n */\n windowControllers?: ReactNode\n // AdwBreakpoint elements, evaluated against the window's own allocated\n // size. Only meaningful under chrome: \"content\" WITH Adw actually present\n // (AdwApplicationWindow) — GtkApplicationWindow has no such concept, so\n // this is ignored (with a dev warning) under the default \"system\" chrome,\n // and equally ignored (with a different dev warning, once) when chrome:\n // \"content\" was requested but this app has no \"Adw-1\" declared.\n breakpoints?: ReactNode\n}\n\n// RN parity: system \"reduce animations\" hints never auto-stop RN animations\n// (ActivityIndicator keeps spinning; honoring reduce-motion is an explicit\n// opt-in via AccessibilityInfo). Our Animated runs on GLib timers and already\n// ignores the hint, so GTK-internal animations (GtkSpinner, switch slides)\n// must behave the same — otherwise e.g. GNOME under software rendering\n// reports enable-animations=false through the settings portal and spinners\n// freeze while Animated keeps moving.\nlet animationsForced = false\nconst forceEnableAnimations = (): void => {\n if (animationsForced) {\n return\n }\n const settings = Gtk.Settings.getDefault()\n if (!settings) {\n return\n }\n animationsForced = true\n // Application-set GtkSettings values outrank the desktop backend, so a\n // later portal update cannot flip this back.\n settings.gtkEnableAnimations = true\n}\n\n// chrome \"content\": the app component IS the window content (a navigation\n// container whose pages host their own NestedRoots) — no window-level Yoga\n// root in between: the layout root would not allocate foreign GTK children.\nconst ContentChrome = ({\n App,\n initialProps,\n}: {\n App: ComponentType<Record<string, unknown>>\n initialProps: Record<string, unknown>\n}) => {\n useLayoutEffect(() => {\n forceEnableAnimations()\n }, [])\n return <App {...initialProps} />\n}\n\nconst WindowContent = ({\n App,\n initialProps,\n initialWidth,\n initialHeight,\n}: {\n App: ComponentType<Record<string, unknown>>\n initialProps: Record<string, unknown>\n initialWidth: number\n initialHeight: number\n}) => {\n // Settings need an initialized display, and writing them mid-render is\n // unsafe — apply after the window mounts.\n useLayoutEffect(() => {\n forceEnableAnimations()\n }, [])\n\n // The Root is the window's direct child: RnGtkxLayout reports a zero\n // minimum (the window shrinks freely — no ratchet) and adopts the actual\n // content-area allocation as the layout viewport. No scrollable wrapper\n // means a window can never scroll its own root — RN semantics, scrolling\n // stays opt-in via <ScrollView>.\n return (\n <Root\n width={initialWidth}\n height={initialHeight}\n followAllocation\n >\n <App {...initialProps} />\n </Root>\n )\n}\n\nexport const AppRegistry = {\n registerComponent(appKey: string, provider: ComponentProvider): string {\n registry.set(appKey, provider)\n return appKey\n },\n\n getAppKeys(): string[] {\n return [...registry.keys()]\n },\n\n runApplication(appKey: string, params: RunApplicationParams = {}): void {\n const provider = registry.get(appKey)\n if (!provider) {\n throw new Error(\n `AppRegistry: no component registered for \"${appKey}\". ` +\n `Registered: ${[...registry.keys()].join(\", \") || \"(none)\"}`,\n )\n }\n const App = provider()\n const width = params.width ?? 800\n const height = params.height ?? 600\n\n const contentChrome = params.chrome === \"content\"\n // What chrome: \"content\" actually gets: AdwApplicationWindow needs Adw\n // itself, so a plain-GTK app (no \"Adw-1\" in gtkx.config.ts) falls back to\n // exactly the \"system\" chrome path — same window type, same content\n // wrapper — rather than throwing, per\n // .claude/epics/adw-optional/002.md. The Adw profile is unaffected:\n // adwAvailable() is true there, so useAdwChrome === contentChrome always,\n // and every branch below takes the same route it did before this probe\n // existed.\n const useAdwChrome = contentChrome && adwAvailable()\n activeChrome = useAdwChrome ? \"content\" : \"system\"\n\n if (process.env.NODE_ENV !== \"production\" && params.breakpoints) {\n if (!contentChrome) {\n console.warn(\n '[react-native-gtkx] breakpoints was passed to runApplication without chrome: \"content\" — GtkApplicationWindow (the \"system\" chrome default) has no breakpoints concept, so it is ignored.',\n )\n } else if (!useAdwChrome) {\n // Once, not every run: contentChrome apps in a long-lived dev loop\n // (Fast Refresh) would otherwise re-warn on every reload.\n warnOnce(\n \"app-registry:breakpoints-no-adw\",\n '[react-native-gtkx] breakpoints was passed to runApplication with chrome: \"content\", but this app has no \"Adw-1\" in its gtkx.config.ts `libraries` — falling back to GtkApplicationWindow, which has no breakpoints concept, so it is ignored.',\n )\n }\n }\n\n const content = useAdwChrome ? (\n <ContentChrome\n App={App}\n initialProps={params.initialProps ?? {}}\n />\n ) : (\n <WindowContent\n App={App}\n initialProps={params.initialProps ?? {}}\n initialWidth={width}\n initialHeight={height}\n />\n )\n\n // Adw is reached ONLY here, and only when chrome: \"content\" was actually\n // requested AND this app's codegen store actually has it — chrome:\n // \"system\" (the default), and a plain-GTK app's chrome: \"content\", never\n // touch it, which is what lets an app with no Adw-1 declared run at all\n // (see .claude/epics/adw-optional/001.md and 002.md for the fallback).\n const AppWindow = () =>\n useAdwChrome ? (\n (() => {\n const { AdwApplicationWindow } = requireAdwJsx('chrome: \"content\"')\n return (\n <AdwApplicationWindow\n title={params.title ?? appKey}\n defaultWidth={width}\n defaultHeight={height}\n onCloseRequest={quit}\n actions={params.windowActions}\n controllers={params.windowControllers}\n breakpoints={params.breakpoints}\n >\n {content}\n </AdwApplicationWindow>\n )\n })()\n ) : (\n <GtkApplicationWindow\n title={params.title ?? appKey}\n defaultWidth={width}\n defaultHeight={height}\n onCloseRequest={quit}\n actions={params.windowActions}\n controllers={params.windowControllers}\n >\n {content}\n </GtkApplicationWindow>\n )\n\n createRoot().render(\n <GtkApplication\n actions={params.applicationActions}\n actionAccels={params.actionAccels}\n >\n <AppWindow />\n </GtkApplication>,\n )\n },\n}\n"]}
@@ -0,0 +1,33 @@
1
+ import type { FrameScheduler } from "../animated/index";
2
+ declare global {
3
+ function requestAnimationFrame(callback: (time: number) => void): number;
4
+ function cancelAnimationFrame(handle: number | null | undefined): void;
5
+ }
6
+ export type RequestAnimationFrame = (callback: (time: number) => void) => number;
7
+ export type CancelAnimationFrame = (handle: number | null | undefined) => void;
8
+ export type RequestAnimationFramePair = {
9
+ requestAnimationFrame: RequestAnimationFrame;
10
+ cancelAnimationFrame: CancelAnimationFrame;
11
+ };
12
+ /**
13
+ * Pure factory over an injected `FrameScheduler` — the part a test drives
14
+ * directly, without touching `globalThis`. Every request made before a
15
+ * batch flushes lands in that SAME batch and gets the SAME timestamp
16
+ * (rAF's usual "one paint, one time" contract); a request made from inside
17
+ * a running callback is queued fresh and therefore always lands one batch
18
+ * later, because the batch already in flight was taken off `pending` before
19
+ * the first callback in it ran.
20
+ */
21
+ export declare const createRequestAnimationFrame: (scheduler: FrameScheduler) => RequestAnimationFramePair;
22
+ /**
23
+ * Installs requestAnimationFrame/cancelAnimationFrame as globals, running
24
+ * the batch above off `scheduler`. Called once from the package entry
25
+ * (src/index.ts) with the real `glibScheduler` — every app importing
26
+ * "react-native" (aliased here) pulls it in before any of its own code
27
+ * runs, on both toolchains, the same way InitializeCore would have.
28
+ * Idempotent, because `installed` is module state: re-importing the entry
29
+ * module a second time in the same process must not orphan whatever the
30
+ * first install has already queued behind a fresh, disconnected id
31
+ * namespace.
32
+ */
33
+ export declare const installGlobalRequestAnimationFrame: (scheduler: FrameScheduler) => void;
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Pure factory over an injected `FrameScheduler` — the part a test drives
3
+ * directly, without touching `globalThis`. Every request made before a
4
+ * batch flushes lands in that SAME batch and gets the SAME timestamp
5
+ * (rAF's usual "one paint, one time" contract); a request made from inside
6
+ * a running callback is queued fresh and therefore always lands one batch
7
+ * later, because the batch already in flight was taken off `pending` before
8
+ * the first callback in it ran.
9
+ */
10
+ export const createRequestAnimationFrame = (scheduler) => {
11
+ let nextHandle = 1;
12
+ let pending = new Map();
13
+ let unbook = null;
14
+ const flush = (time) => {
15
+ unbook = null;
16
+ // Snapshot-then-clear: a callback that re-requests itself — every
17
+ // rAF-driven animation loop does exactly that — must land in the batch
18
+ // that starts empty again below, not in the one currently running.
19
+ const batch = pending;
20
+ pending = new Map();
21
+ for (const callback of batch.values()) {
22
+ try {
23
+ callback(time);
24
+ }
25
+ catch (error) {
26
+ // One throwing callback does not take its siblings down with it:
27
+ // browsers report the exception and keep running the rest of the
28
+ // batch, and RN's own JSTimers wraps each callback in its own
29
+ // try/catch for the same reason. See docs/api.md.
30
+ console.error("[react-native-gtkx] requestAnimationFrame callback threw:", error);
31
+ }
32
+ }
33
+ };
34
+ const requestAnimationFrame = (callback) => {
35
+ const handle = nextHandle++;
36
+ pending.set(handle, callback);
37
+ // Only the FIRST request in an otherwise-empty queue books a frame —
38
+ // every other one rides the booking already pending, so N calls in the
39
+ // same tick cost one scheduler subscription, not N.
40
+ if (unbook === null) {
41
+ unbook = scheduler.schedule(flush);
42
+ }
43
+ return handle;
44
+ };
45
+ const cancelAnimationFrame = (handle) => {
46
+ // `null`/`undefined` — an unset ref, the common cleanup shape — is a
47
+ // silent no-op, the same as an unknown or already-delivered handle.
48
+ if (handle == null) {
49
+ return;
50
+ }
51
+ pending.delete(handle);
52
+ // No idle tick once the last pending callback is gone — mirrors
53
+ // animated/frame-loop.ts's stop(), same reason: a booking held for
54
+ // nothing is a tick nobody asked for.
55
+ if (pending.size === 0 && unbook !== null) {
56
+ unbook();
57
+ unbook = null;
58
+ }
59
+ };
60
+ return { requestAnimationFrame, cancelAnimationFrame };
61
+ };
62
+ let installed = false;
63
+ /**
64
+ * Installs requestAnimationFrame/cancelAnimationFrame as globals, running
65
+ * the batch above off `scheduler`. Called once from the package entry
66
+ * (src/index.ts) with the real `glibScheduler` — every app importing
67
+ * "react-native" (aliased here) pulls it in before any of its own code
68
+ * runs, on both toolchains, the same way InitializeCore would have.
69
+ * Idempotent, because `installed` is module state: re-importing the entry
70
+ * module a second time in the same process must not orphan whatever the
71
+ * first install has already queued behind a fresh, disconnected id
72
+ * namespace.
73
+ */
74
+ export const installGlobalRequestAnimationFrame = (scheduler) => {
75
+ if (installed) {
76
+ return;
77
+ }
78
+ installed = true;
79
+ const pair = createRequestAnimationFrame(scheduler);
80
+ globalThis.requestAnimationFrame = pair.requestAnimationFrame;
81
+ globalThis.cancelAnimationFrame = pair.cancelAnimationFrame;
82
+ };
83
+ //# sourceMappingURL=request-animation-frame.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-animation-frame.js","sourceRoot":"","sources":["../../src/components/request-animation-frame.ts"],"names":[],"mappings":"AAyDA;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CACzC,SAAyB,EACE,EAAE;IAC7B,IAAI,UAAU,GAAG,CAAC,CAAA;IAClB,IAAI,OAAO,GAAG,IAAI,GAAG,EAAkC,CAAA;IACvD,IAAI,MAAM,GAAwB,IAAI,CAAA;IAEtC,MAAM,KAAK,GAAG,CAAC,IAAY,EAAQ,EAAE;QACnC,MAAM,GAAG,IAAI,CAAA;QACb,kEAAkE;QAClE,uEAAuE;QACvE,mEAAmE;QACnE,MAAM,KAAK,GAAG,OAAO,CAAA;QACrB,OAAO,GAAG,IAAI,GAAG,EAAE,CAAA;QACnB,KAAK,MAAM,QAAQ,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YACtC,IAAI,CAAC;gBACH,QAAQ,CAAC,IAAI,CAAC,CAAA;YAChB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,iEAAiE;gBACjE,iEAAiE;gBACjE,8DAA8D;gBAC9D,kDAAkD;gBAClD,OAAO,CAAC,KAAK,CACX,2DAA2D,EAC3D,KAAK,CACN,CAAA;YACH,CAAC;QACH,CAAC;IACH,CAAC,CAAA;IAED,MAAM,qBAAqB,GAA0B,CAAC,QAAQ,EAAE,EAAE;QAChE,MAAM,MAAM,GAAG,UAAU,EAAE,CAAA;QAC3B,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;QAC7B,qEAAqE;QACrE,uEAAuE;QACvE,oDAAoD;QACpD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACpB,MAAM,GAAG,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;QACpC,CAAC;QACD,OAAO,MAAM,CAAA;IACf,CAAC,CAAA;IAED,MAAM,oBAAoB,GAAyB,CAAC,MAAM,EAAE,EAAE;QAC5D,qEAAqE;QACrE,oEAAoE;QACpE,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;YACnB,OAAM;QACR,CAAC;QACD,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QACtB,gEAAgE;QAChE,mEAAmE;QACnE,sCAAsC;QACtC,IAAI,OAAO,CAAC,IAAI,KAAK,CAAC,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YAC1C,MAAM,EAAE,CAAA;YACR,MAAM,GAAG,IAAI,CAAA;QACf,CAAC;IACH,CAAC,CAAA;IAED,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,CAAA;AACxD,CAAC,CAAA;AAED,IAAI,SAAS,GAAG,KAAK,CAAA;AAErB;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,kCAAkC,GAAG,CAChD,SAAyB,EACnB,EAAE;IACR,IAAI,SAAS,EAAE,CAAC;QACd,OAAM;IACR,CAAC;IACD,SAAS,GAAG,IAAI,CAAA;IAChB,MAAM,IAAI,GAAG,2BAA2B,CAAC,SAAS,CAAC,CAAA;IACnD,UAAU,CAAC,qBAAqB,GAAG,IAAI,CAAC,qBAAqB,CAAA;IAC7D,UAAU,CAAC,oBAAoB,GAAG,IAAI,CAAC,oBAAoB,CAAA;AAC7D,CAAC,CAAA","sourcesContent":["// requestAnimationFrame/cancelAnimationFrame as GLOBALS — RN provides both\n// from its own bootstrap on every platform (InitializeCore's JSTimers); this\n// one dropped that bootstrap entirely (../metro/index.ts: \"the runtime\n// environment IS Node, the host provides everything\"), which held for\n// everything except this one browser-ism library code reaches for directly.\n// docs/research/upstream-libraries.md's third experiment is what found the\n// gap: react-native-sortables crashes the whole process at mount on a bare\n// `requestAnimationFrame`, a `ReferenceError` — Node has no such global,\n// browsers and RN both do.\n//\n// Semantics match the browser spec's \"run the animation frame callbacks\"\n// algorithm (which is also what RN's own JSTimers mirrors): an id comes\n// back; a callback receives a monotonic, high-resolution timestamp in\n// milliseconds; a callback booked WHILE a batch is running lands on the\n// frame AFTER that one — the spec snapshots the callback list before\n// invoking it, so same-frame recursion never happens; cancelling before the\n// frame fires is silent; and one callback throwing is reported and does NOT\n// stop its siblings in the same batch (the spec's \"invoke callback\" for each\n// entry, un-nested — one throw ends one iteration, not the loop).\n//\n// PURE ON PURPOSE — no import of frame-scheduler.ts (and therefore no\n// @gtkx/* bridge, no GLib): both `createRequestAnimationFrame` and\n// `installGlobalRequestAnimationFrame` below take a `FrameScheduler`\n// argument rather than reaching for `glibScheduler` themselves, exactly the\n// split `animated/frame-loop.ts` (pure) already draws against\n// `components/animated.tsx` (the impure thing that wires it to the real\n// clock). That is what lets this file's own scheduling semantics be unit\n// tested off Linux, with no codegen store and no GTK — the caller (this\n// package's entry, src/index.ts) is what supplies the real\n// `glibScheduler`, the one frame driver on this platform (see\n// frame-scheduler.ts's header for why nothing else may add a second one).\nimport type { FrameScheduler } from \"../animated/index\"\n\n// No \"dom\" lib in this package's tsconfig (lib: [\"esnext\"]), so these are\n// stated by hand — matching react-native's OWN ambient declaration\n// (node_modules/react-native/src/types/globals.d.ts) exactly, parameter for\n// parameter, rather than inventing a narrower one: that file is what\n// `@react-navigation/native`'s types pull into this program transitively\n// (through its own `react-native` import), so a mismatched signature here\n// does not stay private — it becomes a real overload conflict the moment\n// both are ambient in the same program. `cancelAnimationFrame` accepting\n// `null | undefined` is not padding: it is what makes\n// `cancelAnimationFrame(handleRef.current)` — a ref that starts unset — a\n// no-op that also type-checks, the common cleanup shape in a `useEffect`.\ndeclare global {\n function requestAnimationFrame(callback: (time: number) => void): number\n function cancelAnimationFrame(handle: number | null | undefined): void\n}\n\nexport type RequestAnimationFrame = (callback: (time: number) => void) => number\nexport type CancelAnimationFrame = (handle: number | null | undefined) => void\n\nexport type RequestAnimationFramePair = {\n requestAnimationFrame: RequestAnimationFrame\n cancelAnimationFrame: CancelAnimationFrame\n}\n\n/**\n * Pure factory over an injected `FrameScheduler` — the part a test drives\n * directly, without touching `globalThis`. Every request made before a\n * batch flushes lands in that SAME batch and gets the SAME timestamp\n * (rAF's usual \"one paint, one time\" contract); a request made from inside\n * a running callback is queued fresh and therefore always lands one batch\n * later, because the batch already in flight was taken off `pending` before\n * the first callback in it ran.\n */\nexport const createRequestAnimationFrame = (\n scheduler: FrameScheduler,\n): RequestAnimationFramePair => {\n let nextHandle = 1\n let pending = new Map<number, (time: number) => void>()\n let unbook: (() => void) | null = null\n\n const flush = (time: number): void => {\n unbook = null\n // Snapshot-then-clear: a callback that re-requests itself — every\n // rAF-driven animation loop does exactly that — must land in the batch\n // that starts empty again below, not in the one currently running.\n const batch = pending\n pending = new Map()\n for (const callback of batch.values()) {\n try {\n callback(time)\n } catch (error) {\n // One throwing callback does not take its siblings down with it:\n // browsers report the exception and keep running the rest of the\n // batch, and RN's own JSTimers wraps each callback in its own\n // try/catch for the same reason. See docs/api.md.\n console.error(\n \"[react-native-gtkx] requestAnimationFrame callback threw:\",\n error,\n )\n }\n }\n }\n\n const requestAnimationFrame: RequestAnimationFrame = (callback) => {\n const handle = nextHandle++\n pending.set(handle, callback)\n // Only the FIRST request in an otherwise-empty queue books a frame —\n // every other one rides the booking already pending, so N calls in the\n // same tick cost one scheduler subscription, not N.\n if (unbook === null) {\n unbook = scheduler.schedule(flush)\n }\n return handle\n }\n\n const cancelAnimationFrame: CancelAnimationFrame = (handle) => {\n // `null`/`undefined` — an unset ref, the common cleanup shape — is a\n // silent no-op, the same as an unknown or already-delivered handle.\n if (handle == null) {\n return\n }\n pending.delete(handle)\n // No idle tick once the last pending callback is gone — mirrors\n // animated/frame-loop.ts's stop(), same reason: a booking held for\n // nothing is a tick nobody asked for.\n if (pending.size === 0 && unbook !== null) {\n unbook()\n unbook = null\n }\n }\n\n return { requestAnimationFrame, cancelAnimationFrame }\n}\n\nlet installed = false\n\n/**\n * Installs requestAnimationFrame/cancelAnimationFrame as globals, running\n * the batch above off `scheduler`. Called once from the package entry\n * (src/index.ts) with the real `glibScheduler` — every app importing\n * \"react-native\" (aliased here) pulls it in before any of its own code\n * runs, on both toolchains, the same way InitializeCore would have.\n * Idempotent, because `installed` is module state: re-importing the entry\n * module a second time in the same process must not orphan whatever the\n * first install has already queued behind a fresh, disconnected id\n * namespace.\n */\nexport const installGlobalRequestAnimationFrame = (\n scheduler: FrameScheduler,\n): void => {\n if (installed) {\n return\n }\n installed = true\n const pair = createRequestAnimationFrame(scheduler)\n globalThis.requestAnimationFrame = pair.requestAnimationFrame\n globalThis.cancelAnimationFrame = pair.cancelAnimationFrame\n}\n"]}
@@ -157,6 +157,55 @@ export const ScrollView = forwardRef(({ style, contentContainerStyle, horizontal
157
157
  // view against, and the widget whose own kinetics a `Gesture.Native()`
158
158
  // over this scrollable is talking about.
159
159
  useResponder(outerRef, responderProps);
160
+ // `scrollTo`/`scrollToEnd` can run before GTK's OWN widget-level
161
+ // measure/allocate has caught up to content the ENGINE already
162
+ // committed — Yoga's flush is a same-microtask JS computation, while a
163
+ // GtkScrolledWindow only learns its child's real size on its next native
164
+ // allocate, which is queued (a frame away) rather than synchronous.
165
+ // `Gtk.Adjustment.setValue()` clamps to ITS OWN `upper`/`page-size`, so a
166
+ // target beyond a still-stale (freshly mounted: 0/0) range is silently
167
+ // absorbed as a no-op — the value does not actually change, so
168
+ // `value-changed` never fires and the scroll this call was supposed to
169
+ // produce is dropped with no event at all. Reproduced deterministically
170
+ // by calling `scrollTo` immediately after `render()` with no extra
171
+ // settling tick: the adjustment reads upper=0/page=0 while
172
+ // `contentNode`/`outerNode` already have the real (correct) rect.
173
+ //
174
+ // The engine's own rects are the fix: they commit before GTK's real
175
+ // geometry ever could (same tick vs. next frame), so reconfiguring the
176
+ // adjustment's range from them — right before setting the value — closes
177
+ // the gap using truth this component already has in hand. Only these two
178
+ // rare, explicitly-invoked entry points pay for it; the per-event
179
+ // `onAdjustment`/`readScrollEvent` path (docs/research/scroll-phases.md)
180
+ // is untouched.
181
+ const syncAdjustmentRange = (adjustment, axis) => {
182
+ const content = contentNode.getRect();
183
+ const viewport = outerNode.getRect();
184
+ if (!content || !viewport) {
185
+ return;
186
+ }
187
+ const pageSize = viewport[axis];
188
+ // A viewport taller/wider than its content (a short list in a tall
189
+ // ScrollView — routine, not an edge case) must still satisfy GTK's own
190
+ // `gtk_adjustment_configure` precondition (`lower + page_size <=
191
+ // upper`): clamping `upper` up to at least `pageSize` is the standard
192
+ // "nothing to scroll" range, exactly like a GtkScrollbar whose thumb
193
+ // fills the whole trough. Without this, a page size bigger than the
194
+ // content invalidates every `configure()` call — GTK logs a
195
+ // Gtk-CRITICAL and, per `gtk_adjustment_configure`'s own
196
+ // `g_return_if_fail`, leaves the adjustment UNCHANGED, so the
197
+ // `getUpper()`/`getPageSize()` guard above never starts matching and a
198
+ // caller retrying every frame (`autoscroll.tsx`'s tick callback, while
199
+ // active) reissues the same invalid call on every tick for as long as
200
+ // it stays active — a burst of thousands of identical criticals from
201
+ // one stuck loop rather than one-off noise.
202
+ const upper = Math.max(content[axis], pageSize);
203
+ if (adjustment.getUpper() === upper &&
204
+ adjustment.getPageSize() === pageSize) {
205
+ return;
206
+ }
207
+ adjustment.configure(adjustment.getValue(), 0, upper, adjustment.getStepIncrement(), adjustment.getPageIncrement(), pageSize);
208
+ };
160
209
  useImperativeHandle(handleRef, () => {
161
210
  const handle = {
162
211
  ...createMeasureHandle(outerRef, outerNode),
@@ -166,10 +215,18 @@ export const ScrollView = forwardRef(({ style, contentContainerStyle, horizontal
166
215
  return;
167
216
  }
168
217
  if (y !== undefined) {
169
- widget.getVadjustment()?.setValue(y);
218
+ const vadjustment = widget.getVadjustment();
219
+ if (vadjustment) {
220
+ syncAdjustmentRange(vadjustment, "height");
221
+ vadjustment.setValue(y);
222
+ }
170
223
  }
171
224
  if (x !== undefined) {
172
- widget.getHadjustment()?.setValue(x);
225
+ const hadjustment = widget.getHadjustment();
226
+ if (hadjustment) {
227
+ syncAdjustmentRange(hadjustment, "width");
228
+ hadjustment.setValue(x);
229
+ }
173
230
  }
174
231
  },
175
232
  scrollToEnd() {
@@ -178,6 +235,7 @@ export const ScrollView = forwardRef(({ style, contentContainerStyle, horizontal
178
235
  ? widget?.getHadjustment()
179
236
  : widget?.getVadjustment();
180
237
  if (adjustment) {
238
+ syncAdjustmentRange(adjustment, horizontal ? "width" : "height");
181
239
  adjustment.setValue(adjustment.getUpper() - adjustment.getPageSize());
182
240
  }
183
241
  },
@@ -1 +1 @@
1
- {"version":3,"file":"scroll-view.js","sourceRoot":"","sources":["../../src/components/scroll-view.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n Children,\n createContext,\n forwardRef,\n useContext,\n useImperativeHandle,\n useLayoutEffect,\n useRef,\n useState,\n type ReactNode,\n} from \"react\"\nimport { splitStyle, StyleSheet } from \"../style/index\"\nimport type { StyleProp } from \"../contracts\"\nimport {\n Gdk,\n Gtk,\n GtkBox,\n GtkScrolledWindow,\n queueAllocate,\n queueResize,\n useSignal,\n} from \"../gtkx/bridge/index\"\nimport {\n ensurePerfReporter,\n perfAddTime,\n perfCount,\n perfEnabled,\n perfFrameTick,\n perfNow,\n} from \"../perf\"\nimport type { ResponderProps } from \"../responder/types\"\nimport { useResponder } from \"../responder/use-responder\"\nimport { HostNodeContext } from \"./host-node\"\nimport {\n createMeasureHandle,\n registerHandleWidget,\n type MeasureHandle,\n} from \"./measure\"\nimport { deferDuringAllocate, setStoredOffset } from \"./rect-store\"\nimport { scrollPhaseSink, type ScrollPhase } from \"./scroll-phase\"\nimport {\n useLayoutChild,\n useRnContainer,\n type LayoutEvent,\n} from \"./use-layout-child\"\nimport { createWheelScrollSession } from \"./wheel-scroll-session\"\n\n/**\n * The scroll half on its own, because the windowed list core builds its own\n * handle on top of it and is a COMPOSITE — it owns no widget, so it has no\n * honest geometry to report and does not claim any.\n */\nexport type ScrollHandle = {\n scrollTo(options: { x?: number; y?: number; animated?: boolean }): void\n scrollToEnd(options?: { animated?: boolean }): void\n}\n\n// RN's ScrollView ref carries the geometry methods as well as the scroll\n// ones — it is a host component, and every host component has them. Here the\n// composition also carries the widget: `registerHandleWidget` below is what\n// lets `measureLayout(scrollViewRef, …)` resolve, and what lets\n// `Animated.ScrollView` find the scrolled window to write a transform to.\nexport type ScrollViewHandle = MeasureHandle & ScrollHandle\n\nexport type ScrollEvent = {\n nativeEvent: {\n contentOffset: { x: number; y: number }\n contentSize: { width: number; height: number }\n // RN's viewport size. GTK's adjustments call it the page size, which is\n // the same thing: the extent the scrolled window shows at once. Without\n // it nothing can compute \"how far from the end am I\" — autoscroll during\n // a drag, custom end-reached logic — which is why RN carries it.\n layoutMeasurement: { width: number; height: number }\n }\n}\n\n/**\n * `ResponderProps` because a `ScrollView` is a HOST component in RN, and every\n * host component takes them. It matters more here than it reads: a\n * `GestureDetector` reaches its child through the child's own responder\n * registration, so a scrollable that did not accept them could never carry a\n * gesture — and `Gesture.Native()` exists precisely to put a scrollable into\n * the arbitration (`@gorhom/bottom-sheet` wraps its scrollable in exactly that\n * shape). `useResponder` installs nothing unless some responder prop is\n * actually present, so a plain `ScrollView` gains no controller.\n */\nexport type ScrollViewProps = ResponderProps & {\n style?: StyleProp\n contentContainerStyle?: StyleProp\n horizontal?: boolean\n // RN sticky headers (vertical lists): the children at these indices pin to\n // the top while scrolled past, each pushed out by the next one.\n stickyHeaderIndices?: readonly number[]\n onScroll?: (event: ScrollEvent) => void\n // RN's four scroll PHASES. What each one is here — and which input devices\n // produce it — is measured in docs/research/scroll-phases.md. A wheel gets\n // one synthetic begin/end pair around a burst and no momentum; a touchpad\n // glide gets all four from its real GTK sequence. `onScroll` is unaffected.\n //\n // Every one of them is OPTIONAL in the strong sense: while no phase\n // handler is attached — neither a prop here nor a Reanimated handler\n // carrying a phase sink — no controller is installed, no signal is\n // connected, and a scroll costs exactly what it cost before they existed.\n onScrollBeginDrag?: (event: ScrollEvent) => void\n onScrollEndDrag?: (event: ScrollEvent) => void\n onMomentumScrollBegin?: (event: ScrollEvent) => void\n onMomentumScrollEnd?: (event: ScrollEvent) => void\n // RN onContentSizeChange(contentWidth, contentHeight): fires when the\n // scrollable content changes size. GTK's adjustments emit \"changed\" right\n // after the allocation that resized their range — exactly the moment the\n // new scroll extent is real, which windowed lists rely on to reposition.\n onContentSizeChange?: (width: number, height: number) => void\n onLayout?: (event: LayoutEvent) => void\n children?: ReactNode\n testID?: string\n}\n\ntype StickyRecord = { y: number; height: number; widget: Gtk.Widget }\n\n// The sticky registry lives on the nearest ScrollView: any descendant may\n// register a slot with its content-relative geometry (measured children of\n// the ScrollView itself, or windowed list cells with engine-known offsets).\nexport type StickyRegistry = {\n register(key: string, record: StickyRecord): void\n unregister(key: string): void\n}\n\nexport const StickyRegistryContext = createContext<StickyRegistry | null>(null)\n\n// A wrapper around a sticky child: a plain container that reports its layout\n// and widget into the registry, so the scroll handler can translate/reorder\n// the REAL instance (RN semantics — no duplicate is ever rendered, external\n// margins travel with the header exactly as designed). An explicit `top`\n// (windowed cells) overrides the measured position.\nexport const StickySlot = ({\n stickyKey,\n style,\n top,\n onLayout,\n children,\n}: {\n stickyKey: string\n style?: StyleProp\n top?: number\n onLayout?: (event: LayoutEvent) => void\n children?: ReactNode\n}) => {\n const registry = useContext(StickyRegistryContext)\n const widgetRef = useRef<Gtk.Box | null>(null)\n const { host, node } = useLayoutChild(widgetRef, {\n style,\n onLayout: (event) => {\n onLayout?.(event)\n const widget = widgetRef.current\n if (registry && widget) {\n registry.register(stickyKey, {\n y: top ?? event.nativeEvent.layout.y,\n height: event.nativeEvent.layout.height,\n widget,\n })\n }\n },\n })\n useRnContainer(widgetRef, node)\n useLayoutEffect(() => {\n return () => {\n registry?.unregister(stickyKey)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [stickyKey])\n return (\n <GtkBox ref={widgetRef}>\n <HostNodeContext.Provider\n value={{ engine: host.engine, node, widgetRef }}\n >\n {children}\n </HostNodeContext.Provider>\n </GtkBox>\n )\n}\n\n// GtkScrolledWindow whose child is a content GtkBox backed by its own Yoga\n// node inside the same engine: the content's RnGtkxLayout measures the engine\n// content size (which grows past the viewport — that IS the scroll range;\n// overflow: scroll on the outer node) and the scrolled window pans it.\nexport const ScrollView = forwardRef<ScrollViewHandle, ScrollViewProps>(\n (\n {\n style,\n contentContainerStyle,\n horizontal = false,\n stickyHeaderIndices,\n onScroll,\n onScrollBeginDrag,\n onScrollEndDrag,\n onMomentumScrollBegin,\n onMomentumScrollEnd,\n onContentSizeChange,\n onLayout,\n children,\n testID,\n ...responderProps\n },\n handleRef,\n ) => {\n const outerRef = useRef<Gtk.ScrolledWindow | null>(null)\n const contentRef = useRef<Gtk.Box | null>(null)\n const [scrolled, setScrolled] = useState<Gtk.ScrolledWindow | null>(null)\n\n const { host, node: outerNode } = useLayoutChild(outerRef, {\n // Yoga unconstrains a scroll node's MAIN axis — align it with the\n // scroll direction or a horizontal list's content clamps to the\n // viewport width (hadjustment upper == page).\n //\n // `flexGrow: 1, flexShrink: 1` is RN's own base style for the scroller\n // (`styles.baseVertical` / `baseHorizontal` in ScrollView.js, applied as\n // `StyleSheet.compose(baseStyle, this.props.style)` — hence base first,\n // the app's style wins). The shrink half is what turns a scrollable with\n // NO style of its own into a viewport: a flex item defaults to\n // flexShrink 0 on RN's Yoga config, so without this the scroller sizes\n // to its content, outgrows a bounded parent and its scroll range stays\n // empty. It cost a whole investigation once: `@gorhom/bottom-sheet`\n // hands its list down unstyled, so the sheet's scroll lock had nothing\n // to lock — the list never received a scroll event because it had never\n // become a viewport.\n //\n // Composing base-first does NOT protect an explicit `height`: grow and\n // height are different properties, so nothing overrides anything — the\n // height is the flex BASIS and grow expands from it, and a scroller with\n // `height: 200` in a 400px column lays out at 400. RN does the same\n // (same base, same order, same node, same Yoga resolution), so this is\n // parity and not a bug; docs/api.md says so where people will look.\n // Bound the PARENT to bound the viewport.\n style: [\n { flexGrow: 1, flexShrink: 1 },\n style,\n { overflow: \"scroll\", flexDirection: horizontal ? \"row\" : \"column\" },\n ],\n onLayout,\n // RN semantics: a ScrollView CLIPS its content. Containers paint-\n // overflow by default, so without this the scrolled-away content (e.g.\n // the original of a pinned sticky header) keeps drawing past the scroll\n // area's edges. The `overflow: \"scroll\"` above would now clip the\n // viewport on its own, but a viewport that clips only while nobody\n // overrides the style is not a contract.\n alwaysClips: true,\n })\n\n const [contentNode] = useState(() => host.engine.createNode())\n // Sticky positions are computed INSIDE the same allocation pass that\n // places the children (beforeAllocate): the viewport has already settled\n // on this frame's scroll offset, so the pinned header lands exactly —\n // reacting to the adjustment signal instead lags a frame and jitters.\n useRnContainer(contentRef, contentNode, {\n beforeAllocate: () => {\n if (stickyRecords.current.size > 0) {\n updateSticky(outerRef.current?.getVadjustment()?.getValue() ?? 0)\n }\n },\n })\n\n useLayoutEffect(() => {\n outerNode.insertChild(contentNode, 0)\n contentNode.setCommit(() => {\n // The content size IS this widget's measure (scroll range) — the\n // ScrolledWindow itself allocates the content widget.\n const widget = contentRef.current\n if (!widget) {\n return\n }\n const queue = (): void => {\n queueResize(widget)\n }\n if (!deferDuringAllocate(widget, queue)) {\n queue()\n }\n })\n return () => {\n contentNode.setCommit(null)\n outerNode.removeChild(contentNode)\n contentNode.free()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n const contentLayout = splitStyle(\n StyleSheet.flatten(contentContainerStyle),\n ).layout\n const contentKey = JSON.stringify({ horizontal, contentLayout })\n useLayoutEffect(() => {\n contentNode.setStyle({\n flexDirection: horizontal ? \"row\" : \"column\",\n // RN's content container is a plain `View`, and a `View`'s default\n // `alignItems` is `stretch` — so a vertical ScrollView's children are\n // as wide as the viewport unless they say otherwise. This used to be\n // `flex-start`, which made every child shrink to its intrinsic width\n // and any `flex: 1` inside it collapse to zero.\n //\n // It was found twice. First by `Sortable`, whose rows rendered as\n // bare drag handles with no text (docs/research/drag-and-drop.md\n // recorded it as a probable ScrollView parity bug and did not act on\n // it, because changing a shared default under every example was not\n // that epic's business). Then by porting\n // `react-native-reanimated-dnd`'s example app, where it broke\n // seventeen screens at once — at which point \"a ported app changes\n // nothing in its source\" was simply false, and the default was the\n // thing that was wrong.\n //\n // An app that wants the old behaviour writes it, exactly as it would\n // on iOS and Android: `contentContainerStyle={{ alignItems:\n // \"flex-start\" }}`.\n alignItems: \"stretch\",\n ...contentLayout,\n })\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [contentNode, contentKey])\n\n // On the SCROLLED WINDOW, not on the content box: that is the widget the\n // handle stands for, the widget a `GestureDetector` measures its gesture's\n // view against, and the widget whose own kinetics a `Gesture.Native()`\n // over this scrollable is talking about.\n useResponder(outerRef, responderProps)\n\n useImperativeHandle(handleRef, () => {\n const handle: ScrollViewHandle = {\n ...createMeasureHandle(outerRef, outerNode),\n\n scrollTo({ x, y }) {\n const widget = outerRef.current\n if (!widget) {\n return\n }\n if (y !== undefined) {\n widget.getVadjustment()?.setValue(y)\n }\n if (x !== undefined) {\n widget.getHadjustment()?.setValue(x)\n }\n },\n scrollToEnd() {\n const widget = outerRef.current\n const adjustment = horizontal\n ? widget?.getHadjustment()\n : widget?.getVadjustment()\n if (adjustment) {\n adjustment.setValue(\n adjustment.getUpper() - adjustment.getPageSize(),\n )\n }\n },\n }\n // Spreading built a NEW object, and the widget lookup is keyed by\n // handle identity — without this the composed handle is a stranger to\n // measure.ts.\n registerHandleWidget(handle, outerRef)\n return handle\n })\n\n // --- sticky headers -------------------------------------------------\n // The RN model, faithfully: the REAL child is translated (no duplicate,\n // state preserved). RN gives the pinned cell a zIndex; this reorders the\n // active slot to be the LAST content child while pinned and restores it\n // afterwards, which predates `zIndex` working (gtkx/bridge/view-box.ts)\n // and is kept because it is exact: the slots here are the ScrollView's own\n // and their Yoga order is rebuilt from the same list, so the reorder that\n // would be a hazard for arbitrary app children is not one for these. Per-frame movement\n // goes through the rect-store offset fast path — zero React work while\n // scrolling.\n const stickySet = stickyHeaderIndices ?? []\n const stickyRecords = useRef(new Map<string, StickyRecord>())\n const restoreSibling = useRef(new Map<string, Gtk.Widget | null>())\n const activeStickyRef = useRef<string | null>(null)\n const [hasSticky, setHasSticky] = useState(false)\n\n const stickyRegistry = useRef<StickyRegistry>({\n register: (key, record) => {\n stickyRecords.current.set(key, record)\n setHasSticky(true)\n const parent = record.widget.getParent()\n if (parent) {\n queueAllocate(parent)\n }\n },\n unregister: (key) => {\n stickyRecords.current.delete(key)\n restoreSibling.current.delete(key)\n if (activeStickyRef.current === key) {\n activeStickyRef.current = null\n }\n if (stickyRecords.current.size === 0) {\n setHasSticky(false)\n }\n },\n })\n\n const updateSticky = (rawScrollTop: number): void => {\n if (stickyRecords.current.size === 0) {\n return\n }\n // The viewport translates the content by the FRACTIONAL adjustment\n // value while widget allocations are integer — an unquantized pin\n // disagrees with the translation by ±1px and shimmers every frame.\n // Frame telemetry (docs/research/sticky-probe.md) proved the viewport quantizes\n // like floor: with Math.floor the header's window-relative position is\n // a flat 0.00 across every frame; round oscillates 0/1, ceil sits at 1.\n const scrollTop = Math.floor(rawScrollTop)\n let active: string | null = null\n let next: StickyRecord | null = null\n for (const [key, record] of stickyRecords.current) {\n if (record.y <= scrollTop) {\n if (\n active === null ||\n record.y >= stickyRecords.current.get(active)!.y\n ) {\n active = key\n }\n } else if (next === null || record.y < next.y) {\n next = record\n }\n }\n\n const previous = activeStickyRef.current\n if (previous !== active) {\n activeStickyRef.current = active\n if (previous !== null) {\n const record = stickyRecords.current.get(previous)\n if (record) {\n setStoredOffset(record.widget, 0, 0)\n }\n }\n // Reordering widgets queues GTK work — not allowed mid-allocation.\n // The z-switch lands a frame later; the POSITION is already exact.\n setTimeout(() => {\n applyStickyReorder(previous, active)\n }, 0)\n }\n\n if (active !== null) {\n const record = stickyRecords.current.get(active)!\n // Pin at the viewport top; the next sticky header pushes it out.\n const pinned = next\n ? Math.min(scrollTop, next.y - record.height)\n : scrollTop\n setStoredOffset(record.widget, 0, Math.max(0, pinned - record.y))\n }\n }\n\n const applyStickyReorder = (\n previous: string | null,\n active: string | null,\n ): void => {\n if (previous !== null) {\n const record = stickyRecords.current.get(previous)\n // Slot parents are always our GtkBox containers.\n const parent = record?.widget.getParent() as Gtk.Box | null\n if (record && parent) {\n // Put the slot back where the reconciler expects it.\n parent.reorderChildAfter(\n record.widget,\n restoreSibling.current.get(previous) ?? null,\n )\n queueAllocate(parent)\n }\n }\n if (active !== null) {\n const record = stickyRecords.current.get(active)\n const parent = record?.widget.getParent() as Gtk.Box | null\n if (record && parent) {\n restoreSibling.current.set(active, record.widget.getPrevSibling())\n parent.reorderChildAfter(record.widget, parent.getLastChild())\n queueAllocate(parent)\n }\n }\n }\n\n // The one place the payload is read off the widget, shared by `onScroll`\n // and by all four phases — RN hands the same `ScrollEvent` to every one\n // of them, and reading it twice would be two sets of FFI calls for one\n // truth. Null when there is nothing honest to report yet (no widget, or\n // a content size the engine has not committed).\n const readScrollEvent = (): ScrollEvent | null => {\n const widget = outerRef.current\n const contentRect = contentNode.getRect()\n if (!widget || !contentRect) {\n return null\n }\n const hadjustment = widget.getHadjustment()\n const vadjustment = widget.getVadjustment()\n return {\n nativeEvent: {\n contentOffset: {\n x: hadjustment?.getValue() ?? 0,\n y: vadjustment?.getValue() ?? 0,\n },\n contentSize: { width: contentRect.width, height: contentRect.height },\n layoutMeasurement: {\n width: hadjustment?.getPageSize() ?? 0,\n height: vadjustment?.getPageSize() ?? 0,\n },\n },\n }\n }\n\n const emitScroll = (): void => {\n if (!onScroll) {\n return\n }\n const event = readScrollEvent()\n if (event) {\n onScroll(event)\n }\n }\n\n const adjustment = horizontal\n ? (scrolled?.getHadjustment() ?? null)\n : (scrolled?.getVadjustment() ?? null)\n const onAdjustment = (): void => {\n if (!perfEnabled) {\n emitScroll()\n return\n }\n // Perf: full wall time of one scroll tick's JS work — the FFI reads in\n // emitScroll plus the app/VirtualizedList onScroll handler (windowing,\n // viewability, endReached) — everything that blocks the GTK frame.\n perfCount(\"scroll.events\")\n const start = perfNow()\n emitScroll()\n perfAddTime(\"scroll.js\", perfNow() - start)\n }\n\n // --- the four scroll phases -----------------------------------------\n //\n // What GTK actually reports, measured on 4.22.4 under a real pointer\n // (docs/research/scroll-phases.md, and the trace is in\n // tests/gtk/components/scroll-phases.gtk.test.tsx):\n //\n // - a mouse WHEEL emits `::scroll` per detent and NOTHING around it.\n // GTK gives us no sequence, so while a consumer asks for begin/end we\n // synthesize a desktop scroll SESSION: begin before the first detent\n // mutates the adjustment, end after a measured idle window, and no\n // momentum. This is an intentional desktop extension — RN has no\n // wheel — and is what lets a phase-aware consumer capture the offset\n // it is about to constrain instead of discovering it one event late.\n // - a touchpad GLIDE emits `::scroll-begin`, a stream of `::scroll`,\n // `::scroll-end` and `::decelerate` — a real sequence with a real\n // beginning and end — and the scrolled window's own kinetic\n // animation then carries the adjustment on for another second or so.\n // All four phases exist, and all four are reported.\n //\n // Drag maps onto the scroll SEQUENCE rather than onto a finger on the\n // content: `::scroll-begin` is \"the user started driving this scroller\",\n // which is what a consumer of `onScrollBeginDrag` acts on and is as close\n // as a device that never touches the content can get. That is the one\n // approximation here, and it is named in docs/api.md.\n //\n // Momentum is read off the ADJUSTMENT, not off `::decelerate`. The\n // controller emits `decelerate` at every `scroll-end` — it reports the\n // velocity it measured, not a decision to coast — while the scrolled\n // window decides for itself whether that velocity is worth animating. RN\n // fires `onMomentumScrollBegin` only when the view actually keeps\n // moving, so the honest source is the movement itself.\n const phaseSink = scrollPhaseSink(onScroll)\n // `wants()` rather than the sink's mere presence: every Reanimated scroll\n // handler carries one, and most of them ask for nothing but `onScroll`.\n // Asking is what keeps the common case free.\n const wantsPhases = Boolean(\n onScrollBeginDrag ||\n onScrollEndDrag ||\n onMomentumScrollBegin ||\n onMomentumScrollEnd ||\n phaseSink?.wants(),\n )\n // GTK brackets touchpad gestures for us. A wheel has only detents, so the\n // begin/end extension below needs to see those detents — but only when a\n // consumer asks for either end of the session. A momentum-only handler\n // still pays no JS call per wheel event, because a wheel never coasts.\n const wantsWheelSessions = Boolean(\n onScrollBeginDrag ||\n onScrollEndDrag ||\n phaseSink?.wants(\"beginDrag\") ||\n phaseSink?.wants(\"endDrag\"),\n )\n\n // Read per event rather than captured, exactly as `useAnimatedScrollHandler`\n // does it: a re-render that changed a handler must be picked up without\n // tearing the GTK controller down and building a new one.\n const phaseHandlers = useRef({\n onScrollBeginDrag,\n onScrollEndDrag,\n onMomentumScrollBegin,\n onMomentumScrollEnd,\n phaseSink,\n readScrollEvent,\n })\n phaseHandlers.current = {\n onScrollBeginDrag,\n onScrollEndDrag,\n onMomentumScrollBegin,\n onMomentumScrollEnd,\n phaseSink,\n readScrollEvent,\n }\n\n useLayoutEffect(() => {\n // The whole cost story is this early return. No phase handler means no\n // controller, no signal, no tick callback and no bookkeeping — the\n // widget is byte-for-byte the one this component built before the\n // phases existed.\n if (!wantsPhases) {\n return\n }\n const widget = scrolled\n if (!widget) {\n return\n }\n\n const emitPhase = (phase: ScrollPhase): void => {\n const handlers = phaseHandlers.current\n const event = handlers.readScrollEvent()\n if (!event) {\n return\n }\n if (phase === \"beginDrag\") {\n handlers.onScrollBeginDrag?.(event)\n } else if (phase === \"endDrag\") {\n handlers.onScrollEndDrag?.(event)\n } else if (phase === \"momentumBegin\") {\n handlers.onMomentumScrollBegin?.(event)\n } else {\n handlers.onMomentumScrollEnd?.(event)\n }\n handlers.phaseSink?.deliver(phase, event)\n }\n\n const axis = horizontal\n ? widget.getHadjustment()\n : widget.getVadjustment()\n\n // Momentum is watched on the ADJUSTMENT, with two timers and no tick\n // callback. A frame-polling watcher was the first shape and it was\n // wrong twice over: it burns a callback per frame for the length of\n // every coast, and it counts FRAMES, which is not a unit — under the\n // headless compositor the frame clock free-runs at ~106 ticks per\n // millisecond (measured, docs/research/scroll-phases.md), so a\n // four-frame grace expired in microseconds. Wall time means the same\n // thing everywhere.\n //\n // Milliseconds to wait after `::scroll-end` for the scrolled window's\n // kinetic animation to take over before concluding it never will.\n // Measured handoff: `::scroll-end` at 1921.9 ms, first inertial value\n // change at 1927.5 ms — 5.6 ms. 120 is slack, not a guess.\n const handoffMs = 120\n // Milliseconds of a motionless adjustment that end the momentum. GTK's\n // deceleration steps the value every frame right to the end (16.7 ms\n // apart in the trace), so 60 ms is three and a half missed steps.\n const restMs = 60\n\n let watching = false\n let coasting = false\n let lastMoveAt = 0\n let handoffTimer: ReturnType<typeof setTimeout> | null = null\n let restTimer: ReturnType<typeof setTimeout> | null = null\n\n const clearTimers = (): void => {\n if (handoffTimer !== null) {\n clearTimeout(handoffTimer)\n handoffTimer = null\n }\n if (restTimer !== null) {\n clearTimeout(restTimer)\n restTimer = null\n }\n }\n\n // Re-armed for the REMAINING idle time rather than reset on every step,\n // so the number of timers a coast costs is bounded by its duration\n // (one per `restMs`) and not by how many times the adjustment moved.\n // The difference is not academic: under the headless compositor's\n // free-running frame clock a single coast steps the value ~150,000\n // times, and a clear-and-rearm per step is 150,000 timers.\n const checkRest = (): void => {\n const idle = perfNow() - lastMoveAt\n if (idle < restMs) {\n restTimer = setTimeout(checkRest, restMs - idle)\n return\n }\n restTimer = null\n stopMomentumWatch()\n emitPhase(\"momentumEnd\")\n }\n\n const onCoastStep = (): void => {\n lastMoveAt = perfNow()\n if (coasting) {\n return\n }\n coasting = true\n if (handoffTimer !== null) {\n clearTimeout(handoffTimer)\n handoffTimer = null\n }\n emitPhase(\"momentumBegin\")\n restTimer = setTimeout(checkRest, restMs)\n }\n\n const stopMomentumWatch = (): void => {\n clearTimers()\n if (watching) {\n axis?.off(\"value-changed\", onCoastStep)\n watching = false\n }\n coasting = false\n }\n\n const watchMomentum = (): void => {\n if (watching) {\n return\n }\n watching = true\n axis?.on(\"value-changed\", onCoastStep)\n handoffTimer = setTimeout(() => {\n handoffTimer = null\n if (!coasting) {\n // The scroller came to rest with the drag. No inertia, so no\n // momentum pair — which is RN's behaviour too.\n stopMomentumWatch()\n }\n }, handoffMs)\n }\n\n const controller = Gtk.EventControllerScroll.new(\n // An axis flag is REQUIRED, and not for the deltas: measured, a\n // controller created with `KINETIC` alone emits nothing at all — not\n // even `::scroll-begin`. `::scroll` is therefore emitted. It stays\n // unconnected unless somebody asks for begin/end: only then does a\n // wheel detent enter JS to maintain the desktop session below; a\n // momentum-only or untracked scroller keeps the old empty C signal\n // path.\n Gtk.EventControllerScrollFlags.BOTH_AXES |\n Gtk.EventControllerScrollFlags.KINETIC,\n )\n // CAPTURE, so the phase is reported before the scrolled window's own\n // controller acts on the same event and moves the adjustment. Nothing\n // here handles the event, so propagation is untouched either way.\n controller.setPropagationPhase(Gtk.PropagationPhase.CAPTURE)\n\n const beginDrag = (): void => {\n // A new drag during a coast: RN ends the momentum before beginning\n // the drag, so a consumer never sees two live phases at once.\n if (coasting) {\n stopMomentumWatch()\n emitPhase(\"momentumEnd\")\n } else {\n stopMomentumWatch()\n }\n emitPhase(\"beginDrag\")\n }\n // A wheel gives GTK no begin/end signals. Group its detents into one\n // user-driven session, ending after an idle interval longer than the\n // ordinary detent spacing measured in docs/research/scroll-phases.md\n // (20–33 ms in the trace). The callback runs in CAPTURE, so beginDrag\n // sees the PRE-scroll offset before GtkScrolledWindow mutates its\n // adjustment and emits the onScroll callback that consumes the context.\n const wheelSession = createWheelScrollSession(beginDrag, () =>\n emitPhase(\"endDrag\"),\n )\n // A touchpad brackets itself, so its sequence owns the session while it\n // runs. Two live sessions at once would hand a consumer a second\n // `beginDrag` before the first `endDrag` — which is exactly the state\n // gorhom's lock reads to decide where to pin its list.\n let nativeSequence = false\n const onScrollBegin = (): void => {\n nativeSequence = true\n // A wheel burst may still be waiting out its idle timer.\n wheelSession.finish()\n beginDrag()\n }\n const onScrollEnd = (): void => {\n nativeSequence = false\n emitPhase(\"endDrag\")\n watchMomentum()\n }\n const onScroll = (): boolean => {\n if (\n !wantsWheelSessions ||\n nativeSequence ||\n controller.getUnit() !== Gdk.ScrollUnit.WHEEL\n ) {\n return false\n }\n wheelSession.detent()\n // Observe only. GtkScrolledWindow still receives and applies the\n // detent, which is what makes this phase seam transparent.\n return false\n }\n\n controller.on(\"scroll-begin\", onScrollBegin)\n controller.on(\"scroll-end\", onScrollEnd)\n if (wantsWheelSessions) {\n controller.on(\"scroll\", onScroll)\n }\n widget.addController(controller)\n\n return () => {\n controller.off(\"scroll-begin\", onScrollBegin)\n controller.off(\"scroll-end\", onScrollEnd)\n if (wantsWheelSessions) {\n controller.off(\"scroll\", onScroll)\n }\n wheelSession.dispose()\n widget.removeController(controller)\n stopMomentumWatch()\n }\n }, [scrolled, wantsPhases, wantsWheelSessions, horizontal])\n\n // Content-size reports dedupe on the engine rect: \"changed\" also fires\n // for pure viewport (page-size) changes, which RN does not report.\n const lastContentSize = useRef({ width: -1, height: -1 })\n const onRangeChanged = (): void => {\n if (!onContentSizeChange) {\n return\n }\n const rect = contentNode.getRect()\n if (!rect) {\n return\n }\n const last = lastContentSize.current\n if (rect.width === last.width && rect.height === last.height) {\n return\n }\n lastContentSize.current = { width: rect.width, height: rect.height }\n onContentSizeChange(rect.width, rect.height)\n }\n\n // Frame-synced sticky driver: a pure scroll TRANSLATES the content\n // without re-allocating it (same size → GTK skips the vfunc), so signal\n // handlers land a frame late and the pinned header jitters. A tick\n // callback runs in the frame's UPDATE phase, before layout: when the\n // scroll offset moved, queue an allocation for THIS frame — the\n // translation and the pin correction then paint atomically.\n useLayoutEffect(() => {\n if (!hasSticky || horizontal) {\n return\n }\n const widget = scrolled\n if (!widget) {\n return\n }\n const vadjustment = widget.getVadjustment()\n let lastValue = vadjustment?.getValue() ?? 0\n // Frames to keep requeueing after the last observed move. The scrolled\n // window's own kinetic tick may run AFTER ours, so this tick's read can\n // be one frame stale: without the grace window the first frame of a\n // deceleration (its value change lands after our read) would miss its\n // correction. Kinetic deceleration changes the value every frame, so\n // three frames of slack cover the handoff at both ends of the motion.\n const graceFrames = 3\n let remaining = graceFrames\n const id = widget.addTickCallback(() => {\n perfCount(\"sticky.ticks\")\n const value = vadjustment?.getValue() ?? 0\n if (value !== lastValue) {\n lastValue = value\n remaining = graceFrames\n } else if (remaining > 0) {\n remaining -= 1\n } else {\n // At rest: the pin is already correct and nothing translated it,\n // so an allocation pass per frame would be pure idle cost.\n return true\n }\n perfCount(\"sticky.queues\")\n const content = contentRef.current\n if (content) {\n queueAllocate(content)\n }\n return true\n })\n return () => {\n widget.removeTickCallback(id)\n }\n }, [scrolled, hasSticky, horizontal])\n\n // Perf: observe the GTK frame clock through a tick callback — the delta\n // between consecutive ticks is the real frame interval (late frames show\n // up directly). Note: registering a tick callback keeps the frame clock\n // running, so idle seconds show ~60 ticks too; the reporter separates\n // load by the accompanying counters.\n useLayoutEffect(() => {\n if (!perfEnabled) {\n return\n }\n ensurePerfReporter()\n const widget = scrolled\n if (!widget) {\n return\n }\n const id = widget.addTickCallback(() => {\n perfFrameTick(perfNow())\n return true\n })\n return () => {\n widget.removeTickCallback(id)\n }\n }, [scrolled])\n useSignal(adjustment, \"value-changed\", onAdjustment)\n // Both axes report into the same handler — RN's callback carries both\n // dimensions no matter which one moved.\n useSignal(scrolled?.getVadjustment() ?? null, \"changed\", onRangeChanged)\n useSignal(scrolled?.getHadjustment() ?? null, \"changed\", onRangeChanged)\n\n return (\n <GtkScrolledWindow\n ref={(widget: Gtk.ScrolledWindow | null) => {\n outerRef.current = widget\n setScrolled(widget)\n }}\n name={testID}\n >\n <GtkBox ref={contentRef}>\n <HostNodeContext.Provider\n value={{\n engine: host.engine,\n node: contentNode,\n widgetRef: contentRef,\n }}\n >\n <StickyRegistryContext.Provider value={stickyRegistry.current}>\n {stickySet.length === 0\n ? children\n : Children.toArray(children).map((child, index) =>\n stickySet.includes(index) ? (\n <StickySlot\n key={`sticky-slot-${index}`}\n stickyKey={`index-${index}`}\n >\n {child}\n </StickySlot>\n ) : (\n child\n ),\n )}\n </StickyRegistryContext.Provider>\n </HostNodeContext.Provider>\n </GtkBox>\n </GtkScrolledWindow>\n )\n },\n)\nScrollView.displayName = \"ScrollView\"\n"]}
1
+ {"version":3,"file":"scroll-view.js","sourceRoot":"","sources":["../../src/components/scroll-view.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n Children,\n createContext,\n forwardRef,\n useContext,\n useImperativeHandle,\n useLayoutEffect,\n useRef,\n useState,\n type ReactNode,\n} from \"react\"\nimport { splitStyle, StyleSheet } from \"../style/index\"\nimport type { StyleProp } from \"../contracts\"\nimport {\n Gdk,\n Gtk,\n GtkBox,\n GtkScrolledWindow,\n queueAllocate,\n queueResize,\n useSignal,\n} from \"../gtkx/bridge/index\"\nimport {\n ensurePerfReporter,\n perfAddTime,\n perfCount,\n perfEnabled,\n perfFrameTick,\n perfNow,\n} from \"../perf\"\nimport type { ResponderProps } from \"../responder/types\"\nimport { useResponder } from \"../responder/use-responder\"\nimport { HostNodeContext } from \"./host-node\"\nimport {\n createMeasureHandle,\n registerHandleWidget,\n type MeasureHandle,\n} from \"./measure\"\nimport { deferDuringAllocate, setStoredOffset } from \"./rect-store\"\nimport { scrollPhaseSink, type ScrollPhase } from \"./scroll-phase\"\nimport {\n useLayoutChild,\n useRnContainer,\n type LayoutEvent,\n} from \"./use-layout-child\"\nimport { createWheelScrollSession } from \"./wheel-scroll-session\"\n\n/**\n * The scroll half on its own, because the windowed list core builds its own\n * handle on top of it and is a COMPOSITE — it owns no widget, so it has no\n * honest geometry to report and does not claim any.\n */\nexport type ScrollHandle = {\n scrollTo(options: { x?: number; y?: number; animated?: boolean }): void\n scrollToEnd(options?: { animated?: boolean }): void\n}\n\n// RN's ScrollView ref carries the geometry methods as well as the scroll\n// ones — it is a host component, and every host component has them. Here the\n// composition also carries the widget: `registerHandleWidget` below is what\n// lets `measureLayout(scrollViewRef, …)` resolve, and what lets\n// `Animated.ScrollView` find the scrolled window to write a transform to.\nexport type ScrollViewHandle = MeasureHandle & ScrollHandle\n\nexport type ScrollEvent = {\n nativeEvent: {\n contentOffset: { x: number; y: number }\n contentSize: { width: number; height: number }\n // RN's viewport size. GTK's adjustments call it the page size, which is\n // the same thing: the extent the scrolled window shows at once. Without\n // it nothing can compute \"how far from the end am I\" — autoscroll during\n // a drag, custom end-reached logic — which is why RN carries it.\n layoutMeasurement: { width: number; height: number }\n }\n}\n\n/**\n * `ResponderProps` because a `ScrollView` is a HOST component in RN, and every\n * host component takes them. It matters more here than it reads: a\n * `GestureDetector` reaches its child through the child's own responder\n * registration, so a scrollable that did not accept them could never carry a\n * gesture — and `Gesture.Native()` exists precisely to put a scrollable into\n * the arbitration (`@gorhom/bottom-sheet` wraps its scrollable in exactly that\n * shape). `useResponder` installs nothing unless some responder prop is\n * actually present, so a plain `ScrollView` gains no controller.\n */\nexport type ScrollViewProps = ResponderProps & {\n style?: StyleProp\n contentContainerStyle?: StyleProp\n horizontal?: boolean\n // RN sticky headers (vertical lists): the children at these indices pin to\n // the top while scrolled past, each pushed out by the next one.\n stickyHeaderIndices?: readonly number[]\n onScroll?: (event: ScrollEvent) => void\n // RN's four scroll PHASES. What each one is here — and which input devices\n // produce it — is measured in docs/research/scroll-phases.md. A wheel gets\n // one synthetic begin/end pair around a burst and no momentum; a touchpad\n // glide gets all four from its real GTK sequence. `onScroll` is unaffected.\n //\n // Every one of them is OPTIONAL in the strong sense: while no phase\n // handler is attached — neither a prop here nor a Reanimated handler\n // carrying a phase sink — no controller is installed, no signal is\n // connected, and a scroll costs exactly what it cost before they existed.\n onScrollBeginDrag?: (event: ScrollEvent) => void\n onScrollEndDrag?: (event: ScrollEvent) => void\n onMomentumScrollBegin?: (event: ScrollEvent) => void\n onMomentumScrollEnd?: (event: ScrollEvent) => void\n // RN onContentSizeChange(contentWidth, contentHeight): fires when the\n // scrollable content changes size. GTK's adjustments emit \"changed\" right\n // after the allocation that resized their range — exactly the moment the\n // new scroll extent is real, which windowed lists rely on to reposition.\n onContentSizeChange?: (width: number, height: number) => void\n onLayout?: (event: LayoutEvent) => void\n children?: ReactNode\n testID?: string\n}\n\ntype StickyRecord = { y: number; height: number; widget: Gtk.Widget }\n\n// The sticky registry lives on the nearest ScrollView: any descendant may\n// register a slot with its content-relative geometry (measured children of\n// the ScrollView itself, or windowed list cells with engine-known offsets).\nexport type StickyRegistry = {\n register(key: string, record: StickyRecord): void\n unregister(key: string): void\n}\n\nexport const StickyRegistryContext = createContext<StickyRegistry | null>(null)\n\n// A wrapper around a sticky child: a plain container that reports its layout\n// and widget into the registry, so the scroll handler can translate/reorder\n// the REAL instance (RN semantics — no duplicate is ever rendered, external\n// margins travel with the header exactly as designed). An explicit `top`\n// (windowed cells) overrides the measured position.\nexport const StickySlot = ({\n stickyKey,\n style,\n top,\n onLayout,\n children,\n}: {\n stickyKey: string\n style?: StyleProp\n top?: number\n onLayout?: (event: LayoutEvent) => void\n children?: ReactNode\n}) => {\n const registry = useContext(StickyRegistryContext)\n const widgetRef = useRef<Gtk.Box | null>(null)\n const { host, node } = useLayoutChild(widgetRef, {\n style,\n onLayout: (event) => {\n onLayout?.(event)\n const widget = widgetRef.current\n if (registry && widget) {\n registry.register(stickyKey, {\n y: top ?? event.nativeEvent.layout.y,\n height: event.nativeEvent.layout.height,\n widget,\n })\n }\n },\n })\n useRnContainer(widgetRef, node)\n useLayoutEffect(() => {\n return () => {\n registry?.unregister(stickyKey)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [stickyKey])\n return (\n <GtkBox ref={widgetRef}>\n <HostNodeContext.Provider\n value={{ engine: host.engine, node, widgetRef }}\n >\n {children}\n </HostNodeContext.Provider>\n </GtkBox>\n )\n}\n\n// GtkScrolledWindow whose child is a content GtkBox backed by its own Yoga\n// node inside the same engine: the content's RnGtkxLayout measures the engine\n// content size (which grows past the viewport — that IS the scroll range;\n// overflow: scroll on the outer node) and the scrolled window pans it.\nexport const ScrollView = forwardRef<ScrollViewHandle, ScrollViewProps>(\n (\n {\n style,\n contentContainerStyle,\n horizontal = false,\n stickyHeaderIndices,\n onScroll,\n onScrollBeginDrag,\n onScrollEndDrag,\n onMomentumScrollBegin,\n onMomentumScrollEnd,\n onContentSizeChange,\n onLayout,\n children,\n testID,\n ...responderProps\n },\n handleRef,\n ) => {\n const outerRef = useRef<Gtk.ScrolledWindow | null>(null)\n const contentRef = useRef<Gtk.Box | null>(null)\n const [scrolled, setScrolled] = useState<Gtk.ScrolledWindow | null>(null)\n\n const { host, node: outerNode } = useLayoutChild(outerRef, {\n // Yoga unconstrains a scroll node's MAIN axis — align it with the\n // scroll direction or a horizontal list's content clamps to the\n // viewport width (hadjustment upper == page).\n //\n // `flexGrow: 1, flexShrink: 1` is RN's own base style for the scroller\n // (`styles.baseVertical` / `baseHorizontal` in ScrollView.js, applied as\n // `StyleSheet.compose(baseStyle, this.props.style)` — hence base first,\n // the app's style wins). The shrink half is what turns a scrollable with\n // NO style of its own into a viewport: a flex item defaults to\n // flexShrink 0 on RN's Yoga config, so without this the scroller sizes\n // to its content, outgrows a bounded parent and its scroll range stays\n // empty. It cost a whole investigation once: `@gorhom/bottom-sheet`\n // hands its list down unstyled, so the sheet's scroll lock had nothing\n // to lock — the list never received a scroll event because it had never\n // become a viewport.\n //\n // Composing base-first does NOT protect an explicit `height`: grow and\n // height are different properties, so nothing overrides anything — the\n // height is the flex BASIS and grow expands from it, and a scroller with\n // `height: 200` in a 400px column lays out at 400. RN does the same\n // (same base, same order, same node, same Yoga resolution), so this is\n // parity and not a bug; docs/api.md says so where people will look.\n // Bound the PARENT to bound the viewport.\n style: [\n { flexGrow: 1, flexShrink: 1 },\n style,\n { overflow: \"scroll\", flexDirection: horizontal ? \"row\" : \"column\" },\n ],\n onLayout,\n // RN semantics: a ScrollView CLIPS its content. Containers paint-\n // overflow by default, so without this the scrolled-away content (e.g.\n // the original of a pinned sticky header) keeps drawing past the scroll\n // area's edges. The `overflow: \"scroll\"` above would now clip the\n // viewport on its own, but a viewport that clips only while nobody\n // overrides the style is not a contract.\n alwaysClips: true,\n })\n\n const [contentNode] = useState(() => host.engine.createNode())\n // Sticky positions are computed INSIDE the same allocation pass that\n // places the children (beforeAllocate): the viewport has already settled\n // on this frame's scroll offset, so the pinned header lands exactly —\n // reacting to the adjustment signal instead lags a frame and jitters.\n useRnContainer(contentRef, contentNode, {\n beforeAllocate: () => {\n if (stickyRecords.current.size > 0) {\n updateSticky(outerRef.current?.getVadjustment()?.getValue() ?? 0)\n }\n },\n })\n\n useLayoutEffect(() => {\n outerNode.insertChild(contentNode, 0)\n contentNode.setCommit(() => {\n // The content size IS this widget's measure (scroll range) — the\n // ScrolledWindow itself allocates the content widget.\n const widget = contentRef.current\n if (!widget) {\n return\n }\n const queue = (): void => {\n queueResize(widget)\n }\n if (!deferDuringAllocate(widget, queue)) {\n queue()\n }\n })\n return () => {\n contentNode.setCommit(null)\n outerNode.removeChild(contentNode)\n contentNode.free()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n const contentLayout = splitStyle(\n StyleSheet.flatten(contentContainerStyle),\n ).layout\n const contentKey = JSON.stringify({ horizontal, contentLayout })\n useLayoutEffect(() => {\n contentNode.setStyle({\n flexDirection: horizontal ? \"row\" : \"column\",\n // RN's content container is a plain `View`, and a `View`'s default\n // `alignItems` is `stretch` — so a vertical ScrollView's children are\n // as wide as the viewport unless they say otherwise. This used to be\n // `flex-start`, which made every child shrink to its intrinsic width\n // and any `flex: 1` inside it collapse to zero.\n //\n // It was found twice. First by `Sortable`, whose rows rendered as\n // bare drag handles with no text (docs/research/drag-and-drop.md\n // recorded it as a probable ScrollView parity bug and did not act on\n // it, because changing a shared default under every example was not\n // that epic's business). Then by porting\n // `react-native-reanimated-dnd`'s example app, where it broke\n // seventeen screens at once — at which point \"a ported app changes\n // nothing in its source\" was simply false, and the default was the\n // thing that was wrong.\n //\n // An app that wants the old behaviour writes it, exactly as it would\n // on iOS and Android: `contentContainerStyle={{ alignItems:\n // \"flex-start\" }}`.\n alignItems: \"stretch\",\n ...contentLayout,\n })\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [contentNode, contentKey])\n\n // On the SCROLLED WINDOW, not on the content box: that is the widget the\n // handle stands for, the widget a `GestureDetector` measures its gesture's\n // view against, and the widget whose own kinetics a `Gesture.Native()`\n // over this scrollable is talking about.\n useResponder(outerRef, responderProps)\n\n // `scrollTo`/`scrollToEnd` can run before GTK's OWN widget-level\n // measure/allocate has caught up to content the ENGINE already\n // committed — Yoga's flush is a same-microtask JS computation, while a\n // GtkScrolledWindow only learns its child's real size on its next native\n // allocate, which is queued (a frame away) rather than synchronous.\n // `Gtk.Adjustment.setValue()` clamps to ITS OWN `upper`/`page-size`, so a\n // target beyond a still-stale (freshly mounted: 0/0) range is silently\n // absorbed as a no-op — the value does not actually change, so\n // `value-changed` never fires and the scroll this call was supposed to\n // produce is dropped with no event at all. Reproduced deterministically\n // by calling `scrollTo` immediately after `render()` with no extra\n // settling tick: the adjustment reads upper=0/page=0 while\n // `contentNode`/`outerNode` already have the real (correct) rect.\n //\n // The engine's own rects are the fix: they commit before GTK's real\n // geometry ever could (same tick vs. next frame), so reconfiguring the\n // adjustment's range from them — right before setting the value — closes\n // the gap using truth this component already has in hand. Only these two\n // rare, explicitly-invoked entry points pay for it; the per-event\n // `onAdjustment`/`readScrollEvent` path (docs/research/scroll-phases.md)\n // is untouched.\n const syncAdjustmentRange = (\n adjustment: Gtk.Adjustment,\n axis: \"width\" | \"height\",\n ): void => {\n const content = contentNode.getRect()\n const viewport = outerNode.getRect()\n if (!content || !viewport) {\n return\n }\n const pageSize = viewport[axis]\n // A viewport taller/wider than its content (a short list in a tall\n // ScrollView — routine, not an edge case) must still satisfy GTK's own\n // `gtk_adjustment_configure` precondition (`lower + page_size <=\n // upper`): clamping `upper` up to at least `pageSize` is the standard\n // \"nothing to scroll\" range, exactly like a GtkScrollbar whose thumb\n // fills the whole trough. Without this, a page size bigger than the\n // content invalidates every `configure()` call — GTK logs a\n // Gtk-CRITICAL and, per `gtk_adjustment_configure`'s own\n // `g_return_if_fail`, leaves the adjustment UNCHANGED, so the\n // `getUpper()`/`getPageSize()` guard above never starts matching and a\n // caller retrying every frame (`autoscroll.tsx`'s tick callback, while\n // active) reissues the same invalid call on every tick for as long as\n // it stays active — a burst of thousands of identical criticals from\n // one stuck loop rather than one-off noise.\n const upper = Math.max(content[axis], pageSize)\n if (\n adjustment.getUpper() === upper &&\n adjustment.getPageSize() === pageSize\n ) {\n return\n }\n adjustment.configure(\n adjustment.getValue(),\n 0,\n upper,\n adjustment.getStepIncrement(),\n adjustment.getPageIncrement(),\n pageSize,\n )\n }\n\n useImperativeHandle(handleRef, () => {\n const handle: ScrollViewHandle = {\n ...createMeasureHandle(outerRef, outerNode),\n\n scrollTo({ x, y }) {\n const widget = outerRef.current\n if (!widget) {\n return\n }\n if (y !== undefined) {\n const vadjustment = widget.getVadjustment()\n if (vadjustment) {\n syncAdjustmentRange(vadjustment, \"height\")\n vadjustment.setValue(y)\n }\n }\n if (x !== undefined) {\n const hadjustment = widget.getHadjustment()\n if (hadjustment) {\n syncAdjustmentRange(hadjustment, \"width\")\n hadjustment.setValue(x)\n }\n }\n },\n scrollToEnd() {\n const widget = outerRef.current\n const adjustment = horizontal\n ? widget?.getHadjustment()\n : widget?.getVadjustment()\n if (adjustment) {\n syncAdjustmentRange(adjustment, horizontal ? \"width\" : \"height\")\n adjustment.setValue(\n adjustment.getUpper() - adjustment.getPageSize(),\n )\n }\n },\n }\n // Spreading built a NEW object, and the widget lookup is keyed by\n // handle identity — without this the composed handle is a stranger to\n // measure.ts.\n registerHandleWidget(handle, outerRef)\n return handle\n })\n\n // --- sticky headers -------------------------------------------------\n // The RN model, faithfully: the REAL child is translated (no duplicate,\n // state preserved). RN gives the pinned cell a zIndex; this reorders the\n // active slot to be the LAST content child while pinned and restores it\n // afterwards, which predates `zIndex` working (gtkx/bridge/view-box.ts)\n // and is kept because it is exact: the slots here are the ScrollView's own\n // and their Yoga order is rebuilt from the same list, so the reorder that\n // would be a hazard for arbitrary app children is not one for these. Per-frame movement\n // goes through the rect-store offset fast path — zero React work while\n // scrolling.\n const stickySet = stickyHeaderIndices ?? []\n const stickyRecords = useRef(new Map<string, StickyRecord>())\n const restoreSibling = useRef(new Map<string, Gtk.Widget | null>())\n const activeStickyRef = useRef<string | null>(null)\n const [hasSticky, setHasSticky] = useState(false)\n\n const stickyRegistry = useRef<StickyRegistry>({\n register: (key, record) => {\n stickyRecords.current.set(key, record)\n setHasSticky(true)\n const parent = record.widget.getParent()\n if (parent) {\n queueAllocate(parent)\n }\n },\n unregister: (key) => {\n stickyRecords.current.delete(key)\n restoreSibling.current.delete(key)\n if (activeStickyRef.current === key) {\n activeStickyRef.current = null\n }\n if (stickyRecords.current.size === 0) {\n setHasSticky(false)\n }\n },\n })\n\n const updateSticky = (rawScrollTop: number): void => {\n if (stickyRecords.current.size === 0) {\n return\n }\n // The viewport translates the content by the FRACTIONAL adjustment\n // value while widget allocations are integer — an unquantized pin\n // disagrees with the translation by ±1px and shimmers every frame.\n // Frame telemetry (docs/research/sticky-probe.md) proved the viewport quantizes\n // like floor: with Math.floor the header's window-relative position is\n // a flat 0.00 across every frame; round oscillates 0/1, ceil sits at 1.\n const scrollTop = Math.floor(rawScrollTop)\n let active: string | null = null\n let next: StickyRecord | null = null\n for (const [key, record] of stickyRecords.current) {\n if (record.y <= scrollTop) {\n if (\n active === null ||\n record.y >= stickyRecords.current.get(active)!.y\n ) {\n active = key\n }\n } else if (next === null || record.y < next.y) {\n next = record\n }\n }\n\n const previous = activeStickyRef.current\n if (previous !== active) {\n activeStickyRef.current = active\n if (previous !== null) {\n const record = stickyRecords.current.get(previous)\n if (record) {\n setStoredOffset(record.widget, 0, 0)\n }\n }\n // Reordering widgets queues GTK work — not allowed mid-allocation.\n // The z-switch lands a frame later; the POSITION is already exact.\n setTimeout(() => {\n applyStickyReorder(previous, active)\n }, 0)\n }\n\n if (active !== null) {\n const record = stickyRecords.current.get(active)!\n // Pin at the viewport top; the next sticky header pushes it out.\n const pinned = next\n ? Math.min(scrollTop, next.y - record.height)\n : scrollTop\n setStoredOffset(record.widget, 0, Math.max(0, pinned - record.y))\n }\n }\n\n const applyStickyReorder = (\n previous: string | null,\n active: string | null,\n ): void => {\n if (previous !== null) {\n const record = stickyRecords.current.get(previous)\n // Slot parents are always our GtkBox containers.\n const parent = record?.widget.getParent() as Gtk.Box | null\n if (record && parent) {\n // Put the slot back where the reconciler expects it.\n parent.reorderChildAfter(\n record.widget,\n restoreSibling.current.get(previous) ?? null,\n )\n queueAllocate(parent)\n }\n }\n if (active !== null) {\n const record = stickyRecords.current.get(active)\n const parent = record?.widget.getParent() as Gtk.Box | null\n if (record && parent) {\n restoreSibling.current.set(active, record.widget.getPrevSibling())\n parent.reorderChildAfter(record.widget, parent.getLastChild())\n queueAllocate(parent)\n }\n }\n }\n\n // The one place the payload is read off the widget, shared by `onScroll`\n // and by all four phases — RN hands the same `ScrollEvent` to every one\n // of them, and reading it twice would be two sets of FFI calls for one\n // truth. Null when there is nothing honest to report yet (no widget, or\n // a content size the engine has not committed).\n const readScrollEvent = (): ScrollEvent | null => {\n const widget = outerRef.current\n const contentRect = contentNode.getRect()\n if (!widget || !contentRect) {\n return null\n }\n const hadjustment = widget.getHadjustment()\n const vadjustment = widget.getVadjustment()\n return {\n nativeEvent: {\n contentOffset: {\n x: hadjustment?.getValue() ?? 0,\n y: vadjustment?.getValue() ?? 0,\n },\n contentSize: { width: contentRect.width, height: contentRect.height },\n layoutMeasurement: {\n width: hadjustment?.getPageSize() ?? 0,\n height: vadjustment?.getPageSize() ?? 0,\n },\n },\n }\n }\n\n const emitScroll = (): void => {\n if (!onScroll) {\n return\n }\n const event = readScrollEvent()\n if (event) {\n onScroll(event)\n }\n }\n\n const adjustment = horizontal\n ? (scrolled?.getHadjustment() ?? null)\n : (scrolled?.getVadjustment() ?? null)\n const onAdjustment = (): void => {\n if (!perfEnabled) {\n emitScroll()\n return\n }\n // Perf: full wall time of one scroll tick's JS work — the FFI reads in\n // emitScroll plus the app/VirtualizedList onScroll handler (windowing,\n // viewability, endReached) — everything that blocks the GTK frame.\n perfCount(\"scroll.events\")\n const start = perfNow()\n emitScroll()\n perfAddTime(\"scroll.js\", perfNow() - start)\n }\n\n // --- the four scroll phases -----------------------------------------\n //\n // What GTK actually reports, measured on 4.22.4 under a real pointer\n // (docs/research/scroll-phases.md, and the trace is in\n // tests/gtk/components/scroll-phases.gtk.test.tsx):\n //\n // - a mouse WHEEL emits `::scroll` per detent and NOTHING around it.\n // GTK gives us no sequence, so while a consumer asks for begin/end we\n // synthesize a desktop scroll SESSION: begin before the first detent\n // mutates the adjustment, end after a measured idle window, and no\n // momentum. This is an intentional desktop extension — RN has no\n // wheel — and is what lets a phase-aware consumer capture the offset\n // it is about to constrain instead of discovering it one event late.\n // - a touchpad GLIDE emits `::scroll-begin`, a stream of `::scroll`,\n // `::scroll-end` and `::decelerate` — a real sequence with a real\n // beginning and end — and the scrolled window's own kinetic\n // animation then carries the adjustment on for another second or so.\n // All four phases exist, and all four are reported.\n //\n // Drag maps onto the scroll SEQUENCE rather than onto a finger on the\n // content: `::scroll-begin` is \"the user started driving this scroller\",\n // which is what a consumer of `onScrollBeginDrag` acts on and is as close\n // as a device that never touches the content can get. That is the one\n // approximation here, and it is named in docs/api.md.\n //\n // Momentum is read off the ADJUSTMENT, not off `::decelerate`. The\n // controller emits `decelerate` at every `scroll-end` — it reports the\n // velocity it measured, not a decision to coast — while the scrolled\n // window decides for itself whether that velocity is worth animating. RN\n // fires `onMomentumScrollBegin` only when the view actually keeps\n // moving, so the honest source is the movement itself.\n const phaseSink = scrollPhaseSink(onScroll)\n // `wants()` rather than the sink's mere presence: every Reanimated scroll\n // handler carries one, and most of them ask for nothing but `onScroll`.\n // Asking is what keeps the common case free.\n const wantsPhases = Boolean(\n onScrollBeginDrag ||\n onScrollEndDrag ||\n onMomentumScrollBegin ||\n onMomentumScrollEnd ||\n phaseSink?.wants(),\n )\n // GTK brackets touchpad gestures for us. A wheel has only detents, so the\n // begin/end extension below needs to see those detents — but only when a\n // consumer asks for either end of the session. A momentum-only handler\n // still pays no JS call per wheel event, because a wheel never coasts.\n const wantsWheelSessions = Boolean(\n onScrollBeginDrag ||\n onScrollEndDrag ||\n phaseSink?.wants(\"beginDrag\") ||\n phaseSink?.wants(\"endDrag\"),\n )\n\n // Read per event rather than captured, exactly as `useAnimatedScrollHandler`\n // does it: a re-render that changed a handler must be picked up without\n // tearing the GTK controller down and building a new one.\n const phaseHandlers = useRef({\n onScrollBeginDrag,\n onScrollEndDrag,\n onMomentumScrollBegin,\n onMomentumScrollEnd,\n phaseSink,\n readScrollEvent,\n })\n phaseHandlers.current = {\n onScrollBeginDrag,\n onScrollEndDrag,\n onMomentumScrollBegin,\n onMomentumScrollEnd,\n phaseSink,\n readScrollEvent,\n }\n\n useLayoutEffect(() => {\n // The whole cost story is this early return. No phase handler means no\n // controller, no signal, no tick callback and no bookkeeping — the\n // widget is byte-for-byte the one this component built before the\n // phases existed.\n if (!wantsPhases) {\n return\n }\n const widget = scrolled\n if (!widget) {\n return\n }\n\n const emitPhase = (phase: ScrollPhase): void => {\n const handlers = phaseHandlers.current\n const event = handlers.readScrollEvent()\n if (!event) {\n return\n }\n if (phase === \"beginDrag\") {\n handlers.onScrollBeginDrag?.(event)\n } else if (phase === \"endDrag\") {\n handlers.onScrollEndDrag?.(event)\n } else if (phase === \"momentumBegin\") {\n handlers.onMomentumScrollBegin?.(event)\n } else {\n handlers.onMomentumScrollEnd?.(event)\n }\n handlers.phaseSink?.deliver(phase, event)\n }\n\n const axis = horizontal\n ? widget.getHadjustment()\n : widget.getVadjustment()\n\n // Momentum is watched on the ADJUSTMENT, with two timers and no tick\n // callback. A frame-polling watcher was the first shape and it was\n // wrong twice over: it burns a callback per frame for the length of\n // every coast, and it counts FRAMES, which is not a unit — under the\n // headless compositor the frame clock free-runs at ~106 ticks per\n // millisecond (measured, docs/research/scroll-phases.md), so a\n // four-frame grace expired in microseconds. Wall time means the same\n // thing everywhere.\n //\n // Milliseconds to wait after `::scroll-end` for the scrolled window's\n // kinetic animation to take over before concluding it never will.\n // Measured handoff: `::scroll-end` at 1921.9 ms, first inertial value\n // change at 1927.5 ms — 5.6 ms. 120 is slack, not a guess.\n const handoffMs = 120\n // Milliseconds of a motionless adjustment that end the momentum. GTK's\n // deceleration steps the value every frame right to the end (16.7 ms\n // apart in the trace), so 60 ms is three and a half missed steps.\n const restMs = 60\n\n let watching = false\n let coasting = false\n let lastMoveAt = 0\n let handoffTimer: ReturnType<typeof setTimeout> | null = null\n let restTimer: ReturnType<typeof setTimeout> | null = null\n\n const clearTimers = (): void => {\n if (handoffTimer !== null) {\n clearTimeout(handoffTimer)\n handoffTimer = null\n }\n if (restTimer !== null) {\n clearTimeout(restTimer)\n restTimer = null\n }\n }\n\n // Re-armed for the REMAINING idle time rather than reset on every step,\n // so the number of timers a coast costs is bounded by its duration\n // (one per `restMs`) and not by how many times the adjustment moved.\n // The difference is not academic: under the headless compositor's\n // free-running frame clock a single coast steps the value ~150,000\n // times, and a clear-and-rearm per step is 150,000 timers.\n const checkRest = (): void => {\n const idle = perfNow() - lastMoveAt\n if (idle < restMs) {\n restTimer = setTimeout(checkRest, restMs - idle)\n return\n }\n restTimer = null\n stopMomentumWatch()\n emitPhase(\"momentumEnd\")\n }\n\n const onCoastStep = (): void => {\n lastMoveAt = perfNow()\n if (coasting) {\n return\n }\n coasting = true\n if (handoffTimer !== null) {\n clearTimeout(handoffTimer)\n handoffTimer = null\n }\n emitPhase(\"momentumBegin\")\n restTimer = setTimeout(checkRest, restMs)\n }\n\n const stopMomentumWatch = (): void => {\n clearTimers()\n if (watching) {\n axis?.off(\"value-changed\", onCoastStep)\n watching = false\n }\n coasting = false\n }\n\n const watchMomentum = (): void => {\n if (watching) {\n return\n }\n watching = true\n axis?.on(\"value-changed\", onCoastStep)\n handoffTimer = setTimeout(() => {\n handoffTimer = null\n if (!coasting) {\n // The scroller came to rest with the drag. No inertia, so no\n // momentum pair — which is RN's behaviour too.\n stopMomentumWatch()\n }\n }, handoffMs)\n }\n\n const controller = Gtk.EventControllerScroll.new(\n // An axis flag is REQUIRED, and not for the deltas: measured, a\n // controller created with `KINETIC` alone emits nothing at all — not\n // even `::scroll-begin`. `::scroll` is therefore emitted. It stays\n // unconnected unless somebody asks for begin/end: only then does a\n // wheel detent enter JS to maintain the desktop session below; a\n // momentum-only or untracked scroller keeps the old empty C signal\n // path.\n Gtk.EventControllerScrollFlags.BOTH_AXES |\n Gtk.EventControllerScrollFlags.KINETIC,\n )\n // CAPTURE, so the phase is reported before the scrolled window's own\n // controller acts on the same event and moves the adjustment. Nothing\n // here handles the event, so propagation is untouched either way.\n controller.setPropagationPhase(Gtk.PropagationPhase.CAPTURE)\n\n const beginDrag = (): void => {\n // A new drag during a coast: RN ends the momentum before beginning\n // the drag, so a consumer never sees two live phases at once.\n if (coasting) {\n stopMomentumWatch()\n emitPhase(\"momentumEnd\")\n } else {\n stopMomentumWatch()\n }\n emitPhase(\"beginDrag\")\n }\n // A wheel gives GTK no begin/end signals. Group its detents into one\n // user-driven session, ending after an idle interval longer than the\n // ordinary detent spacing measured in docs/research/scroll-phases.md\n // (20–33 ms in the trace). The callback runs in CAPTURE, so beginDrag\n // sees the PRE-scroll offset before GtkScrolledWindow mutates its\n // adjustment and emits the onScroll callback that consumes the context.\n const wheelSession = createWheelScrollSession(beginDrag, () =>\n emitPhase(\"endDrag\"),\n )\n // A touchpad brackets itself, so its sequence owns the session while it\n // runs. Two live sessions at once would hand a consumer a second\n // `beginDrag` before the first `endDrag` — which is exactly the state\n // gorhom's lock reads to decide where to pin its list.\n let nativeSequence = false\n const onScrollBegin = (): void => {\n nativeSequence = true\n // A wheel burst may still be waiting out its idle timer.\n wheelSession.finish()\n beginDrag()\n }\n const onScrollEnd = (): void => {\n nativeSequence = false\n emitPhase(\"endDrag\")\n watchMomentum()\n }\n const onScroll = (): boolean => {\n if (\n !wantsWheelSessions ||\n nativeSequence ||\n controller.getUnit() !== Gdk.ScrollUnit.WHEEL\n ) {\n return false\n }\n wheelSession.detent()\n // Observe only. GtkScrolledWindow still receives and applies the\n // detent, which is what makes this phase seam transparent.\n return false\n }\n\n controller.on(\"scroll-begin\", onScrollBegin)\n controller.on(\"scroll-end\", onScrollEnd)\n if (wantsWheelSessions) {\n controller.on(\"scroll\", onScroll)\n }\n widget.addController(controller)\n\n return () => {\n controller.off(\"scroll-begin\", onScrollBegin)\n controller.off(\"scroll-end\", onScrollEnd)\n if (wantsWheelSessions) {\n controller.off(\"scroll\", onScroll)\n }\n wheelSession.dispose()\n widget.removeController(controller)\n stopMomentumWatch()\n }\n }, [scrolled, wantsPhases, wantsWheelSessions, horizontal])\n\n // Content-size reports dedupe on the engine rect: \"changed\" also fires\n // for pure viewport (page-size) changes, which RN does not report.\n const lastContentSize = useRef({ width: -1, height: -1 })\n const onRangeChanged = (): void => {\n if (!onContentSizeChange) {\n return\n }\n const rect = contentNode.getRect()\n if (!rect) {\n return\n }\n const last = lastContentSize.current\n if (rect.width === last.width && rect.height === last.height) {\n return\n }\n lastContentSize.current = { width: rect.width, height: rect.height }\n onContentSizeChange(rect.width, rect.height)\n }\n\n // Frame-synced sticky driver: a pure scroll TRANSLATES the content\n // without re-allocating it (same size → GTK skips the vfunc), so signal\n // handlers land a frame late and the pinned header jitters. A tick\n // callback runs in the frame's UPDATE phase, before layout: when the\n // scroll offset moved, queue an allocation for THIS frame — the\n // translation and the pin correction then paint atomically.\n useLayoutEffect(() => {\n if (!hasSticky || horizontal) {\n return\n }\n const widget = scrolled\n if (!widget) {\n return\n }\n const vadjustment = widget.getVadjustment()\n let lastValue = vadjustment?.getValue() ?? 0\n // Frames to keep requeueing after the last observed move. The scrolled\n // window's own kinetic tick may run AFTER ours, so this tick's read can\n // be one frame stale: without the grace window the first frame of a\n // deceleration (its value change lands after our read) would miss its\n // correction. Kinetic deceleration changes the value every frame, so\n // three frames of slack cover the handoff at both ends of the motion.\n const graceFrames = 3\n let remaining = graceFrames\n const id = widget.addTickCallback(() => {\n perfCount(\"sticky.ticks\")\n const value = vadjustment?.getValue() ?? 0\n if (value !== lastValue) {\n lastValue = value\n remaining = graceFrames\n } else if (remaining > 0) {\n remaining -= 1\n } else {\n // At rest: the pin is already correct and nothing translated it,\n // so an allocation pass per frame would be pure idle cost.\n return true\n }\n perfCount(\"sticky.queues\")\n const content = contentRef.current\n if (content) {\n queueAllocate(content)\n }\n return true\n })\n return () => {\n widget.removeTickCallback(id)\n }\n }, [scrolled, hasSticky, horizontal])\n\n // Perf: observe the GTK frame clock through a tick callback — the delta\n // between consecutive ticks is the real frame interval (late frames show\n // up directly). Note: registering a tick callback keeps the frame clock\n // running, so idle seconds show ~60 ticks too; the reporter separates\n // load by the accompanying counters.\n useLayoutEffect(() => {\n if (!perfEnabled) {\n return\n }\n ensurePerfReporter()\n const widget = scrolled\n if (!widget) {\n return\n }\n const id = widget.addTickCallback(() => {\n perfFrameTick(perfNow())\n return true\n })\n return () => {\n widget.removeTickCallback(id)\n }\n }, [scrolled])\n useSignal(adjustment, \"value-changed\", onAdjustment)\n // Both axes report into the same handler — RN's callback carries both\n // dimensions no matter which one moved.\n useSignal(scrolled?.getVadjustment() ?? null, \"changed\", onRangeChanged)\n useSignal(scrolled?.getHadjustment() ?? null, \"changed\", onRangeChanged)\n\n return (\n <GtkScrolledWindow\n ref={(widget: Gtk.ScrolledWindow | null) => {\n outerRef.current = widget\n setScrolled(widget)\n }}\n name={testID}\n >\n <GtkBox ref={contentRef}>\n <HostNodeContext.Provider\n value={{\n engine: host.engine,\n node: contentNode,\n widgetRef: contentRef,\n }}\n >\n <StickyRegistryContext.Provider value={stickyRegistry.current}>\n {stickySet.length === 0\n ? children\n : Children.toArray(children).map((child, index) =>\n stickySet.includes(index) ? (\n <StickySlot\n key={`sticky-slot-${index}`}\n stickyKey={`index-${index}`}\n >\n {child}\n </StickySlot>\n ) : (\n child\n ),\n )}\n </StickyRegistryContext.Provider>\n </HostNodeContext.Provider>\n </GtkBox>\n </GtkScrolledWindow>\n )\n },\n)\nScrollView.displayName = \"ScrollView\"\n"]}
@@ -695,10 +695,7 @@ const VirtualizedListInner = forwardRef(({ data, getItem, getItemCount, renderIt
695
695
  void extraData;
696
696
  // Accepted for RN parity only; no desktop gesture ever calls it.
697
697
  void onRefresh;
698
- return (_jsxs(ScrollView, { ...scrollProps, horizontal: horizontal, ref: scrollRef, onScroll: handleScroll, onContentSizeChange: handleContentSizeChange, onLayout: handleLayout, children: [refreshing ? (
699
- // Desktop RefreshControl: no pull gesture exists, so `refreshing`
700
- // simply shows an in-flow spinner row above the list content.
701
- _jsx(View, { style: { alignItems: "center", padding: 8 }, children: _jsx(ActivityIndicator, {}) })) : null, renderAux(inverted ? ListFooterComponent : ListHeaderComponent), count === 0 && renderAux(ListEmptyComponent), count > 0 && (_jsx(View, { style: horizontal
698
+ return (_jsxs(ScrollView, { ...scrollProps, horizontal: horizontal, ref: scrollRef, onScroll: handleScroll, onContentSizeChange: handleContentSizeChange, onLayout: handleLayout, children: [refreshing ? (_jsx(View, { style: { alignItems: "center", padding: 8 }, children: _jsx(ActivityIndicator, {}) })) : null, renderAux(inverted ? ListFooterComponent : ListHeaderComponent), count === 0 && renderAux(ListEmptyComponent), count > 0 && (_jsx(View, { style: horizontal
702
699
  ? { width: offsets[count] }
703
700
  : { height: offsets[count] }, children: cells })), renderAux(inverted ? ListHeaderComponent : ListFooterComponent)] }));
704
701
  });