react-native-gtkx 0.1.0-alpha.2 → 0.1.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 (85) hide show
  1. package/README.md +4 -5
  2. package/dist/apis/back-handler.d.ts +9 -0
  3. package/dist/apis/back-handler.js +30 -0
  4. package/dist/apis/back-handler.js.map +1 -0
  5. package/dist/apis/host.d.ts +7 -1
  6. package/dist/apis/host.gtkx.js +12 -1
  7. package/dist/apis/host.gtkx.js.map +1 -1
  8. package/dist/apis/host.js.map +1 -1
  9. package/dist/apis/i18n-manager.d.ts +14 -0
  10. package/dist/apis/i18n-manager.js +20 -0
  11. package/dist/apis/i18n-manager.js.map +1 -0
  12. package/dist/apis/index.d.ts +17 -0
  13. package/dist/apis/index.js +5 -0
  14. package/dist/apis/index.js.map +1 -1
  15. package/dist/apis/interaction-manager.d.ts +16 -0
  16. package/dist/apis/interaction-manager.js +96 -0
  17. package/dist/apis/interaction-manager.js.map +1 -0
  18. package/dist/apis/linking.d.ts +4 -0
  19. package/dist/apis/linking.js +9 -0
  20. package/dist/apis/linking.js.map +1 -1
  21. package/dist/components/app-registry.d.ts +2 -0
  22. package/dist/components/app-registry.js +19 -2
  23. package/dist/components/app-registry.js.map +1 -1
  24. package/dist/components/flat-list.d.ts +2 -0
  25. package/dist/components/flat-list.js.map +1 -1
  26. package/dist/components/index.d.ts +1 -1
  27. package/dist/components/index.js +1 -1
  28. package/dist/components/index.js.map +1 -1
  29. package/dist/components/modal.d.ts +1 -1
  30. package/dist/components/root.d.ts +10 -1
  31. package/dist/components/root.js +39 -1
  32. package/dist/components/root.js.map +1 -1
  33. package/dist/components/scroll-view.js +58 -5
  34. package/dist/components/scroll-view.js.map +1 -1
  35. package/dist/components/text-input.d.ts +1 -0
  36. package/dist/components/text-input.js +31 -3
  37. package/dist/components/text-input.js.map +1 -1
  38. package/dist/components/use-layout-child.d.ts +1 -1
  39. package/dist/components/use-layout-child.js +22 -2
  40. package/dist/components/use-layout-child.js.map +1 -1
  41. package/dist/components/virtualized-list.d.ts +2 -0
  42. package/dist/components/virtualized-list.js +132 -10
  43. package/dist/components/virtualized-list.js.map +1 -1
  44. package/dist/gtkx/bridge/index.d.ts +4 -2
  45. package/dist/gtkx/bridge/index.js +6 -2
  46. package/dist/gtkx/bridge/index.js.map +1 -1
  47. package/dist/gtkx/bridge/measure.js +5 -0
  48. package/dist/gtkx/bridge/measure.js.map +1 -1
  49. package/dist/gtkx/bridge/use-signal.d.ts +2 -0
  50. package/dist/gtkx/bridge/use-signal.js +30 -0
  51. package/dist/gtkx/bridge/use-signal.js.map +1 -0
  52. package/dist/index.d.ts +2 -2
  53. package/dist/index.js +2 -2
  54. package/dist/index.js.map +1 -1
  55. package/dist/layout/engine.d.ts +4 -0
  56. package/dist/layout/engine.js +97 -5
  57. package/dist/layout/engine.js.map +1 -1
  58. package/dist/layout/node.d.ts +1 -1
  59. package/dist/layout/node.js +8 -5
  60. package/dist/layout/node.js.map +1 -1
  61. package/dist/metro/index.js +1 -0
  62. package/dist/metro/index.js.map +1 -1
  63. package/dist/navigation/index.d.ts +44 -0
  64. package/dist/navigation/index.js +216 -0
  65. package/dist/navigation/index.js.map +1 -0
  66. package/dist/navigation/option-warnings.d.ts +2 -0
  67. package/dist/navigation/option-warnings.js +61 -0
  68. package/dist/navigation/option-warnings.js.map +1 -0
  69. package/dist/navigation/sidebar.d.ts +37 -0
  70. package/dist/navigation/sidebar.js +71 -0
  71. package/dist/navigation/sidebar.js.map +1 -0
  72. package/dist/perf.d.ts +9 -0
  73. package/dist/perf.js +156 -0
  74. package/dist/perf.js.map +1 -0
  75. package/dist/runner/host-dev.js +15 -6
  76. package/dist/runner/host-dev.js.map +1 -1
  77. package/dist/runner/host.js +24 -9
  78. package/dist/runner/host.js.map +1 -1
  79. package/dist/runner/index.js +4 -1
  80. package/dist/runner/index.js.map +1 -1
  81. package/dist/vite/index.d.ts +5 -4
  82. package/dist/vite/index.js +41 -7
  83. package/dist/vite/index.js.map +1 -1
  84. package/package.json +18 -10
  85. package/types.d.ts +13 -0
@@ -1 +1 @@
1
- {"version":3,"file":"app-registry.js","sourceRoot":"","sources":["../../src/components/app-registry.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,eAAe,EAAsB,MAAM,OAAO,CAAA;AAC3D,OAAO,EACL,UAAU,EACV,GAAG,EACH,cAAc,EACd,oBAAoB,EACpB,IAAI,GACL,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAI7B,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA6B,CAAA;AASrD,4EAA4E;AAC5E,2EAA2E;AAC3E,8EAA8E;AAC9E,2EAA2E;AAC3E,uEAAuE;AACvE,2EAA2E;AAC3E,sCAAsC;AACtC,IAAI,gBAAgB,GAAG,KAAK,CAAA;AAC5B,MAAM,qBAAqB,GAAG,GAAS,EAAE;IACvC,IAAI,gBAAgB,EAAE,CAAC;QACrB,OAAM;IACR,CAAC;IACD,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAA;IAC1C,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAM;IACR,CAAC;IACD,gBAAgB,GAAG,IAAI,CAAA;IACvB,uEAAuE;IACvE,6CAA6C;IAC7C,QAAQ,CAAC,mBAAmB,GAAG,IAAI,CAAA;AACrC,CAAC,CAAA;AAED,MAAM,aAAa,GAAG,CAAC,EACrB,GAAG,EACH,YAAY,EACZ,YAAY,EACZ,aAAa,GAMd,EAAE,EAAE;IACH,uEAAuE;IACvE,0CAA0C;IAC1C,eAAe,CAAC,GAAG,EAAE;QACnB,qBAAqB,EAAE,CAAA;IACzB,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,qEAAqE;IACrE,yEAAyE;IACzE,wEAAwE;IACxE,yEAAyE;IACzE,iCAAiC;IACjC,OAAO,CACL,KAAC,IAAI,IACH,KAAK,EAAE,YAAY,EACnB,MAAM,EAAE,aAAa,EACrB,gBAAgB,kBAEhB,KAAC,GAAG,OAAK,YAAY,GAAI,GACpB,CACR,CAAA;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,iBAAiB,CAAC,MAAc,EAAE,QAA2B;QAC3D,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;QAC9B,OAAO,MAAM,CAAA;IACf,CAAC;IAED,UAAU;QACR,OAAO,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAA;IAC7B,CAAC;IAED,cAAc,CAAC,MAAc,EAAE,SAA+B,EAAE;QAC9D,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;QACrC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CACb,6CAA6C,MAAM,KAAK;gBACtD,eAAe,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,QAAQ,EAAE,CAC/D,CAAA;QACH,CAAC;QACD,MAAM,GAAG,GAAG,QAAQ,EAAE,CAAA;QACtB,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,GAAG,CAAA;QACjC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,GAAG,CAAA;QAEnC,MAAM,SAAS,GAAG,GAAG,EAAE,CAAC,CACtB,KAAC,oBAAoB,IACnB,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,MAAM,EAC7B,YAAY,EAAE,KAAK,EACnB,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,IAAI,YAEpB,KAAC,aAAa,IACZ,GAAG,EAAE,GAAG,EACR,YAAY,EAAE,MAAM,CAAC,YAAY,IAAI,EAAE,EACvC,YAAY,EAAE,KAAK,EACnB,aAAa,EAAE,MAAM,GACrB,GACmB,CACxB,CAAA;QAED,UAAU,EAAE,CAAC,MAAM,CACjB,KAAC,cAAc,cACb,KAAC,SAAS,KAAG,GACE,CAClB,CAAA;IACH,CAAC;CACF,CAAA","sourcesContent":["import { useLayoutEffect, type ComponentType } from \"react\"\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\nexport type RunApplicationParams = {\n initialProps?: Record<string, unknown>\n title?: string\n width?: number\n height?: number\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\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 AppWindow = () => (\n <GtkApplicationWindow\n title={params.title ?? appKey}\n defaultWidth={width}\n defaultHeight={height}\n onCloseRequest={quit}\n >\n <WindowContent\n App={App}\n initialProps={params.initialProps ?? {}}\n initialWidth={width}\n initialHeight={height}\n />\n </GtkApplicationWindow>\n )\n\n createRoot().render(\n <GtkApplication>\n <AppWindow />\n </GtkApplication>,\n )\n },\n}\n"]}
1
+ {"version":3,"file":"app-registry.js","sourceRoot":"","sources":["../../src/components/app-registry.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,eAAe,EAAsB,MAAM,OAAO,CAAA;AAC3D,OAAO,EACL,oBAAoB,EACpB,UAAU,EACV,GAAG,EACH,cAAc,EACd,oBAAoB,EACpB,IAAI,GACL,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAI7B,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA6B,CAAA;AAErD,uEAAuE;AACvE,0EAA0E;AAC1E,0EAA0E;AAC1E,IAAI,YAAY,GAAgC,IAAI,CAAA;AACpD,MAAM,CAAC,MAAM,eAAe,GAAG,GAAgC,EAAE,CAAC,YAAY,CAAA;AAc9E,4EAA4E;AAC5E,2EAA2E;AAC3E,8EAA8E;AAC9E,2EAA2E;AAC3E,uEAAuE;AACvE,2EAA2E;AAC3E,sCAAsC;AACtC,IAAI,gBAAgB,GAAG,KAAK,CAAA;AAC5B,MAAM,qBAAqB,GAAG,GAAS,EAAE;IACvC,IAAI,gBAAgB,EAAE,CAAC;QACrB,OAAM;IACR,CAAC;IACD,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAA;IAC1C,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAM;IACR,CAAC;IACD,gBAAgB,GAAG,IAAI,CAAA;IACvB,uEAAuE;IACvE,6CAA6C;IAC7C,QAAQ,CAAC,mBAAmB,GAAG,IAAI,CAAA;AACrC,CAAC,CAAA;AAED,0EAA0E;AAC1E,2EAA2E;AAC3E,4EAA4E;AAC5E,MAAM,aAAa,GAAG,CAAC,EACrB,GAAG,EACH,YAAY,GAIb,EAAE,EAAE;IACH,eAAe,CAAC,GAAG,EAAE;QACnB,qBAAqB,EAAE,CAAA;IACzB,CAAC,EAAE,EAAE,CAAC,CAAA;IACN,OAAO,KAAC,GAAG,OAAK,YAAY,GAAI,CAAA;AAClC,CAAC,CAAA;AAED,MAAM,aAAa,GAAG,CAAC,EACrB,GAAG,EACH,YAAY,EACZ,YAAY,EACZ,aAAa,GAMd,EAAE,EAAE;IACH,uEAAuE;IACvE,0CAA0C;IAC1C,eAAe,CAAC,GAAG,EAAE;QACnB,qBAAqB,EAAE,CAAA;IACzB,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,qEAAqE;IACrE,yEAAyE;IACzE,wEAAwE;IACxE,yEAAyE;IACzE,iCAAiC;IACjC,OAAO,CACL,KAAC,IAAI,IACH,KAAK,EAAE,YAAY,EACnB,MAAM,EAAE,aAAa,EACrB,gBAAgB,kBAEhB,KAAC,GAAG,OAAK,YAAY,GAAI,GACpB,CACR,CAAA;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,iBAAiB,CAAC,MAAc,EAAE,QAA2B;QAC3D,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;QAC9B,OAAO,MAAM,CAAA;IACf,CAAC;IAED,UAAU;QACR,OAAO,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAA;IAC7B,CAAC;IAED,cAAc,CAAC,MAAc,EAAE,SAA+B,EAAE;QAC9D,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;QACrC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CACb,6CAA6C,MAAM,KAAK;gBACtD,eAAe,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,QAAQ,EAAE,CAC/D,CAAA;QACH,CAAC;QACD,MAAM,GAAG,GAAG,QAAQ,EAAE,CAAA;QACtB,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,GAAG,CAAA;QACjC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,GAAG,CAAA;QAEnC,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,KAAK,SAAS,CAAA;QACjD,YAAY,GAAG,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAA;QACnD,MAAM,MAAM,GAAG,aAAa,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,oBAAoB,CAAA;QAC1E,MAAM,SAAS,GAAG,GAAG,EAAE,CAAC,CACtB,KAAC,MAAM,IACL,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,MAAM,EAC7B,YAAY,EAAE,KAAK,EACnB,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,IAAI,YAEnB,aAAa,CAAC,CAAC,CAAC,CACf,KAAC,aAAa,IACZ,GAAG,EAAE,GAAG,EACR,YAAY,EAAE,MAAM,CAAC,YAAY,IAAI,EAAE,GACvC,CACH,CAAC,CAAC,CAAC,CACF,KAAC,aAAa,IACZ,GAAG,EAAE,GAAG,EACR,YAAY,EAAE,MAAM,CAAC,YAAY,IAAI,EAAE,EACvC,YAAY,EAAE,KAAK,EACnB,aAAa,EAAE,MAAM,GACrB,CACH,GACM,CACV,CAAA;QAED,UAAU,EAAE,CAAC,MAAM,CACjB,KAAC,cAAc,cACb,KAAC,SAAS,KAAG,GACE,CAClB,CAAA;IACH,CAAC;CACF,CAAA","sourcesContent":["import { useLayoutEffect, type ComponentType } 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\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\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 const Window = contentChrome ? AdwApplicationWindow : GtkApplicationWindow\n const AppWindow = () => (\n <Window\n title={params.title ?? appKey}\n defaultWidth={width}\n defaultHeight={height}\n onCloseRequest={quit}\n >\n {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 </Window>\n )\n\n createRoot().render(\n <GtkApplication>\n <AppWindow />\n </GtkApplication>,\n )\n },\n}\n"]}
@@ -23,6 +23,8 @@ export type FlatListProps<T> = Omit<ScrollViewProps, "children"> & {
23
23
  getItemLayout?: (data: readonly T[], index: number) => ItemLayout;
24
24
  windowSize?: number;
25
25
  initialNumToRender?: number;
26
+ maxToRenderPerBatch?: number;
27
+ updateCellsBatchingPeriod?: number;
26
28
  extraData?: unknown;
27
29
  inverted?: boolean;
28
30
  refreshing?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"flat-list.js","sourceRoot":"","sources":["../../src/components/flat-list.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAyC,MAAM,OAAO,CAAA;AAGzE,OAAO,EACL,eAAe,GAMhB,MAAM,oBAAoB,CAAA;AAqC3B,6EAA6E;AAC7E,mEAAmE;AACnE,MAAM,aAAa,GAAG,UAAU,CAC9B,CAAK,KAAuB,EAAE,GAAqC,EAAE,EAAE,CAAC,CACtE,KAAC,eAAe,OACV,KAAK,EACT,GAAG,EAAE,GAAG,GACR,CACH,CACF,CAAA;AACD,aAAa,CAAC,WAAW,GAAG,UAAU,CAAA;AAEtC,MAAM,CAAC,MAAM,QAAQ,GAAG,aAEP,CAAA;AA+BjB,MAAM,CAAC,MAAM,WAAW,GAAG,CAAK,EAC9B,QAAQ,EACR,UAAU,EACV,mBAAmB,EACnB,2BAA2B,GAAG,IAAI,EAClC,GAAG,EACH,GAAG,IAAI,EACa,EAAgB,EAAE;IACtC,MAAM,IAAI,GAAoB,EAAE,CAAA;IAChC,MAAM,aAAa,GAAa,EAAE,CAAA;IAClC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAC/B,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAA;QACtC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;YACnC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;QAC1C,CAAC,CAAC,CAAA;IACJ,CAAC;IACD,OAAO,CACL,KAAC,QAAQ,OACH,IAAI,EACR,GAAG,EAAE,GAAG,EACR,IAAI,EAAE,IAAI,EACV,mBAAmB,EACjB,2BAA2B,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,EAEzD,YAAY,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,EAC5C,UAAU,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,EAAE,CAC5B,GAAG,CAAC,IAAI,KAAK,QAAQ;YACnB,CAAC,CAAC,CAAC,mBAAmB,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,IAAI,IAAI,CAAC;YAC3D,CAAC,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC,GAEtD,CACH,CAAA;AACH,CAAC,CAAA","sourcesContent":["import { forwardRef, type ComponentType, type ReactElement } from \"react\"\nimport type { StyleProp } from \"../contracts\"\nimport type { ScrollViewProps } from \"./scroll-view\"\nimport {\n VirtualizedList,\n type ItemLayout,\n type ListRenderItemInfo,\n type ViewabilityConfig,\n type ViewToken,\n type VirtualizedListHandle,\n} from \"./virtualized-list\"\n\nexport type {\n ListRenderItemInfo,\n ViewabilityConfig,\n ViewToken,\n} from \"./virtualized-list\"\n// The FlatList ref exposes the full scroll surface of the windowed core\n// (scrollTo, scrollToEnd, scrollToIndex, scrollToItem, scrollToOffset).\nexport type { VirtualizedListHandle as FlatListHandle } from \"./virtualized-list\"\n\nexport type FlatListProps<T> = Omit<ScrollViewProps, \"children\"> & {\n data: readonly T[]\n renderItem: (info: ListRenderItemInfo<T>) => ReactElement | null\n keyExtractor?: (item: T, index: number) => string\n ItemSeparatorComponent?: ComponentType | null\n ListHeaderComponent?: ComponentType | ReactElement | null\n ListFooterComponent?: ComponentType | ReactElement | null\n ListEmptyComponent?: ComponentType | ReactElement | null\n onEndReached?: () => void\n onEndReachedThreshold?: number\n onViewableItemsChanged?: (info: {\n viewableItems: ViewToken<T>[]\n changed: ViewToken<T>[]\n }) => void\n viewabilityConfig?: ViewabilityConfig\n estimatedItemSize?: number\n getItemLayout?: (data: readonly T[], index: number) => ItemLayout\n windowSize?: number\n initialNumToRender?: number\n extraData?: unknown\n inverted?: boolean\n refreshing?: boolean\n onRefresh?: () => void\n style?: StyleProp\n}\n\n// FlatList is a thin veneer over the windowed VirtualizedList core: only the\n// rows around the viewport are mounted (see virtualized-list.tsx).\nconst FlatListInner = forwardRef(\n <T,>(props: FlatListProps<T>, ref: React.Ref<VirtualizedListHandle>) => (\n <VirtualizedList\n {...props}\n ref={ref}\n />\n ),\n)\nFlatListInner.displayName = \"FlatList\"\n\nexport const FlatList = FlatListInner as <T>(\n props: FlatListProps<T> & { ref?: React.Ref<VirtualizedListHandle> },\n) => ReactElement\n\nexport type SectionListData<T> = { title: string; data: readonly T[] }\n\n// Viewability props are excluded: SectionList flattens sections into private\n// row records, so ViewTokens would leak that internal row type instead of T —\n// section-aware tokens are not implemented yet.\nexport type SectionListProps<T> = Omit<\n FlatListProps<T>,\n | \"data\"\n | \"renderItem\"\n | \"getItemLayout\"\n | \"onViewableItemsChanged\"\n | \"viewabilityConfig\"\n> & {\n sections: readonly SectionListData<T>[]\n renderItem: (info: ListRenderItemInfo<T>) => ReactElement | null\n renderSectionHeader?: (info: {\n section: SectionListData<T>\n }) => ReactElement | null\n // RN default on iOS; ours defaults to true too — section headers pin to\n // the top and get pushed out by the next section's header.\n stickySectionHeadersEnabled?: boolean\n // React 19 ref-as-prop: the same scroll surface FlatList exposes.\n ref?: React.Ref<VirtualizedListHandle>\n}\n\ntype SectionRow<T> =\n | { kind: \"header\"; section: SectionListData<T> }\n | { kind: \"item\"; item: T; index: number }\n\nexport const SectionList = <T,>({\n sections,\n renderItem,\n renderSectionHeader,\n stickySectionHeadersEnabled = true,\n ref,\n ...rest\n}: SectionListProps<T>): ReactElement => {\n const rows: SectionRow<T>[] = []\n const headerIndices: number[] = []\n for (const section of sections) {\n headerIndices.push(rows.length)\n rows.push({ kind: \"header\", section })\n section.data.forEach((item, index) => {\n rows.push({ kind: \"item\", item, index })\n })\n }\n return (\n <FlatList\n {...rest}\n ref={ref}\n data={rows}\n stickyHeaderIndices={\n stickySectionHeadersEnabled ? headerIndices : undefined\n }\n keyExtractor={(_row, index) => String(index)}\n renderItem={({ item: row }) =>\n row.kind === \"header\"\n ? (renderSectionHeader?.({ section: row.section }) ?? null)\n : renderItem({ item: row.item, index: row.index })\n }\n />\n )\n}\n"]}
1
+ {"version":3,"file":"flat-list.js","sourceRoot":"","sources":["../../src/components/flat-list.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAyC,MAAM,OAAO,CAAA;AAGzE,OAAO,EACL,eAAe,GAMhB,MAAM,oBAAoB,CAAA;AAuC3B,6EAA6E;AAC7E,mEAAmE;AACnE,MAAM,aAAa,GAAG,UAAU,CAC9B,CAAK,KAAuB,EAAE,GAAqC,EAAE,EAAE,CAAC,CACtE,KAAC,eAAe,OACV,KAAK,EACT,GAAG,EAAE,GAAG,GACR,CACH,CACF,CAAA;AACD,aAAa,CAAC,WAAW,GAAG,UAAU,CAAA;AAEtC,MAAM,CAAC,MAAM,QAAQ,GAAG,aAEP,CAAA;AA+BjB,MAAM,CAAC,MAAM,WAAW,GAAG,CAAK,EAC9B,QAAQ,EACR,UAAU,EACV,mBAAmB,EACnB,2BAA2B,GAAG,IAAI,EAClC,GAAG,EACH,GAAG,IAAI,EACa,EAAgB,EAAE;IACtC,MAAM,IAAI,GAAoB,EAAE,CAAA;IAChC,MAAM,aAAa,GAAa,EAAE,CAAA;IAClC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAC/B,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAA;QACtC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;YACnC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;QAC1C,CAAC,CAAC,CAAA;IACJ,CAAC;IACD,OAAO,CACL,KAAC,QAAQ,OACH,IAAI,EACR,GAAG,EAAE,GAAG,EACR,IAAI,EAAE,IAAI,EACV,mBAAmB,EACjB,2BAA2B,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,EAEzD,YAAY,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,EAC5C,UAAU,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,EAAE,CAC5B,GAAG,CAAC,IAAI,KAAK,QAAQ;YACnB,CAAC,CAAC,CAAC,mBAAmB,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,IAAI,IAAI,CAAC;YAC3D,CAAC,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC,GAEtD,CACH,CAAA;AACH,CAAC,CAAA","sourcesContent":["import { forwardRef, type ComponentType, type ReactElement } from \"react\"\nimport type { StyleProp } from \"../contracts\"\nimport type { ScrollViewProps } from \"./scroll-view\"\nimport {\n VirtualizedList,\n type ItemLayout,\n type ListRenderItemInfo,\n type ViewabilityConfig,\n type ViewToken,\n type VirtualizedListHandle,\n} from \"./virtualized-list\"\n\nexport type {\n ListRenderItemInfo,\n ViewabilityConfig,\n ViewToken,\n} from \"./virtualized-list\"\n// The FlatList ref exposes the full scroll surface of the windowed core\n// (scrollTo, scrollToEnd, scrollToIndex, scrollToItem, scrollToOffset).\nexport type { VirtualizedListHandle as FlatListHandle } from \"./virtualized-list\"\n\nexport type FlatListProps<T> = Omit<ScrollViewProps, \"children\"> & {\n data: readonly T[]\n renderItem: (info: ListRenderItemInfo<T>) => ReactElement | null\n keyExtractor?: (item: T, index: number) => string\n ItemSeparatorComponent?: ComponentType | null\n ListHeaderComponent?: ComponentType | ReactElement | null\n ListFooterComponent?: ComponentType | ReactElement | null\n ListEmptyComponent?: ComponentType | ReactElement | null\n onEndReached?: () => void\n onEndReachedThreshold?: number\n onViewableItemsChanged?: (info: {\n viewableItems: ViewToken<T>[]\n changed: ViewToken<T>[]\n }) => void\n viewabilityConfig?: ViewabilityConfig\n estimatedItemSize?: number\n getItemLayout?: (data: readonly T[], index: number) => ItemLayout\n windowSize?: number\n initialNumToRender?: number\n maxToRenderPerBatch?: number\n updateCellsBatchingPeriod?: number\n extraData?: unknown\n inverted?: boolean\n refreshing?: boolean\n onRefresh?: () => void\n style?: StyleProp\n}\n\n// FlatList is a thin veneer over the windowed VirtualizedList core: only the\n// rows around the viewport are mounted (see virtualized-list.tsx).\nconst FlatListInner = forwardRef(\n <T,>(props: FlatListProps<T>, ref: React.Ref<VirtualizedListHandle>) => (\n <VirtualizedList\n {...props}\n ref={ref}\n />\n ),\n)\nFlatListInner.displayName = \"FlatList\"\n\nexport const FlatList = FlatListInner as <T>(\n props: FlatListProps<T> & { ref?: React.Ref<VirtualizedListHandle> },\n) => ReactElement\n\nexport type SectionListData<T> = { title: string; data: readonly T[] }\n\n// Viewability props are excluded: SectionList flattens sections into private\n// row records, so ViewTokens would leak that internal row type instead of T —\n// section-aware tokens are not implemented yet.\nexport type SectionListProps<T> = Omit<\n FlatListProps<T>,\n | \"data\"\n | \"renderItem\"\n | \"getItemLayout\"\n | \"onViewableItemsChanged\"\n | \"viewabilityConfig\"\n> & {\n sections: readonly SectionListData<T>[]\n renderItem: (info: ListRenderItemInfo<T>) => ReactElement | null\n renderSectionHeader?: (info: {\n section: SectionListData<T>\n }) => ReactElement | null\n // RN default on iOS; ours defaults to true too — section headers pin to\n // the top and get pushed out by the next section's header.\n stickySectionHeadersEnabled?: boolean\n // React 19 ref-as-prop: the same scroll surface FlatList exposes.\n ref?: React.Ref<VirtualizedListHandle>\n}\n\ntype SectionRow<T> =\n | { kind: \"header\"; section: SectionListData<T> }\n | { kind: \"item\"; item: T; index: number }\n\nexport const SectionList = <T,>({\n sections,\n renderItem,\n renderSectionHeader,\n stickySectionHeadersEnabled = true,\n ref,\n ...rest\n}: SectionListProps<T>): ReactElement => {\n const rows: SectionRow<T>[] = []\n const headerIndices: number[] = []\n for (const section of sections) {\n headerIndices.push(rows.length)\n rows.push({ kind: \"header\", section })\n section.data.forEach((item, index) => {\n rows.push({ kind: \"item\", item, index })\n })\n }\n return (\n <FlatList\n {...rest}\n ref={ref}\n data={rows}\n stickyHeaderIndices={\n stickySectionHeadersEnabled ? headerIndices : undefined\n }\n keyExtractor={(_row, index) => String(index)}\n renderItem={({ item: row }) =>\n row.kind === \"header\"\n ? (renderSectionHeader?.({ section: row.section }) ?? null)\n : renderItem({ item: row.item, index: row.index })\n }\n />\n )\n}\n"]}
@@ -5,7 +5,7 @@ export { FlatList, SectionList, type FlatListProps, type ListRenderItemInfo, typ
5
5
  export { Image, type ImageProps, type ImageSource } from "./image";
6
6
  export { Modal, type ModalProps } from "./modal";
7
7
  export { Pressable, TouchableOpacity, type PressableProps, type PressableStateCallbackType, type PressEvent, type TouchableOpacityProps, } from "./pressable";
8
- export { Root, type RootProps } from "./root";
8
+ export { IntrinsicRoot, NestedRoot, Root, type IntrinsicRootProps, type NestedRootProps, type RootProps, } from "./root";
9
9
  export { ScrollView, type ScrollEvent, type ScrollViewHandle, type ScrollViewProps, } from "./scroll-view";
10
10
  export { Switch, type SwitchProps } from "./switch";
11
11
  export { Text, type TextProps } from "./text";
@@ -5,7 +5,7 @@ export { FlatList, SectionList, } from "./flat-list";
5
5
  export { Image } from "./image";
6
6
  export { Modal } from "./modal";
7
7
  export { Pressable, TouchableOpacity, } from "./pressable";
8
- export { Root } from "./root";
8
+ export { IntrinsicRoot, NestedRoot, Root, } from "./root";
9
9
  export { ScrollView, } from "./scroll-view";
10
10
  export { Switch } from "./switch";
11
11
  export { Text } from "./text";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,GAElB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EACL,QAAQ,EACR,MAAM,GAGP,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,WAAW,EAA6B,MAAM,gBAAgB,CAAA;AACvE,OAAO,EACL,QAAQ,EACR,WAAW,GAOZ,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,KAAK,EAAqC,MAAM,SAAS,CAAA;AAClE,OAAO,EAAE,KAAK,EAAmB,MAAM,SAAS,CAAA;AAChD,OAAO,EACL,SAAS,EACT,gBAAgB,GAKjB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,IAAI,EAAkB,MAAM,QAAQ,CAAA;AAC7C,OAAO,EACL,UAAU,GAIX,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,MAAM,EAAoB,MAAM,UAAU,CAAA;AACnD,OAAO,EAAE,IAAI,EAAkB,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,SAAS,EAAuB,MAAM,cAAc,CAAA;AAC7D,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,IAAI,EAAkB,MAAM,QAAQ,CAAA","sourcesContent":["export {\n ActivityIndicator,\n type ActivityIndicatorProps,\n} from \"./activity-indicator\"\nexport {\n Animated,\n Easing,\n type AnimatedViewProps,\n type AnimatedViewStyle,\n} from \"./animated\"\nexport { AppRegistry, type RunApplicationParams } from \"./app-registry\"\nexport {\n FlatList,\n SectionList,\n type FlatListProps,\n type ListRenderItemInfo,\n type SectionListData,\n type SectionListProps,\n type ViewabilityConfig,\n type ViewToken,\n} from \"./flat-list\"\nexport { Image, type ImageProps, type ImageSource } from \"./image\"\nexport { Modal, type ModalProps } from \"./modal\"\nexport {\n Pressable,\n TouchableOpacity,\n type PressableProps,\n type PressableStateCallbackType,\n type PressEvent,\n type TouchableOpacityProps,\n} from \"./pressable\"\nexport { Root, type RootProps } from \"./root\"\nexport {\n ScrollView,\n type ScrollEvent,\n type ScrollViewHandle,\n type ScrollViewProps,\n} from \"./scroll-view\"\nexport { Switch, type SwitchProps } from \"./switch\"\nexport { Text, type TextProps } from \"./text\"\nexport { TextInput, type TextInputProps } from \"./text-input\"\nexport { SafeAreaView, StatusBar, View, type ViewProps } from \"./view\"\nexport { type LayoutEvent } from \"./use-layout-child\"\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,GAElB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EACL,QAAQ,EACR,MAAM,GAGP,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,WAAW,EAA6B,MAAM,gBAAgB,CAAA;AACvE,OAAO,EACL,QAAQ,EACR,WAAW,GAOZ,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,KAAK,EAAqC,MAAM,SAAS,CAAA;AAClE,OAAO,EAAE,KAAK,EAAmB,MAAM,SAAS,CAAA;AAChD,OAAO,EACL,SAAS,EACT,gBAAgB,GAKjB,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,aAAa,EACb,UAAU,EACV,IAAI,GAIL,MAAM,QAAQ,CAAA;AACf,OAAO,EACL,UAAU,GAIX,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,MAAM,EAAoB,MAAM,UAAU,CAAA;AACnD,OAAO,EAAE,IAAI,EAAkB,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,SAAS,EAAuB,MAAM,cAAc,CAAA;AAC7D,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,IAAI,EAAkB,MAAM,QAAQ,CAAA","sourcesContent":["export {\n ActivityIndicator,\n type ActivityIndicatorProps,\n} from \"./activity-indicator\"\nexport {\n Animated,\n Easing,\n type AnimatedViewProps,\n type AnimatedViewStyle,\n} from \"./animated\"\nexport { AppRegistry, type RunApplicationParams } from \"./app-registry\"\nexport {\n FlatList,\n SectionList,\n type FlatListProps,\n type ListRenderItemInfo,\n type SectionListData,\n type SectionListProps,\n type ViewabilityConfig,\n type ViewToken,\n} from \"./flat-list\"\nexport { Image, type ImageProps, type ImageSource } from \"./image\"\nexport { Modal, type ModalProps } from \"./modal\"\nexport {\n Pressable,\n TouchableOpacity,\n type PressableProps,\n type PressableStateCallbackType,\n type PressEvent,\n type TouchableOpacityProps,\n} from \"./pressable\"\nexport {\n IntrinsicRoot,\n NestedRoot,\n Root,\n type IntrinsicRootProps,\n type NestedRootProps,\n type RootProps,\n} from \"./root\"\nexport {\n ScrollView,\n type ScrollEvent,\n type ScrollViewHandle,\n type ScrollViewProps,\n} from \"./scroll-view\"\nexport { Switch, type SwitchProps } from \"./switch\"\nexport { Text, type TextProps } from \"./text\"\nexport { TextInput, type TextInputProps } from \"./text-input\"\nexport { SafeAreaView, StatusBar, View, type ViewProps } from \"./view\"\nexport { type LayoutEvent } from \"./use-layout-child\"\n"]}
@@ -9,4 +9,4 @@ export type ModalProps = {
9
9
  height?: number;
10
10
  children?: ReactNode;
11
11
  };
12
- export declare const Modal: ({ visible, onRequestClose, title, width, height, children, }: ModalProps) => React.ReactPortal | null;
12
+ export declare const Modal: ({ visible, onRequestClose, title, width, height, children, }: ModalProps) => ReactNode;
@@ -3,6 +3,15 @@ export type RootProps = {
3
3
  width: number;
4
4
  height: number;
5
5
  followAllocation?: boolean;
6
+ expand?: boolean;
6
7
  children?: ReactNode;
7
8
  };
8
- export declare const Root: ({ width, height, followAllocation, children, }: RootProps) => React.JSX.Element;
9
+ export declare const Root: ({ width, height, followAllocation, expand, children, }: RootProps) => React.JSX.Element;
10
+ export type NestedRootProps = {
11
+ children?: ReactNode;
12
+ };
13
+ export declare const NestedRoot: ({ children }: NestedRootProps) => React.JSX.Element;
14
+ export type IntrinsicRootProps = {
15
+ children?: ReactNode;
16
+ };
17
+ export declare const IntrinsicRoot: ({ children }: IntrinsicRootProps) => React.JSX.Element;
@@ -9,7 +9,7 @@ import { useRnContainer } from "./use-layout-child";
9
9
  // reports the engine viewport as its measure (via RnGtkxLayout) and is sized
10
10
  // by its parent (window/harness); the owner keeps the engine viewport in sync
11
11
  // (AppRegistry does it from window-size changes).
12
- export const Root = ({ width, height, followAllocation = false, children, }) => {
12
+ export const Root = ({ width, height, followAllocation = false, expand = false, children, }) => {
13
13
  const widgetRef = useRef(null);
14
14
  // useState lazy init — see the note in use-layout-child.ts (React Compiler).
15
15
  const [engine] = useState(() => new LayoutEngine({ width, height }));
@@ -54,6 +54,44 @@ export const Root = ({ width, height, followAllocation = false, children, }) =>
54
54
  },
55
55
  }
56
56
  : undefined);
57
+ return (_jsx(GtkBox, { ref: widgetRef, hexpand: expand, vexpand: expand, children: _jsx(HostNodeContext.Provider, { value: host, children: children }) }));
58
+ };
59
+ // Nested layout root: a full Yoga engine mounted inside ANY GTK container
60
+ // slot — an Adw.NavigationPage, a toolbar view content area, a future
61
+ // gtk-components container. The slot's allocation is the layout viewport
62
+ // (viewport-following; the initial 0×0 viewport is replaced synchronously
63
+ // inside the first allocation pass, so nothing paints at zero). Attach is
64
+ // the JSX mount; detach is the unmount — the engine is disposed with it.
65
+ // Allocate passes of sibling/nested roots may overlap: the rect-store
66
+ // tracks pass depth, so deferred GTK queue jobs run only after the
67
+ // outermost pass ends.
68
+ export const NestedRoot = ({ children }) => (_jsx(Root, { width: 0, height: 0, followAllocation: true, expand: true, children: children }));
69
+ // Content-sized layout root: unlike NestedRoot (which fills a slot), the
70
+ // intrinsic root REPORTS its Yoga-computed content size to GTK — this is
71
+ // what lets RN content live in size-to-content chrome slots (HeaderBar
72
+ // start/end, sidebar rows). Measure runs a speculative uncommitted Yoga
73
+ // pass (height honors the width constraint); the allocation pass then
74
+ // recomputes at the real size and commits.
75
+ export const IntrinsicRoot = ({ children }) => {
76
+ const widgetRef = useRef(null);
77
+ const [engine] = useState(() => new LayoutEngine({ width: 0, height: 0 }));
78
+ useEffect(() => () => {
79
+ engine.dispose();
80
+ }, [engine]);
81
+ const host = useMemo(() => ({ engine, node: engine.root, widgetRef }), [engine]);
82
+ useRnContainer(widgetRef, engine.root, {
83
+ measure: (orientation, forSize) => engine.measureContent(orientation, forSize),
84
+ beforeAllocate: (allocatedWidth, allocatedHeight) => {
85
+ beginAllocatePass();
86
+ try {
87
+ engine.setViewport({ width: allocatedWidth, height: allocatedHeight });
88
+ engine.flushSync();
89
+ }
90
+ finally {
91
+ endAllocatePass();
92
+ }
93
+ },
94
+ });
57
95
  return (_jsx(GtkBox, { ref: widgetRef, children: _jsx(HostNodeContext.Provider, { value: host, children: children }) }));
58
96
  };
59
97
  //# sourceMappingURL=root.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"root.js","sourceRoot":"","sources":["../../src/components/root.tsx"],"names":[],"mappings":";AAAA,OAAO,EACL,SAAS,EACT,eAAe,EACf,OAAO,EACP,MAAM,EACN,QAAQ,GAET,MAAM,OAAO,CAAA;AACd,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,EAAE,MAAM,EAAE,WAAW,EAAY,MAAM,sBAAsB,CAAA;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAYnD,4EAA4E;AAC5E,6EAA6E;AAC7E,8EAA8E;AAC9E,kDAAkD;AAClD,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,EACnB,KAAK,EACL,MAAM,EACN,gBAAgB,GAAG,KAAK,EACxB,QAAQ,GACE,EAAE,EAAE;IACd,MAAM,SAAS,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAA;IAE9C,6EAA6E;IAC7E,MAAM,CAAC,MAAM,CAAC,GAAG,QAAQ,CACvB,GAAG,EAAE,CAAC,IAAI,YAAY,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAC1C,CAAA;IAED,eAAe,CAAC,GAAG,EAAE;QACnB,IAAI,gBAAgB,EAAE,CAAC;YACrB,OAAM;QACR,CAAC;QACD,MAAM,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAA;QACrC,qEAAqE;QACrE,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAA;QAChC,IAAI,MAAM,EAAE,CAAC;YACX,WAAW,CAAC,MAAM,CAAC,CAAA;QACrB,CAAC;IACH,CAAC,EAAE,CAAC,MAAM,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAA;IAE7C,SAAS,CACP,GAAG,EAAE,CAAC,GAAG,EAAE;QACT,MAAM,CAAC,OAAO,EAAE,CAAA;IAClB,CAAC,EACD,CAAC,MAAM,CAAC,CACT,CAAA;IAED,MAAM,IAAI,GAAG,OAAO,CAClB,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC,EAChD,CAAC,MAAM,CAAC,CACT,CAAA;IAED,cAAc,CACZ,SAAS,EACT,MAAM,CAAC,IAAI,EACX,gBAAgB;QACd,CAAC,CAAC;YACE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;YAChB,cAAc,EAAE,CAAC,cAAc,EAAE,eAAe,EAAE,EAAE;gBAClD,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAA;gBACrC,IACE,OAAO;oBACP,OAAO,CAAC,KAAK,KAAK,cAAc;oBAChC,OAAO,CAAC,MAAM,KAAK,eAAe,EAClC,CAAC;oBACD,OAAM;gBACR,CAAC;gBACD,kEAAkE;gBAClE,oEAAoE;gBACpE,iBAAiB,EAAE,CAAA;gBACnB,IAAI,CAAC;oBACH,MAAM,CAAC,WAAW,CAAC;wBACjB,KAAK,EAAE,cAAc;wBACrB,MAAM,EAAE,eAAe;qBACxB,CAAC,CAAA;oBACF,MAAM,CAAC,SAAS,EAAE,CAAA;gBACpB,CAAC;wBAAS,CAAC;oBACT,eAAe,EAAE,CAAA;gBACnB,CAAC;YACH,CAAC;SACF;QACH,CAAC,CAAC,SAAS,CACd,CAAA;IAED,OAAO,CACL,KAAC,MAAM,IAAC,GAAG,EAAE,SAAS,YACpB,KAAC,eAAe,CAAC,QAAQ,IAAC,KAAK,EAAE,IAAI,YAClC,QAAQ,GACgB,GACpB,CACV,CAAA;AACH,CAAC,CAAA","sourcesContent":["import {\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\"\nimport { LayoutEngine } from \"../layout/index\"\nimport { GtkBox, queueResize, type Gtk } from \"../gtkx/bridge/index\"\nimport { HostNodeContext } from \"./host-node\"\nimport { beginAllocatePass, endAllocatePass } from \"./rect-store\"\nimport { useRnContainer } from \"./use-layout-child\"\n\nexport type RootProps = {\n width: number\n height: number\n // Window mode: report a zero minimum (the window may shrink freely) and\n // adopt whatever the window actually allocates as the engine viewport —\n // the allocation IS the layout viewport, headerbar excluded by GTK itself.\n followAllocation?: boolean\n children?: ReactNode\n}\n\n// Layout root: one engine per window (or per test harness). The root GtkBox\n// reports the engine viewport as its measure (via RnGtkxLayout) and is sized\n// by its parent (window/harness); the owner keeps the engine viewport in sync\n// (AppRegistry does it from window-size changes).\nexport const Root = ({\n width,\n height,\n followAllocation = false,\n children,\n}: RootProps) => {\n const widgetRef = useRef<Gtk.Box | null>(null)\n\n // useState lazy init — see the note in use-layout-child.ts (React Compiler).\n const [engine] = useState<LayoutEngine>(\n () => new LayoutEngine({ width, height }),\n )\n\n useLayoutEffect(() => {\n if (followAllocation) {\n return\n }\n engine.setViewport({ width, height })\n // The viewport is this widget's measure — invalidate the cached one.\n const widget = widgetRef.current\n if (widget) {\n queueResize(widget)\n }\n }, [engine, followAllocation, width, height])\n\n useEffect(\n () => () => {\n engine.dispose()\n },\n [engine],\n )\n\n const host = useMemo(\n () => ({ engine, node: engine.root, widgetRef }),\n [engine],\n )\n\n useRnContainer(\n widgetRef,\n engine.root,\n followAllocation\n ? {\n measure: () => 0,\n beforeAllocate: (allocatedWidth, allocatedHeight) => {\n const current = engine.root.getRect()\n if (\n current &&\n current.width === allocatedWidth &&\n current.height === allocatedHeight\n ) {\n return\n }\n // Synchronous reflow inside the allocation pass: commits fill the\n // rect store now; their GTK queue calls are deferred past the pass.\n beginAllocatePass()\n try {\n engine.setViewport({\n width: allocatedWidth,\n height: allocatedHeight,\n })\n engine.flushSync()\n } finally {\n endAllocatePass()\n }\n },\n }\n : undefined,\n )\n\n return (\n <GtkBox ref={widgetRef}>\n <HostNodeContext.Provider value={host}>\n {children}\n </HostNodeContext.Provider>\n </GtkBox>\n )\n}\n"]}
1
+ {"version":3,"file":"root.js","sourceRoot":"","sources":["../../src/components/root.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\"\nimport { LayoutEngine } from \"../layout/index\"\nimport { GtkBox, queueResize, type Gtk } from \"../gtkx/bridge/index\"\nimport { HostNodeContext } from \"./host-node\"\nimport { beginAllocatePass, endAllocatePass } from \"./rect-store\"\nimport { useRnContainer } from \"./use-layout-child\"\n\nexport type RootProps = {\n width: number\n height: number\n // Window mode: report a zero minimum (the window may shrink freely) and\n // adopt whatever the window actually allocates as the engine viewport —\n // the allocation IS the layout viewport, headerbar excluded by GTK itself.\n followAllocation?: boolean\n // Claim the parent slot: a zero-minimum box inside a plain GTK container\n // gets no space unless it expands (window children fill implicitly, other\n // containers honor the expand flags).\n expand?: boolean\n children?: ReactNode\n}\n\n// Layout root: one engine per window (or per test harness). The root GtkBox\n// reports the engine viewport as its measure (via RnGtkxLayout) and is sized\n// by its parent (window/harness); the owner keeps the engine viewport in sync\n// (AppRegistry does it from window-size changes).\nexport const Root = ({\n width,\n height,\n followAllocation = false,\n expand = false,\n children,\n}: RootProps) => {\n const widgetRef = useRef<Gtk.Box | null>(null)\n\n // useState lazy init — see the note in use-layout-child.ts (React Compiler).\n const [engine] = useState<LayoutEngine>(\n () => new LayoutEngine({ width, height }),\n )\n\n useLayoutEffect(() => {\n if (followAllocation) {\n return\n }\n engine.setViewport({ width, height })\n // The viewport is this widget's measure — invalidate the cached one.\n const widget = widgetRef.current\n if (widget) {\n queueResize(widget)\n }\n }, [engine, followAllocation, width, height])\n\n useEffect(\n () => () => {\n engine.dispose()\n },\n [engine],\n )\n\n const host = useMemo(\n () => ({ engine, node: engine.root, widgetRef }),\n [engine],\n )\n\n useRnContainer(\n widgetRef,\n engine.root,\n followAllocation\n ? {\n measure: () => 0,\n beforeAllocate: (allocatedWidth, allocatedHeight) => {\n const current = engine.root.getRect()\n if (\n current &&\n current.width === allocatedWidth &&\n current.height === allocatedHeight\n ) {\n return\n }\n // Synchronous reflow inside the allocation pass: commits fill the\n // rect store now; their GTK queue calls are deferred past the pass.\n beginAllocatePass()\n try {\n engine.setViewport({\n width: allocatedWidth,\n height: allocatedHeight,\n })\n engine.flushSync()\n } finally {\n endAllocatePass()\n }\n },\n }\n : undefined,\n )\n\n return (\n <GtkBox\n ref={widgetRef}\n hexpand={expand}\n vexpand={expand}\n >\n <HostNodeContext.Provider value={host}>\n {children}\n </HostNodeContext.Provider>\n </GtkBox>\n )\n}\n\nexport type NestedRootProps = {\n children?: ReactNode\n}\n\n// Nested layout root: a full Yoga engine mounted inside ANY GTK container\n// slot — an Adw.NavigationPage, a toolbar view content area, a future\n// gtk-components container. The slot's allocation is the layout viewport\n// (viewport-following; the initial 0×0 viewport is replaced synchronously\n// inside the first allocation pass, so nothing paints at zero). Attach is\n// the JSX mount; detach is the unmount — the engine is disposed with it.\n// Allocate passes of sibling/nested roots may overlap: the rect-store\n// tracks pass depth, so deferred GTK queue jobs run only after the\n// outermost pass ends.\nexport const NestedRoot = ({ children }: NestedRootProps) => (\n <Root\n width={0}\n height={0}\n followAllocation\n expand\n >\n {children}\n </Root>\n)\n\nexport type IntrinsicRootProps = {\n children?: ReactNode\n}\n\n// Content-sized layout root: unlike NestedRoot (which fills a slot), the\n// intrinsic root REPORTS its Yoga-computed content size to GTK — this is\n// what lets RN content live in size-to-content chrome slots (HeaderBar\n// start/end, sidebar rows). Measure runs a speculative uncommitted Yoga\n// pass (height honors the width constraint); the allocation pass then\n// recomputes at the real size and commits.\nexport const IntrinsicRoot = ({ children }: IntrinsicRootProps) => {\n const widgetRef = useRef<Gtk.Box | null>(null)\n const [engine] = useState<LayoutEngine>(\n () => new LayoutEngine({ width: 0, height: 0 }),\n )\n useEffect(\n () => () => {\n engine.dispose()\n },\n [engine],\n )\n const host = useMemo(\n () => ({ engine, node: engine.root, widgetRef }),\n [engine],\n )\n useRnContainer(widgetRef, engine.root, {\n measure: (orientation, forSize) =>\n engine.measureContent(orientation, forSize),\n beforeAllocate: (allocatedWidth, allocatedHeight) => {\n beginAllocatePass()\n try {\n engine.setViewport({ width: allocatedWidth, height: allocatedHeight })\n engine.flushSync()\n } finally {\n endAllocatePass()\n }\n },\n })\n return (\n <GtkBox ref={widgetRef}>\n <HostNodeContext.Provider value={host}>\n {children}\n </HostNodeContext.Provider>\n </GtkBox>\n )\n}\n"]}
@@ -2,6 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Children, createContext, forwardRef, useContext, useImperativeHandle, useLayoutEffect, useRef, useState, } from "react";
3
3
  import { splitStyle, StyleSheet } from "../style/index";
4
4
  import { Gtk, GtkBox, GtkScrolledWindow, queueAllocate, queueResize, useSignal, } from "../gtkx/bridge/index";
5
+ import { ensurePerfReporter, perfAddTime, perfCount, perfEnabled, perfFrameTick, perfNow, } from "../perf";
5
6
  import { HostNodeContext } from "./host-node";
6
7
  import { deferDuringAllocate, setStoredOffset } from "./rect-store";
7
8
  import { useLayoutChild, useRnContainer, } from "./use-layout-child";
@@ -254,7 +255,17 @@ export const ScrollView = forwardRef(({ style, contentContainerStyle, horizontal
254
255
  ? (scrolled?.getHadjustment() ?? null)
255
256
  : (scrolled?.getVadjustment() ?? null);
256
257
  const onAdjustment = () => {
258
+ if (!perfEnabled) {
259
+ emitScroll();
260
+ return;
261
+ }
262
+ // Perf: full wall time of one scroll tick's JS work — the FFI reads in
263
+ // emitScroll plus the app/VirtualizedList onScroll handler (windowing,
264
+ // viewability, endReached) — everything that blocks the GTK frame.
265
+ perfCount("scroll.events");
266
+ const start = perfNow();
257
267
  emitScroll();
268
+ perfAddTime("scroll.js", perfNow() - start);
258
269
  };
259
270
  // Content-size reports dedupe on the engine rect: "changed" also fires
260
271
  // for pure viewport (page-size) changes, which RN does not report.
@@ -288,12 +299,32 @@ export const ScrollView = forwardRef(({ style, contentContainerStyle, horizontal
288
299
  if (!widget) {
289
300
  return;
290
301
  }
302
+ const vadjustment = widget.getVadjustment();
303
+ let lastValue = vadjustment?.getValue() ?? 0;
304
+ // Frames to keep requeueing after the last observed move. The scrolled
305
+ // window's own kinetic tick may run AFTER ours, so this tick's read can
306
+ // be one frame stale: without the grace window the first frame of a
307
+ // deceleration (its value change lands after our read) would miss its
308
+ // correction. Kinetic deceleration changes the value every frame, so
309
+ // three frames of slack cover the handoff at both ends of the motion.
310
+ const graceFrames = 3;
311
+ let remaining = graceFrames;
291
312
  const id = widget.addTickCallback(() => {
292
- // Unconditionally, every frame: the scrolled window's own kinetic
293
- // tick may run AFTER ours, so comparing values here can miss the
294
- // frame's final offset. The allocation itself reads the adjustment
295
- // in the LAYOUT phase — always after every update-phase mutation —
296
- // so queuing each frame guarantees a same-frame correction.
313
+ perfCount("sticky.ticks");
314
+ const value = vadjustment?.getValue() ?? 0;
315
+ if (value !== lastValue) {
316
+ lastValue = value;
317
+ remaining = graceFrames;
318
+ }
319
+ else if (remaining > 0) {
320
+ remaining -= 1;
321
+ }
322
+ else {
323
+ // At rest: the pin is already correct and nothing translated it,
324
+ // so an allocation pass per frame would be pure idle cost.
325
+ return true;
326
+ }
327
+ perfCount("sticky.queues");
297
328
  const content = contentRef.current;
298
329
  if (content) {
299
330
  queueAllocate(content);
@@ -304,6 +335,28 @@ export const ScrollView = forwardRef(({ style, contentContainerStyle, horizontal
304
335
  widget.removeTickCallback(id);
305
336
  };
306
337
  }, [scrolled, hasSticky, horizontal]);
338
+ // Perf: observe the GTK frame clock through a tick callback — the delta
339
+ // between consecutive ticks is the real frame interval (late frames show
340
+ // up directly). Note: registering a tick callback keeps the frame clock
341
+ // running, so idle seconds show ~60 ticks too; the reporter separates
342
+ // load by the accompanying counters.
343
+ useLayoutEffect(() => {
344
+ if (!perfEnabled) {
345
+ return;
346
+ }
347
+ ensurePerfReporter();
348
+ const widget = scrolled;
349
+ if (!widget) {
350
+ return;
351
+ }
352
+ const id = widget.addTickCallback(() => {
353
+ perfFrameTick(perfNow());
354
+ return true;
355
+ });
356
+ return () => {
357
+ widget.removeTickCallback(id);
358
+ };
359
+ }, [scrolled]);
307
360
  useSignal(adjustment, "value-changed", onAdjustment);
308
361
  // Both axes report into the same handler — RN's callback carries both
309
362
  // dimensions no matter which one moved.
@@ -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 Gtk,\n GtkBox,\n GtkScrolledWindow,\n queueAllocate,\n queueResize,\n useSignal,\n} from \"../gtkx/bridge/index\"\nimport { HostNodeContext } from \"./host-node\"\nimport { deferDuringAllocate, setStoredOffset } from \"./rect-store\"\nimport {\n useLayoutChild,\n useRnContainer,\n type LayoutEvent,\n} from \"./use-layout-child\"\n\nexport type ScrollViewHandle = {\n scrollTo(options: { x?: number; y?: number; animated?: boolean }): void\n scrollToEnd(options?: { animated?: boolean }): void\n}\n\nexport type ScrollEvent = {\n nativeEvent: {\n contentOffset: { x: number; y: number }\n contentSize: { width: number; height: number }\n }\n}\n\nexport type ScrollViewProps = {\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 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 onContentSizeChange,\n onLayout,\n children,\n testID,\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 style: [\n style,\n { overflow: \"scroll\", flexDirection: horizontal ? \"row\" : \"column\" },\n ],\n onLayout,\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 // RN semantics: a ScrollView CLIPS its content. Branch B made containers\n // paint-overflow by default, so without this the scrolled-away content\n // (e.g. the original of a pinned sticky header) keeps drawing past the\n // scroll area's edges.\n useLayoutEffect(() => {\n outerRef.current?.setOverflow(Gtk.Overflow.HIDDEN)\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 alignItems: \"flex-start\",\n ...contentLayout,\n })\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [contentNode, contentKey])\n\n useImperativeHandle(handleRef, () => ({\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(adjustment.getUpper() - adjustment.getPageSize())\n }\n },\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; GTK's z-order is\n // sibling paint order, so the active slot is reordered to be the LAST\n // content child while pinned and restored afterwards. 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 const emitScroll = (): void => {\n if (!onScroll) {\n return\n }\n const widget = outerRef.current\n const contentRect = contentNode.getRect()\n if (!widget || !contentRect) {\n return\n }\n onScroll({\n nativeEvent: {\n contentOffset: {\n x: widget.getHadjustment()?.getValue() ?? 0,\n y: widget.getVadjustment()?.getValue() ?? 0,\n },\n contentSize: { width: contentRect.width, height: contentRect.height },\n },\n })\n }\n\n const adjustment = horizontal\n ? (scrolled?.getHadjustment() ?? null)\n : (scrolled?.getVadjustment() ?? null)\n const onAdjustment = (): void => {\n emitScroll()\n }\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 id = widget.addTickCallback(() => {\n // Unconditionally, every frame: the scrolled window's own kinetic\n // tick may run AFTER ours, so comparing values here can miss the\n // frame's final offset. The allocation itself reads the adjustment\n // in the LAYOUT phase — always after every update-phase mutation —\n // so queuing each frame guarantees a same-frame correction.\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 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 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 { HostNodeContext } from \"./host-node\"\nimport { deferDuringAllocate, setStoredOffset } from \"./rect-store\"\nimport {\n useLayoutChild,\n useRnContainer,\n type LayoutEvent,\n} from \"./use-layout-child\"\n\nexport type ScrollViewHandle = {\n scrollTo(options: { x?: number; y?: number; animated?: boolean }): void\n scrollToEnd(options?: { animated?: boolean }): void\n}\n\nexport type ScrollEvent = {\n nativeEvent: {\n contentOffset: { x: number; y: number }\n contentSize: { width: number; height: number }\n }\n}\n\nexport type ScrollViewProps = {\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 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 onContentSizeChange,\n onLayout,\n children,\n testID,\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 style: [\n style,\n { overflow: \"scroll\", flexDirection: horizontal ? \"row\" : \"column\" },\n ],\n onLayout,\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 // RN semantics: a ScrollView CLIPS its content. Branch B made containers\n // paint-overflow by default, so without this the scrolled-away content\n // (e.g. the original of a pinned sticky header) keeps drawing past the\n // scroll area's edges.\n useLayoutEffect(() => {\n outerRef.current?.setOverflow(Gtk.Overflow.HIDDEN)\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 alignItems: \"flex-start\",\n ...contentLayout,\n })\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [contentNode, contentKey])\n\n useImperativeHandle(handleRef, () => ({\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(adjustment.getUpper() - adjustment.getPageSize())\n }\n },\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; GTK's z-order is\n // sibling paint order, so the active slot is reordered to be the LAST\n // content child while pinned and restored afterwards. 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 const emitScroll = (): void => {\n if (!onScroll) {\n return\n }\n const widget = outerRef.current\n const contentRect = contentNode.getRect()\n if (!widget || !contentRect) {\n return\n }\n onScroll({\n nativeEvent: {\n contentOffset: {\n x: widget.getHadjustment()?.getValue() ?? 0,\n y: widget.getVadjustment()?.getValue() ?? 0,\n },\n contentSize: { width: contentRect.width, height: contentRect.height },\n },\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 // 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"]}
@@ -16,6 +16,7 @@ export type TextInputProps = {
16
16
  editable?: boolean;
17
17
  keyboardType?: "default" | "numeric" | "email-address" | "phone-pad" | "url";
18
18
  multiline?: boolean;
19
+ clearButtonMode?: "never" | "while-editing" | "unless-editing" | "always";
19
20
  style?: StyleProp;
20
21
  onLayout?: (event: LayoutEvent) => void;
21
22
  testID?: string;
@@ -35,8 +35,9 @@ const useFocusController = (widgetRef, onFocus, onBlur) => {
35
35
  // Single-line input over GtkEntry. gtkx's controlled-text behavior keeps the
36
36
  // widget in sync with the `text` prop (RN controlled semantics); uncontrolled
37
37
  // usage passes defaultValue once.
38
- const SingleLineTextInput = ({ value, defaultValue, onChangeText, onSubmitEditing, onFocus, onBlur, placeholder, secureTextEntry = false, editable = true, keyboardType = "default", style, onLayout, testID, }) => {
38
+ const SingleLineTextInput = ({ value, defaultValue, onChangeText, onSubmitEditing, onFocus, onBlur, placeholder, secureTextEntry = false, editable = true, keyboardType = "default", clearButtonMode = "never", style, onLayout, testID, }) => {
39
39
  const widgetRef = useRef(null);
40
+ const [focused, setFocused] = useState(false);
40
41
  useLayoutChild(widgetRef, {
41
42
  style,
42
43
  onLayout,
@@ -46,8 +47,35 @@ const SingleLineTextInput = ({ value, defaultValue, onChangeText, onSubmitEditin
46
47
  extraLayout: { minWidth: 60 },
47
48
  });
48
49
  const controlled = value !== undefined;
49
- useFocusController(widgetRef, onFocus, onBlur);
50
- return (_jsx(GtkEntry, { ref: widgetRef, name: testID, text: controlled ? value : (defaultValue ?? ""), placeholderText: placeholder ?? "", visibility: !secureTextEntry, editable: editable, sensitive: editable, inputPurpose: INPUT_PURPOSE[keyboardType], onChanged: () => {
50
+ useFocusController(widgetRef, () => {
51
+ setFocused(true);
52
+ onFocus?.();
53
+ }, () => {
54
+ setFocused(false);
55
+ onBlur?.();
56
+ });
57
+ // RN semantics: the affordance only shows when there is something to
58
+ // clear, and while-editing / unless-editing follow focus.
59
+ const text = controlled ? value : undefined;
60
+ const hasText = text !== undefined
61
+ ? text.length > 0
62
+ : (widgetRef.current?.getText().length ?? 0) > 0;
63
+ const showClear = hasText &&
64
+ (clearButtonMode === "always" ||
65
+ (clearButtonMode === "while-editing" && focused) ||
66
+ (clearButtonMode === "unless-editing" && !focused));
67
+ return (_jsx(GtkEntry, { ref: widgetRef, name: testID, secondaryIconName: showClear ? "edit-clear-symbolic" : null, secondaryIconActivatable: showClear, secondaryIconTooltipText: showClear ? "Clear" : null, onIconRelease: () => {
68
+ const widget = widgetRef.current;
69
+ if (!widget) {
70
+ return;
71
+ }
72
+ // Uncontrolled inputs clear themselves; controlled ones report an
73
+ // empty string and let the owner drive the value (RN contract).
74
+ if (!controlled) {
75
+ widget.setText("");
76
+ }
77
+ onChangeText?.("");
78
+ }, text: controlled ? value : (defaultValue ?? ""), placeholderText: placeholder ?? "", visibility: !secureTextEntry, editable: editable, sensitive: editable, inputPurpose: INPUT_PURPOSE[keyboardType], onChanged: () => {
51
79
  const widget = widgetRef.current;
52
80
  if (widget) {
53
81
  onChangeText?.(widget.getText());