@inhuman.tools/quickdraw 0.0.0-bootstrap.0 → 0.2.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 (522) hide show
  1. package/LICENSE.md +7 -0
  2. package/README.md +102 -1
  3. package/aurora.vert +168 -0
  4. package/dist/bench/host.d.ts +62 -0
  5. package/dist/bench/host.d.ts.map +1 -0
  6. package/dist/bench/host.js +77 -0
  7. package/dist/bench/host.js.map +1 -0
  8. package/dist/bench/main.d.ts +6 -0
  9. package/dist/bench/main.d.ts.map +1 -0
  10. package/dist/bench/main.js +118 -0
  11. package/dist/bench/main.js.map +1 -0
  12. package/dist/bench/model.d.ts +36 -0
  13. package/dist/bench/model.d.ts.map +1 -0
  14. package/dist/bench/model.js +112 -0
  15. package/dist/bench/model.js.map +1 -0
  16. package/dist/bench/tea.d.ts +14 -0
  17. package/dist/bench/tea.d.ts.map +1 -0
  18. package/dist/bench/tea.js +6 -0
  19. package/dist/bench/tea.js.map +1 -0
  20. package/dist/bench/view.d.ts +9 -0
  21. package/dist/bench/view.d.ts.map +1 -0
  22. package/dist/bench/view.js +88 -0
  23. package/dist/bench/view.js.map +1 -0
  24. package/dist/bench/worker.d.ts +6 -0
  25. package/dist/bench/worker.d.ts.map +1 -0
  26. package/dist/bench/worker.js +46 -0
  27. package/dist/bench/worker.js.map +1 -0
  28. package/dist/buffer/bgfx-mark.d.ts +32 -0
  29. package/dist/buffer/bgfx-mark.d.ts.map +1 -0
  30. package/dist/buffer/bgfx-mark.js +43 -0
  31. package/dist/buffer/bgfx-mark.js.map +1 -0
  32. package/dist/buffer/index.d.ts +191 -0
  33. package/dist/buffer/index.d.ts.map +1 -0
  34. package/dist/buffer/index.js +353 -0
  35. package/dist/buffer/index.js.map +1 -0
  36. package/dist/capabilities/env.d.ts +23 -0
  37. package/dist/capabilities/env.d.ts.map +1 -0
  38. package/dist/capabilities/env.js +35 -0
  39. package/dist/capabilities/env.js.map +1 -0
  40. package/dist/capabilities/index.d.ts +13 -0
  41. package/dist/capabilities/index.d.ts.map +1 -0
  42. package/dist/capabilities/index.js +13 -0
  43. package/dist/capabilities/index.js.map +1 -0
  44. package/dist/capabilities/parse.d.ts +41 -0
  45. package/dist/capabilities/parse.d.ts.map +1 -0
  46. package/dist/capabilities/parse.js +86 -0
  47. package/dist/capabilities/parse.js.map +1 -0
  48. package/dist/capabilities/probe.d.ts +37 -0
  49. package/dist/capabilities/probe.d.ts.map +1 -0
  50. package/dist/capabilities/probe.js +75 -0
  51. package/dist/capabilities/probe.js.map +1 -0
  52. package/dist/capabilities/sequences.d.ts +33 -0
  53. package/dist/capabilities/sequences.d.ts.map +1 -0
  54. package/dist/capabilities/sequences.js +36 -0
  55. package/dist/capabilities/sequences.js.map +1 -0
  56. package/dist/capabilities/types.d.ts +25 -0
  57. package/dist/capabilities/types.d.ts.map +1 -0
  58. package/dist/capabilities/types.js +5 -0
  59. package/dist/capabilities/types.js.map +1 -0
  60. package/dist/index.d.ts +11 -0
  61. package/dist/index.d.ts.map +1 -0
  62. package/dist/index.js +11 -0
  63. package/dist/index.js.map +1 -0
  64. package/dist/input/decode.d.ts +167 -0
  65. package/dist/input/decode.d.ts.map +1 -0
  66. package/dist/input/decode.js +825 -0
  67. package/dist/input/decode.js.map +1 -0
  68. package/dist/input/draft.d.ts +85 -0
  69. package/dist/input/draft.d.ts.map +1 -0
  70. package/dist/input/draft.js +276 -0
  71. package/dist/input/draft.js.map +1 -0
  72. package/dist/input/index.d.ts +3 -0
  73. package/dist/input/index.d.ts.map +1 -0
  74. package/dist/input/index.js +3 -0
  75. package/dist/input/index.js.map +1 -0
  76. package/dist/input/prompt-blocks.d.ts +58 -0
  77. package/dist/input/prompt-blocks.d.ts.map +1 -0
  78. package/dist/input/prompt-blocks.js +48 -0
  79. package/dist/input/prompt-blocks.js.map +1 -0
  80. package/dist/input/rich-draft.d.ts +46 -0
  81. package/dist/input/rich-draft.d.ts.map +1 -0
  82. package/dist/input/rich-draft.js +391 -0
  83. package/dist/input/rich-draft.js.map +1 -0
  84. package/dist/input/rich-hit.d.ts +17 -0
  85. package/dist/input/rich-hit.d.ts.map +1 -0
  86. package/dist/input/rich-hit.js +89 -0
  87. package/dist/input/rich-hit.js.map +1 -0
  88. package/dist/input/rich-intake.d.ts +16 -0
  89. package/dist/input/rich-intake.d.ts.map +1 -0
  90. package/dist/input/rich-intake.js +22 -0
  91. package/dist/input/rich-intake.js.map +1 -0
  92. package/dist/input/rich-paint.d.ts +13 -0
  93. package/dist/input/rich-paint.d.ts.map +1 -0
  94. package/dist/input/rich-paint.js +18 -0
  95. package/dist/input/rich-paint.js.map +1 -0
  96. package/dist/internals/bgfx/index.d.ts +17 -0
  97. package/dist/internals/bgfx/index.d.ts.map +1 -0
  98. package/dist/internals/bgfx/index.js +17 -0
  99. package/dist/internals/bgfx/index.js.map +1 -0
  100. package/dist/internals/bgfx/materialize.d.ts +31 -0
  101. package/dist/internals/bgfx/materialize.d.ts.map +1 -0
  102. package/dist/internals/bgfx/materialize.js +117 -0
  103. package/dist/internals/bgfx/materialize.js.map +1 -0
  104. package/dist/internals/bgfx/opacity.d.ts +37 -0
  105. package/dist/internals/bgfx/opacity.d.ts.map +1 -0
  106. package/dist/internals/bgfx/opacity.js +76 -0
  107. package/dist/internals/bgfx/opacity.js.map +1 -0
  108. package/dist/internals/bgfx/registry.d.ts +13 -0
  109. package/dist/internals/bgfx/registry.d.ts.map +1 -0
  110. package/dist/internals/bgfx/registry.js +24 -0
  111. package/dist/internals/bgfx/registry.js.map +1 -0
  112. package/dist/internals/bgfx/sab-layout.d.ts +61 -0
  113. package/dist/internals/bgfx/sab-layout.d.ts.map +1 -0
  114. package/dist/internals/bgfx/sab-layout.js +107 -0
  115. package/dist/internals/bgfx/sab-layout.js.map +1 -0
  116. package/dist/internals/bgfx/shader-frame-host.d.ts +22 -0
  117. package/dist/internals/bgfx/shader-frame-host.d.ts.map +1 -0
  118. package/dist/internals/bgfx/shader-frame-host.js +165 -0
  119. package/dist/internals/bgfx/shader-frame-host.js.map +1 -0
  120. package/dist/internals/bgfx/shadertoy-catalog.d.ts +41 -0
  121. package/dist/internals/bgfx/shadertoy-catalog.d.ts.map +1 -0
  122. package/dist/internals/bgfx/shadertoy-catalog.js +200 -0
  123. package/dist/internals/bgfx/shadertoy-catalog.js.map +1 -0
  124. package/dist/internals/bgfx/shadertoy-gl.d.ts +38 -0
  125. package/dist/internals/bgfx/shadertoy-gl.d.ts.map +1 -0
  126. package/dist/internals/bgfx/shadertoy-gl.js +283 -0
  127. package/dist/internals/bgfx/shadertoy-gl.js.map +1 -0
  128. package/dist/internals/bgfx/types.d.ts +51 -0
  129. package/dist/internals/bgfx/types.d.ts.map +1 -0
  130. package/dist/internals/bgfx/types.js +14 -0
  131. package/dist/internals/bgfx/types.js.map +1 -0
  132. package/dist/internals/bgfx-worker.d.ts +6 -0
  133. package/dist/internals/bgfx-worker.d.ts.map +1 -0
  134. package/dist/internals/bgfx-worker.js +131 -0
  135. package/dist/internals/bgfx-worker.js.map +1 -0
  136. package/dist/layout/birth.d.ts +77 -0
  137. package/dist/layout/birth.d.ts.map +1 -0
  138. package/dist/layout/birth.js +148 -0
  139. package/dist/layout/birth.js.map +1 -0
  140. package/dist/layout/children.d.ts +5 -0
  141. package/dist/layout/children.d.ts.map +1 -0
  142. package/dist/layout/children.js +19 -0
  143. package/dist/layout/children.js.map +1 -0
  144. package/dist/layout/chrome_frame.d.ts +254 -0
  145. package/dist/layout/chrome_frame.d.ts.map +1 -0
  146. package/dist/layout/chrome_frame.js +1080 -0
  147. package/dist/layout/chrome_frame.js.map +1 -0
  148. package/dist/layout/chrome_mask.d.ts +40 -0
  149. package/dist/layout/chrome_mask.d.ts.map +1 -0
  150. package/dist/layout/chrome_mask.js +150 -0
  151. package/dist/layout/chrome_mask.js.map +1 -0
  152. package/dist/layout/engine.d.ts +49 -0
  153. package/dist/layout/engine.d.ts.map +1 -0
  154. package/dist/layout/engine.js +1269 -0
  155. package/dist/layout/engine.js.map +1 -0
  156. package/dist/layout/flex.d.ts +36 -0
  157. package/dist/layout/flex.d.ts.map +1 -0
  158. package/dist/layout/flex.js +111 -0
  159. package/dist/layout/flex.js.map +1 -0
  160. package/dist/layout/grid.d.ts +36 -0
  161. package/dist/layout/grid.d.ts.map +1 -0
  162. package/dist/layout/grid.js +126 -0
  163. package/dist/layout/grid.js.map +1 -0
  164. package/dist/layout/index.d.ts +30 -0
  165. package/dist/layout/index.d.ts.map +1 -0
  166. package/dist/layout/index.js +30 -0
  167. package/dist/layout/index.js.map +1 -0
  168. package/dist/layout/indication.d.ts +34 -0
  169. package/dist/layout/indication.d.ts.map +1 -0
  170. package/dist/layout/indication.js +68 -0
  171. package/dist/layout/indication.js.map +1 -0
  172. package/dist/layout/length.d.ts +18 -0
  173. package/dist/layout/length.d.ts.map +1 -0
  174. package/dist/layout/length.js +24 -0
  175. package/dist/layout/length.js.map +1 -0
  176. package/dist/layout/live_paint.d.ts +145 -0
  177. package/dist/layout/live_paint.d.ts.map +1 -0
  178. package/dist/layout/live_paint.js +255 -0
  179. package/dist/layout/live_paint.js.map +1 -0
  180. package/dist/layout/markdown.d.ts +22 -0
  181. package/dist/layout/markdown.d.ts.map +1 -0
  182. package/dist/layout/markdown.js +167 -0
  183. package/dist/layout/markdown.js.map +1 -0
  184. package/dist/layout/measure.d.ts +46 -0
  185. package/dist/layout/measure.d.ts.map +1 -0
  186. package/dist/layout/measure.js +307 -0
  187. package/dist/layout/measure.js.map +1 -0
  188. package/dist/layout/paint-hotkey.d.ts +45 -0
  189. package/dist/layout/paint-hotkey.d.ts.map +1 -0
  190. package/dist/layout/paint-hotkey.js +152 -0
  191. package/dist/layout/paint-hotkey.js.map +1 -0
  192. package/dist/layout/paint.d.ts +25 -0
  193. package/dist/layout/paint.d.ts.map +1 -0
  194. package/dist/layout/paint.js +189 -0
  195. package/dist/layout/paint.js.map +1 -0
  196. package/dist/layout/paint_theme.d.ts +85 -0
  197. package/dist/layout/paint_theme.d.ts.map +1 -0
  198. package/dist/layout/paint_theme.js +76 -0
  199. package/dist/layout/paint_theme.js.map +1 -0
  200. package/dist/layout/progress.d.ts +31 -0
  201. package/dist/layout/progress.d.ts.map +1 -0
  202. package/dist/layout/progress.js +83 -0
  203. package/dist/layout/progress.js.map +1 -0
  204. package/dist/layout/selection.d.ts +136 -0
  205. package/dist/layout/selection.d.ts.map +1 -0
  206. package/dist/layout/selection.js +312 -0
  207. package/dist/layout/selection.js.map +1 -0
  208. package/dist/layout/selection_gesture.d.ts +88 -0
  209. package/dist/layout/selection_gesture.d.ts.map +1 -0
  210. package/dist/layout/selection_gesture.js +239 -0
  211. package/dist/layout/selection_gesture.js.map +1 -0
  212. package/dist/layout/selection_paint.d.ts +47 -0
  213. package/dist/layout/selection_paint.d.ts.map +1 -0
  214. package/dist/layout/selection_paint.js +320 -0
  215. package/dist/layout/selection_paint.js.map +1 -0
  216. package/dist/layout/serialize.d.ts +49 -0
  217. package/dist/layout/serialize.d.ts.map +1 -0
  218. package/dist/layout/serialize.js +437 -0
  219. package/dist/layout/serialize.js.map +1 -0
  220. package/dist/layout/squeeze.d.ts +58 -0
  221. package/dist/layout/squeeze.d.ts.map +1 -0
  222. package/dist/layout/squeeze.js +239 -0
  223. package/dist/layout/squeeze.js.map +1 -0
  224. package/dist/layout/text.d.ts +44 -0
  225. package/dist/layout/text.d.ts.map +1 -0
  226. package/dist/layout/text.js +230 -0
  227. package/dist/layout/text.js.map +1 -0
  228. package/dist/layout/theme_store.d.ts +21 -0
  229. package/dist/layout/theme_store.d.ts.map +1 -0
  230. package/dist/layout/theme_store.js +133 -0
  231. package/dist/layout/theme_store.js.map +1 -0
  232. package/dist/layout/types.d.ts +564 -0
  233. package/dist/layout/types.d.ts.map +1 -0
  234. package/dist/layout/types.js +2 -0
  235. package/dist/layout/types.js.map +1 -0
  236. package/dist/layout/visual_draft.d.ts +60 -0
  237. package/dist/layout/visual_draft.d.ts.map +1 -0
  238. package/dist/layout/visual_draft.js +199 -0
  239. package/dist/layout/visual_draft.js.map +1 -0
  240. package/dist/lexicon/color.d.ts +22 -0
  241. package/dist/lexicon/color.d.ts.map +1 -0
  242. package/dist/lexicon/color.js +6 -0
  243. package/dist/lexicon/color.js.map +1 -0
  244. package/dist/lexicon/index.d.ts +15 -0
  245. package/dist/lexicon/index.d.ts.map +1 -0
  246. package/dist/lexicon/index.js +15 -0
  247. package/dist/lexicon/index.js.map +1 -0
  248. package/dist/lexicon/prominence.d.ts +46 -0
  249. package/dist/lexicon/prominence.d.ts.map +1 -0
  250. package/dist/lexicon/prominence.js +73 -0
  251. package/dist/lexicon/prominence.js.map +1 -0
  252. package/dist/lexicon/quarantine.d.ts +21 -0
  253. package/dist/lexicon/quarantine.d.ts.map +1 -0
  254. package/dist/lexicon/quarantine.js +15 -0
  255. package/dist/lexicon/quarantine.js.map +1 -0
  256. package/dist/lexicon/roles.d.ts +37 -0
  257. package/dist/lexicon/roles.d.ts.map +1 -0
  258. package/dist/lexicon/roles.js +52 -0
  259. package/dist/lexicon/roles.js.map +1 -0
  260. package/dist/lexicon/tiers.d.ts +18 -0
  261. package/dist/lexicon/tiers.d.ts.map +1 -0
  262. package/dist/lexicon/tiers.js +17 -0
  263. package/dist/lexicon/tiers.js.map +1 -0
  264. package/dist/measure/index.d.ts +43 -0
  265. package/dist/measure/index.d.ts.map +1 -0
  266. package/dist/measure/index.js +120 -0
  267. package/dist/measure/index.js.map +1 -0
  268. package/dist/render.d.ts +72 -0
  269. package/dist/render.d.ts.map +1 -0
  270. package/dist/render.js +164 -0
  271. package/dist/render.js.map +1 -0
  272. package/dist/runtime/agents_keys.d.ts +8 -0
  273. package/dist/runtime/agents_keys.d.ts.map +1 -0
  274. package/dist/runtime/agents_keys.js +24 -0
  275. package/dist/runtime/agents_keys.js.map +1 -0
  276. package/dist/runtime/agents_peek.d.ts +44 -0
  277. package/dist/runtime/agents_peek.d.ts.map +1 -0
  278. package/dist/runtime/agents_peek.js +96 -0
  279. package/dist/runtime/agents_peek.js.map +1 -0
  280. package/dist/runtime/agents_preview.d.ts +27 -0
  281. package/dist/runtime/agents_preview.d.ts.map +1 -0
  282. package/dist/runtime/agents_preview.js +122 -0
  283. package/dist/runtime/agents_preview.js.map +1 -0
  284. package/dist/runtime/box_bus.d.ts +144 -0
  285. package/dist/runtime/box_bus.d.ts.map +1 -0
  286. package/dist/runtime/box_bus.js +74 -0
  287. package/dist/runtime/box_bus.js.map +1 -0
  288. package/dist/runtime/clipboard-image.d.ts +20 -0
  289. package/dist/runtime/clipboard-image.d.ts.map +1 -0
  290. package/dist/runtime/clipboard-image.js +62 -0
  291. package/dist/runtime/clipboard-image.js.map +1 -0
  292. package/dist/runtime/copy_toast.d.ts +46 -0
  293. package/dist/runtime/copy_toast.d.ts.map +1 -0
  294. package/dist/runtime/copy_toast.js +117 -0
  295. package/dist/runtime/copy_toast.js.map +1 -0
  296. package/dist/runtime/echo.d.ts +41 -0
  297. package/dist/runtime/echo.d.ts.map +1 -0
  298. package/dist/runtime/echo.js +112 -0
  299. package/dist/runtime/echo.js.map +1 -0
  300. package/dist/runtime/frame.d.ts +79 -0
  301. package/dist/runtime/frame.d.ts.map +1 -0
  302. package/dist/runtime/frame.js +258 -0
  303. package/dist/runtime/frame.js.map +1 -0
  304. package/dist/runtime/index.d.ts +16 -0
  305. package/dist/runtime/index.d.ts.map +1 -0
  306. package/dist/runtime/index.js +16 -0
  307. package/dist/runtime/index.js.map +1 -0
  308. package/dist/runtime/key_buffer.d.ts +19 -0
  309. package/dist/runtime/key_buffer.d.ts.map +1 -0
  310. package/dist/runtime/key_buffer.js +24 -0
  311. package/dist/runtime/key_buffer.js.map +1 -0
  312. package/dist/runtime/keys.d.ts +66 -0
  313. package/dist/runtime/keys.d.ts.map +1 -0
  314. package/dist/runtime/keys.js +220 -0
  315. package/dist/runtime/keys.js.map +1 -0
  316. package/dist/runtime/paint_snapshot.d.ts +9 -0
  317. package/dist/runtime/paint_snapshot.d.ts.map +1 -0
  318. package/dist/runtime/paint_snapshot.js +8 -0
  319. package/dist/runtime/paint_snapshot.js.map +1 -0
  320. package/dist/runtime/paint_source.d.ts +266 -0
  321. package/dist/runtime/paint_source.d.ts.map +1 -0
  322. package/dist/runtime/paint_source.js +532 -0
  323. package/dist/runtime/paint_source.js.map +1 -0
  324. package/dist/runtime/present_loop.d.ts +11 -0
  325. package/dist/runtime/present_loop.d.ts.map +1 -0
  326. package/dist/runtime/present_loop.js +46 -0
  327. package/dist/runtime/present_loop.js.map +1 -0
  328. package/dist/runtime/present_profile.d.ts +58 -0
  329. package/dist/runtime/present_profile.d.ts.map +1 -0
  330. package/dist/runtime/present_profile.js +130 -0
  331. package/dist/runtime/present_profile.js.map +1 -0
  332. package/dist/runtime/raster.d.ts +24 -0
  333. package/dist/runtime/raster.d.ts.map +1 -0
  334. package/dist/runtime/raster.js +43 -0
  335. package/dist/runtime/raster.js.map +1 -0
  336. package/dist/runtime/renderer.d.ts +151 -0
  337. package/dist/runtime/renderer.d.ts.map +1 -0
  338. package/dist/runtime/renderer.js +1607 -0
  339. package/dist/runtime/renderer.js.map +1 -0
  340. package/dist/runtime/screenshot.d.ts +9 -0
  341. package/dist/runtime/screenshot.d.ts.map +1 -0
  342. package/dist/runtime/screenshot.js +111 -0
  343. package/dist/runtime/screenshot.js.map +1 -0
  344. package/dist/runtime/selection_host.d.ts +41 -0
  345. package/dist/runtime/selection_host.d.ts.map +1 -0
  346. package/dist/runtime/selection_host.js +190 -0
  347. package/dist/runtime/selection_host.js.map +1 -0
  348. package/dist/runtime/stderr_hold.d.ts +16 -0
  349. package/dist/runtime/stderr_hold.d.ts.map +1 -0
  350. package/dist/runtime/stderr_hold.js +23 -0
  351. package/dist/runtime/stderr_hold.js.map +1 -0
  352. package/dist/runtime/thought_birth.d.ts +25 -0
  353. package/dist/runtime/thought_birth.d.ts.map +1 -0
  354. package/dist/runtime/thought_birth.js +122 -0
  355. package/dist/runtime/thought_birth.js.map +1 -0
  356. package/dist/runtime/thought_collapsed.d.ts +35 -0
  357. package/dist/runtime/thought_collapsed.d.ts.map +1 -0
  358. package/dist/runtime/thought_collapsed.js +94 -0
  359. package/dist/runtime/thought_collapsed.js.map +1 -0
  360. package/dist/runtime/tty.d.ts +58 -0
  361. package/dist/runtime/tty.d.ts.map +1 -0
  362. package/dist/runtime/tty.js +130 -0
  363. package/dist/runtime/tty.js.map +1 -0
  364. package/dist/theme/ansi16.d.ts +34 -0
  365. package/dist/theme/ansi16.d.ts.map +1 -0
  366. package/dist/theme/ansi16.js +181 -0
  367. package/dist/theme/ansi16.js.map +1 -0
  368. package/dist/theme/ansi256.d.ts +15 -0
  369. package/dist/theme/ansi256.d.ts.map +1 -0
  370. package/dist/theme/ansi256.js +94 -0
  371. package/dist/theme/ansi256.js.map +1 -0
  372. package/dist/theme/ground.d.ts +8 -0
  373. package/dist/theme/ground.d.ts.map +1 -0
  374. package/dist/theme/ground.js +11 -0
  375. package/dist/theme/ground.js.map +1 -0
  376. package/dist/theme/index.d.ts +10 -0
  377. package/dist/theme/index.d.ts.map +1 -0
  378. package/dist/theme/index.js +10 -0
  379. package/dist/theme/index.js.map +1 -0
  380. package/dist/theme/region.d.ts +42 -0
  381. package/dist/theme/region.d.ts.map +1 -0
  382. package/dist/theme/region.js +105 -0
  383. package/dist/theme/region.js.map +1 -0
  384. package/dist/theme/resolver.d.ts +126 -0
  385. package/dist/theme/resolver.d.ts.map +1 -0
  386. package/dist/theme/resolver.js +366 -0
  387. package/dist/theme/resolver.js.map +1 -0
  388. package/dist/theme/salience.d.ts +90 -0
  389. package/dist/theme/salience.d.ts.map +1 -0
  390. package/dist/theme/salience.js +208 -0
  391. package/dist/theme/salience.js.map +1 -0
  392. package/dist/theme/seeds.d.ts +113 -0
  393. package/dist/theme/seeds.d.ts.map +1 -0
  394. package/dist/theme/seeds.js +52 -0
  395. package/dist/theme/seeds.js.map +1 -0
  396. package/dist/theme/selection.d.ts +94 -0
  397. package/dist/theme/selection.d.ts.map +1 -0
  398. package/dist/theme/selection.js +135 -0
  399. package/dist/theme/selection.js.map +1 -0
  400. package/dist/theme/theme.d.ts +41 -0
  401. package/dist/theme/theme.d.ts.map +1 -0
  402. package/dist/theme/theme.js +34 -0
  403. package/dist/theme/theme.js.map +1 -0
  404. package/dist/widgets/composer-caret.d.ts +47 -0
  405. package/dist/widgets/composer-caret.d.ts.map +1 -0
  406. package/dist/widgets/composer-caret.js +90 -0
  407. package/dist/widgets/composer-caret.js.map +1 -0
  408. package/dist/widgets/composer-click.d.ts +30 -0
  409. package/dist/widgets/composer-click.d.ts.map +1 -0
  410. package/dist/widgets/composer-click.js +89 -0
  411. package/dist/widgets/composer-click.js.map +1 -0
  412. package/dist/widgets/composer-command.d.ts +64 -0
  413. package/dist/widgets/composer-command.d.ts.map +1 -0
  414. package/dist/widgets/composer-command.js +127 -0
  415. package/dist/widgets/composer-command.js.map +1 -0
  416. package/dist/widgets/composer-frame.d.ts +74 -0
  417. package/dist/widgets/composer-frame.d.ts.map +1 -0
  418. package/dist/widgets/composer-frame.js +284 -0
  419. package/dist/widgets/composer-frame.js.map +1 -0
  420. package/dist/widgets/composer-picker.d.ts +89 -0
  421. package/dist/widgets/composer-picker.d.ts.map +1 -0
  422. package/dist/widgets/composer-picker.js +240 -0
  423. package/dist/widgets/composer-picker.js.map +1 -0
  424. package/dist/widgets/diff.d.ts +11 -0
  425. package/dist/widgets/diff.d.ts.map +1 -0
  426. package/dist/widgets/diff.js +19 -0
  427. package/dist/widgets/diff.js.map +1 -0
  428. package/dist/widgets/expand.d.ts +28 -0
  429. package/dist/widgets/expand.d.ts.map +1 -0
  430. package/dist/widgets/expand.js +94 -0
  431. package/dist/widgets/expand.js.map +1 -0
  432. package/dist/widgets/index.d.ts +126 -0
  433. package/dist/widgets/index.d.ts.map +1 -0
  434. package/dist/widgets/index.js +455 -0
  435. package/dist/widgets/index.js.map +1 -0
  436. package/dist/widgets/internals/arrival-lang.d.ts +88 -0
  437. package/dist/widgets/internals/arrival-lang.d.ts.map +1 -0
  438. package/dist/widgets/internals/arrival-lang.js +211 -0
  439. package/dist/widgets/internals/arrival-lang.js.map +1 -0
  440. package/dist/widgets/internals/diff-expand.d.ts +93 -0
  441. package/dist/widgets/internals/diff-expand.d.ts.map +1 -0
  442. package/dist/widgets/internals/diff-expand.js +1106 -0
  443. package/dist/widgets/internals/diff-expand.js.map +1 -0
  444. package/dist/widgets/internals/embedded-syntax.d.ts +62 -0
  445. package/dist/widgets/internals/embedded-syntax.d.ts.map +1 -0
  446. package/dist/widgets/internals/embedded-syntax.js +465 -0
  447. package/dist/widgets/internals/embedded-syntax.js.map +1 -0
  448. package/dist/widgets/internals/markdown-expand.d.ts +57 -0
  449. package/dist/widgets/internals/markdown-expand.d.ts.map +1 -0
  450. package/dist/widgets/internals/markdown-expand.js +980 -0
  451. package/dist/widgets/internals/markdown-expand.js.map +1 -0
  452. package/dist/widgets/internals/overlay-syntax.d.ts +24 -0
  453. package/dist/widgets/internals/overlay-syntax.d.ts.map +1 -0
  454. package/dist/widgets/internals/overlay-syntax.js +90 -0
  455. package/dist/widgets/internals/overlay-syntax.js.map +1 -0
  456. package/dist/widgets/internals/pierre-parse/cleanLastNewline.d.ts +6 -0
  457. package/dist/widgets/internals/pierre-parse/cleanLastNewline.d.ts.map +1 -0
  458. package/dist/widgets/internals/pierre-parse/cleanLastNewline.js +8 -0
  459. package/dist/widgets/internals/pierre-parse/cleanLastNewline.js.map +1 -0
  460. package/dist/widgets/internals/pierre-parse/constants.d.ts +13 -0
  461. package/dist/widgets/internals/pierre-parse/constants.d.ts.map +1 -0
  462. package/dist/widgets/internals/pierre-parse/constants.js +13 -0
  463. package/dist/widgets/internals/pierre-parse/constants.js.map +1 -0
  464. package/dist/widgets/internals/pierre-parse/detachString.d.ts +3 -0
  465. package/dist/widgets/internals/pierre-parse/detachString.d.ts.map +1 -0
  466. package/dist/widgets/internals/pierre-parse/detachString.js +24 -0
  467. package/dist/widgets/internals/pierre-parse/detachString.js.map +1 -0
  468. package/dist/widgets/internals/pierre-parse/index.d.ts +8 -0
  469. package/dist/widgets/internals/pierre-parse/index.d.ts.map +1 -0
  470. package/dist/widgets/internals/pierre-parse/index.js +8 -0
  471. package/dist/widgets/internals/pierre-parse/index.js.map +1 -0
  472. package/dist/widgets/internals/pierre-parse/parseDiffFromFile.d.ts +15 -0
  473. package/dist/widgets/internals/pierre-parse/parseDiffFromFile.d.ts.map +1 -0
  474. package/dist/widgets/internals/pierre-parse/parseDiffFromFile.js +39 -0
  475. package/dist/widgets/internals/pierre-parse/parseDiffFromFile.js.map +1 -0
  476. package/dist/widgets/internals/pierre-parse/parsePatchFiles.d.ts +11 -0
  477. package/dist/widgets/internals/pierre-parse/parsePatchFiles.d.ts.map +1 -0
  478. package/dist/widgets/internals/pierre-parse/parsePatchFiles.js +621 -0
  479. package/dist/widgets/internals/pierre-parse/parsePatchFiles.js.map +1 -0
  480. package/dist/widgets/internals/pierre-parse/types.d.ts +73 -0
  481. package/dist/widgets/internals/pierre-parse/types.d.ts.map +1 -0
  482. package/dist/widgets/internals/pierre-parse/types.js +7 -0
  483. package/dist/widgets/internals/pierre-parse/types.js.map +1 -0
  484. package/dist/widgets/internals/syntax.d.ts +55 -0
  485. package/dist/widgets/internals/syntax.d.ts.map +1 -0
  486. package/dist/widgets/internals/syntax.js +261 -0
  487. package/dist/widgets/internals/syntax.js.map +1 -0
  488. package/dist/widgets/markdown.d.ts +14 -0
  489. package/dist/widgets/markdown.d.ts.map +1 -0
  490. package/dist/widgets/markdown.js +9 -0
  491. package/dist/widgets/markdown.js.map +1 -0
  492. package/dist/widgets/shadow-stream.d.ts +88 -0
  493. package/dist/widgets/shadow-stream.d.ts.map +1 -0
  494. package/dist/widgets/shadow-stream.js +359 -0
  495. package/dist/widgets/shadow-stream.js.map +1 -0
  496. package/dist/widgets/speech.d.ts +36 -0
  497. package/dist/widgets/speech.d.ts.map +1 -0
  498. package/dist/widgets/speech.js +238 -0
  499. package/dist/widgets/speech.js.map +1 -0
  500. package/dist/widgets/stream-birth.d.ts +13 -0
  501. package/dist/widgets/stream-birth.d.ts.map +1 -0
  502. package/dist/widgets/stream-birth.js +70 -0
  503. package/dist/widgets/stream-birth.js.map +1 -0
  504. package/dist/widgets/syntax-boot.d.ts +25 -0
  505. package/dist/widgets/syntax-boot.d.ts.map +1 -0
  506. package/dist/widgets/syntax-boot.js +181 -0
  507. package/dist/widgets/syntax-boot.js.map +1 -0
  508. package/dist/widgets/text.d.ts +16 -0
  509. package/dist/widgets/text.d.ts.map +1 -0
  510. package/dist/widgets/text.js +14 -0
  511. package/dist/widgets/text.js.map +1 -0
  512. package/dist/widgets/transcript.d.ts +44 -0
  513. package/dist/widgets/transcript.d.ts.map +1 -0
  514. package/dist/widgets/transcript.js +106 -0
  515. package/dist/widgets/transcript.js.map +1 -0
  516. package/doom.vert +1840 -0
  517. package/fonts/DejaVuSans.ttf +0 -0
  518. package/fonts/DejaVuSansMono.ttf +0 -0
  519. package/fonts/LICENSE-DejaVu.txt +187 -0
  520. package/fonts/README.md +6 -0
  521. package/gradient.vert +75 -0
  522. package/package.json +147 -4
@@ -0,0 +1,1269 @@
1
+ /**
2
+ * Layout resolve: LayoutNode → Frame.
3
+ * Pure geometry. Does not write terminal.
4
+ */
5
+ import { chromeFramePaintOps, isChromeFrameShowingPlaceholder } from "./chrome_frame.js";
6
+ import { distributeExact, flexMainSizes, resolveFlexItem } from "./flex.js";
7
+ import { trackOffsets } from "./grid.js";
8
+ import { INDICATION_CONTENT_INSET, indicationGutterLines, indicationGutterWide } from "./indication.js";
9
+ import { resolveLength } from "./length.js";
10
+ import { collectLiveDemand, spinnerLive } from "./live_paint.js";
11
+ import { markdownToNodes } from "./markdown.js";
12
+ import { clamp0, measureGridDims, measureNode as measureNodePure, pad } from "./measure.js";
13
+ import { minPeekHeight, omittedLabel, planSqueeze, squeezeOverlay } from "./squeeze.js";
14
+ import { wrapLayoutText } from "./text.js";
15
+ import { FgP } from "../lexicon/index.js";
16
+ import { defaultMeasure } from "../measure/index.js";
17
+ /**
18
+ * A `userSelect` value that makes an `intent:none` node eligible for the
19
+ * hitlist (i.e. a drag can start a selection on it). The CSS-aligned union
20
+ * rejects the boolean truthiness gate, which would admit
21
+ * `"none"` (a truthy string) — the exact trap these explicit values fix.
22
+ *
23
+ * `"auto"` is treated as non-eligible here: at the root it defaults to "auto"
24
+ * and inherits; a node must explicitly opt into selectability with one of
25
+ * {text, all, contain}. This matches CSS, where `auto` on a non-editable
26
+ * ancestor resolves to `none`.
27
+ */
28
+ const SELECTABLE_HIT = new Set(["text", "all", "contain"]);
29
+ /** Hit-eligibility check — the union-correct replacement for truthiness. */
30
+ function isSelectableHit(v) {
31
+ return SELECTABLE_HIT.has(v);
32
+ }
33
+ /**
34
+ * Admission gate shared by {@link elementAt} and {@link elementAtPath}. A node
35
+ * is admissible (returned to callers) when it is
36
+ * NOT inert, NOT a phantom container, AND either has a non-none intent OR is
37
+ * explicitly selectable (text/all/contain) OR carries an onClick.
38
+ *
39
+ * Phantoms (flex/gap, grid) are structural containers but never selectable —
40
+ * they are skipped everywhere except the byKey fallback in {@link elementAt},
41
+ * which already filters them via `meta.phantom`.
42
+ */
43
+ function isAdmissibleHit(meta) {
44
+ if (meta.inert || meta.phantom)
45
+ return false;
46
+ if (meta.intent === "none" && !isSelectableHit(meta.userSelect) && !meta.onClick)
47
+ return false;
48
+ return true;
49
+ }
50
+ /** Rect contains point (half-open: right/bottom edges excluded). */
51
+ function rectContains(r, x, y) {
52
+ return x >= r.x && x < r.x + r.w && y >= r.y && y < r.y + r.h;
53
+ }
54
+ /** Ctx-bound measure — same pure measureNode used by structure HTML. */
55
+ function measureNode(node, maxW, maxH, ctx) {
56
+ return measureNodePure(node, maxW, maxH, ctx.measure);
57
+ }
58
+ function keyOf(node, path) {
59
+ if ("key" in node && node.key)
60
+ return node.key;
61
+ if ("id" in node && node.id)
62
+ return node.id;
63
+ return path.join("/");
64
+ }
65
+ function intersect(a, b) {
66
+ const x1 = Math.max(a.x, b.x);
67
+ const y1 = Math.max(a.y, b.y);
68
+ const x2 = Math.min(a.x + a.w, b.x + b.w);
69
+ const y2 = Math.min(a.y + a.h, b.y + b.h);
70
+ if (x2 <= x1 || y2 <= y1)
71
+ return null;
72
+ return { x: x1, y: y1, w: x2 - x1, h: y2 - y1 };
73
+ }
74
+ function overflowAxisTight(v) {
75
+ return v === "hidden" || v === "clip" || v === "auto" || v === "scroll";
76
+ }
77
+ function overflowTight(style) {
78
+ return overflowAxisTight(style.overflow) || overflowAxisTight(style.overflowX) || overflowAxisTight(style.overflowY);
79
+ }
80
+ /** Sticky-bottom / squeeze / overlay still place when fully below the clip. */
81
+ function placesFullyBelowClip(node) {
82
+ if (node.type === "overlay")
83
+ return true;
84
+ if (node.type === "box" && (node.style?.sticky === "bottom" || node.style?.squeeze))
85
+ return true;
86
+ return false;
87
+ }
88
+ /** Sticky-top still places once its top edge has crossed above the clip. */
89
+ function pinsFromAboveClip(node, childRect, clip) {
90
+ return isStickyTop(node) && childRect.y + childRect.h <= clip.y;
91
+ }
92
+ /** Move shifted sticky-top subtrees above paint that was recorded after them. */
93
+ function flushStuckLifts(ctx, from) {
94
+ const lifts = ctx.stuckLifts.splice(from);
95
+ const paintSlices = [];
96
+ const hitSlices = [];
97
+ for (let i = lifts.length - 1; i >= 0; i--) {
98
+ const lift = lifts[i];
99
+ paintSlices.push(ctx.paintOrder.splice(lift.paintAt, lift.paintTo - lift.paintAt));
100
+ hitSlices.push(ctx.hitList.splice(lift.hitAt, lift.hitTo - lift.hitAt));
101
+ }
102
+ for (let i = paintSlices.length - 1; i >= 0; i--) {
103
+ const paint = paintSlices[i];
104
+ const hits = hitSlices[i];
105
+ if (paint.length > 0)
106
+ ctx.paintOrder.push(...paint);
107
+ if (hits.length > 0)
108
+ ctx.hitList.push(...hits);
109
+ }
110
+ }
111
+ function pushHit(ctx, key, hitRect, intent, opts) {
112
+ const userSelect = opts.userSelect ?? "auto";
113
+ const meta = {
114
+ key,
115
+ hitRect,
116
+ intent,
117
+ userSelect,
118
+ inert: opts.inert ?? false,
119
+ onClick: opts.onClick,
120
+ element: opts.kind ? { kind: opts.kind, ...(opts.viewport ?? {}) } : undefined,
121
+ phantom: opts.phantom,
122
+ parent: opts.parent,
123
+ tableCell: opts.phantom ? undefined : ctx.tableCell,
124
+ // Phantoms (flex/gap) must not carry semantic stamps — they span the
125
+ // whole half and would steal participation from real code text hits.
126
+ diffLine: opts.phantom ? undefined : ctx.diffLine,
127
+ };
128
+ ctx.byKey.set(key, meta);
129
+ // Hitlist admission: a node is hittable if it has a non-none intent, OR is
130
+ // a phantom container (flex/gap), OR is explicitly selectable (text/all/contain).
131
+ // The userSelect gate uses the union-correct check, NOT truthiness — "none"
132
+ // and "auto" are not hit-eligible on their own.
133
+ if (intent !== "none" || opts.phantom || isSelectableHit(userSelect)) {
134
+ ctx.hitList.push(meta);
135
+ }
136
+ }
137
+ /** Default subtle chrome for inner grid bars — same role/prominence as box border. */
138
+ const DEFAULT_GRID_LINE = { role: "border", prominence: FgP.ghost };
139
+ /**
140
+ * Default region fade for `overlay` with `backdrop: true` and no
141
+ * `backdropStyle`. Decision 1 (color-hygiene): content behind a modal is
142
+ * region-dimmed — the room recedes, identity stays visible. Layout stamps
143
+ * this onto every paint op already in the order (base content); overlay
144
+ * children place *after* and stay full-prominence. Hosts no longer need to
145
+ * rebind paintTheme for a single-modal case. Matches storybook modal-backdrop
146
+ * law (stories/theme.ts theme-region-dim uses 0.4).
147
+ */
148
+ const BACKDROP_REGION_PROMINENCE = 0.4;
149
+ /**
150
+ * Stamp region-dim onto already-placed paint (the room behind a modal).
151
+ * Nested overlays multiply so a second scrim compounds (sidebar 1×, tape 2×)
152
+ * and never raises a lower region. `raw` ops own their styles and are left alone.
153
+ */
154
+ function stampBackdropRegion(paintOrder, regionP) {
155
+ for (const pos of paintOrder) {
156
+ for (const op of pos.paint) {
157
+ if (op.kind === "raw")
158
+ continue;
159
+ const prev = op.style.regionProminence;
160
+ const next = prev === undefined ? regionP : prev * regionP;
161
+ if (prev === next)
162
+ continue;
163
+ op.style = { ...op.style, regionProminence: next };
164
+ }
165
+ }
166
+ }
167
+ function paintInnerGridBars(rect, colWidths, rowHeights, colOff, rowOff, colGap, rowGap, nCols, nRows, lineStyle) {
168
+ const paints = [];
169
+ for (let r = 0; r < nRows - 1; r++) {
170
+ if (rowGap <= 0)
171
+ continue;
172
+ const y = rect.y + rowOff[r] + rowHeights[r];
173
+ for (let g = 0; g < rowGap; g++) {
174
+ let line = "";
175
+ for (let c = 0; c < nCols; c++) {
176
+ line += "─".repeat(Math.max(0, colWidths[c]));
177
+ if (c < nCols - 1) {
178
+ line += (g === 0 ? "┼" : "│").repeat(Math.max(0, colGap));
179
+ }
180
+ }
181
+ if (line.length > 0) {
182
+ paints.push({
183
+ kind: "text",
184
+ rect: {
185
+ x: rect.x,
186
+ y: y + g,
187
+ w: line.length,
188
+ h: 1,
189
+ },
190
+ lines: [line],
191
+ style: lineStyle,
192
+ });
193
+ }
194
+ }
195
+ }
196
+ for (let c = 0; c < nCols - 1; c++) {
197
+ if (colGap <= 0)
198
+ continue;
199
+ const x = rect.x + colOff[c] + colWidths[c];
200
+ for (let r = 0; r < nRows; r++) {
201
+ const y = rect.y + rowOff[r];
202
+ const h = rowHeights[r];
203
+ for (let g = 0; g < colGap; g++) {
204
+ paints.push({
205
+ kind: "text",
206
+ rect: { x: x + g, y, w: 1, h },
207
+ lines: Array.from({ length: h }, () => "│"),
208
+ style: lineStyle,
209
+ });
210
+ }
211
+ }
212
+ }
213
+ return paints;
214
+ }
215
+ function placeGrid(node, rect, clip, ctx, inherit, key, userSelect, inert, parentKey) {
216
+ const dims = measureGridDims(node, rect.w, ctx.measure);
217
+ const { colWidths, rowHeights, colGap, rowGap, padX, padY, nCols, nRows } = dims;
218
+ const children = node.children ?? [];
219
+ const lines = node.style.lines ?? "none";
220
+ const lineStyle = node.style.lineStyle ?? DEFAULT_GRID_LINE;
221
+ const gridClip = intersect(rect, clip);
222
+ if (gridClip) {
223
+ pushHit(ctx, key, gridClip, "none", {
224
+ kind: "grid",
225
+ phantom: true,
226
+ userSelect,
227
+ inert,
228
+ parent: parentKey,
229
+ });
230
+ }
231
+ const colOff = trackOffsets(colWidths, colGap);
232
+ const rowOff = trackOffsets(rowHeights, rowGap);
233
+ if (lines === "inner" && gridClip) {
234
+ const paints = paintInnerGridBars(rect, colWidths, rowHeights, colOff, rowOff, colGap, rowGap, nCols, nRows, lineStyle);
235
+ if (paints.length > 0) {
236
+ ctx.paintOrder.push({
237
+ key: `${key}:lines`,
238
+ rect,
239
+ paint: paints,
240
+ clip: gridClip,
241
+ });
242
+ }
243
+ }
244
+ // Place cells (row-major). Stamp tableCell so selection copy can rebuild
245
+ // compact markdown instead of the full terminal bar padding.
246
+ for (let r = 0; r < nRows; r++) {
247
+ for (let c = 0; c < nCols; c++) {
248
+ const idx = r * nCols + c;
249
+ const child = children[idx];
250
+ if (!child)
251
+ continue;
252
+ const cellX = rect.x + colOff[c];
253
+ const cellY = rect.y + rowOff[r];
254
+ const cellW = colWidths[c];
255
+ const cellH = rowHeights[r];
256
+ const contentRect = {
257
+ x: cellX + padX,
258
+ y: cellY + padY,
259
+ w: Math.max(0, cellW - padX * 2),
260
+ h: Math.max(0, cellH - padY * 2),
261
+ };
262
+ const childClip = intersect(contentRect, clip);
263
+ if (!childClip)
264
+ continue;
265
+ const prevTable = ctx.tableCell;
266
+ ctx.tableCell = {
267
+ gridKey: key,
268
+ row: r,
269
+ col: c,
270
+ nCols,
271
+ nRows,
272
+ };
273
+ place(child, contentRect, childClip, ctx, inherit, key);
274
+ ctx.tableCell = prevTable;
275
+ }
276
+ }
277
+ }
278
+ function stickyBottomRect(node, rect, clip) {
279
+ if (node.type !== "box" || node.style?.sticky !== "bottom")
280
+ return rect;
281
+ const overflow = rect.y + rect.h - (clip.y + clip.h);
282
+ if (overflow <= 0)
283
+ return rect;
284
+ return { ...rect, y: rect.y - overflow };
285
+ }
286
+ function isStickyTop(node) {
287
+ return node.type === "box" && node.style?.sticky === "top";
288
+ }
289
+ /**
290
+ * Pin once the box's top edge meets the clip. While the top is still below
291
+ * that edge the box stays in flow; it sticks the moment the top arrives, not
292
+ * after the rest of the box has scrolled away. `floor` is the next sticky
293
+ * sibling (the next turn); it pushes this box back out.
294
+ *
295
+ * A box that does not fit never pins while any of it is still in the clip:
296
+ * full height would blanket the viewport. Once it has left entirely it
297
+ * squeezes to its first row, so the prompt the visible lines belong to stays
298
+ * named. That row is this same box, not a copy.
299
+ */
300
+ function stickyTopRect(node, rect, clip, floor) {
301
+ if (!isStickyTop(node))
302
+ return rect;
303
+ if (rect.y >= clip.y)
304
+ return rect;
305
+ const left = rect.y + rect.h <= clip.y;
306
+ if (!left && rect.h >= clip.h)
307
+ return rect;
308
+ const squeezed = left && rect.h >= clip.h;
309
+ const h = squeezed ? 1 : rect.h;
310
+ const room = (floor ?? clip.y + clip.h) - clip.y;
311
+ if (h > room)
312
+ return rect;
313
+ const limit = (floor ?? clip.y + clip.h) - h;
314
+ const y = Math.max(rect.y, Math.min(clip.y, limit));
315
+ if (y === rect.y && h === rect.h)
316
+ return rect;
317
+ return { ...rect, y, h };
318
+ }
319
+ function omitNode(count, key) {
320
+ return {
321
+ type: "text",
322
+ key,
323
+ content: omittedLabel(count),
324
+ whiteSpace: "nowrap",
325
+ ellipsis: true,
326
+ lineClamp: 1,
327
+ userSelect: "none",
328
+ style: { role: "chrome", prominence: FgP.ghost },
329
+ };
330
+ }
331
+ function placeSqueezedChildren(node, content, clip, ctx, inherit, parentKey, pin) {
332
+ const children = node.children ?? [];
333
+ if (children.length === 0)
334
+ return;
335
+ const heights = children.map((c) => measureNode(c, content.w, 10_000, ctx).h);
336
+ const naturalH = heights.reduce((a, b) => a + b, 0);
337
+ const peek = minPeekHeight(heights, pin.head, pin.tail);
338
+ const overlay = squeezeOverlay(content, clip, naturalH, peek);
339
+ if (!overlay)
340
+ return;
341
+ // Overlay sits on scrolled tape. Void cells would keep the tape's
342
+ // glyph + wash via mergeStyle. Occupy the overlay first with a
343
+ // transparent cast — no surface tint, no fuse.
344
+ const floor = { x: overlay.x, y: overlay.y, w: overlay.w, h: overlay.h };
345
+ ctx.paintOrder.push({
346
+ key: `${parentKey}:void`,
347
+ rect: floor,
348
+ paint: [{ kind: "fill", rect: floor, style: { cast: "transparent" } }],
349
+ clip,
350
+ });
351
+ const slots = planSqueeze({
352
+ heights,
353
+ overlay,
354
+ naturalY: content.y,
355
+ clipH: clip.h,
356
+ head: pin.head,
357
+ tail: pin.tail,
358
+ });
359
+ for (const slot of slots) {
360
+ if (slot.kind === "child") {
361
+ const child = children[slot.index];
362
+ if (!child)
363
+ continue;
364
+ place(child, { x: content.x, y: slot.y, w: content.w, h: slot.h }, clip, ctx, inherit, parentKey);
365
+ continue;
366
+ }
367
+ place(omitNode(slot.count, `${parentKey}:omit-${slot.edge}`), { x: content.x, y: slot.y, w: content.w, h: 1 }, clip, ctx, inherit, parentKey);
368
+ }
369
+ }
370
+ function place(node, rect, clip, ctx, inherit, parentKey) {
371
+ const regionStart = ctx.paintOrder.length;
372
+ const hitAt = ctx.hitList.length;
373
+ const naturalY = rect.y;
374
+ rect = stickyBottomRect(node, rect, clip);
375
+ rect = stickyTopRect(node, rect, clip, ctx.stickyFloor);
376
+ const path = [...ctx.path, node.type];
377
+ const prevPath = ctx.path;
378
+ ctx.path = path;
379
+ const key = keyOf(node, path);
380
+ const userSelect = "userSelect" in node && node.userSelect !== undefined ? node.userSelect : inherit.userSelect;
381
+ const inert = "inert" in node && node.inert !== undefined ? node.inert : inherit.inert;
382
+ const nextInherit = { userSelect, inert };
383
+ switch (node.type) {
384
+ case "text": {
385
+ const vis = intersect(rect, clip);
386
+ if (!vis)
387
+ break;
388
+ const style = node.style ?? {};
389
+ // Live label: one op, resolved at paintFrame(nowMs).
390
+ if (node.live) {
391
+ ctx.paintOrder.push({
392
+ key,
393
+ rect,
394
+ paint: [
395
+ {
396
+ kind: "live",
397
+ rect,
398
+ style,
399
+ live: node.live,
400
+ content: node.live.kind === "birth" ? node.content : undefined,
401
+ },
402
+ ],
403
+ clip,
404
+ });
405
+ break;
406
+ }
407
+ const lines = wrapLayoutText(node, rect.w, ctx.measure).lines;
408
+ // Top-clip: skip lines above the visible rect (viewport scroll).
409
+ const topSkip = Math.max(0, vis.y - rect.y);
410
+ const visible = lines.slice(topSkip, topSkip + vis.h);
411
+ ctx.paintOrder.push({
412
+ key,
413
+ rect,
414
+ paint: [
415
+ {
416
+ kind: "text",
417
+ rect: {
418
+ x: rect.x,
419
+ y: rect.y + topSkip,
420
+ w: rect.w,
421
+ h: visible.length,
422
+ },
423
+ lines: visible,
424
+ style,
425
+ },
426
+ ],
427
+ clip,
428
+ });
429
+ // Text was paint-only historically. Grid / diff stamps need a hit for
430
+ // semantic copy; keyed text is also addressable.
431
+ if (ctx.tableCell || ctx.diffLine || ("key" in node && node.key)) {
432
+ // Chrome under a stamp (gutter/nums with userSelect:"none") stays none.
433
+ // Only auto/inherit upgrades to text so content is hittable.
434
+ let hitUs = userSelect;
435
+ if ((ctx.tableCell || ctx.diffLine) && !isSelectableHit(userSelect) && userSelect !== "none") {
436
+ hitUs = "text";
437
+ }
438
+ pushHit(ctx, key, vis, "none", {
439
+ userSelect: hitUs,
440
+ inert,
441
+ kind: "text",
442
+ parent: parentKey,
443
+ });
444
+ }
445
+ break;
446
+ }
447
+ case "raw": {
448
+ const vis = intersect(rect, clip);
449
+ if (vis) {
450
+ ctx.paintOrder.push({
451
+ key,
452
+ rect,
453
+ paint: [{ kind: "raw", rect, paint: node.paint }],
454
+ clip,
455
+ });
456
+ const intent = inert ? "none" : (node.intent ?? "none");
457
+ pushHit(ctx, key, vis, intent, {
458
+ userSelect,
459
+ inert,
460
+ kind: "raw",
461
+ parent: parentKey,
462
+ });
463
+ }
464
+ break;
465
+ }
466
+ case "box": {
467
+ const style = node.style ?? {};
468
+ const pd = pad(style.padding);
469
+ const border = style.border ? 1 : 0;
470
+ const clipped = intersect(rect, clip);
471
+ // Off-screen children are skipped unless they themselves are sticky,
472
+ // squeeze, or overlay. Nested sticky inside a skipped unit is out.
473
+ const paints = [];
474
+ const pinClip = rect.y !== naturalY && style.sticky === "top" ? clipped : null;
475
+ if (clipped && style.background) {
476
+ paints.push({ kind: "fill", rect: pinClip ?? rect, style: style.background });
477
+ }
478
+ if (clipped && border) {
479
+ paints.push({
480
+ kind: "border",
481
+ rect,
482
+ style: { role: "border", prominence: FgP.ghost },
483
+ });
484
+ }
485
+ if (paints.length > 0) {
486
+ ctx.paintOrder.push({ key, rect, paint: paints, clip });
487
+ }
488
+ const intent = inert ? "none" : (node.intent ?? (node.onClick === undefined ? "none" : "activate"));
489
+ // Border ring is chrome for selection — same law as chrome_frame. Without
490
+ // this stamp a selectable ancestor (storybook demo-stage) claims ╭─╮/│
491
+ // cells and free-text paints box-drawing into the highlight. Interaction
492
+ // hit stays the full outer rect so bordered buttons still click on the
493
+ // frame; chrome_mask step-2 none-stamp wins over the selectable claim
494
+ // for paint/copy only.
495
+ if (clipped && border && key) {
496
+ pushHit(ctx, `${key}:chrome`, clipped, "none", {
497
+ userSelect: "none",
498
+ inert,
499
+ kind: "box_chrome",
500
+ parent: parentKey,
501
+ });
502
+ }
503
+ if (clipped) {
504
+ pushHit(ctx, key, pinClip ?? clipped, intent, {
505
+ userSelect,
506
+ inert,
507
+ onClick: node.onClick,
508
+ kind: "box",
509
+ parent: parentKey,
510
+ });
511
+ }
512
+ const content = {
513
+ x: rect.x + pd.l + border,
514
+ y: rect.y + pd.t + border,
515
+ w: Math.max(0, rect.w - pd.l - pd.r - border * 2),
516
+ h: Math.max(0, rect.h - pd.t - pd.b - border * 2),
517
+ };
518
+ let childClip = clip;
519
+ if (overflowTight(style)) {
520
+ childClip = intersect(content, clip);
521
+ }
522
+ // Diff line stamp scopes to descendants (tableCell analog).
523
+ const prevDiff = ctx.diffLine;
524
+ if (node.diffLine)
525
+ ctx.diffLine = node.diffLine;
526
+ const prevFloor = ctx.stickyFloor;
527
+ ctx.stickyFloor = rect.y + rect.h;
528
+ const squeeze = style.squeeze;
529
+ if (squeeze && (squeeze.head > 0 || squeeze.tail > 0) && (node.children?.length ?? 0) > 0) {
530
+ placeSqueezedChildren(node, content, clip, ctx, nextInherit, key, squeeze);
531
+ ctx.diffLine = prevDiff;
532
+ ctx.stickyFloor = prevFloor;
533
+ break;
534
+ }
535
+ const boxChildren = node.children ?? [];
536
+ // Sticky-top lives on every user prompt, so a "any sticky?" premeasure
537
+ // would wrap the whole tape. Measure as we go; look ahead only for the
538
+ // next prompt's natural y (sticky floor). Sticky-top below the clip
539
+ // cannot pin — do not keep walking the rest of the column for it.
540
+ const heightAt = [];
541
+ const childH = (i) => {
542
+ const item = boxChildren[i];
543
+ if (item.type === "overlay")
544
+ return 0;
545
+ const cached = heightAt[i];
546
+ if (cached !== undefined)
547
+ return cached;
548
+ const h = measureNode(item, content.w, content.h, ctx).h;
549
+ heightAt[i] = h;
550
+ return h;
551
+ };
552
+ let specialAfter = false;
553
+ const specialTail = Array.from({ length: boxChildren.length });
554
+ for (let i = boxChildren.length - 1; i >= 0; i--) {
555
+ specialTail[i] = specialAfter;
556
+ if (placesFullyBelowClip(boxChildren[i]))
557
+ specialAfter = true;
558
+ }
559
+ let cy = content.y;
560
+ for (let boxI = 0; boxI < boxChildren.length; boxI++) {
561
+ const child = boxChildren[boxI];
562
+ if (!childClip)
563
+ break;
564
+ // Overlay is absolute: full parent content rect, does not advance flow.
565
+ if (child.type === "overlay") {
566
+ place(child, content, childClip, ctx, nextInherit, key);
567
+ continue;
568
+ }
569
+ const mh = childH(boxI);
570
+ // Column stack: stretch to content width (TUI default). Shrink-wrap
571
+ // width broke indication remeasure (wrap at 1ch → caret/glyph split).
572
+ const childRect = {
573
+ x: content.x,
574
+ y: cy,
575
+ w: content.w,
576
+ h: Math.min(content.h - (cy - content.y), mh),
577
+ };
578
+ // A held tail (an ancestor placed despite being fully below the clip)
579
+ // keeps its own last child placed, so a sticky-bottom or squeeze
580
+ // descendant can still paint from below the floor. Off-screen wrap of
581
+ // every other clipped child stays skipped.
582
+ const heldTail = ctx.holdTail === true && boxI === boxChildren.length - 1;
583
+ if (!heldTail &&
584
+ !intersect(childRect, childClip) &&
585
+ !pinsFromAboveClip(child, childRect, childClip) &&
586
+ !placesFullyBelowClip(child)) {
587
+ cy += mh;
588
+ // Once we are fully below the clip, later ordinary rows cannot paint.
589
+ // A held tail still walks so the last child keeps its natural y.
590
+ if (cy >= childClip.y + childClip.h && specialTail[boxI] !== true) {
591
+ if (ctx.holdTail === true && boxI < boxChildren.length - 1)
592
+ continue;
593
+ break;
594
+ }
595
+ continue;
596
+ }
597
+ let nextTurn = rect.y + rect.h;
598
+ if (isStickyTop(child)) {
599
+ let lookY = cy + mh;
600
+ for (let j = boxI + 1; j < boxChildren.length; j++) {
601
+ if (boxChildren[j].type === "overlay")
602
+ continue;
603
+ if (isStickyTop(boxChildren[j])) {
604
+ nextTurn = lookY;
605
+ break;
606
+ }
607
+ lookY += childH(j);
608
+ }
609
+ }
610
+ const prevHold = ctx.holdTail;
611
+ const savedFloor = ctx.stickyFloor;
612
+ ctx.holdTail = heldTail;
613
+ if (isStickyTop(child))
614
+ ctx.stickyFloor = nextTurn;
615
+ place(child, childRect, childClip, ctx, nextInherit, key);
616
+ ctx.stickyFloor = savedFloor;
617
+ ctx.holdTail = prevHold;
618
+ cy += mh;
619
+ }
620
+ ctx.diffLine = prevDiff;
621
+ ctx.stickyFloor = prevFloor;
622
+ break;
623
+ }
624
+ case "flex": {
625
+ const style = node.style ?? {};
626
+ const dir = style.direction === "column" || style.direction === "column_reverse" ? "column" : "row";
627
+ const gap = clamp0(style.gap ?? 0);
628
+ const pd = pad(style.padding);
629
+ const content = {
630
+ x: rect.x + pd.l,
631
+ y: rect.y + pd.t,
632
+ w: Math.max(0, rect.w - pd.l - pd.r),
633
+ h: Math.max(0, rect.h - pd.t - pd.b),
634
+ };
635
+ const children = node.children ?? [];
636
+ const mainSize = dir === "row" ? content.w : content.h;
637
+ const crossSize = dir === "row" ? content.h : content.w;
638
+ const measured = children.map((c) => {
639
+ const m = measureNode(c, dir === "row" ? mainSize : content.w, dir === "row" ? content.h : mainSize, ctx);
640
+ return m;
641
+ });
642
+ const flexInputs = children.map((c, i) => {
643
+ const m = measured[i];
644
+ const contentMain = dir === "row" ? m.w : m.h;
645
+ const boxStyle = c.type === "box" ? c.style : undefined;
646
+ const explicit = boxStyle &&
647
+ (dir === "row" ? resolveLength(boxStyle.width, content.w) : resolveLength(boxStyle.height, content.h));
648
+ return resolveFlexItem({
649
+ flex: boxStyle?.flex,
650
+ flexGrow: boxStyle?.flexGrow,
651
+ flexShrink: boxStyle?.flexShrink,
652
+ flexBasis: typeof boxStyle?.flexBasis === "number" ? boxStyle.flexBasis : undefined,
653
+ minMain: boxStyle &&
654
+ (dir === "row"
655
+ ? resolveLength(boxStyle.minWidth, content.w)
656
+ : resolveLength(boxStyle.minHeight, content.h)) !== "auto"
657
+ ? resolveLength(dir === "row" ? boxStyle.minWidth : boxStyle.minHeight, dir === "row" ? content.w : content.h)
658
+ : undefined,
659
+ explicitMain: explicit === "auto" ? undefined : explicit,
660
+ }, contentMain);
661
+ });
662
+ const mains = flexMainSizes(flexInputs, mainSize, gap);
663
+ const align = style.alignItems ?? "stretch";
664
+ const justify = style.justifyContent ?? "flex_start";
665
+ // phantom for gap ownership
666
+ const flexClip = intersect(rect, clip);
667
+ if (flexClip) {
668
+ pushHit(ctx, `${key}:flex`, flexClip, "none", {
669
+ phantom: true,
670
+ kind: "flex",
671
+ userSelect,
672
+ inert,
673
+ parent: parentKey,
674
+ });
675
+ }
676
+ // Free main-axis space after grow/shrink — justify-content places it.
677
+ // Use largest-remainder (distributeExact) so free cells are not dropped;
678
+ // pure floor leaves last item short of flush for space_between (n≥3).
679
+ const n = children.length;
680
+ const usedMain = mains.reduce((a, b) => a + b, 0) + gap * Math.max(0, n - 1);
681
+ const freeMain = Math.max(0, mainSize - usedMain);
682
+ let startOff = 0;
683
+ // betweens[i] = gap (+ free) after child i, for i in 0..n-2
684
+ const betweens = Array.from({ length: Math.max(0, n - 1) }, () => gap);
685
+ if (freeMain > 0 && n > 0) {
686
+ switch (justify) {
687
+ case "flex_end":
688
+ startOff = freeMain;
689
+ break;
690
+ case "center":
691
+ startOff = Math.floor(freeMain / 2);
692
+ break;
693
+ case "space_between":
694
+ // first at start, last at end; n=1 → start (CSS)
695
+ if (n > 1) {
696
+ const extras = distributeExact(Array.from({ length: n - 1 }, () => 1), freeMain);
697
+ for (let i = 0; i < n - 1; i++) {
698
+ betweens[i] = gap + extras[i];
699
+ }
700
+ }
701
+ break;
702
+ case "space_around": {
703
+ // CSS: equal padding around each item → end slots weight 1, mid weight 2
704
+ const weights = Array.from({ length: n + 1 }, () => 2);
705
+ weights[0] = 1;
706
+ weights[n] = 1;
707
+ const parts = distributeExact(weights, freeMain);
708
+ startOff = parts[0];
709
+ for (let i = 0; i < n - 1; i++) {
710
+ betweens[i] = gap + parts[i + 1];
711
+ }
712
+ break;
713
+ }
714
+ case "space_evenly": {
715
+ const parts = distributeExact(Array.from({ length: n + 1 }, () => 1), freeMain);
716
+ startOff = parts[0];
717
+ for (let i = 0; i < n - 1; i++) {
718
+ betweens[i] = gap + parts[i + 1];
719
+ }
720
+ break;
721
+ }
722
+ default:
723
+ break;
724
+ }
725
+ }
726
+ let cursor = (dir === "row" ? content.x : content.y) + startOff;
727
+ for (const [i, child] of children.entries()) {
728
+ const main = mains[i];
729
+ const m = measured[i];
730
+ let cross = dir === "row" ? m.h : m.w;
731
+ if (align === "stretch")
732
+ cross = crossSize;
733
+ let crossOff = 0;
734
+ if (align === "center")
735
+ crossOff = Math.floor((crossSize - cross) / 2);
736
+ if (align === "flex_end")
737
+ crossOff = crossSize - cross;
738
+ const childRect = dir === "row"
739
+ ? {
740
+ x: cursor,
741
+ y: content.y + crossOff,
742
+ w: main,
743
+ h: Math.min(cross, content.h),
744
+ }
745
+ : {
746
+ x: content.x + crossOff,
747
+ y: cursor,
748
+ w: Math.min(cross, content.w),
749
+ h: main,
750
+ };
751
+ place(child, childRect, clip, ctx, nextInherit, key);
752
+ if (i < n - 1)
753
+ cursor += main + betweens[i];
754
+ }
755
+ break;
756
+ }
757
+ case "grid": {
758
+ placeGrid(node, rect, clip, ctx, nextInherit, key, userSelect, inert, parentKey);
759
+ break;
760
+ }
761
+ case "viewport": {
762
+ const clipped = intersect(rect, clip);
763
+ if (!clipped)
764
+ break;
765
+ const children = node.children ?? [];
766
+ // Stack children vertically; scroll by content row (not child index).
767
+ const anchor = node.overflowAnchor ?? "auto";
768
+ let totalH = 0;
769
+ const heights = children.map((c) => {
770
+ const m = measureNode(c, rect.w, 10_000, ctx);
771
+ totalH += m.h;
772
+ return m.h;
773
+ });
774
+ const maxScroll = Math.max(0, totalH - rect.h);
775
+ const scrollY = anchor === "auto" ? maxScroll : Math.max(0, Math.min(maxScroll, node.scroll ?? 0));
776
+ // Short stack: park the last child on the floor (height-0 flex-1 gap).
777
+ const floorGap = children.length >= 2 && totalH < rect.h ? rect.h - totalH : 0;
778
+ pushHit(ctx, key, clipped, "none", {
779
+ kind: "viewport",
780
+ userSelect,
781
+ inert,
782
+ parent: parentKey,
783
+ viewport: { scrollY, maxScroll },
784
+ });
785
+ let y = rect.y - scrollY;
786
+ const liftMark = ctx.stuckLifts.length;
787
+ for (let i = 0; i < children.length; i++) {
788
+ const last = i === children.length - 1;
789
+ if (last)
790
+ y += floorGap;
791
+ const h = heights[i];
792
+ // Skip fully above / break fully below — except the last child, which
793
+ // may carry sticky-bottom or squeeze descendants that must still place().
794
+ if (!last && y + h <= clipped.y) {
795
+ y += h;
796
+ continue;
797
+ }
798
+ if (!last && y >= clipped.y + clipped.h)
799
+ break;
800
+ // Full natural height — top/bottom clip via place() + line skip.
801
+ const prevHold = ctx.holdTail;
802
+ ctx.holdTail = last && y >= clipped.y + clipped.h;
803
+ place(children[i], { x: rect.x, y, w: rect.w, h }, clipped, ctx, nextInherit, key);
804
+ ctx.holdTail = prevHold;
805
+ y += h;
806
+ }
807
+ flushStuckLifts(ctx, liftMark);
808
+ break;
809
+ }
810
+ case "indication": {
811
+ const clipped = intersect(rect, clip);
812
+ if (!clipped)
813
+ break;
814
+ const style = node.style ?? {};
815
+ // Law 6: [lead][gutter][gap][content] — content at column 3.
816
+ // Gutter spans **content height** (not stretched allocation) so ∵…∴
817
+ // close on the last body row, not the bottom of a tall flex cell.
818
+ const contentW = Math.max(0, rect.w - INDICATION_CONTENT_INSET);
819
+ let contentH = 0;
820
+ const childMs = [];
821
+ for (const child of node.children ?? []) {
822
+ const m = measureNode(child, contentW, 10_000, ctx);
823
+ childMs.push(m);
824
+ contentH += m.h;
825
+ }
826
+ // Paint gutter for full content height; clip + line-skip handle scroll.
827
+ const barH = Math.max(1, contentH || 1);
828
+ const g = node.gutter;
829
+ // A two-cell sigil starts in the lead column. A one-cell glyph stays at column 1.
830
+ const wideGutter = indicationGutterWide(g);
831
+ const gutterRect = {
832
+ x: rect.x + (wideGutter ? 0 : 1),
833
+ y: rect.y,
834
+ w: wideGutter ? 2 : 1,
835
+ h: barH,
836
+ };
837
+ const gutterClipped = intersect(gutterRect, clipped);
838
+ // Spinner gutter → live paint ops (render-side; no braille in tree).
839
+ if (g?.kind === "spinner" && gutterClipped) {
840
+ const profile = g.profile ?? "working";
841
+ const live = spinnerLive(profile);
842
+ const rows = g.rows ?? "top";
843
+ const topSkip = Math.max(0, gutterClipped.y - gutterRect.y);
844
+ const paint = [];
845
+ for (let i = 0; i < gutterClipped.h; i++) {
846
+ const rowInBar = topSkip + i;
847
+ const spinHere = rows === "all" || rowInBar === 0;
848
+ const cell = {
849
+ x: gutterClipped.x,
850
+ y: gutterClipped.y + i,
851
+ w: 1,
852
+ h: 1,
853
+ };
854
+ if (spinHere) {
855
+ paint.push({
856
+ kind: "live",
857
+ rect: cell,
858
+ style: { ...style, bold: true },
859
+ live,
860
+ });
861
+ }
862
+ else {
863
+ paint.push({
864
+ kind: "text",
865
+ rect: cell,
866
+ lines: [" "],
867
+ style: { ...style, bold: true },
868
+ });
869
+ }
870
+ }
871
+ if (paint.length > 0) {
872
+ ctx.paintOrder.push({
873
+ key: `${key}:glyph`,
874
+ rect: gutterClipped,
875
+ paint,
876
+ clip: clipped,
877
+ });
878
+ }
879
+ }
880
+ else if (gutterClipped) {
881
+ const gutterLines = indicationGutterLines(node, barH);
882
+ if (gutterLines.length > 0) {
883
+ const topSkip = Math.max(0, gutterClipped.y - gutterRect.y);
884
+ const visibleGlyphs = gutterLines.slice(topSkip, topSkip + gutterClipped.h);
885
+ ctx.paintOrder.push({
886
+ key: `${key}:glyph`,
887
+ rect: gutterClipped,
888
+ paint: [
889
+ {
890
+ kind: "text",
891
+ rect: gutterClipped,
892
+ lines: visibleGlyphs,
893
+ style,
894
+ },
895
+ ],
896
+ clip: clipped,
897
+ });
898
+ }
899
+ }
900
+ const intent = inert ? "none" : (node.intent ?? "none");
901
+ pushHit(ctx, key, clipped, intent, {
902
+ userSelect,
903
+ inert,
904
+ onClick: node.onClick,
905
+ kind: "indication",
906
+ parent: parentKey,
907
+ });
908
+ const content = {
909
+ x: rect.x + INDICATION_CONTENT_INSET,
910
+ y: rect.y,
911
+ w: contentW,
912
+ h: barH,
913
+ };
914
+ let cy = content.y;
915
+ let i = 0;
916
+ for (const child of node.children ?? []) {
917
+ const m = childMs[i++] ?? measureNode(child, content.w, 10_000, ctx);
918
+ place(child, {
919
+ x: content.x,
920
+ y: cy,
921
+ w: Math.min(content.w, m.w),
922
+ h: m.h,
923
+ }, clipped, ctx, nextInherit, key);
924
+ cy += m.h;
925
+ }
926
+ break;
927
+ }
928
+ case "indentation_exception": {
929
+ const clipped = intersect(rect, clip);
930
+ if (clipped) {
931
+ pushHit(ctx, key, clipped, "none", {
932
+ kind: `indentation_exception:${node.kind}`,
933
+ userSelect,
934
+ inert,
935
+ parent: parentKey,
936
+ });
937
+ }
938
+ let cy = rect.y;
939
+ for (const child of node.children ?? []) {
940
+ const m = measureNode(child, rect.w, 10_000, ctx);
941
+ place(child, {
942
+ x: rect.x,
943
+ y: cy,
944
+ w: Math.min(rect.w, m.w),
945
+ h: m.h,
946
+ }, clip, ctx, nextInherit, key);
947
+ cy += m.h;
948
+ }
949
+ break;
950
+ }
951
+ case "markdown": {
952
+ const kids = markdownToNodes(node.content, {
953
+ key: node.key ?? key,
954
+ baseStyle: node.style,
955
+ });
956
+ let cy = rect.y;
957
+ for (const child of kids) {
958
+ const m = measureNode(child, rect.w, 10_000, ctx);
959
+ place(child, {
960
+ x: rect.x,
961
+ y: cy,
962
+ w: Math.min(rect.w, m.w),
963
+ h: Math.max(1, m.h),
964
+ }, clip, ctx, nextInherit, key);
965
+ cy += Math.max(1, m.h);
966
+ }
967
+ break;
968
+ }
969
+ case "diff": {
970
+ // Unexpanded hole — product always pre-expands. Occupy width, paint nothing.
971
+ break;
972
+ }
973
+ case "chrome_frame": {
974
+ // Special TEA strategy: fixed-width multi-role chrome (not flex).
975
+ const clipped = intersect(rect, clip);
976
+ if (!clipped)
977
+ break;
978
+ const m = measureNode(node, rect.w, rect.h, ctx);
979
+ const placeRect = {
980
+ x: rect.x,
981
+ y: rect.y,
982
+ w: Math.min(rect.w, m.w),
983
+ h: Math.min(rect.h, m.h),
984
+ };
985
+ const { ops, bodyHit, progressHit } = chromeFramePaintOps(node, placeRect);
986
+ if (ops.length > 0) {
987
+ ctx.paintOrder.push({
988
+ key: key ? `${key}:frame` : "chrome_frame:frame",
989
+ rect: placeRect,
990
+ paint: ops,
991
+ clip,
992
+ });
993
+ }
994
+ // Two hits, deliberately:
995
+ // 1) full outer rect as userSelect:none chrome — top/bottom edges
996
+ // (╭─╮ / ╰─╯), side bars (│), mark col. Without this a
997
+ // selectable ancestor (storybook stage) claims border cells and
998
+ // free-text paints ─/│ into the highlight.
999
+ // 2) body text column as contain (or none for placeholder) — the only
1000
+ // selectable surface. chrome_mask restores leaf contain over the
1001
+ // chrome stamp so draft text stays selectable/confined.
1002
+ // Key on the body hit stays the frame key (callers use "composer") for
1003
+ // caret park y/h + content-relative col.
1004
+ if (key) {
1005
+ const showingPh = isChromeFrameShowingPlaceholder(node);
1006
+ const chromeHit = intersect(placeRect, clip);
1007
+ const bodyVis = intersect(bodyHit, clip);
1008
+ if (chromeHit) {
1009
+ pushHit(ctx, `${key}:chrome`, chromeHit, "none", {
1010
+ userSelect: "none",
1011
+ inert,
1012
+ kind: "chrome_frame_chrome",
1013
+ parent: parentKey,
1014
+ });
1015
+ }
1016
+ if (bodyVis) {
1017
+ pushHit(ctx, key, bodyVis, "focus", {
1018
+ userSelect: node.bodyUserSelect ?? (showingPh ? "none" : "contain"),
1019
+ inert,
1020
+ kind: "chrome_frame",
1021
+ parent: parentKey,
1022
+ });
1023
+ }
1024
+ const progressVis = intersect(progressHit, clip);
1025
+ if (progressVis && progressVis.w > 0 && progressVis.h > 0) {
1026
+ pushHit(ctx, `${key}:progress`, progressVis, "none", {
1027
+ userSelect: "none",
1028
+ inert,
1029
+ kind: "chrome_frame_progress",
1030
+ parent: parentKey,
1031
+ });
1032
+ }
1033
+ }
1034
+ break;
1035
+ }
1036
+ case "disclosure": {
1037
+ const clipped = intersect(rect, clip);
1038
+ if (!clipped)
1039
+ break;
1040
+ const intent = inert ? "none" : (node.intent ?? "none");
1041
+ pushHit(ctx, key, clipped, intent, {
1042
+ userSelect,
1043
+ inert,
1044
+ onClick: node.onClick,
1045
+ kind: "disclosure",
1046
+ parent: parentKey,
1047
+ });
1048
+ const kids = node.collapsed === false ? (node.children ?? []) : [node.summary];
1049
+ let cy = rect.y;
1050
+ for (const child of kids) {
1051
+ const m = measureNode(child, rect.w, 10_000, ctx);
1052
+ if (cy + m.h <= clipped.y) {
1053
+ cy += m.h;
1054
+ continue;
1055
+ }
1056
+ if (cy >= clipped.y + clipped.h)
1057
+ break;
1058
+ place(child, {
1059
+ x: rect.x,
1060
+ y: cy,
1061
+ w: Math.min(rect.w, m.w),
1062
+ h: m.h,
1063
+ }, clipped, ctx, nextInherit, key);
1064
+ cy += m.h;
1065
+ }
1066
+ break;
1067
+ }
1068
+ case "overlay": {
1069
+ const full = intersect(rect, clip);
1070
+ if (!full)
1071
+ break;
1072
+ if (node.backdrop) {
1073
+ // Decision 1: content behind a modal is region-dimmed
1074
+ // (regionProminence) — the room recedes, identity stays visible.
1075
+ // Default (no backdropStyle): stamp the region onto every paint op
1076
+ // already in the order (base content). Overlay children place after
1077
+ // and keep full prominence. Explicit backdropStyle is painted
1078
+ // furniture the caller owns (bgRole + BgP or a literal wash) and
1079
+ // does not stamp — the scrim covers rather than fades.
1080
+ // Hit still registers so click-outside dismiss works either way.
1081
+ if (node.backdropStyle) {
1082
+ ctx.paintOrder.push({
1083
+ key: `${key}:backdrop`,
1084
+ rect,
1085
+ paint: [{ kind: "fill", rect, style: node.backdropStyle }],
1086
+ clip,
1087
+ });
1088
+ }
1089
+ else {
1090
+ stampBackdropRegion(ctx.paintOrder, BACKDROP_REGION_PROMINENCE);
1091
+ }
1092
+ pushHit(ctx, `${key}:backdrop`, full, node.intent ?? "dismiss", {
1093
+ kind: "overlay-backdrop",
1094
+ inert,
1095
+ onClick: node.onClick,
1096
+ parent: parentKey,
1097
+ });
1098
+ }
1099
+ const children = node.children ?? [];
1100
+ if (node.origin != null && children.length > 0) {
1101
+ const ox = rect.x + clamp0(node.origin.x);
1102
+ const oy = rect.y + clamp0(node.origin.y);
1103
+ for (const child of children) {
1104
+ const maxW = Math.max(0, rect.x + rect.w - ox);
1105
+ const maxH = Math.max(0, rect.y + rect.h - oy);
1106
+ const m = measureNode(child, maxW, maxH, ctx);
1107
+ place(child, { x: ox, y: oy, w: Math.min(maxW, m.w), h: Math.min(maxH, m.h) }, full, ctx, nextInherit, key);
1108
+ }
1109
+ }
1110
+ else if (node.center && children.length > 0) {
1111
+ const child = children[0];
1112
+ const m = measureNode(child, Math.floor(rect.w * 0.8), Math.floor(rect.h * 0.6), ctx);
1113
+ const cx = rect.x + Math.floor((rect.w - m.w) / 2);
1114
+ const cy = rect.y + Math.floor((rect.h - m.h) / 2);
1115
+ place(child, { x: cx, y: cy, w: m.w, h: m.h }, full, ctx, nextInherit, key);
1116
+ }
1117
+ else {
1118
+ let cy = rect.y + 1;
1119
+ for (const child of children) {
1120
+ const m = measureNode(child, rect.w - 2, rect.h, ctx);
1121
+ place(child, { x: rect.x + 1, y: cy, w: Math.min(rect.w - 2, m.w), h: m.h }, full, ctx, nextInherit, key);
1122
+ cy += m.h;
1123
+ }
1124
+ }
1125
+ break;
1126
+ }
1127
+ }
1128
+ if (node.regionProminence !== undefined) {
1129
+ stampBackdropRegion(ctx.paintOrder.slice(regionStart), node.regionProminence);
1130
+ }
1131
+ if (rect.y !== naturalY && node.type === "box" && node.style?.sticky === "top") {
1132
+ ctx.stuckLifts.push({
1133
+ paintAt: regionStart,
1134
+ paintTo: ctx.paintOrder.length,
1135
+ hitAt,
1136
+ hitTo: ctx.hitList.length,
1137
+ });
1138
+ }
1139
+ ctx.path = prevPath;
1140
+ }
1141
+ export function layout(root, viewport, opts = {}) {
1142
+ const measure = opts.measure ?? defaultMeasure;
1143
+ const ctx = {
1144
+ measure,
1145
+ path: [],
1146
+ paintOrder: [],
1147
+ hitList: [],
1148
+ byKey: new Map(),
1149
+ counters: { invalidStyle: 0, frame: 0 },
1150
+ stuckLifts: [],
1151
+ };
1152
+ const w = Math.max(1, viewport.w);
1153
+ const h = Math.max(1, viewport.h);
1154
+ const rect = { x: 0, y: 0, w, h };
1155
+ place(root, rect, rect, ctx, { userSelect: "auto", inert: false });
1156
+ flushStuckLifts(ctx, 0);
1157
+ // hit list reverse paint for occlusion: reverse so later paint wins
1158
+ ctx.hitList.reverse();
1159
+ ctx.counters.frame += 1;
1160
+ const liveDemand = collectLiveDemand(ctx.paintOrder);
1161
+ return {
1162
+ version: opts.frameVersion ?? ctx.counters.frame,
1163
+ paintOrder: ctx.paintOrder,
1164
+ hitList: ctx.hitList,
1165
+ byKey: ctx.byKey,
1166
+ viewport: { w, h },
1167
+ liveDemand,
1168
+ };
1169
+ }
1170
+ export function elementAt(frame, x, y) {
1171
+ for (const meta of frame.hitList) {
1172
+ if (meta.inert)
1173
+ continue;
1174
+ if (!rectContains(meta.hitRect, x, y))
1175
+ continue;
1176
+ if (meta.intent === "none" && meta.phantom)
1177
+ continue;
1178
+ // Union-correct check: "none"/"auto" are not hit-eligible on their own;
1179
+ // only {text, all, contain} admit a none-intent node (truthiness would admit them,
1180
+ // which incorrectly admitted the literal "none" string).
1181
+ if (meta.intent === "none" && !isSelectableHit(meta.userSelect) && !meta.onClick)
1182
+ continue;
1183
+ return { key: meta.key, meta };
1184
+ }
1185
+ // fallback: addressable box (byKey) containing point. Apply the SAME
1186
+ // admission gate as the primary path — otherwise chrome (userSelect:"none")
1187
+ // with no intent/onClick would be returned here, defeating the gate above.
1188
+ for (const meta of frame.byKey.values()) {
1189
+ if (!isAdmissibleHit(meta))
1190
+ continue;
1191
+ if (!rectContains(meta.hitRect, x, y))
1192
+ continue;
1193
+ return { key: meta.key, meta };
1194
+ }
1195
+ return null;
1196
+ }
1197
+ /**
1198
+ * Root→leaf chain of admissible hits at `(x, y)`, or `null` if nothing is
1199
+ * hittable there. The leaf is the same node {@link elementAt} returns
1200
+ * (topmost in paint order); the chain then walks `HitMeta.parent` up to the
1201
+ * root and is returned root-first.
1202
+ *
1203
+ * Structural selection (Phase 4) uses this to walk up the LayoutNode AST:
1204
+ * "expand selection from this cell to its containing block." Each link in the
1205
+ * returned chain passes the same admission gate as {@link elementAt}
1206
+ * (see {@link isAdmissibleHit}) — so a `userSelect:"none"` structural
1207
+ * container is traversed (its parent pointer is followed) but excluded from
1208
+ * the result, while selectable containers (`text` / `all` / `contain`) appear
1209
+ * so a caller can offer "select this block" at each level.
1210
+ *
1211
+ * Parent links resolve via {@link Frame.byKey}; the walk terminates when a
1212
+ * parent key is absent (e.g. a flex container, which is structural but not in
1213
+ * byKey) or undefined (the root). A cycle guard caps the walk at the frame's
1214
+ * byKey size to defend against malformed parent loops.
1215
+ */
1216
+ export function elementAtPath(frame, x, y) {
1217
+ const leaf = elementAt(frame, x, y);
1218
+ if (!leaf)
1219
+ return null;
1220
+ // Walk parent pointers from the leaf upward. Collect every meta; admission
1221
+ // filtering happens after the walk so a non-admissible structural container
1222
+ // (chrome) still links its selectable descendants to their grand-parents.
1223
+ const chain = [];
1224
+ const seen = new Set();
1225
+ let cur = leaf.meta;
1226
+ // Cap at byKey.size + 1 so a malformed parent loop can't spin forever; +1
1227
+ // covers the leaf itself before any parent is entered.
1228
+ const cap = frame.byKey.size + 1;
1229
+ let steps = 0;
1230
+ while (cur && steps < cap) {
1231
+ if (seen.has(cur.key))
1232
+ break;
1233
+ seen.add(cur.key);
1234
+ chain.push({ key: cur.key, meta: cur });
1235
+ steps++;
1236
+ const parentKey = cur.parent;
1237
+ if (parentKey === undefined)
1238
+ break;
1239
+ cur = frame.byKey.get(parentKey);
1240
+ }
1241
+ // Root-first order, then drop non-admissible structural containers
1242
+ // (chrome userSelect:"none", phantoms). The leaf itself is always kept —
1243
+ // elementAt already admitted it.
1244
+ chain.reverse();
1245
+ return chain.filter((link, idx) => idx === chain.length - 1 || isAdmissibleHit(link.meta));
1246
+ }
1247
+ /**
1248
+ * Nearest `userSelect: "contain"` region covering `(x, y)`, if any.
1249
+ * Walks the root→leaf hit path from the leaf upward so nested contain boxes
1250
+ * pick the innermost (composer inside a larger pane).
1251
+ *
1252
+ * Used by the gesture machine to arm confinement: once a drag starts inside
1253
+ * a contain region, endpoints are clamped to {@link ContainRegion.rect}.
1254
+ */
1255
+ export function containRegionAt(frame, x, y) {
1256
+ const path = elementAtPath(frame, x, y);
1257
+ if (!path || path.length === 0)
1258
+ return null;
1259
+ // Leaf-first search for the innermost contain.
1260
+ for (let i = path.length - 1; i >= 0; i--) {
1261
+ const link = path[i];
1262
+ if (link.meta.userSelect === "contain") {
1263
+ const r = link.meta.hitRect;
1264
+ return { key: link.key, rect: { x: r.x, y: r.y, w: r.w, h: r.h } };
1265
+ }
1266
+ }
1267
+ return null;
1268
+ }
1269
+ //# sourceMappingURL=engine.js.map