@react-x11/components 0.8.0 → 0.10.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 (721) hide show
  1. package/README.md +105 -38
  2. package/dist/charts/node.d.ts +12 -2
  3. package/dist/charts/node.d.ts.map +1 -1
  4. package/dist/charts/node.js +50 -16
  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 +3 -1
  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/languages/glsl.d.ts.map +1 -1
  24. package/dist/code-language/languages/glsl.js +10 -2
  25. package/dist/code-language/languages/glsl.js.map +1 -1
  26. package/dist/code-language/languages/javascript.d.ts.map +1 -1
  27. package/dist/code-language/languages/javascript.js +18 -3
  28. package/dist/code-language/languages/javascript.js.map +1 -1
  29. package/dist/code-language/languages/json.d.ts.map +1 -1
  30. package/dist/code-language/languages/json.js +10 -2
  31. package/dist/code-language/languages/json.js.map +1 -1
  32. package/dist/code-language/languages/shell.d.ts.map +1 -1
  33. package/dist/code-language/languages/shell.js +10 -2
  34. package/dist/code-language/languages/shell.js.map +1 -1
  35. package/dist/code-language/languages/sql.d.ts.map +1 -1
  36. package/dist/code-language/languages/sql.js +20 -2
  37. package/dist/code-language/languages/sql.js.map +1 -1
  38. package/dist/code-language/stream.d.ts +22 -0
  39. package/dist/code-language/stream.d.ts.map +1 -1
  40. package/dist/code-language/stream.js +259 -9
  41. package/dist/code-language/stream.js.map +1 -1
  42. package/dist/code-language/types.d.ts +5 -4
  43. package/dist/code-language/types.d.ts.map +1 -1
  44. package/dist/codeblock/index.d.ts +13 -2
  45. package/dist/codeblock/index.d.ts.map +1 -1
  46. package/dist/codeblock/index.js +20 -3
  47. package/dist/codeblock/index.js.map +1 -1
  48. package/dist/embed/client.d.ts +48 -3
  49. package/dist/embed/client.d.ts.map +1 -1
  50. package/dist/embed/client.js +79 -5
  51. package/dist/embed/client.js.map +1 -1
  52. package/dist/embed/index.d.ts +1 -1
  53. package/dist/embed/index.d.ts.map +1 -1
  54. package/dist/embed/index.js +1 -1
  55. package/dist/embed/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 +240 -6
  115. package/dist/html/css/cascade.d.ts.map +1 -1
  116. package/dist/html/css/cascade.js +1327 -66
  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 +498 -15
  139. package/dist/html/css/style.d.ts.map +1 -1
  140. package/dist/html/css/style.js +3698 -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 +57 -11
  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 +2937 -197
  172. package/dist/html/layout/block.js.map +1 -1
  173. package/dist/html/layout/boxes.d.ts +340 -17
  174. package/dist/html/layout/boxes.d.ts.map +1 -1
  175. package/dist/html/layout/boxes.js +1714 -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 +151 -1
  205. package/dist/html/layout/inline.d.ts.map +1 -1
  206. package/dist/html/layout/inline.js +3375 -174
  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 +172 -5
  221. package/dist/html/node.d.ts.map +1 -1
  222. package/dist/html/node.js +1336 -103
  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 +3503 -141
  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 +435 -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 +2 -2
  245. package/dist/index.d.ts.map +1 -1
  246. package/dist/index.js +1 -1
  247. package/dist/index.js.map +1 -1
  248. package/dist/internal/collate.d.ts +3 -0
  249. package/dist/internal/collate.d.ts.map +1 -0
  250. package/dist/internal/collate.js +11 -0
  251. package/dist/internal/collate.js.map +1 -0
  252. package/dist/{markdown → internal/markdown}/ast.d.ts +23 -0
  253. package/dist/internal/markdown/ast.d.ts.map +1 -0
  254. package/dist/internal/markdown/ast.js +22 -0
  255. package/dist/internal/markdown/ast.js.map +1 -0
  256. package/dist/internal/markdown/parse.d.ts.map +1 -0
  257. package/dist/{markdown → internal/markdown}/parse.js +221 -30
  258. package/dist/internal/markdown/parse.js.map +1 -0
  259. package/dist/internal/markdown/stringify.d.ts +4 -0
  260. package/dist/internal/markdown/stringify.d.ts.map +1 -0
  261. package/dist/internal/markdown/stringify.js +684 -0
  262. package/dist/internal/markdown/stringify.js.map +1 -0
  263. package/dist/internal/markdown/tags.d.ts.map +1 -0
  264. package/dist/internal/markdown/tags.js.map +1 -0
  265. package/dist/internal/prewarm.d.ts +18 -0
  266. package/dist/internal/prewarm.d.ts.map +1 -0
  267. package/dist/internal/prewarm.js +42 -0
  268. package/dist/internal/prewarm.js.map +1 -0
  269. package/dist/internal/scroll.d.ts +3 -2
  270. package/dist/internal/scroll.d.ts.map +1 -1
  271. package/dist/internal/scroll.js +1 -0
  272. package/dist/internal/scroll.js.map +1 -1
  273. package/dist/internal/sdf.d.ts +20 -0
  274. package/dist/internal/sdf.d.ts.map +1 -0
  275. package/dist/internal/sdf.js +216 -0
  276. package/dist/internal/sdf.js.map +1 -0
  277. package/dist/internal/splice.d.ts +12 -0
  278. package/dist/internal/splice.d.ts.map +1 -0
  279. package/dist/internal/splice.js +23 -0
  280. package/dist/internal/splice.js.map +1 -0
  281. package/dist/internal/window.d.ts +12 -0
  282. package/dist/internal/window.d.ts.map +1 -1
  283. package/dist/internal/window.js +23 -6
  284. package/dist/internal/window.js.map +1 -1
  285. package/dist/maps/anchors.d.ts +103 -0
  286. package/dist/maps/anchors.d.ts.map +1 -0
  287. package/dist/maps/anchors.js +790 -0
  288. package/dist/maps/anchors.js.map +1 -0
  289. package/dist/maps/controller.d.ts +340 -0
  290. package/dist/maps/controller.d.ts.map +1 -0
  291. package/dist/maps/controller.js +822 -0
  292. package/dist/maps/controller.js.map +1 -0
  293. package/dist/maps/gl/buckets.d.ts +99 -0
  294. package/dist/maps/gl/buckets.d.ts.map +1 -0
  295. package/dist/maps/gl/buckets.js +365 -0
  296. package/dist/maps/gl/buckets.js.map +1 -0
  297. package/dist/maps/gl/build-worker.d.ts +15 -0
  298. package/dist/maps/gl/build-worker.d.ts.map +1 -0
  299. package/dist/maps/gl/build-worker.js +44 -0
  300. package/dist/maps/gl/build-worker.js.map +1 -0
  301. package/dist/maps/gl/color.d.ts +8 -0
  302. package/dist/maps/gl/color.d.ts.map +1 -0
  303. package/dist/maps/gl/color.js +57 -0
  304. package/dist/maps/gl/color.js.map +1 -0
  305. package/dist/maps/gl/cover.d.ts +70 -0
  306. package/dist/maps/gl/cover.d.ts.map +1 -0
  307. package/dist/maps/gl/cover.js +137 -0
  308. package/dist/maps/gl/cover.js.map +1 -0
  309. package/dist/maps/gl/index.d.ts +3 -0
  310. package/dist/maps/gl/index.d.ts.map +1 -0
  311. package/dist/maps/gl/index.js +20 -0
  312. package/dist/maps/gl/index.js.map +1 -0
  313. package/dist/maps/gl/markers.d.ts +28 -0
  314. package/dist/maps/gl/markers.d.ts.map +1 -0
  315. package/dist/maps/gl/markers.js +60 -0
  316. package/dist/maps/gl/markers.js.map +1 -0
  317. package/dist/maps/gl/overlays.d.ts +68 -0
  318. package/dist/maps/gl/overlays.d.ts.map +1 -0
  319. package/dist/maps/gl/overlays.js +300 -0
  320. package/dist/maps/gl/overlays.js.map +1 -0
  321. package/dist/maps/gl/pane.d.ts +12 -0
  322. package/dist/maps/gl/pane.d.ts.map +1 -0
  323. package/dist/maps/gl/pane.js +37 -0
  324. package/dist/maps/gl/pane.js.map +1 -0
  325. package/dist/maps/gl/placement.d.ts +124 -0
  326. package/dist/maps/gl/placement.d.ts.map +1 -0
  327. package/dist/maps/gl/placement.js +642 -0
  328. package/dist/maps/gl/placement.js.map +1 -0
  329. package/dist/maps/gl/renderer.d.ts +384 -0
  330. package/dist/maps/gl/renderer.d.ts.map +1 -0
  331. package/dist/maps/gl/renderer.js +1184 -0
  332. package/dist/maps/gl/renderer.js.map +1 -0
  333. package/dist/maps/gl/shaders.d.ts +121 -0
  334. package/dist/maps/gl/shaders.d.ts.map +1 -0
  335. package/dist/maps/gl/shaders.js +453 -0
  336. package/dist/maps/gl/shaders.js.map +1 -0
  337. package/dist/maps/gl/store.d.ts +125 -0
  338. package/dist/maps/gl/store.d.ts.map +1 -0
  339. package/dist/maps/gl/store.js +512 -0
  340. package/dist/maps/gl/store.js.map +1 -0
  341. package/dist/maps/gl/text.d.ts +283 -0
  342. package/dist/maps/gl/text.d.ts.map +1 -0
  343. package/dist/maps/gl/text.js +698 -0
  344. package/dist/maps/gl/text.js.map +1 -0
  345. package/dist/maps/gl/view.d.ts +345 -0
  346. package/dist/maps/gl/view.d.ts.map +1 -0
  347. package/dist/maps/gl/view.js +1209 -0
  348. package/dist/maps/gl/view.js.map +1 -0
  349. package/dist/maps/icons.d.ts +32 -0
  350. package/dist/maps/icons.d.ts.map +1 -0
  351. package/dist/maps/icons.js +151 -0
  352. package/dist/maps/icons.js.map +1 -0
  353. package/dist/maps/index.d.ts +3 -1
  354. package/dist/maps/index.d.ts.map +1 -1
  355. package/dist/maps/index.js +218 -52
  356. package/dist/maps/index.js.map +1 -1
  357. package/dist/maps/labels.d.ts +91 -18
  358. package/dist/maps/labels.d.ts.map +1 -1
  359. package/dist/maps/labels.js +376 -140
  360. package/dist/maps/labels.js.map +1 -1
  361. package/dist/maps/node.d.ts +179 -56
  362. package/dist/maps/node.d.ts.map +1 -1
  363. package/dist/maps/node.js +663 -537
  364. package/dist/maps/node.js.map +1 -1
  365. package/dist/maps/overlay.d.ts +72 -9
  366. package/dist/maps/overlay.d.ts.map +1 -1
  367. package/dist/maps/overlay.js +108 -28
  368. package/dist/maps/overlay.js.map +1 -1
  369. package/dist/maps/paint.d.ts +3 -0
  370. package/dist/maps/paint.d.ts.map +1 -1
  371. package/dist/maps/paint.js.map +1 -1
  372. package/dist/maps/proj.d.ts +62 -0
  373. package/dist/maps/proj.d.ts.map +1 -1
  374. package/dist/maps/proj.js +101 -0
  375. package/dist/maps/proj.js.map +1 -1
  376. package/dist/maps/renderer.d.ts +69 -0
  377. package/dist/maps/renderer.d.ts.map +1 -0
  378. package/dist/maps/renderer.js +66 -0
  379. package/dist/maps/renderer.js.map +1 -0
  380. package/dist/maps/sources.d.ts +30 -6
  381. package/dist/maps/sources.d.ts.map +1 -1
  382. package/dist/maps/sources.js +22 -3
  383. package/dist/maps/sources.js.map +1 -1
  384. package/dist/maps/style.d.ts +35 -0
  385. package/dist/maps/style.d.ts.map +1 -1
  386. package/dist/maps/style.js.map +1 -1
  387. package/dist/maps/styles.d.ts +12 -0
  388. package/dist/maps/styles.d.ts.map +1 -1
  389. package/dist/maps/styles.js +120 -6
  390. package/dist/maps/styles.js.map +1 -1
  391. package/dist/maps/theme.d.ts +19 -0
  392. package/dist/maps/theme.d.ts.map +1 -0
  393. package/dist/maps/theme.js +44 -0
  394. package/dist/maps/theme.js.map +1 -0
  395. package/dist/maps/tiles.d.ts +147 -27
  396. package/dist/maps/tiles.d.ts.map +1 -1
  397. package/dist/maps/tiles.js +221 -54
  398. package/dist/maps/tiles.js.map +1 -1
  399. package/dist/maps/types.d.ts +213 -21
  400. package/dist/maps/types.d.ts.map +1 -1
  401. package/dist/markdown/expressions.d.ts +1 -1
  402. package/dist/markdown/expressions.d.ts.map +1 -1
  403. package/dist/markdown/expressions.js.map +1 -1
  404. package/dist/markdown/index.d.ts +7 -6
  405. package/dist/markdown/index.d.ts.map +1 -1
  406. package/dist/markdown/index.js +121 -50
  407. package/dist/markdown/index.js.map +1 -1
  408. package/dist/markdown/spans.d.ts +1 -1
  409. package/dist/markdown/spans.d.ts.map +1 -1
  410. package/dist/markdown/spans.js +6 -0
  411. package/dist/markdown/spans.js.map +1 -1
  412. package/dist/media-player/index.d.ts +11 -3
  413. package/dist/media-player/index.d.ts.map +1 -1
  414. package/dist/media-player/index.js +13 -2
  415. package/dist/media-player/index.js.map +1 -1
  416. package/dist/qml/containers.d.ts.map +1 -1
  417. package/dist/qml/containers.js +6 -1
  418. package/dist/qml/containers.js.map +1 -1
  419. package/dist/qml/interaction.d.ts.map +1 -1
  420. package/dist/qml/interaction.js +38 -5
  421. package/dist/qml/interaction.js.map +1 -1
  422. package/dist/qml/layouts.d.ts.map +1 -1
  423. package/dist/qml/layouts.js +21 -6
  424. package/dist/qml/layouts.js.map +1 -1
  425. package/dist/qml/qtquick.d.ts.map +1 -1
  426. package/dist/qml/qtquick.js +9 -2
  427. package/dist/qml/qtquick.js.map +1 -1
  428. package/dist/qml/react.d.ts.map +1 -1
  429. package/dist/qml/react.js +9 -7
  430. package/dist/qml/react.js.map +1 -1
  431. package/dist/reorder/clock.d.ts +7 -0
  432. package/dist/reorder/clock.d.ts.map +1 -0
  433. package/dist/reorder/clock.js +26 -0
  434. package/dist/reorder/clock.js.map +1 -0
  435. package/dist/reorder/index.d.ts +25 -3
  436. package/dist/reorder/index.d.ts.map +1 -1
  437. package/dist/reorder/index.js +64 -19
  438. package/dist/reorder/index.js.map +1 -1
  439. package/dist/rich-text-editor/clipboard.d.ts +23 -0
  440. package/dist/rich-text-editor/clipboard.d.ts.map +1 -0
  441. package/dist/rich-text-editor/clipboard.js +307 -0
  442. package/dist/rich-text-editor/clipboard.js.map +1 -0
  443. package/dist/rich-text-editor/collab.d.ts +32 -0
  444. package/dist/rich-text-editor/collab.d.ts.map +1 -0
  445. package/dist/rich-text-editor/collab.js +55 -0
  446. package/dist/rich-text-editor/collab.js.map +1 -0
  447. package/dist/rich-text-editor/commands.d.ts +50 -0
  448. package/dist/rich-text-editor/commands.d.ts.map +1 -0
  449. package/dist/rich-text-editor/commands.js +376 -0
  450. package/dist/rich-text-editor/commands.js.map +1 -0
  451. package/dist/rich-text-editor/drag.d.ts +68 -0
  452. package/dist/rich-text-editor/drag.d.ts.map +1 -0
  453. package/dist/rich-text-editor/drag.js +148 -0
  454. package/dist/rich-text-editor/drag.js.map +1 -0
  455. package/dist/rich-text-editor/html.d.ts +145 -0
  456. package/dist/rich-text-editor/html.d.ts.map +1 -0
  457. package/dist/rich-text-editor/html.js +516 -0
  458. package/dist/rich-text-editor/html.js.map +1 -0
  459. package/dist/rich-text-editor/index.d.ts +252 -0
  460. package/dist/rich-text-editor/index.d.ts.map +1 -0
  461. package/dist/rich-text-editor/index.js +856 -0
  462. package/dist/rich-text-editor/index.js.map +1 -0
  463. package/dist/rich-text-editor/inline.d.ts +94 -0
  464. package/dist/rich-text-editor/inline.d.ts.map +1 -0
  465. package/dist/rich-text-editor/inline.js +252 -0
  466. package/dist/rich-text-editor/inline.js.map +1 -0
  467. package/dist/rich-text-editor/keymap.d.ts +71 -0
  468. package/dist/rich-text-editor/keymap.d.ts.map +1 -0
  469. package/dist/rich-text-editor/keymap.js +394 -0
  470. package/dist/rich-text-editor/keymap.js.map +1 -0
  471. package/dist/rich-text-editor/keys.d.ts +40 -0
  472. package/dist/rich-text-editor/keys.d.ts.map +1 -0
  473. package/dist/rich-text-editor/keys.js +202 -0
  474. package/dist/rich-text-editor/keys.js.map +1 -0
  475. package/dist/rich-text-editor/look.d.ts +71 -0
  476. package/dist/rich-text-editor/look.d.ts.map +1 -0
  477. package/dist/rich-text-editor/look.js +342 -0
  478. package/dist/rich-text-editor/look.js.map +1 -0
  479. package/dist/rich-text-editor/markdown.d.ts +18 -0
  480. package/dist/rich-text-editor/markdown.d.ts.map +1 -0
  481. package/dist/rich-text-editor/markdown.js +496 -0
  482. package/dist/rich-text-editor/markdown.js.map +1 -0
  483. package/dist/rich-text-editor/marks.d.ts +9 -0
  484. package/dist/rich-text-editor/marks.d.ts.map +1 -0
  485. package/dist/rich-text-editor/marks.js +83 -0
  486. package/dist/rich-text-editor/marks.js.map +1 -0
  487. package/dist/rich-text-editor/nodes.d.ts +135 -0
  488. package/dist/rich-text-editor/nodes.d.ts.map +1 -0
  489. package/dist/rich-text-editor/nodes.js +397 -0
  490. package/dist/rich-text-editor/nodes.js.map +1 -0
  491. package/dist/rich-text-editor/render.d.ts +68 -0
  492. package/dist/rich-text-editor/render.d.ts.map +1 -0
  493. package/dist/rich-text-editor/render.js +518 -0
  494. package/dist/rich-text-editor/render.js.map +1 -0
  495. package/dist/rich-text-editor/schema.d.ts +16 -0
  496. package/dist/rich-text-editor/schema.d.ts.map +1 -0
  497. package/dist/rich-text-editor/schema.js +386 -0
  498. package/dist/rich-text-editor/schema.js.map +1 -0
  499. package/dist/rich-text-editor/store.d.ts +11 -0
  500. package/dist/rich-text-editor/store.d.ts.map +1 -0
  501. package/dist/rich-text-editor/store.js +43 -0
  502. package/dist/rich-text-editor/store.js.map +1 -0
  503. package/dist/rich-text-editor/suggest.d.ts +127 -0
  504. package/dist/rich-text-editor/suggest.d.ts.map +1 -0
  505. package/dist/rich-text-editor/suggest.js +0 -0
  506. package/dist/rich-text-editor/suggest.js.map +1 -0
  507. package/dist/rich-text-editor/tables.d.ts +41 -0
  508. package/dist/rich-text-editor/tables.d.ts.map +1 -0
  509. package/dist/rich-text-editor/tables.js +204 -0
  510. package/dist/rich-text-editor/tables.js.map +1 -0
  511. package/dist/rich-text-editor/toolbar.d.ts +58 -0
  512. package/dist/rich-text-editor/toolbar.d.ts.map +1 -0
  513. package/dist/rich-text-editor/toolbar.js +548 -0
  514. package/dist/rich-text-editor/toolbar.js.map +1 -0
  515. package/dist/rich-text-editor/view.d.ts +368 -0
  516. package/dist/rich-text-editor/view.d.ts.map +1 -0
  517. package/dist/rich-text-editor/view.js +1968 -0
  518. package/dist/rich-text-editor/view.js.map +1 -0
  519. package/dist/rich-text-editor/virtual.d.ts +54 -0
  520. package/dist/rich-text-editor/virtual.d.ts.map +1 -0
  521. package/dist/rich-text-editor/virtual.js +279 -0
  522. package/dist/rich-text-editor/virtual.js.map +1 -0
  523. package/dist/richtext/node.d.ts +71 -4
  524. package/dist/richtext/node.d.ts.map +1 -1
  525. package/dist/richtext/node.js +233 -28
  526. package/dist/richtext/node.js.map +1 -1
  527. package/dist/richtext/runs.d.ts +13 -8
  528. package/dist/richtext/runs.d.ts.map +1 -1
  529. package/dist/richtext/runs.js +18 -8
  530. package/dist/richtext/runs.js.map +1 -1
  531. package/dist/table/index.d.ts.map +1 -1
  532. package/dist/table/index.js +23 -0
  533. package/dist/table/index.js.map +1 -1
  534. package/dist/table/rows.d.ts +3 -2
  535. package/dist/table/rows.d.ts.map +1 -1
  536. package/dist/table/rows.js +20 -6
  537. package/dist/table/rows.js.map +1 -1
  538. package/dist/terminal/backends.d.ts +4 -1
  539. package/dist/terminal/backends.d.ts.map +1 -1
  540. package/dist/terminal/backends.js.map +1 -1
  541. package/dist/terminal/index.d.ts +18 -7
  542. package/dist/terminal/index.d.ts.map +1 -1
  543. package/dist/terminal/index.js +32 -11
  544. package/dist/terminal/index.js.map +1 -1
  545. package/dist/terminal/vt/pty.d.ts +13 -2
  546. package/dist/terminal/vt/pty.d.ts.map +1 -1
  547. package/dist/terminal/vt/pty.js +18 -3
  548. package/dist/terminal/vt/pty.js.map +1 -1
  549. package/dist/terminal-output/index.d.ts.map +1 -1
  550. package/dist/terminal-output/index.js +6 -1
  551. package/dist/terminal-output/index.js.map +1 -1
  552. package/dist/three/canvas.d.ts.map +1 -1
  553. package/dist/three/canvas.js +85 -7
  554. package/dist/three/canvas.js.map +1 -1
  555. package/dist/three/events.d.ts +22 -4
  556. package/dist/three/events.d.ts.map +1 -1
  557. package/dist/three/events.js +34 -4
  558. package/dist/three/events.js.map +1 -1
  559. package/dist/tree/index.d.ts.map +1 -1
  560. package/dist/tree/index.js +17 -0
  561. package/dist/tree/index.js.map +1 -1
  562. package/package.json +59 -31
  563. package/src/charts/node.ts +68 -28
  564. package/src/charts/render.ts +120 -30
  565. package/src/code/index.ts +3 -1
  566. package/src/code-editor/completion.ts +3 -2
  567. package/src/code-editor/index.ts +19 -0
  568. package/src/code-editor/node.ts +1574 -184
  569. package/src/code-language/languages/glsl.ts +12 -2
  570. package/src/code-language/languages/javascript.ts +21 -4
  571. package/src/code-language/languages/json.ts +12 -2
  572. package/src/code-language/languages/shell.ts +12 -2
  573. package/src/code-language/languages/sql.ts +22 -2
  574. package/src/code-language/stream.ts +257 -17
  575. package/src/code-language/types.ts +5 -4
  576. package/src/codeblock/index.ts +20 -3
  577. package/src/embed/client.ts +102 -8
  578. package/src/embed/index.ts +2 -0
  579. package/src/flow/draw.ts +167 -41
  580. package/src/flow/gl/color.ts +85 -0
  581. package/src/flow/gl/index.ts +342 -0
  582. package/src/flow/gl/pack.ts +585 -0
  583. package/src/flow/gl/renderer.ts +679 -0
  584. package/src/flow/gl/shaders.ts +371 -0
  585. package/src/flow/gl/text.ts +1295 -0
  586. package/src/flow/index.ts +794 -67
  587. package/src/flow/node.ts +2496 -1256
  588. package/src/flow/paint.ts +511 -0
  589. package/src/flow/paths.ts +80 -15
  590. package/src/flow/scene.ts +2199 -0
  591. package/src/flow/types.ts +113 -1
  592. package/src/html/controls.ts +73 -2
  593. package/src/html/css/calc.ts +385 -0
  594. package/src/html/css/cascade.ts +1403 -62
  595. package/src/html/css/color.ts +755 -0
  596. package/src/html/css/content.ts +417 -0
  597. package/src/html/css/counter-styles.ts +973 -0
  598. package/src/html/css/decode.ts +130 -0
  599. package/src/html/css/parse.ts +1514 -160
  600. package/src/html/css/style.ts +4102 -222
  601. package/src/html/css/ua.ts +58 -12
  602. package/src/html/css/values.ts +150 -50
  603. package/src/html/css/vars.ts +348 -0
  604. package/src/html/dom.ts +284 -17
  605. package/src/html/fonts.ts +641 -0
  606. package/src/html/gif.ts +233 -0
  607. package/src/html/index.ts +180 -20
  608. package/src/html/layout/block.ts +3456 -230
  609. package/src/html/layout/boxes.ts +2100 -190
  610. package/src/html/layout/cache.ts +289 -0
  611. package/src/html/layout/collapse.ts +317 -0
  612. package/src/html/layout/css-grid.ts +1063 -0
  613. package/src/html/layout/flex.ts +1115 -106
  614. package/src/html/layout/floats.ts +128 -14
  615. package/src/html/layout/grid-lines.ts +141 -0
  616. package/src/html/layout/grid.ts +181 -0
  617. package/src/html/layout/inline.ts +3943 -192
  618. package/src/html/layout/shaping.ts +140 -0
  619. package/src/html/layout/table.ts +696 -186
  620. package/src/html/layout/tracks.ts +437 -0
  621. package/src/html/node.ts +1486 -121
  622. package/src/html/paint.ts +4293 -169
  623. package/src/html/resources.ts +161 -50
  624. package/src/html/surfaces.ts +81 -0
  625. package/src/html/svg.ts +493 -0
  626. package/src/html/url.ts +86 -0
  627. package/src/index.ts +4 -0
  628. package/src/internal/collate.ts +12 -0
  629. package/src/{markdown → internal/markdown}/ast.ts +35 -7
  630. package/src/{markdown → internal/markdown}/parse.ts +288 -35
  631. package/src/internal/markdown/stringify.ts +772 -0
  632. package/src/internal/prewarm.ts +45 -0
  633. package/src/internal/scroll.ts +5 -3
  634. package/src/internal/sdf.ts +227 -0
  635. package/src/internal/splice.ts +25 -0
  636. package/src/internal/window.ts +36 -5
  637. package/src/maps/anchors.ts +913 -0
  638. package/src/maps/controller.ts +1084 -0
  639. package/src/maps/gl/buckets.ts +461 -0
  640. package/src/maps/gl/build-worker.ts +66 -0
  641. package/src/maps/gl/color.ts +62 -0
  642. package/src/maps/gl/cover.ts +205 -0
  643. package/src/maps/gl/index.ts +23 -0
  644. package/src/maps/gl/markers.ts +83 -0
  645. package/src/maps/gl/overlays.ts +379 -0
  646. package/src/maps/gl/pane.ts +42 -0
  647. package/src/maps/gl/placement.ts +968 -0
  648. package/src/maps/gl/renderer.ts +1660 -0
  649. package/src/maps/gl/shaders.ts +489 -0
  650. package/src/maps/gl/store.ts +615 -0
  651. package/src/maps/gl/text.ts +868 -0
  652. package/src/maps/gl/view.ts +1489 -0
  653. package/src/maps/icons.ts +183 -0
  654. package/src/maps/index.ts +274 -81
  655. package/src/maps/labels.ts +457 -144
  656. package/src/maps/node.ts +832 -665
  657. package/src/maps/overlay.ts +148 -43
  658. package/src/maps/paint.ts +3 -0
  659. package/src/maps/proj.ts +118 -0
  660. package/src/maps/renderer.ts +155 -0
  661. package/src/maps/sources.ts +43 -6
  662. package/src/maps/style.ts +32 -0
  663. package/src/maps/styles.ts +215 -4
  664. package/src/maps/theme.ts +51 -0
  665. package/src/maps/tiles.ts +253 -62
  666. package/src/maps/types.ts +213 -21
  667. package/src/markdown/expressions.ts +5 -1
  668. package/src/markdown/index.ts +163 -78
  669. package/src/markdown/spans.ts +6 -1
  670. package/src/media-player/index.ts +18 -4
  671. package/src/qml/containers.tsx +6 -1
  672. package/src/qml/interaction.tsx +42 -5
  673. package/src/qml/layouts.tsx +21 -6
  674. package/src/qml/qtquick.tsx +9 -2
  675. package/src/qml/react.tsx +7 -5
  676. package/src/reorder/clock.ts +27 -0
  677. package/src/reorder/index.ts +100 -27
  678. package/src/rich-text-editor/clipboard.ts +439 -0
  679. package/src/rich-text-editor/collab.ts +91 -0
  680. package/src/rich-text-editor/commands.ts +448 -0
  681. package/src/rich-text-editor/dom-globals.d.ts +23 -0
  682. package/src/rich-text-editor/drag.ts +211 -0
  683. package/src/rich-text-editor/html.ts +619 -0
  684. package/src/rich-text-editor/index.ts +1364 -0
  685. package/src/rich-text-editor/inline.ts +345 -0
  686. package/src/rich-text-editor/keymap.ts +510 -0
  687. package/src/rich-text-editor/keys.ts +228 -0
  688. package/src/rich-text-editor/look.ts +427 -0
  689. package/src/rich-text-editor/markdown.ts +653 -0
  690. package/src/rich-text-editor/marks.ts +101 -0
  691. package/src/rich-text-editor/nodes.ts +497 -0
  692. package/src/rich-text-editor/render.ts +837 -0
  693. package/src/rich-text-editor/schema.ts +431 -0
  694. package/src/rich-text-editor/store.ts +39 -0
  695. package/src/rich-text-editor/suggest.ts +597 -0
  696. package/src/rich-text-editor/tables.ts +272 -0
  697. package/src/rich-text-editor/toolbar.ts +724 -0
  698. package/src/rich-text-editor/view.ts +2287 -0
  699. package/src/rich-text-editor/virtual.ts +372 -0
  700. package/src/richtext/node.ts +294 -44
  701. package/src/richtext/runs.ts +23 -12
  702. package/src/table/index.ts +29 -0
  703. package/src/table/rows.ts +18 -8
  704. package/src/terminal/backends.ts +4 -1
  705. package/src/terminal/index.ts +47 -15
  706. package/src/terminal/vt/pty.ts +16 -2
  707. package/src/terminal-output/index.ts +6 -1
  708. package/src/three/canvas.ts +102 -8
  709. package/src/three/events.ts +49 -9
  710. package/src/tree/index.ts +19 -0
  711. package/dist/markdown/ast.d.ts.map +0 -1
  712. package/dist/markdown/ast.js +0 -17
  713. package/dist/markdown/ast.js.map +0 -1
  714. package/dist/markdown/parse.d.ts.map +0 -1
  715. package/dist/markdown/parse.js.map +0 -1
  716. package/dist/markdown/tags.d.ts.map +0 -1
  717. package/dist/markdown/tags.js.map +0 -1
  718. /package/dist/{markdown → internal/markdown}/parse.d.ts +0 -0
  719. /package/dist/{markdown → internal/markdown}/tags.d.ts +0 -0
  720. /package/dist/{markdown → internal/markdown}/tags.js +0 -0
  721. /package/src/{markdown → internal/markdown}/tags.ts +0 -0
package/dist/html/node.js CHANGED
@@ -39,19 +39,25 @@
39
39
  import { registerElement, registeredElements } from 'react-x11/host';
40
40
  import { Node } from 'react-x11/node';
41
41
  import { codePointAtOffset, codeUnitOffsets } from '../internal/text.js';
42
- import { attr, HtmlSource, isElement, tagOf } from './dom.js';
42
+ import { attr, HtmlSource, imageUrlOf, isElement, tagOf } from './dom.js';
43
43
  import { Cascade } from './css/cascade.js';
44
- import { parseStylesheet } from './css/parse.js';
44
+ import { mediaMatches, parseStylesheet } from './css/parse.js';
45
45
  import { uaStylesheet } from './css/ua.js';
46
- import { buildBoxes } from './layout/boxes.js';
46
+ import { buildBoxes, CONTENT_IMAGES } from './layout/boxes.js';
47
47
  import { layoutDocument } from './layout/block.js';
48
+ import { TextLayoutCache } from './layout/cache.js';
49
+ import { shapingSafe } from './layout/shaping.js';
50
+ import { SurfaceCache } from './surfaces.js';
51
+ import { inlineDecoration, runFor } from './layout/inline.js';
48
52
  // Through the inline module rather than a second cache: the offsets table for
49
53
  // a layout is built once, on the first selection that needs it.
50
- import { layoutOffsets as layoutOffsetsOf } from './layout/inline.js';
54
+ import { documentOffsetOf, layoutOffsetOf, layoutOffsets as layoutOffsetsOf, } from './layout/inline.js';
51
55
  import { lineBands as bandsFor } from '../richtext/runs.js';
52
- import { paintDocument, queryChildIndex } from './paint.js';
56
+ import { clipsOverflow, containingBlockOf, hasRect, holds, holdsAbsolute, paintDocument, queryChildIndex, } from './paint.js';
53
57
  import { controlRectsOf, measureControl } from './controls.js';
54
58
  import { ResourceStore } from './resources.js';
59
+ import { WebFonts } from './fonts.js';
60
+ import { resolveUrl, UrlResolver } from './url.js';
55
61
  /** The element name — registration key, `node.kind` and JSX tag alike. */
56
62
  export const ELEMENT = 'htmlview';
57
63
  export function registerHtmlView() {
@@ -62,10 +68,35 @@ export function registerHtmlView() {
62
68
  // `source` and `look` are this element's own vocabulary and neither is a
63
69
  // style name today; declaring them keeps the DEV flat-style-prop
64
70
  // assertion honest if core's vocabulary grows underneath us.
65
- semanticNames: ['source', 'look', 'stylesheet', 'complete'],
71
+ semanticNames: [
72
+ 'source',
73
+ 'look',
74
+ 'stylesheet',
75
+ 'charset',
76
+ 'complete',
77
+ 'baseUrl',
78
+ ],
66
79
  childrenAllowed: false,
67
80
  });
68
81
  }
82
+ /** How many widths' sizes a document keeps (`HtmlViewNode._sizeAt`): the
83
+ * two a frame of a resize asks, and a little slack for a drag that turns
84
+ * back. */
85
+ const SIZES_KEPT = 4;
86
+ /**
87
+ * A size in the whole pixels core lays out in, rounded up — less the float
88
+ * noise a sum of Yoga's single-precision positions carries: a page exactly
89
+ * `100vh` tall came to 737.0000076 under a 737-pixel viewport, and a
90
+ * rounding that took it for 738 scrolled it by a pixel under a scrollbar.
91
+ */
92
+ function wholePixels(n) {
93
+ return Math.max(0, Math.ceil(n - 1 / 64));
94
+ }
95
+ /** How deep `@import`s are followed: an import in an import in an import is
96
+ * a stylesheet; sixteen of them is a loop that changes its URL each time. */
97
+ const MAX_IMPORT_DEPTH = 16;
98
+ /** A scheme at the start: an absolute URL, a base that means something. */
99
+ const ABSOLUTE_URL = /^[a-zA-Z][a-zA-Z0-9+.-]*:/;
69
100
  /** What changed, and therefore how far back up the pipeline to go. */
70
101
  var Stale;
71
102
  (function (Stale) {
@@ -77,14 +108,43 @@ var Stale;
77
108
  })(Stale || (Stale = {}));
78
109
  export class HtmlViewNode extends Node {
79
110
  _source = new HtmlSource();
111
+ /** Where the document's relative URLs resolve: its `<base href>` against
112
+ * the `baseUrl` prop, or the prop, or nowhere (`url.ts`). */
113
+ _urls = new UrlResolver();
80
114
  _resources;
115
+ /** The families the document's `@font-face` rules declare (`fonts.ts`). */
116
+ _webFonts;
117
+ /** The sheets the last restyle read, and the cascade built from them. */
118
+ _sheetsRead = null;
119
+ /** Blurred shadows, drawn once each. */
120
+ _shadowCache = null;
81
121
  _cascade = null;
82
122
  _tree = null;
123
+ /** The faces `_warmFaces` has asked the font matcher for, and of which
124
+ * fonts. */
125
+ _warmedFaces = new Set();
126
+ _warmedFor = null;
83
127
  _stale = Stale.Everything;
84
- _laidOutAt = -1;
128
+ /** The width the document was last laid out at. Not `_laidOutAt`: core's
129
+ * `Node` has a method of that name, which a field would hide. */
130
+ _laidOutWidth = -1;
85
131
  _mediaBand = -1;
132
+ /** The viewport the box tree's styles were computed at, for the ones
133
+ * that read it (`vw`, `vh`). */
134
+ _styledWidth = -1;
135
+ _styledHeight = -1;
86
136
  _documentHeight = 0;
87
137
  _documentWidth = 0;
138
+ /** The viewport height the document was last laid out under, and whether
139
+ * that layout read it (`LayoutResult.readsViewportHeight`). */
140
+ _laidOutUnder = -1;
141
+ _layoutReadsViewport = false;
142
+ /** Whether core is asking for this element's size right now — inside
143
+ * its layout pass, where a node must not ask to be measured again. */
144
+ _measuring = false;
145
+ /** What the document came to at the widths it was last laid out at, while
146
+ * nothing a layout reads has changed (`_sizeAt`). */
147
+ _sizes = new Map();
88
148
  _textPoints = null;
89
149
  /** Whether code points and code units are the same index — true unless
90
150
  * the text carries surrogate pairs. null until checked. */
@@ -95,12 +155,23 @@ export class HtmlViewNode extends Node {
95
155
  _reportedDomRevision = -1;
96
156
  constructor(props, app) {
97
157
  super(ELEMENT, props, app);
98
- this._resources = new ResourceStore((request) => this._props().onResource?.(request) ?? null, () => {
99
- // A late resource changes intrinsic sizes, so the box tree is what
100
- // has to be rebuilt — not merely repainted. An image that arrives
101
- // after first paint is the ordinary case, not an error path.
158
+ const ask = (request) => this._props().onResource?.(request) ?? null;
159
+ this._resources = new ResourceStore(ask, (what) => {
160
+ // A late stylesheet is a new cascade: its rules, what it imports,
161
+ // the faces it declares. A late image changes intrinsic sizes, so
162
+ // the box tree is what has to be rebuilt — not merely repainted.
163
+ // Either arriving after first paint is the ordinary case, not an
164
+ // error path: a host on a network answers every request that way.
165
+ this._invalidate(what === 'stylesheet' ? Stale.Style : Stale.Boxes);
166
+ }, this._urls);
167
+ this._webFonts = new WebFonts(app, ask, () => {
168
+ // A face arrived: the family lists change, so the styles do, and
169
+ // every text layout kept from before was set in the face it
170
+ // replaces — under the same list, where a family of the document's
171
+ // had loaded one weight and now has another.
172
+ this._layouts = null;
102
173
  this._invalidate(Stale.Boxes);
103
- });
174
+ }, 'sans-serif');
104
175
  this._read();
105
176
  }
106
177
  _props() {
@@ -181,8 +252,24 @@ export class HtmlViewNode extends Node {
181
252
  props.onDocument?.(this._source.document);
182
253
  this._reportedDomRevision = props.domRevision ?? 0;
183
254
  }
255
+ this._updateBase();
184
256
  this._sweep();
185
257
  }
258
+ /**
259
+ * The document's base URL: its first `<base href>`, resolved against the
260
+ * `baseUrl` prop, or the prop — and only an absolute one, since a relative
261
+ * base resolves nothing. Everything resolved against the old one is
262
+ * stale when it moves, the sheets' URLs with it, so the cascade is.
263
+ */
264
+ _updateBase() {
265
+ const given = this._props().baseUrl || null;
266
+ const href = this._source.facts().base;
267
+ let base = href ? resolveUrl(href, given) : given;
268
+ if (base && !ABSOLUTE_URL.test(base))
269
+ base = null;
270
+ if (this._urls.setBase(base))
271
+ this._invalidate(Stale.Style);
272
+ }
186
273
  /**
187
274
  * Hand the document's own declarations to the host: every `<script>` once,
188
275
  * and every resource that has not been asked for.
@@ -211,7 +298,7 @@ export class HtmlViewNode extends Node {
211
298
  }
212
299
  for (const el of facts.resources) {
213
300
  const tag = tagOf(el);
214
- const url = tag === 'link' ? attr(el, 'href') : attr(el, 'src');
301
+ const url = tag === 'link' ? attr(el, 'href') : imageUrlOf(el);
215
302
  if (!url)
216
303
  continue;
217
304
  this._resources.request({
@@ -221,75 +308,356 @@ export class HtmlViewNode extends Node {
221
308
  });
222
309
  }
223
310
  }
311
+ /**
312
+ * Ask for every `background-image` the styles name. Known only once the
313
+ * cascade has run, where an `<img>` is known from the markup; the store
314
+ * asks once a URL, so a tree built again asks nothing new.
315
+ */
316
+ _requestBackgrounds(tree) {
317
+ for (const box of tree.backgrounds) {
318
+ if (!box.el)
319
+ continue;
320
+ // each layer's, where there is more than one
321
+ for (const url of box.style.backgroundImages ?? [
322
+ box.style.backgroundImage,
323
+ ]) {
324
+ if (typeof url === 'string') {
325
+ this._resources.request({ url, kind: 'image', element: box.el });
326
+ }
327
+ }
328
+ const border = box.style.borderImage.source;
329
+ if (typeof border === 'string') {
330
+ this._resources.request({
331
+ url: border,
332
+ kind: 'image',
333
+ element: box.el,
334
+ });
335
+ }
336
+ }
337
+ // and every image generated content names, which is only known there
338
+ // too
339
+ for (const { url, element } of tree.contentImages) {
340
+ this._resources.request({ url, kind: 'image', element });
341
+ }
342
+ }
343
+ /** Whether an image generated content names arrived as it was asked for,
344
+ * the tree built without its size: a host that answers at once is told
345
+ * nothing later, where an `<img>` is asked for before the build. */
346
+ _contentImagesArrived(tree) {
347
+ for (const { url, sized } of tree.contentImages) {
348
+ if (!sized && this._resources.imageSize(url))
349
+ return true;
350
+ }
351
+ return false;
352
+ }
353
+ /**
354
+ * Ask the font matcher for the faces the document's text is set in, as
355
+ * soon as its boxes say which and while its layout is still ahead. A face
356
+ * nothing had warmed was a synchronous fc-match inside that layout: the
357
+ * benchmark report's `th`, at 600, waited 38 ms before its first paint.
358
+ * One child answers every face a call names, off the event loop (ntk's
359
+ * `FontManager#prewarm`), and a layout reaching a face still on its way
360
+ * takes the answer rather than asking again. Each face is asked once, by
361
+ * the family, weight and style the layout hands the engine.
362
+ */
363
+ _warmFaces(tree) {
364
+ const fonts = this._fonts();
365
+ if (typeof fonts?.prewarm !== 'function')
366
+ return;
367
+ if (this._warmedFor !== fonts) {
368
+ this._warmedFor = fonts;
369
+ this._warmedFaces.clear();
370
+ }
371
+ let faces = null;
372
+ for (const style of tree.textStyles) {
373
+ const key = `${style.fontFamily}|${style.fontWeight}|${style.fontStyle}`;
374
+ if (this._warmedFaces.has(key))
375
+ continue;
376
+ this._warmedFaces.add(key);
377
+ faces ??= new Map();
378
+ let named = faces.get(style.fontFamily);
379
+ if (!named)
380
+ faces.set(style.fontFamily, (named = []));
381
+ named.push({ weight: style.fontWeight, style: style.fontStyle });
382
+ }
383
+ if (faces)
384
+ for (const [family, named] of faces)
385
+ fonts.prewarm(family, named);
386
+ }
224
387
  /** Rebuild the cascade — the document's sheets plus the host's. */
225
388
  _restyle(width) {
226
389
  const props = this._props();
227
390
  const look = this._deviceLook();
228
- const sheets = [uaStylesheet(look)];
229
- let order = 0;
391
+ const documentBase = this._urls.base;
392
+ // What the sheets are read from, in order: a `<style>`'s text or a
393
+ // fetched `<link>`'s, and after them the host's — each with the URL its
394
+ // own relative URLs resolve against: the document's for a `<style>`,
395
+ // the sheet's own for a `<link>`.
396
+ const read = [];
230
397
  for (const ref of this._source.facts().sheets) {
231
- const text = ref.kind === 'inline'
232
- ? ref.text
233
- : this._resources.stylesheetText(ref.href);
398
+ // A sheet handed over as bytes that names no encoding of its own is in
399
+ // its referrer's: a `<link charset>`, then the document's (CSS 2.1
400
+ // 4.4), and an import is in the encoding of the sheet importing it.
401
+ const linked = ref.kind === 'inline'
402
+ ? null
403
+ : this._resources.stylesheet(ref.href, [
404
+ attr(ref.element, 'charset') || undefined,
405
+ props.charset,
406
+ ]);
407
+ const text = ref.kind === 'inline' ? ref.text : linked?.text;
234
408
  if (!text)
235
409
  continue;
236
- const sheet = parseStylesheet(text, order);
237
- order += sheet.rules.length + 1;
238
- // `@import` is a resource like any other, and its rules sit *before*
239
- // the importing sheet's — so a fetched import is spliced in ahead.
240
- for (const url of sheet.imports) {
241
- this._resources.request({
242
- url,
243
- kind: 'stylesheet',
244
- element: ref.element,
245
- });
246
- const imported = this._resources.stylesheetText(url);
247
- if (imported) {
248
- const parsed = parseStylesheet(imported, order);
249
- order += parsed.rules.length + 1;
250
- sheets.push(parsed);
251
- }
252
- }
253
- sheets.push(sheet);
410
+ const encoding = linked ? linked.encoding : props.charset;
411
+ const base = ref.kind === 'inline'
412
+ ? documentBase
413
+ : this._resources.sheetBase(ref.href);
414
+ read.push({ text, encoding, element: ref.element, base });
254
415
  }
255
416
  const extra = props.stylesheet;
256
- for (const text of Array.isArray(extra) ? extra : extra ? [extra] : []) {
257
- const sheet = parseStylesheet(text, order);
258
- order += sheet.rules.length + 1;
259
- sheets.push(sheet);
417
+ const extras = Array.isArray(extra) ? extra : extra ? [extra] : [];
418
+ const fonts = this._fonts();
419
+ // Sheets that read as they did last time are the same rules: an append
420
+ // to a streamed document, or a width across a `@media` breakpoint,
421
+ // restyles the elements without parsing a framework's stylesheet again
422
+ // or indexing its thousand rules again.
423
+ const kept = this._sheetsRead;
424
+ let faces;
425
+ if (kept &&
426
+ kept.look === look &&
427
+ kept.scale === this._scale &&
428
+ kept.fonts === fonts &&
429
+ this._sameSheets(kept, read, extras)) {
430
+ this._cascade = kept.cascade;
431
+ kept.cascade.viewportWidth = width;
432
+ kept.cascade.viewportHeight = this._viewportHeight();
433
+ faces = kept.faces;
434
+ }
435
+ else {
436
+ const sheets = [uaStylesheet(look)];
437
+ const imports = [];
438
+ faces = [];
439
+ let order = 0;
440
+ const layers = new Map();
441
+ // A sheet takes its place in the cascade's order once everything it
442
+ // imports has taken theirs: `@import` is a resource like any other,
443
+ // and an imported sheet's rules sit *before* the importing sheet's
444
+ // (CSS 2.1 6.4.1), or an imported rule would win a tie against the
445
+ // sheet importing it.
446
+ const place = (sheet, element) => {
447
+ for (const rule of sheet.rules)
448
+ rule.order = order++;
449
+ order += 1;
450
+ sheets.push(sheet);
451
+ for (const rule of sheet.fontFaces)
452
+ faces.push({ rule, element });
453
+ };
454
+ for (const { text, encoding, element, base } of read) {
455
+ const sheet = parseStylesheet(text, 0, layers, base);
456
+ const seen = [];
457
+ this._placeImports(sheet, encoding, element, layers, seen, place);
458
+ imports.push(seen);
459
+ place(sheet, element);
460
+ }
461
+ for (const text of extras) {
462
+ const sheet = parseStylesheet(text, order, layers);
463
+ order += sheet.rules.length + 1;
464
+ sheets.push(sheet);
465
+ }
466
+ this._cascade = new Cascade(sheets, look, width, this._viewportHeight(), this._scale, fonts ? (family, size) => xHeightOf(fonts, family, size) : null, fonts ? (family, size) => zeroWidthOf(fonts, family, size) : null, fonts ? (family, size) => normalLineOf(fonts, family, size) : null, faces.length ? this._webFonts : null);
467
+ this._sheetsRead = {
468
+ look,
469
+ scale: this._scale,
470
+ fonts,
471
+ texts: read.map((r) => r.text),
472
+ encodings: read.map((r) => r.encoding),
473
+ bases: read.map((r) => r.base),
474
+ imports,
475
+ extras,
476
+ faces,
477
+ cascade: this._cascade,
478
+ };
260
479
  }
261
- this._cascade = new Cascade(sheets, look, width, this._viewportHeight(), this._scale);
480
+ // The faces this width and scheme declare: a `@font-face` may sit in a
481
+ // `@media` block like any rule.
482
+ const cssWidth = width / this._scale;
483
+ this._webFonts.setFallback(look.fontFamily);
484
+ this._webFonts.setFaces(faces.filter((f) => mediaMatches(f.rule.media, cssWidth, look.colorScheme)));
262
485
  this._cascade.setPointer({
263
486
  hovered: new Set(this._hovered),
264
487
  active: EMPTY_SET,
265
488
  });
266
489
  this._mediaBand = this._cascade.mediaBand(width);
267
490
  }
491
+ /**
492
+ * Place what a sheet imports, and what each of those imports, depth
493
+ * first — asking for each through the seam, as a parse of the sheet has
494
+ * to. `seen` records every import read, in order, which is what tells the
495
+ * next restyle whether the sheets read the same (`_sameSheets`). A sheet
496
+ * that imports itself, or one of the sheets importing it, is read once.
497
+ */
498
+ _placeImports(sheet, encoding, element, layers, seen, place, depth = 0, chain = new Set()) {
499
+ for (const url of sheet.imports) {
500
+ this._resources.request({ url, kind: 'stylesheet', element });
501
+ const fetched = this._resources.stylesheet(url, [encoding]);
502
+ const base = this._resources.sheetBase(url);
503
+ seen.push({ url, text: fetched?.text ?? null, base });
504
+ const key = base ?? url;
505
+ if (!fetched || depth >= MAX_IMPORT_DEPTH || chain.has(key))
506
+ continue;
507
+ const imported = parseStylesheet(fetched.text, 0, layers, base);
508
+ chain.add(key);
509
+ this._placeImports(imported, fetched.encoding, element, layers, seen, place, depth + 1, chain);
510
+ chain.delete(key);
511
+ place(imported, element);
512
+ }
513
+ }
514
+ /** Whether the sheets read the same as the ones `kept` was built from,
515
+ * down to what each imports — asking for the imports again, as a parse
516
+ * of the sheet would have. */
517
+ _sameSheets(kept, read, extras) {
518
+ if (read.length !== kept.texts.length)
519
+ return false;
520
+ if (extras.length !== kept.extras.length)
521
+ return false;
522
+ for (let i = 0; i < extras.length; i += 1) {
523
+ if (extras[i] !== kept.extras[i])
524
+ return false;
525
+ }
526
+ for (let i = 0; i < read.length; i += 1) {
527
+ const r = read[i];
528
+ if (r.text !== kept.texts[i] ||
529
+ r.encoding !== kept.encodings[i] ||
530
+ r.base !== kept.bases[i]) {
531
+ return false;
532
+ }
533
+ // The encodings an import falls back to are its importer's, which
534
+ // only a parse knows; the text an import decoded to under them is what
535
+ // was kept, and bytes that decode differently now are a new sheet.
536
+ for (const { url, text, base } of kept.imports[i]) {
537
+ this._resources.request({
538
+ url,
539
+ kind: 'stylesheet',
540
+ element: r.element,
541
+ });
542
+ const fetched = this._resources.stylesheet(url, [r.encoding]);
543
+ if ((fetched?.text ?? null) !== text)
544
+ return false;
545
+ if (this._resources.sheetBase(url) !== base)
546
+ return false;
547
+ }
548
+ }
549
+ return true;
550
+ }
268
551
  _viewportHeight() {
269
- // The viewport a `vh` resolves against is the window's, not the
270
- // document's — a document taller than the window does not make `100vh`
271
- // taller with it.
552
+ // The viewport a `vh` resolves against is the one the document is seen
553
+ // through, not the document — a document taller than it does not make
554
+ // `100vh` taller with it. That is the box that scrolls the element,
555
+ // where one does: a browser's page area, below its tab strip and its
556
+ // toolbar, where `100vh` measured by the window put a page's footer the
557
+ // toolbar's height below the fold. The window, where nothing scrolls it.
558
+ for (let node = this.parent; node; node = node.parent) {
559
+ // `Scrollable`'s, and not every node's: asked the way core asks it
560
+ const scroller = node;
561
+ if (!scroller.isScroller?.())
562
+ continue;
563
+ const height = node.contentBox().height;
564
+ if (height > 0)
565
+ return height;
566
+ break;
567
+ }
272
568
  const root = this.root;
273
569
  const height = root?.abs?.height;
274
570
  return height && height > 0 ? height : 600;
275
571
  }
276
572
  _fonts() {
277
573
  const fonts = this.app?.fonts;
278
- return fonts ?? null;
574
+ // a face the engine cannot shape from costs its characters, not the
575
+ // document (`shaping.ts`)
576
+ return fonts ? shapingSafe(fonts) : null;
577
+ }
578
+ /** The text layouts the last pass made, for this one to reuse. */
579
+ _layouts = null;
580
+ /** The fonts a layout pass lays text out with: the engine's, through the
581
+ * layouts the pass before made (`TextLayoutCache`). */
582
+ _layoutFonts() {
583
+ const fonts = this._fonts();
584
+ if (!fonts)
585
+ return null;
586
+ if (this._layouts?.engine !== fonts)
587
+ this._layouts = new TextLayoutCache(fonts);
588
+ this._layouts.begin();
589
+ return this._layouts.fonts;
279
590
  }
280
- /** Bring the pipeline up to date for a width. */
591
+ /**
592
+ * Bring the pipeline up to date for a width — or, where that throws, leave
593
+ * the document blank. From a paint a throw is the application's end, for
594
+ * a document it did not write: a stack a thousand nested elements run
595
+ * down, or a limit of the text engine's or the server's that one more
596
+ * document finds. Reported once for each thing that went wrong, and tried
597
+ * again only when something changes, or the width does.
598
+ */
281
599
  _prepare(width) {
282
600
  const target = Math.max(1, Math.floor(width));
601
+ if (this._failedAt === target && this._stale === Stale.Nothing)
602
+ return;
603
+ try {
604
+ this._update(target);
605
+ this._failedAt = -1;
606
+ }
607
+ catch (error) {
608
+ this._fail(error, target);
609
+ }
610
+ }
611
+ /** The width the pipeline threw at, until something changes (`_prepare`). */
612
+ _failedAt = -1;
613
+ _failures = null;
614
+ _fail(error, width) {
615
+ this._tree = null;
616
+ this._stale = Stale.Nothing;
617
+ this._failedAt = width;
618
+ this._laidOutWidth = -1;
619
+ this._documentWidth = 0;
620
+ this._documentHeight = 0;
621
+ const message = String(error?.stack ?? error);
622
+ this._failures ??= new Set();
623
+ if (this._failures.has(message))
624
+ return;
625
+ this._failures.add(message);
626
+ const g = globalThis;
627
+ if (g.process?.env?.NODE_ENV === 'production')
628
+ return;
629
+ g.console?.error('@react-x11/components: <Html> could not lay out or paint its ' +
630
+ `document, and leaves it blank.\n${message}`);
631
+ }
632
+ _update(target) {
283
633
  const props = this._props();
284
634
  if ((props.domRevision ?? 0) !== this._reportedDomRevision) {
285
635
  this._reportedDomRevision = props.domRevision ?? 0;
286
636
  this._source.touch();
637
+ this._updateBase();
287
638
  this._sweep();
288
639
  if (this._stale < Stale.Style)
289
640
  this._stale = Stale.Style;
290
641
  }
291
- if (this._stale === Stale.Nothing && this._laidOutAt === target)
642
+ // A document that reads its viewport's height — a `vh`, a percentage
643
+ // height on the root, a box placed against the initial containing block
644
+ // — is laid out again when that height moves. One that reads none, which
645
+ // is most, goes on skipping layout when the window only grew taller.
646
+ const viewport = this._viewportHeight();
647
+ const viewportMoved = viewport !== this._laidOutUnder && this._readsViewportHeight();
648
+ // the sizes other widths came to were read from what just changed
649
+ if (this._stale !== Stale.Nothing || viewportMoved)
650
+ this._sizes.clear();
651
+ if (this._stale === Stale.Nothing &&
652
+ this._laidOutWidth === target &&
653
+ !viewportMoved) {
292
654
  return;
655
+ }
656
+ // Nothing but the viewport: the size core laid this element out at came
657
+ // from a layout under the old one, and nothing else will tell it so.
658
+ const onlyViewport = this._stale === Stale.Nothing && this._laidOutWidth === target;
659
+ const wasWidth = this._documentWidth;
660
+ const wasHeight = this._documentHeight;
293
661
  if (this._stale >= Stale.Style || !this._cascade) {
294
662
  this._restyle(target);
295
663
  this._stale = Math.max(this._stale, Stale.Boxes);
@@ -305,28 +673,74 @@ export class HtmlViewNode extends Node {
305
673
  if (!cascade)
306
674
  return;
307
675
  cascade.viewportWidth = target;
308
- cascade.viewportHeight = this._viewportHeight();
676
+ cascade.viewportHeight = viewport;
677
+ // A `vw` or a `vh` is a number by the time a style holds it, so the
678
+ // styles computed for another viewport are wrong for this one: built
679
+ // again, where some style reads the side that moved. A document that
680
+ // reads neither — most — goes on skipping the cascade on a resize.
681
+ if (this._stale < Stale.Boxes &&
682
+ ((cascade.readsViewportWidth && this._styledWidth !== target) ||
683
+ (cascade.readsViewportHeight && this._styledHeight !== viewport))) {
684
+ this._stale = Stale.Boxes;
685
+ }
309
686
  if (this._stale >= Stale.Boxes || !this._tree) {
310
687
  const look = this._deviceLook();
311
- this._tree = buildBoxes(this._source.document, {
688
+ const build = () => buildBoxes(this._source.document, {
312
689
  cascade,
313
690
  scale: this._scale,
314
- imageSize: (el) => this._resources.imageSize(attr(el, 'src') ?? ''),
691
+ imageSize: (el) => this._resources.imageSize(imageUrlOf(el) ?? ''),
692
+ urlSize: (url) => this._resources.imageSize(url),
315
693
  controlSize: (el, kind, style) => measureControl(el, kind, style, this._fonts(), look),
316
694
  });
695
+ this._tree = build();
696
+ this._styledWidth = target;
697
+ this._styledHeight = viewport;
698
+ this._requestBackgrounds(this._tree);
699
+ let again = this._contentImagesArrived(this._tree);
700
+ // the faces the styles just asked for, of the families the document
701
+ // loads itself — which, answered at once, change the styles asking
702
+ if (this._webFonts.request(this._tree.text)) {
703
+ this._layouts = null;
704
+ again = true;
705
+ }
706
+ if (again)
707
+ this._tree = build();
708
+ this._warmFaces(this._tree);
317
709
  this._textPoints = null;
318
710
  this._pointsAreUnits = null;
319
- this._laidOutAt = -1;
711
+ this._laidOutWidth = -1;
320
712
  }
321
713
  if (this._tree &&
322
- (this._laidOutAt !== target || this._stale >= Stale.Layout)) {
323
- const result = layoutDocument(this._tree, this._fonts(), target, this._viewportHeight());
714
+ (this._laidOutWidth !== target ||
715
+ viewportMoved ||
716
+ this._stale >= Stale.Layout)) {
717
+ const result = layoutDocument(this._tree, this._layoutFonts(), target, viewport);
324
718
  this._documentWidth = result.width;
325
719
  this._documentHeight = result.height;
326
- this._laidOutAt = target;
720
+ this._laidOutWidth = target;
721
+ this._laidOutUnder = viewport;
722
+ this._layoutReadsViewport = result.readsViewportHeight;
723
+ this._sizes.delete(target);
724
+ this._sizes.set(target, { width: result.width, height: result.height });
725
+ if (this._sizes.size > SIZES_KEPT) {
726
+ this._sizes.delete(this._sizes.keys().next().value);
727
+ }
327
728
  this._reportControls();
328
729
  }
329
730
  this._stale = Stale.Nothing;
731
+ // Read after core's layout pass — the viewport is the box around this
732
+ // one, whose new height only that pass decides — so it is a paint that
733
+ // finds it moved, and the next frame that measures this element again.
734
+ // Inside a measure the answer already is the new size.
735
+ if (onlyViewport &&
736
+ !this._measuring &&
737
+ (this._documentWidth !== wasWidth || this._documentHeight !== wasHeight)) {
738
+ this.invalidateMeasure('content');
739
+ }
740
+ }
741
+ /** Whether the document as laid out reads its viewport's height. */
742
+ _readsViewportHeight() {
743
+ return (this._layoutReadsViewport || this._cascade?.readsViewportHeight === true);
330
744
  }
331
745
  _reportControls() {
332
746
  const tree = this._tree;
@@ -344,6 +758,16 @@ export class HtmlViewNode extends Node {
344
758
  y: r.y / s,
345
759
  width: r.width / s,
346
760
  height: r.height / s,
761
+ ...(r.bare && {
762
+ bare: {
763
+ ...r.bare,
764
+ x: r.bare.x / s,
765
+ y: r.bare.y / s,
766
+ width: r.bare.width / s,
767
+ height: r.bare.height / s,
768
+ fontSize: r.bare.fontSize / s,
769
+ },
770
+ }),
347
771
  });
348
772
  if (sameRects(rects, this._controls))
349
773
  return;
@@ -362,17 +786,57 @@ export class HtmlViewNode extends Node {
362
786
  */
363
787
  measureContent({ width }) {
364
788
  const offered = Number.isFinite(width) ? width : 800;
365
- this._prepare(offered);
789
+ this._measuring = true;
790
+ let size;
791
+ try {
792
+ size = this._sizeAt(offered);
793
+ }
794
+ finally {
795
+ this._measuring = false;
796
+ }
797
+ // core takes finite numbers only, and throws on any other from the
798
+ // layout — a document of lengths no clamp foresaw is none too tall
799
+ const finite = (n) => (Number.isFinite(n) ? n : 0);
366
800
  return {
367
- width: Math.ceil(Math.min(this._documentWidth, offered)),
368
- height: Math.ceil(this._documentHeight),
801
+ width: wholePixels(Math.min(finite(size.width), offered)),
802
+ height: wholePixels(finite(size.height)),
369
803
  };
370
804
  }
805
+ /**
806
+ * The size the document comes to at `width`.
807
+ *
808
+ * Core asks a leaf for its height at the width it was measured at as well
809
+ * as at the one it has now (`probeHeightFloors`), to learn whether a
810
+ * relayout changed what the leaf needs — so every frame of a resize asks
811
+ * two widths, and the boxes hold one. Laying the document out at the old
812
+ * width to answer, and then at the new one again for the pass after, was
813
+ * three whole passes a frame. The size a width came to is kept instead,
814
+ * and answers until anything a layout reads changes: the source, the
815
+ * styles, a resource, the viewport height. Nothing but a size comes from
816
+ * it — paint and the text accessors read the boxes, which are only ever
817
+ * laid out for real (`_prepare`).
818
+ */
819
+ _sizeAt(width) {
820
+ const target = Math.max(1, Math.floor(width));
821
+ if (target !== this._laidOutWidth &&
822
+ this._stale === Stale.Nothing &&
823
+ (this._props().domRevision ?? 0) === this._reportedDomRevision &&
824
+ (!this._readsViewportHeight() ||
825
+ this._viewportHeight() === this._laidOutUnder)) {
826
+ const known = this._sizes.get(target);
827
+ if (known)
828
+ return known;
829
+ }
830
+ this._prepare(width);
831
+ return { width: this._documentWidth, height: this._documentHeight };
832
+ }
371
833
  applyProps(nextProps, prevProps) {
372
834
  super.applyProps(nextProps, prevProps);
373
835
  const next = nextProps;
374
836
  const prev = prevProps;
375
- if (next.look !== prev.look || next.stylesheet !== prev.stylesheet) {
837
+ if (next.look !== prev.look ||
838
+ next.stylesheet !== prev.stylesheet ||
839
+ next.charset !== prev.charset) {
376
840
  this._invalidate(Stale.Style);
377
841
  }
378
842
  if (next.source !== prev.source || next.complete !== prev.complete) {
@@ -381,10 +845,19 @@ export class HtmlViewNode extends Node {
381
845
  else if ((next.domRevision ?? 0) !== (prev.domRevision ?? 0)) {
382
846
  this._invalidate(Stale.Style);
383
847
  }
848
+ else if ((next.baseUrl ?? null) !== (prev.baseUrl ?? null)) {
849
+ // the same document somewhere else: every URL in it is another one
850
+ this._updateBase();
851
+ this._sweep();
852
+ }
384
853
  }
385
854
  destroySubtree() {
386
855
  this._resources.destroy();
856
+ this._webFonts.destroy();
387
857
  this._source.destroy();
858
+ this._shadowCache?.destroy();
859
+ this._shadowCache = null;
860
+ this._sheetsRead = null;
388
861
  this._tree = null;
389
862
  this._cascade = null;
390
863
  super.destroySubtree();
@@ -482,27 +955,106 @@ export class HtmlViewNode extends Node {
482
955
  // event's `x`/`y` carry and what `useLinkClicks` and an application's
483
956
  // handler hand over — and are the one place the two units meet on the way
484
957
  // in. `_toDocument` multiplies.
485
- /** The link under a logical window point, if any. Not part of the
486
- * selection seam: core deliberately left hover and `cursorAt` out of
487
- * #291, so following a link stays this package's. */
958
+ /** The link under a logical window point, if any — resolved against the
959
+ * document's base where it has one. Not part of the selection seam: core
960
+ * deliberately left hover and `cursorAt` out of #291, so following a
961
+ * link stays this package's. */
488
962
  hrefAtPoint(x, y) {
489
963
  const el = this.elementAtPoint(x, y);
490
964
  let node = el;
491
965
  while (node) {
492
966
  const href = attr(node, 'href');
493
- if (href && (tagOf(node) === 'a' || tagOf(node) === 'area'))
494
- return href;
967
+ if (href && (tagOf(node) === 'a' || tagOf(node) === 'area')) {
968
+ return this._urls.resolve(href);
969
+ }
495
970
  node = isElement(node.parent) ? node.parent : null;
496
971
  }
497
972
  return null;
498
973
  }
974
+ /** The URL the document's relative URLs resolve against, or null where
975
+ * it has none (`_updateBase`). */
976
+ get documentBase() {
977
+ return this._urls.base;
978
+ }
979
+ /**
980
+ * Where an element is, in document coordinates: logical pixels from this
981
+ * element's top left, the space the scroll offset of a box around it is
982
+ * in — so scrolling to a fragment is `scrollTo({ y: rect.y })`. A block's
983
+ * border box. An inline element's fragments, from its first line to its
984
+ * last, each across its text, the inline-blocks and inline boxes' edges
985
+ * it holds, and its own padding and border where it starts or ends — its
986
+ * border box across, as `getBoundingClientRect` is (CSSOM View 6.1) — and
987
+ * as tall as its lines; or where its text would start when it has none,
988
+ * as `<a name>` has none. Null for an element with no box — `display:
989
+ * none`, or not in the document.
990
+ */
991
+ elementRect(element) {
992
+ this._prepare(this.abs.width || 1);
993
+ const tree = this._tree;
994
+ if (!tree)
995
+ return null;
996
+ const box = boxFor(tree.root, element);
997
+ if (!box)
998
+ return null;
999
+ let rect = null;
1000
+ if (box.kind !== 'inline' && box.kind !== 'text') {
1001
+ rect = { x: box.x, y: box.y, width: box.width, height: box.height };
1002
+ }
1003
+ else {
1004
+ const bands = [];
1005
+ if (box.subtreeTextEnd > box.subtreeTextStart) {
1006
+ collectBands(tree.root, box.subtreeTextStart, box.subtreeTextEnd, 0, 0, bands);
1007
+ }
1008
+ if (box.kind === 'inline')
1009
+ fragmentReach(box, bands);
1010
+ for (const band of bands)
1011
+ rect = rect ? unionRect(rect, band) : band;
1012
+ if (!rect) {
1013
+ // an empty subtree's range is `[0, 0)`, wherever it stands: where
1014
+ // its text would be is where the text after it starts
1015
+ const caret = caretAt(tree.root, textAfter(tree.root, box));
1016
+ if (!caret)
1017
+ return null;
1018
+ rect = { x: caret.x, y: caret.y, width: 0, height: caret.height };
1019
+ }
1020
+ }
1021
+ const s = this._scale;
1022
+ return {
1023
+ x: rect.x / s,
1024
+ y: rect.y / s,
1025
+ width: rect.width / s,
1026
+ height: rect.height / s,
1027
+ };
1028
+ }
499
1029
  /** The deepest element whose box contains a logical window point. */
500
1030
  elementAtPoint(x, y) {
501
1031
  const tree = this._tree;
502
1032
  if (!tree)
503
1033
  return null;
504
1034
  const local = this._toDocument(x, y);
505
- return deepestAt(tree.root, local.x, local.y);
1035
+ return deepestAt(tree, local.x, local.y);
1036
+ }
1037
+ /**
1038
+ * The cursor for a point of this element's, in device pixels: what core
1039
+ * asks a drawn element as the pointer moves over it (`cursorAt`,
1040
+ * react-x11#757). The `cursor` the document's styles give what is under
1041
+ * it — a link's `pointer`, the user-agent sheet's — and where they say
1042
+ * nothing, text's I-beam over text and the arrow elsewhere, as a browser
1043
+ * shows them. The arrow is named rather than left to null: null lets
1044
+ * core fall through to the element's `defaultCursor`, which is the
1045
+ * I-beam on a selectable surface, and every `<Html>` is one.
1046
+ */
1047
+ cursorAt(x, y) {
1048
+ const tree = this._tree;
1049
+ if (!tree)
1050
+ return null;
1051
+ const hit = { text: false };
1052
+ const el = deepestAt(tree, x - this.abs.x, y - this.abs.y, hit);
1053
+ const cursor = el ? tree.styles.get(el)?.style.cursor : null;
1054
+ // a keyword; a `url()` this cannot load falls back, as its list would
1055
+ if (cursor && cursor !== 'auto' && /^[a-z-]+$/.test(cursor))
1056
+ return cursor;
1057
+ return hit.text ? 'text' : 'default';
506
1058
  }
507
1059
  /**
508
1060
  * The pointer moved, to a logical window point. Returns true when the
@@ -522,19 +1074,233 @@ export class HtmlViewNode extends Node {
522
1074
  }
523
1075
  if (sameChain(chain, this._hovered))
524
1076
  return false;
1077
+ const was = this._hovered;
525
1078
  this._hovered = chain;
526
1079
  cascade.setPointer({ hovered: new Set(chain), active: EMPTY_SET });
527
- this._invalidate(Stale.Boxes);
1080
+ this._restyleHover(was, chain);
528
1081
  return true;
529
1082
  }
530
1083
  clearHover() {
531
1084
  if (!this._hovered.length)
532
1085
  return false;
1086
+ const was = this._hovered;
533
1087
  this._hovered = [];
534
1088
  this._cascade?.setPointer({ hovered: new Set(), active: EMPTY_SET });
535
- this._invalidate(Stale.Boxes);
1089
+ this._restyleHover(was, []);
536
1090
  return true;
537
1091
  }
1092
+ /** The hovered chain moved: restyle where it did, or the document. */
1093
+ _restyleHover(was, now) {
1094
+ const done = this._hoverInPlace(was, now);
1095
+ if (done === 'painted')
1096
+ this.invalidate(false, this, 'props');
1097
+ else if (done === false)
1098
+ this._invalidate(Stale.Boxes);
1099
+ }
1100
+ /**
1101
+ * A pointer move restyled where it happened, when all it changed is ink.
1102
+ *
1103
+ * A move to another element is a change to the cascade, and the pipeline
1104
+ * answers one by building every box again and laying the document out:
1105
+ * 270 ms on X11 and twice that on Cocoa for a Wikipedia article, on every
1106
+ * link the pointer crossed. But the rules a page writes for `:hover`
1107
+ * nearly all change a colour, an underline, a background or a border's
1108
+ * colour — 77 of that article's 79 — and those move nothing. So:
1109
+ *
1110
+ * - Only an element whose hover state flipped and that a compound testing
1111
+ * the pointer could match can change (`Cascade.hoverTouches`), with its
1112
+ * subtree, and its later siblings where a sibling combinator follows.
1113
+ * A move between two paragraphs under `a:hover` touches nothing.
1114
+ * - Those are styled again from their parents. Where anything but ink
1115
+ * differs (`PAINT_ONLY`), or a box is not the element's own — an
1116
+ * anonymous or a pseudo-element box, a marker, a control — this is
1117
+ * not the move to take.
1118
+ * - The boxes take their new styles, and each layout of their text is
1119
+ * made again from the runs it was made from (`TextLayoutCache.inputsOf`)
1120
+ * with the new ink (`runFor`), where its geometry comes out the same.
1121
+ *
1122
+ * Everything is checked before anything is changed. 'none' where nothing
1123
+ * could change, 'painted' where it was restyled here, false where the
1124
+ * document has to be built again.
1125
+ */
1126
+ _hoverInPlace(was, now) {
1127
+ const cascade = this._cascade;
1128
+ const tree = this._tree;
1129
+ const layouts = this._layouts;
1130
+ if (!cascade || !tree || !layouts || !cascade.hoverLocal)
1131
+ return false;
1132
+ if (this._stale !== Stale.Nothing || this._laidOutWidth < 0)
1133
+ return false;
1134
+ const before = new Set(was);
1135
+ const after = new Set(now);
1136
+ const roots = [];
1137
+ // and the elements a `:has()` testing the pointer may flip, which
1138
+ // are around the ones that changed; the chains are ancestor chains,
1139
+ // so the deepest that changed on each side is where to look from
1140
+ const flipped = (el, other) => {
1141
+ if (other.has(el))
1142
+ return;
1143
+ if (cascade.hoverTouches(el))
1144
+ roots.push(el);
1145
+ };
1146
+ for (const el of was)
1147
+ flipped(el, after);
1148
+ for (const el of now)
1149
+ flipped(el, before);
1150
+ const deepest = (chain, other) => {
1151
+ const el = chain[0];
1152
+ if (el && !other.has(el))
1153
+ cascade.hoverAnchors(el, roots);
1154
+ };
1155
+ deepest(was, after);
1156
+ deepest(now, before);
1157
+ if (!roots.length)
1158
+ return 'none';
1159
+ // what may restyle: the roots' subtrees, which inherit from them and a
1160
+ // descendant combinator reaches, and their later siblings' too where a
1161
+ // sibling combinator follows a compound that tests the pointer
1162
+ const reach = new Set();
1163
+ const collect = (el) => {
1164
+ const stack = [el];
1165
+ while (stack.length) {
1166
+ const at = stack.pop();
1167
+ if (reach.has(at))
1168
+ continue;
1169
+ reach.add(at);
1170
+ if (reach.size > HOVER_RESTYLE_LIMIT)
1171
+ return false;
1172
+ for (const child of at.children)
1173
+ if (isElement(child))
1174
+ stack.push(child);
1175
+ }
1176
+ return true;
1177
+ };
1178
+ for (const root of roots) {
1179
+ if (!collect(root))
1180
+ return false;
1181
+ if (!cascade.hoverSiblings)
1182
+ continue;
1183
+ for (let s = root.nextSibling; s; s = s.nextSibling) {
1184
+ if (isElement(s) && !collect(s))
1185
+ return false;
1186
+ }
1187
+ }
1188
+ // styled again from their parents, a parent first
1189
+ const fresh = new Map();
1190
+ const changed = new Map();
1191
+ let refused = false;
1192
+ const styleOf = (el) => {
1193
+ const kept = tree.styles.get(el);
1194
+ if (!kept)
1195
+ return null;
1196
+ if (!reach.has(el))
1197
+ return kept.style;
1198
+ const done = fresh.get(el);
1199
+ if (done)
1200
+ return done;
1201
+ // an element at the top of the document is styled from the box above
1202
+ // it all, as the builder styled it (`BoxBuilder.run`)
1203
+ const parent = isElement(el.parent) ? el.parent : null;
1204
+ const parentStyle = parent ? styleOf(parent) : tree.root.style;
1205
+ if (!parentStyle) {
1206
+ refused = true;
1207
+ return null;
1208
+ }
1209
+ const style = cascade.styleFor(el, parentStyle, kept.inFlex);
1210
+ fresh.set(el, style);
1211
+ const diff = inkOnly(kept.style, style);
1212
+ if (diff === false)
1213
+ refused = true;
1214
+ else if (diff)
1215
+ changed.set(el, style);
1216
+ return style;
1217
+ };
1218
+ for (const el of reach) {
1219
+ styleOf(el);
1220
+ if (refused)
1221
+ return false;
1222
+ }
1223
+ if (!changed.size)
1224
+ return 'none';
1225
+ for (const el of changed.keys()) {
1226
+ const tag = tagOf(el);
1227
+ // their backgrounds are the canvas's
1228
+ if (tag === 'html' || tag === 'body')
1229
+ return false;
1230
+ }
1231
+ // the boxes: each changed element's own, and nothing that takes its
1232
+ // style from one without being it
1233
+ const fonts = layouts.fonts;
1234
+ const restyled = [];
1235
+ const redecorated = [];
1236
+ const walk = [tree.root];
1237
+ while (walk.length) {
1238
+ const box = walk.pop();
1239
+ for (const child of box.children)
1240
+ walk.push(child);
1241
+ const owner = box.el ?? nearestElement(box);
1242
+ if (!owner)
1243
+ continue;
1244
+ const style = changed.get(owner);
1245
+ if (!style)
1246
+ continue;
1247
+ const kept = tree.styles.get(owner);
1248
+ // an anonymous box, a pseudo-element, a marker, a control: a style
1249
+ // derived from the element's, or drawn from it somewhere else
1250
+ if (!box.el || box.style !== kept.style)
1251
+ return false;
1252
+ if (box.marker || box.replaced !== 'none')
1253
+ return false;
1254
+ restyled.push([box, style]);
1255
+ if (box.kind === 'inline') {
1256
+ const decoration = inlineDecoration(fonts, box, style);
1257
+ if ((decoration === null) !== (box.decoration === null)) {
1258
+ // a rounded box with a background is laid out with its edges
1259
+ if (style.borderRadius.some((r) => r !== 0))
1260
+ return false;
1261
+ }
1262
+ if (decoration !== box.decoration)
1263
+ redecorated.push([box, decoration]);
1264
+ }
1265
+ }
1266
+ // the text: each layout holding a run of a changed element's, made
1267
+ // again with its new ink, where it comes out the same shape
1268
+ const relaid = new Map();
1269
+ const texts = [];
1270
+ walk.push(tree.root);
1271
+ while (walk.length) {
1272
+ const box = walk.pop();
1273
+ for (const child of box.children)
1274
+ walk.push(child);
1275
+ if (!box.lines)
1276
+ continue;
1277
+ for (const line of box.lines) {
1278
+ for (const text of line.texts) {
1279
+ texts.push(text);
1280
+ if (relaid.has(text.layout))
1281
+ continue;
1282
+ const next = reinked(text, layouts, changed, tree.styles);
1283
+ if (next === false)
1284
+ return false;
1285
+ relaid.set(text.layout, next);
1286
+ }
1287
+ }
1288
+ }
1289
+ // and only now, all of it
1290
+ for (const [box, style] of restyled)
1291
+ box.style = style;
1292
+ for (const [box, decoration] of redecorated)
1293
+ box.decoration = decoration;
1294
+ for (const text of texts) {
1295
+ const next = relaid.get(text.layout);
1296
+ if (next)
1297
+ text.layout = next;
1298
+ }
1299
+ for (const [el, style] of changed) {
1300
+ tree.styles.set(el, { style, inFlex: tree.styles.get(el).inFlex });
1301
+ }
1302
+ return 'painted';
1303
+ }
538
1304
  /** The document, for an application that wants to read or change it. */
539
1305
  get document() {
540
1306
  return this._source.document;
@@ -548,6 +1314,7 @@ export class HtmlViewNode extends Node {
548
1314
  */
549
1315
  touchDocument() {
550
1316
  this._source.touch();
1317
+ this._updateBase();
551
1318
  this._sweep();
552
1319
  this._invalidate(Stale.Style);
553
1320
  }
@@ -598,20 +1365,86 @@ export class HtmlViewNode extends Node {
598
1365
  damage = { x: 0, y: 0, width: window.width, height: window.height };
599
1366
  }
600
1367
  }
1368
+ try {
1369
+ this._paint(ctx, tree, range, damage);
1370
+ }
1371
+ catch (error) {
1372
+ this._fail(error, this._laidOutWidth);
1373
+ }
1374
+ }
1375
+ _paint(ctx, tree, range, damage) {
601
1376
  paintDocument(ctx, tree, {
602
1377
  originX: this.abs.x,
603
1378
  originY: this.abs.y,
1379
+ // the root's background covers the whole element, not only the
1380
+ // document: an element grown past its content is canvas too
1381
+ canvas: this.abs,
604
1382
  scale: this._scale,
605
1383
  damage,
606
1384
  selection: range
607
1385
  ? { start: this._toUnits(range.start), end: this._toUnits(range.end) }
608
1386
  : null,
609
1387
  selectionColor: this.selectionColor,
610
- imageFor: (box) => box.el ? this._resources.image(attr(box.el, 'src') ?? '') : null,
1388
+ imageFor: (box) => {
1389
+ const url = box.el ? imageUrlOf(box.el) : CONTENT_IMAGES.get(box);
1390
+ return url === undefined ? null : this._resources.image(url);
1391
+ },
1392
+ backgroundImageFor: (url) => {
1393
+ const image = this._resources.image(url);
1394
+ const size = image ? this._resources.imageSize(url) : null;
1395
+ return image && size ? { image, ...size } : null;
1396
+ },
1397
+ cached: (key, width, height, draw) => (this._shadowCache ??= new SurfaceCache(this.app)).get(key, width, height, draw),
611
1398
  });
612
1399
  }
613
1400
  }
614
1401
  const EMPTY_SET = new Set();
1402
+ /** The first box an element made, depth first. */
1403
+ function boxFor(root, element) {
1404
+ const stack = [root];
1405
+ while (stack.length) {
1406
+ const box = stack.pop();
1407
+ if (box.el === element)
1408
+ return box;
1409
+ for (let i = box.children.length - 1; i >= 0; i -= 1) {
1410
+ stack.push(box.children[i]);
1411
+ }
1412
+ }
1413
+ return null;
1414
+ }
1415
+ /** Where the document's text goes on after a box with none: the start of
1416
+ * the next text in document order, or the end of the last before it. */
1417
+ function textAfter(root, target) {
1418
+ const stack = [root];
1419
+ let passed = false;
1420
+ let before = 0;
1421
+ while (stack.length) {
1422
+ const box = stack.pop();
1423
+ if (box === target) {
1424
+ passed = true;
1425
+ continue;
1426
+ }
1427
+ if (box.kind === 'text' && box.textEnd > box.textStart) {
1428
+ if (passed)
1429
+ return box.textStart;
1430
+ before = box.textEnd;
1431
+ }
1432
+ for (let i = box.children.length - 1; i >= 0; i -= 1) {
1433
+ stack.push(box.children[i]);
1434
+ }
1435
+ }
1436
+ return before;
1437
+ }
1438
+ function unionRect(a, b) {
1439
+ const x = Math.min(a.x, b.x);
1440
+ const y = Math.min(a.y, b.y);
1441
+ return {
1442
+ x,
1443
+ y,
1444
+ width: Math.max(a.x + a.width, b.x + b.width) - x,
1445
+ height: Math.max(a.y + a.height, b.y + b.height) - y,
1446
+ };
1447
+ }
615
1448
  function textOf(el) {
616
1449
  let out = '';
617
1450
  for (const child of el.children) {
@@ -638,12 +1471,24 @@ function sameRects(a, b) {
638
1471
  p.x !== q.x ||
639
1472
  p.y !== q.y ||
640
1473
  p.width !== q.width ||
641
- p.height !== q.height) {
1474
+ p.height !== q.height ||
1475
+ !sameBare(p.bare, q.bare)) {
642
1476
  return false;
643
1477
  }
644
1478
  }
645
1479
  return true;
646
1480
  }
1481
+ function sameBare(a, b) {
1482
+ if (!a || !b)
1483
+ return a === b;
1484
+ return (a.x === b.x &&
1485
+ a.y === b.y &&
1486
+ a.width === b.width &&
1487
+ a.height === b.height &&
1488
+ a.color === b.color &&
1489
+ a.fontFamily === b.fontFamily &&
1490
+ a.fontSize === b.fontSize);
1491
+ }
647
1492
  // --- walks over the laid-out tree -------------------------------------------
648
1493
  /** Distance from a coordinate to an interval, zero inside it. */
649
1494
  function axisDistance(v, lo, span) {
@@ -683,7 +1528,7 @@ function nearestText(box, x, y) {
683
1528
  const units = offsets.length
684
1529
  ? offsets[Math.max(0, Math.min(local, offsets.length - 1))]
685
1530
  : local;
686
- best = text.textStart + Math.max(0, units - text.layoutStart);
1531
+ best = Math.max(text.textStart, documentOffsetOf(text, units));
687
1532
  }
688
1533
  };
689
1534
  const visit = (node) => {
@@ -797,7 +1642,7 @@ function caretAt(box, units) {
797
1642
  if (units < text.textStart || units > text.textEnd)
798
1643
  continue;
799
1644
  const offsets = layoutOffsetsOf(text.layout);
800
- const layoutUnits = text.layoutStart + (units - text.textStart);
1645
+ const layoutUnits = layoutOffsetOf(text, units);
801
1646
  const caret = text.layout.caretPosition(codePointAtOffset(offsets, layoutUnits));
802
1647
  found = { x: text.drawX + caret.x, y: line.y, height: line.height };
803
1648
  return;
@@ -818,6 +1663,60 @@ function caretAt(box, units) {
818
1663
  /** Every band a document range covers, in window coordinates. The subtree
819
1664
  * text ranges prune the walk to the boxes the range actually crosses, so a
820
1665
  * drag's small range costs its own paragraphs; only Ctrl+A pays for all. */
1666
+ /**
1667
+ * What an inline box reaches on each line besides its text, as `out`
1668
+ * rectangles as tall as the line: its own padding and border where it
1669
+ * starts or ends there — its edge less its margin, which is outside it —
1670
+ * and the inline-blocks and inline boxes' edges inside it, whole. The rest
1671
+ * of its border box across, which its text alone missed: a padded link was
1672
+ * as wide as its words (`elementRect`).
1673
+ */
1674
+ function fragmentReach(box, out) {
1675
+ let block = box.parent;
1676
+ while (block && !block.lines)
1677
+ block = block.parent;
1678
+ if (!block?.lines)
1679
+ return;
1680
+ const inside = (b) => {
1681
+ for (let at = b; at && at !== block; at = at.parent) {
1682
+ if (at === box)
1683
+ return true;
1684
+ }
1685
+ return false;
1686
+ };
1687
+ const rtl = box.style.direction === 'rtl';
1688
+ for (const line of block.lines) {
1689
+ let left = Infinity;
1690
+ let right = -Infinity;
1691
+ for (const edge of line.edges ?? []) {
1692
+ if (edge.box === box) {
1693
+ // the element's `direction` says which side its start is on, as
1694
+ // paint reads it (`paintInlineBoxes`)
1695
+ const onLeft = (edge.side === 'start') !== rtl;
1696
+ left = Math.min(left, onLeft ? edge.x + box.marginLeft : edge.x);
1697
+ right = Math.max(right, onLeft ? edge.x + edge.width : edge.x + edge.width - box.marginRight);
1698
+ }
1699
+ else if (inside(edge.box)) {
1700
+ left = Math.min(left, edge.x);
1701
+ right = Math.max(right, edge.x + edge.width);
1702
+ }
1703
+ }
1704
+ for (const placed of line.atomics) {
1705
+ if (!inside(placed.box))
1706
+ continue;
1707
+ left = Math.min(left, placed.x - placed.box.marginLeft);
1708
+ right = Math.max(right, placed.x + placed.box.width + placed.box.marginRight);
1709
+ }
1710
+ if (right > left) {
1711
+ out.push({
1712
+ x: left,
1713
+ y: line.y,
1714
+ width: right - left,
1715
+ height: line.height,
1716
+ });
1717
+ }
1718
+ }
1719
+ }
821
1720
  function collectBands(box, from, to, dx, dy, out) {
822
1721
  if (box.subtreeTextEnd <= box.subtreeTextStart)
823
1722
  return;
@@ -850,7 +1749,7 @@ function collectBands(box, from, to, dx, dy, out) {
850
1749
  if (b <= a)
851
1750
  continue;
852
1751
  const offsets = layoutOffsetsOf(text.layout);
853
- for (const band of bandsFor(text.layout, natural, offsets, text.layoutStart + (a - text.textStart), text.layoutStart + (b - text.textStart))) {
1752
+ for (const band of bandsFor(text.layout, natural, offsets, layoutOffsetOf(text, a), layoutOffsetOf(text, b, true))) {
854
1753
  out.push({
855
1754
  x: dx + band.x + text.drawX,
856
1755
  y: dy + line.y,
@@ -869,10 +1768,105 @@ function collectBands(box, from, to, dx, dy, out) {
869
1768
  collectBands(child, from, to, dx, dy, out);
870
1769
  }
871
1770
  }
1771
+ /** The layers of a hit test, in the order CSS paints them within a
1772
+ * context (`deepestAt`). */
1773
+ const HIT_NEGATIVE = -1;
1774
+ const HIT_BLOCK = 0;
1775
+ const HIT_FLOAT = 1;
1776
+ const HIT_INLINE = 2;
1777
+ const HIT_POSITIONED = 3;
872
1778
  /** The deepest element box containing a document-space point. */
873
- function deepestAt(box, x, y) {
1779
+ function deepestAt(tree, x, y,
1780
+ /** Told whether what was found was found under text: a run's, rather
1781
+ * than a box's. */
1782
+ hit) {
1783
+ const box = tree.root;
874
1784
  let found = box.el;
875
- const visit = (node) => {
1785
+ let foundKey = [];
1786
+ let viaText = false;
1787
+ // What is under a point is what was painted there last (CSS 2.1 Appendix
1788
+ // E): within a context, an in-flow block's own box, then a float over it,
1789
+ // then a line's text and atomics, then a positioned box — and a float, an
1790
+ // atomic or a positioned box paints its own content whole in its turn,
1791
+ // those layers again inside it. So a hit carries the layers down to it,
1792
+ // and takes the place of the one before where those come after them, or
1793
+ // tie: an infobox floated out of one section hangs over the next, whose
1794
+ // own box took every link in it, and a skin that puts the article in a
1795
+ // `position: relative` box put all of it in one layer. Positioned boxes
1796
+ // are painted in `z-index` order and then the document's (`byZIndex`),
1797
+ // the negative ones under the flow: the Zen Garden's `›` has
1798
+ // `z-index: 3` over the bar the "View All Designs" link fills after it.
1799
+ const take = (el, key, text) => {
1800
+ if (compareKeys(key, foundKey) < 0)
1801
+ return;
1802
+ found = el;
1803
+ foundKey = key;
1804
+ viaText = text;
1805
+ };
1806
+ // A box is walked into wherever it draws — its reach, overflow and all
1807
+ // (`computePaintBounds`) — and named only where its own rectangle is. A
1808
+ // page's `html, body { height: 100% }` is one viewport tall and its
1809
+ // article overflows it, and a walk that went no further than a box's own
1810
+ // rectangle found nothing below the first screen: once Wikipedia was
1811
+ // scrolled, no link lit up. One whose reach is not known yet — before its
1812
+ // first paint — is walked into at its rectangle, as before.
1813
+ //
1814
+ // A box that clips what overflows it hides what it holds past its edge —
1815
+ // but not the positioned boxes whose containing block is outside it (CSS
1816
+ // 2.1 11.1.1), which paint puts off until its clip ends
1817
+ // (`paintPositioned`). So past the edge the walk goes on only into a box
1818
+ // that holds positioned ones, carrying the clips the point is outside
1819
+ // of, and names nothing until a positioned box escapes them all: the
1820
+ // Zen Garden's archive links are absolute `<li>`s in an `overflow:
1821
+ // hidden` list with no height of its own, and not one of them could be
1822
+ // hovered or pressed.
1823
+ const enter = (child, context, clipped) => {
1824
+ const style = child.style;
1825
+ if (style.position !== 'static' ||
1826
+ (child.parent?.kind === 'flex' && typeof style.zIndex === 'number')) {
1827
+ // a flex item with a `z-index` is layered unpositioned (`layered`)
1828
+ const z = style.zIndex === 'auto' ? 0 : style.zIndex;
1829
+ context = [...context, z < 0 ? HIT_NEGATIVE : HIT_POSITIONED, z];
1830
+ }
1831
+ else if (style.float !== 'none')
1832
+ context = [...context, HIT_FLOAT];
1833
+ if (clipped.length !== 0 && child.outOfFlow) {
1834
+ const containing = containingBlockOf(child);
1835
+ clipped = containing
1836
+ ? clipped.filter((clip) => holds(clip, containing))
1837
+ : [];
1838
+ }
1839
+ const inside = x >= child.x &&
1840
+ x < child.x + child.width &&
1841
+ y >= child.y &&
1842
+ y < child.y + child.height;
1843
+ const own = inside && hasRect(child);
1844
+ if (!own) {
1845
+ const reach = child.boundsY;
1846
+ const known = Number.isFinite(reach) &&
1847
+ (child.boundsWidth > 0 || child.boundsHeight > 0);
1848
+ if (!known) {
1849
+ if (!inside)
1850
+ return;
1851
+ }
1852
+ else if (x < child.boundsX ||
1853
+ x >= child.boundsX + child.boundsWidth ||
1854
+ y < reach ||
1855
+ y >= reach + child.boundsHeight) {
1856
+ return;
1857
+ }
1858
+ if (clipsOverflow(child)) {
1859
+ if (!holdsAbsolute(child))
1860
+ return;
1861
+ clipped = [...clipped, child];
1862
+ }
1863
+ }
1864
+ if (own && child.el && clipped.length === 0) {
1865
+ take(child.el, [...context, HIT_BLOCK], false);
1866
+ }
1867
+ visit(child, context, clipped);
1868
+ };
1869
+ const visit = (node, context, clipped) => {
876
1870
  // The paint index answers a point query too — the wide level of a flat
877
1871
  // document is the root's child list, and a hit test that walked all of
878
1872
  // it would run per pointer move once hover is in the picture.
@@ -882,43 +1876,26 @@ function deepestAt(box, x, y) {
882
1876
  for (const child of candidates) {
883
1877
  if (child.kind === 'text' || child.kind === 'break')
884
1878
  continue;
885
- if (x >= child.x &&
886
- x < child.x + child.width &&
887
- y >= child.y &&
888
- y < child.y + child.height) {
889
- if (child.el)
890
- found = child.el;
891
- visit(child);
892
- }
1879
+ enter(child, context, clipped);
893
1880
  }
894
1881
  if (node.paintIndex && node.positionedPaint) {
895
1882
  for (const child of node.positionedPaint) {
896
- if (x >= child.x &&
897
- x < child.x + child.width &&
898
- y >= child.y &&
899
- y < child.y + child.height) {
900
- if (child.el)
901
- found = child.el;
902
- visit(child);
903
- }
1883
+ enter(child, context, clipped);
904
1884
  }
905
1885
  }
906
1886
  if (node.lines) {
907
1887
  for (const line of node.lines) {
908
1888
  for (const placed of line.atomics) {
909
- if (x >= placed.box.x &&
910
- x < placed.box.x + placed.box.width &&
911
- y >= placed.box.y &&
912
- y < placed.box.y + placed.box.height) {
913
- if (placed.box.el)
914
- found = placed.box.el;
915
- visit(placed.box);
916
- }
1889
+ enter(placed.box, [...context, HIT_INLINE], clipped);
917
1890
  }
918
1891
  // An inline box has no box of its own — its extent is the runs on
919
1892
  // this line — so the element under a point inside a paragraph is
920
- // found from the run rather than from a rectangle.
921
- if (y >= line.y && y < line.y + line.height) {
1893
+ // found from the run rather than from a rectangle: from where its
1894
+ // text sits in the document, whose text boxes know their element.
1895
+ // Not from the run itself, whose layout may be one an earlier parse
1896
+ // made (`TextLayoutCache`), and which an engine may hand back with
1897
+ // nothing on it but its extent.
1898
+ if (clipped.length === 0 && y >= line.y && y < line.y + line.height) {
922
1899
  for (const text of line.texts) {
923
1900
  const natural = text.layout.lines[text.layoutLine];
924
1901
  if (!natural)
@@ -926,10 +1903,9 @@ function deepestAt(box, x, y) {
926
1903
  for (const run of natural.runs) {
927
1904
  const left = text.drawX + natural.x + run.x;
928
1905
  if (x >= left && x < left + run.width) {
929
- const owner = run.span
930
- ?.element;
1906
+ const owner = ownerOf(tree.textBoxes, text.spans.documentAt(run.start));
931
1907
  if (owner)
932
- found = owner;
1908
+ take(owner, [...context, HIT_INLINE], true);
933
1909
  }
934
1910
  }
935
1911
  }
@@ -937,7 +1913,264 @@ function deepestAt(box, x, y) {
937
1913
  }
938
1914
  }
939
1915
  };
940
- visit(box);
1916
+ visit(box, [], []);
1917
+ if (hit)
1918
+ hit.text = viaText;
941
1919
  return found;
942
1920
  }
1921
+ /** Paint order between two hits' layers, outermost first: negative where
1922
+ * `a` was painted under `b`. */
1923
+ function compareKeys(a, b) {
1924
+ const n = Math.min(a.length, b.length);
1925
+ for (let i = 0; i < n; i += 1)
1926
+ if (a[i] !== b[i])
1927
+ return a[i] - b[i];
1928
+ return a.length - b.length;
1929
+ }
1930
+ // --- a pointer move restyled where it happened (`_hoverInPlace`) -----------
1931
+ /** How many elements a pointer move restyles in place before the document
1932
+ * is built again instead: a link's subtree is a handful, and a compound
1933
+ * that matches a container reaches everything in it. */
1934
+ const HOVER_RESTYLE_LIMIT = 300;
1935
+ /**
1936
+ * The computed properties a pointer move may change in place: ink, which
1937
+ * moves nothing. A background colour is drawn inside the box it colours
1938
+ * and a border's colour on the border it has; `box-shadow`, `text-shadow`
1939
+ * and an outline's width reach past it, where the paint index has already
1940
+ * looked, and a background image has to be fetched.
1941
+ */
1942
+ const PAINT_ONLY = new Set([
1943
+ 'color',
1944
+ 'textFillColor',
1945
+ 'cursor',
1946
+ 'backgroundColor',
1947
+ 'borderTopColor',
1948
+ 'borderRightColor',
1949
+ 'borderBottomColor',
1950
+ 'borderLeftColor',
1951
+ 'outlineColor',
1952
+ 'textDecorationLine',
1953
+ 'textDecorationColor',
1954
+ 'textDecorationStyle',
1955
+ 'textDecorationThickness',
1956
+ 'textUnderlineOffset',
1957
+ 'underline',
1958
+ 'underlineStyle',
1959
+ 'underlineThickness',
1960
+ 'underlineOffset',
1961
+ 'lineThrough',
1962
+ ]);
1963
+ /** The fields of a run its ink is: what `runFor` takes from `PAINT_ONLY`. */
1964
+ const INK_FIELDS = [
1965
+ 'color',
1966
+ 'underline',
1967
+ 'underlineStyle',
1968
+ 'underlineOffset',
1969
+ 'underlineThickness',
1970
+ 'strike',
1971
+ ];
1972
+ /** The fields of a run its shape is, which ink never changes. */
1973
+ const FACE_FIELDS = [
1974
+ 'family',
1975
+ 'size',
1976
+ 'weight',
1977
+ 'style',
1978
+ 'letterSpacing',
1979
+ 'features',
1980
+ ];
1981
+ /** Whether two styles differ in ink alone: true where they do, null where
1982
+ * they do not differ, false where something else does. Custom properties
1983
+ * are read through the properties that use them. */
1984
+ function inkOnly(was, now) {
1985
+ const a = was;
1986
+ const b = now;
1987
+ let ink = false;
1988
+ for (const key in b) {
1989
+ if (key === 'custom' || sameValue(a[key], b[key]))
1990
+ continue;
1991
+ if (!PAINT_ONLY.has(key))
1992
+ return false;
1993
+ ink = true;
1994
+ }
1995
+ return ink ? true : null;
1996
+ }
1997
+ /** Structural equality for a computed value: a number, a string, or the
1998
+ * plain objects and arrays a length or a shadow list is. */
1999
+ function sameValue(x, y) {
2000
+ if (x === y)
2001
+ return true;
2002
+ if (typeof x === 'number' && typeof y === 'number') {
2003
+ return Number.isNaN(x) && Number.isNaN(y);
2004
+ }
2005
+ if (!x || !y || typeof x !== 'object' || typeof y !== 'object')
2006
+ return false;
2007
+ if (Array.isArray(x) !== Array.isArray(y))
2008
+ return false;
2009
+ const a = x;
2010
+ const b = y;
2011
+ const keys = Object.keys(a);
2012
+ if (keys.length !== Object.keys(b).length)
2013
+ return false;
2014
+ for (const key of keys)
2015
+ if (!sameValue(a[key], b[key]))
2016
+ return false;
2017
+ return true;
2018
+ }
2019
+ /** The element an anonymous box takes its style from: its nearest
2020
+ * ancestor's that has one. */
2021
+ function nearestElement(box) {
2022
+ for (let at = box.parent; at; at = at.parent)
2023
+ if (at.el)
2024
+ return at.el;
2025
+ return null;
2026
+ }
2027
+ /**
2028
+ * `text`'s layout made again with the ink of the elements a pointer move
2029
+ * restyled: from the runs it was made from, each of a changed element's
2030
+ * given the ink its new style makes (`runFor`) and nothing else. Null where
2031
+ * none of its runs is theirs; false where that cannot be told — a run this
2032
+ * cannot place in the document, one some other pass inked (a first line's),
2033
+ * or a layout that comes out another shape.
2034
+ */
2035
+ function reinked(text, layouts, changed, styles) {
2036
+ const spans = text.spans;
2037
+ const ownerAt = (offset) => spans.boxAt ? (spans.boxAt(offset)?.el ?? null) : null;
2038
+ const inputs = layouts.inputsOf(text.layout);
2039
+ if (!inputs || !spans.boxAt) {
2040
+ // not one this could make again: none of its text may be theirs
2041
+ for (const line of text.layout.lines) {
2042
+ for (const run of line.runs) {
2043
+ const owner = spans.boxAt ? ownerAt(run.start) : null;
2044
+ if (!spans.boxAt || (owner && changed.has(owner)))
2045
+ return false;
2046
+ }
2047
+ }
2048
+ return null;
2049
+ }
2050
+ let runs = null;
2051
+ let offset = 0;
2052
+ const content = inputs.content;
2053
+ for (let i = 0; i < content.length; i += 1) {
2054
+ const run = content[i];
2055
+ const length = run.text.length;
2056
+ const owner = length ? ownerAt(offset) : null;
2057
+ const style = owner ? changed.get(owner) : undefined;
2058
+ if (owner && style) {
2059
+ if (ownerAt(offset + length - 1) !== owner)
2060
+ return false;
2061
+ const was = runFor(run.text, styles.get(owner).style);
2062
+ const now = runFor(run.text, style);
2063
+ for (const f of FACE_FIELDS)
2064
+ if (!sameValue(was[f], now[f]))
2065
+ return false;
2066
+ let next = null;
2067
+ for (const f of INK_FIELDS) {
2068
+ // a run that is not what its style made — a first line's colour
2069
+ if (!sameValue(run[f], was[f]))
2070
+ return false;
2071
+ if (sameValue(was[f], now[f]))
2072
+ continue;
2073
+ next ??= { ...run };
2074
+ if (now[f] === undefined)
2075
+ delete next[f];
2076
+ else
2077
+ next[f] = now[f];
2078
+ }
2079
+ if (next) {
2080
+ runs ??= content.slice();
2081
+ runs[i] = next;
2082
+ }
2083
+ }
2084
+ offset += length;
2085
+ }
2086
+ if (!runs)
2087
+ return null;
2088
+ const layout = layouts.fonts.layout(runs, inputs.style, inputs.options);
2089
+ return sameShape(text.layout, layout) ? layout : false;
2090
+ }
2091
+ /** Whether two layouts put the same text in the same places: ink aside,
2092
+ * the same layout. */
2093
+ function sameShape(a, b) {
2094
+ if (a.width !== b.width || a.height !== b.height)
2095
+ return false;
2096
+ if (a.lines.length !== b.lines.length)
2097
+ return false;
2098
+ for (let i = 0; i < a.lines.length; i += 1) {
2099
+ const x = a.lines[i];
2100
+ const y = b.lines[i];
2101
+ if (x.x !== y.x ||
2102
+ x.y !== y.y ||
2103
+ x.width !== y.width ||
2104
+ x.height !== y.height ||
2105
+ x.baseline !== y.baseline ||
2106
+ x.ascent !== y.ascent ||
2107
+ x.descent !== y.descent ||
2108
+ x.start !== y.start ||
2109
+ x.end !== y.end ||
2110
+ x.runs.length !== y.runs.length) {
2111
+ return false;
2112
+ }
2113
+ for (let j = 0; j < x.runs.length; j += 1) {
2114
+ const r = x.runs[j];
2115
+ const t = y.runs[j];
2116
+ if (r.x !== t.x ||
2117
+ r.width !== t.width ||
2118
+ r.start !== t.start ||
2119
+ r.end !== t.end) {
2120
+ return false;
2121
+ }
2122
+ }
2123
+ }
2124
+ return true;
2125
+ }
2126
+ /** The element whose text holds a document index: the text box around it,
2127
+ * found by bisecting the boxes in document order. */
2128
+ function ownerOf(boxes, index) {
2129
+ let lo = 0;
2130
+ let hi = boxes.length - 1;
2131
+ while (lo < hi) {
2132
+ const mid = (lo + hi + 1) >> 1;
2133
+ if (boxes[mid].textStart <= index)
2134
+ lo = mid;
2135
+ else
2136
+ hi = mid - 1;
2137
+ }
2138
+ const box = boxes[lo];
2139
+ return box && index >= box.textStart && index < box.textEnd ? box.el : null;
2140
+ }
2141
+ /** The advance of a font's "0" at a size, laid out: null where the engine
2142
+ * cannot lay it out. */
2143
+ function zeroWidthOf(fonts, family, size) {
2144
+ try {
2145
+ return fonts.layout([{ text: '0', family, size }], { family, size }, {})
2146
+ .width;
2147
+ }
2148
+ catch {
2149
+ return null;
2150
+ }
2151
+ }
2152
+ /** A font's own line height at a size, as the engine reports it: null
2153
+ * where it cannot say. */
2154
+ function normalLineOf(fonts, family, size) {
2155
+ try {
2156
+ const line = fonts.match(family, { size }).metrics(size).lineHeight;
2157
+ return line > 0 ? line : null;
2158
+ }
2159
+ catch {
2160
+ return null;
2161
+ }
2162
+ }
2163
+ /** A font's x-height at a size, as the engine reports it: null where the
2164
+ * face states none, or the engine does not say. */
2165
+ function xHeightOf(fonts, family, size) {
2166
+ try {
2167
+ const metrics = fonts.match(family, { size }).metrics(size);
2168
+ const x = metrics.xHeight;
2169
+ // NaN where the face states none, which is a number
2170
+ return typeof x === 'number' && x > 0 ? x : null;
2171
+ }
2172
+ catch {
2173
+ return null;
2174
+ }
2175
+ }
943
2176
  //# sourceMappingURL=node.js.map