@react-x11/components 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (579) hide show
  1. package/README.md +10 -4
  2. package/dist/charts/node.d.ts +11 -1
  3. package/dist/charts/node.d.ts.map +1 -1
  4. package/dist/charts/node.js +44 -14
  5. package/dist/charts/node.js.map +1 -1
  6. package/dist/charts/render.d.ts +12 -3
  7. package/dist/charts/render.d.ts.map +1 -1
  8. package/dist/charts/render.js +94 -25
  9. package/dist/charts/render.js.map +1 -1
  10. package/dist/code/index.d.ts.map +1 -1
  11. package/dist/code/index.js +27 -24
  12. package/dist/code/index.js.map +1 -1
  13. package/dist/code-editor/completion.d.ts.map +1 -1
  14. package/dist/code-editor/completion.js +3 -2
  15. package/dist/code-editor/completion.js.map +1 -1
  16. package/dist/code-editor/index.d.ts.map +1 -1
  17. package/dist/code-editor/index.js +19 -0
  18. package/dist/code-editor/index.js.map +1 -1
  19. package/dist/code-editor/node.d.ts +177 -7
  20. package/dist/code-editor/node.d.ts.map +1 -1
  21. package/dist/code-editor/node.js +1329 -164
  22. package/dist/code-editor/node.js.map +1 -1
  23. package/dist/code-language/index.d.ts +1 -1
  24. package/dist/code-language/index.d.ts.map +1 -1
  25. package/dist/code-language/index.js +1 -1
  26. package/dist/code-language/index.js.map +1 -1
  27. package/dist/code-language/languages/glsl.d.ts.map +1 -1
  28. package/dist/code-language/languages/glsl.js +10 -2
  29. package/dist/code-language/languages/glsl.js.map +1 -1
  30. package/dist/code-language/languages/javascript.d.ts.map +1 -1
  31. package/dist/code-language/languages/javascript.js +18 -3
  32. package/dist/code-language/languages/javascript.js.map +1 -1
  33. package/dist/code-language/languages/json.d.ts.map +1 -1
  34. package/dist/code-language/languages/json.js +10 -2
  35. package/dist/code-language/languages/json.js.map +1 -1
  36. package/dist/code-language/languages/shell.d.ts.map +1 -1
  37. package/dist/code-language/languages/shell.js +10 -2
  38. package/dist/code-language/languages/shell.js.map +1 -1
  39. package/dist/code-language/languages/sql.d.ts.map +1 -1
  40. package/dist/code-language/languages/sql.js +20 -2
  41. package/dist/code-language/languages/sql.js.map +1 -1
  42. package/dist/code-language/runs.d.ts +31 -0
  43. package/dist/code-language/runs.d.ts.map +1 -1
  44. package/dist/code-language/runs.js +105 -0
  45. package/dist/code-language/runs.js.map +1 -1
  46. package/dist/code-language/stream.d.ts +22 -0
  47. package/dist/code-language/stream.d.ts.map +1 -1
  48. package/dist/code-language/stream.js +259 -9
  49. package/dist/code-language/stream.js.map +1 -1
  50. package/dist/code-language/types.d.ts +5 -4
  51. package/dist/code-language/types.d.ts.map +1 -1
  52. package/dist/codeblock/index.d.ts +26 -2
  53. package/dist/codeblock/index.d.ts.map +1 -1
  54. package/dist/codeblock/index.js +57 -4
  55. package/dist/codeblock/index.js.map +1 -1
  56. package/dist/flow/draw.d.ts +50 -0
  57. package/dist/flow/draw.d.ts.map +1 -1
  58. package/dist/flow/draw.js +132 -33
  59. package/dist/flow/draw.js.map +1 -1
  60. package/dist/flow/gl/color.d.ts +13 -0
  61. package/dist/flow/gl/color.d.ts.map +1 -0
  62. package/dist/flow/gl/color.js +82 -0
  63. package/dist/flow/gl/color.js.map +1 -0
  64. package/dist/flow/gl/index.d.ts +60 -0
  65. package/dist/flow/gl/index.d.ts.map +1 -0
  66. package/dist/flow/gl/index.js +283 -0
  67. package/dist/flow/gl/index.js.map +1 -0
  68. package/dist/flow/gl/pack.d.ts +133 -0
  69. package/dist/flow/gl/pack.d.ts.map +1 -0
  70. package/dist/flow/gl/pack.js +408 -0
  71. package/dist/flow/gl/pack.js.map +1 -0
  72. package/dist/flow/gl/renderer.d.ts +95 -0
  73. package/dist/flow/gl/renderer.d.ts.map +1 -0
  74. package/dist/flow/gl/renderer.js +459 -0
  75. package/dist/flow/gl/renderer.js.map +1 -0
  76. package/dist/flow/gl/shaders.d.ts +79 -0
  77. package/dist/flow/gl/shaders.d.ts.map +1 -0
  78. package/dist/flow/gl/shaders.js +346 -0
  79. package/dist/flow/gl/shaders.js.map +1 -0
  80. package/dist/flow/gl/text.d.ts +318 -0
  81. package/dist/flow/gl/text.d.ts.map +1 -0
  82. package/dist/flow/gl/text.js +1103 -0
  83. package/dist/flow/gl/text.js.map +1 -0
  84. package/dist/flow/index.d.ts +1 -1
  85. package/dist/flow/index.d.ts.map +1 -1
  86. package/dist/flow/index.js +619 -64
  87. package/dist/flow/index.js.map +1 -1
  88. package/dist/flow/node.d.ts +621 -72
  89. package/dist/flow/node.d.ts.map +1 -1
  90. package/dist/flow/node.js +2253 -1018
  91. package/dist/flow/node.js.map +1 -1
  92. package/dist/flow/paint.d.ts +35 -0
  93. package/dist/flow/paint.d.ts.map +1 -0
  94. package/dist/flow/paint.js +393 -0
  95. package/dist/flow/paint.js.map +1 -0
  96. package/dist/flow/paths.d.ts +14 -0
  97. package/dist/flow/paths.d.ts.map +1 -1
  98. package/dist/flow/paths.js +45 -19
  99. package/dist/flow/paths.js.map +1 -1
  100. package/dist/flow/scene.d.ts +584 -0
  101. package/dist/flow/scene.d.ts.map +1 -0
  102. package/dist/flow/scene.js +1453 -0
  103. package/dist/flow/scene.js.map +1 -0
  104. package/dist/flow/types.d.ts +111 -1
  105. package/dist/flow/types.d.ts.map +1 -1
  106. package/dist/html/controls.d.ts +28 -0
  107. package/dist/html/controls.d.ts.map +1 -1
  108. package/dist/html/controls.js +52 -2
  109. package/dist/html/controls.js.map +1 -1
  110. package/dist/html/css/calc.d.ts +14 -0
  111. package/dist/html/css/calc.d.ts.map +1 -0
  112. package/dist/html/css/calc.js +344 -0
  113. package/dist/html/css/calc.js.map +1 -0
  114. package/dist/html/css/cascade.d.ts +262 -6
  115. package/dist/html/css/cascade.d.ts.map +1 -1
  116. package/dist/html/css/cascade.js +1373 -67
  117. package/dist/html/css/cascade.js.map +1 -1
  118. package/dist/html/css/color.d.ts +23 -0
  119. package/dist/html/css/color.d.ts.map +1 -0
  120. package/dist/html/css/color.js +740 -0
  121. package/dist/html/css/color.js.map +1 -0
  122. package/dist/html/css/content.d.ts +92 -0
  123. package/dist/html/css/content.d.ts.map +1 -0
  124. package/dist/html/css/content.js +388 -0
  125. package/dist/html/css/content.js.map +1 -0
  126. package/dist/html/css/counter-styles.d.ts +76 -0
  127. package/dist/html/css/counter-styles.d.ts.map +1 -0
  128. package/dist/html/css/counter-styles.js +893 -0
  129. package/dist/html/css/counter-styles.js.map +1 -0
  130. package/dist/html/css/decode.d.ts +12 -0
  131. package/dist/html/css/decode.d.ts.map +1 -0
  132. package/dist/html/css/decode.js +116 -0
  133. package/dist/html/css/decode.js.map +1 -0
  134. package/dist/html/css/parse.d.ts +110 -10
  135. package/dist/html/css/parse.d.ts.map +1 -1
  136. package/dist/html/css/parse.js +1473 -180
  137. package/dist/html/css/parse.js.map +1 -1
  138. package/dist/html/css/style.d.ts +501 -15
  139. package/dist/html/css/style.d.ts.map +1 -1
  140. package/dist/html/css/style.js +3729 -206
  141. package/dist/html/css/style.js.map +1 -1
  142. package/dist/html/css/ua.d.ts.map +1 -1
  143. package/dist/html/css/ua.js +65 -16
  144. package/dist/html/css/ua.js.map +1 -1
  145. package/dist/html/css/values.d.ts +37 -3
  146. package/dist/html/css/values.d.ts.map +1 -1
  147. package/dist/html/css/values.js +122 -48
  148. package/dist/html/css/values.js.map +1 -1
  149. package/dist/html/css/vars.d.ts +52 -0
  150. package/dist/html/css/vars.d.ts.map +1 -0
  151. package/dist/html/css/vars.js +358 -0
  152. package/dist/html/css/vars.js.map +1 -0
  153. package/dist/html/dom.d.ts +30 -4
  154. package/dist/html/dom.d.ts.map +1 -1
  155. package/dist/html/dom.js +278 -17
  156. package/dist/html/dom.js.map +1 -1
  157. package/dist/html/fonts.d.ts +106 -0
  158. package/dist/html/fonts.d.ts.map +1 -0
  159. package/dist/html/fonts.js +565 -0
  160. package/dist/html/fonts.js.map +1 -0
  161. package/dist/html/gif.d.ts +15 -0
  162. package/dist/html/gif.d.ts.map +1 -0
  163. package/dist/html/gif.js +229 -0
  164. package/dist/html/gif.js.map +1 -0
  165. package/dist/html/index.d.ts +50 -11
  166. package/dist/html/index.d.ts.map +1 -1
  167. package/dist/html/index.js +115 -9
  168. package/dist/html/index.js.map +1 -1
  169. package/dist/html/layout/block.d.ts +226 -3
  170. package/dist/html/layout/block.d.ts.map +1 -1
  171. package/dist/html/layout/block.js +3077 -219
  172. package/dist/html/layout/block.js.map +1 -1
  173. package/dist/html/layout/boxes.d.ts +367 -17
  174. package/dist/html/layout/boxes.d.ts.map +1 -1
  175. package/dist/html/layout/boxes.js +1742 -182
  176. package/dist/html/layout/boxes.js.map +1 -1
  177. package/dist/html/layout/cache.d.ts +58 -0
  178. package/dist/html/layout/cache.d.ts.map +1 -0
  179. package/dist/html/layout/cache.js +224 -0
  180. package/dist/html/layout/cache.js.map +1 -0
  181. package/dist/html/layout/collapse.d.ts +45 -0
  182. package/dist/html/layout/collapse.d.ts.map +1 -0
  183. package/dist/html/layout/collapse.js +246 -0
  184. package/dist/html/layout/collapse.js.map +1 -0
  185. package/dist/html/layout/css-grid.d.ts +7 -0
  186. package/dist/html/layout/css-grid.d.ts.map +1 -0
  187. package/dist/html/layout/css-grid.js +900 -0
  188. package/dist/html/layout/css-grid.js.map +1 -0
  189. package/dist/html/layout/flex.d.ts.map +1 -1
  190. package/dist/html/layout/flex.js +1025 -89
  191. package/dist/html/layout/flex.js.map +1 -1
  192. package/dist/html/layout/floats.d.ts +45 -6
  193. package/dist/html/layout/floats.d.ts.map +1 -1
  194. package/dist/html/layout/floats.js +113 -14
  195. package/dist/html/layout/floats.js.map +1 -1
  196. package/dist/html/layout/grid-lines.d.ts +39 -0
  197. package/dist/html/layout/grid-lines.d.ts.map +1 -0
  198. package/dist/html/layout/grid-lines.js +119 -0
  199. package/dist/html/layout/grid-lines.js.map +1 -0
  200. package/dist/html/layout/grid.d.ts +53 -0
  201. package/dist/html/layout/grid.d.ts.map +1 -0
  202. package/dist/html/layout/grid.js +153 -0
  203. package/dist/html/layout/grid.js.map +1 -0
  204. package/dist/html/layout/inline.d.ts +154 -1
  205. package/dist/html/layout/inline.d.ts.map +1 -1
  206. package/dist/html/layout/inline.js +3598 -175
  207. package/dist/html/layout/inline.js.map +1 -1
  208. package/dist/html/layout/shaping.d.ts +6 -0
  209. package/dist/html/layout/shaping.d.ts.map +1 -0
  210. package/dist/html/layout/shaping.js +100 -0
  211. package/dist/html/layout/shaping.js.map +1 -0
  212. package/dist/html/layout/table.d.ts +3 -0
  213. package/dist/html/layout/table.d.ts.map +1 -1
  214. package/dist/html/layout/table.js +681 -175
  215. package/dist/html/layout/table.js.map +1 -1
  216. package/dist/html/layout/tracks.d.ts +44 -0
  217. package/dist/html/layout/tracks.d.ts.map +1 -0
  218. package/dist/html/layout/tracks.js +349 -0
  219. package/dist/html/layout/tracks.js.map +1 -0
  220. package/dist/html/node.d.ts +169 -4
  221. package/dist/html/node.d.ts.map +1 -1
  222. package/dist/html/node.js +1429 -137
  223. package/dist/html/node.js.map +1 -1
  224. package/dist/html/paint.d.ts +92 -3
  225. package/dist/html/paint.d.ts.map +1 -1
  226. package/dist/html/paint.js +3635 -107
  227. package/dist/html/paint.js.map +1 -1
  228. package/dist/html/resources.d.ts +60 -14
  229. package/dist/html/resources.d.ts.map +1 -1
  230. package/dist/html/resources.js +122 -36
  231. package/dist/html/resources.js.map +1 -1
  232. package/dist/html/surfaces.d.ts +23 -0
  233. package/dist/html/surfaces.d.ts.map +1 -0
  234. package/dist/html/surfaces.js +68 -0
  235. package/dist/html/surfaces.js.map +1 -0
  236. package/dist/html/svg.d.ts +80 -0
  237. package/dist/html/svg.d.ts.map +1 -0
  238. package/dist/html/svg.js +442 -0
  239. package/dist/html/svg.js.map +1 -0
  240. package/dist/html/url.d.ts +23 -0
  241. package/dist/html/url.d.ts.map +1 -0
  242. package/dist/html/url.js +80 -0
  243. package/dist/html/url.js.map +1 -0
  244. package/dist/index.d.ts +1 -1
  245. package/dist/index.d.ts.map +1 -1
  246. package/dist/index.js.map +1 -1
  247. package/dist/internal/codelines.d.ts +42 -0
  248. package/dist/internal/codelines.d.ts.map +1 -0
  249. package/dist/internal/codelines.js +170 -0
  250. package/dist/internal/codelines.js.map +1 -0
  251. package/dist/internal/collate.d.ts +3 -0
  252. package/dist/internal/collate.d.ts.map +1 -0
  253. package/dist/internal/collate.js +11 -0
  254. package/dist/internal/collate.js.map +1 -0
  255. package/dist/internal/markdown/ast.d.ts +11 -0
  256. package/dist/internal/markdown/ast.d.ts.map +1 -1
  257. package/dist/internal/markdown/parse.d.ts.map +1 -1
  258. package/dist/internal/markdown/parse.js +109 -8
  259. package/dist/internal/markdown/parse.js.map +1 -1
  260. package/dist/internal/prewarm.d.ts +18 -0
  261. package/dist/internal/prewarm.d.ts.map +1 -0
  262. package/dist/internal/prewarm.js +42 -0
  263. package/dist/internal/prewarm.js.map +1 -0
  264. package/dist/internal/sdf.d.ts +20 -0
  265. package/dist/internal/sdf.d.ts.map +1 -0
  266. package/dist/internal/sdf.js +216 -0
  267. package/dist/internal/sdf.js.map +1 -0
  268. package/dist/internal/splice.d.ts +12 -0
  269. package/dist/internal/splice.d.ts.map +1 -0
  270. package/dist/internal/splice.js +23 -0
  271. package/dist/internal/splice.js.map +1 -0
  272. package/dist/internal/window.d.ts +12 -0
  273. package/dist/internal/window.d.ts.map +1 -1
  274. package/dist/internal/window.js +23 -6
  275. package/dist/internal/window.js.map +1 -1
  276. package/dist/maps/anchors.d.ts +6 -1
  277. package/dist/maps/anchors.d.ts.map +1 -1
  278. package/dist/maps/anchors.js +260 -1
  279. package/dist/maps/anchors.js.map +1 -1
  280. package/dist/maps/controller.d.ts +57 -0
  281. package/dist/maps/controller.d.ts.map +1 -1
  282. package/dist/maps/controller.js +97 -18
  283. package/dist/maps/controller.js.map +1 -1
  284. package/dist/maps/gl/buckets.d.ts +5 -1
  285. package/dist/maps/gl/buckets.d.ts.map +1 -1
  286. package/dist/maps/gl/buckets.js +5 -2
  287. package/dist/maps/gl/buckets.js.map +1 -1
  288. package/dist/maps/gl/build-worker.d.ts +2 -0
  289. package/dist/maps/gl/build-worker.d.ts.map +1 -1
  290. package/dist/maps/gl/build-worker.js +1 -1
  291. package/dist/maps/gl/build-worker.js.map +1 -1
  292. package/dist/maps/gl/cover.d.ts +23 -4
  293. package/dist/maps/gl/cover.d.ts.map +1 -1
  294. package/dist/maps/gl/cover.js +61 -48
  295. package/dist/maps/gl/cover.js.map +1 -1
  296. package/dist/maps/gl/placement.d.ts +21 -8
  297. package/dist/maps/gl/placement.d.ts.map +1 -1
  298. package/dist/maps/gl/placement.js +133 -58
  299. package/dist/maps/gl/placement.js.map +1 -1
  300. package/dist/maps/gl/renderer.d.ts +36 -4
  301. package/dist/maps/gl/renderer.d.ts.map +1 -1
  302. package/dist/maps/gl/renderer.js +81 -24
  303. package/dist/maps/gl/renderer.js.map +1 -1
  304. package/dist/maps/gl/shaders.d.ts +18 -17
  305. package/dist/maps/gl/shaders.d.ts.map +1 -1
  306. package/dist/maps/gl/shaders.js +34 -32
  307. package/dist/maps/gl/shaders.js.map +1 -1
  308. package/dist/maps/gl/store.d.ts +10 -0
  309. package/dist/maps/gl/store.d.ts.map +1 -1
  310. package/dist/maps/gl/store.js +47 -4
  311. package/dist/maps/gl/store.js.map +1 -1
  312. package/dist/maps/gl/text.d.ts +153 -24
  313. package/dist/maps/gl/text.d.ts.map +1 -1
  314. package/dist/maps/gl/text.js +379 -63
  315. package/dist/maps/gl/text.js.map +1 -1
  316. package/dist/maps/gl/view.d.ts +293 -2
  317. package/dist/maps/gl/view.d.ts.map +1 -1
  318. package/dist/maps/gl/view.js +317 -82
  319. package/dist/maps/gl/view.js.map +1 -1
  320. package/dist/maps/icons.d.ts +32 -0
  321. package/dist/maps/icons.d.ts.map +1 -0
  322. package/dist/maps/icons.js +151 -0
  323. package/dist/maps/icons.js.map +1 -0
  324. package/dist/maps/index.d.ts +2 -0
  325. package/dist/maps/index.d.ts.map +1 -1
  326. package/dist/maps/index.js +2 -0
  327. package/dist/maps/index.js.map +1 -1
  328. package/dist/maps/labels.d.ts +56 -4
  329. package/dist/maps/labels.d.ts.map +1 -1
  330. package/dist/maps/labels.js +213 -46
  331. package/dist/maps/labels.js.map +1 -1
  332. package/dist/maps/node.d.ts +17 -0
  333. package/dist/maps/node.d.ts.map +1 -1
  334. package/dist/maps/node.js +64 -30
  335. package/dist/maps/node.js.map +1 -1
  336. package/dist/maps/proj.d.ts +48 -0
  337. package/dist/maps/proj.d.ts.map +1 -1
  338. package/dist/maps/proj.js +73 -0
  339. package/dist/maps/proj.js.map +1 -1
  340. package/dist/maps/sources.d.ts +5 -3
  341. package/dist/maps/sources.d.ts.map +1 -1
  342. package/dist/maps/sources.js +5 -3
  343. package/dist/maps/sources.js.map +1 -1
  344. package/dist/maps/style.d.ts +35 -0
  345. package/dist/maps/style.d.ts.map +1 -1
  346. package/dist/maps/style.js.map +1 -1
  347. package/dist/maps/styles.d.ts +12 -0
  348. package/dist/maps/styles.d.ts.map +1 -1
  349. package/dist/maps/styles.js +120 -6
  350. package/dist/maps/styles.js.map +1 -1
  351. package/dist/maps/tiles.d.ts +31 -11
  352. package/dist/maps/tiles.d.ts.map +1 -1
  353. package/dist/maps/tiles.js +61 -27
  354. package/dist/maps/tiles.js.map +1 -1
  355. package/dist/maps/types.d.ts +28 -4
  356. package/dist/maps/types.d.ts.map +1 -1
  357. package/dist/markdown/index.d.ts.map +1 -1
  358. package/dist/markdown/index.js +209 -68
  359. package/dist/markdown/index.js.map +1 -1
  360. package/dist/markdown/spans.d.ts.map +1 -1
  361. package/dist/markdown/spans.js +6 -0
  362. package/dist/markdown/spans.js.map +1 -1
  363. package/dist/qml/containers.d.ts.map +1 -1
  364. package/dist/qml/containers.js +6 -1
  365. package/dist/qml/containers.js.map +1 -1
  366. package/dist/qml/interaction.d.ts.map +1 -1
  367. package/dist/qml/interaction.js +38 -5
  368. package/dist/qml/interaction.js.map +1 -1
  369. package/dist/qml/layouts.d.ts.map +1 -1
  370. package/dist/qml/layouts.js +21 -6
  371. package/dist/qml/layouts.js.map +1 -1
  372. package/dist/qml/qtquick.d.ts.map +1 -1
  373. package/dist/qml/qtquick.js +9 -2
  374. package/dist/qml/qtquick.js.map +1 -1
  375. package/dist/qml/react.d.ts.map +1 -1
  376. package/dist/qml/react.js +9 -7
  377. package/dist/qml/react.js.map +1 -1
  378. package/dist/reorder/clock.d.ts +7 -0
  379. package/dist/reorder/clock.d.ts.map +1 -0
  380. package/dist/reorder/clock.js +26 -0
  381. package/dist/reorder/clock.js.map +1 -0
  382. package/dist/reorder/index.d.ts +25 -3
  383. package/dist/reorder/index.d.ts.map +1 -1
  384. package/dist/reorder/index.js +64 -19
  385. package/dist/reorder/index.js.map +1 -1
  386. package/dist/rich-text-editor/index.d.ts +0 -11
  387. package/dist/rich-text-editor/index.d.ts.map +1 -1
  388. package/dist/rich-text-editor/index.js +38 -9
  389. package/dist/rich-text-editor/index.js.map +1 -1
  390. package/dist/rich-text-editor/keymap.d.ts.map +1 -1
  391. package/dist/rich-text-editor/keymap.js +2 -1
  392. package/dist/rich-text-editor/keymap.js.map +1 -1
  393. package/dist/rich-text-editor/keys.d.ts +17 -6
  394. package/dist/rich-text-editor/keys.d.ts.map +1 -1
  395. package/dist/rich-text-editor/keys.js +135 -26
  396. package/dist/rich-text-editor/keys.js.map +1 -1
  397. package/dist/rich-text-editor/markdown.d.ts.map +1 -1
  398. package/dist/rich-text-editor/markdown.js +30 -1
  399. package/dist/rich-text-editor/markdown.js.map +1 -1
  400. package/dist/rich-text-editor/marks.d.ts +9 -0
  401. package/dist/rich-text-editor/marks.d.ts.map +1 -0
  402. package/dist/rich-text-editor/marks.js +83 -0
  403. package/dist/rich-text-editor/marks.js.map +1 -0
  404. package/dist/rich-text-editor/render.d.ts.map +1 -1
  405. package/dist/rich-text-editor/render.js +28 -10
  406. package/dist/rich-text-editor/render.js.map +1 -1
  407. package/dist/rich-text-editor/replace.d.ts +13 -0
  408. package/dist/rich-text-editor/replace.d.ts.map +1 -0
  409. package/dist/rich-text-editor/replace.js +64 -0
  410. package/dist/rich-text-editor/replace.js.map +1 -0
  411. package/dist/rich-text-editor/toolbar.d.ts.map +1 -1
  412. package/dist/rich-text-editor/toolbar.js +1 -1
  413. package/dist/rich-text-editor/toolbar.js.map +1 -1
  414. package/dist/rich-text-editor/virtual.d.ts.map +1 -1
  415. package/dist/rich-text-editor/virtual.js +17 -0
  416. package/dist/rich-text-editor/virtual.js.map +1 -1
  417. package/dist/richtext/node.d.ts +46 -2
  418. package/dist/richtext/node.d.ts.map +1 -1
  419. package/dist/richtext/node.js +187 -4
  420. package/dist/richtext/node.js.map +1 -1
  421. package/dist/richtext/runs.d.ts +13 -8
  422. package/dist/richtext/runs.d.ts.map +1 -1
  423. package/dist/richtext/runs.js +18 -8
  424. package/dist/richtext/runs.js.map +1 -1
  425. package/dist/table/index.d.ts.map +1 -1
  426. package/dist/table/index.js +23 -0
  427. package/dist/table/index.js.map +1 -1
  428. package/dist/table/rows.d.ts +3 -2
  429. package/dist/table/rows.d.ts.map +1 -1
  430. package/dist/table/rows.js +20 -6
  431. package/dist/table/rows.js.map +1 -1
  432. package/dist/terminal/index.d.ts.map +1 -1
  433. package/dist/terminal/index.js +4 -0
  434. package/dist/terminal/index.js.map +1 -1
  435. package/dist/terminal/vt/pty.d.ts +13 -2
  436. package/dist/terminal/vt/pty.d.ts.map +1 -1
  437. package/dist/terminal/vt/pty.js +18 -3
  438. package/dist/terminal/vt/pty.js.map +1 -1
  439. package/dist/terminal-output/index.d.ts.map +1 -1
  440. package/dist/terminal-output/index.js +103 -23
  441. package/dist/terminal-output/index.js.map +1 -1
  442. package/dist/terminal-output/runs.d.ts +4 -1
  443. package/dist/terminal-output/runs.d.ts.map +1 -1
  444. package/dist/terminal-output/runs.js +5 -1
  445. package/dist/terminal-output/runs.js.map +1 -1
  446. package/dist/three/canvas.d.ts.map +1 -1
  447. package/dist/three/canvas.js +85 -7
  448. package/dist/three/canvas.js.map +1 -1
  449. package/dist/three/events.d.ts +22 -4
  450. package/dist/three/events.d.ts.map +1 -1
  451. package/dist/three/events.js +34 -4
  452. package/dist/three/events.js.map +1 -1
  453. package/dist/tree/index.d.ts.map +1 -1
  454. package/dist/tree/index.js +17 -0
  455. package/dist/tree/index.js.map +1 -1
  456. package/package.json +42 -33
  457. package/src/charts/node.ts +62 -26
  458. package/src/charts/render.ts +120 -30
  459. package/src/code/index.ts +36 -32
  460. package/src/code-editor/completion.ts +3 -2
  461. package/src/code-editor/index.ts +19 -0
  462. package/src/code-editor/node.ts +1574 -184
  463. package/src/code-language/index.ts +1 -1
  464. package/src/code-language/languages/glsl.ts +12 -2
  465. package/src/code-language/languages/javascript.ts +21 -4
  466. package/src/code-language/languages/json.ts +12 -2
  467. package/src/code-language/languages/shell.ts +12 -2
  468. package/src/code-language/languages/sql.ts +22 -2
  469. package/src/code-language/runs.ts +111 -1
  470. package/src/code-language/stream.ts +257 -17
  471. package/src/code-language/types.ts +5 -4
  472. package/src/codeblock/index.ts +68 -4
  473. package/src/flow/draw.ts +167 -41
  474. package/src/flow/gl/color.ts +85 -0
  475. package/src/flow/gl/index.ts +342 -0
  476. package/src/flow/gl/pack.ts +585 -0
  477. package/src/flow/gl/renderer.ts +679 -0
  478. package/src/flow/gl/shaders.ts +371 -0
  479. package/src/flow/gl/text.ts +1295 -0
  480. package/src/flow/index.ts +794 -67
  481. package/src/flow/node.ts +2496 -1256
  482. package/src/flow/paint.ts +511 -0
  483. package/src/flow/paths.ts +80 -15
  484. package/src/flow/scene.ts +2199 -0
  485. package/src/flow/types.ts +113 -1
  486. package/src/html/controls.ts +73 -2
  487. package/src/html/css/calc.ts +385 -0
  488. package/src/html/css/cascade.ts +1481 -62
  489. package/src/html/css/color.ts +755 -0
  490. package/src/html/css/content.ts +417 -0
  491. package/src/html/css/counter-styles.ts +973 -0
  492. package/src/html/css/decode.ts +130 -0
  493. package/src/html/css/parse.ts +1514 -160
  494. package/src/html/css/style.ts +4131 -223
  495. package/src/html/css/ua.ts +66 -17
  496. package/src/html/css/values.ts +150 -50
  497. package/src/html/css/vars.ts +348 -0
  498. package/src/html/dom.ts +284 -17
  499. package/src/html/fonts.ts +641 -0
  500. package/src/html/gif.ts +233 -0
  501. package/src/html/index.ts +180 -20
  502. package/src/html/layout/block.ts +3560 -208
  503. package/src/html/layout/boxes.ts +2141 -190
  504. package/src/html/layout/cache.ts +289 -0
  505. package/src/html/layout/collapse.ts +317 -0
  506. package/src/html/layout/css-grid.ts +1063 -0
  507. package/src/html/layout/flex.ts +1115 -106
  508. package/src/html/layout/floats.ts +128 -14
  509. package/src/html/layout/grid-lines.ts +141 -0
  510. package/src/html/layout/grid.ts +181 -0
  511. package/src/html/layout/inline.ts +4187 -192
  512. package/src/html/layout/shaping.ts +140 -0
  513. package/src/html/layout/table.ts +696 -186
  514. package/src/html/layout/tracks.ts +437 -0
  515. package/src/html/node.ts +1585 -156
  516. package/src/html/paint.ts +4582 -267
  517. package/src/html/resources.ts +161 -50
  518. package/src/html/surfaces.ts +81 -0
  519. package/src/html/svg.ts +504 -0
  520. package/src/html/url.ts +86 -0
  521. package/src/index.ts +2 -0
  522. package/src/internal/codelines.ts +200 -0
  523. package/src/internal/collate.ts +12 -0
  524. package/src/internal/markdown/ast.ts +11 -0
  525. package/src/internal/markdown/parse.ts +154 -11
  526. package/src/internal/prewarm.ts +45 -0
  527. package/src/internal/sdf.ts +227 -0
  528. package/src/internal/splice.ts +25 -0
  529. package/src/internal/window.ts +36 -5
  530. package/src/maps/anchors.ts +307 -1
  531. package/src/maps/controller.ts +116 -31
  532. package/src/maps/gl/buckets.ts +5 -1
  533. package/src/maps/gl/build-worker.ts +13 -2
  534. package/src/maps/gl/cover.ts +79 -44
  535. package/src/maps/gl/placement.ts +221 -69
  536. package/src/maps/gl/renderer.ts +104 -20
  537. package/src/maps/gl/shaders.ts +35 -32
  538. package/src/maps/gl/store.ts +66 -17
  539. package/src/maps/gl/text.ts +444 -71
  540. package/src/maps/gl/view.ts +361 -96
  541. package/src/maps/icons.ts +183 -0
  542. package/src/maps/index.ts +4 -0
  543. package/src/maps/labels.ts +269 -49
  544. package/src/maps/node.ts +82 -49
  545. package/src/maps/proj.ts +87 -0
  546. package/src/maps/sources.ts +5 -3
  547. package/src/maps/style.ts +32 -0
  548. package/src/maps/styles.ts +215 -4
  549. package/src/maps/tiles.ts +64 -31
  550. package/src/maps/types.ts +28 -4
  551. package/src/markdown/index.ts +293 -92
  552. package/src/markdown/spans.ts +5 -0
  553. package/src/qml/containers.tsx +6 -1
  554. package/src/qml/interaction.tsx +42 -5
  555. package/src/qml/layouts.tsx +21 -6
  556. package/src/qml/qtquick.tsx +9 -2
  557. package/src/qml/react.tsx +7 -5
  558. package/src/reorder/clock.ts +27 -0
  559. package/src/reorder/index.ts +100 -27
  560. package/src/rich-text-editor/index.ts +37 -12
  561. package/src/rich-text-editor/keymap.ts +1 -1
  562. package/src/rich-text-editor/keys.ts +135 -26
  563. package/src/rich-text-editor/markdown.ts +36 -1
  564. package/src/rich-text-editor/marks.ts +101 -0
  565. package/src/rich-text-editor/render.ts +49 -12
  566. package/src/rich-text-editor/replace.ts +85 -0
  567. package/src/rich-text-editor/toolbar.ts +1 -1
  568. package/src/rich-text-editor/virtual.ts +25 -0
  569. package/src/richtext/node.ts +235 -14
  570. package/src/richtext/runs.ts +23 -12
  571. package/src/table/index.ts +29 -0
  572. package/src/table/rows.ts +18 -8
  573. package/src/terminal/index.ts +4 -0
  574. package/src/terminal/vt/pty.ts +16 -2
  575. package/src/terminal-output/index.ts +129 -31
  576. package/src/terminal-output/runs.ts +16 -2
  577. package/src/three/canvas.ts +102 -8
  578. package/src/three/events.ts +49 -9
  579. package/src/tree/index.ts +19 -0
package/src/flow/index.ts CHANGED
@@ -16,13 +16,14 @@
16
16
  // constraint").
17
17
  import React, {
18
18
  useEffect,
19
+ useLayoutEffect,
19
20
  useImperativeHandle,
20
21
  useMemo,
21
22
  useRef,
22
23
  useState,
23
24
  } from 'react';
24
25
  import type { Dispatch, ReactElement, SetStateAction } from 'react';
25
- import { Renderer } from 'react-x11';
26
+ import { Renderer, useApp, useScale, useSupports, useTheme } from 'react-x11';
26
27
  import { registerElement, registeredElements } from 'react-x11/host';
27
28
  import { createStyles, flattenStyle } from 'react-x11/style';
28
29
  // Loads the module the JSX augmentation at the bottom targets: nothing in
@@ -31,7 +32,8 @@ import { createStyles, flattenStyle } from 'react-x11/style';
31
32
  // addition. Type-only, so it is erased.
32
33
  import type {} from 'react-x11/jsx-runtime';
33
34
 
34
- import { applyEdgeChanges, applyNodeChanges } from './model.js';
35
+ import { applyEdgeChanges, applyNodeChanges, resolvePalette } from './model.js';
36
+ import { CULL_MARGIN } from './scene.js';
35
37
  import { ELEMENT, FlowGraphNode, SELF_DAMAGED_PROPS } from './node.js';
36
38
  import type {
37
39
  EdgeChange,
@@ -41,8 +43,10 @@ import type {
41
43
  FlowNodeData,
42
44
  FlowNodeType,
43
45
  FlowProps,
46
+ FlowRect,
44
47
  NodeBodyRect,
45
48
  NodeChange,
49
+ XYPosition,
46
50
  } from './types.js';
47
51
 
48
52
  if (!registeredElements().includes(ELEMENT)) {
@@ -75,6 +79,19 @@ const styles = createStyles({
75
79
  pane: { flexGrow: 1, overflow: 'hidden' },
76
80
  clip: { overflow: 'hidden' },
77
81
  fill: { flexGrow: 1 },
82
+ // Over the pane, holding the bodies' layer and clipping it to the pane:
83
+ // a 2D pan carries the layer's pixels in its blit, and what the layer can
84
+ // paint outside the pane — in the border round it — would be repainted
85
+ // on every step (`_blitPan` in ./node.ts).
86
+ bodiesClip: {
87
+ position: 'absolute',
88
+ left: 0,
89
+ top: 0,
90
+ right: 0,
91
+ bottom: 0,
92
+ overflow: 'hidden',
93
+ pointerEvents: 'box-none',
94
+ },
78
95
  });
79
96
 
80
97
  /**
@@ -103,6 +120,163 @@ const flushSync: (fn: () => void) => void =
103
120
  ).flushSyncFromReconciler(fn)
104
121
  : (fn) => fn();
105
122
 
123
+ /**
124
+ * The GL renderer's module, once it has loaded — reached by dynamic
125
+ * `import()` and never statically, so an application that never asks for
126
+ * `renderer="gl"` bundles none of it.
127
+ */
128
+ type GlModule = typeof import('./gl/index.js');
129
+
130
+ let glModule: GlModule | null = null;
131
+ let glLoading: Promise<GlModule> | null = null;
132
+
133
+ /** Load the GL renderer, once per process however many panes ask. A failed
134
+ * load is forgotten, so the next pane to ask tries again. */
135
+ function loadGl(): Promise<GlModule> {
136
+ glLoading ??= import('./gl/index.js').then(
137
+ (module) => (glModule = module),
138
+ (error: unknown) => {
139
+ glLoading = null;
140
+ throw error;
141
+ },
142
+ );
143
+ return glLoading;
144
+ }
145
+
146
+ /** How far a card's handles and grips ink outside its box — the canvas
147
+ * its card is painted on in the bodies' layer reaches that far round it. */
148
+ const CARD_INK = CULL_MARGIN;
149
+
150
+ /** The grid the bodies' layer grows to, in logical pixels. Every card is
151
+ * placed relative to the layer's corner, so a corner that followed the
152
+ * cards exactly moved — and moved every card with it — whenever the one
153
+ * being dragged was the outermost; grown to this grid it moves only when
154
+ * a card crosses a line of it. */
155
+ const EXTENT_STEP = 256;
156
+
157
+ /** A number per object, for a card's cache key: a style or a handle list
158
+ * that changed is a new object, and its card is painted again. */
159
+ const serials = new WeakMap<object, number>();
160
+ let serial = 0;
161
+ function serialOf(node: object): number {
162
+ let n = serials.get(node);
163
+ if (n === undefined) serials.set(node, (n = ++serial));
164
+ return n;
165
+ }
166
+
167
+ /**
168
+ * What of a node its card draws, for the card's cache key: the label, the
169
+ * description, the style and the handles. A body's own data — a queue
170
+ * length, a chart's points — is none of it: keyed on the node object, a
171
+ * live body that patched its data repainted its card on every patch, the
172
+ * title's text shaped and drawn again for nothing. A type that paints its
173
+ * own card may draw anything of the node, so it is keyed on the node whole.
174
+ */
175
+ function cardShows(
176
+ node: FlowNode<unknown>,
177
+ type: FlowNodeType<unknown>,
178
+ ): string {
179
+ if (type.paint) return `n${serialOf(node)}`;
180
+ const data = node.data as FlowNodeData | undefined;
181
+ const style = node.style ? serialOf(node.style) : 0;
182
+ const handles = node.handles ? serialOf(node.handles) : 0;
183
+ return (
184
+ `${style}:${handles}:${String(data?.label ?? '')}` +
185
+ `\u0000${String(data?.description ?? '')}`
186
+ );
187
+ }
188
+
189
+ /** What `forwardWheel` reads off a wheel that landed on a body. */
190
+ interface WheelLike {
191
+ x: number;
192
+ y: number;
193
+ deltaX: number;
194
+ deltaY: number;
195
+ defaultPrevented?: boolean;
196
+ preventDefault(): void;
197
+ target?: WheelTarget;
198
+ }
199
+ interface WheelTarget {
200
+ parent?: WheelTarget | null;
201
+ scale?: number;
202
+ kind?: string;
203
+ props?: Record<string, unknown>;
204
+ style?: { cursor?: string };
205
+ canScroll?(dx: number, dy: number): boolean;
206
+ }
207
+
208
+ /** What `forwardPress` reads off a press, a drag step or a release. */
209
+ interface PressLike extends WheelLike {
210
+ button?: number;
211
+ capturePointer?(): void;
212
+ }
213
+
214
+ /** The pane's half of a pointer gesture, as `forwardPress` drives it. */
215
+ interface PressSink {
216
+ scale?: number;
217
+ parent?: unknown;
218
+ defaultMouseDown?(ev: never): void;
219
+ defaultMouseDrag?(ev: never): void;
220
+ defaultMouseUp?(ev: never): void;
221
+ }
222
+
223
+ /** Roles a press means something to — the body keeps those presses. */
224
+ const INTERACTIVE_ROLES = new Set([
225
+ 'button',
226
+ 'checkbox',
227
+ 'radio',
228
+ 'switch',
229
+ 'slider',
230
+ 'spinbutton',
231
+ 'textbox',
232
+ 'searchbox',
233
+ 'combobox',
234
+ 'listbox',
235
+ 'option',
236
+ 'menuitem',
237
+ 'tab',
238
+ 'link',
239
+ 'scrollbar',
240
+ ]);
241
+
242
+ /**
243
+ * Whether a press on `n` is the body's own: a control, something that
244
+ * listens for the press, something whose cursor says it can be clicked or
245
+ * typed into, or something that scrolls. Everything else in a body — its
246
+ * padding, a label, a progress bar — is the card, and a press there is the
247
+ * card's: it selects and drags the node, as it would on a card with no body.
248
+ */
249
+ function interactive(n: WheelTarget): boolean {
250
+ const props = n.props ?? {};
251
+ if (
252
+ typeof props.onMouseDown === 'function' ||
253
+ typeof props.onClick === 'function' ||
254
+ typeof props.onPress === 'function' ||
255
+ typeof props.onChange === 'function' ||
256
+ typeof props.onMouseUp === 'function'
257
+ ) {
258
+ return true;
259
+ }
260
+ if (typeof props.role === 'string' && INTERACTIVE_ROLES.has(props.role)) {
261
+ return true;
262
+ }
263
+ if (typeof props.tabIndex === 'number' && props.tabIndex >= 0) return true;
264
+ const cursor = n.style?.cursor;
265
+ if (cursor === 'pointer' || cursor === 'text') return true;
266
+ if (n.kind === 'textinput' || n.kind === 'textarea') return true;
267
+ return Boolean(n.canScroll?.(0, 1) || n.canScroll?.(1, 0));
268
+ }
269
+
270
+ /** The event as the pane reads it: its coordinates taken out of the unit of
271
+ * the body they landed in (`scale`, the zoom) and into the window's. */
272
+ function inPaneUnits<E extends WheelLike>(ev: E, sink: PressSink): E {
273
+ const ratio = (ev.target?.scale ?? 1) / (sink.scale || 1);
274
+ return Object.assign(Object.create(ev) as E, {
275
+ x: ev.x * ratio,
276
+ y: ev.y * ratio,
277
+ });
278
+ }
279
+
106
280
  interface FlowNodeBodyProps {
107
281
  type: FlowNodeType<unknown>;
108
282
  node: FlowNode<unknown>;
@@ -114,6 +288,27 @@ interface FlowNodeBodyProps {
114
288
  height: number;
115
289
  }
116
290
 
291
+ /**
292
+ * The same node, as far as its body can tell: every field but `position`.
293
+ *
294
+ * Identity is not the test. A drag — and every commit of a controlled graph
295
+ * — hands the pane a *new* node object each step with only its position
296
+ * changed, so comparing identity called `render` on every step of every
297
+ * drag: 60 renders with unchanged `data` in a 60-step drag, counted by
298
+ * `examples/flow-stress.tsx`. Any other field that changes — `data`,
299
+ * `selected`, `style`, a size — still re-renders, as it must.
300
+ */
301
+ function sameButPosition(a: FlowNode<unknown>, b: FlowNode<unknown>): boolean {
302
+ if (a === b) return true;
303
+ const keys = Object.keys(a) as (keyof FlowNode<unknown>)[];
304
+ if (keys.length !== Object.keys(b).length) return false;
305
+ for (const key of keys) {
306
+ if (key === 'position') continue;
307
+ if (!Object.is(a[key], b[key])) return false;
308
+ }
309
+ return true;
310
+ }
311
+
117
312
  /**
118
313
  * One mounted node body. Memoized so that a *move* re-renders nothing: a
119
314
  * drag or a pan changes only the overlay box's `left`/`top` — the body's
@@ -141,13 +336,136 @@ const FlowNodeBody = React.memo(
141
336
  },
142
337
  (a, b) =>
143
338
  a.type === b.type &&
144
- a.node === b.node &&
339
+ sameButPosition(a.node, b.node) &&
145
340
  a.selected === b.selected &&
146
341
  a.zoom === b.zoom &&
147
342
  a.width === b.width &&
148
343
  a.height === b.height,
149
344
  );
150
345
 
346
+ /** What one mounted card is drawn from — see {@link FlowBodyCard}. */
347
+ interface FlowBodyCardProps {
348
+ body: NodeBodyRect;
349
+ node: FlowNode<unknown>;
350
+ type: FlowNodeType<unknown>;
351
+ /** The card's box in the layer it sits in, on the device-pixel grid. */
352
+ left: number;
353
+ top: number;
354
+ width: number;
355
+ height: number;
356
+ /** The card's fill, which its body's box takes too. */
357
+ fill: string;
358
+ cacheKey: string;
359
+ /** One per card for as long as what paints cards holds (`drawCard`). */
360
+ onDraw: (ctx: unknown, info: { node: { abs: XYPosition } }) => void;
361
+ /** Where each card is the surface's child in its own right (no layer),
362
+ * where the layer would have put it, whether it sits a zoom out, and
363
+ * the forwarding it does for itself. */
364
+ offsetX?: number;
365
+ offsetY?: number;
366
+ hidden?: boolean;
367
+ handlers?: Record<string, (ev: unknown) => void>;
368
+ }
369
+
370
+ /**
371
+ * One mounted node: its card, painted by the pane's own 2D painter
372
+ * (`FlowGraphNode.paintCard`), and its body over it. A component of its own
373
+ * so that React can skip it: every prop is a value or a reference that
374
+ * holds while the card does, and a drag re-renders the card that moved.
375
+ */
376
+ const FlowBodyCard = React.memo(function FlowBodyCard(
377
+ props: FlowBodyCardProps,
378
+ ): ReactElement {
379
+ const { body, node, type } = props;
380
+ const { card } = body;
381
+ // The two boxes are two units. The outer one is the pane's: it is
382
+ // positioned and clipped in the same logical window pixels the pane
383
+ // painted the card in, so the body lands on the card exactly. The inner
384
+ // one is the body's, and `scale` is what makes the difference between
385
+ // them the zoom (react-x11#449, core 2.6): every length under it is
386
+ // multiplied by that factor, so a body sized in graph units comes out at
387
+ // the size the card is drawn at, with its text shaped at that size rather
388
+ // than stretched.
389
+ const width = body.width / body.zoom;
390
+ const height = body.height / body.zoom;
391
+ const bodyBox = React.createElement(
392
+ 'box',
393
+ {
394
+ key: 'body',
395
+ style: {
396
+ position: 'absolute',
397
+ left: body.x - card.x + CARD_INK,
398
+ top: body.y - card.y + CARD_INK,
399
+ width: body.width,
400
+ height: body.height,
401
+ // A body is free to overflow what it was given — a popup's
402
+ // fallback, a long line — and this is what stops it spilling over
403
+ // the graph.
404
+ overflow: 'hidden',
405
+ // Opaque, in the card's own fill: the card painted under it shows
406
+ // none of what a transparent body would let through.
407
+ backgroundColor: props.fill,
408
+ },
409
+ },
410
+ React.createElement(
411
+ 'box',
412
+ {
413
+ scale: body.zoom,
414
+ // Sized in the unit it establishes, which is what makes it fill the
415
+ // outer box at every zoom.
416
+ style: { width, height },
417
+ },
418
+ React.createElement(FlowNodeBody, {
419
+ type,
420
+ node,
421
+ selected: body.selected,
422
+ zoom: body.zoom,
423
+ // Graph units, like everything else the body sees.
424
+ x: body.x / body.zoom,
425
+ y: body.y / body.zoom,
426
+ width,
427
+ height,
428
+ }),
429
+ ),
430
+ );
431
+ // The card, just under its body: the bodies share one layer over every
432
+ // card the graph draws, so without it a body covered the header, border
433
+ // and handles of any card over its own.
434
+ const cardCanvas = React.createElement('canvas', {
435
+ key: 'card',
436
+ style: {
437
+ position: 'absolute',
438
+ left: 0,
439
+ top: 0,
440
+ width: card.width + CARD_INK * 2,
441
+ height: card.height + CARD_INK * 2,
442
+ pointerEvents: 'none',
443
+ },
444
+ cacheKey: props.cacheKey,
445
+ onDraw: props.onDraw,
446
+ });
447
+ return React.createElement(
448
+ 'box',
449
+ {
450
+ style: {
451
+ position: 'absolute',
452
+ left: props.left + (props.offsetX ?? 0),
453
+ top: props.top + (props.offsetY ?? 0),
454
+ width: props.width,
455
+ height: props.height,
456
+ // the card's presses are the pane's; its body's are the body's
457
+ pointerEvents: 'box-none',
458
+ ...(props.hidden === undefined
459
+ ? null
460
+ : { display: props.hidden ? 'none' : 'flex' }),
461
+ },
462
+ ...props.handlers,
463
+ },
464
+ cardCanvas,
465
+ bodyBox,
466
+ );
467
+ });
468
+
151
469
  /**
152
470
  * A directed graph.
153
471
  *
@@ -188,10 +506,16 @@ export function Flow<N = FlowNodeData, E = unknown>(
188
506
  onWheel,
189
507
  style,
190
508
  ref,
509
+ renderer,
510
+ onFrame,
511
+ onError,
191
512
  ...rest
192
513
  } = props;
193
514
 
194
515
  const pane = useRef<FlowGraphNode | null>(null);
516
+ // whether the surface draws the graph, as of the last render — read by a
517
+ // layout effect declared before the surface is decided
518
+ const drawsGlRef = useRef(false);
195
519
 
196
520
  const [ownNodes, setOwnNodes] = useState<readonly FlowNode<N>[]>(
197
521
  () => defaultNodes ?? [],
@@ -227,12 +551,20 @@ export function Flow<N = FlowNodeData, E = unknown>(
227
551
  // it: the ref attaches after the commit that created the node, and a
228
552
  // handle built once must still work for the whole mount.
229
553
  getViewport: () => pane.current?.getViewport() ?? { x: 0, y: 0, zoom: 1 },
230
- setViewport: (viewport) => pane.current?.setViewport(viewport),
231
- zoomIn: (step) => pane.current?.zoomIn(step),
232
- zoomOut: (step) => pane.current?.zoomOut(step),
233
- zoomTo: (zoom) => pane.current?.zoomTo(zoom),
234
- fitView: (options) => pane.current?.fitView(options),
235
- setCenter: (x, y, options) => pane.current?.setCenter(x, y, options),
554
+ // the ones that move the viewport, as calls through the handle
555
+ // (`FlowGraphNode.fromHandle`): their bodies commit with the pan
556
+ setViewport: (viewport) =>
557
+ pane.current?.fromHandle(() => pane.current?.setViewport(viewport)),
558
+ zoomIn: (step) =>
559
+ pane.current?.fromHandle(() => pane.current?.zoomIn(step)),
560
+ zoomOut: (step) =>
561
+ pane.current?.fromHandle(() => pane.current?.zoomOut(step)),
562
+ zoomTo: (zoom) =>
563
+ pane.current?.fromHandle(() => pane.current?.zoomTo(zoom)),
564
+ fitView: (options) =>
565
+ pane.current?.fromHandle(() => pane.current?.fitView(options)),
566
+ setCenter: (x, y, options) =>
567
+ pane.current?.fromHandle(() => pane.current?.setCenter(x, y, options)),
236
568
  screenToFlowPosition: (point) =>
237
569
  pane.current?.screenToFlowPosition(point) ?? point,
238
570
  flowToScreenPosition: (point) =>
@@ -280,23 +612,65 @@ export function Flow<N = FlowNodeData, E = unknown>(
280
612
 
281
613
  // --- mounted node bodies ------------------------------------------------
282
614
  //
283
- // Only the node types that asked for one cost anything: with no `render`
284
- // in the registry the pane is never given `onNodeBodies`, never computes a
285
- // rect, and this half of the component is one `useState` that stays empty.
615
+ // Only the nodes whose type asked for one cost anything: with none in the
616
+ // graph the pane is never given `onNodeBodies`, never computes a rect, and
617
+ // this half of the component is one `useState` that stays empty. Asked of
618
+ // the nodes and not of the registry, because the registry is an app's
619
+ // whole vocabulary: a type with a body that no node uses is not a body,
620
+ // and it cost a graph of plain cards the GL surface on XQuartz (below) —
621
+ // the stress example registers its widget types for every scene, and its
622
+ // lattices drew in 2D there, a node dragged at 20 fps where GL drags it
623
+ // at 75. A scan per render of the nodes, a few microseconds at 2,000.
286
624
  const { nodeTypes } = rest;
625
+ const bodyTypes = useMemo(() => {
626
+ if (!nodeTypes) return null;
627
+ const names = Object.keys(nodeTypes).filter(
628
+ (name) => nodeTypes[name]?.render != null,
629
+ );
630
+ return names.length > 0 ? new Set(names) : null;
631
+ }, [nodeTypes]);
287
632
  const mounts = useMemo(
288
633
  () =>
289
- nodeTypes
290
- ? Object.values(nodeTypes).some((type) => type?.render != null)
291
- : false,
292
- [nodeTypes],
634
+ bodyTypes != null &&
635
+ currentNodes.some((node) => bodyTypes.has(node.type ?? 'default')),
636
+ [bodyTypes, currentNodes],
293
637
  );
294
638
  const [bodies, setBodies] = useState<readonly NodeBodyRect[]>([]);
295
- // Gesture-time emissions commit inline (see `flushSync` above); the rest —
296
- // a programmatic `fitView`, the first paint — take the ordinary path.
297
- const handleBodies = (next: readonly NodeBodyRect[], sync: boolean): void => {
298
- if (sync) flushSync(() => setBodies(next));
299
- else setBodies(next);
639
+ // Where the graph's origin sits in the pane. The bodies are laid out in
640
+ // one box placed here, so a pan — which moves this and nothing else — is
641
+ // one style change rather than a render of every body.
642
+ const [origin, setOrigin] = useState<XYPosition>({ x: 0, y: 0 });
643
+ // Hidden while a zoom moves, and nothing else about them touched — see
644
+ // `_holdBodies` in ./node.ts for what re-scaling them every step cost.
645
+ const [held, setHeld] = useState(false);
646
+ // Where the minimap and controls are, painted over the bodies when there
647
+ // are any to be over.
648
+ const [panels, setPanels] = useState<readonly FlowRect[]>([]);
649
+ const panelCanvases = useRef<
650
+ ({
651
+ invalidate(layout: boolean, damage: unknown, reason: string): void;
652
+ } | null)[]
653
+ >([]);
654
+ // Gesture-time emissions commit inline (see `flushSync` above), and so do a
655
+ // handle's — a programmatic pan (`FlowGraphNode.fromHandle`). One made
656
+ // while React is already committing, a pan from a layout effect, is not
657
+ // flushed there: the reconciler runs it at sync priority as the commit
658
+ // ends. The rest — the first paint, what the pane's own frames emit — take
659
+ // the ordinary path.
660
+ const handleBodies = (
661
+ next: readonly NodeBodyRect[],
662
+ sync: boolean,
663
+ at: XYPosition,
664
+ hold: boolean,
665
+ ): void => {
666
+ const apply = (): void => {
667
+ // the same array on a pan: React bails out of this update
668
+ setBodies(next);
669
+ setOrigin((was) => (was.x === at.x && was.y === at.y ? was : at));
670
+ setHeld(hold);
671
+ };
672
+ if (sync) flushSync(apply);
673
+ else apply();
300
674
  };
301
675
  const byId = useMemo(() => {
302
676
  const map = new Map<string, FlowNode<N>>();
@@ -304,58 +678,394 @@ export function Flow<N = FlowNodeData, E = unknown>(
304
678
  return map;
305
679
  }, [mounts, currentNodes]);
306
680
 
307
- const overlays = mounts
308
- ? bodies.map((body) => {
309
- const node = byId.get(body.id);
310
- const type = node && nodeTypes?.[node.type ?? 'default'];
311
- if (!node || !type?.render) return null;
312
- // The two boxes are two units. The outer one is the pane's: it is
313
- // positioned and clipped in the same logical window pixels the pane
314
- // painted the card in, so the body lands on the card exactly. The
315
- // inner one is the body's, and `scale` is what makes the difference
316
- // between them the zoom (react-x11#449, core 2.6): every length
317
- // under it is multiplied by that factor, so a body sized in graph
318
- // units comes out at the size the card is drawn at, with its text
319
- // shaped at that size rather than stretched.
320
- const width = body.width / body.zoom;
321
- const height = body.height / body.zoom;
322
- return React.createElement(
681
+ // Every box the bodies are laid out in sits on the device-pixel grid.
682
+ // Layout rounds a box's two edges to whole device pixels separately, so at
683
+ // a fractional scale (1.25, 1.5) a box that moved by a fraction of a
684
+ // device pixel also changed size by one — and core moves a `<glarea>`
685
+ // child's pixels on its pane only when it moved and nothing else
686
+ // (sidorares/react-x11#644): a pan over bodies repainted every one of
687
+ // them, every step. On the grid, a pan moves the layer by whole pixels
688
+ // and every card in it by exactly as many.
689
+ const rootScale = useScale();
690
+ // the pane's own, where it is mounted: a `scale` prop above it counts
691
+ const deviceScale = pane.current?.scale ?? rootScale;
692
+ const snap = (v: number): number => Math.round(v * deviceScale) / deviceScale;
693
+
694
+ // The box the bodies are laid out in covers exactly them. It has to have a
695
+ // real size: core culls a child whose *own* box is off screen or outside
696
+ // a clipping ancestor before it looks at the child's subtree, and a 0×0
697
+ // box at the graph's origin is off screen whenever the graph has been
698
+ // panned past the pane's top-left — which took every body with it.
699
+ const extent = useMemo(() => {
700
+ if (bodies.length === 0) return { x: 0, y: 0, width: 0, height: 0 };
701
+ let x0 = Infinity;
702
+ let y0 = Infinity;
703
+ let x1 = -Infinity;
704
+ let y1 = -Infinity;
705
+ for (const { card } of bodies) {
706
+ x0 = Math.min(x0, card.x - CARD_INK);
707
+ y0 = Math.min(y0, card.y - CARD_INK);
708
+ x1 = Math.max(x1, card.x + card.width + CARD_INK);
709
+ y1 = Math.max(y1, card.y + card.height + CARD_INK);
710
+ }
711
+ const out = (v: number, up: boolean): number =>
712
+ snap((up ? Math.ceil : Math.floor)(v / EXTENT_STEP) * EXTENT_STEP);
713
+ x0 = out(x0, false);
714
+ y0 = out(y0, false);
715
+ x1 = out(x1, true);
716
+ y1 = out(y1, true);
717
+ return { x: x0, y: y0, width: x1 - x0, height: y1 - y0 };
718
+ }, [bodies, deviceScale]);
719
+
720
+ // The fill a card is drawn in, which its body's box takes too.
721
+ const theme = useTheme();
722
+ const nodeFill = resolvePalette(
723
+ theme as unknown as Record<string, unknown> | null,
724
+ rest.palette,
725
+ ).nodeBackground;
726
+
727
+ // One `onDraw` per card for as long as what paints cards holds: core
728
+ // repaints a canvas whose `onDraw` is a new function (react-x11
729
+ // src/nodes/canvas.js), and a closure made per render made every step of
730
+ // a drag repaint every card on screen and every body over them — 36
731
+ // claims, past the frame's rect cap, one rect the size of the pane. What a
732
+ // card shows is in its `cacheKey`; the palette, the theme and the node
733
+ // types are what paint it, and a change to any of them starts over.
734
+ const cardPaint = useMemo(
735
+ () => ({
736
+ drawers: new Map<
737
+ string,
738
+ (ctx: unknown, info: { node: { abs: XYPosition } }) => void
739
+ >(),
740
+ }),
741
+ [theme, rest.palette, nodeTypes],
742
+ );
743
+ const drawCard = (
744
+ id: string,
745
+ ): ((ctx: unknown, info: { node: { abs: XYPosition } }) => void) => {
746
+ let draw = cardPaint.drawers.get(id);
747
+ if (!draw) {
748
+ draw = (ctx, info) => pane.current?.paintCard(id, ctx, info.node.abs);
749
+ cardPaint.drawers.set(id, draw);
750
+ }
751
+ return draw;
752
+ };
753
+
754
+ // One card per body, each a memoized component: a drag step moves one
755
+ // card and hands the others the same props they had — the same body
756
+ // object (`_emitBodies` reuses it), the same node object, the same
757
+ // `onDraw` — so React re-renders the card that moved and bails out of the
758
+ // rest. Built as one element list per render, which a pan does not
759
+ // trigger: the box they sit in moves, and nothing here is asked.
760
+ const cards = useMemo(() => {
761
+ if (!mounts) return null;
762
+ const out: FlowBodyCardProps[] = [];
763
+ for (const body of bodies) {
764
+ const node = byId.get(body.id);
765
+ const type = node && nodeTypes?.[node.type ?? 'default'];
766
+ if (!node || !type?.render) continue;
767
+ const { card } = body;
768
+ // on the grid, like the layer (`snap` above)
769
+ const left = snap(card.x - CARD_INK);
770
+ const top = snap(card.y - CARD_INK);
771
+ out.push({
772
+ body,
773
+ node: node as FlowNode<unknown>,
774
+ type: type as FlowNodeType<unknown>,
775
+ left: left - extent.x,
776
+ top: top - extent.y,
777
+ // Sized from the card alone, not as the gap between two edges each
778
+ // put on the grid: at 1.25x a card dragged a fraction of a pixel
779
+ // grew or shrank by one as its edges rounded apart, and a box that
780
+ // changed size is no longer a box that only moved — every step of
781
+ // a drag measured the content floors and repainted the card where
782
+ // core would have moved it. The box draws nothing and takes no
783
+ // pointer; the canvas and the body inside it keep sizes of their
784
+ // own.
785
+ width: snap(card.width + CARD_INK * 2),
786
+ height: snap(card.height + CARD_INK * 2),
787
+ fill:
788
+ (node.style as { background?: string } | undefined)?.background ??
789
+ nodeFill,
790
+ // Cached until something it shows changes (`cardShows`): a new
791
+ // label or style repaints it, a body's own data does not.
792
+ cacheKey:
793
+ `${body.id}:${cardShows(node as FlowNode<unknown>, type as FlowNodeType<unknown>)}:` +
794
+ `${card.width}x${card.height}:` +
795
+ `${body.zoom}:${body.selected}:${body.hovered}:` +
796
+ `${serialOf(cardPaint)}`,
797
+ onDraw: drawCard(body.id),
798
+ });
799
+ }
800
+ return out;
801
+ }, [
802
+ mounts,
803
+ bodies,
804
+ extent,
805
+ byId,
806
+ nodeTypes,
807
+ nodeFill,
808
+ deviceScale,
809
+ cardPaint,
810
+ ]);
811
+ const overlays = cards?.map((props) =>
812
+ React.createElement(FlowBodyCard, { key: props.body.id, ...props }),
813
+ );
814
+ // A wheel over a body is a wheel over the graph. Core runs the wheel's
815
+ // default action on the node under the pointer and scrolls up from there,
816
+ // and the bodies are the pane's siblings, not its children — so over a
817
+ // card's body the pane never heard it, and zooming a graph whose cards
818
+ // are mostly bodies stalled wherever the pointer rested. Forwarded unless
819
+ // something between the pointer and the pane can scroll that way itself:
820
+ // a list inside a card keeps its wheel.
821
+ const forwardWheel = useMemo(
822
+ () =>
823
+ (ev: unknown): void => {
824
+ const node = pane.current;
825
+ const wheel = ev as WheelLike;
826
+ if (!node || wheel.defaultPrevented) return;
827
+ const stop = node.parent;
828
+ for (
829
+ let n: WheelTarget | null | undefined = wheel.target;
830
+ n && n !== (stop as unknown);
831
+ n = n.parent
832
+ ) {
833
+ if (n.canScroll?.(wheel.deltaX, wheel.deltaY)) return;
834
+ }
835
+ node.defaultWheel(inPaneUnits(wheel, node) as never);
836
+ wheel.preventDefault();
837
+ },
838
+ [],
839
+ );
840
+ // A press on a body's card-like part is a press on the card. Core runs a
841
+ // press's default actions — select, drag, and the drag's steps and
842
+ // release — on the node that took it, so a body took them all: the cards
843
+ // of a graph whose nodes are mostly body could not be selected or dragged
844
+ // by most of their area. A press that bubbles up here from something that
845
+ // is not a control is the pane's instead: its default is vetoed on the
846
+ // body, the pointer captured so the drag and the release come back here
847
+ // wherever they wander, and each step handed to the pane.
848
+ const pressing = useRef(false);
849
+ const forwardPress = useMemo(() => {
850
+ // From the pressed node up to the bodies' box — which listens for the
851
+ // press itself, and is where the walk stops.
852
+ const within = (ev: PressLike, sink: PressSink): boolean => {
853
+ for (
854
+ let n: WheelTarget | null | undefined = ev.target;
855
+ n && n !== (sink.parent as unknown);
856
+ n = n.parent
857
+ ) {
858
+ if (n.props?.onMouseDown === handlers.onMouseDown) return true;
859
+ if (interactive(n)) return false;
860
+ }
861
+ return true;
862
+ };
863
+ const handlers = {
864
+ onMouseDown: (ev: unknown): void => {
865
+ const press = ev as PressLike;
866
+ const sink = pane.current as unknown as PressSink | null;
867
+ if (!sink || press.defaultPrevented || (press.button ?? 1) !== 1)
868
+ return;
869
+ if (!within(press, sink)) return;
870
+ pressing.current = true;
871
+ press.preventDefault();
872
+ press.capturePointer?.();
873
+ sink.defaultMouseDown?.(inPaneUnits(press, sink) as never);
874
+ },
875
+ onMouseMove: (ev: unknown): void => {
876
+ if (!pressing.current) return;
877
+ const sink = pane.current as unknown as PressSink | null;
878
+ sink?.defaultMouseDrag?.(inPaneUnits(ev as PressLike, sink) as never);
879
+ },
880
+ onMouseUp: (ev: unknown): void => {
881
+ if (!pressing.current) return;
882
+ pressing.current = false;
883
+ const sink = pane.current as unknown as PressSink | null;
884
+ sink?.defaultMouseUp?.(inPaneUnits(ev as PressLike, sink) as never);
885
+ },
886
+ };
887
+ return handlers;
888
+ }, []);
889
+ // what each card forwards on its own where it is the surface's child
890
+ const directHandlers = useMemo(
891
+ () => ({ onWheel: forwardWheel, ...forwardPress }),
892
+ [forwardWheel, forwardPress],
893
+ );
894
+ const bodiesLayer = useRef<unknown>(null);
895
+ const layer =
896
+ overlays && overlays.length > 0
897
+ ? React.createElement(
323
898
  'box',
324
899
  {
325
- key: body.id,
900
+ key: 'bodies',
901
+ ref: bodiesLayer,
326
902
  style: {
327
903
  position: 'absolute',
328
- left: body.x,
329
- top: body.y,
330
- width: body.width,
331
- height: body.height,
332
- // A body is free to overflow what it was given — a popup's
333
- // fallback, a long line — and this is what stops it spilling
334
- // over the graph.
335
- overflow: 'hidden',
904
+ left: snap(origin.x) + extent.x,
905
+ top: snap(origin.y) + extent.y,
906
+ width: extent.width,
907
+ height: extent.height,
908
+ // The box spans every body, the gaps between them and the
909
+ // cards' headers with them — taking the pointer itself, it ate
910
+ // every press in that span: no pan, no pane click, no selecting
911
+ // a node by its header. Its bodies take the pointer; it does not.
912
+ pointerEvents: 'box-none',
913
+ // held through a zoom gesture: see `_holdBodies` in ./node.ts
914
+ display: held ? 'none' : 'flex',
336
915
  },
916
+ onWheel: forwardWheel,
917
+ ...forwardPress,
337
918
  },
338
- React.createElement(
339
- 'box',
340
- {
341
- scale: body.zoom,
342
- // Sized in the unit it establishes, which is what makes it
343
- // fill the outer box at every zoom.
344
- style: { width, height },
919
+ overlays,
920
+ )
921
+ : null;
922
+
923
+ // The box a 2D pan carries along with the pane's pixels (`_blitPan`).
924
+ useLayoutEffect(() => {
925
+ pane.current?.setBodiesLayer(
926
+ drawsGlRef.current ? null : bodiesLayer.current,
927
+ );
928
+ });
929
+
930
+ // The cards whose canvases are on screen, told to the pane once they are,
931
+ // so the 2D pane stops drawing them itself — and the empty set while
932
+ // bodies are held out of a zoom, when their cards are the pane's again.
933
+ // The GL world draws every card whatever this says (`_cardInLayer`).
934
+ useLayoutEffect(() => {
935
+ const shown = new Set<string>();
936
+ if (mounts && !held) for (const body of bodies) shown.add(body.id);
937
+ pane.current?.setShownBodies(shown);
938
+ }, [mounts, held, bodies]);
939
+
940
+ // The minimap and the controls over the bodies: the bodies' layer is over
941
+ // the graph, so panels the graph drew went under any card that reached
942
+ // them. Painted by the pane (`paintPanels`) on canvases after the layer,
943
+ // which take no pointer — a press on a button is the pane's, as before.
944
+ const panelLayer =
945
+ mounts && panels.length > 0
946
+ ? panels.map((rect, i) =>
947
+ React.createElement('canvas', {
948
+ key: `panel-${i}`,
949
+ ref: (
950
+ node: {
951
+ invalidate(
952
+ layout: boolean,
953
+ damage: unknown,
954
+ reason: string,
955
+ ): void;
956
+ } | null,
957
+ ) => {
958
+ panelCanvases.current[i] = node;
959
+ },
960
+ style: {
961
+ position: 'absolute',
962
+ left: rect.x,
963
+ top: rect.y,
964
+ width: rect.width,
965
+ height: rect.height,
966
+ pointerEvents: 'none',
345
967
  },
346
- React.createElement(FlowNodeBody, {
347
- type: type as FlowNodeType<unknown>,
348
- node: node as FlowNode<unknown>,
349
- selected: body.selected,
350
- zoom: body.zoom,
351
- // Graph units, like everything else the body sees.
352
- x: body.x / body.zoom,
353
- y: body.y / body.zoom,
354
- width,
355
- height,
356
- }),
357
- ),
358
- );
968
+ onDraw: (ctx: unknown, info: { node: { abs: FlowRect } }) =>
969
+ pane.current?.paintPanels(ctx, info.node.abs),
970
+ }),
971
+ )
972
+ : null;
973
+ useLayoutEffect(() => {
974
+ const live = panelCanvases.current
975
+ .slice(0, panelLayer?.length ?? 0)
976
+ .filter((c) => c != null);
977
+ pane.current?.setPanelCanvases(live);
978
+ }, [panelLayer?.length, panels]);
979
+
980
+ // On X11 a <glarea>'s children are not composited over its frame: each
981
+ // child gets an opaque child window, as big as the region it reaches,
982
+ // filled with the surface's clear colour (react-x11's src/gloverlay.js).
983
+ // The bodies' one box reaches every card and the gaps between them, so it
984
+ // became one window over the whole view: cards and bodies, and not one
985
+ // edge. There each card is the surface's child in its own right, placed
986
+ // at the origin itself, and a pane covers a card and nothing more. Where
987
+ // panes composite (Cocoa) the one box stays — a pan moves it alone.
988
+ const app = useApp() as { createOverlayPane?: unknown } | null;
989
+ const composited = typeof app?.createOverlayPane === 'function';
990
+ // …and on XQuartz not shown at all: the macOS window server composites
991
+ // every GL surface there above everything the X server draws, panes
992
+ // included. Core says so (`useSupports('glOverlay')` false, from the first
993
+ // render — sidorares/react-x11#653), and there a graph with nodes whose
994
+ // types mount bodies draws with the 2D renderer: for as long as it has
995
+ // one, not per zoom, so the surface does not come and go as bodies mount.
996
+ const overlaySupported = useSupports('glOverlay');
997
+ const cardsDirect: ReactElement[] = [];
998
+ if (cards && !composited) {
999
+ const offsetX = snap(origin.x) + extent.x;
1000
+ const offsetY = snap(origin.y) + extent.y;
1001
+ for (const props of cards) {
1002
+ cardsDirect.push(
1003
+ React.createElement(FlowBodyCard, {
1004
+ key: props.body.id,
1005
+ ...props,
1006
+ offsetX,
1007
+ offsetY,
1008
+ hidden: held,
1009
+ handlers: directHandlers,
1010
+ }),
1011
+ );
1012
+ }
1013
+ }
1014
+
1015
+ // --- the GL surface -----------------------------------------------------
1016
+ //
1017
+ // Loaded when it is asked for and not before — see `./gl/index.ts` for why
1018
+ // the import is dynamic. Until it arrives, and for good once it has failed,
1019
+ // the pane draws itself: `renderer` reaches the element only while a
1020
+ // surface is there to draw, so there is never a frame with neither.
1021
+ const wantGl = renderer === 'gl';
1022
+ const [gl, setGl] = useState<GlModule | null>(glModule);
1023
+ const [glFailed, setGlFailed] = useState(false);
1024
+ const latestError = useRef(onError);
1025
+ latestError.current = onError;
1026
+ const failGl = useMemo(
1027
+ () =>
1028
+ (error: Error): void => {
1029
+ setGlFailed(true);
1030
+ latestError.current?.(error);
1031
+ },
1032
+ [],
1033
+ );
1034
+ useEffect(() => {
1035
+ if (!wantGl || gl || glFailed) return;
1036
+ let live = true;
1037
+ loadGl().then(
1038
+ (module) => {
1039
+ if (live) setGl(module);
1040
+ },
1041
+ (error: unknown) => {
1042
+ if (live)
1043
+ failGl(error instanceof Error ? error : new Error(String(error)));
1044
+ },
1045
+ );
1046
+ return () => {
1047
+ live = false;
1048
+ };
1049
+ }, [wantGl, gl, glFailed]);
1050
+ const drawsGl =
1051
+ wantGl && gl != null && !glFailed && !(mounts && !overlaySupported);
1052
+ drawsGlRef.current = drawsGl;
1053
+ const surface = drawsGl
1054
+ ? React.createElement(gl.FlowGlSurface, {
1055
+ key: 'gl',
1056
+ pane,
1057
+ clearColor: resolvePalette(
1058
+ theme as unknown as Record<string, unknown> | null,
1059
+ rest.palette,
1060
+ ).background,
1061
+ onFrame: onFrame,
1062
+ onError: failGl,
1063
+ // A `<glarea>`'s children are drawn over its surface, so that is
1064
+ // where the bodies go under GL — beside the pane, they would be
1065
+ // under it.
1066
+ children: composited
1067
+ ? [layer, panelLayer]
1068
+ : [cardsDirect.length > 0 ? cardsDirect : null, panelLayer],
359
1069
  })
360
1070
  : null;
361
1071
 
@@ -380,11 +1090,26 @@ export function Flow<N = FlowNodeData, E = unknown>(
380
1090
  // `<flowgraph>` element zooms and hovers on its own.
381
1091
  onWheel,
382
1092
  onNodeBodies: mounts ? handleBodies : undefined,
1093
+ onPanels: mounts ? setPanels : undefined,
1094
+ renderer: drawsGl ? 'gl' : undefined,
1095
+ // the element reports the 2D renderer's frames, the surface the GL
1096
+ // one's — one callback either way
1097
+ onFrame,
383
1098
  style: styles.fill,
384
1099
  role: 'group',
385
1100
  'aria-label': rest['aria-label'] ?? 'Flow graph',
386
1101
  }),
387
- overlays,
1102
+ // After the pane, so the surface covers exactly its box — and with the
1103
+ // bodies inside it under GL, or beside the pane without.
1104
+ surface,
1105
+ drawsGl || !layer
1106
+ ? null
1107
+ : React.createElement(
1108
+ 'box',
1109
+ { key: 'bodies-clip', style: styles.bodiesClip },
1110
+ layer,
1111
+ ),
1112
+ drawsGl ? null : panelLayer,
388
1113
  );
389
1114
  }
390
1115
 
@@ -453,6 +1178,8 @@ export type {
453
1178
  EdgeType,
454
1179
  FitViewOptions,
455
1180
  FlowEdge,
1181
+ FlowBodyBudget,
1182
+ FlowFrameStats,
456
1183
  FlowInstance,
457
1184
  FlowNode,
458
1185
  FlowNodeData,