@react-x11/components 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (579) hide show
  1. package/README.md +10 -4
  2. package/dist/charts/node.d.ts +11 -1
  3. package/dist/charts/node.d.ts.map +1 -1
  4. package/dist/charts/node.js +44 -14
  5. package/dist/charts/node.js.map +1 -1
  6. package/dist/charts/render.d.ts +12 -3
  7. package/dist/charts/render.d.ts.map +1 -1
  8. package/dist/charts/render.js +94 -25
  9. package/dist/charts/render.js.map +1 -1
  10. package/dist/code/index.d.ts.map +1 -1
  11. package/dist/code/index.js +27 -24
  12. package/dist/code/index.js.map +1 -1
  13. package/dist/code-editor/completion.d.ts.map +1 -1
  14. package/dist/code-editor/completion.js +3 -2
  15. package/dist/code-editor/completion.js.map +1 -1
  16. package/dist/code-editor/index.d.ts.map +1 -1
  17. package/dist/code-editor/index.js +19 -0
  18. package/dist/code-editor/index.js.map +1 -1
  19. package/dist/code-editor/node.d.ts +177 -7
  20. package/dist/code-editor/node.d.ts.map +1 -1
  21. package/dist/code-editor/node.js +1329 -164
  22. package/dist/code-editor/node.js.map +1 -1
  23. package/dist/code-language/index.d.ts +1 -1
  24. package/dist/code-language/index.d.ts.map +1 -1
  25. package/dist/code-language/index.js +1 -1
  26. package/dist/code-language/index.js.map +1 -1
  27. package/dist/code-language/languages/glsl.d.ts.map +1 -1
  28. package/dist/code-language/languages/glsl.js +10 -2
  29. package/dist/code-language/languages/glsl.js.map +1 -1
  30. package/dist/code-language/languages/javascript.d.ts.map +1 -1
  31. package/dist/code-language/languages/javascript.js +18 -3
  32. package/dist/code-language/languages/javascript.js.map +1 -1
  33. package/dist/code-language/languages/json.d.ts.map +1 -1
  34. package/dist/code-language/languages/json.js +10 -2
  35. package/dist/code-language/languages/json.js.map +1 -1
  36. package/dist/code-language/languages/shell.d.ts.map +1 -1
  37. package/dist/code-language/languages/shell.js +10 -2
  38. package/dist/code-language/languages/shell.js.map +1 -1
  39. package/dist/code-language/languages/sql.d.ts.map +1 -1
  40. package/dist/code-language/languages/sql.js +20 -2
  41. package/dist/code-language/languages/sql.js.map +1 -1
  42. package/dist/code-language/runs.d.ts +31 -0
  43. package/dist/code-language/runs.d.ts.map +1 -1
  44. package/dist/code-language/runs.js +105 -0
  45. package/dist/code-language/runs.js.map +1 -1
  46. package/dist/code-language/stream.d.ts +22 -0
  47. package/dist/code-language/stream.d.ts.map +1 -1
  48. package/dist/code-language/stream.js +259 -9
  49. package/dist/code-language/stream.js.map +1 -1
  50. package/dist/code-language/types.d.ts +5 -4
  51. package/dist/code-language/types.d.ts.map +1 -1
  52. package/dist/codeblock/index.d.ts +26 -2
  53. package/dist/codeblock/index.d.ts.map +1 -1
  54. package/dist/codeblock/index.js +57 -4
  55. package/dist/codeblock/index.js.map +1 -1
  56. package/dist/flow/draw.d.ts +50 -0
  57. package/dist/flow/draw.d.ts.map +1 -1
  58. package/dist/flow/draw.js +132 -33
  59. package/dist/flow/draw.js.map +1 -1
  60. package/dist/flow/gl/color.d.ts +13 -0
  61. package/dist/flow/gl/color.d.ts.map +1 -0
  62. package/dist/flow/gl/color.js +82 -0
  63. package/dist/flow/gl/color.js.map +1 -0
  64. package/dist/flow/gl/index.d.ts +60 -0
  65. package/dist/flow/gl/index.d.ts.map +1 -0
  66. package/dist/flow/gl/index.js +283 -0
  67. package/dist/flow/gl/index.js.map +1 -0
  68. package/dist/flow/gl/pack.d.ts +133 -0
  69. package/dist/flow/gl/pack.d.ts.map +1 -0
  70. package/dist/flow/gl/pack.js +408 -0
  71. package/dist/flow/gl/pack.js.map +1 -0
  72. package/dist/flow/gl/renderer.d.ts +95 -0
  73. package/dist/flow/gl/renderer.d.ts.map +1 -0
  74. package/dist/flow/gl/renderer.js +459 -0
  75. package/dist/flow/gl/renderer.js.map +1 -0
  76. package/dist/flow/gl/shaders.d.ts +79 -0
  77. package/dist/flow/gl/shaders.d.ts.map +1 -0
  78. package/dist/flow/gl/shaders.js +346 -0
  79. package/dist/flow/gl/shaders.js.map +1 -0
  80. package/dist/flow/gl/text.d.ts +318 -0
  81. package/dist/flow/gl/text.d.ts.map +1 -0
  82. package/dist/flow/gl/text.js +1103 -0
  83. package/dist/flow/gl/text.js.map +1 -0
  84. package/dist/flow/index.d.ts +1 -1
  85. package/dist/flow/index.d.ts.map +1 -1
  86. package/dist/flow/index.js +619 -64
  87. package/dist/flow/index.js.map +1 -1
  88. package/dist/flow/node.d.ts +621 -72
  89. package/dist/flow/node.d.ts.map +1 -1
  90. package/dist/flow/node.js +2253 -1018
  91. package/dist/flow/node.js.map +1 -1
  92. package/dist/flow/paint.d.ts +35 -0
  93. package/dist/flow/paint.d.ts.map +1 -0
  94. package/dist/flow/paint.js +393 -0
  95. package/dist/flow/paint.js.map +1 -0
  96. package/dist/flow/paths.d.ts +14 -0
  97. package/dist/flow/paths.d.ts.map +1 -1
  98. package/dist/flow/paths.js +45 -19
  99. package/dist/flow/paths.js.map +1 -1
  100. package/dist/flow/scene.d.ts +584 -0
  101. package/dist/flow/scene.d.ts.map +1 -0
  102. package/dist/flow/scene.js +1453 -0
  103. package/dist/flow/scene.js.map +1 -0
  104. package/dist/flow/types.d.ts +111 -1
  105. package/dist/flow/types.d.ts.map +1 -1
  106. package/dist/html/controls.d.ts +28 -0
  107. package/dist/html/controls.d.ts.map +1 -1
  108. package/dist/html/controls.js +52 -2
  109. package/dist/html/controls.js.map +1 -1
  110. package/dist/html/css/calc.d.ts +14 -0
  111. package/dist/html/css/calc.d.ts.map +1 -0
  112. package/dist/html/css/calc.js +344 -0
  113. package/dist/html/css/calc.js.map +1 -0
  114. package/dist/html/css/cascade.d.ts +262 -6
  115. package/dist/html/css/cascade.d.ts.map +1 -1
  116. package/dist/html/css/cascade.js +1373 -67
  117. package/dist/html/css/cascade.js.map +1 -1
  118. package/dist/html/css/color.d.ts +23 -0
  119. package/dist/html/css/color.d.ts.map +1 -0
  120. package/dist/html/css/color.js +740 -0
  121. package/dist/html/css/color.js.map +1 -0
  122. package/dist/html/css/content.d.ts +92 -0
  123. package/dist/html/css/content.d.ts.map +1 -0
  124. package/dist/html/css/content.js +388 -0
  125. package/dist/html/css/content.js.map +1 -0
  126. package/dist/html/css/counter-styles.d.ts +76 -0
  127. package/dist/html/css/counter-styles.d.ts.map +1 -0
  128. package/dist/html/css/counter-styles.js +893 -0
  129. package/dist/html/css/counter-styles.js.map +1 -0
  130. package/dist/html/css/decode.d.ts +12 -0
  131. package/dist/html/css/decode.d.ts.map +1 -0
  132. package/dist/html/css/decode.js +116 -0
  133. package/dist/html/css/decode.js.map +1 -0
  134. package/dist/html/css/parse.d.ts +110 -10
  135. package/dist/html/css/parse.d.ts.map +1 -1
  136. package/dist/html/css/parse.js +1473 -180
  137. package/dist/html/css/parse.js.map +1 -1
  138. package/dist/html/css/style.d.ts +501 -15
  139. package/dist/html/css/style.d.ts.map +1 -1
  140. package/dist/html/css/style.js +3729 -206
  141. package/dist/html/css/style.js.map +1 -1
  142. package/dist/html/css/ua.d.ts.map +1 -1
  143. package/dist/html/css/ua.js +65 -16
  144. package/dist/html/css/ua.js.map +1 -1
  145. package/dist/html/css/values.d.ts +37 -3
  146. package/dist/html/css/values.d.ts.map +1 -1
  147. package/dist/html/css/values.js +122 -48
  148. package/dist/html/css/values.js.map +1 -1
  149. package/dist/html/css/vars.d.ts +52 -0
  150. package/dist/html/css/vars.d.ts.map +1 -0
  151. package/dist/html/css/vars.js +358 -0
  152. package/dist/html/css/vars.js.map +1 -0
  153. package/dist/html/dom.d.ts +30 -4
  154. package/dist/html/dom.d.ts.map +1 -1
  155. package/dist/html/dom.js +278 -17
  156. package/dist/html/dom.js.map +1 -1
  157. package/dist/html/fonts.d.ts +106 -0
  158. package/dist/html/fonts.d.ts.map +1 -0
  159. package/dist/html/fonts.js +565 -0
  160. package/dist/html/fonts.js.map +1 -0
  161. package/dist/html/gif.d.ts +15 -0
  162. package/dist/html/gif.d.ts.map +1 -0
  163. package/dist/html/gif.js +229 -0
  164. package/dist/html/gif.js.map +1 -0
  165. package/dist/html/index.d.ts +50 -11
  166. package/dist/html/index.d.ts.map +1 -1
  167. package/dist/html/index.js +115 -9
  168. package/dist/html/index.js.map +1 -1
  169. package/dist/html/layout/block.d.ts +226 -3
  170. package/dist/html/layout/block.d.ts.map +1 -1
  171. package/dist/html/layout/block.js +3077 -219
  172. package/dist/html/layout/block.js.map +1 -1
  173. package/dist/html/layout/boxes.d.ts +367 -17
  174. package/dist/html/layout/boxes.d.ts.map +1 -1
  175. package/dist/html/layout/boxes.js +1742 -182
  176. package/dist/html/layout/boxes.js.map +1 -1
  177. package/dist/html/layout/cache.d.ts +58 -0
  178. package/dist/html/layout/cache.d.ts.map +1 -0
  179. package/dist/html/layout/cache.js +224 -0
  180. package/dist/html/layout/cache.js.map +1 -0
  181. package/dist/html/layout/collapse.d.ts +45 -0
  182. package/dist/html/layout/collapse.d.ts.map +1 -0
  183. package/dist/html/layout/collapse.js +246 -0
  184. package/dist/html/layout/collapse.js.map +1 -0
  185. package/dist/html/layout/css-grid.d.ts +7 -0
  186. package/dist/html/layout/css-grid.d.ts.map +1 -0
  187. package/dist/html/layout/css-grid.js +900 -0
  188. package/dist/html/layout/css-grid.js.map +1 -0
  189. package/dist/html/layout/flex.d.ts.map +1 -1
  190. package/dist/html/layout/flex.js +1025 -89
  191. package/dist/html/layout/flex.js.map +1 -1
  192. package/dist/html/layout/floats.d.ts +45 -6
  193. package/dist/html/layout/floats.d.ts.map +1 -1
  194. package/dist/html/layout/floats.js +113 -14
  195. package/dist/html/layout/floats.js.map +1 -1
  196. package/dist/html/layout/grid-lines.d.ts +39 -0
  197. package/dist/html/layout/grid-lines.d.ts.map +1 -0
  198. package/dist/html/layout/grid-lines.js +119 -0
  199. package/dist/html/layout/grid-lines.js.map +1 -0
  200. package/dist/html/layout/grid.d.ts +53 -0
  201. package/dist/html/layout/grid.d.ts.map +1 -0
  202. package/dist/html/layout/grid.js +153 -0
  203. package/dist/html/layout/grid.js.map +1 -0
  204. package/dist/html/layout/inline.d.ts +154 -1
  205. package/dist/html/layout/inline.d.ts.map +1 -1
  206. package/dist/html/layout/inline.js +3598 -175
  207. package/dist/html/layout/inline.js.map +1 -1
  208. package/dist/html/layout/shaping.d.ts +6 -0
  209. package/dist/html/layout/shaping.d.ts.map +1 -0
  210. package/dist/html/layout/shaping.js +100 -0
  211. package/dist/html/layout/shaping.js.map +1 -0
  212. package/dist/html/layout/table.d.ts +3 -0
  213. package/dist/html/layout/table.d.ts.map +1 -1
  214. package/dist/html/layout/table.js +681 -175
  215. package/dist/html/layout/table.js.map +1 -1
  216. package/dist/html/layout/tracks.d.ts +44 -0
  217. package/dist/html/layout/tracks.d.ts.map +1 -0
  218. package/dist/html/layout/tracks.js +349 -0
  219. package/dist/html/layout/tracks.js.map +1 -0
  220. package/dist/html/node.d.ts +169 -4
  221. package/dist/html/node.d.ts.map +1 -1
  222. package/dist/html/node.js +1429 -137
  223. package/dist/html/node.js.map +1 -1
  224. package/dist/html/paint.d.ts +92 -3
  225. package/dist/html/paint.d.ts.map +1 -1
  226. package/dist/html/paint.js +3635 -107
  227. package/dist/html/paint.js.map +1 -1
  228. package/dist/html/resources.d.ts +60 -14
  229. package/dist/html/resources.d.ts.map +1 -1
  230. package/dist/html/resources.js +122 -36
  231. package/dist/html/resources.js.map +1 -1
  232. package/dist/html/surfaces.d.ts +23 -0
  233. package/dist/html/surfaces.d.ts.map +1 -0
  234. package/dist/html/surfaces.js +68 -0
  235. package/dist/html/surfaces.js.map +1 -0
  236. package/dist/html/svg.d.ts +80 -0
  237. package/dist/html/svg.d.ts.map +1 -0
  238. package/dist/html/svg.js +442 -0
  239. package/dist/html/svg.js.map +1 -0
  240. package/dist/html/url.d.ts +23 -0
  241. package/dist/html/url.d.ts.map +1 -0
  242. package/dist/html/url.js +80 -0
  243. package/dist/html/url.js.map +1 -0
  244. package/dist/index.d.ts +1 -1
  245. package/dist/index.d.ts.map +1 -1
  246. package/dist/index.js.map +1 -1
  247. package/dist/internal/codelines.d.ts +42 -0
  248. package/dist/internal/codelines.d.ts.map +1 -0
  249. package/dist/internal/codelines.js +170 -0
  250. package/dist/internal/codelines.js.map +1 -0
  251. package/dist/internal/collate.d.ts +3 -0
  252. package/dist/internal/collate.d.ts.map +1 -0
  253. package/dist/internal/collate.js +11 -0
  254. package/dist/internal/collate.js.map +1 -0
  255. package/dist/internal/markdown/ast.d.ts +11 -0
  256. package/dist/internal/markdown/ast.d.ts.map +1 -1
  257. package/dist/internal/markdown/parse.d.ts.map +1 -1
  258. package/dist/internal/markdown/parse.js +109 -8
  259. package/dist/internal/markdown/parse.js.map +1 -1
  260. package/dist/internal/prewarm.d.ts +18 -0
  261. package/dist/internal/prewarm.d.ts.map +1 -0
  262. package/dist/internal/prewarm.js +42 -0
  263. package/dist/internal/prewarm.js.map +1 -0
  264. package/dist/internal/sdf.d.ts +20 -0
  265. package/dist/internal/sdf.d.ts.map +1 -0
  266. package/dist/internal/sdf.js +216 -0
  267. package/dist/internal/sdf.js.map +1 -0
  268. package/dist/internal/splice.d.ts +12 -0
  269. package/dist/internal/splice.d.ts.map +1 -0
  270. package/dist/internal/splice.js +23 -0
  271. package/dist/internal/splice.js.map +1 -0
  272. package/dist/internal/window.d.ts +12 -0
  273. package/dist/internal/window.d.ts.map +1 -1
  274. package/dist/internal/window.js +23 -6
  275. package/dist/internal/window.js.map +1 -1
  276. package/dist/maps/anchors.d.ts +6 -1
  277. package/dist/maps/anchors.d.ts.map +1 -1
  278. package/dist/maps/anchors.js +260 -1
  279. package/dist/maps/anchors.js.map +1 -1
  280. package/dist/maps/controller.d.ts +57 -0
  281. package/dist/maps/controller.d.ts.map +1 -1
  282. package/dist/maps/controller.js +97 -18
  283. package/dist/maps/controller.js.map +1 -1
  284. package/dist/maps/gl/buckets.d.ts +5 -1
  285. package/dist/maps/gl/buckets.d.ts.map +1 -1
  286. package/dist/maps/gl/buckets.js +5 -2
  287. package/dist/maps/gl/buckets.js.map +1 -1
  288. package/dist/maps/gl/build-worker.d.ts +2 -0
  289. package/dist/maps/gl/build-worker.d.ts.map +1 -1
  290. package/dist/maps/gl/build-worker.js +1 -1
  291. package/dist/maps/gl/build-worker.js.map +1 -1
  292. package/dist/maps/gl/cover.d.ts +23 -4
  293. package/dist/maps/gl/cover.d.ts.map +1 -1
  294. package/dist/maps/gl/cover.js +61 -48
  295. package/dist/maps/gl/cover.js.map +1 -1
  296. package/dist/maps/gl/placement.d.ts +21 -8
  297. package/dist/maps/gl/placement.d.ts.map +1 -1
  298. package/dist/maps/gl/placement.js +133 -58
  299. package/dist/maps/gl/placement.js.map +1 -1
  300. package/dist/maps/gl/renderer.d.ts +36 -4
  301. package/dist/maps/gl/renderer.d.ts.map +1 -1
  302. package/dist/maps/gl/renderer.js +81 -24
  303. package/dist/maps/gl/renderer.js.map +1 -1
  304. package/dist/maps/gl/shaders.d.ts +18 -17
  305. package/dist/maps/gl/shaders.d.ts.map +1 -1
  306. package/dist/maps/gl/shaders.js +34 -32
  307. package/dist/maps/gl/shaders.js.map +1 -1
  308. package/dist/maps/gl/store.d.ts +10 -0
  309. package/dist/maps/gl/store.d.ts.map +1 -1
  310. package/dist/maps/gl/store.js +47 -4
  311. package/dist/maps/gl/store.js.map +1 -1
  312. package/dist/maps/gl/text.d.ts +153 -24
  313. package/dist/maps/gl/text.d.ts.map +1 -1
  314. package/dist/maps/gl/text.js +379 -63
  315. package/dist/maps/gl/text.js.map +1 -1
  316. package/dist/maps/gl/view.d.ts +293 -2
  317. package/dist/maps/gl/view.d.ts.map +1 -1
  318. package/dist/maps/gl/view.js +317 -82
  319. package/dist/maps/gl/view.js.map +1 -1
  320. package/dist/maps/icons.d.ts +32 -0
  321. package/dist/maps/icons.d.ts.map +1 -0
  322. package/dist/maps/icons.js +151 -0
  323. package/dist/maps/icons.js.map +1 -0
  324. package/dist/maps/index.d.ts +2 -0
  325. package/dist/maps/index.d.ts.map +1 -1
  326. package/dist/maps/index.js +2 -0
  327. package/dist/maps/index.js.map +1 -1
  328. package/dist/maps/labels.d.ts +56 -4
  329. package/dist/maps/labels.d.ts.map +1 -1
  330. package/dist/maps/labels.js +213 -46
  331. package/dist/maps/labels.js.map +1 -1
  332. package/dist/maps/node.d.ts +17 -0
  333. package/dist/maps/node.d.ts.map +1 -1
  334. package/dist/maps/node.js +64 -30
  335. package/dist/maps/node.js.map +1 -1
  336. package/dist/maps/proj.d.ts +48 -0
  337. package/dist/maps/proj.d.ts.map +1 -1
  338. package/dist/maps/proj.js +73 -0
  339. package/dist/maps/proj.js.map +1 -1
  340. package/dist/maps/sources.d.ts +5 -3
  341. package/dist/maps/sources.d.ts.map +1 -1
  342. package/dist/maps/sources.js +5 -3
  343. package/dist/maps/sources.js.map +1 -1
  344. package/dist/maps/style.d.ts +35 -0
  345. package/dist/maps/style.d.ts.map +1 -1
  346. package/dist/maps/style.js.map +1 -1
  347. package/dist/maps/styles.d.ts +12 -0
  348. package/dist/maps/styles.d.ts.map +1 -1
  349. package/dist/maps/styles.js +120 -6
  350. package/dist/maps/styles.js.map +1 -1
  351. package/dist/maps/tiles.d.ts +31 -11
  352. package/dist/maps/tiles.d.ts.map +1 -1
  353. package/dist/maps/tiles.js +61 -27
  354. package/dist/maps/tiles.js.map +1 -1
  355. package/dist/maps/types.d.ts +28 -4
  356. package/dist/maps/types.d.ts.map +1 -1
  357. package/dist/markdown/index.d.ts.map +1 -1
  358. package/dist/markdown/index.js +209 -68
  359. package/dist/markdown/index.js.map +1 -1
  360. package/dist/markdown/spans.d.ts.map +1 -1
  361. package/dist/markdown/spans.js +6 -0
  362. package/dist/markdown/spans.js.map +1 -1
  363. package/dist/qml/containers.d.ts.map +1 -1
  364. package/dist/qml/containers.js +6 -1
  365. package/dist/qml/containers.js.map +1 -1
  366. package/dist/qml/interaction.d.ts.map +1 -1
  367. package/dist/qml/interaction.js +38 -5
  368. package/dist/qml/interaction.js.map +1 -1
  369. package/dist/qml/layouts.d.ts.map +1 -1
  370. package/dist/qml/layouts.js +21 -6
  371. package/dist/qml/layouts.js.map +1 -1
  372. package/dist/qml/qtquick.d.ts.map +1 -1
  373. package/dist/qml/qtquick.js +9 -2
  374. package/dist/qml/qtquick.js.map +1 -1
  375. package/dist/qml/react.d.ts.map +1 -1
  376. package/dist/qml/react.js +9 -7
  377. package/dist/qml/react.js.map +1 -1
  378. package/dist/reorder/clock.d.ts +7 -0
  379. package/dist/reorder/clock.d.ts.map +1 -0
  380. package/dist/reorder/clock.js +26 -0
  381. package/dist/reorder/clock.js.map +1 -0
  382. package/dist/reorder/index.d.ts +25 -3
  383. package/dist/reorder/index.d.ts.map +1 -1
  384. package/dist/reorder/index.js +64 -19
  385. package/dist/reorder/index.js.map +1 -1
  386. package/dist/rich-text-editor/index.d.ts +0 -11
  387. package/dist/rich-text-editor/index.d.ts.map +1 -1
  388. package/dist/rich-text-editor/index.js +38 -9
  389. package/dist/rich-text-editor/index.js.map +1 -1
  390. package/dist/rich-text-editor/keymap.d.ts.map +1 -1
  391. package/dist/rich-text-editor/keymap.js +2 -1
  392. package/dist/rich-text-editor/keymap.js.map +1 -1
  393. package/dist/rich-text-editor/keys.d.ts +17 -6
  394. package/dist/rich-text-editor/keys.d.ts.map +1 -1
  395. package/dist/rich-text-editor/keys.js +135 -26
  396. package/dist/rich-text-editor/keys.js.map +1 -1
  397. package/dist/rich-text-editor/markdown.d.ts.map +1 -1
  398. package/dist/rich-text-editor/markdown.js +30 -1
  399. package/dist/rich-text-editor/markdown.js.map +1 -1
  400. package/dist/rich-text-editor/marks.d.ts +9 -0
  401. package/dist/rich-text-editor/marks.d.ts.map +1 -0
  402. package/dist/rich-text-editor/marks.js +83 -0
  403. package/dist/rich-text-editor/marks.js.map +1 -0
  404. package/dist/rich-text-editor/render.d.ts.map +1 -1
  405. package/dist/rich-text-editor/render.js +28 -10
  406. package/dist/rich-text-editor/render.js.map +1 -1
  407. package/dist/rich-text-editor/replace.d.ts +13 -0
  408. package/dist/rich-text-editor/replace.d.ts.map +1 -0
  409. package/dist/rich-text-editor/replace.js +64 -0
  410. package/dist/rich-text-editor/replace.js.map +1 -0
  411. package/dist/rich-text-editor/toolbar.d.ts.map +1 -1
  412. package/dist/rich-text-editor/toolbar.js +1 -1
  413. package/dist/rich-text-editor/toolbar.js.map +1 -1
  414. package/dist/rich-text-editor/virtual.d.ts.map +1 -1
  415. package/dist/rich-text-editor/virtual.js +17 -0
  416. package/dist/rich-text-editor/virtual.js.map +1 -1
  417. package/dist/richtext/node.d.ts +46 -2
  418. package/dist/richtext/node.d.ts.map +1 -1
  419. package/dist/richtext/node.js +187 -4
  420. package/dist/richtext/node.js.map +1 -1
  421. package/dist/richtext/runs.d.ts +13 -8
  422. package/dist/richtext/runs.d.ts.map +1 -1
  423. package/dist/richtext/runs.js +18 -8
  424. package/dist/richtext/runs.js.map +1 -1
  425. package/dist/table/index.d.ts.map +1 -1
  426. package/dist/table/index.js +23 -0
  427. package/dist/table/index.js.map +1 -1
  428. package/dist/table/rows.d.ts +3 -2
  429. package/dist/table/rows.d.ts.map +1 -1
  430. package/dist/table/rows.js +20 -6
  431. package/dist/table/rows.js.map +1 -1
  432. package/dist/terminal/index.d.ts.map +1 -1
  433. package/dist/terminal/index.js +4 -0
  434. package/dist/terminal/index.js.map +1 -1
  435. package/dist/terminal/vt/pty.d.ts +13 -2
  436. package/dist/terminal/vt/pty.d.ts.map +1 -1
  437. package/dist/terminal/vt/pty.js +18 -3
  438. package/dist/terminal/vt/pty.js.map +1 -1
  439. package/dist/terminal-output/index.d.ts.map +1 -1
  440. package/dist/terminal-output/index.js +103 -23
  441. package/dist/terminal-output/index.js.map +1 -1
  442. package/dist/terminal-output/runs.d.ts +4 -1
  443. package/dist/terminal-output/runs.d.ts.map +1 -1
  444. package/dist/terminal-output/runs.js +5 -1
  445. package/dist/terminal-output/runs.js.map +1 -1
  446. package/dist/three/canvas.d.ts.map +1 -1
  447. package/dist/three/canvas.js +85 -7
  448. package/dist/three/canvas.js.map +1 -1
  449. package/dist/three/events.d.ts +22 -4
  450. package/dist/three/events.d.ts.map +1 -1
  451. package/dist/three/events.js +34 -4
  452. package/dist/three/events.js.map +1 -1
  453. package/dist/tree/index.d.ts.map +1 -1
  454. package/dist/tree/index.js +17 -0
  455. package/dist/tree/index.js.map +1 -1
  456. package/package.json +42 -33
  457. package/src/charts/node.ts +62 -26
  458. package/src/charts/render.ts +120 -30
  459. package/src/code/index.ts +36 -32
  460. package/src/code-editor/completion.ts +3 -2
  461. package/src/code-editor/index.ts +19 -0
  462. package/src/code-editor/node.ts +1574 -184
  463. package/src/code-language/index.ts +1 -1
  464. package/src/code-language/languages/glsl.ts +12 -2
  465. package/src/code-language/languages/javascript.ts +21 -4
  466. package/src/code-language/languages/json.ts +12 -2
  467. package/src/code-language/languages/shell.ts +12 -2
  468. package/src/code-language/languages/sql.ts +22 -2
  469. package/src/code-language/runs.ts +111 -1
  470. package/src/code-language/stream.ts +257 -17
  471. package/src/code-language/types.ts +5 -4
  472. package/src/codeblock/index.ts +68 -4
  473. package/src/flow/draw.ts +167 -41
  474. package/src/flow/gl/color.ts +85 -0
  475. package/src/flow/gl/index.ts +342 -0
  476. package/src/flow/gl/pack.ts +585 -0
  477. package/src/flow/gl/renderer.ts +679 -0
  478. package/src/flow/gl/shaders.ts +371 -0
  479. package/src/flow/gl/text.ts +1295 -0
  480. package/src/flow/index.ts +794 -67
  481. package/src/flow/node.ts +2496 -1256
  482. package/src/flow/paint.ts +511 -0
  483. package/src/flow/paths.ts +80 -15
  484. package/src/flow/scene.ts +2199 -0
  485. package/src/flow/types.ts +113 -1
  486. package/src/html/controls.ts +73 -2
  487. package/src/html/css/calc.ts +385 -0
  488. package/src/html/css/cascade.ts +1481 -62
  489. package/src/html/css/color.ts +755 -0
  490. package/src/html/css/content.ts +417 -0
  491. package/src/html/css/counter-styles.ts +973 -0
  492. package/src/html/css/decode.ts +130 -0
  493. package/src/html/css/parse.ts +1514 -160
  494. package/src/html/css/style.ts +4131 -223
  495. package/src/html/css/ua.ts +66 -17
  496. package/src/html/css/values.ts +150 -50
  497. package/src/html/css/vars.ts +348 -0
  498. package/src/html/dom.ts +284 -17
  499. package/src/html/fonts.ts +641 -0
  500. package/src/html/gif.ts +233 -0
  501. package/src/html/index.ts +180 -20
  502. package/src/html/layout/block.ts +3560 -208
  503. package/src/html/layout/boxes.ts +2141 -190
  504. package/src/html/layout/cache.ts +289 -0
  505. package/src/html/layout/collapse.ts +317 -0
  506. package/src/html/layout/css-grid.ts +1063 -0
  507. package/src/html/layout/flex.ts +1115 -106
  508. package/src/html/layout/floats.ts +128 -14
  509. package/src/html/layout/grid-lines.ts +141 -0
  510. package/src/html/layout/grid.ts +181 -0
  511. package/src/html/layout/inline.ts +4187 -192
  512. package/src/html/layout/shaping.ts +140 -0
  513. package/src/html/layout/table.ts +696 -186
  514. package/src/html/layout/tracks.ts +437 -0
  515. package/src/html/node.ts +1585 -156
  516. package/src/html/paint.ts +4582 -267
  517. package/src/html/resources.ts +161 -50
  518. package/src/html/surfaces.ts +81 -0
  519. package/src/html/svg.ts +504 -0
  520. package/src/html/url.ts +86 -0
  521. package/src/index.ts +2 -0
  522. package/src/internal/codelines.ts +200 -0
  523. package/src/internal/collate.ts +12 -0
  524. package/src/internal/markdown/ast.ts +11 -0
  525. package/src/internal/markdown/parse.ts +154 -11
  526. package/src/internal/prewarm.ts +45 -0
  527. package/src/internal/sdf.ts +227 -0
  528. package/src/internal/splice.ts +25 -0
  529. package/src/internal/window.ts +36 -5
  530. package/src/maps/anchors.ts +307 -1
  531. package/src/maps/controller.ts +116 -31
  532. package/src/maps/gl/buckets.ts +5 -1
  533. package/src/maps/gl/build-worker.ts +13 -2
  534. package/src/maps/gl/cover.ts +79 -44
  535. package/src/maps/gl/placement.ts +221 -69
  536. package/src/maps/gl/renderer.ts +104 -20
  537. package/src/maps/gl/shaders.ts +35 -32
  538. package/src/maps/gl/store.ts +66 -17
  539. package/src/maps/gl/text.ts +444 -71
  540. package/src/maps/gl/view.ts +361 -96
  541. package/src/maps/icons.ts +183 -0
  542. package/src/maps/index.ts +4 -0
  543. package/src/maps/labels.ts +269 -49
  544. package/src/maps/node.ts +82 -49
  545. package/src/maps/proj.ts +87 -0
  546. package/src/maps/sources.ts +5 -3
  547. package/src/maps/style.ts +32 -0
  548. package/src/maps/styles.ts +215 -4
  549. package/src/maps/tiles.ts +64 -31
  550. package/src/maps/types.ts +28 -4
  551. package/src/markdown/index.ts +293 -92
  552. package/src/markdown/spans.ts +5 -0
  553. package/src/qml/containers.tsx +6 -1
  554. package/src/qml/interaction.tsx +42 -5
  555. package/src/qml/layouts.tsx +21 -6
  556. package/src/qml/qtquick.tsx +9 -2
  557. package/src/qml/react.tsx +7 -5
  558. package/src/reorder/clock.ts +27 -0
  559. package/src/reorder/index.ts +100 -27
  560. package/src/rich-text-editor/index.ts +37 -12
  561. package/src/rich-text-editor/keymap.ts +1 -1
  562. package/src/rich-text-editor/keys.ts +135 -26
  563. package/src/rich-text-editor/markdown.ts +36 -1
  564. package/src/rich-text-editor/marks.ts +101 -0
  565. package/src/rich-text-editor/render.ts +49 -12
  566. package/src/rich-text-editor/replace.ts +85 -0
  567. package/src/rich-text-editor/toolbar.ts +1 -1
  568. package/src/rich-text-editor/virtual.ts +25 -0
  569. package/src/richtext/node.ts +235 -14
  570. package/src/richtext/runs.ts +23 -12
  571. package/src/table/index.ts +29 -0
  572. package/src/table/rows.ts +18 -8
  573. package/src/terminal/index.ts +4 -0
  574. package/src/terminal/vt/pty.ts +16 -2
  575. package/src/terminal-output/index.ts +129 -31
  576. package/src/terminal-output/runs.ts +16 -2
  577. package/src/three/canvas.ts +102 -8
  578. package/src/three/events.ts +49 -9
  579. package/src/tree/index.ts +19 -0
@@ -19,9 +19,12 @@ import { compile } from 'css-select';
19
19
  import * as DomUtils from 'domutils';
20
20
  import { Element as DomElement, isTag } from 'domhandler';
21
21
  import { attr, tagOf } from '../dom.js';
22
- import { mediaMatches } from './parse.js';
23
- import { applyDeclaration, blockify, inherit, initialStyle } from './style.js';
22
+ import { svgSizeHint } from '../svg.js';
23
+ import { asciiLower, escapeEnd, mediaMatches, readIdent, startsIdent, } from './parse.js';
24
+ import { applyDeclaration, blockify, settleContentVisibility, settleOverflow, settleClamp, copyStyle, decorate, inherit, initialOne, initialStyle, isInherited, } from './style.js';
24
25
  import { parseDeclarations } from './parse.js';
26
+ import { CounterStyles, counterStyleRule } from './counter-styles.js';
27
+ import { customProperties, substituteIn } from './vars.js';
25
28
  /** Where a declaration came from. Higher wins before specificity is asked. */
26
29
  var Origin;
27
30
  (function (Origin) {
@@ -32,6 +35,20 @@ var Origin;
32
35
  Origin[Origin["AuthorImportant"] = 4] = "AuthorImportant";
33
36
  Origin[Origin["InlineImportant"] = 5] = "InlineImportant";
34
37
  })(Origin || (Origin = {}));
38
+ /**
39
+ * What makes a selector's match depend on more than the element's own tag and
40
+ * attributes and its ancestors' — its siblings, where it sits among them,
41
+ * what it contains, or where the document is scrolled to. Two elements that
42
+ * look alike from the root down can still differ under a rule with one of
43
+ * these, so an element such a rule could reach does not share its style
44
+ * (`Cascade.sharedStyleFor`). css-select spells some of them under other
45
+ * names, and those count too: `:checked` and `:selected` read which option
46
+ * comes first, `:disabled` and `:enabled` which legend does, and `:parent`
47
+ * and `:contains()` the element's contents. Over-broad on purpose: a `+` or
48
+ * `~` inside an attribute value, or the `~=` operator, costs sharing and
49
+ * nothing else.
50
+ */
51
+ const UNSHAREABLE = /[+~]|:(?:first|last|only)-(?:child|of-type)|:nth-|:empty|:blank|:has\(|:focus-within|:target|:scope|:(?:checked|selected|disabled|enabled|parent)\b|:i?contains\(/;
35
52
  /**
36
53
  * Rules bucketed by the key of their rightmost compound selector. An element
37
54
  * only ever tries `id`, its classes, its tag and the universal bucket.
@@ -44,12 +61,64 @@ class RuleIndex {
44
61
  /** Whether any rule in here is pointer-sensitive, so the renderer knows
45
62
  * whether a pointer move can change the cascade at all. */
46
63
  hoverSensitive = false;
64
+ /** The buckets holding a rule an element cannot share its style under
65
+ * (`UNSHAREABLE`): the ids, classes and tags whose elements compute
66
+ * their own, and whether the universal bucket makes every element do so. */
67
+ ownStyleIds = new Set();
68
+ ownStyleClasses = new Set();
69
+ ownStyleTags = new Set();
70
+ ownStyleEverywhere = false;
71
+ /** How many rules are in here, so an empty index costs one comparison. */
72
+ size = 0;
73
+ _nextId = 0;
74
+ /**
75
+ * Whether a bucket here could hold a rule for `el`, by its tag, id and
76
+ * classes and without matching anything: an index of a rule or two is
77
+ * asked of every element — the user-agent sheet's `q::before` and
78
+ * `q::after` made every element of a document split its `class` twice —
79
+ * and almost none of them has a rule in it.
80
+ */
81
+ reaches(el) {
82
+ if (this.universal.length || this.byTag.has(tagOf(el)))
83
+ return true;
84
+ if (this.byId.size) {
85
+ const id = attr(el, 'id');
86
+ if (id && this.byId.has(id))
87
+ return true;
88
+ }
89
+ if (this.byClass.size) {
90
+ const className = attr(el, 'class');
91
+ if (className) {
92
+ for (const name of className.split(/\s+/)) {
93
+ if (name && this.byClass.has(name))
94
+ return true;
95
+ }
96
+ }
97
+ }
98
+ return false;
99
+ }
47
100
  add(rule) {
48
- const indexed = { rule, match: null, compiled: false };
101
+ this.size += 1;
102
+ const indexed = {
103
+ rule,
104
+ match: null,
105
+ compiled: false,
106
+ id: this._nextId++,
107
+ };
49
108
  if (rule.selector.includes(':hover') || rule.selector.includes(':active')) {
50
109
  this.hoverSensitive = true;
51
110
  }
52
111
  const key = rightmostKey(rule.selector);
112
+ if (UNSHAREABLE.test(rule.selector)) {
113
+ if (key.kind === 'id')
114
+ this.ownStyleIds.add(key.name);
115
+ else if (key.kind === 'class')
116
+ this.ownStyleClasses.add(key.name);
117
+ else if (key.kind === 'tag')
118
+ this.ownStyleTags.add(key.name);
119
+ else
120
+ this.ownStyleEverywhere = true;
121
+ }
53
122
  const bucket = key.kind === 'id'
54
123
  ? mapBucket(this.byId, key.name)
55
124
  : key.kind === 'class'
@@ -60,6 +129,200 @@ class RuleIndex {
60
129
  bucket.push(indexed);
61
130
  }
62
131
  }
132
+ /** A selector's trailing `::before`, `::after`, `::first-letter`,
133
+ * `::first-line` or `::marker`, or CSS 2's single-colon spelling of any of
134
+ * its four. */
135
+ const PSEUDO_ELEMENT = /::?(before|after|first-letter|first-line)$|::marker$/i;
136
+ /**
137
+ * A rule for a pseudo-element, as the pseudo-element it styles and a rule
138
+ * for the element it hangs off, which is what gets matched. The specificity
139
+ * is the whole selector's, the pseudo-element counted in. `p::before`
140
+ * matches `p`; `p ::before` and `p > ::before` have nothing left of their
141
+ * last compound and match any child, `p *` and `p > *`.
142
+ */
143
+ function splitPseudoElement(rule) {
144
+ const m = PSEUDO_ELEMENT.exec(rule.selector);
145
+ if (!m)
146
+ return null;
147
+ const head = rule.selector.slice(0, m.index);
148
+ const trimmed = head.trimEnd();
149
+ const selector = !trimmed
150
+ ? '*'
151
+ : head !== trimmed || /[>+~]$/.test(trimmed)
152
+ ? `${trimmed} *`
153
+ : trimmed;
154
+ return {
155
+ which: (m[1] ?? 'marker').toLowerCase(),
156
+ rule: { ...rule, selector },
157
+ };
158
+ }
159
+ /**
160
+ * The compounds of a selector that test `:hover`, each without it (and
161
+ * without an `:active` beside it), and whether one is followed by a sibling
162
+ * combinator, which reaches the element's later siblings. `nested` where a
163
+ * `:hover` sits inside a functional pseudo-class — `:not(:hover)`,
164
+ * `:is(:hover)` — and reaches elements no compound names.
165
+ *
166
+ * A `:hover` in a compound's `:has()` is the one exception, since what it
167
+ * reaches is known: the compound, without its `:has()`, is an ancestor of
168
+ * the element the pointer is over — or an earlier sibling of it or of an
169
+ * ancestor, where the argument starts at a sibling (Selectors 4, 4.5). So
170
+ * it is given as an anchor (`has`) rather than making the rule `nested`.
171
+ *
172
+ * `:active` is not the pointer's here: nothing sets it (`setPointer` is
173
+ * handed none), so a selector that tests only it never changes as the
174
+ * pointer moves — Wikipedia's buttons' `:focus:not(:active)` among them.
175
+ * A press that sets it would have to be counted here too.
176
+ */
177
+ export function pointerCompounds(selector) {
178
+ const out = {
179
+ compounds: [],
180
+ siblings: false,
181
+ nested: false,
182
+ has: [],
183
+ };
184
+ // the compounds at the top level, each with the combinator after it
185
+ const parts = [];
186
+ let start = 0;
187
+ let depth = 0;
188
+ let quote = '';
189
+ const split = (end, next) => {
190
+ const text = selector.slice(start, end).trim();
191
+ if (text)
192
+ parts.push({ text, next });
193
+ else if (parts.length && next !== ' ')
194
+ parts[parts.length - 1].next = next;
195
+ };
196
+ for (let i = 0; i < selector.length; i += 1) {
197
+ const c = selector[i];
198
+ if (quote) {
199
+ if (c === quote && selector[i - 1] !== '\\')
200
+ quote = '';
201
+ continue;
202
+ }
203
+ if (c === '\\')
204
+ i = escapeEnd(selector, i) - 1;
205
+ else if (c === '"' || c === "'")
206
+ quote = c;
207
+ else if (c === '(' || c === '[')
208
+ depth += 1;
209
+ else if (c === ')' || c === ']')
210
+ depth = Math.max(0, depth - 1);
211
+ else if (depth === 0 && /[\s>+~]/.test(c)) {
212
+ split(i, c === '+' || c === '~' || c === '>' ? c : ' ');
213
+ start = i + 1;
214
+ }
215
+ }
216
+ split(selector.length, '');
217
+ for (const { text, next } of parts) {
218
+ let bare = '';
219
+ let pointer = false;
220
+ depth = 0;
221
+ quote = '';
222
+ /** Where a top-level `:has(` starts in `bare`, and its argument in
223
+ * `text`, while one is open. */
224
+ let hasAt = -1;
225
+ let argAt = -1;
226
+ let hasPointer = false;
227
+ let anchored = false;
228
+ const args = [];
229
+ for (let i = 0; i < text.length; i += 1) {
230
+ const c = text[i];
231
+ if (quote) {
232
+ if (c === quote && text[i - 1] !== '\\')
233
+ quote = '';
234
+ bare += c;
235
+ continue;
236
+ }
237
+ if (c === '\\') {
238
+ const end = escapeEnd(text, i);
239
+ bare += text.slice(i, end);
240
+ i = end - 1;
241
+ continue;
242
+ }
243
+ if (c === '"' || c === "'")
244
+ quote = c;
245
+ else if (c === '(' || c === '[') {
246
+ if (depth === 0 && c === '(' && /:has$/i.test(bare)) {
247
+ hasAt = bare.length - 4;
248
+ argAt = i + 1;
249
+ hasPointer = false;
250
+ }
251
+ depth += 1;
252
+ }
253
+ else if (c === ')' || c === ']') {
254
+ depth = Math.max(0, depth - 1);
255
+ if (depth === 0 && hasAt >= 0 && c === ')') {
256
+ // the compound is kept without it: its argument is what the
257
+ // pointer changes
258
+ if (hasPointer) {
259
+ anchored = true;
260
+ args.push(text.slice(argAt, i));
261
+ bare = bare.slice(0, hasAt);
262
+ }
263
+ else
264
+ bare += c;
265
+ hasAt = -1;
266
+ continue;
267
+ }
268
+ }
269
+ else if (c === ':' && text[i + 1] !== ':') {
270
+ const m = POINTER_PSEUDO_AT.exec(text.slice(i));
271
+ if (m) {
272
+ const hover = m[0].length === 6;
273
+ if (depth > 0) {
274
+ // whatever holds it in the argument, the element it tests is
275
+ // in the anchor's subtree or a later sibling's
276
+ if (hover && hasAt >= 0)
277
+ hasPointer = true;
278
+ else if (hover)
279
+ out.nested = true;
280
+ }
281
+ else {
282
+ if (hover)
283
+ pointer = true;
284
+ i += m[0].length - 1;
285
+ continue;
286
+ }
287
+ }
288
+ }
289
+ bare += c;
290
+ }
291
+ if (anchored) {
292
+ out.has.push({
293
+ anchor: bare.trim() || '*',
294
+ siblings: args.some((arg) => arg.split(',').some((one) => /^\s*[+~]/.test(one))),
295
+ });
296
+ }
297
+ if (!pointer)
298
+ continue;
299
+ out.compounds.push(bare.trim() || '*');
300
+ if (next === '+' || next === '~')
301
+ out.siblings = true;
302
+ }
303
+ return out;
304
+ }
305
+ /** `:hover` or `:active` at the start of a string, and nothing longer. */
306
+ const POINTER_PSEUDO_AT = /^:(?:hover|active)(?![\w-])/i;
307
+ /** `:hover` or `:active` anywhere in a selector. */
308
+ const POINTER_PSEUDO = /:(?:hover|active)(?![\w-])/i;
309
+ /**
310
+ * A selector compiled to a matcher over this adapter. css-select keeps the
311
+ * answers of a `:has()` above the subject, or of the ancestors a
312
+ * descendant combinator tried under one, for as long as the matcher lives
313
+ * (`cacheResults`) — which is the cascade's life, and a pointer move is no
314
+ * new cascade. So a selector that tests the pointer keeps none: with them,
315
+ * `#box:has(a:hover) .m` answered what it did before the first move, for
316
+ * good.
317
+ */
318
+ function compileSelector(selector, adapter) {
319
+ return compile(noEmptyWords(selector), {
320
+ adapter,
321
+ xmlMode: false,
322
+ pseudos: PSEUDOS,
323
+ cacheResults: !POINTER_PSEUDO.test(selector),
324
+ });
325
+ }
63
326
  function mapBucket(map, key) {
64
327
  let bucket = map.get(key);
65
328
  if (!bucket) {
@@ -87,14 +350,26 @@ function rightmostKey(selector) {
87
350
  quote = '';
88
351
  continue;
89
352
  }
90
- if (c === '"' || c === "'")
353
+ // an escaped character is part of a name, whatever it is — and a hex
354
+ // escape takes the space after it: `.c\6c ass` is the class `class`
355
+ if (c === '\\')
356
+ i = escapeEnd(selector, i) - 1;
357
+ else if (c === '"' || c === "'")
91
358
  quote = c;
92
359
  else if (c === '(' || c === '[')
93
360
  depth += 1;
94
361
  else if (c === ')' || c === ']')
95
362
  depth = Math.max(0, depth - 1);
96
363
  else if (depth === 0 &&
97
- (c === ' ' || c === '>' || c === '+' || c === '~')) {
364
+ (c === ' ' ||
365
+ c === '>' ||
366
+ c === '+' ||
367
+ c === '~' ||
368
+ // CSS's other white space: `div\fp` is a descendant too
369
+ c === '\t' ||
370
+ c === '\n' ||
371
+ c === '\r' ||
372
+ c === '\f')) {
98
373
  start = i + 1;
99
374
  }
100
375
  }
@@ -105,31 +380,29 @@ function rightmostKey(selector) {
105
380
  let i = 0;
106
381
  while (i < compound.length) {
107
382
  const c = compound[i];
108
- if (c === '#') {
109
- const end = identEnd(compound, i + 1);
110
- if (!id)
111
- id = compound.slice(i + 1, end);
112
- i = end;
113
- }
114
- else if (c === '.') {
115
- const end = identEnd(compound, i + 1);
116
- if (!cls)
117
- cls = compound.slice(i + 1, end);
118
- i = end;
383
+ // Names as the matcher reads them, escapes resolved: `.md\:flex` is
384
+ // the class `md:flex`, and filed under its first half it never matched.
385
+ if (c === '#' || c === '.') {
386
+ const name = readIdent(compound, i + 1);
387
+ if (c === '#' && !id)
388
+ id = name.value;
389
+ if (c === '.' && !cls)
390
+ cls = name.value;
391
+ i = Math.max(name.end, i + 1);
119
392
  }
120
393
  else if (c === '[') {
121
394
  i = balancedEnd(compound, i, '[', ']');
122
395
  }
123
396
  else if (c === ':') {
124
397
  const skip = compound[i + 1] === ':' ? 2 : 1;
125
- const end = identEnd(compound, i + skip);
398
+ const end = Math.max(readIdent(compound, i + skip).end, i + skip);
126
399
  i = compound[end] === '(' ? balancedEnd(compound, end, '(', ')') : end;
127
400
  }
128
- else if (/[a-zA-Z]/.test(c)) {
129
- const end = identEnd(compound, i);
401
+ else if (startsIdent(compound, i)) {
402
+ const name = readIdent(compound, i);
130
403
  if (!tag)
131
- tag = compound.slice(i, end).toLowerCase();
132
- i = end;
404
+ tag = name.value.toLowerCase();
405
+ i = name.end;
133
406
  }
134
407
  else {
135
408
  i += 1;
@@ -143,12 +416,6 @@ function rightmostKey(selector) {
143
416
  return { kind: 'tag', name: tag };
144
417
  return { kind: 'any', name: '' };
145
418
  }
146
- function identEnd(text, from) {
147
- let i = from;
148
- while (i < text.length && /[a-zA-Z0-9_\-\\]/.test(text[i]))
149
- i += 1;
150
- return i;
151
- }
152
419
  function balancedEnd(text, from, open, close) {
153
420
  let depth = 0;
154
421
  for (let i = from; i < text.length; i += 1) {
@@ -163,13 +430,50 @@ function balancedEnd(text, from, open, close) {
163
430
  return text.length;
164
431
  }
165
432
  const NO_POINTER = { hovered: new Set(), active: new Set() };
433
+ function faceOf(style) {
434
+ return {
435
+ family: style.fontFamily,
436
+ size: style.fontSize,
437
+ weight: style.fontWeight,
438
+ style: style.fontStyle,
439
+ };
440
+ }
441
+ function faceKey(style) {
442
+ return `${style.fontFamily}\u0001${style.fontSize}\u0001${style.fontWeight}\u0001${style.fontStyle}`;
443
+ }
166
444
  /**
167
445
  * A cascade over a fixed set of stylesheets. Rebuilt when the sheets change;
168
446
  * re-run when the DOM, the viewport band or the pointer state does.
169
447
  */
170
448
  export class Cascade {
171
449
  _index = new RuleIndex();
450
+ /** The rules for pseudo-elements, kept apart: they never style the
451
+ * element itself, and a document with none of them asks nothing. */
452
+ _pseudo = {
453
+ before: new RuleIndex(),
454
+ after: new RuleIndex(),
455
+ 'first-letter': new RuleIndex(),
456
+ 'first-line': new RuleIndex(),
457
+ marker: new RuleIndex(),
458
+ };
172
459
  _adapter;
460
+ /** The compounds of the selectors that test the pointer, each without
461
+ * its `:hover` or `:active` (`pointerCompounds`), and their matchers,
462
+ * compiled when a pointer move first asks (`hoverTouches`). */
463
+ _hoverCompounds = new Set();
464
+ _hoverMatchers = null;
465
+ /** Whether a pointer move can be restyled where it happened
466
+ * (`HtmlViewNode._hoverInPlace`): false where a selector tests the
467
+ * pointer inside a functional pseudo-class, which reaches elements no
468
+ * compound names. */
469
+ hoverLocal = true;
470
+ /** Whether a compound that tests the pointer is followed by a sibling
471
+ * combinator, so an element's later siblings restyle with it. */
472
+ hoverSiblings = false;
473
+ /** The compounds whose `:has()` tests the pointer, each without it, and
474
+ * whether its argument starts at a sibling (`hoverAnchors`). */
475
+ _hoverAnchors = new Map();
476
+ _anchorMatchers = null;
173
477
  _pointer = NO_POINTER;
174
478
  initial;
175
479
  look;
@@ -183,20 +487,74 @@ export class Cascade {
183
487
  /** Every width at which some `@media` rule changes its mind, in CSS
184
488
  * pixels — the unit the author wrote them in. */
185
489
  breakpoints;
186
- constructor(sheets, look, viewportWidth, viewportHeight, scale = 1) {
490
+ /** The counter styles the sheets define, over the predefined ones. */
491
+ counterStyles;
492
+ /** A face's x-height, for `ex`, where the fonts can say. */
493
+ _xHeightOf;
494
+ _xHeights = new Map();
495
+ /** The advance of a face's "0", for `ch`, likewise. */
496
+ _zeroWidthOf;
497
+ _zeroWidths = new Map();
498
+ /** A face's own line height, for `lh` where `line-height` is `normal`,
499
+ * likewise. */
500
+ _normalLineOf;
501
+ _normalLines = new Map();
502
+ /** Whether any declaration sets a custom property or reads one: without
503
+ * one, no element's style asks about them. */
504
+ _vars = false;
505
+ /** Whether any declaration is in a unit of the viewport's width — `vw`,
506
+ * `vmin`, `vmax` — or of its height, `vh` and the same two: a style
507
+ * holds such a length as a number, so it is computed again when that
508
+ * side of the viewport moves (`HtmlViewNode._update`). */
509
+ readsViewportWidth = false;
510
+ readsViewportHeight = false;
511
+ /** The families the document loads itself (`fonts.ts`), or null for a
512
+ * document with no `@font-face`. */
513
+ _families;
514
+ _mapFamilies;
515
+ /** Whether any declaration has a length in `lh` or `rlh`: only then is
516
+ * the line height settled ahead of the declarations that read it. */
517
+ _lh = false;
518
+ constructor(sheets, look, viewportWidth, viewportHeight, scale = 1, xHeight = null, zeroWidth = null, normalLine = null, families = null) {
519
+ this._xHeightOf = xHeight;
520
+ this._zeroWidthOf = zeroWidth;
521
+ this._normalLineOf = normalLine;
522
+ this._families = families;
523
+ this._mapFamilies = families
524
+ ? (list) => families.map(list)
525
+ : undefined;
187
526
  this.look = look;
188
- this.initial = initialStyle(look);
527
+ this.initial = initialStyle(look, scale);
189
528
  this.viewportWidth = viewportWidth;
190
529
  this.viewportHeight = viewportHeight;
191
530
  this.scale = scale;
192
531
  const breakpoints = new Set();
532
+ const counterStyles = new Map();
193
533
  for (const sheet of sheets) {
194
- for (const rule of sheet.rules)
195
- this._index.add(rule);
534
+ // a counter style's rule whole over any before it of its name
535
+ for (const { prelude, declarations } of sheet.counterStyles ?? []) {
536
+ const style = counterStyleRule(prelude, declarations);
537
+ if (style)
538
+ counterStyles.set(style.name, style.rule);
539
+ }
540
+ for (const rule of sheet.rules) {
541
+ if (!this._vars && usesVars(rule.declarations))
542
+ this._vars = true;
543
+ this._noteViewportUnits(rule.declarations);
544
+ if (!this._lh && usesLh(rule.declarations))
545
+ this._lh = true;
546
+ const pseudo = splitPseudoElement(rule);
547
+ if (pseudo)
548
+ this._pseudo[pseudo.which].add(pseudo.rule);
549
+ else
550
+ this._index.add(rule);
551
+ this._noteHover((pseudo?.rule ?? rule).selector);
552
+ }
196
553
  for (const bp of sheet.breakpoints)
197
554
  breakpoints.add(bp);
198
555
  }
199
556
  this.breakpoints = [...breakpoints].sort((a, b) => a - b);
557
+ this.counterStyles = new CounterStyles(counterStyles);
200
558
  // css-select's default adapter is domutils; `isHovered` and `isActive`
201
559
  // are its documented hooks for exactly this, so `:hover` costs an
202
560
  // adapter field rather than a fork of the matcher.
@@ -207,18 +565,124 @@ export class Cascade {
207
565
  this._adapter = {
208
566
  ...DomUtils,
209
567
  isTag,
568
+ // a type selector sees `tagOf`'s name — XHTML's `<svg:svg>` is an
569
+ // `svg` — asked only of a name that could be one: this runs for every
570
+ // type selector tried, and the parser has lowercased the rest
571
+ getName: (el) => el.name.endsWith(':svg') ? tagOf(el) : el.name,
210
572
  isHovered: (el) => this._pointer.hovered.has(el),
211
573
  isActive: (el) => this._pointer.active.has(el),
212
574
  isVisited: () => false,
213
575
  };
214
576
  }
577
+ /** Whether declarations read the viewport's width or height (`vw`,
578
+ * `vh`, `vmin`, `vmax`), noted on the cascade. */
579
+ _noteViewportUnits(declarations) {
580
+ if (this.readsViewportWidth && this.readsViewportHeight)
581
+ return;
582
+ for (const d of declarations) {
583
+ if (!VIEWPORT_UNIT.test(d.value))
584
+ continue;
585
+ for (const m of d.value.matchAll(VIEWPORT_UNITS)) {
586
+ const unit = m[1].toLowerCase();
587
+ if (unit !== 'vh')
588
+ this.readsViewportWidth = true;
589
+ if (unit !== 'vw')
590
+ this.readsViewportHeight = true;
591
+ }
592
+ }
593
+ }
215
594
  /** Whether a pointer move can change what this cascade produces. */
216
595
  get hoverSensitive() {
217
- return this._index.hoverSensitive;
596
+ return (this._index.hoverSensitive ||
597
+ this._pseudo.before.hoverSensitive ||
598
+ this._pseudo.after.hoverSensitive ||
599
+ this._pseudo['first-letter'].hoverSensitive ||
600
+ this._pseudo['first-line'].hoverSensitive ||
601
+ this._pseudo.marker.hoverSensitive);
218
602
  }
219
603
  setPointer(pointer) {
220
604
  this._pointer = pointer;
221
605
  }
606
+ /**
607
+ * Whether the pointer entering or leaving `el` can change a style: one of
608
+ * the compounds a selector tests the pointer in matches it, the pointer
609
+ * aside. A move between two paragraphs of a page whose only such rule is
610
+ * `a:hover` touches nothing, and restyles nothing.
611
+ */
612
+ hoverTouches(el) {
613
+ this._hoverMatchers ??= [...this._hoverCompounds].flatMap((c) => {
614
+ const match = this._compile(c);
615
+ return match ? [match] : [];
616
+ });
617
+ for (const match of this._hoverMatchers)
618
+ if (match(el))
619
+ return true;
620
+ return false;
621
+ }
622
+ /**
623
+ * The elements whose `:has()` may have changed its answer as `el` was
624
+ * hovered or left: the ancestors of it a compound whose `:has()` tests
625
+ * the pointer names, and their earlier siblings and its own where the
626
+ * argument starts at a sibling — a superset, which is restyled and found
627
+ * the same where it did not. Into `into`.
628
+ */
629
+ hoverAnchors(el, into) {
630
+ if (!this._hoverAnchors.size)
631
+ return;
632
+ this._anchorMatchers ??= [...this._hoverAnchors].flatMap(([anchor, siblings]) => {
633
+ const match = this._compile(anchor);
634
+ return match ? [{ match, siblings }] : [];
635
+ });
636
+ const siblings = this._anchorMatchers.some((m) => m.siblings);
637
+ for (let at = el; at;) {
638
+ if (at !== el) {
639
+ for (const { match } of this._anchorMatchers) {
640
+ if (match(at)) {
641
+ into.push(at);
642
+ break;
643
+ }
644
+ }
645
+ }
646
+ if (siblings) {
647
+ for (let s = at.prev; s; s = s.prev) {
648
+ if (!isTag(s))
649
+ continue;
650
+ for (const { match, siblings: apart } of this._anchorMatchers) {
651
+ if (apart && match(s)) {
652
+ into.push(s);
653
+ break;
654
+ }
655
+ }
656
+ }
657
+ }
658
+ const parent = at.parent;
659
+ at = parent && isTag(parent) ? parent : null;
660
+ }
661
+ }
662
+ _noteHover(selector) {
663
+ if (!/:(?:hover|active)/i.test(selector))
664
+ return;
665
+ const found = pointerCompounds(selector);
666
+ if (found.nested)
667
+ this.hoverLocal = false;
668
+ if (found.siblings)
669
+ this.hoverSiblings = true;
670
+ for (const c of found.compounds)
671
+ this._hoverCompounds.add(c);
672
+ for (const { anchor, siblings } of found.has) {
673
+ this._hoverAnchors.set(anchor, siblings || (this._hoverAnchors.get(anchor) ?? false));
674
+ }
675
+ }
676
+ /** A selector compiled as a rule's is, or null where css-select refuses
677
+ * it, as a rule it refuses drops out of the cascade. */
678
+ _compile(selector) {
679
+ try {
680
+ return compileSelector(selector, this._adapter);
681
+ }
682
+ catch {
683
+ return null;
684
+ }
685
+ }
222
686
  /** Which media band a device-pixel width falls in. Two widths in the same
223
687
  * band produce identical styles, which is what lets a resize skip
224
688
  * restyling. */
@@ -233,14 +697,323 @@ export class Cascade {
233
697
  }
234
698
  return band;
235
699
  }
700
+ /** Computed styles by sharing key, for one build (`sharedStyleFor`): one
701
+ * map for the elements shared without matching, one for the elements
702
+ * shared by what they matched. */
703
+ _shared = new Map();
704
+ _sharedByMatch = new Map();
705
+ _nextShareKey = 1;
706
+ /** Custom property sets by what made them, for one build (`_customFor`). */
707
+ _customs = new Map();
708
+ _ids = new WeakMap();
709
+ _nextId = 1;
710
+ /** A box tree is about to be built: the styles shared in the last build
711
+ * were computed against a pointer and a viewport that may have moved. */
712
+ beginSharing() {
713
+ this._shared.clear();
714
+ this._sharedByMatch.clear();
715
+ this._customs.clear();
716
+ }
717
+ /**
718
+ * An element's custom properties: its parent's, or its own over them —
719
+ * shared by every element whose parent has the same ones and whose own
720
+ * come from the same declarations. Tailwind 4 sets thirty-five of them on
721
+ * every element (`*, ::before, ::after { --tw-shadow: 0 0 #0000; … }`),
722
+ * and a set per element was a fifth of building the boxes; it also kept
723
+ * each element's `var()`s from being replaced once for its siblings,
724
+ * which `substituteIn` remembers by the set they were read against.
725
+ */
726
+ _customFor(parentStyle, candidates) {
727
+ let key = '';
728
+ for (const c of candidates) {
729
+ if (!this._hasCustom(c))
730
+ continue;
731
+ key += `${this._idOf(c.declarations)}:${c.only},`;
732
+ }
733
+ const parent = parentStyle.custom;
734
+ if (!key)
735
+ return parent;
736
+ key = `${parent ? this._idOf(parent) : 0}|${key}`;
737
+ const hit = this._customs.get(key);
738
+ if (hit)
739
+ return hit;
740
+ const own = new Map();
741
+ for (const c of candidates) {
742
+ for (const d of pick(c))
743
+ if (d.custom)
744
+ own.set(d.prop, d.value);
745
+ }
746
+ const made = customProperties(own, parent);
747
+ this._customs.set(key, made);
748
+ return made;
749
+ }
750
+ /** Whether a candidate sets a custom property. */
751
+ _hasCustom(c) {
752
+ if (c.only >= 0)
753
+ return !!c.declarations[c.only].custom;
754
+ let has = CUSTOM_IN.get(c.declarations);
755
+ if (has === undefined) {
756
+ has = c.declarations.some((d) => !!d.custom);
757
+ CUSTOM_IN.set(c.declarations, has);
758
+ }
759
+ return has;
760
+ }
761
+ /** A number for an object, to key a string by. */
762
+ _idOf(of) {
763
+ let id = this._ids.get(of);
764
+ if (id === undefined) {
765
+ id = this._nextId++;
766
+ this._ids.set(of, id);
767
+ }
768
+ return id;
769
+ }
770
+ /**
771
+ * `styleFor`, shared between the elements that must compute the same
772
+ * style, with the key this element's children share under.
773
+ *
774
+ * A style is a function of the parent's style, the rules that match, the
775
+ * presentational attributes, the inline style and whether the parent is a
776
+ * flex container. Outside the rules `UNSHAREABLE` names, what matches
777
+ * depends only on the element's tag and attributes and its ancestors', and
778
+ * the hints read nothing else either. So the key is the parent's key, the
779
+ * flex flag, the tag and every attribute — plus the pointer state where a
780
+ * rule asks for it — and an element whose key has been computed in this
781
+ * build takes that style object, matching nothing. A long document is a
782
+ * few kinds of element many times over: the benchmark's 600 KB report is
783
+ * 9,039 elements and 110 keys, and computing every style again was a third
784
+ * of an edit.
785
+ *
786
+ * An element a rule `UNSHAREABLE` could reach is matched, since where it
787
+ * sits decides what matches — and then shared by what matched: the same
788
+ * parent, attributes and rules make the same style. That is what keeps a
789
+ * striped table from being a style per row, and it keeps the key its
790
+ * children share under, which an element-per-key would have taken from the
791
+ * whole subtree.
792
+ *
793
+ * The style is handed out shared, so nothing may write to it — nothing
794
+ * does after the cascade, and `rootStyle` does not go through here.
795
+ */
796
+ sharedStyleFor(el, parentStyle, parentKey, inFlexContainer) {
797
+ let key = `${parentKey}\u0001${inFlexContainer ? 1 : 0}`;
798
+ const tag = tagOf(el);
799
+ key += `\u0001${tag.length}:${tag}`;
800
+ if (this._index.hoverSensitive) {
801
+ if (this._pointer.hovered.has(el))
802
+ key += '\u0001:hover';
803
+ if (this._pointer.active.has(el))
804
+ key += '\u0001:active';
805
+ }
806
+ const attribs = el.attribs;
807
+ for (const name in attribs) {
808
+ const value = attribs[name];
809
+ key += `\u0001${name.length}:${name}=${value.length}:${value}`;
810
+ }
811
+ if (!this._shareable(el)) {
812
+ const matched = [];
813
+ const candidates = this._candidates(el, matched);
814
+ key += `\u0001${matched.join(',')}`;
815
+ let shared = this._sharedByMatch.get(key);
816
+ if (shared === undefined) {
817
+ shared = {
818
+ style: this._computeStyle(el, parentStyle, inFlexContainer, candidates),
819
+ key: this._nextShareKey++,
820
+ };
821
+ this._sharedByMatch.set(key, shared);
822
+ }
823
+ return shared;
824
+ }
825
+ let shared = this._shared.get(key);
826
+ if (shared === undefined) {
827
+ shared = {
828
+ style: this.styleFor(el, parentStyle, inFlexContainer),
829
+ key: this._nextShareKey++,
830
+ };
831
+ this._shared.set(key, shared);
832
+ }
833
+ return shared;
834
+ }
835
+ /** Whether no rule `UNSHAREABLE` names could reach this element: the same
836
+ * buckets `_candidates` looks in. */
837
+ _shareable(el) {
838
+ const index = this._index;
839
+ if (index.ownStyleEverywhere)
840
+ return false;
841
+ if (index.ownStyleTags.size && index.ownStyleTags.has(tagOf(el))) {
842
+ return false;
843
+ }
844
+ if (index.ownStyleIds.size) {
845
+ const id = attr(el, 'id');
846
+ if (id && index.ownStyleIds.has(id))
847
+ return false;
848
+ }
849
+ if (index.ownStyleClasses.size) {
850
+ const className = attr(el, 'class');
851
+ if (className) {
852
+ for (const name of className.split(/\s+/)) {
853
+ if (name && index.ownStyleClasses.has(name))
854
+ return false;
855
+ }
856
+ }
857
+ }
858
+ return true;
859
+ }
236
860
  /**
237
- * The computed style for one element, given its parent's. Called once per
238
- * element per style pass, in document order — the box builder drives it,
239
- * so there is no second traversal and no map of styles to allocate.
861
+ * The computed style for one element, given its parent's. The box builder
862
+ * asks for every element in document order, through `sharedStyleFor`, so
863
+ * a style is computed only for the elements with none to share.
240
864
  */
241
865
  styleFor(el, parentStyle, inFlexContainer) {
866
+ return this._computeStyle(el, parentStyle, inFlexContainer, this._candidates(el));
867
+ }
868
+ /**
869
+ * The style of an element's `::before` or `::after`, or null when it has
870
+ * none: no rule reaches it, or the rules that do leave `content` at
871
+ * `normal` or `none`, which make no box (CSS 2.1 12.2). It inherits from
872
+ * the element's own style, as a pseudo-element does, and a flex
873
+ * container's are flex items. Not shared: few elements have one, and where
874
+ * a rule gives every element one — a clearfix — the style is the cost of
875
+ * the box it makes.
876
+ */
877
+ pseudoStyleFor(el, which, elementStyle) {
878
+ const index = this._pseudo[which];
879
+ if (!index.size || !index.reaches(el))
880
+ return null;
881
+ const candidates = [];
882
+ this._matchInto(index, el, candidates);
883
+ if (!candidates.length)
884
+ return null;
885
+ // One that no rule gives a `content` is none, whatever else reaches it —
886
+ // `content` is not inherited, and its initial `normal` is nothing here.
887
+ // Tailwind's `*, ::before, ::after` reaches both of every element's.
888
+ if (!candidates.some(setsContent))
889
+ return null;
890
+ candidates.sort(byCascade);
891
+ const style = this._computeStyle(el, elementStyle, elementStyle.display === 'flex' || elementStyle.display === 'inline-flex', candidates);
892
+ return style.content === 'normal' || style.content === 'none'
893
+ ? null
894
+ : style;
895
+ }
896
+ /**
897
+ * The rules for an element's `::first-letter`, or null when none reaches
898
+ * it. They are matched against the element here, and its style computed
899
+ * later by `firstLetterStyle`, because the pseudo-element inherits from
900
+ * the box its letter turns out to be in — a `<span>` the block opens with,
901
+ * a `::before` — which the builder has not reached yet.
902
+ */
903
+ firstLetterRules(el) {
904
+ const index = this._pseudo['first-letter'];
905
+ if (!index.size || !index.reaches(el))
906
+ return null;
907
+ const candidates = [];
908
+ this._matchInto(index, el, candidates);
909
+ if (!candidates.length)
910
+ return null;
911
+ candidates.sort(byCascade);
912
+ return { el, candidates };
913
+ }
914
+ /** Whether any rule styles a `::first-line`: a document with none asks
915
+ * nothing more of its blocks. */
916
+ get hasFirstLine() {
917
+ return this._pseudo['first-line'].size > 0;
918
+ }
919
+ /**
920
+ * A list item's `::marker` style, inheriting from its own, or null when no
921
+ * rule reaches it (CSS Lists 3, 3.1): the colour and the font a bullet or
922
+ * a number is set in, and a `content` it is set as instead.
923
+ */
924
+ markerStyle(el, style) {
925
+ const index = this._pseudo.marker;
926
+ if (!index.size || !index.reaches(el))
927
+ return null;
928
+ const candidates = [];
929
+ this._matchInto(index, el, candidates);
930
+ if (!candidates.length)
931
+ return null;
932
+ candidates.sort(byCascade);
933
+ const out = this._computeStyle(el, style, false, candidates);
934
+ // a marker's direction is its own unless a rule says otherwise (the
935
+ // HTML style sheet's `::marker { unicode-bidi: isolate }`)
936
+ const set = candidates.some((c) => c.declarations.some((d) => d.prop === 'unicode-bidi'));
937
+ if (!set)
938
+ out.unicodeBidi = 'isolate';
939
+ return out;
940
+ }
941
+ /**
942
+ * An element's `::first-line` style, inheriting from its own, or null
943
+ * when no rule reaches it (CSS 2.1 5.12.1).
944
+ */
945
+ firstLineStyle(el, style) {
946
+ const index = this._pseudo['first-line'];
947
+ if (!index.size)
948
+ return null;
949
+ const candidates = [];
950
+ this._matchInto(index, el, candidates);
951
+ if (!candidates.length)
952
+ return null;
953
+ candidates.sort(byCascade);
954
+ return this._computeStyle(el, style, false, candidates);
955
+ }
956
+ /**
957
+ * The style of a `::first-letter` inside a box of `parentStyle`. It is an
958
+ * inline box, or a float where it floats, whatever the rules say of its
959
+ * `display` or its `position` (CSS 2.1 5.12.2).
960
+ */
961
+ firstLetterStyle(rules, parentStyle) {
962
+ const style = this._computeStyle(rules.el, parentStyle, false, rules.candidates);
963
+ style.display = style.float === 'none' ? 'inline' : 'block';
964
+ style.position = 'static';
965
+ return style;
966
+ }
967
+ /** The x-height of a style's font: the font's own, asked once per face,
968
+ * or half an em. */
969
+ _exOf(style) {
970
+ const key = faceKey(style);
971
+ let ex = this._xHeights.get(key);
972
+ if (ex === undefined) {
973
+ ex = this._xHeightOf?.(faceOf(style)) ?? NaN;
974
+ if (!(ex > 0))
975
+ ex = style.fontSize * 0.5;
976
+ this._xHeights.set(key, ex);
977
+ }
978
+ return ex;
979
+ }
980
+ /** The advance of a style's font's "0": the font's own, asked once per
981
+ * face, or half an em. */
982
+ _chOf(style) {
983
+ const key = faceKey(style);
984
+ let ch = this._zeroWidths.get(key);
985
+ if (ch === undefined) {
986
+ ch = this._zeroWidthOf?.(faceOf(style)) ?? NaN;
987
+ if (!(ch > 0))
988
+ ch = style.fontSize * 0.5;
989
+ this._zeroWidths.set(key, ch);
990
+ }
991
+ return ch;
992
+ }
993
+ /** A style's computed line height as a length, for `lh`: `normal` as
994
+ * its font's own, asked once per face, or 1.2em. */
995
+ _lineHeightOf(style) {
996
+ const set = style.lineHeight;
997
+ if (set !== 'normal') {
998
+ return style.lineHeightIsLength ? set : set * style.fontSize;
999
+ }
1000
+ const key = faceKey(style);
1001
+ let line = this._normalLines.get(key);
1002
+ if (line === undefined) {
1003
+ line = this._normalLineOf?.(faceOf(style)) ?? NaN;
1004
+ if (!(line > 0))
1005
+ line = style.fontSize * 1.2;
1006
+ this._normalLines.set(key, line);
1007
+ }
1008
+ return line;
1009
+ }
1010
+ /** `styleFor`, from the rules and hints already gathered for `el`. */
1011
+ _computeStyle(el, parentStyle, inFlexContainer, candidates) {
242
1012
  const style = inherit(parentStyle, this.initial);
243
- const candidates = this._candidates(el);
1013
+ // custom properties first, in cascade order, so every `var()` in the
1014
+ // declarations below finds the one that wins
1015
+ if (this._vars)
1016
+ style.custom = this._customFor(parentStyle, candidates);
244
1017
  // The unit context has to be built twice: once with the parent's font
245
1018
  // size, so a `font-size: 1.2em` in the cascade resolves against the
246
1019
  // right em, and again after the font size is settled so every *other*
@@ -248,28 +1021,150 @@ export class Cascade {
248
1021
  const ctxParent = {
249
1022
  em: parentStyle.fontSize,
250
1023
  rem: this.initial.fontSize,
1024
+ initial: this.initial,
251
1025
  vw: this.viewportWidth,
252
1026
  vh: this.viewportHeight,
253
1027
  scale: this.scale,
1028
+ ex: () => this._exOf(parentStyle),
1029
+ ch: () => this._chOf(parentStyle),
1030
+ families: this._mapFamilies,
1031
+ lh: () => this._lineHeightOf(parentStyle),
1032
+ rlh: () => this._lineHeightOf(this.initial),
254
1033
  };
1034
+ // The family, the weight and the slant go with the size: together they
1035
+ // pick the face an `ex`, a `ch` or an `lh` in any declaration is
1036
+ // measured in, however the declarations are ordered. A `width: 10ex`
1037
+ // in a sheet under an inline `font-weight: 900` was measured in the
1038
+ // family's regular face.
1039
+ let sized = false;
1040
+ let keyword = false;
255
1041
  for (const c of candidates) {
256
1042
  for (const d of pick(c)) {
257
- if (d.prop === 'font-size' || d.prop === 'font') {
258
- applyDeclaration(style, parentStyle, d.prop, d.value, ctxParent);
1043
+ if (d.prop === 'font-family' ||
1044
+ d.prop === 'font-weight' ||
1045
+ d.prop === 'font-style') {
1046
+ this._apply(style, parentStyle, d, ctxParent);
1047
+ }
1048
+ else if (d.prop === 'font-size' || d.prop === 'font') {
1049
+ // NaN until a size is set, so one the declaration did not take
1050
+ // leaves the size where it was, and says nothing of it
1051
+ const was = style.fontSize;
1052
+ style.fontSize = NaN;
1053
+ this._apply(style, parentStyle, d, ctxParent);
1054
+ if (Number.isNaN(style.fontSize)) {
1055
+ style.fontSize = was;
1056
+ }
1057
+ else {
1058
+ sized = true;
1059
+ keyword = !d.vars && sizeKeyword(d.prop, d.value);
1060
+ }
1061
+ }
1062
+ }
1063
+ }
1064
+ // The generic `monospace` on its own has a smaller size than the rest,
1065
+ // 13 pixels to their 16 (the "fixed" font size every browser keeps):
1066
+ // an element whose family becomes it, or stops being it, scales the
1067
+ // size it inherits by that, and a keyword size is read from the
1068
+ // smaller scale — the size it sets itself stays its own. Blink's
1069
+ // CheckForGenericFamilyChange, and why a `<pre>` in a browser is 13px.
1070
+ const mono = monospaceOnly(style.fontFamily);
1071
+ if (sized ? keyword && mono : mono !== monospaceOnly(parentStyle.fontFamily)) {
1072
+ style.fontSize *= mono ? FIXED_SIZE : 1 / FIXED_SIZE;
1073
+ }
1074
+ // The face as the first pass settled it. The second applies every
1075
+ // declaration again in cascade order, the font's among them, so the
1076
+ // style passes through the fonts of every rule on the way: read from
1077
+ // it, the `10ex` in `div { font-family: foo; width: 10ex }` was `foo`'s
1078
+ // under an inline `font-family: Ahem` that came after it.
1079
+ const face = {
1080
+ fontFamily: style.fontFamily,
1081
+ fontSize: style.fontSize,
1082
+ fontWeight: style.fontWeight,
1083
+ fontStyle: style.fontStyle,
1084
+ };
1085
+ let line = null;
1086
+ const ctx = {
1087
+ ...ctxParent,
1088
+ em: style.fontSize,
1089
+ ex: () => this._exOf(face),
1090
+ ch: () => this._chOf(face),
1091
+ lh: () => this._lineHeightOf(line ?? style),
1092
+ };
1093
+ // the line height, which an `lh` in any other declaration reads, ahead
1094
+ // of them — a `font` sets one too — and read as it is then, for the
1095
+ // same reason as the face: set again with the rest, in its place among
1096
+ // them. A `font` sets the size as well, which the first pass settled.
1097
+ if (this._lh) {
1098
+ for (const c of candidates) {
1099
+ for (const d of pick(c)) {
1100
+ if (d.prop === 'line-height' || d.prop === 'font') {
1101
+ this._apply(style, parentStyle, d, ctx);
1102
+ }
259
1103
  }
260
1104
  }
1105
+ style.fontSize = face.fontSize;
1106
+ line = {
1107
+ ...face,
1108
+ lineHeight: style.lineHeight,
1109
+ lineHeightIsLength: style.lineHeightIsLength,
1110
+ };
261
1111
  }
262
- const ctx = { ...ctxParent, em: style.fontSize };
1112
+ const settled = style.fontSize;
263
1113
  for (const c of candidates) {
264
1114
  for (const d of pick(c)) {
265
- if (d.prop === 'font-size')
1115
+ if (d.prop === 'font-size' || d.custom)
266
1116
  continue;
267
- applyDeclaration(style, parentStyle, d.prop, d.value, ctx);
1117
+ this._apply(style, parentStyle, d, ctx);
268
1118
  }
269
1119
  }
1120
+ // A `font` is applied again, to keep its other longhands in cascade
1121
+ // order, and it sets the size it names as well: the size is the first
1122
+ // pass's, which the declarations that outrank the `font` had their say
1123
+ // in, and the scale after them. `span { font: 15px/1 Ahem }` under
1124
+ // `.b > span { font-size: 3.75em }` was 15px, its `em`s 3.75 of its
1125
+ // parent's.
1126
+ style.fontSize = settled;
1127
+ // A table never keeps HTML's alignment, `-webkit-center` and its kin:
1128
+ // the `<td align="center">` every mail centres its body table in
1129
+ // centres the table, and the text in its cells stays at their start —
1130
+ // as Blink resets it ("tables never support the -webkit-* values for
1131
+ // text-align"). An author's own `text-align: center` is not HTML's, and
1132
+ // is inherited into the table as it always was.
1133
+ if (style.alignBlocks !== null && tagOf(el) === 'table') {
1134
+ style.textAlign = 'start';
1135
+ style.alignBlocks = null;
1136
+ }
1137
+ settleClamp(style, parentStyle);
1138
+ settleOverflow(style);
1139
+ settleContentVisibility(style);
270
1140
  blockify(style, inFlexContainer);
1141
+ decorate(style);
1142
+ // which faces of the document's own families this family, weight and
1143
+ // slant ask for — known only now, with all three computed
1144
+ this._families?.note(style);
271
1145
  return style;
272
1146
  }
1147
+ /** One declaration, its `var()`s replaced first. One that names a
1148
+ * custom property with no value, and has no fallback, is invalid at
1149
+ * computed-value time, and the property is as though `unset`. */
1150
+ _apply(style, parentStyle, d, ctx) {
1151
+ if (!d.vars) {
1152
+ applyDeclaration(style, parentStyle, d.prop, d.value, ctx);
1153
+ return;
1154
+ }
1155
+ // unset first: a value that does not parse once it is substituted is
1156
+ // invalid at computed-value time, and does not leave the property what
1157
+ // an earlier declaration gave it
1158
+ if (isInherited(d.prop)) {
1159
+ applyDeclaration(style, parentStyle, d.prop, 'inherit', ctx);
1160
+ }
1161
+ else {
1162
+ initialOne(style, this.initial, d.prop);
1163
+ }
1164
+ const value = substituteIn(d.value, style.custom);
1165
+ if (value !== null)
1166
+ applyDeclaration(style, parentStyle, d.prop, value, ctx);
1167
+ }
273
1168
  /**
274
1169
  * The root's style — nothing above it to inherit from.
275
1170
  *
@@ -278,24 +1173,44 @@ export class Cascade {
278
1173
  * a `<body>` would have had: the UA sheet's margin and font, and any
279
1174
  * author `body { … }` rule. Without it a fragment renders hard against the
280
1175
  * left edge while the same markup inside `<html><body>` does not, which
281
- * reads as a bug in the renderer rather than as a missing element.
1176
+ * reads as a bug in the renderer rather than as a missing element. With
1177
+ * no `<html>` either, the body inherits from an `<html>` that author
1178
+ * `html { … }` rules have styled, as the one a browser implies would be.
1179
+ *
1180
+ * A document with a `<body>` and no `<html>` — the usual shape of a page
1181
+ * that starts `<!DOCTYPE html><title>` — has the root box standing in for
1182
+ * that implied `<html>` instead, so an `html { font-size }` still reaches
1183
+ * the body. Taken as the initial style, the rule matched nothing and the
1184
+ * whole page came out at the theme's size.
1185
+ *
1186
+ * `html` is the implied `<html>`'s own style where the root box stands in
1187
+ * for the body inside it, and null otherwise: what an `html { … }` or a
1188
+ * `:root { … }` rule gives it that is not inherited has no box to be
1189
+ * drawn on, and its background is the canvas's (`paintDocument`).
282
1190
  */
283
- rootStyle(hasBody) {
284
- const style = { ...this.initial };
1191
+ rootStyle(hasBody, hasHtml = true) {
1192
+ const style = copyStyle(this.initial);
285
1193
  style.display = 'block';
286
- if (hasBody)
287
- return style;
1194
+ if (hasBody && hasHtml)
1195
+ return { style, html: null };
288
1196
  const synthetic = new DomElement('body', {}, []);
289
- const bodyStyle = this.styleFor(synthetic, style, false);
1197
+ let parent = style;
1198
+ let html = null;
1199
+ if (!hasHtml) {
1200
+ const element = new DomElement('html', {}, hasBody ? [] : [synthetic]);
1201
+ synthetic.parent = element;
1202
+ parent = this.styleFor(element, style, false);
1203
+ if (hasBody)
1204
+ return { style: asRoot(parent), html: null };
1205
+ html = parent;
1206
+ }
290
1207
  // Only the box the body would have drawn is taken, not its layout role:
291
1208
  // the root is still the initial containing block.
292
- bodyStyle.display = 'block';
293
- bodyStyle.position = 'static';
294
- bodyStyle.float = 'none';
295
- return bodyStyle;
1209
+ return { style: asRoot(this.styleFor(synthetic, parent, false)), html };
296
1210
  }
297
- _candidates(el) {
298
- const out = [];
1211
+ /** The rules of `index` that match `el`, pushed onto `out` as candidates,
1212
+ * and their ids onto `matched` in the order they were tried. */
1213
+ _matchInto(index, el, out, matched) {
299
1214
  // A media query's width is CSS pixels; the viewport is kept in device.
300
1215
  const width = this.viewportWidth / this.scale;
301
1216
  const consider = (bucket) => {
@@ -308,10 +1223,7 @@ export class Cascade {
308
1223
  if (!indexed.compiled) {
309
1224
  indexed.compiled = true;
310
1225
  try {
311
- indexed.match = compile(rule.selector, {
312
- adapter: this._adapter,
313
- xmlMode: false,
314
- });
1226
+ indexed.match = compileSelector(rule.selector, this._adapter);
315
1227
  }
316
1228
  catch {
317
1229
  // A selector this matcher does not know (`::-moz-…`, a CSS4 form
@@ -322,26 +1234,35 @@ export class Cascade {
322
1234
  }
323
1235
  if (!indexed.match || !indexed.match(el))
324
1236
  continue;
1237
+ matched?.push(indexed.id);
325
1238
  const origin = rule.order < 0 ? Origin.UserAgent : Origin.Author;
326
1239
  pushRule(out, rule, origin);
327
1240
  }
328
1241
  };
329
1242
  const id = attr(el, 'id');
330
1243
  if (id)
331
- consider(this._index.byId.get(id));
1244
+ consider(index.byId.get(id));
332
1245
  const className = attr(el, 'class');
333
1246
  if (className) {
334
1247
  for (const name of className.split(/\s+/)) {
335
1248
  if (name)
336
- consider(this._index.byClass.get(name));
1249
+ consider(index.byClass.get(name));
337
1250
  }
338
1251
  }
339
- consider(this._index.byTag.get(tagOf(el)));
340
- consider(this._index.universal);
1252
+ consider(index.byTag.get(tagOf(el)));
1253
+ consider(index.universal);
1254
+ }
1255
+ /** Every rule and hint that applies to `el`, in cascade order — and, into
1256
+ * `matched` when it is passed, the ids of the rules, in the order they
1257
+ * were tried. */
1258
+ _candidates(el, matched) {
1259
+ const out = [];
1260
+ this._matchInto(this._index, el, out, matched);
341
1261
  const hints = presentationHints(el);
342
1262
  if (hints.length) {
343
1263
  out.push({
344
1264
  origin: Origin.Presentation,
1265
+ layer: null,
345
1266
  specificity: 0,
346
1267
  order: 0,
347
1268
  declarations: hints,
@@ -351,11 +1272,17 @@ export class Cascade {
351
1272
  const inline = attr(el, 'style');
352
1273
  if (inline) {
353
1274
  const declarations = parseDeclarations(inline);
1275
+ if (!this._vars && usesVars(declarations))
1276
+ this._vars = true;
1277
+ this._noteViewportUnits(declarations);
1278
+ if (!this._lh && usesLh(declarations))
1279
+ this._lh = true;
354
1280
  const normal = declarations.filter((d) => !d.important);
355
1281
  const important = declarations.filter((d) => d.important);
356
1282
  if (normal.length) {
357
1283
  out.push({
358
1284
  origin: Origin.Inline,
1285
+ layer: null,
359
1286
  specificity: 0,
360
1287
  order: 0,
361
1288
  declarations: normal,
@@ -365,6 +1292,7 @@ export class Cascade {
365
1292
  if (important.length) {
366
1293
  out.push({
367
1294
  origin: Origin.InlineImportant,
1295
+ layer: null,
368
1296
  specificity: 0,
369
1297
  order: 0,
370
1298
  declarations: important,
@@ -376,6 +1304,144 @@ export class Cascade {
376
1304
  return out;
377
1305
  }
378
1306
  }
1307
+ /** An implied element's style as the root box's: its look, not its role —
1308
+ * the root is still a block, in flow, and the initial containing block. */
1309
+ function asRoot(style) {
1310
+ style.display = 'block';
1311
+ style.position = 'static';
1312
+ style.float = 'none';
1313
+ return style;
1314
+ }
1315
+ /** `:root` is the `<html>` element: the one a browser implies around a
1316
+ * fragment, whose style the root box takes (`rootStyle`), and never a
1317
+ * fragment's top-level elements, which css-select would take for it. */
1318
+ const PSEUDOS = {
1319
+ root: (el) => el.name === 'html' && !(el.parent && isTag(el.parent)),
1320
+ // css-select's, but its ranges and tags lower-cased as ASCII has it, and
1321
+ // no other script (CSS 2.1 4.1.3): Unicode's took `:lang(\u212Al)`, a
1322
+ // Kelvin sign for the K, for `:lang(kl)`
1323
+ lang: (el, code) => {
1324
+ const ranges = (code ?? '')
1325
+ .split(',')
1326
+ .map((range) => range.trim().replace(/^['"]|['"]$/g, ''))
1327
+ .filter((range) => range.length > 0)
1328
+ .map((range) => asciiLower(range).split('-'));
1329
+ let root = el;
1330
+ for (let node = el; node;) {
1331
+ const value = node.attribs['xml:lang'] ?? node.attribs.lang;
1332
+ if (value != null) {
1333
+ if (!value)
1334
+ return ranges.some((range) => range[0] === '');
1335
+ const tag = asciiLower(value).split('-');
1336
+ return ranges.some((range) => langRangeMatches(tag, range));
1337
+ }
1338
+ root = node;
1339
+ const parent = node.parent;
1340
+ node = parent && isTag(parent) ? parent : null;
1341
+ }
1342
+ // with no element saying, the document's, as its `<meta>` sets it —
1343
+ // searched from the document, where a fragment's `<meta>` is a
1344
+ // sibling of what it covers
1345
+ const pragma = pragmaLanguage(root.parent ?? root);
1346
+ if (!pragma)
1347
+ return ranges.some((range) => range[0] === '');
1348
+ const tag = asciiLower(pragma).split('-');
1349
+ return ranges.some((range) => langRangeMatches(tag, range));
1350
+ },
1351
+ };
1352
+ /** A length in a unit of the viewport: a number, then the unit, and no
1353
+ * more of a name after it. */
1354
+ const VIEWPORT_UNIT = /\d(?:vw|vh|vmin|vmax)(?![\w-])/i;
1355
+ const VIEWPORT_UNITS = /\d(vw|vh|vmin|vmax)(?![\w-])/gi;
1356
+ /**
1357
+ * A selector with its `[attr~=""]` made one that matches nothing: an empty
1358
+ * string is no word of a list, and such a selector represents nothing
1359
+ * (Selectors 3, 6.3.1) — which the matcher's `~=` is not told, taking the
1360
+ * empty word for one between two spaces, or at either end of none.
1361
+ */
1362
+ function noEmptyWords(selector) {
1363
+ if (!selector.includes('~='))
1364
+ return selector;
1365
+ return selector.replace(/\[[^\]"']*~=\s*(?:""|'')\s*(?:[is]\s*)?\]/gi, ':not(*)');
1366
+ }
1367
+ /**
1368
+ * A document's language as a `<meta http-equiv="content-language">` sets
1369
+ * it — HTML's pragma-set default language, the last such `<meta>`'s
1370
+ * `content` up to its first white space, and none where it lists more
1371
+ * than one — by the document's root element, found once.
1372
+ */
1373
+ function pragmaLanguage(root) {
1374
+ let lang = PRAGMA_LANGUAGE.get(root);
1375
+ if (lang !== undefined)
1376
+ return lang;
1377
+ lang = '';
1378
+ const stack = [root];
1379
+ while (stack.length) {
1380
+ const node = stack.pop();
1381
+ if (isTag(node) &&
1382
+ node.name === 'meta' &&
1383
+ node.attribs['http-equiv']?.trim().toLowerCase() === 'content-language') {
1384
+ const content = node.attribs.content;
1385
+ if (content !== undefined && !content.includes(',')) {
1386
+ const candidate = content.trim().split(/[\t\n\f\r ]/)[0];
1387
+ if (candidate)
1388
+ lang = candidate;
1389
+ }
1390
+ }
1391
+ // in document order: the last one pushed is taken first
1392
+ for (let i = node.children.length - 1; i >= 0; i -= 1) {
1393
+ const child = node.children[i];
1394
+ if (isTag(child))
1395
+ stack.push(child);
1396
+ }
1397
+ }
1398
+ PRAGMA_LANGUAGE.set(root, lang);
1399
+ return lang;
1400
+ }
1401
+ const PRAGMA_LANGUAGE = new WeakMap();
1402
+ /** RFC 4647's extended filtering, of a tag's subtags by a range's, as
1403
+ * css-select does it. */
1404
+ function langRangeMatches(tag, range) {
1405
+ if (range[0] !== '*' && range[0] !== tag[0])
1406
+ return false;
1407
+ let at = 1;
1408
+ for (let r = 1; r < range.length; r += 1) {
1409
+ if (range[r] === '*')
1410
+ continue;
1411
+ while (at < tag.length && tag[at] !== range[r]) {
1412
+ if (tag[at++].length <= 1)
1413
+ return false;
1414
+ }
1415
+ if (at >= tag.length)
1416
+ return false;
1417
+ at += 1;
1418
+ }
1419
+ return true;
1420
+ }
1421
+ /** Whether any of these declarations sets a custom property or reads one. */
1422
+ function usesVars(declarations) {
1423
+ for (const d of declarations)
1424
+ if (d.custom || d.vars)
1425
+ return true;
1426
+ return false;
1427
+ }
1428
+ /** Whether any of the declarations has a length in `lh` or `rlh`. */
1429
+ function usesLh(declarations) {
1430
+ for (const d of declarations)
1431
+ if (LH.test(d.value))
1432
+ return true;
1433
+ return false;
1434
+ }
1435
+ const LH = /\d\.?r?lh\b/i;
1436
+ /** Whether a candidate declares `content`. */
1437
+ function setsContent(c) {
1438
+ for (const d of pick(c))
1439
+ if (d.prop === 'content')
1440
+ return true;
1441
+ return false;
1442
+ }
1443
+ /** Whether a rule's declarations set a custom property, by the array. */
1444
+ const CUSTOM_IN = new WeakMap();
379
1445
  function pick(c) {
380
1446
  return c.only < 0 ? c.declarations : [c.declarations[c.only]];
381
1447
  }
@@ -390,6 +1456,7 @@ function pushRule(out, rule, origin) {
390
1456
  if (!hasImportant) {
391
1457
  out.push({
392
1458
  origin,
1459
+ layer: rule.layer,
393
1460
  specificity: rule.specificity,
394
1461
  order: rule.order,
395
1462
  declarations: rule.declarations,
@@ -407,6 +1474,7 @@ function pushRule(out, rule, origin) {
407
1474
  ? Origin.UserAgent
408
1475
  : Origin.AuthorImportant
409
1476
  : origin,
1477
+ layer: rule.layer,
410
1478
  specificity: rule.specificity,
411
1479
  order: rule.order,
412
1480
  declarations: rule.declarations,
@@ -417,10 +1485,34 @@ function pushRule(out, rule, origin) {
417
1485
  function byCascade(a, b) {
418
1486
  if (a.origin !== b.origin)
419
1487
  return a.origin - b.origin;
1488
+ if (a.layer !== b.layer) {
1489
+ // a later layer wins over an earlier one whatever the specificity, and
1490
+ // for `!important` an earlier one does (CSS Cascade 5, 6.4)
1491
+ const by = compareLayers(a.layer, b.layer);
1492
+ if (by !== 0)
1493
+ return a.origin >= Origin.AuthorImportant ? -by : by;
1494
+ }
420
1495
  if (a.specificity !== b.specificity)
421
1496
  return a.specificity - b.specificity;
422
1497
  return a.order - b.order;
423
1498
  }
1499
+ /**
1500
+ * Which of two layers wins for a normal declaration: a later one over an
1501
+ * earlier one, and a rule in no layer over any, as a rule directly in a
1502
+ * layer is over the layers inside it.
1503
+ */
1504
+ function compareLayers(a, b) {
1505
+ if (a === null)
1506
+ return b === null ? 0 : 1;
1507
+ if (b === null)
1508
+ return -1;
1509
+ const n = Math.min(a.length, b.length);
1510
+ for (let i = 0; i < n; i += 1) {
1511
+ if (a[i] !== b[i])
1512
+ return a[i] - b[i];
1513
+ }
1514
+ return b.length - a.length;
1515
+ }
424
1516
  /**
425
1517
  * The presentational attributes, as declarations. They sit above the UA
426
1518
  * sheet and below every author rule, which is where HTML says they go — and
@@ -437,9 +1529,44 @@ function presentationHints(el) {
437
1529
  const align = attr(el, 'align');
438
1530
  if (align) {
439
1531
  const v = align.toLowerCase();
440
- if (tag === 'img' && (v === 'left' || v === 'right'))
1532
+ const side = v === 'middle' ? 'center' : v;
1533
+ if ((tag === 'img' || tag === 'table') && (v === 'left' || v === 'right')) {
441
1534
  push('float', v);
442
- else if (v === 'center' || v === 'middle')
1535
+ }
1536
+ else if (tag === 'img') {
1537
+ // an image's other alignments are its line's
1538
+ const va = IMAGE_ALIGN[v];
1539
+ if (va)
1540
+ push('vertical-align', va);
1541
+ }
1542
+ else if (tag === 'hr') {
1543
+ // a rule narrower than its line stands where it is aligned
1544
+ if (v === 'left') {
1545
+ push('margin-left', '0');
1546
+ push('margin-right', 'auto');
1547
+ }
1548
+ else if (v === 'right') {
1549
+ push('margin-left', 'auto');
1550
+ push('margin-right', '0');
1551
+ }
1552
+ }
1553
+ else if (tag === 'table') {
1554
+ // a table's `align` places the table and leaves its text alone: set
1555
+ // as its text's, `<table align="center">` — the frame of nearly
1556
+ // every mail — stood at the left with its cells' text centred
1557
+ if (side === 'center') {
1558
+ push('margin-left', 'auto');
1559
+ push('margin-right', 'auto');
1560
+ }
1561
+ }
1562
+ else if (ALIGNS_BLOCKS.has(tag) &&
1563
+ (side === 'center' || side === 'left' || side === 'right')) {
1564
+ // these align the blocks in them as well as their text (HTML's
1565
+ // rendering, "align descendants"): the table a `<td align="center">`
1566
+ // or a `<div align="center">` holds is centred in it
1567
+ push('text-align', `-webkit-${side}`);
1568
+ }
1569
+ else if (side === 'center')
443
1570
  push('text-align', 'center');
444
1571
  else if (v === 'left' || v === 'right' || v === 'justify')
445
1572
  push('text-align', v);
@@ -447,9 +1574,42 @@ function presentationHints(el) {
447
1574
  const valign = attr(el, 'valign');
448
1575
  if (valign)
449
1576
  push('vertical-align', valign.toLowerCase());
1577
+ // HTML's `dir` is its element's `direction`: `<table dir="rtl">` starts
1578
+ // at the right, and a Hebrew or an Arabic message is written this way
1579
+ const dir = attr(el, 'dir')?.toLowerCase();
1580
+ if (dir === 'rtl' || dir === 'ltr')
1581
+ push('direction', dir);
1582
+ // and it isolates the element from the text around it, or with `auto`
1583
+ // takes its first strong letter's direction (HTML's UA sheet: `[dir]`,
1584
+ // `[dir=auto i]`, `bdo[dir]`)
1585
+ if (dir !== undefined) {
1586
+ push('unicode-bidi', tag === 'bdo'
1587
+ ? 'isolate-override'
1588
+ : dir === 'auto'
1589
+ ? 'plaintext'
1590
+ : 'isolate');
1591
+ }
450
1592
  const bgcolor = attr(el, 'bgcolor');
451
1593
  if (bgcolor)
452
1594
  push('background-color', bgcolor);
1595
+ if (BACKGROUNDS.has(tag)) {
1596
+ const background = attr(el, 'background')?.trim();
1597
+ if (background) {
1598
+ push('background-image', `url("${background.replace(/["\\\n]/g, '')}")`);
1599
+ }
1600
+ }
1601
+ if (tag === 'body') {
1602
+ // the text's colour, and the links'
1603
+ const text = attr(el, 'text');
1604
+ if (text)
1605
+ push('color', text);
1606
+ }
1607
+ else if (tag === 'a' && attr(el, 'href') !== undefined) {
1608
+ const body = closestBody(el);
1609
+ const link = body ? attr(body, 'link') : undefined;
1610
+ if (link)
1611
+ push('color', link);
1612
+ }
453
1613
  const color = attr(el, 'color');
454
1614
  if (color && (tag === 'font' || tag === 'basefont'))
455
1615
  push('color', color);
@@ -472,6 +1632,27 @@ function presentationHints(el) {
472
1632
  const height = attr(el, 'height');
473
1633
  if (height)
474
1634
  push('height', lengthAttr(height));
1635
+ // and, both numbers, an image's the ratio it has before it loads, or
1636
+ // where it has none of its own (HTML 15.4.3, "map to the aspect-ratio
1637
+ // property")
1638
+ if (width && height && RATIO_SIZED.has(tag)) {
1639
+ const w = parseFloat(width);
1640
+ const h = parseFloat(height);
1641
+ if (w > 0 && h > 0 && !/%/.test(width + height)) {
1642
+ push('aspect-ratio', `auto ${w} / ${h}`);
1643
+ }
1644
+ }
1645
+ }
1646
+ else if (tag === 'svg') {
1647
+ // an SVG's are CSS lengths of their own, units and all (SVG 2, 5.1.1)
1648
+ const width = attr(el, 'width');
1649
+ const w = width ? svgSizeHint(width) : null;
1650
+ if (w)
1651
+ push('width', w);
1652
+ const height = attr(el, 'height');
1653
+ const h = height ? svgSizeHint(height) : null;
1654
+ if (h)
1655
+ push('height', h);
475
1656
  }
476
1657
  if (tag === 'table') {
477
1658
  const border = attr(el, 'border');
@@ -496,15 +1677,54 @@ function presentationHints(el) {
496
1677
  const noshade = attr(el, 'noshade');
497
1678
  if (noshade !== undefined)
498
1679
  push('border-top-width', '2px');
1680
+ // the rule's colour and its thickness
1681
+ const color = attr(el, 'color');
1682
+ if (color)
1683
+ push('border-top-color', color);
1684
+ const size = attr(el, 'size');
1685
+ if (size && Number(size) > 0)
1686
+ push('border-top-width', `${Number(size)}px`);
1687
+ }
1688
+ if ((tag === 'td' || tag === 'th') && attr(el, 'nowrap') !== undefined) {
1689
+ push('white-space', 'nowrap');
1690
+ }
1691
+ if (tag === 'br') {
1692
+ // below the floats: `<br clear="all">` after a floated image
1693
+ const clear = attr(el, 'clear')?.toLowerCase();
1694
+ if (clear === 'all' || clear === 'both')
1695
+ push('clear', 'both');
1696
+ else if (clear === 'left' || clear === 'right')
1697
+ push('clear', clear);
499
1698
  }
500
1699
  if (tag === 'ol') {
501
1700
  const type = attr(el, 'type');
502
1701
  const mapped = OL_TYPES[type ?? ''];
503
1702
  if (mapped)
504
1703
  push('list-style-type', mapped);
505
- const start = attr(el, 'start');
506
- if (start)
507
- push('counter-reset', start);
1704
+ // the number a list starts at is its `list-item` counter's, one before
1705
+ // it, or counting down one after it (HTML's rendering, "Lists")
1706
+ const start = htmlInteger(attr(el, 'start'));
1707
+ if (start !== null) {
1708
+ push('counter-reset', attr(el, 'reversed') !== undefined
1709
+ ? `reversed(list-item) ${start + 1}`
1710
+ : `list-item ${start - 1}`);
1711
+ }
1712
+ }
1713
+ if (tag === 'ul') {
1714
+ const type = attr(el, 'type')?.toLowerCase();
1715
+ if (type && BULLETS.has(type))
1716
+ push('list-style-type', type);
1717
+ }
1718
+ if (tag === 'li') {
1719
+ // an item's `value` is the number it goes on from
1720
+ const value = htmlInteger(attr(el, 'value'));
1721
+ if (value !== null)
1722
+ push('counter-set', `list-item ${value}`);
1723
+ const type = attr(el, 'type');
1724
+ const mapped = OL_TYPES[type ?? ''] ??
1725
+ (type && BULLETS.has(type.toLowerCase()) ? type.toLowerCase() : null);
1726
+ if (mapped)
1727
+ push('list-style-type', mapped);
508
1728
  }
509
1729
  if (tag === 'img' || tag === 'object') {
510
1730
  const hspace = attr(el, 'hspace');
@@ -520,6 +1740,51 @@ function presentationHints(el) {
520
1740
  }
521
1741
  return out;
522
1742
  }
1743
+ /** An image's `align` other than a side, as its `vertical-align`. */
1744
+ const IMAGE_ALIGN = {
1745
+ top: 'top',
1746
+ texttop: 'text-top',
1747
+ middle: 'middle',
1748
+ absmiddle: 'middle',
1749
+ center: 'middle',
1750
+ bottom: 'baseline',
1751
+ baseline: 'baseline',
1752
+ absbottom: 'bottom',
1753
+ };
1754
+ /** The elements whose `align` aligns the blocks in them too. */
1755
+ const ALIGNS_BLOCKS = new Set([
1756
+ 'div',
1757
+ 'caption',
1758
+ 'thead',
1759
+ 'tbody',
1760
+ 'tfoot',
1761
+ 'tr',
1762
+ 'td',
1763
+ 'th',
1764
+ ]);
1765
+ /** The elements a `background` attribute gives a background image. */
1766
+ const BACKGROUNDS = new Set([
1767
+ 'body',
1768
+ 'table',
1769
+ 'thead',
1770
+ 'tbody',
1771
+ 'tfoot',
1772
+ 'tr',
1773
+ 'td',
1774
+ 'th',
1775
+ ]);
1776
+ function closestBody(el) {
1777
+ let node = el.parent;
1778
+ while (node) {
1779
+ if (node.type === 'tag' && node.name === 'body') {
1780
+ return node;
1781
+ }
1782
+ node = node.parent;
1783
+ }
1784
+ return null;
1785
+ }
1786
+ /** What the `width` and `height` attributes give a ratio as well. */
1787
+ const RATIO_SIZED = new Set(['img', 'video']);
523
1788
  const SIZED = new Set([
524
1789
  'img',
525
1790
  'table',
@@ -529,7 +1794,6 @@ const SIZED = new Set([
529
1794
  'colgroup',
530
1795
  'iframe',
531
1796
  'video',
532
- 'canvas',
533
1797
  'object',
534
1798
  'embed',
535
1799
  'hr',
@@ -541,6 +1805,17 @@ const OL_TYPES = {
541
1805
  i: 'lower-roman',
542
1806
  I: 'upper-roman',
543
1807
  };
1808
+ /** What a `<ul type>` or an `<li type>` names that is not a number. */
1809
+ const BULLETS = new Set(['disc', 'circle', 'square', 'none']);
1810
+ /** An attribute read as HTML reads an integer: its leading digits, signed,
1811
+ * whatever follows them; null where it has none. A counter is a 32-bit
1812
+ * integer in a browser, and one past that is kept to it. */
1813
+ function htmlInteger(value) {
1814
+ const match = value === undefined ? null : /^\s*([+-]?\d+)/.exec(value);
1815
+ if (!match)
1816
+ return null;
1817
+ return Math.max(-2147483648, Math.min(2147483647, Number(match[1])));
1818
+ }
544
1819
  const FONT_SIZE_STEPS = [1, 0.63, 0.82, 1, 1.13, 1.5, 2, 3];
545
1820
  /** A presentational length: bare numbers are pixels, `50%` stays a percent. */
546
1821
  function lengthAttr(value) {
@@ -559,4 +1834,35 @@ function closestTable(el) {
559
1834
  }
560
1835
  return null;
561
1836
  }
1837
+ /** The generic fixed-width family's size to everyone else's: 13 to 16. */
1838
+ const FIXED_SIZE = 13 / 16;
1839
+ /** Whether a family list is the generic `monospace` and nothing else,
1840
+ * which has the smaller size; a list with a fallback after it does not,
1841
+ * so `monospace, monospace` keeps the size it had. */
1842
+ function monospaceOnly(family) {
1843
+ return family.length === 9 && family.toLowerCase() === 'monospace';
1844
+ }
1845
+ /** Whether a `font-size`, or the size in a `font`, is an absolute-size
1846
+ * keyword, or `initial`, which is `medium`. */
1847
+ function sizeKeyword(prop, value) {
1848
+ const words = value.trim().toLowerCase();
1849
+ if (prop === 'font-size')
1850
+ return ABSOLUTE_SIZES.has(words);
1851
+ for (const word of words.split(/[\s/]+/)) {
1852
+ if (ABSOLUTE_SIZES.has(word))
1853
+ return true;
1854
+ }
1855
+ return false;
1856
+ }
1857
+ const ABSOLUTE_SIZES = new Set([
1858
+ 'xx-small',
1859
+ 'x-small',
1860
+ 'small',
1861
+ 'medium',
1862
+ 'large',
1863
+ 'x-large',
1864
+ 'xx-large',
1865
+ 'xxx-large',
1866
+ 'initial',
1867
+ ]);
562
1868
  //# sourceMappingURL=cascade.js.map