@promptctl/rich-js 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 (410) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +20 -110
  3. package/THIRD-PARTY-NOTICES +81 -0
  4. package/dist/core/box.d.ts +0 -1
  5. package/dist/core/box.js +0 -1
  6. package/dist/core/cells.d.ts +0 -1
  7. package/dist/core/cells.js +0 -1
  8. package/dist/core/color.d.ts +6 -1
  9. package/dist/core/color.js +6 -1
  10. package/dist/core/console.d.ts +13 -4
  11. package/dist/core/console.js +78 -87
  12. package/dist/core/emoji.d.ts +0 -1
  13. package/dist/core/emoji.js +0 -1
  14. package/dist/core/export-html.d.ts +24 -0
  15. package/dist/core/export-html.js +106 -0
  16. package/dist/core/export-lines.d.ts +121 -0
  17. package/dist/core/export-lines.js +141 -0
  18. package/dist/core/highlighter.d.ts +0 -1
  19. package/dist/core/highlighter.js +0 -1
  20. package/dist/core/json.d.ts +0 -1
  21. package/dist/core/json.js +0 -1
  22. package/dist/core/markup.d.ts +33 -2
  23. package/dist/core/markup.js +128 -16
  24. package/dist/core/measure.d.ts +0 -1
  25. package/dist/core/measure.js +0 -1
  26. package/dist/core/oklch.d.ts +0 -1
  27. package/dist/core/oklch.js +0 -1
  28. package/dist/core/osc8.d.ts +68 -0
  29. package/dist/core/osc8.js +90 -0
  30. package/dist/core/pretty.d.ts +21 -2
  31. package/dist/core/pretty.js +90 -16
  32. package/dist/core/protocol.d.ts +18 -2
  33. package/dist/core/protocol.js +0 -1
  34. package/dist/core/render.d.ts +0 -1
  35. package/dist/core/render.js +3 -10
  36. package/dist/core/segment.d.ts +0 -1
  37. package/dist/core/segment.js +0 -1
  38. package/dist/core/spinnerData.d.ts +0 -1
  39. package/dist/core/spinnerData.js +0 -1
  40. package/dist/core/strip.d.ts +0 -1
  41. package/dist/core/strip.js +12 -1
  42. package/dist/core/style.d.ts +1 -7
  43. package/dist/core/style.js +84 -67
  44. package/dist/core/subscription.d.ts +0 -1
  45. package/dist/core/subscription.js +0 -1
  46. package/dist/core/text.d.ts +1 -8
  47. package/dist/core/text.js +25 -29
  48. package/dist/core/wrap.d.ts +0 -1
  49. package/dist/core/wrap.js +0 -1
  50. package/dist/host/host-stream.d.ts +0 -1
  51. package/dist/host/host-stream.js +0 -1
  52. package/dist/host/index.d.ts +0 -1
  53. package/dist/host/index.js +0 -1
  54. package/dist/host/terminal-host.d.ts +0 -1
  55. package/dist/host/terminal-host.js +0 -1
  56. package/dist/index.d.ts +4 -3
  57. package/dist/index.js +3 -2
  58. package/dist/node/prompt.d.ts +0 -1
  59. package/dist/node/prompt.js +0 -1
  60. package/dist/node/save.d.ts +2 -7
  61. package/dist/node/save.js +0 -1
  62. package/dist/node/terminal-host.d.ts +0 -1
  63. package/dist/node/terminal-host.js +0 -1
  64. package/dist/node/traceback.d.ts +0 -1
  65. package/dist/node/traceback.js +0 -1
  66. package/dist/renderables/align.d.ts +0 -1
  67. package/dist/renderables/align.js +0 -1
  68. package/dist/renderables/columns.d.ts +12 -1
  69. package/dist/renderables/columns.js +29 -5
  70. package/dist/renderables/constrain.d.ts +0 -1
  71. package/dist/renderables/constrain.js +0 -1
  72. package/dist/renderables/flexStrip.d.ts +0 -1
  73. package/dist/renderables/flexStrip.js +0 -1
  74. package/dist/renderables/group.d.ts +0 -1
  75. package/dist/renderables/group.js +0 -1
  76. package/dist/renderables/layout.d.ts +0 -1
  77. package/dist/renderables/layout.js +20 -7
  78. package/dist/renderables/live.d.ts +0 -1
  79. package/dist/renderables/live.js +0 -1
  80. package/dist/renderables/markdown.d.ts +0 -1
  81. package/dist/renderables/markdown.js +0 -1
  82. package/dist/renderables/padding.d.ts +0 -1
  83. package/dist/renderables/padding.js +0 -1
  84. package/dist/renderables/panel.d.ts +0 -1
  85. package/dist/renderables/panel.js +12 -4
  86. package/dist/renderables/progress.d.ts +0 -1
  87. package/dist/renderables/progress.js +7 -2
  88. package/dist/renderables/progressBar.d.ts +0 -1
  89. package/dist/renderables/progressBar.js +0 -1
  90. package/dist/renderables/prompt.d.ts +0 -1
  91. package/dist/renderables/prompt.js +0 -1
  92. package/dist/renderables/rule.d.ts +0 -1
  93. package/dist/renderables/rule.js +0 -1
  94. package/dist/renderables/spinner.d.ts +0 -1
  95. package/dist/renderables/spinner.js +0 -1
  96. package/dist/renderables/status.d.ts +0 -1
  97. package/dist/renderables/status.js +0 -1
  98. package/dist/renderables/syntax.d.ts +0 -1
  99. package/dist/renderables/syntax.js +0 -1
  100. package/dist/renderables/table.d.ts +0 -1
  101. package/dist/renderables/table.js +8 -3
  102. package/dist/renderables/traceback.d.ts +8 -7
  103. package/dist/renderables/traceback.js +14 -10
  104. package/dist/renderables/tree.d.ts +0 -1
  105. package/dist/renderables/tree.js +0 -1
  106. package/dist/template-bindings/color-funcs.d.ts +0 -1
  107. package/dist/template-bindings/color-funcs.js +0 -1
  108. package/dist/template-bindings/helpers.d.ts +0 -1
  109. package/dist/template-bindings/helpers.js +0 -1
  110. package/dist/template-bindings/index.d.ts +0 -1
  111. package/dist/template-bindings/index.js +0 -1
  112. package/dist/template-bindings/palette-funcs.d.ts +0 -1
  113. package/dist/template-bindings/palette-funcs.js +0 -1
  114. package/dist/template-bindings/style-funcs.d.ts +0 -1
  115. package/dist/template-bindings/style-funcs.js +0 -1
  116. package/dist/themes/buildPalette.d.ts +0 -1
  117. package/dist/themes/buildPalette.js +0 -1
  118. package/dist/themes/colorMath.d.ts +0 -1
  119. package/dist/themes/colorMath.js +0 -1
  120. package/dist/themes/colorRef.d.ts +0 -1
  121. package/dist/themes/colorRef.js +0 -1
  122. package/dist/themes/data/atom-one-dark.d.ts +0 -1
  123. package/dist/themes/data/atom-one-dark.js +0 -1
  124. package/dist/themes/data/atom-one-light.d.ts +0 -1
  125. package/dist/themes/data/atom-one-light.js +0 -1
  126. package/dist/themes/data/catppuccin-frappe.d.ts +0 -1
  127. package/dist/themes/data/catppuccin-frappe.js +0 -1
  128. package/dist/themes/data/catppuccin-latte.d.ts +0 -1
  129. package/dist/themes/data/catppuccin-latte.js +0 -1
  130. package/dist/themes/data/catppuccin-macchiato.d.ts +0 -1
  131. package/dist/themes/data/catppuccin-macchiato.js +0 -1
  132. package/dist/themes/data/catppuccin-mocha.d.ts +0 -1
  133. package/dist/themes/data/catppuccin-mocha.js +0 -1
  134. package/dist/themes/data/cyberpunk.d.ts +0 -1
  135. package/dist/themes/data/cyberpunk.js +0 -1
  136. package/dist/themes/data/default.d.ts +0 -1
  137. package/dist/themes/data/default.js +0 -1
  138. package/dist/themes/data/dracula.d.ts +0 -1
  139. package/dist/themes/data/dracula.js +0 -1
  140. package/dist/themes/data/flexoki.d.ts +0 -1
  141. package/dist/themes/data/flexoki.js +0 -1
  142. package/dist/themes/data/gruvbox.d.ts +0 -1
  143. package/dist/themes/data/gruvbox.js +0 -1
  144. package/dist/themes/data/index.d.ts +0 -1
  145. package/dist/themes/data/index.js +0 -1
  146. package/dist/themes/data/monokai.d.ts +0 -1
  147. package/dist/themes/data/monokai.js +0 -1
  148. package/dist/themes/data/nord.d.ts +0 -1
  149. package/dist/themes/data/nord.js +0 -1
  150. package/dist/themes/data/rose-pine-dawn.d.ts +0 -1
  151. package/dist/themes/data/rose-pine-dawn.js +0 -1
  152. package/dist/themes/data/rose-pine-moon.d.ts +0 -1
  153. package/dist/themes/data/rose-pine-moon.js +0 -1
  154. package/dist/themes/data/rose-pine.d.ts +0 -1
  155. package/dist/themes/data/rose-pine.js +0 -1
  156. package/dist/themes/data/solarized-dark.d.ts +0 -1
  157. package/dist/themes/data/solarized-dark.js +0 -1
  158. package/dist/themes/data/solarized-light.d.ts +0 -1
  159. package/dist/themes/data/solarized-light.js +0 -1
  160. package/dist/themes/data/svg-export.d.ts +0 -1
  161. package/dist/themes/data/svg-export.js +0 -1
  162. package/dist/themes/data/textual-ansi.d.ts +0 -1
  163. package/dist/themes/data/textual-ansi.js +0 -1
  164. package/dist/themes/data/textual-dark.d.ts +0 -1
  165. package/dist/themes/data/textual-dark.js +0 -1
  166. package/dist/themes/data/textual-light.d.ts +0 -1
  167. package/dist/themes/data/textual-light.js +0 -1
  168. package/dist/themes/data/tokyo-night.d.ts +0 -1
  169. package/dist/themes/data/tokyo-night.js +0 -1
  170. package/dist/themes/data/types.d.ts +0 -1
  171. package/dist/themes/data/types.js +0 -1
  172. package/dist/themes/palette.d.ts +0 -1
  173. package/dist/themes/palette.js +0 -1
  174. package/dist/themes/ramp.d.ts +0 -1
  175. package/dist/themes/ramp.js +0 -1
  176. package/dist/themes/registry.d.ts +0 -1
  177. package/dist/themes/registry.js +0 -1
  178. package/dist/themes/terminalThemes.d.ts +0 -1
  179. package/dist/themes/terminalThemes.js +0 -1
  180. package/dist/themes/transpose.d.ts +0 -1
  181. package/dist/themes/transpose.js +0 -1
  182. package/dist/widgets/button.d.ts +0 -1
  183. package/dist/widgets/button.js +4 -6
  184. package/dist/widgets/checkbox.d.ts +0 -1
  185. package/dist/widgets/checkbox.js +3 -5
  186. package/dist/widgets/dropdown.d.ts +0 -1
  187. package/dist/widgets/dropdown.js +4 -6
  188. package/dist/widgets/event-router.d.ts +0 -1
  189. package/dist/widgets/event-router.js +0 -1
  190. package/dist/widgets/focus-manager.d.ts +0 -2
  191. package/dist/widgets/focus-manager.js +3 -8
  192. package/dist/widgets/index.d.ts +0 -1
  193. package/dist/widgets/index.js +0 -1
  194. package/dist/widgets/screen.d.ts +0 -1
  195. package/dist/widgets/screen.js +2 -5
  196. package/dist/widgets/slider.d.ts +0 -1
  197. package/dist/widgets/slider.js +7 -9
  198. package/dist/widgets/static-item.d.ts +0 -1
  199. package/dist/widgets/static-item.js +0 -1
  200. package/dist/widgets/text-input.d.ts +0 -1
  201. package/dist/widgets/text-input.js +5 -7
  202. package/dist/widgets/toggle.d.ts +0 -1
  203. package/dist/widgets/toggle.js +4 -6
  204. package/dist/widgets/types.d.ts +0 -1
  205. package/dist/widgets/types.js +0 -1
  206. package/dist/widgets/widget-base.d.ts +3 -4
  207. package/dist/widgets/widget-base.js +4 -7
  208. package/package.json +14 -13
  209. package/dist/core/box.d.ts.map +0 -1
  210. package/dist/core/box.js.map +0 -1
  211. package/dist/core/cells.d.ts.map +0 -1
  212. package/dist/core/cells.js.map +0 -1
  213. package/dist/core/color.d.ts.map +0 -1
  214. package/dist/core/color.js.map +0 -1
  215. package/dist/core/console.d.ts.map +0 -1
  216. package/dist/core/console.js.map +0 -1
  217. package/dist/core/emoji.d.ts.map +0 -1
  218. package/dist/core/emoji.js.map +0 -1
  219. package/dist/core/highlighter.d.ts.map +0 -1
  220. package/dist/core/highlighter.js.map +0 -1
  221. package/dist/core/json.d.ts.map +0 -1
  222. package/dist/core/json.js.map +0 -1
  223. package/dist/core/markup.d.ts.map +0 -1
  224. package/dist/core/markup.js.map +0 -1
  225. package/dist/core/measure.d.ts.map +0 -1
  226. package/dist/core/measure.js.map +0 -1
  227. package/dist/core/oklch.d.ts.map +0 -1
  228. package/dist/core/oklch.js.map +0 -1
  229. package/dist/core/pretty.d.ts.map +0 -1
  230. package/dist/core/pretty.js.map +0 -1
  231. package/dist/core/protocol.d.ts.map +0 -1
  232. package/dist/core/protocol.js.map +0 -1
  233. package/dist/core/render.d.ts.map +0 -1
  234. package/dist/core/render.js.map +0 -1
  235. package/dist/core/sanitize.d.ts +0 -16
  236. package/dist/core/sanitize.d.ts.map +0 -1
  237. package/dist/core/sanitize.js +0 -19
  238. package/dist/core/sanitize.js.map +0 -1
  239. package/dist/core/segment.d.ts.map +0 -1
  240. package/dist/core/segment.js.map +0 -1
  241. package/dist/core/spinnerData.d.ts.map +0 -1
  242. package/dist/core/spinnerData.js.map +0 -1
  243. package/dist/core/strip.d.ts.map +0 -1
  244. package/dist/core/strip.js.map +0 -1
  245. package/dist/core/style.d.ts.map +0 -1
  246. package/dist/core/style.js.map +0 -1
  247. package/dist/core/subscription.d.ts.map +0 -1
  248. package/dist/core/subscription.js.map +0 -1
  249. package/dist/core/text.d.ts.map +0 -1
  250. package/dist/core/text.js.map +0 -1
  251. package/dist/core/wrap.d.ts.map +0 -1
  252. package/dist/core/wrap.js.map +0 -1
  253. package/dist/host/host-stream.d.ts.map +0 -1
  254. package/dist/host/host-stream.js.map +0 -1
  255. package/dist/host/index.d.ts.map +0 -1
  256. package/dist/host/index.js.map +0 -1
  257. package/dist/host/terminal-host.d.ts.map +0 -1
  258. package/dist/host/terminal-host.js.map +0 -1
  259. package/dist/index.d.ts.map +0 -1
  260. package/dist/index.js.map +0 -1
  261. package/dist/node/prompt.d.ts.map +0 -1
  262. package/dist/node/prompt.js.map +0 -1
  263. package/dist/node/save.d.ts.map +0 -1
  264. package/dist/node/save.js.map +0 -1
  265. package/dist/node/terminal-host.d.ts.map +0 -1
  266. package/dist/node/terminal-host.js.map +0 -1
  267. package/dist/node/traceback.d.ts.map +0 -1
  268. package/dist/node/traceback.js.map +0 -1
  269. package/dist/renderables/align.d.ts.map +0 -1
  270. package/dist/renderables/align.js.map +0 -1
  271. package/dist/renderables/columns.d.ts.map +0 -1
  272. package/dist/renderables/columns.js.map +0 -1
  273. package/dist/renderables/constrain.d.ts.map +0 -1
  274. package/dist/renderables/constrain.js.map +0 -1
  275. package/dist/renderables/flexStrip.d.ts.map +0 -1
  276. package/dist/renderables/flexStrip.js.map +0 -1
  277. package/dist/renderables/group.d.ts.map +0 -1
  278. package/dist/renderables/group.js.map +0 -1
  279. package/dist/renderables/layout.d.ts.map +0 -1
  280. package/dist/renderables/layout.js.map +0 -1
  281. package/dist/renderables/live.d.ts.map +0 -1
  282. package/dist/renderables/live.js.map +0 -1
  283. package/dist/renderables/markdown.d.ts.map +0 -1
  284. package/dist/renderables/markdown.js.map +0 -1
  285. package/dist/renderables/padding.d.ts.map +0 -1
  286. package/dist/renderables/padding.js.map +0 -1
  287. package/dist/renderables/panel.d.ts.map +0 -1
  288. package/dist/renderables/panel.js.map +0 -1
  289. package/dist/renderables/progress.d.ts.map +0 -1
  290. package/dist/renderables/progress.js.map +0 -1
  291. package/dist/renderables/progressBar.d.ts.map +0 -1
  292. package/dist/renderables/progressBar.js.map +0 -1
  293. package/dist/renderables/prompt.d.ts.map +0 -1
  294. package/dist/renderables/prompt.js.map +0 -1
  295. package/dist/renderables/rule.d.ts.map +0 -1
  296. package/dist/renderables/rule.js.map +0 -1
  297. package/dist/renderables/spinner.d.ts.map +0 -1
  298. package/dist/renderables/spinner.js.map +0 -1
  299. package/dist/renderables/status.d.ts.map +0 -1
  300. package/dist/renderables/status.js.map +0 -1
  301. package/dist/renderables/syntax.d.ts.map +0 -1
  302. package/dist/renderables/syntax.js.map +0 -1
  303. package/dist/renderables/table.d.ts.map +0 -1
  304. package/dist/renderables/table.js.map +0 -1
  305. package/dist/renderables/traceback.d.ts.map +0 -1
  306. package/dist/renderables/traceback.js.map +0 -1
  307. package/dist/renderables/tree.d.ts.map +0 -1
  308. package/dist/renderables/tree.js.map +0 -1
  309. package/dist/template-bindings/color-funcs.d.ts.map +0 -1
  310. package/dist/template-bindings/color-funcs.js.map +0 -1
  311. package/dist/template-bindings/helpers.d.ts.map +0 -1
  312. package/dist/template-bindings/helpers.js.map +0 -1
  313. package/dist/template-bindings/index.d.ts.map +0 -1
  314. package/dist/template-bindings/index.js.map +0 -1
  315. package/dist/template-bindings/palette-funcs.d.ts.map +0 -1
  316. package/dist/template-bindings/palette-funcs.js.map +0 -1
  317. package/dist/template-bindings/style-funcs.d.ts.map +0 -1
  318. package/dist/template-bindings/style-funcs.js.map +0 -1
  319. package/dist/themes/buildPalette.d.ts.map +0 -1
  320. package/dist/themes/buildPalette.js.map +0 -1
  321. package/dist/themes/colorMath.d.ts.map +0 -1
  322. package/dist/themes/colorMath.js.map +0 -1
  323. package/dist/themes/colorRef.d.ts.map +0 -1
  324. package/dist/themes/colorRef.js.map +0 -1
  325. package/dist/themes/data/atom-one-dark.d.ts.map +0 -1
  326. package/dist/themes/data/atom-one-dark.js.map +0 -1
  327. package/dist/themes/data/atom-one-light.d.ts.map +0 -1
  328. package/dist/themes/data/atom-one-light.js.map +0 -1
  329. package/dist/themes/data/catppuccin-frappe.d.ts.map +0 -1
  330. package/dist/themes/data/catppuccin-frappe.js.map +0 -1
  331. package/dist/themes/data/catppuccin-latte.d.ts.map +0 -1
  332. package/dist/themes/data/catppuccin-latte.js.map +0 -1
  333. package/dist/themes/data/catppuccin-macchiato.d.ts.map +0 -1
  334. package/dist/themes/data/catppuccin-macchiato.js.map +0 -1
  335. package/dist/themes/data/catppuccin-mocha.d.ts.map +0 -1
  336. package/dist/themes/data/catppuccin-mocha.js.map +0 -1
  337. package/dist/themes/data/cyberpunk.d.ts.map +0 -1
  338. package/dist/themes/data/cyberpunk.js.map +0 -1
  339. package/dist/themes/data/default.d.ts.map +0 -1
  340. package/dist/themes/data/default.js.map +0 -1
  341. package/dist/themes/data/dracula.d.ts.map +0 -1
  342. package/dist/themes/data/dracula.js.map +0 -1
  343. package/dist/themes/data/flexoki.d.ts.map +0 -1
  344. package/dist/themes/data/flexoki.js.map +0 -1
  345. package/dist/themes/data/gruvbox.d.ts.map +0 -1
  346. package/dist/themes/data/gruvbox.js.map +0 -1
  347. package/dist/themes/data/index.d.ts.map +0 -1
  348. package/dist/themes/data/index.js.map +0 -1
  349. package/dist/themes/data/monokai.d.ts.map +0 -1
  350. package/dist/themes/data/monokai.js.map +0 -1
  351. package/dist/themes/data/nord.d.ts.map +0 -1
  352. package/dist/themes/data/nord.js.map +0 -1
  353. package/dist/themes/data/rose-pine-dawn.d.ts.map +0 -1
  354. package/dist/themes/data/rose-pine-dawn.js.map +0 -1
  355. package/dist/themes/data/rose-pine-moon.d.ts.map +0 -1
  356. package/dist/themes/data/rose-pine-moon.js.map +0 -1
  357. package/dist/themes/data/rose-pine.d.ts.map +0 -1
  358. package/dist/themes/data/rose-pine.js.map +0 -1
  359. package/dist/themes/data/solarized-dark.d.ts.map +0 -1
  360. package/dist/themes/data/solarized-dark.js.map +0 -1
  361. package/dist/themes/data/solarized-light.d.ts.map +0 -1
  362. package/dist/themes/data/solarized-light.js.map +0 -1
  363. package/dist/themes/data/svg-export.d.ts.map +0 -1
  364. package/dist/themes/data/svg-export.js.map +0 -1
  365. package/dist/themes/data/textual-ansi.d.ts.map +0 -1
  366. package/dist/themes/data/textual-ansi.js.map +0 -1
  367. package/dist/themes/data/textual-dark.d.ts.map +0 -1
  368. package/dist/themes/data/textual-dark.js.map +0 -1
  369. package/dist/themes/data/textual-light.d.ts.map +0 -1
  370. package/dist/themes/data/textual-light.js.map +0 -1
  371. package/dist/themes/data/tokyo-night.d.ts.map +0 -1
  372. package/dist/themes/data/tokyo-night.js.map +0 -1
  373. package/dist/themes/data/types.d.ts.map +0 -1
  374. package/dist/themes/data/types.js.map +0 -1
  375. package/dist/themes/palette.d.ts.map +0 -1
  376. package/dist/themes/palette.js.map +0 -1
  377. package/dist/themes/ramp.d.ts.map +0 -1
  378. package/dist/themes/ramp.js.map +0 -1
  379. package/dist/themes/registry.d.ts.map +0 -1
  380. package/dist/themes/registry.js.map +0 -1
  381. package/dist/themes/terminalThemes.d.ts.map +0 -1
  382. package/dist/themes/terminalThemes.js.map +0 -1
  383. package/dist/themes/transpose.d.ts.map +0 -1
  384. package/dist/themes/transpose.js.map +0 -1
  385. package/dist/widgets/button.d.ts.map +0 -1
  386. package/dist/widgets/button.js.map +0 -1
  387. package/dist/widgets/checkbox.d.ts.map +0 -1
  388. package/dist/widgets/checkbox.js.map +0 -1
  389. package/dist/widgets/dropdown.d.ts.map +0 -1
  390. package/dist/widgets/dropdown.js.map +0 -1
  391. package/dist/widgets/event-router.d.ts.map +0 -1
  392. package/dist/widgets/event-router.js.map +0 -1
  393. package/dist/widgets/focus-manager.d.ts.map +0 -1
  394. package/dist/widgets/focus-manager.js.map +0 -1
  395. package/dist/widgets/index.d.ts.map +0 -1
  396. package/dist/widgets/index.js.map +0 -1
  397. package/dist/widgets/screen.d.ts.map +0 -1
  398. package/dist/widgets/screen.js.map +0 -1
  399. package/dist/widgets/slider.d.ts.map +0 -1
  400. package/dist/widgets/slider.js.map +0 -1
  401. package/dist/widgets/static-item.d.ts.map +0 -1
  402. package/dist/widgets/static-item.js.map +0 -1
  403. package/dist/widgets/text-input.d.ts.map +0 -1
  404. package/dist/widgets/text-input.js.map +0 -1
  405. package/dist/widgets/toggle.d.ts.map +0 -1
  406. package/dist/widgets/toggle.js.map +0 -1
  407. package/dist/widgets/types.d.ts.map +0 -1
  408. package/dist/widgets/types.js.map +0 -1
  409. package/dist/widgets/widget-base.d.ts.map +0 -1
  410. package/dist/widgets/widget-base.js.map +0 -1
@@ -14,8 +14,9 @@
14
14
  * (`[bold]a[italic]b[/bold]c[/italic]`) because a style is an annotation and
15
15
  * annotations may overlap freely; a plugin tag is a replacement whose handler
16
16
  * takes one contiguous `inner`, so an overlapping pair has no slice to hand it
17
- * and is rejected with a `MarkupError`.
17
+ * and is rejected with a `MarkupSyntaxError`.
18
18
  */
19
+ import { cellLen } from "./cells.js";
19
20
  import { Style, StyleSyntaxError } from "./style.js";
20
21
  import { RichText, Span } from "./text.js";
21
22
  import { emojiReplace } from "./emoji.js";
@@ -45,6 +46,94 @@ export class MarkupError extends Error {
45
46
  this.name = "MarkupError";
46
47
  }
47
48
  }
49
+ // --- MarkupSyntaxError ---
50
+ /**
51
+ * A markup string that cannot be parsed, with where it went wrong.
52
+ *
53
+ * [LAW:types-are-the-program] A subclass rather than optional fields on
54
+ * `MarkupError`, because the parent is also what `MarkupRegistry.register`
55
+ * throws, and a registration has no source text to point into. Every error this
56
+ * class describes has a location, so none of its fields is optional and no
57
+ * caller has to ask whether one is present — `instanceof` is the only question.
58
+ *
59
+ * [LAW:one-source-of-truth] The constructor takes the facts the parser holds —
60
+ * the string, the offset of the offending tag, what was open — and derives
61
+ * `line`, `column` and the message from them, so the numbers a caller reads
62
+ * and the caret the message draws cannot disagree.
63
+ */
64
+ export class MarkupSyntaxError extends MarkupError {
65
+ /** The problem alone, with no location, for callers building their own message. */
66
+ reason;
67
+ /** The whole markup string that failed, as the caller passed it. */
68
+ markup;
69
+ /** Index into `markup` of the tag the parser rejected. */
70
+ offset;
71
+ /** 1-based line of `offset`; lines are separated by `\n`. */
72
+ line;
73
+ /** 1-based column of `offset` within its line, in UTF-16 code units. */
74
+ column;
75
+ /**
76
+ * The opening tags still open at `offset`, outermost first, as written. For
77
+ * overlapping plugin tags, only plugin tags are named.
78
+ */
79
+ openTags;
80
+ constructor(reason, markup, offset, openTags) {
81
+ const lineStart = markup.lastIndexOf("\n", offset - 1) + 1;
82
+ const lineEnd = markup.indexOf("\n", offset);
83
+ const text = markup.slice(lineStart, lineEnd === -1 ? markup.length : lineEnd);
84
+ const line = countNewlines(markup.slice(0, lineStart)) + 1;
85
+ const column = offset - lineStart + 1;
86
+ const open = openTags.length > 0 ? openTags.join(" ") : "none";
87
+ super(`${printable(reason)} (line ${line}, column ${column})\n` +
88
+ `${excerpt(text, offset - lineStart)}\n` +
89
+ `Open tags: ${printable(open)}`);
90
+ this.name = "MarkupSyntaxError";
91
+ this.reason = reason;
92
+ this.markup = markup;
93
+ this.offset = offset;
94
+ this.line = line;
95
+ this.column = column;
96
+ this.openTags = openTags;
97
+ }
98
+ }
99
+ function countNewlines(text) {
100
+ return text.split("\n").length - 1;
101
+ }
102
+ // Code points shown either side of the error before the excerpt is cut and
103
+ // marked with an ellipsis. Enough to recognise the surrounding tags on a line
104
+ // that is long, templated, or machine-assembled, without printing all of it.
105
+ const EXCERPT_CONTEXT = 30;
106
+ /**
107
+ * `text` with each C0 control and DEL shown as one space.
108
+ *
109
+ * [LAW:single-enforcer] Every part of the message that quotes the caller's
110
+ * markup passes through here: the reason and the open tags quote tag text, and
111
+ * the tag grammar admits control characters, so an `ESC ]` inside a tag would
112
+ * otherwise open an OSC sequence in the terminal printing the error. In the
113
+ * excerpt it also keeps the caret aligned, since a control occupies no cell.
114
+ * The fields keep the text as written; only the message is made printable.
115
+ */
116
+ function printable(text) {
117
+ return text.replace(/[\x00-\x1f\x7f]/g, " ");
118
+ }
119
+ /**
120
+ * Two lines: the source line around `index`, and a caret under it.
121
+ *
122
+ * The window is cut in code points, so a surrogate pair is never split, and the
123
+ * caret is placed by cell width, so a wide character before the error still
124
+ * leaves it under the right cell.
125
+ */
126
+ function excerpt(text, index) {
127
+ const chars = Array.from(printable(text));
128
+ const at = Array.from(text.slice(0, index)).length;
129
+ const from = Math.max(0, at - EXCERPT_CONTEXT);
130
+ const to = Math.min(chars.length, at + EXCERPT_CONTEXT);
131
+ const head = from > 0 ? "…" : "";
132
+ const tail = to < chars.length ? "…" : "";
133
+ const before = head + chars.slice(from, at).join("");
134
+ const shown = before + chars.slice(at, to).join("") + tail;
135
+ return ` ${shown}\n ${" ".repeat(cellLen(before))}^`;
136
+ }
48
137
  // --- escape ---
49
138
  /**
50
139
  * Escapes markup characters so they render as literal text.
@@ -106,6 +195,9 @@ function parseTags(markup) {
106
195
  }
107
196
  return tags;
108
197
  }
198
+ function wholeString(markup) {
199
+ return { source: markup, base: 0, enclosing: [] };
200
+ }
109
201
  /**
110
202
  * Parses the built-in style dialect — `[bold red]text[/bold red]` — into a
111
203
  * `RichText` with styled spans. Module-private on purpose: `renderMarkup` is
@@ -127,7 +219,7 @@ function parseTags(markup) {
127
219
  * function names; an empty registry puts it in the data, where the one
128
220
  * boundary admits both.
129
221
  */
130
- function render(markup, baseStyle, options) {
222
+ function render(markup, origin, baseStyle, options) {
131
223
  // Fast path: no brackets at all
132
224
  if (!HAS_TAG_RE.test(markup)) {
133
225
  let text = markup;
@@ -145,6 +237,10 @@ function render(markup, baseStyle, options) {
145
237
  let plainText = "";
146
238
  const spans = [];
147
239
  const openStack = [];
240
+ const unparsable = (reason, tag) => new MarkupSyntaxError(reason, origin.source, origin.base + tag.start, [
241
+ ...origin.enclosing,
242
+ ...openStack.map((opened) => opened.tag),
243
+ ]);
148
244
  let lastEnd = 0;
149
245
  for (const tag of tags) {
150
246
  // Add text before this tag
@@ -154,7 +250,9 @@ function render(markup, baseStyle, options) {
154
250
  if (tag.isImplicitClose) {
155
251
  // [/] — close the most recent open tag
156
252
  if (openStack.length === 0) {
157
- throw new MarkupError("Closing tag [/] has nothing to close");
253
+ // [/] never closes a plugin tag, so an enclosing plugin tag can be open
254
+ // here while no style tag is.
255
+ throw unparsable(`Closing tag ${tag.fullMatch} has no open style tag to close`, tag);
158
256
  }
159
257
  const opened = openStack.pop();
160
258
  spans.push(new Span(opened.textStart, plainText.length, openTagStyle(opened)));
@@ -163,7 +261,7 @@ function render(markup, baseStyle, options) {
163
261
  // [/style] — find and close matching open tag
164
262
  const idx = findLastOpen(openStack, tag.styleName);
165
263
  if (idx === -1) {
166
- throw new MarkupError(`Closing tag [/${tag.styleName}] doesn't match any open tag`);
264
+ throw unparsable(`Closing tag ${tag.fullMatch} doesn't match any open tag`, tag);
167
265
  }
168
266
  const opened = openStack[idx];
169
267
  spans.push(new Span(opened.textStart, plainText.length, openTagStyle(opened)));
@@ -172,6 +270,7 @@ function render(markup, baseStyle, options) {
172
270
  else {
173
271
  // Opening tag
174
272
  openStack.push({
273
+ tag: tag.fullMatch,
175
274
  styleName: tag.styleName,
176
275
  parameters: tag.parameters,
177
276
  textStart: plainText.length,
@@ -390,20 +489,23 @@ function parsePluginAttrs(after) {
390
489
  * append path is uniform.
391
490
  */
392
491
  export function renderMarkup(markup, options) {
492
+ return renderSlice(markup, wholeString(markup), options);
493
+ }
494
+ function renderSlice(markup, origin, options) {
393
495
  const registry = options?.registry ?? globalMarkupRegistry;
394
496
  const baseStyle = options?.baseStyle;
395
497
  const doEmoji = options?.emoji !== false;
396
498
  // Fast path: no `[` at all → no possible tags.
397
499
  if (!HAS_TAG_RE.test(markup)) {
398
- return render(markup, baseStyle, { emoji: doEmoji });
500
+ return render(markup, origin, baseStyle, { emoji: doEmoji });
399
501
  }
400
502
  const tags = parseTags(markup);
401
503
  // Pair each opening plugin tag with its matching closer up-front, so the
402
504
  // splice walk can just iterate top-level pairs in source order with no
403
505
  // nested-state book-keeping.
404
- const { annotated, topLevel: tagPairs } = pairPluginTags(tags, registry);
506
+ const { annotated, topLevel: tagPairs } = pairPluginTags(tags, registry, origin);
405
507
  if (tagPairs.size === 0) {
406
- return render(markup, baseStyle, { emoji: doEmoji });
508
+ return render(markup, origin, baseStyle, { emoji: doEmoji });
407
509
  }
408
510
  // [LAW:one-type-per-behavior] Always assemble a single RichText. Fragments
409
511
  // between plugin pairs are parsed by the built-in `render`, plugin pairs
@@ -415,22 +517,26 @@ export function renderMarkup(markup, options) {
415
517
  const open = annotated[openIdx];
416
518
  const close = annotated[closeIdx];
417
519
  if (open.start > cursor) {
418
- out.append(render(markup.slice(cursor, open.start), baseStyle, { emoji: doEmoji }));
520
+ out.append(render(markup.slice(cursor, open.start), offsetBy(origin, cursor), baseStyle, { emoji: doEmoji }));
419
521
  }
420
522
  const innerRaw = markup.slice(open.end, close.start);
421
- const innerRichText = renderMarkup(innerRaw, options);
523
+ const innerRichText = renderSlice(innerRaw, { ...offsetBy(origin, open.end), enclosing: [...origin.enclosing, open.fullMatch] }, options);
422
524
  const handler = registry.get(open.pluginName);
423
525
  out.append(handler({ attrs: open.attrs, children: innerRichText, raw: innerRaw }));
424
526
  cursor = close.end;
425
527
  }
426
528
  if (cursor < markup.length) {
427
- out.append(render(markup.slice(cursor), baseStyle, { emoji: doEmoji }));
529
+ out.append(render(markup.slice(cursor), offsetBy(origin, cursor), baseStyle, { emoji: doEmoji }));
428
530
  }
429
531
  if (baseStyle)
430
532
  out.stylize(baseStyle);
431
533
  return out;
432
534
  }
433
- function pairPluginTags(tags, registry) {
535
+ /** The origin of a sub-slice starting `offset` into the slice `origin` describes. */
536
+ function offsetBy(origin, offset) {
537
+ return { ...origin, base: origin.base + offset };
538
+ }
539
+ function pairPluginTags(tags, registry, origin) {
434
540
  // Annotate tags with plugin info, then pair openers with closers. Only
435
541
  // top-level pairs are returned; inner pairs will be re-discovered by the
436
542
  // recursive `renderMarkup` call on the inner slice.
@@ -477,11 +583,18 @@ function pairPluginTags(tags, registry) {
477
583
  // hand it. A style span may overlap because it annotates; a plugin pair
478
584
  // may not because it replaces.
479
585
  if (annotated[closeIdx].end > outerEnd) {
480
- const inner = annotated[openIdx].pluginName;
481
- const outer = annotated[outerOpenIdx].pluginName;
482
- throw new MarkupError(`Plugin tag [${inner}] overlaps [${outer}]: plugin tags must nest, ` +
586
+ const inner = annotated[openIdx];
587
+ const outer = annotated[outerOpenIdx];
588
+ // The caret goes under the outer pair's closing tag, the point where
589
+ // nesting breaks. This pass sees plugin tags only; of those, a pair is
590
+ // open there when it opens before that tag and closes at or after it.
591
+ const caret = annotated[pairs.get(outerOpenIdx)].start;
592
+ const openAtCaret = sortedOpens
593
+ .filter((o) => annotated[o].start < caret && annotated[pairs.get(o)].start >= caret)
594
+ .map((o) => annotated[o].fullMatch);
595
+ throw new MarkupSyntaxError(`Plugin tag [${inner.pluginName}] overlaps [${outer.pluginName}]: plugin tags must nest, ` +
483
596
  `because a handler receives one contiguous slice. ` +
484
- `Close [/${inner}] before [/${outer}].`);
597
+ `Close [/${inner.pluginName}] before [/${outer.pluginName}].`, origin.source, origin.base + caret, [...origin.enclosing, ...openAtCaret]);
485
598
  }
486
599
  continue;
487
600
  }
@@ -504,4 +617,3 @@ function annotatePluginTag(tag, registry) {
504
617
  const attrs = tag.isClosing ? {} : parsePluginAttrs(after);
505
618
  return { ...tag, pluginName: name, attrs };
506
619
  }
507
- //# sourceMappingURL=markup.js.map
@@ -25,4 +25,3 @@ export declare class Measurement {
25
25
  static get(options: RenderOptions, measurable: Measurable): Measurement;
26
26
  }
27
27
  export declare function measureRenderables(options: RenderOptions, measurables: Measurable[]): Measurement;
28
- //# sourceMappingURL=measure.d.ts.map
@@ -59,4 +59,3 @@ export function measureRenderables(options, measurables) {
59
59
  }
60
60
  return new Measurement(minOfAll, maxOfAll);
61
61
  }
62
- //# sourceMappingURL=measure.js.map
@@ -125,4 +125,3 @@ export declare class Oklch {
125
125
  */
126
126
  private findInGamutChroma;
127
127
  }
128
- //# sourceMappingURL=oklch.d.ts.map
@@ -262,4 +262,3 @@ export class Oklch {
262
262
  return lo;
263
263
  }
264
264
  }
265
- //# sourceMappingURL=oklch.js.map
@@ -0,0 +1,68 @@
1
+ /**
2
+ * The OSC 8 hyperlink wire grammar — which bytes may not appear inside a
3
+ * link, how a link becomes bytes, and how bytes are read back as a link.
4
+ *
5
+ * [LAW:one-source-of-truth] Every producer (`segmentsToString`,
6
+ * `Style.render`) opens a link through `osc8Open` and closes it with
7
+ * `OSC8_CLOSE`; every consumer that reads rendered bytes (a width measure, a
8
+ * test extracting URLs) matches them with `OSC8`; the data-model boundary
9
+ * (RichText) cleans URLs with `stripOscTerminators`. All four read the one
10
+ * terminator set below, so the bytes a sanitizer removes and the bytes a
11
+ * reader stops at cannot disagree.
12
+ *
13
+ * [LAW:locality-or-seam] This module depends on nothing; every consumer
14
+ * imports downward.
15
+ *
16
+ * The `id=` parameter is what makes one link hover as one link. A terminal
17
+ * treats cells as the same hyperlink when they share BOTH the URI and the id
18
+ * (the OSC 8 spec, and VTE / iTerm2 / kitty / WezTerm alike); without an id,
19
+ * each open sequence is its own link. The coalescer can only share one OSC 8
20
+ * pair across a run of identical SGR, so a link whose text changes style
21
+ * mid-span — a bold glyph beside plain text, a padded cell — is emitted as
22
+ * several pairs, and would highlight piecewise on hover.
23
+ *
24
+ * [LAW:types-are-the-program] The id is a pure function of the URI, so the
25
+ * byte stream stays a pure function of (style, text, colorSystem): no counter,
26
+ * no construction-order dependence. The consequence is deliberate: two spans
27
+ * with the same URI hover as one link wherever they sit on screen, adjacent
28
+ * or not — a click on either does the same thing, so they ARE one link.
29
+ * Because terminals key on the (id, URI) PAIR, a hash collision between two
30
+ * different URIs merges nothing — the URIs still differ — so a 32-bit hash is
31
+ * exact, not approximate.
32
+ */
33
+ /** Remove every byte that could break a URL out of its OSC 8 wrap. */
34
+ export declare function stripOscTerminators(url: string): string;
35
+ /**
36
+ * The bytes that open a hyperlink to `url`.
37
+ *
38
+ * [LAW:single-enforcer] Wire-byte trust boundary — the URL is sanitized at
39
+ * the one place it becomes an OSC 8 sequence, whichever upstream API attached
40
+ * it to the Style (RichText sanitizes at its data-model boundary too; a Style
41
+ * built directly with `new Style({ link })` reaches here unsanitized).
42
+ */
43
+ export declare function osc8Open(url: string): string;
44
+ /** The bytes that close the current hyperlink. */
45
+ export declare const OSC8_CLOSE = "\u001B]8;;\u001B\\";
46
+ /**
47
+ * Matches one OSC 8 sequence — an open or a close — terminated by `ESC \`,
48
+ * BEL, or the 8-bit ST. Group 1 is the params, group 2 the URI; both are empty
49
+ * on a close. To read bytes back use `osc8Sequences`; this pattern is exported
50
+ * for composing into a larger one (a width measure's zero-width alternation).
51
+ */
52
+ export declare const OSC8: RegExp;
53
+ /** One OSC 8 sequence found in rendered bytes. A close has `uri === ""`. */
54
+ export interface Osc8Sequence {
55
+ /** Offset of the sequence in the scanned string. */
56
+ readonly index: number;
57
+ /** Length of the whole sequence, terminator included. */
58
+ readonly length: number;
59
+ /** The params field (`id=…`); empty on a close. */
60
+ readonly params: string;
61
+ /** The URI; empty on a close. */
62
+ readonly uri: string;
63
+ }
64
+ /**
65
+ * Every OSC 8 sequence in `text`, in order — the typed way to read rendered
66
+ * bytes back.
67
+ */
68
+ export declare function osc8Sequences(text: string): Osc8Sequence[];
@@ -0,0 +1,90 @@
1
+ /**
2
+ * The OSC 8 hyperlink wire grammar — which bytes may not appear inside a
3
+ * link, how a link becomes bytes, and how bytes are read back as a link.
4
+ *
5
+ * [LAW:one-source-of-truth] Every producer (`segmentsToString`,
6
+ * `Style.render`) opens a link through `osc8Open` and closes it with
7
+ * `OSC8_CLOSE`; every consumer that reads rendered bytes (a width measure, a
8
+ * test extracting URLs) matches them with `OSC8`; the data-model boundary
9
+ * (RichText) cleans URLs with `stripOscTerminators`. All four read the one
10
+ * terminator set below, so the bytes a sanitizer removes and the bytes a
11
+ * reader stops at cannot disagree.
12
+ *
13
+ * [LAW:locality-or-seam] This module depends on nothing; every consumer
14
+ * imports downward.
15
+ *
16
+ * The `id=` parameter is what makes one link hover as one link. A terminal
17
+ * treats cells as the same hyperlink when they share BOTH the URI and the id
18
+ * (the OSC 8 spec, and VTE / iTerm2 / kitty / WezTerm alike); without an id,
19
+ * each open sequence is its own link. The coalescer can only share one OSC 8
20
+ * pair across a run of identical SGR, so a link whose text changes style
21
+ * mid-span — a bold glyph beside plain text, a padded cell — is emitted as
22
+ * several pairs, and would highlight piecewise on hover.
23
+ *
24
+ * [LAW:types-are-the-program] The id is a pure function of the URI, so the
25
+ * byte stream stays a pure function of (style, text, colorSystem): no counter,
26
+ * no construction-order dependence. The consequence is deliberate: two spans
27
+ * with the same URI hover as one link wherever they sit on screen, adjacent
28
+ * or not — a click on either does the same thing, so they ARE one link.
29
+ * Because terminals key on the (id, URI) PAIR, a hash collision between two
30
+ * different URIs merges nothing — the URIs still differ — so a 32-bit hash is
31
+ * exact, not approximate.
32
+ */
33
+ /**
34
+ * The bytes that prematurely terminate an OSC 8 sequence: ESC (`\x1b`, which
35
+ * begins ST `ESC \`), BEL (`\x07`), and the 8-bit ST (`\x9c`).
36
+ */
37
+ const TERMINATORS = "\\x1b\\x07\\x9c";
38
+ const TERMINATOR_RE = new RegExp(`[${TERMINATORS}]`, "g");
39
+ /** Remove every byte that could break a URL out of its OSC 8 wrap. */
40
+ export function stripOscTerminators(url) {
41
+ return url.replace(TERMINATOR_RE, "");
42
+ }
43
+ const utf8 = new TextEncoder();
44
+ /**
45
+ * FNV-1a, 32-bit, over the URL's UTF-8 bytes, as 8 lowercase hex digits — a
46
+ * legal `id=` value (no `:`/`;`). UTF-8 rather than UTF-16 code units, so any
47
+ * other runtime computing standard FNV-1a over the same URL gets the same id.
48
+ */
49
+ function linkId(url) {
50
+ let h = 0x811c9dc5;
51
+ for (const byte of utf8.encode(url)) {
52
+ h ^= byte;
53
+ h = Math.imul(h, 0x01000193);
54
+ }
55
+ return (h >>> 0).toString(16).padStart(8, "0");
56
+ }
57
+ /**
58
+ * The bytes that open a hyperlink to `url`.
59
+ *
60
+ * [LAW:single-enforcer] Wire-byte trust boundary — the URL is sanitized at
61
+ * the one place it becomes an OSC 8 sequence, whichever upstream API attached
62
+ * it to the Style (RichText sanitizes at its data-model boundary too; a Style
63
+ * built directly with `new Style({ link })` reaches here unsanitized).
64
+ */
65
+ export function osc8Open(url) {
66
+ const clean = stripOscTerminators(url);
67
+ return `\x1b]8;id=${linkId(clean)};${clean}\x1b\\`;
68
+ }
69
+ /** The bytes that close the current hyperlink. */
70
+ export const OSC8_CLOSE = "\x1b]8;;\x1b\\";
71
+ /**
72
+ * Matches one OSC 8 sequence — an open or a close — terminated by `ESC \`,
73
+ * BEL, or the 8-bit ST. Group 1 is the params, group 2 the URI; both are empty
74
+ * on a close. To read bytes back use `osc8Sequences`; this pattern is exported
75
+ * for composing into a larger one (a width measure's zero-width alternation).
76
+ */
77
+ export const OSC8 = new RegExp(`\\x1b\\]8;([^;${TERMINATORS}]*);([^${TERMINATORS}]*)(?:\\x1b\\\\|\\x07|\\x9c)`);
78
+ const OSC8_SCAN = new RegExp(OSC8.source, "g");
79
+ /**
80
+ * Every OSC 8 sequence in `text`, in order — the typed way to read rendered
81
+ * bytes back.
82
+ */
83
+ export function osc8Sequences(text) {
84
+ return [...text.matchAll(OSC8_SCAN)].map((m) => ({
85
+ index: m.index,
86
+ length: m[0].length,
87
+ params: m[1],
88
+ uri: m[2],
89
+ }));
90
+ }
@@ -97,7 +97,27 @@ export declare class Pretty implements Renderable, Measurable {
97
97
  private _format;
98
98
  /** The arms for a non-null object, with `value` already on the open path. */
99
99
  private _formatObject;
100
- /** One position, with every value in it laid out. */
100
+ /**
101
+ * One position, with every value in it laid out.
102
+ *
103
+ * `at.column` is where `slot.head` begins. Rather than hand-tracking a
104
+ * second, parallel "column so far" alongside `out` — two values a future
105
+ * edit could update out of step, silently reintroducing a stale-budget bug
106
+ * of the same shape this file just fixed — each hole derives its own
107
+ * column fresh from `out` via `lineColumn`, the one place "text just
108
+ * emitted" becomes "column now". [LAW:one-source-of-truth] `out` is
109
+ * already the complete record; `column` is a read of it, not a second copy
110
+ * of the same fact. `_joinOneLine`'s probe sibling tracks the analogous
111
+ * thing via `budget` shrinking instead, because a probe already discards
112
+ * anything that overruns and so never needs to know where a multi-line
113
+ * insert's last line ends.
114
+ *
115
+ * Each hole's `reserve` is its own `tail` plus, only for the slot's last
116
+ * hole, whatever `at.reserve` already asked this whole slot to leave room
117
+ * for (`_formatObject`'s trailing `,`). An earlier hole's tail is never
118
+ * folded into a later hole's reserve — it is spent, not carried, the moment
119
+ * `out` grows past it.
120
+ */
101
121
  private _expandSlot;
102
122
  /** The one-line form of a value, or `null` when it will not fit `at.budget`. */
103
123
  private _oneLine;
@@ -119,4 +139,3 @@ export declare class Pretty implements Renderable, Measurable {
119
139
  private _slotOneLine;
120
140
  private _addIndentGuides;
121
141
  }
122
- //# sourceMappingURL=pretty.d.ts.map
@@ -20,7 +20,28 @@ import { cellLen } from "./cells.js";
20
20
  import { RichText } from "./text.js";
21
21
  import { ReprHighlighter } from "./highlighter.js";
22
22
  const reprHighlighter = new ReprHighlighter();
23
- const rootFrame = (maxWidth) => ({ inset: 0, level: 0, maxWidth, open: new WeakSet() });
23
+ const rootFrame = (maxWidth) => ({
24
+ inset: 0,
25
+ level: 0,
26
+ maxWidth,
27
+ column: 0,
28
+ reserve: 0,
29
+ open: new WeakSet(),
30
+ });
31
+ /**
32
+ * The column reached after `text` is appended to a line currently at `column`.
33
+ *
34
+ * A hole's formatted value can itself span multiple lines — a nested
35
+ * container that failed its own compact try. What comes after it (a Map
36
+ * entry's tail, the next hole) sits after `text`'s *last* line, not at
37
+ * `column + cellLen(text)`, so a multi-line insert resets the column to that
38
+ * last line's width instead of accumulating across lines that were never on
39
+ * the same row.
40
+ */
41
+ function lineColumn(column, text) {
42
+ const lastNewline = text.lastIndexOf("\n");
43
+ return lastNewline === -1 ? column + cellLen(text) : cellLen(text.slice(lastNewline + 1));
44
+ }
24
45
  /**
25
46
  * The text, when it is a single line no wider than `budget` — otherwise `null`.
26
47
  *
@@ -111,6 +132,15 @@ function describesItself(value) {
111
132
  }
112
133
  /** What joins a container's positions on one line. Its width is charged for, so it is named once. */
113
134
  const SEPARATOR = ", ";
135
+ /**
136
+ * What joins a container's positions across lines, once it has expanded —
137
+ * `SEPARATOR` without the trailing space a newline already provides.
138
+ * rich-pretty-xms: named, and derived rather than a second hand-typed
139
+ * literal, so the width it costs a non-last slot's own compact try —
140
+ * reserved via `Frame.reserve` — cannot drift from what `_formatObject`
141
+ * actually joins with.
142
+ */
143
+ const EXPAND_SEPARATOR = SEPARATOR.trimEnd();
114
144
  /** The marker for positions the bound dropped, or nothing when it dropped none. */
115
145
  const elided = (dropped) => dropped > 0 ? [{ head: `... +${dropped}`, holes: [] }] : [];
116
146
  export class Pretty {
@@ -165,11 +195,15 @@ export class Pretty {
165
195
  return "undefined";
166
196
  switch (typeof value) {
167
197
  case "string": {
168
- let str = value;
169
- if (this.maxString !== undefined && str.length > this.maxString) {
170
- str = str.slice(0, this.maxString) + `+${value.length - this.maxString}`;
198
+ // The dropped count is an annotation about the value, not part of it,
199
+ // so it goes after the closing quote — the way `maxLength`'s `... +N`
200
+ // sits beside the kept entries rather than inside the last one. Inside
201
+ // the quotes it reads as content, and copying it out yields a string
202
+ // the program never held.
203
+ if (this.maxString === undefined || value.length <= this.maxString) {
204
+ return JSON.stringify(value);
171
205
  }
172
- return JSON.stringify(str);
206
+ return JSON.stringify(value.slice(0, this.maxString)) + `+${value.length - this.maxString}`;
173
207
  }
174
208
  case "number":
175
209
  case "bigint":
@@ -281,35 +315,76 @@ export class Pretty {
281
315
  const shape = this._shape(value, at.level, Infinity);
282
316
  if (shape.kind === "text")
283
317
  return shape.text;
284
- const indentStr = " ".repeat(this.indent * at.inset);
285
318
  if (!this.expandAll) {
319
+ // rich-pretty-xms: budget from `at.column` and `at.reserve`, not from
320
+ // `at.inset` alone. `at.column` is where *this* value's text actually
321
+ // starts on its line, which already includes whatever `_expandSlot`
322
+ // prepended for us (a key and its separator, a Map entry's `" => "`,
323
+ // ...); `at.reserve` is what a fixed piece of literal text — the rest
324
+ // of this hole's own tail, or the trailing `,` a non-last slot gets
325
+ // joined with — is known to cost right after, before this value's own
326
+ // text even starts. Deriving the budget from the indent alone assumed
327
+ // nothing precedes *or follows* the value on its line, which is only
328
+ // true at the root and for the last hole of an array's last element.
286
329
  const compact = this._joinOneLine(shape, {
287
330
  level: at.level + 1,
288
- budget: at.maxWidth - cellLen(indentStr),
331
+ budget: at.maxWidth - at.column - at.reserve,
289
332
  open: at.open,
290
333
  });
291
334
  if (compact !== null)
292
335
  return compact;
293
336
  }
337
+ const indentStr = " ".repeat(this.indent * at.inset);
294
338
  const innerIndent = " ".repeat(this.indent * (at.inset + 1));
295
- const deeper = { inset: at.inset + 1, level: at.level + 1, maxWidth: at.maxWidth, open: at.open };
296
- const parts = shape.slots.map((slot) => innerIndent + this._expandSlot(slot, deeper));
297
- return shape.open + "\n" + parts.join(",\n") + "\n" + indentStr + shape.close;
339
+ // Every non-last slot gets `EXPAND_SEPARATOR` appended right after it
340
+ // below, on the same line as whatever its own last character was — the
341
+ // last slot doesn't. Two frames, not one per slot: `reserve` is the only
342
+ // field that varies, and it only ever takes these two values.
343
+ const base = { inset: at.inset + 1, level: at.level + 1, maxWidth: at.maxWidth, column: cellLen(innerIndent), open: at.open };
344
+ const midFrame = { ...base, reserve: cellLen(EXPAND_SEPARATOR) };
345
+ const lastFrame = { ...base, reserve: 0 };
346
+ const lastSlot = shape.slots.length - 1;
347
+ const parts = shape.slots.map((slot, i) => innerIndent + this._expandSlot(slot, i === lastSlot ? lastFrame : midFrame));
348
+ return shape.open + "\n" + parts.join(EXPAND_SEPARATOR + "\n") + "\n" + indentStr + shape.close;
298
349
  }
299
- /** One position, with every value in it laid out. */
350
+ /**
351
+ * One position, with every value in it laid out.
352
+ *
353
+ * `at.column` is where `slot.head` begins. Rather than hand-tracking a
354
+ * second, parallel "column so far" alongside `out` — two values a future
355
+ * edit could update out of step, silently reintroducing a stale-budget bug
356
+ * of the same shape this file just fixed — each hole derives its own
357
+ * column fresh from `out` via `lineColumn`, the one place "text just
358
+ * emitted" becomes "column now". [LAW:one-source-of-truth] `out` is
359
+ * already the complete record; `column` is a read of it, not a second copy
360
+ * of the same fact. `_joinOneLine`'s probe sibling tracks the analogous
361
+ * thing via `budget` shrinking instead, because a probe already discards
362
+ * anything that overruns and so never needs to know where a multi-line
363
+ * insert's last line ends.
364
+ *
365
+ * Each hole's `reserve` is its own `tail` plus, only for the slot's last
366
+ * hole, whatever `at.reserve` already asked this whole slot to leave room
367
+ * for (`_formatObject`'s trailing `,`). An earlier hole's tail is never
368
+ * folded into a later hole's reserve — it is spent, not carried, the moment
369
+ * `out` grows past it.
370
+ */
300
371
  _expandSlot(slot, at) {
301
372
  let out = slot.head;
302
- for (const hole of slot.holes) {
373
+ const lastHole = slot.holes.length - 1;
374
+ for (let i = 0; i < slot.holes.length; i++) {
375
+ const hole = slot.holes[i];
303
376
  // This is the read that costs the least when it fails: neighbours are
304
377
  // unaffected, so `{ a: 1, b: [Threw: …], c: 3 }` still shows everything
305
378
  // that could be read.
379
+ let text;
306
380
  try {
307
- out += this._format(hole.read(), at);
381
+ const reserve = cellLen(hole.tail) + (i === lastHole ? at.reserve : 0);
382
+ text = this._format(hole.read(), { ...at, column: lineColumn(at.column, out), reserve });
308
383
  }
309
384
  catch (error) {
310
- out += threw(error);
385
+ text = threw(error);
311
386
  }
312
- out += hole.tail;
387
+ out += text + hole.tail;
313
388
  }
314
389
  return out;
315
390
  }
@@ -395,4 +470,3 @@ export class Pretty {
395
470
  }
396
471
  }
397
472
  }
398
- //# sourceMappingURL=pretty.js.map
@@ -3,7 +3,7 @@
3
3
  * [LAW:one-source-of-truth] These interfaces are the single authority for the rendering contract.
4
4
  */
5
5
  import type { Segment } from "./segment.js";
6
- import { type Style, type Theme } from "./style.js";
6
+ import { type Style, type StyleSyntaxError, type Theme } from "./style.js";
7
7
  export interface RenderOptions {
8
8
  /**
9
9
  * The cells this renderable may occupy. A count of cells, so a non-negative
@@ -30,7 +30,24 @@ export interface RenderOptions {
30
30
  * built-in defaults apply. Read it through `getStyle` rather than directly.
31
31
  */
32
32
  theme?: Theme;
33
+ /**
34
+ * Called each time a render degrades a style string it cannot parse to
35
+ * unstyled, once per resolution — a style the render reads twice is reported
36
+ * twice. Absent, the failure passes silently. A handler that throws makes
37
+ * the render strict: the error leaves `render`.
38
+ */
39
+ onStyleError?: StyleErrorHandler;
33
40
  }
41
+ /**
42
+ * Receives a style error a render would otherwise absorb: the parse failure,
43
+ * and the whole style string it came from — the error names only the token
44
+ * that failed, and `"bold rd"` loses its `bold` too.
45
+ *
46
+ * [LAW:no-mode-explosion] Strict mode is not a second option beside this one.
47
+ * It is a handler that throws, so there is no "strict and a callback" state
48
+ * whose order anyone has to define.
49
+ */
50
+ export type StyleErrorHandler = (error: StyleSyntaxError, style: string) => void;
34
51
  /**
35
52
  * The style a `string | Style` stands for in this render.
36
53
  *
@@ -109,4 +126,3 @@ export interface Measurable {
109
126
  }
110
127
  export declare function isRenderable(obj: unknown): obj is Renderable;
111
128
  export declare function isMeasurable(obj: unknown): obj is Measurable;
112
- //# sourceMappingURL=protocol.d.ts.map