@zaly/tui 0.1.0 → 0.1.1

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 (245) hide show
  1. package/dist/{actions-DEIKF2ts.mjs → actions-AY6avfEV.mjs} +2 -3
  2. package/dist/{actions-DEIKF2ts.mjs.map → actions-AY6avfEV.mjs.map} +1 -1
  3. package/dist/{actions--ODlgCQ5.d.mts → actions-B0yyZ-Ww.d.mts} +4 -6
  4. package/dist/actions-B0yyZ-Ww.d.mts.map +1 -0
  5. package/dist/{ansi-CMv71xVr.mjs → ansi-CxgRPLA2.mjs} +3 -4
  6. package/dist/{ansi-CMv71xVr.mjs.map → ansi-CxgRPLA2.mjs.map} +1 -1
  7. package/dist/ansi.d.mts +1 -3
  8. package/dist/ansi.d.mts.map +1 -1
  9. package/dist/ansi.mjs +1 -1
  10. package/dist/{autocomplete-CD30kSe_.d.mts → autocomplete-wgceuhLr.d.mts} +5 -7
  11. package/dist/autocomplete-wgceuhLr.d.mts.map +1 -0
  12. package/dist/{box-C_N_7ChP.mjs → box-DToDoXlU.mjs} +8 -10
  13. package/dist/{box-C_N_7ChP.mjs.map → box-DToDoXlU.mjs.map} +1 -1
  14. package/dist/{builder-Chwtk0nQ.mjs → builder-BdYlK1Ew.mjs} +5 -6
  15. package/dist/{builder-Chwtk0nQ.mjs.map → builder-BdYlK1Ew.mjs.map} +1 -1
  16. package/dist/{callbacks-DLecWn7G.mjs → callbacks-DW8XKYKy.mjs} +8 -12
  17. package/dist/{callbacks-DLecWn7G.mjs.map → callbacks-DW8XKYKy.mjs.map} +1 -1
  18. package/dist/{client-D7qz4TZt.mjs → client-BWm6dNZF.mjs} +4 -5
  19. package/dist/{client-D7qz4TZt.mjs.map → client-BWm6dNZF.mjs.map} +1 -1
  20. package/dist/client-C69qWhMG.mjs +1 -0
  21. package/dist/{clipboard-CNPBTX7j.mjs → clipboard-UEoX9sup.mjs} +2 -3
  22. package/dist/{clipboard-CNPBTX7j.mjs.map → clipboard-UEoX9sup.mjs.map} +1 -1
  23. package/dist/clipboard.d.mts +0 -2
  24. package/dist/clipboard.d.mts.map +1 -1
  25. package/dist/clipboard.mjs +1 -1
  26. package/dist/{color-DBMQLv4Y.mjs → color-Cr1VoL49.mjs} +2 -3
  27. package/dist/{color-DBMQLv4Y.mjs.map → color-Cr1VoL49.mjs.map} +1 -1
  28. package/dist/ctx-CYEzzH-7.mjs +1 -0
  29. package/dist/{ctx-BaQ2hP5u.mjs → ctx-DBeFcZt6.mjs} +4 -5
  30. package/dist/{ctx-BaQ2hP5u.mjs.map → ctx-DBeFcZt6.mjs.map} +1 -1
  31. package/dist/{divider-BlNNN2OS.mjs → divider-qEj5prKE.mjs} +3 -4
  32. package/dist/{divider-BlNNN2OS.mjs.map → divider-qEj5prKE.mjs.map} +1 -1
  33. package/dist/{files-BvThQpOf.mjs → files-C3ZnFpvq.mjs} +3 -4
  34. package/dist/{files-BvThQpOf.mjs.map → files-C3ZnFpvq.mjs.map} +1 -1
  35. package/dist/{files-B_5HDBPp.d.mts → files-CdiDnwA_.d.mts} +3 -5
  36. package/dist/files-CdiDnwA_.d.mts.map +1 -0
  37. package/dist/{flex-Bguo-fUe.mjs → flex-CYp4etqn.mjs} +3 -4
  38. package/dist/{flex-Bguo-fUe.mjs.map → flex-CYp4etqn.mjs.map} +1 -1
  39. package/dist/{github-Cy2hfWJ7.mjs → github-CLveT9mq.mjs} +2 -3
  40. package/dist/{github-Cy2hfWJ7.mjs.map → github-CLveT9mq.mjs.map} +1 -1
  41. package/dist/{github-CyRwUQ3y.d.mts → github-DC0XemKa.d.mts} +3 -5
  42. package/dist/github-DC0XemKa.d.mts.map +1 -0
  43. package/dist/{image-BLls2F4t.d.mts → image-DdRDv7q6.d.mts} +2 -4
  44. package/dist/image-DdRDv7q6.d.mts.map +1 -0
  45. package/dist/{image-Cqtj3yRA.mjs → image-M--Bw4bs.mjs} +6 -7
  46. package/dist/{image-Cqtj3yRA.mjs.map → image-M--Bw4bs.mjs.map} +1 -1
  47. package/dist/image.d.mts +1 -3
  48. package/dist/image.d.mts.map +1 -1
  49. package/dist/image.mjs +1 -1
  50. package/dist/index.d.mts +3 -15
  51. package/dist/index.d.mts.map +1 -1
  52. package/dist/index.mjs +11 -14
  53. package/dist/index.mjs.map +1 -1
  54. package/dist/{input-ET2YEMcl.mjs → input-BkE4EpBw.mjs} +6 -7
  55. package/dist/{input-ET2YEMcl.mjs.map → input-BkE4EpBw.mjs.map} +1 -1
  56. package/dist/{input-NuD8TH8N.d.mts → input-C3kfYoEJ.d.mts} +3 -5
  57. package/dist/input-C3kfYoEJ.d.mts.map +1 -0
  58. package/dist/{inspect-Cxzs-W0g.mjs → inspect-Cli1beu8.mjs} +3 -4
  59. package/dist/{inspect-Cxzs-W0g.mjs.map → inspect-Cli1beu8.mjs.map} +1 -1
  60. package/dist/{inspect-B1uXxdKC.d.mts → inspect-hSl3U2BD.d.mts} +2 -6
  61. package/dist/inspect-hSl3U2BD.d.mts.map +1 -0
  62. package/dist/kitty-0sJ8axU_.mjs +1 -0
  63. package/dist/{kitty-JbCJC0iQ.mjs → kitty-BH_eK6lz.mjs} +2 -3
  64. package/dist/{kitty-JbCJC0iQ.mjs.map → kitty-BH_eK6lz.mjs.map} +1 -1
  65. package/dist/{log-B4djzy2m.mjs → log-96b1_J6G.mjs} +6 -7
  66. package/dist/{log-B4djzy2m.mjs.map → log-96b1_J6G.mjs.map} +1 -1
  67. package/dist/{logger-CvJCITzP.mjs → logger-DwfXp8LF.mjs} +5 -6
  68. package/dist/{logger-CvJCITzP.mjs.map → logger-DwfXp8LF.mjs.map} +1 -1
  69. package/dist/{markdown-D3ZtRn6C.mjs → markdown-CgRZBhIX.mjs} +11 -12
  70. package/dist/{markdown-D3ZtRn6C.mjs.map → markdown-CgRZBhIX.mjs.map} +1 -1
  71. package/dist/markdown.d.mts +2 -4
  72. package/dist/markdown.d.mts.map +1 -1
  73. package/dist/markdown.mjs +1 -1
  74. package/dist/math-Dk8hxbmz.mjs +1 -0
  75. package/dist/{math-Dd7a1irv.mjs → math-VPuwFCid.mjs} +1 -2
  76. package/dist/{math-Dd7a1irv.mjs.map → math-VPuwFCid.mjs.map} +1 -1
  77. package/dist/md.bun.d.mts +1 -3
  78. package/dist/md.bun.d.mts.map +1 -1
  79. package/dist/md.bun.mjs +2 -3
  80. package/dist/md.node.d.mts +1 -3
  81. package/dist/md.node.d.mts.map +1 -1
  82. package/dist/md.node.mjs +2 -3
  83. package/dist/{node-BZ7f6HzU.mjs → node-CXNobGaP.mjs} +3 -4
  84. package/dist/{node-BZ7f6HzU.mjs.map → node-CXNobGaP.mjs.map} +1 -1
  85. package/dist/{oklch-Bsh5swbC.mjs → oklch-Boh4YHEc.mjs} +3 -4
  86. package/dist/{oklch-Bsh5swbC.mjs.map → oklch-Boh4YHEc.mjs.map} +1 -1
  87. package/dist/{overlay-LvOjG7kn.mjs → overlay-CevDWgSF.mjs} +3 -4
  88. package/dist/{overlay-LvOjG7kn.mjs.map → overlay-CevDWgSF.mjs.map} +1 -1
  89. package/dist/{picker-f6xjgACX.mjs → picker-D9WrE3zF.mjs} +11 -16
  90. package/dist/{picker-f6xjgACX.mjs.map → picker-D9WrE3zF.mjs.map} +1 -1
  91. package/dist/{picker-B-OxKKnD.d.mts → picker-DsB6--GN.d.mts} +6 -8
  92. package/dist/picker-DsB6--GN.d.mts.map +1 -0
  93. package/dist/{queries-BUkBxkuM.mjs → queries-BIniKnLL.mjs} +3 -5
  94. package/dist/{queries-BUkBxkuM.mjs.map → queries-BIniKnLL.mjs.map} +1 -1
  95. package/dist/{queries-C75-wG1B.d.mts → queries-Dmd3IjSd.d.mts} +3 -51
  96. package/dist/queries-Dmd3IjSd.d.mts.map +1 -0
  97. package/dist/{reactive-f7NpAi69.mjs → reactive-DkP75TPk.mjs} +2 -3
  98. package/dist/{reactive-f7NpAi69.mjs.map → reactive-DkP75TPk.mjs.map} +1 -1
  99. package/dist/{registry-DUWpSPoo.mjs → registry-CQU0VwpM.mjs} +9 -14
  100. package/dist/{registry-DUWpSPoo.mjs.map → registry-CQU0VwpM.mjs.map} +1 -1
  101. package/dist/{render-B7LfNM5K.mjs → render-Bp2N2i6J.mjs} +4 -5
  102. package/dist/{render-B7LfNM5K.mjs.map → render-Bp2N2i6J.mjs.map} +1 -1
  103. package/dist/{renderer-BLsd-wrG.mjs → renderer-B9sAMA10.mjs} +25 -38
  104. package/dist/{renderer-BLsd-wrG.mjs.map → renderer-B9sAMA10.mjs.map} +1 -1
  105. package/dist/{renderer-aAtkFgll.mjs → renderer-BYl2pbdx.mjs} +3 -4
  106. package/dist/{renderer-aAtkFgll.mjs.map → renderer-BYl2pbdx.mjs.map} +1 -1
  107. package/dist/renderer-Ctz0hv5o.mjs +1 -0
  108. package/dist/{search-FvGnswRl.d.mts → search-DsHzamic.d.mts} +1 -9
  109. package/dist/search-DsHzamic.d.mts.map +1 -0
  110. package/dist/{select-BKE5LzdN.d.mts → select-BZCFXdjp.d.mts} +3 -5
  111. package/dist/select-BZCFXdjp.d.mts.map +1 -0
  112. package/dist/{select-m7NE6Rwa.mjs → select-CUmAFKJh.mjs} +5 -6
  113. package/dist/{select-m7NE6Rwa.mjs.map → select-CUmAFKJh.mjs.map} +1 -1
  114. package/dist/services/logger.d.mts +1 -1
  115. package/dist/services/logger.mjs +1 -1
  116. package/dist/services/notifier.d.mts +1 -3
  117. package/dist/services/notifier.d.mts.map +1 -1
  118. package/dist/services/notifier.mjs +3 -4
  119. package/dist/services/picker.d.mts +5 -7
  120. package/dist/services/picker.d.mts.map +1 -1
  121. package/dist/services/picker.mjs +8 -9
  122. package/dist/services/prompt.d.mts +4 -6
  123. package/dist/services/prompt.d.mts.map +1 -1
  124. package/dist/services/prompt.mjs +7 -8
  125. package/dist/shiki/server.mjs +6 -8
  126. package/dist/shiki/server.mjs.map +1 -1
  127. package/dist/{shiki-CJauRusM.mjs → shiki-BAf9PdIc.mjs} +4 -5
  128. package/dist/{shiki-CJauRusM.mjs.map → shiki-BAf9PdIc.mjs.map} +1 -1
  129. package/dist/{shiki-B4U0s8jT.mjs → shiki-DUUAIE6f.mjs} +4 -7
  130. package/dist/{shiki-B4U0s8jT.mjs.map → shiki-DUUAIE6f.mjs.map} +1 -1
  131. package/dist/{show-DR2x1YFc.mjs → show-D5XNXgZf.mjs} +4 -5
  132. package/dist/{show-DR2x1YFc.mjs.map → show-D5XNXgZf.mjs.map} +1 -1
  133. package/dist/{size-D8JogYLy.mjs → size-BHRJuRk9.mjs} +2 -3
  134. package/dist/{size-D8JogYLy.mjs.map → size-BHRJuRk9.mjs.map} +1 -1
  135. package/dist/{style-DslRHzI4.mjs → style-Cw_EuszU.mjs} +5 -6
  136. package/dist/{style-DslRHzI4.mjs.map → style-Cw_EuszU.mjs.map} +1 -1
  137. package/dist/style.d.mts +2 -4
  138. package/dist/style.d.mts.map +1 -1
  139. package/dist/style.mjs +3 -3
  140. package/dist/{text-BoJdKXLU.mjs → text-4arC3odI.mjs} +3 -4
  141. package/dist/{text-BoJdKXLU.mjs.map → text-4arC3odI.mjs.map} +1 -1
  142. package/dist/{text-DPrhma4x.mjs → text-BArwVVkg.mjs} +5 -6
  143. package/dist/{text-DPrhma4x.mjs.map → text-BArwVVkg.mjs.map} +1 -1
  144. package/dist/{text-CmxbA8lk.d.mts → text-DdkJXgoC.d.mts} +3 -5
  145. package/dist/text-DdkJXgoC.d.mts.map +1 -0
  146. package/dist/text.d.mts +1 -1
  147. package/dist/text.mjs +1 -1
  148. package/dist/{theme-C8LkcoaK.mjs → theme-B8hb1lPU.mjs} +3 -4
  149. package/dist/{theme-C8LkcoaK.mjs.map → theme-B8hb1lPU.mjs.map} +1 -1
  150. package/dist/{theme-DVX8dvGG.mjs → theme-BeSaXGNY.mjs} +3 -5
  151. package/dist/{theme-DVX8dvGG.mjs.map → theme-BeSaXGNY.mjs.map} +1 -1
  152. package/dist/themes.d.mts +1 -7
  153. package/dist/themes.d.mts.map +1 -1
  154. package/dist/themes.mjs +1 -1
  155. package/dist/{tree-CTvSpF9b.d.mts → tree-Berzv8Rz.d.mts} +3 -5
  156. package/dist/tree-Berzv8Rz.d.mts.map +1 -0
  157. package/dist/{tree-Bo1afYhe.mjs → tree-D6NvMQx8.mjs} +5 -6
  158. package/dist/{tree-Bo1afYhe.mjs.map → tree-D6NvMQx8.mjs.map} +1 -1
  159. package/dist/{types-C9ceMsbJ.d.mts → types-ByMHEdCF.d.mts} +1 -7
  160. package/dist/types-ByMHEdCF.d.mts.map +1 -0
  161. package/dist/{types-D3jk1rtk.d.mts → types-CUVUJPz6.d.mts} +1 -3
  162. package/dist/types-CUVUJPz6.d.mts.map +1 -0
  163. package/dist/{utils-3M4D_AXZ.mjs → utils-DHXAegVW.mjs} +1 -2
  164. package/dist/{utils-3M4D_AXZ.mjs.map → utils-DHXAegVW.mjs.map} +1 -1
  165. package/dist/{widget-BkMwhY2p.mjs → widget-sFveyrZE.mjs} +3 -4
  166. package/dist/{widget-BkMwhY2p.mjs.map → widget-sFveyrZE.mjs.map} +1 -1
  167. package/dist/{widget-DlF32PRp.d.mts → widget-sPJaKVU2.d.mts} +2 -4
  168. package/dist/widget-sPJaKVU2.d.mts.map +1 -0
  169. package/dist/widgets/autocomplete.d.mts +1 -1
  170. package/dist/widgets/autocomplete.mjs +5 -6
  171. package/dist/widgets/box.d.mts +1 -1
  172. package/dist/widgets/box.mjs +1 -1
  173. package/dist/widgets/code.d.mts +3 -5
  174. package/dist/widgets/code.d.mts.map +1 -1
  175. package/dist/widgets/code.mjs +10 -11
  176. package/dist/widgets/completions/actions.d.mts +1 -1
  177. package/dist/widgets/completions/actions.mjs +1 -1
  178. package/dist/widgets/completions/files.d.mts +1 -1
  179. package/dist/widgets/completions/files.mjs +1 -1
  180. package/dist/widgets/completions/github.d.mts +1 -1
  181. package/dist/widgets/completions/github.mjs +1 -1
  182. package/dist/widgets/completions/index.d.mts +3 -3
  183. package/dist/widgets/completions/index.mjs +3 -3
  184. package/dist/widgets/diff.d.mts +2 -4
  185. package/dist/widgets/diff.d.mts.map +1 -1
  186. package/dist/widgets/diff.mjs +7 -8
  187. package/dist/widgets/divider.d.mts +2 -4
  188. package/dist/widgets/divider.d.mts.map +1 -1
  189. package/dist/widgets/divider.mjs +1 -1
  190. package/dist/widgets/image.d.mts +1 -1
  191. package/dist/widgets/image.mjs +5 -6
  192. package/dist/widgets/input.d.mts +1 -1
  193. package/dist/widgets/input.mjs +1 -1
  194. package/dist/widgets/log.d.mts +1 -1
  195. package/dist/widgets/log.mjs +1 -1
  196. package/dist/widgets/markdown.d.mts +5 -7
  197. package/dist/widgets/markdown.d.mts.map +1 -1
  198. package/dist/widgets/markdown.mjs +1 -1
  199. package/dist/widgets/overlay.d.mts +1 -1
  200. package/dist/widgets/overlay.mjs +1 -1
  201. package/dist/widgets/picker.d.mts +1 -1
  202. package/dist/widgets/picker.mjs +1 -1
  203. package/dist/widgets/progress.d.mts +2 -4
  204. package/dist/widgets/progress.d.mts.map +1 -1
  205. package/dist/widgets/progress.mjs +4 -5
  206. package/dist/widgets/select.d.mts +1 -1
  207. package/dist/widgets/select.mjs +1 -1
  208. package/dist/widgets/show.d.mts +1 -3
  209. package/dist/widgets/show.d.mts.map +1 -1
  210. package/dist/widgets/show.mjs +1 -1
  211. package/dist/widgets/spinner.d.mts +2 -4
  212. package/dist/widgets/spinner.d.mts.map +1 -1
  213. package/dist/widgets/spinner.mjs +3 -4
  214. package/dist/widgets/text.d.mts +1 -1
  215. package/dist/widgets/text.mjs +1 -1
  216. package/dist/widgets/tree.d.mts +1 -1
  217. package/dist/widgets/tree.mjs +1 -1
  218. package/dist/widgets/widget.d.mts +1 -1
  219. package/dist/widgets/widget.mjs +1 -1
  220. package/dist/{worker-T6SdPzUH.mjs → worker-CtEowucz.mjs} +2 -3
  221. package/dist/{worker-T6SdPzUH.mjs.map → worker-CtEowucz.mjs.map} +1 -1
  222. package/dist/worker-Mqi380Et.mjs +1 -0
  223. package/package.json +3 -3
  224. package/dist/actions--ODlgCQ5.d.mts.map +0 -1
  225. package/dist/autocomplete-CD30kSe_.d.mts.map +0 -1
  226. package/dist/client-CNGt-dOt.mjs +0 -1
  227. package/dist/ctx-CndlTp1T.mjs +0 -1
  228. package/dist/files-B_5HDBPp.d.mts.map +0 -1
  229. package/dist/github-CyRwUQ3y.d.mts.map +0 -1
  230. package/dist/image-BLls2F4t.d.mts.map +0 -1
  231. package/dist/input-NuD8TH8N.d.mts.map +0 -1
  232. package/dist/inspect-B1uXxdKC.d.mts.map +0 -1
  233. package/dist/kitty-DKQYsQfH.mjs +0 -1
  234. package/dist/math-CU0wmC9N.mjs +0 -1
  235. package/dist/picker-B-OxKKnD.d.mts.map +0 -1
  236. package/dist/queries-C75-wG1B.d.mts.map +0 -1
  237. package/dist/renderer-YHbR7JZ4.mjs +0 -1
  238. package/dist/search-FvGnswRl.d.mts.map +0 -1
  239. package/dist/select-BKE5LzdN.d.mts.map +0 -1
  240. package/dist/text-CmxbA8lk.d.mts.map +0 -1
  241. package/dist/tree-CTvSpF9b.d.mts.map +0 -1
  242. package/dist/types-C9ceMsbJ.d.mts.map +0 -1
  243. package/dist/types-D3jk1rtk.d.mts.map +0 -1
  244. package/dist/widget-DlF32PRp.d.mts.map +0 -1
  245. package/dist/worker-C01jK2Ip.mjs +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"markdown-D3ZtRn6C.mjs","names":[],"sources":["../src/widgets/markdown.ts"],"sourcesContent":["import type { RenderCtx } from \"../core/ctx.ts\"\nimport type { Accessor } from \"../core/reactive.ts\"\nimport type { State } from \"../core/state.ts\"\nimport type { WrapMode } from \"../layout/text.ts\"\nimport type { MarkdownRenderer } from \"../markdown/renderer.ts\"\nimport type { MdOptions } from \"../markdown/types.ts\"\nimport type { ShikiThemeInput } from \"../shiki/types.ts\"\nimport type { AnyStyle } from \"../style/types.ts\"\nimport type { Image } from \"./image.ts\"\nimport type { TextContent } from \"./text.ts\"\n\nimport { hasColors } from \"@zaly/shared/env\"\nimport { Node } from \"../core/node.ts\"\nimport { createAsync, signal, unwrap, useContext } from \"../core/reactive.ts\"\nimport { RenderContext } from \"../core/render.ts\"\nimport { calcLayout, expandTabs, formatText } from \"../layout/text.ts\"\nimport { shikiWorker } from \"../shiki/client.ts\"\nimport { codeToAnsi } from \"../shiki/shiki.ts\"\nimport { shikiTheme } from \"../shiki/theme.ts\"\nimport { textContent } from \"./text.ts\"\n\nexport interface MarkdownState {\n /** Markdown source. Accepts a plain string or a reactive accessor —\n * pass `signal()` / `memo()` for streaming content that re-parses on\n * each render and subscribes the node to the signal. */\n content: TextContent\n /** Options forwarded to `renderMarkdown`. */\n options?: MdOptions\n /**\n * Enable shiki-backed syntax highlighting for fenced code blocks. Defaults\n * to `true` — code blocks get per-token colors from the shiki theme, with\n * `mdCodeBlock`'s bg applied as a backdrop. Unknown languages fall through\n * as plain text.\n */\n syntax?: boolean\n wrap?: WrapMode\n style?: AnyStyle\n}\n\ntype ShikiCode = {\n key: string\n code: string\n lang: string\n theme?: ShikiThemeInput\n inflight?: Promise<string>\n ac?: AbortController\n result?: string\n gen: number\n}\n\nexport class Markdown extends Node<MarkdownState> {\n // Image nodes cached per-src per-Markdown-instance. Re-rendering the\n // same markdown (streaming updates) reuses the same `Image` — same\n // `placementId`, which the KGP spec guarantees is a flicker-free\n // move/resize of the existing placement.\n readonly images = new Map<string, Image>()\n\n #renderer?: MarkdownRenderer\n #code = new Map<string, ShikiCode>()\n #update = signal(0)\n #gen = 0\n #worker: Accessor<number>\n #images?: Accessor<boolean>\n\n constructor(state: State<MarkdownState>) {\n super(state)\n\n const context = useContext(RenderContext)\n this.#images = context?.images\n\n this.#worker = createAsync(\n async () => {\n const update = this.#update.get() // track\n const todo = [...this.#code.values()].filter((c) => c.gen === this.#gen && !c.result)\n if (!todo.length) return update // nothing to do, skip\n await Promise.all(\n todo.map(async (req) => {\n req.ac ??= new AbortController()\n req.inflight ??= codeToAnsi(req.code, req.lang, {\n signal: req.ac.signal,\n theme: req.theme,\n })\n try {\n req.result = await req.inflight\n } catch (error) {\n if (req.ac.signal.aborted) {\n if (this.#code.get(req.key) === req) this.#code.delete(req.key)\n } else throw error\n }\n })\n )\n return update + 1\n },\n { initialValue: 0 }\n )\n }\n\n /** Find a highlighted result for a prefix of `code` in the cache. This\n * handles streaming updates where the full code block isn't available yet\n * and prevents flashing un-highlighted text on each update. */\n #streamingHighlight(code: string, lang: string, theme?: ShikiThemeInput) {\n let ret: ShikiCode | undefined\n for (const c of this.#code.values()) {\n if (!c.result || c.lang !== lang || c.theme !== theme) continue\n if (code.startsWith(c.code) && c.code.length > (ret?.code.length ?? 0)) ret = c\n }\n if (ret) ret.gen = this.#gen // bump gen to keep it alive in cache\n return ret?.result ? ret.result + code.slice(ret.code.length) : code\n }\n\n #highlight(code: string, lang?: string, theme?: ShikiThemeInput): string {\n if (!lang) return code\n const key = shikiWorker.key({ code, lang, theme })\n const ret = this.#code.get(key)\n if (ret) ret.gen = this.#gen // bump gen to keep it alive in cache\n if (ret?.result) return ret.result\n else if (!ret) this.#code.set(key, { code, gen: this.#gen, key, lang, theme })\n return this.#streamingHighlight(code, lang, theme)\n }\n\n protected async _render(ctx: RenderCtx): Promise<string[]> {\n if (!this.#renderer) {\n const { MarkdownRenderer } = await import(\"../markdown/renderer.ts\")\n this.#renderer = new MarkdownRenderer({ ...this.state.options, parent: this })\n }\n let source = await textContent(this.state.content, ctx)\n source = expandTabs(source)\n let formatted: string\n\n if (!hasColors) formatted = this.#renderer.normalizeEol(source, source)\n else {\n this.#gen++ // bump generation to track what is still used\n\n formatted = await this.#renderer.render(source, {\n ...ctx,\n highlight: (code, lang) => this.#highlight(code, lang, shikiTheme(ctx.style.theme)),\n images: unwrap(this.#images) ?? true,\n })\n\n // prune old entries\n for (const [k, c] of this.#code.entries())\n if (c.gen !== this.#gen) {\n c.ac?.abort()\n if (c.result !== undefined) this.#code.delete(k)\n }\n\n // Trigger the worker if needed\n if (this.#code.values().some((c) => !c.inflight)) {\n this.#update.set(this.#gen) // trigger worker\n this.#worker() // tracked\n }\n }\n\n return formatText(formatted, {\n indent: true,\n style: this.state.style ? ctx.style.add(this.state.style) : undefined,\n width: ctx.width,\n wrap: this.state.wrap,\n wrapBg: true,\n })\n }\n\n override async layout(ctx: RenderCtx) {\n return calcLayout(await textContent(this.state.content, ctx))\n }\n}\n\n/**\n * Render a markdown string as a TUI node. Produces ANSI-styled text that\n * resolves per-element styling through theme slots (`mdHeading1`, `mdCode`,\n * `mdLink`, …). Links become clickable in terminals that support OSC 8.\n *\n * ```ts\n * markdown(\"# Hello\\n\\nThis is **bold** and *italic*.\")\n * markdown({ content, wrap: \"word\", fg: \"fg\" })\n * ```\n */\nexport function markdown(\n content: TextContent,\n style?: Omit<State<MarkdownState>, \"content\">\n): Markdown\nexport function markdown(state: State<MarkdownState>): Markdown\nexport function markdown(\n first: TextContent | MarkdownState,\n style?: Omit<State<MarkdownState>, \"content\">\n): Markdown {\n // Plain strings and accessor functions go through the content path;\n // a non-function object is the full state form.\n if (typeof first === \"string\" || typeof first === \"function\") {\n return new Markdown({ content: first, ...style })\n }\n return new Markdown(first)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"markdown-CgRZBhIX.mjs","names":[],"sources":["../src/widgets/markdown.ts"],"sourcesContent":["import type { RenderCtx } from \"../core/ctx.ts\"\nimport type { Accessor } from \"../core/reactive.ts\"\nimport type { State } from \"../core/state.ts\"\nimport type { WrapMode } from \"../layout/text.ts\"\nimport type { MarkdownRenderer } from \"../markdown/renderer.ts\"\nimport type { MdOptions } from \"../markdown/types.ts\"\nimport type { ShikiThemeInput } from \"../shiki/types.ts\"\nimport type { AnyStyle } from \"../style/types.ts\"\nimport type { Image } from \"./image.ts\"\nimport type { TextContent } from \"./text.ts\"\n\nimport { hasColors } from \"@zaly/shared/env\"\nimport { Node } from \"../core/node.ts\"\nimport { createAsync, signal, unwrap, useContext } from \"../core/reactive.ts\"\nimport { RenderContext } from \"../core/render.ts\"\nimport { calcLayout, expandTabs, formatText } from \"../layout/text.ts\"\nimport { shikiWorker } from \"../shiki/client.ts\"\nimport { codeToAnsi } from \"../shiki/shiki.ts\"\nimport { shikiTheme } from \"../shiki/theme.ts\"\nimport { textContent } from \"./text.ts\"\n\nexport interface MarkdownState {\n /** Markdown source. Accepts a plain string or a reactive accessor —\n * pass `signal()` / `memo()` for streaming content that re-parses on\n * each render and subscribes the node to the signal. */\n content: TextContent\n /** Options forwarded to `renderMarkdown`. */\n options?: MdOptions\n /**\n * Enable shiki-backed syntax highlighting for fenced code blocks. Defaults\n * to `true` — code blocks get per-token colors from the shiki theme, with\n * `mdCodeBlock`'s bg applied as a backdrop. Unknown languages fall through\n * as plain text.\n */\n syntax?: boolean\n wrap?: WrapMode\n style?: AnyStyle\n}\n\ntype ShikiCode = {\n key: string\n code: string\n lang: string\n theme?: ShikiThemeInput\n inflight?: Promise<string>\n ac?: AbortController\n result?: string\n gen: number\n}\n\nexport class Markdown extends Node<MarkdownState> {\n // Image nodes cached per-src per-Markdown-instance. Re-rendering the\n // same markdown (streaming updates) reuses the same `Image` — same\n // `placementId`, which the KGP spec guarantees is a flicker-free\n // move/resize of the existing placement.\n readonly images = new Map<string, Image>()\n\n #renderer?: MarkdownRenderer\n #code = new Map<string, ShikiCode>()\n #update = signal(0)\n #gen = 0\n #worker: Accessor<number>\n #images?: Accessor<boolean>\n\n constructor(state: State<MarkdownState>) {\n super(state)\n\n const context = useContext(RenderContext)\n this.#images = context?.images\n\n this.#worker = createAsync(\n async () => {\n const update = this.#update.get() // track\n const todo = [...this.#code.values()].filter((c) => c.gen === this.#gen && !c.result)\n if (!todo.length) return update // nothing to do, skip\n await Promise.all(\n todo.map(async (req) => {\n req.ac ??= new AbortController()\n req.inflight ??= codeToAnsi(req.code, req.lang, {\n signal: req.ac.signal,\n theme: req.theme,\n })\n try {\n req.result = await req.inflight\n } catch (error) {\n if (req.ac.signal.aborted) {\n if (this.#code.get(req.key) === req) this.#code.delete(req.key)\n } else throw error\n }\n })\n )\n return update + 1\n },\n { initialValue: 0 }\n )\n }\n\n /** Find a highlighted result for a prefix of `code` in the cache. This\n * handles streaming updates where the full code block isn't available yet\n * and prevents flashing un-highlighted text on each update. */\n #streamingHighlight(code: string, lang: string, theme?: ShikiThemeInput) {\n let ret: ShikiCode | undefined\n for (const c of this.#code.values()) {\n if (!c.result || c.lang !== lang || c.theme !== theme) continue\n if (code.startsWith(c.code) && c.code.length > (ret?.code.length ?? 0)) ret = c\n }\n if (ret) ret.gen = this.#gen // bump gen to keep it alive in cache\n return ret?.result ? ret.result + code.slice(ret.code.length) : code\n }\n\n #highlight(code: string, lang?: string, theme?: ShikiThemeInput): string {\n if (!lang) return code\n const key = shikiWorker.key({ code, lang, theme })\n const ret = this.#code.get(key)\n if (ret) ret.gen = this.#gen // bump gen to keep it alive in cache\n if (ret?.result) return ret.result\n else if (!ret) this.#code.set(key, { code, gen: this.#gen, key, lang, theme })\n return this.#streamingHighlight(code, lang, theme)\n }\n\n protected async _render(ctx: RenderCtx): Promise<string[]> {\n if (!this.#renderer) {\n const { MarkdownRenderer } = await import(\"../markdown/renderer.ts\")\n this.#renderer = new MarkdownRenderer({ ...this.state.options, parent: this })\n }\n let source = await textContent(this.state.content, ctx)\n source = expandTabs(source)\n let formatted: string\n\n if (!hasColors) formatted = this.#renderer.normalizeEol(source, source)\n else {\n this.#gen++ // bump generation to track what is still used\n\n formatted = await this.#renderer.render(source, {\n ...ctx,\n highlight: (code, lang) => this.#highlight(code, lang, shikiTheme(ctx.style.theme)),\n images: unwrap(this.#images) ?? true,\n })\n\n // prune old entries\n for (const [k, c] of this.#code.entries())\n if (c.gen !== this.#gen) {\n c.ac?.abort()\n if (c.result !== undefined) this.#code.delete(k)\n }\n\n // Trigger the worker if needed\n if (this.#code.values().some((c) => !c.inflight)) {\n this.#update.set(this.#gen) // trigger worker\n this.#worker() // tracked\n }\n }\n\n return formatText(formatted, {\n indent: true,\n style: this.state.style ? ctx.style.add(this.state.style) : undefined,\n width: ctx.width,\n wrap: this.state.wrap,\n wrapBg: true,\n })\n }\n\n override async layout(ctx: RenderCtx) {\n return calcLayout(await textContent(this.state.content, ctx))\n }\n}\n\n/**\n * Render a markdown string as a TUI node. Produces ANSI-styled text that\n * resolves per-element styling through theme slots (`mdHeading1`, `mdCode`,\n * `mdLink`, …). Links become clickable in terminals that support OSC 8.\n *\n * ```ts\n * markdown(\"# Hello\\n\\nThis is **bold** and *italic*.\")\n * markdown({ content, wrap: \"word\", fg: \"fg\" })\n * ```\n */\nexport function markdown(\n content: TextContent,\n style?: Omit<State<MarkdownState>, \"content\">\n): Markdown\nexport function markdown(state: State<MarkdownState>): Markdown\nexport function markdown(\n first: TextContent | MarkdownState,\n style?: Omit<State<MarkdownState>, \"content\">\n): Markdown {\n // Plain strings and accessor functions go through the content path;\n // a non-function object is the full state form.\n if (typeof first === \"string\" || typeof first === \"function\") {\n return new Markdown({ content: first, ...style })\n }\n return new Markdown(first)\n}\n"],"mappings":"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"}
@@ -1,6 +1,5 @@
1
- import { I as RenderCtx, h as Node } from "./queries-C75-wG1B.mjs";
2
- import { n as MdOptions, r as RenderMarkdown } from "./types-D3jk1rtk.mjs";
3
- //#region src/markdown/renderer.d.ts
1
+ import { I as RenderCtx, h as Node } from "./queries-Dmd3IjSd.mjs";
2
+ import { n as MdOptions, r as RenderMarkdown } from "./types-CUVUJPz6.mjs";
4
3
  export type MarkdownOptions = MdOptions & {
5
4
  parent?: Node;
6
5
  };
@@ -19,6 +18,5 @@ export declare class MarkdownRenderer {
19
18
  /* they asked for it. */
20
19
  normalizeEol(source: string, rendered: string): string;
21
20
  }
22
- //#endregion
23
21
  export type { MdOptions, RenderMarkdown };
24
22
  //# sourceMappingURL=markdown.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"markdown.d.mts","names":[],"sources":["../src/markdown/renderer.ts"],"mappings":";;;YAKY,kBAAkB;EAC5B,SAAS;;YAGC,cAAc;EACxB;EACA,aAAa,cAAc;;qBAGhB;;EAMX,YAAY,OAAM;EAKZ,OAAO,gBAAgB,KAAK,cAAc;;;;;;EAmChD,aAAa,gBAAgB"}
1
+ {"version":3,"file":"markdown.d.mts","names":[],"sources":["../src/markdown/renderer.ts"],"mappings":";;YAKY,kBAAkB;EAC5B,SAAS;;YAGC,cAAc;EACxB;EACA,aAAa,cAAc;;qBAGhB;;EAMX,YAAY,OAAM;EAKZ,OAAO,gBAAgB,KAAK,cAAc;;;;;;EAmChD,aAAa,gBAAgB"}
package/dist/markdown.mjs CHANGED
@@ -1 +1 @@
1
- export { t as MarkdownRenderer } from "./renderer-aAtkFgll.mjs";
1
+ export { t as MarkdownRenderer } from "./renderer-BYl2pbdx.mjs";
@@ -0,0 +1 @@
1
+ export { t as mathImage } from "./math-VPuwFCid.mjs";
@@ -200,7 +200,6 @@ function mathImage(math, color) {
200
200
  files.set(key, pending);
201
201
  return pending;
202
202
  }
203
- //#endregion
204
203
  export { replaceMath as n, mathImage as t };
205
204
 
206
- //# sourceMappingURL=math-Dd7a1irv.mjs.map
205
+ //# sourceMappingURL=math-VPuwFCid.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"math-Dd7a1irv.mjs","names":[],"sources":["../src/markdown/math.ts"],"sourcesContent":["import { hash } from \"@zaly/shared\"\nimport { existsSync } from \"node:fs\"\nimport { mkdir, rename, writeFile } from \"node:fs/promises\"\nimport { tmpdir, userInfo } from \"node:os\"\nimport { join } from \"pathe\"\n\n// Rasterize at three times an 8px cell width, independently of terminal placement size.\nexport const MATH_PIXELS_PER_CELL = 24\n\nexport interface MathExpression {\n tex: string\n display: boolean\n source: string\n}\n\n/** Protect math before Markdown consumes its escapes. Code and incomplete spans stay intact. */\nexport function replaceMath(source: string, replace: (math: MathExpression) => string): string {\n if (!/[$\\\\]|^ {0,3}(?:`{3,}|~{3,})\\s*math\\s*$/m.test(source)) return source\n let out = \"\"\n let i = 0\n const escaped = (pos: number) => {\n let count = 0\n while (pos > 0 && source[--pos] === \"\\\\\") count++\n return count % 2 === 1\n }\n while (i < source.length) {\n if (i === 0 || source[i - 1] === \"\\n\") {\n const lineEnd = source.indexOf(\"\\n\", i)\n const line = source.slice(i, lineEnd === -1 ? source.length : lineEnd)\n const fence = /^ {0,3}(`{3,}|~{3,})([^\\n]*)$/.exec(line)\n if (fence) {\n const close = new RegExp(`^ {0,3}${fence[1][0]}{${fence[1].length},}[ \\\\t]*$`, \"gm\")\n close.lastIndex = lineEnd === -1 ? source.length : lineEnd + 1\n const end = close.exec(source)\n const stop = end ? end.index + end[0].length : source.length\n const raw = source.slice(i, stop)\n out +=\n end && fence[2].trim() === \"math\"\n ? replace({\n display: true,\n source: raw,\n tex: source.slice(lineEnd + 1, end.index).trim(),\n })\n : raw\n i = stop\n continue\n }\n if (/^(?: {4}|\\t)|^ {0,3}\\[[^\\]]+\\]:/.test(line)) {\n out += line\n i += line.length\n continue\n }\n }\n // Existing destinations and raw HTML must not become math or acquire image markers.\n if (source[i] === \"<\") {\n const html = /^(?:<(pre|code|script|style)\\b[^>]*>[\\s\\S]*?<\\/\\1\\s*>|<[^>\\n]*>)/i.exec(\n source.slice(i)\n )\n if (html) {\n out += html[0]\n i += html[0].length\n continue\n }\n }\n if (source.startsWith(\"![\", i)) {\n let end = i + 2\n let depth = 1\n for (; end < source.length && depth > 0; end++) {\n if (escaped(end)) continue\n if (source[end] === \"[\") depth++\n else if (source[end] === \"]\") depth--\n }\n out += source.slice(i, end)\n i = end\n continue\n }\n if (source[i] === \"(\" && source[i - 1] === \"]\") {\n let end = i + 1\n let depth = 1\n for (; end < source.length && depth > 0; end++) {\n if (escaped(end)) continue\n if (source[end] === \"(\") depth++\n else if (source[end] === \")\") depth--\n }\n out += source.slice(i, end)\n i = end\n continue\n }\n let open: string | undefined\n let close = \"\"\n let display = false\n if (source.startsWith(String.raw`\\[`, i)) {\n open = String.raw`\\[`\n close = String.raw`\\]`\n display = true\n } else if (source.startsWith(String.raw`\\(`, i)) {\n open = String.raw`\\(`\n close = String.raw`\\)`\n } else if (source.startsWith(\"$$\", i)) {\n open = \"$$\"\n close = \"$$\"\n display = true\n } else if (source.startsWith(\"$`\", i)) {\n open = \"$`\"\n close = \"`$\"\n } else if (source[i] === \"$\" && /\\S/.test(source[i + 1] ?? \"\")) {\n open = \"$\"\n close = \"$\"\n }\n if (open) {\n let end = i + open.length\n for (; end < source.length; end++) {\n if (!display && source[end] === \"\\n\") break\n if (!source.startsWith(close, end) || escaped(end)) continue\n if (close === \"$\" && (/\\s/.test(source[end - 1]) || /[\\d$]/.test(source[end + 1] ?? \"\")))\n continue\n const stop = end + close.length\n const tex = source.slice(i + open.length, end)\n if (!tex.trim()) break\n out += replace({ display, source: source.slice(i, stop), tex })\n i = stop\n break\n }\n if (i === end + close.length) continue\n // Keep incomplete spans intact, without rescanning every nested opener while streaming.\n out += source.slice(i, end)\n i = end\n continue\n }\n if (source[i] === \"`\") {\n const ticks = /^`+/.exec(source.slice(i))![0]\n let end = source.indexOf(ticks, i + ticks.length)\n while (end >= 0 && (source[end - 1] === \"`\" || source[end + ticks.length] === \"`\"))\n end = source.indexOf(ticks, end + ticks.length)\n const stop = end < 0 ? source.length : end + ticks.length\n out += source.slice(i, stop)\n i = stop\n continue\n }\n if (source[i] === \"\\\\\" && i + 1 < source.length) {\n out += source.slice(i, i + 2)\n i += 2\n continue\n }\n out += source[i++]\n }\n return out\n}\n\nasync function createTypesetter() {\n const [{ mathjax }, { TeX }, { SVG }, { liteAdaptor }, { RegisterHTMLHandler }] =\n await Promise.all([\n import(\"@mathjax/src/js/mathjax.js\"),\n import(\"@mathjax/src/js/input/tex.js\"),\n import(\"@mathjax/src/js/output/svg.js\"),\n import(\"@mathjax/src/js/adaptors/liteAdaptor.js\"),\n import(\"@mathjax/src/js/handlers/html.js\"),\n import(\"@mathjax/src/js/input/tex/ams/AmsConfiguration.js\"),\n import(\"@mathjax/src/js/util/asyncLoad/esm.js\"),\n ])\n const adaptor = liteAdaptor()\n // oxlint-disable-next-line new-cap -- MathJax registration API.\n RegisterHTMLHandler(adaptor)\n const doc = mathjax.document(\"\", {\n InputJax: new TeX({\n formatError: (_jax: unknown, error: Error) => {\n throw error\n },\n maxBuffer: 20_000,\n packages: [\"base\", \"ams\"],\n }),\n OutputJax: new SVG({ fontCache: \"none\", linebreaks: { inline: false } }),\n })\n return async (math: MathExpression, color: string) => {\n const node = await mathjax.handleRetriesFor(() =>\n doc.convert(math.tex, { display: math.display })\n )\n const svg = adaptor.firstChild(node)\n if (!(\"attributes\" in svg) || svg.kind !== \"svg\") throw new Error(\"MathJax did not produce SVG\")\n // Explicit pixel dimensions work with both image-meta and sharp, unlike MathJax's ex units.\n for (const dimension of [\"width\", \"height\"]) {\n const ex = Number.parseFloat(adaptor.getAttribute(svg, dimension))\n adaptor.setAttribute(\n svg,\n dimension,\n String(Math.max(1, Math.ceil(ex * MATH_PIXELS_PER_CELL)))\n )\n }\n const xml = adaptor.serializeXML(svg)\n if (xml.includes('data-mml-node=\"merror\"')) throw new Error(\"Invalid math expression\")\n return xml.replaceAll(\"currentColor\", color)\n }\n}\n\nlet typesetter: ReturnType<typeof createTypesetter> | undefined\nconst files = new Map<string, Promise<string>>()\n\n/** Theme-dependent, self-contained SVG paths shared by repeated occurrences and re-renders. */\nexport function mathImage(math: MathExpression, color: string): Promise<string> {\n if (math.tex.length > 20_000) return Promise.reject(new Error(\"Math expression is too large\"))\n const key = hash(JSON.stringify([\"svg-v3\", math.tex, math.display, color]))\n const cached = files.get(key)\n if (cached) return cached\n const pending = (async () => {\n const dir = join(tmpdir(), `zaly-math-${userInfo().uid}`)\n const path = join(dir, `${key}.svg`)\n if (existsSync(path)) return path\n const render = await (typesetter ??= createTypesetter())\n const svg = await render(math, color)\n await mkdir(dir, { mode: 0o700, recursive: true })\n const temporary = join(dir, `${key}-${process.pid}-${Math.random().toString(36).slice(2)}.svg`)\n await writeFile(temporary, svg, { mode: 0o600 })\n await rename(temporary, path)\n return path\n })()\n if (files.size >= 128) files.delete(files.keys().next().value!)\n files.set(key, pending)\n return pending\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"math-VPuwFCid.mjs","names":[],"sources":["../src/markdown/math.ts"],"sourcesContent":["import { hash } from \"@zaly/shared\"\nimport { existsSync } from \"node:fs\"\nimport { mkdir, rename, writeFile } from \"node:fs/promises\"\nimport { tmpdir, userInfo } from \"node:os\"\nimport { join } from \"pathe\"\n\n// Rasterize at three times an 8px cell width, independently of terminal placement size.\nexport const MATH_PIXELS_PER_CELL = 24\n\nexport interface MathExpression {\n tex: string\n display: boolean\n source: string\n}\n\n/** Protect math before Markdown consumes its escapes. Code and incomplete spans stay intact. */\nexport function replaceMath(source: string, replace: (math: MathExpression) => string): string {\n if (!/[$\\\\]|^ {0,3}(?:`{3,}|~{3,})\\s*math\\s*$/m.test(source)) return source\n let out = \"\"\n let i = 0\n const escaped = (pos: number) => {\n let count = 0\n while (pos > 0 && source[--pos] === \"\\\\\") count++\n return count % 2 === 1\n }\n while (i < source.length) {\n if (i === 0 || source[i - 1] === \"\\n\") {\n const lineEnd = source.indexOf(\"\\n\", i)\n const line = source.slice(i, lineEnd === -1 ? source.length : lineEnd)\n const fence = /^ {0,3}(`{3,}|~{3,})([^\\n]*)$/.exec(line)\n if (fence) {\n const close = new RegExp(`^ {0,3}${fence[1][0]}{${fence[1].length},}[ \\\\t]*$`, \"gm\")\n close.lastIndex = lineEnd === -1 ? source.length : lineEnd + 1\n const end = close.exec(source)\n const stop = end ? end.index + end[0].length : source.length\n const raw = source.slice(i, stop)\n out +=\n end && fence[2].trim() === \"math\"\n ? replace({\n display: true,\n source: raw,\n tex: source.slice(lineEnd + 1, end.index).trim(),\n })\n : raw\n i = stop\n continue\n }\n if (/^(?: {4}|\\t)|^ {0,3}\\[[^\\]]+\\]:/.test(line)) {\n out += line\n i += line.length\n continue\n }\n }\n // Existing destinations and raw HTML must not become math or acquire image markers.\n if (source[i] === \"<\") {\n const html = /^(?:<(pre|code|script|style)\\b[^>]*>[\\s\\S]*?<\\/\\1\\s*>|<[^>\\n]*>)/i.exec(\n source.slice(i)\n )\n if (html) {\n out += html[0]\n i += html[0].length\n continue\n }\n }\n if (source.startsWith(\"![\", i)) {\n let end = i + 2\n let depth = 1\n for (; end < source.length && depth > 0; end++) {\n if (escaped(end)) continue\n if (source[end] === \"[\") depth++\n else if (source[end] === \"]\") depth--\n }\n out += source.slice(i, end)\n i = end\n continue\n }\n if (source[i] === \"(\" && source[i - 1] === \"]\") {\n let end = i + 1\n let depth = 1\n for (; end < source.length && depth > 0; end++) {\n if (escaped(end)) continue\n if (source[end] === \"(\") depth++\n else if (source[end] === \")\") depth--\n }\n out += source.slice(i, end)\n i = end\n continue\n }\n let open: string | undefined\n let close = \"\"\n let display = false\n if (source.startsWith(String.raw`\\[`, i)) {\n open = String.raw`\\[`\n close = String.raw`\\]`\n display = true\n } else if (source.startsWith(String.raw`\\(`, i)) {\n open = String.raw`\\(`\n close = String.raw`\\)`\n } else if (source.startsWith(\"$$\", i)) {\n open = \"$$\"\n close = \"$$\"\n display = true\n } else if (source.startsWith(\"$`\", i)) {\n open = \"$`\"\n close = \"`$\"\n } else if (source[i] === \"$\" && /\\S/.test(source[i + 1] ?? \"\")) {\n open = \"$\"\n close = \"$\"\n }\n if (open) {\n let end = i + open.length\n for (; end < source.length; end++) {\n if (!display && source[end] === \"\\n\") break\n if (!source.startsWith(close, end) || escaped(end)) continue\n if (close === \"$\" && (/\\s/.test(source[end - 1]) || /[\\d$]/.test(source[end + 1] ?? \"\")))\n continue\n const stop = end + close.length\n const tex = source.slice(i + open.length, end)\n if (!tex.trim()) break\n out += replace({ display, source: source.slice(i, stop), tex })\n i = stop\n break\n }\n if (i === end + close.length) continue\n // Keep incomplete spans intact, without rescanning every nested opener while streaming.\n out += source.slice(i, end)\n i = end\n continue\n }\n if (source[i] === \"`\") {\n const ticks = /^`+/.exec(source.slice(i))![0]\n let end = source.indexOf(ticks, i + ticks.length)\n while (end >= 0 && (source[end - 1] === \"`\" || source[end + ticks.length] === \"`\"))\n end = source.indexOf(ticks, end + ticks.length)\n const stop = end < 0 ? source.length : end + ticks.length\n out += source.slice(i, stop)\n i = stop\n continue\n }\n if (source[i] === \"\\\\\" && i + 1 < source.length) {\n out += source.slice(i, i + 2)\n i += 2\n continue\n }\n out += source[i++]\n }\n return out\n}\n\nasync function createTypesetter() {\n const [{ mathjax }, { TeX }, { SVG }, { liteAdaptor }, { RegisterHTMLHandler }] =\n await Promise.all([\n import(\"@mathjax/src/js/mathjax.js\"),\n import(\"@mathjax/src/js/input/tex.js\"),\n import(\"@mathjax/src/js/output/svg.js\"),\n import(\"@mathjax/src/js/adaptors/liteAdaptor.js\"),\n import(\"@mathjax/src/js/handlers/html.js\"),\n import(\"@mathjax/src/js/input/tex/ams/AmsConfiguration.js\"),\n import(\"@mathjax/src/js/util/asyncLoad/esm.js\"),\n ])\n const adaptor = liteAdaptor()\n // oxlint-disable-next-line new-cap -- MathJax registration API.\n RegisterHTMLHandler(adaptor)\n const doc = mathjax.document(\"\", {\n InputJax: new TeX({\n formatError: (_jax: unknown, error: Error) => {\n throw error\n },\n maxBuffer: 20_000,\n packages: [\"base\", \"ams\"],\n }),\n OutputJax: new SVG({ fontCache: \"none\", linebreaks: { inline: false } }),\n })\n return async (math: MathExpression, color: string) => {\n const node = await mathjax.handleRetriesFor(() =>\n doc.convert(math.tex, { display: math.display })\n )\n const svg = adaptor.firstChild(node)\n if (!(\"attributes\" in svg) || svg.kind !== \"svg\") throw new Error(\"MathJax did not produce SVG\")\n // Explicit pixel dimensions work with both image-meta and sharp, unlike MathJax's ex units.\n for (const dimension of [\"width\", \"height\"]) {\n const ex = Number.parseFloat(adaptor.getAttribute(svg, dimension))\n adaptor.setAttribute(\n svg,\n dimension,\n String(Math.max(1, Math.ceil(ex * MATH_PIXELS_PER_CELL)))\n )\n }\n const xml = adaptor.serializeXML(svg)\n if (xml.includes('data-mml-node=\"merror\"')) throw new Error(\"Invalid math expression\")\n return xml.replaceAll(\"currentColor\", color)\n }\n}\n\nlet typesetter: ReturnType<typeof createTypesetter> | undefined\nconst files = new Map<string, Promise<string>>()\n\n/** Theme-dependent, self-contained SVG paths shared by repeated occurrences and re-renders. */\nexport function mathImage(math: MathExpression, color: string): Promise<string> {\n if (math.tex.length > 20_000) return Promise.reject(new Error(\"Math expression is too large\"))\n const key = hash(JSON.stringify([\"svg-v3\", math.tex, math.display, color]))\n const cached = files.get(key)\n if (cached) return cached\n const pending = (async () => {\n const dir = join(tmpdir(), `zaly-math-${userInfo().uid}`)\n const path = join(dir, `${key}.svg`)\n if (existsSync(path)) return path\n const render = await (typesetter ??= createTypesetter())\n const svg = await render(math, color)\n await mkdir(dir, { mode: 0o700, recursive: true })\n const temporary = join(dir, `${key}-${process.pid}-${Math.random().toString(36).slice(2)}.svg`)\n await writeFile(temporary, svg, { mode: 0o600 })\n await rename(temporary, path)\n return path\n })()\n if (files.size >= 128) files.delete(files.keys().next().value!)\n files.set(key, pending)\n return pending\n}\n"],"mappings":"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"}
package/dist/md.bun.d.mts CHANGED
@@ -1,5 +1,4 @@
1
- import { n as MdOptions, t as MdCallbacks } from "./types-D3jk1rtk.mjs";
2
- //#region src/runtime/md.bun.d.ts
1
+ import { n as MdOptions, t as MdCallbacks } from "./types-CUVUJPz6.mjs";
3
2
  /**
4
3
  * Bun wrapper for `renderMarkdown`. `Bun.markdown.render` truncates code
5
4
  * fence info-strings at the first whitespace, so attrs like
@@ -9,5 +8,4 @@ import { n as MdOptions, t as MdCallbacks } from "./types-D3jk1rtk.mjs";
9
8
  * strings natively, so the equivalent dance lives only here.
10
9
  */
11
10
  export declare function renderMarkdown(input: string, callbacks: MdCallbacks, opts?: MdOptions): string;
12
- //#endregion
13
11
  //# sourceMappingURL=md.bun.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"md.bun.d.mts","names":[],"sources":["../src/runtime/md.bun.ts"],"mappings":";;;;;;;;;;wBAYgB,eAAe,eAAe,WAAW,aAAa,OAAO"}
1
+ {"version":3,"file":"md.bun.d.mts","names":[],"sources":["../src/runtime/md.bun.ts"],"mappings":";;;;;;;;;wBAYgB,eAAe,eAAe,WAAW,aAAa,OAAO"}
package/dist/md.bun.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { n as parseCodeInfoString, t as encodeFenceInfoStrings } from "./utils-3M4D_AXZ.mjs";
2
- //#region src/runtime/md.bun.ts
1
+ import { n as parseCodeInfoString, t as encodeFenceInfoStrings } from "./utils-DHXAegVW.mjs";
2
+ // src/runtime/md.bun.ts
3
3
  /**
4
4
  * Bun wrapper for `renderMarkdown`. `Bun.markdown.render` truncates code
5
5
  * fence info-strings at the first whitespace, so attrs like
@@ -22,7 +22,6 @@ function decodeCodeMeta(meta) {
22
22
  if (meta.language === void 0) return meta;
23
23
  return parseCodeInfoString(meta.language) ?? meta;
24
24
  }
25
- //#endregion
26
25
  export { renderMarkdown };
27
26
 
28
27
  //# sourceMappingURL=md.bun.mjs.map
@@ -1,5 +1,4 @@
1
- import { n as MdOptions, t as MdCallbacks } from "./types-D3jk1rtk.mjs";
2
- //#region src/markdown/marked.d.ts
1
+ import { n as MdOptions, t as MdCallbacks } from "./types-CUVUJPz6.mjs";
3
2
  /**
4
3
  * Walk the markdown source with `marked`'s lexer and invoke the caller's
5
4
  * callbacks for each block / inline element. Output shape matches
@@ -9,5 +8,4 @@ import { n as MdOptions, t as MdCallbacks } from "./types-D3jk1rtk.mjs";
9
8
  * @internal
10
9
  */
11
10
  export declare function renderMarkdown(input: string, callbacks: MdCallbacks, opts?: MdOptions): string;
12
- //#endregion
13
11
  //# sourceMappingURL=md.node.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"md.node.d.mts","names":[],"sources":["../src/markdown/marked.ts"],"mappings":";;;;;;;;;;wBAuBgB,eAAe,eAAe,WAAW,aAAa,OAAO"}
1
+ {"version":3,"file":"md.node.d.mts","names":[],"sources":["../src/markdown/marked.ts"],"mappings":";;;;;;;;;wBAuBgB,eAAe,eAAe,WAAW,aAAa,OAAO"}
package/dist/md.node.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { n as parseCodeInfoString } from "./utils-3M4D_AXZ.mjs";
1
+ import { n as parseCodeInfoString } from "./utils-DHXAegVW.mjs";
2
2
  import { marked } from "marked";
3
- //#region src/markdown/marked.ts
3
+ // src/markdown/marked.ts
4
4
  /**
5
5
  * Walk the markdown source with `marked`'s lexer and invoke the caller's
6
6
  * callbacks for each block / inline element. Output shape matches
@@ -120,7 +120,6 @@ function applyText(text, cb) {
120
120
  if (cb.text === void 0) return text;
121
121
  return cb.text(text) ?? "";
122
122
  }
123
- //#endregion
124
123
  export { renderMarkdown };
125
124
 
126
125
  //# sourceMappingURL=md.node.mjs.map
@@ -1,6 +1,6 @@
1
- import { S as unwrap, T as withActiveNode, p as inRenderContextOf, u as createStore } from "./reactive-f7NpAi69.mjs";
1
+ import { S as unwrap, T as withActiveNode, p as inRenderContextOf, u as createStore } from "./reactive-DkP75TPk.mjs";
2
2
  import { Emitter } from "@zaly/shared";
3
- //#region src/core/node.ts
3
+ // src/core/node.ts
4
4
  /**
5
5
  * Public Node handle. Calling `render(ctx)` returns the node's rows from the
6
6
  * per-node cache, recomputing only if state read inside `_render` has changed
@@ -310,7 +310,6 @@ var Node = class extends Emitter {
310
310
  return this.visible && (this.parent ? this.parent.isVisible() : true);
311
311
  }
312
312
  };
313
- //#endregion
314
313
  export { Node as t };
315
314
 
316
- //# sourceMappingURL=node-BZ7f6HzU.mjs.map
315
+ //# sourceMappingURL=node-CXNobGaP.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"node-BZ7f6HzU.mjs","names":[],"sources":["../src/core/node.ts"],"sourcesContent":["import type { MaybePromise } from \"@zaly/shared\"\nimport type { TryResult } from \"@zaly/shared/logger\"\nimport type { ActionCtx, ActionDef, NodeActionMap } from \"../input/actions.ts\"\nimport type { RoutedKey, RoutedPaste } from \"../input/router.ts\"\nimport type { SurfaceType } from \"../renderer/renderer.ts\"\nimport type { MountCtx, RenderCtx } from \"./ctx.ts\"\nimport type { Ref, SignalStore } from \"./reactive.ts\"\nimport type { Layout, State } from \"./state.ts\"\n\nimport { Emitter } from \"@zaly/shared\"\nimport { createStore, inRenderContextOf, unwrap, withActiveNode } from \"./reactive.ts\"\n\n/** Minimum event map every node carries. Custom event maps intersect\n * this with their own events via `&`. */\nexport type BaseEvents = {\n invalidate: {}\n mount: {}\n unmount: {}\n focus: {}\n blur: {}\n show: {}\n hide: {}\n key: { key: RoutedKey }\n paste: { paste: RoutedPaste }\n childadded: { child: Node }\n childremoved: { child: Node }\n}\n\n/**\n * Public Node handle. Calling `render(ctx)` returns the node's rows from the\n * per-node cache, recomputing only if state read inside `_render` has changed\n * since the last call.\n *\n * Concrete subclasses implement the protected `_render(ctx)` hook; the public\n * `render` is the caching wrapper around it.\n *\n * State is a `createStore` proxy: reads inside `_render` track per-field;\n * mutating a read field invalidates this node. Three write forms:\n *\n * - `n.state.field = value` (direct assignment)\n * - `n.state.set({ field: value, ... })` (patch — partial merge)\n * - `n.state.set(current => ({ ... }))` (updater — receives untracked current)\n *\n * Writes through nested objects/arrays (`n.state.padding[0] = 1`) do NOT —\n * reassign the whole field instead.\n */\nexport abstract class Node<T extends object = object, E extends {} = {}> extends Emitter<\n BaseEvents,\n E\n> {\n #cache?: { rows: string[]; version: number; width: number }\n #parent?: Node\n #rendering: Promise<string[]> | undefined\n /** Bumped on every `invalidate()`. Captured at the start of a render\n * and re-checked after `_render` resolves: if the count moved, an\n * external mutation landed during the render and the rows we just\n * produced reflect pre-mutation state. We skip writing the cache in\n * that case so the surface's already-scheduled re-paint sees a\n * cache miss and re-renders against the latest state. */\n #invalidations = 0\n readonly #children: Node[] = []\n #state: SignalStore<T>\n #ctx?: MountCtx\n #id?: string\n #actionTargets = new Set<Node>()\n #mountAc?: AbortController\n actions?: NodeActionMap\n type?: string\n protected layout?(ctx: RenderCtx): MaybePromise<Layout | undefined>\n\n constructor(state: State<T>, ...children: Node[]) {\n super()\n this.onEmitError = (err) => {\n const logger = this.logger\n if (logger) logger.error(err)\n // oxlint-disable-next-line no-console\n else console.error(\"Uncaught node event error (node is unmounted, no ctx):\", err)\n }\n // `createStore` gives us per-field reactivity for free: reads\n // inside `_render` subscribe via the rendering Node's tracking\n // ctx, so mutating a read field invalidates exactly this node.\n // Fields nobody reads never allocate a signal — mutation is just\n // a state update with no cascade. Finer-grained than the old\n // \"any write invalidates everything\" Proxy.\n this.#state = createStore(state)\n children.forEach((c) => this.add(c))\n }\n\n withEvents<Events extends {}>(): this & Emitter<Events> {\n return this as unknown as this & Emitter<Events>\n }\n\n withActions<A extends NodeActionMap>(actions: A): this & { actions: A } {\n this.actions = { ...this.actions, ...actions }\n return this as this & { actions: A }\n }\n\n action(id: keyof NonNullable<this[\"actions\"]> & string, opts?: Partial<ActionCtx>): boolean {\n const entry = this.actions?.[id]\n const fn = typeof entry === \"function\" ? entry : entry?.fn\n if (!fn) return false\n fn({ source: \"programmatic\", target: this, ...opts, id, node: this })\n return true\n }\n\n get mountSignal(): AbortSignal {\n this.#mountAc ??= new AbortController()\n return this.#mountAc.signal\n }\n\n get state(): SignalStore<T> & State<T> {\n return this.#state\n }\n\n get children(): readonly Node[] {\n return this.#children\n }\n\n protected get logger() {\n return this.#ctx?.logger.child({\n id: this.#id,\n name: \"node\",\n node: this.type ?? \"unknown\",\n })\n }\n\n protected try<R>(fn: () => R): TryResult<R> {\n const logger = this.logger\n if (!logger) return fn() as TryResult<R>\n return logger.try(fn) as TryResult<R>\n }\n\n /** Fragment protocol — opt-in. When defined, layout containers\n * (Box's row/column allocator) treat this node as a transparent\n * fragment and substitute the returned children for `this` during\n * flex allocation and rendering. The fragment Node still exists in\n * the tree (it's mounted; it bubbles invalidates from its\n * descendants), but layout sees through it.\n *\n * Used by `show`, `errorBoundary`, `suspense`: their children carry\n * their own flex props through to the parent box, can share its\n * `gap`, and a row containing a `show` with multiple flex children\n * allocates per-leaf-slot rather than collapsing into one. */\n protected layoutChildren?(): readonly Node[]\n\n // Make parent readonly\n get parent(): Node | undefined {\n return this.#parent\n }\n\n /** Mount context when this node is attached to a surface — surface\n * identifier plus scoped handles to router / overlay / tree lookups.\n * `undefined` before mount and after unmount. Exposed so widget\n * authors can reach renderer services without closing over a ref. */\n get ctx(): MountCtx | undefined {\n return this.#ctx\n }\n\n get surface(): SurfaceType | undefined {\n return this.#ctx?.surface\n }\n\n get mounted(): boolean {\n return this.#ctx !== undefined\n }\n\n /**\n * Read or set the node's `id`. Called without args, returns the\n * current id. Called with a string, sets it and returns `this` for\n * chaining — useful when building trees inline:\n *\n * ```ts\n * input({...}).id(\"chat-input\").focus().on(\"submit\", ...)\n * ```\n *\n * The id is used by `ctx.getNode(id)` / `Renderer.getNode(id)` for\n * tree lookups, and by the input router as a per-instance scope for\n * keymap bindings (alongside the class-level `type`).\n */\n id(): string | undefined\n id(value: string): this\n id(value?: string): string | undefined | this {\n if (value === undefined) return this.#id\n this.#id = value\n return this\n }\n\n async getLayout(ctx: RenderCtx): Promise<Layout | undefined> {\n if (!this.layout) return\n return this.layout(ctx)\n }\n\n get layoutNodes(): readonly Node[] {\n if (this.layoutChildren === undefined) return [this]\n const ret: Node[] = []\n for (const c of this.with(() => this.layoutChildren?.()) ?? []) ret.push(...c.layoutNodes)\n return ret\n }\n\n addActionTarget(node: Node): this {\n this.#actionTargets.add(node)\n return this\n }\n\n removeActionTarget(node: Node): this {\n this.#actionTargets.delete(node)\n return this\n }\n\n get actionTargets(): Iterable<Node> {\n return this.#actionTargets\n }\n\n invalidate(): this {\n this.#cache = undefined\n // Suppress only when this invalidate originates from *inside this\n // node's own render call stack* — e.g. Markdown mutating its child\n // Text inside its own `_render`. The active render observes the\n // mutation as part of its own logic and produces rows that already\n // reflect it, so we skip the cascade *and* the generation bump:\n // the cache writeback at the end of the render is valid.\n //\n // External mutations (network callbacks, event handlers) and\n // mutations cascading up from a *deeper* render's own call stack\n // (e.g. Text._render mutating something that bubbles to Markdown\n // mid-Markdown-render) run outside *this* node's ALS scope, so\n // `inRenderContextOf(this)` returns `false` and they emit + bump\n // normally — the in-flight render's rows are stale, the cache\n // writeback gets skipped, and the surface schedules a fresh paint.\n if (inRenderContextOf(this)) return this\n this.#invalidations++\n // Always emit and always cascade — surfaces (stream, UI) dedupe\n // via their own `scheduled` flag, and the parent's cache always\n // includes whatever rows we returned last render, so a child\n // mutation always implies a stale parent cache. (We previously\n // skipped the cascade when `hadCache` was false, which was buggy:\n // a node that intentionally doesn't cache — e.g. one whose\n // `_render` mutates a child mid-render and skips the writeback —\n // would silently fail to dirty its parent on the next mutation,\n // pinning the surface to the first render's output.)\n void this.emit(\"invalidate\")\n this.parent?.invalidate()\n return this\n }\n\n with<R>(fn: () => R): R {\n return withActiveNode(this, () => fn())\n }\n\n async render(ctx: RenderCtx): Promise<string[]> {\n // `visible: false` on the state suppresses the render entirely —\n // no `_render` call, no cached rows, zero layout footprint. Opt-in\n // via `state.visible`; absence or `true` is the default shown path.\n // Resolve inside the tracking ctx so a signal accessor read\n // subscribes this node. Async work registered via `createAsync` is\n // fire-and-forget here; surfaces that need a \"wait until settled\"\n // commit (Stream) install a `SuspenseContext` boundary at their\n // append boundary and await it before painting.\n this.#rendering ??= this.with(() => this.#render(ctx))\n\n try {\n return await this.#rendering\n } finally {\n this.#rendering = undefined\n }\n }\n\n async #render(ctx: RenderCtx): Promise<string[]> {\n // Cache the hidden result too, so a later `invalidate()` sees\n // `hadCache === true` and cascades up — otherwise a toggleable\n // panel (autocomplete, modal) whose first paint happened while\n // hidden would swallow the flip-to-visible invalidate.\n if (!(unwrap(this.state.visible) ?? true)) {\n this.#cache = { rows: [], version: ctx.version, width: ctx.width }\n return this.#cache.rows\n }\n // Cache key includes `ctx.width` because rendered output is\n // width-dependent. `version` alone catches state/theme mutations\n // (top-level bumps version on theme change), but a parent passing\n // a different `width` to the same node — common in flex\n // measure-then-allocate passes — needs a fresh render.\n if (this.#cache?.version === ctx.version && this.#cache.width === ctx.width) {\n return this.#cache.rows\n }\n // Capture the invalidation count *before* awaiting `_render`. If\n // it bumps mid-render, the rows we get back are based on stale\n // state — the external mutation that bumped it has already emitted\n // and re-scheduled the surface, so skip caching. The re-paint will\n // run a fresh `_render` against the latest state.\n const stamp = this.#invalidations\n try {\n const rows = await this._render(ctx)\n if (this.#invalidations === stamp) {\n this.#cache = { rows, version: ctx.version, width: ctx.width }\n }\n return rows\n } catch (error) {\n this.logger?.error(error)\n // Don't cache the error result — a later invalidate should\n // re-render and give the user a chance to recover.\n this.#cache = undefined\n return [\n ctx.style.error(\n `[Node render error: ${error instanceof Error ? error.message : String(error)}]`\n ),\n ]\n }\n }\n\n protected abstract _render(ctx: RenderCtx): Promise<string[]> | string[]\n\n add(child: Node): this {\n this.splice(this.#children.length, 0, child)\n return this\n }\n\n remove(child: Node): this {\n const i = this.#children.indexOf(child)\n if (i === -1) return this\n this.splice(i, 1)\n return this\n }\n\n splice(start: number, deleteCount: number, ...items: Node[]): this {\n // Reject self-insertion — a node in its own children list would\n // create a cycle and stack-overflow on traversal.\n const filtered: Node[] = []\n for (const c of items) {\n if (c === this) continue\n filtered.push(c)\n }\n\n // If any item is already a child of this container, pull it out\n // first so we don't end up with duplicates. Adjust `start` when a\n // removal falls before it so the caller-relative index is preserved.\n let adjustedStart = Math.max(0, Math.min(start, this.#children.length))\n for (const c of filtered) {\n if (c.parent !== this) continue\n const i = this.#children.indexOf(c)\n if (i === -1) continue\n this.#children.splice(i, 1)\n if (i < adjustedStart) adjustedStart--\n }\n\n const removed = this.#children.splice(adjustedStart, deleteCount, ...filtered)\n for (const c of removed) {\n void this.emit(\"childremoved\", { child: c })\n if (c.mounted) c.unmount()\n if (c.parent === this) c.#parent = undefined\n }\n for (const c of filtered) {\n // Detach from old parent before rewriting `#parent`, so the old\n // parent's `remove()` path observes a consistent `c.parent === old`\n // and cleans up its own bookkeeping.\n if (c.parent && c.parent !== this) {\n c.unmount()\n c.parent.remove(c)\n }\n c.#parent = this\n void this.emit(\"childadded\", { child: c })\n if (this.#ctx !== undefined) c.mount(this.#ctx)\n }\n this.invalidate()\n return this\n }\n\n clear(): this {\n this.splice(0, this.#children.length)\n return this\n }\n\n mount(ctx: MountCtx): this {\n if (this.#ctx?.surface === ctx.surface) return this\n if (this.#ctx) {\n throw new Error(\n `Node is already mounted on \"${this.#ctx.surface}\" (requested \"${ctx.surface}\"). Unmount first if you meant to move it.`\n )\n }\n // Make sure state.visible is tracked and will trigger invalidate\n withActiveNode(this, () => unwrap(this.state.visible))\n this.#ctx = ctx\n this.#registerActionMeta(ctx)\n void this.emit(\"mount\")\n for (const c of this.#children) c.mount(ctx)\n return this\n }\n\n /** Contribute any `ActionInfo`-shaped entries in `this.actions` to\n * the catalog. The instance `fn` stays on the node (dispatch finds\n * it via the focus-chain walk); only metadata lands in the registry.\n * Uses `extend: false` so defaults don't clobber user overrides\n * that were registered before the widget mounted. */\n #registerActionMeta(ctx: MountCtx): void {\n if (!this.actions) return\n const metas: Record<string, ActionDef> = {}\n let any = false\n for (const [id, entry] of Object.entries(this.actions)) {\n if (typeof entry === \"function\") continue\n const { fn: _fn, ...info } = entry\n metas[id] = info\n any = true\n }\n if (any) ctx.actions.register(metas, { default: true })\n }\n\n unmount(): this {\n if (!this.#ctx) return this\n this.#mountAc?.abort()\n this.#mountAc = undefined\n for (const c of this.#children) c.unmount()\n void this.emit(\"unmount\")\n this.#ctx.input.blur(this)\n this.#ctx = undefined\n return this\n }\n\n /** Become the focused node. Routes through the MountCtx, which emits\n * `blur` on the previous focus and `focus` on this node.\n *\n * When called before the node is mounted (the common case for\n * `input({ focus: true }).on(...)` style setup), the focus is\n * deferred until the next `mount` event. That keeps the API\n * synchronous and side-effect-free at build time — callers don't\n * have to think about ordering. */\n focus(): this {\n if (!this.#ctx) {\n this.once(\"mount\", () => this.focus())\n return this\n }\n this.#ctx.input.focus(this)\n return this\n }\n\n /** Release focus. No-op when not mounted. */\n blur(): this {\n this.#ctx?.input.blur(this)\n return this\n }\n\n get visible() {\n return unwrap(this.state.visible) ?? true\n }\n\n show(): this {\n if (this.visible) return this\n this.state.visible = true\n void this.emit(\"show\")\n return this\n }\n\n hide(): this {\n if (!this.visible) return this\n this.state.visible = false\n void this.emit(\"hide\")\n return this\n }\n\n toggle(): this {\n this.state.visible = !this.visible\n return this\n }\n\n ref<N extends Node>(ref?: Ref<this extends N ? N : never>): this {\n if (ref) ref.value = this as unknown as Exclude<typeof ref.value, undefined>\n return this\n }\n\n isVisible(): boolean {\n return this.visible && (this.parent ? this.parent.isVisible() : true)\n }\n}\n\n/** Runtime type guard for Node.\n *\n * @internal*/\nexport function isNode(x: unknown): x is Node {\n return x instanceof Node\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"node-CXNobGaP.mjs","names":[],"sources":["../src/core/node.ts"],"sourcesContent":["import type { MaybePromise } from \"@zaly/shared\"\nimport type { TryResult } from \"@zaly/shared/logger\"\nimport type { ActionCtx, ActionDef, NodeActionMap } from \"../input/actions.ts\"\nimport type { RoutedKey, RoutedPaste } from \"../input/router.ts\"\nimport type { SurfaceType } from \"../renderer/renderer.ts\"\nimport type { MountCtx, RenderCtx } from \"./ctx.ts\"\nimport type { Ref, SignalStore } from \"./reactive.ts\"\nimport type { Layout, State } from \"./state.ts\"\n\nimport { Emitter } from \"@zaly/shared\"\nimport { createStore, inRenderContextOf, unwrap, withActiveNode } from \"./reactive.ts\"\n\n/** Minimum event map every node carries. Custom event maps intersect\n * this with their own events via `&`. */\nexport type BaseEvents = {\n invalidate: {}\n mount: {}\n unmount: {}\n focus: {}\n blur: {}\n show: {}\n hide: {}\n key: { key: RoutedKey }\n paste: { paste: RoutedPaste }\n childadded: { child: Node }\n childremoved: { child: Node }\n}\n\n/**\n * Public Node handle. Calling `render(ctx)` returns the node's rows from the\n * per-node cache, recomputing only if state read inside `_render` has changed\n * since the last call.\n *\n * Concrete subclasses implement the protected `_render(ctx)` hook; the public\n * `render` is the caching wrapper around it.\n *\n * State is a `createStore` proxy: reads inside `_render` track per-field;\n * mutating a read field invalidates this node. Three write forms:\n *\n * - `n.state.field = value` (direct assignment)\n * - `n.state.set({ field: value, ... })` (patch — partial merge)\n * - `n.state.set(current => ({ ... }))` (updater — receives untracked current)\n *\n * Writes through nested objects/arrays (`n.state.padding[0] = 1`) do NOT —\n * reassign the whole field instead.\n */\nexport abstract class Node<T extends object = object, E extends {} = {}> extends Emitter<\n BaseEvents,\n E\n> {\n #cache?: { rows: string[]; version: number; width: number }\n #parent?: Node\n #rendering: Promise<string[]> | undefined\n /** Bumped on every `invalidate()`. Captured at the start of a render\n * and re-checked after `_render` resolves: if the count moved, an\n * external mutation landed during the render and the rows we just\n * produced reflect pre-mutation state. We skip writing the cache in\n * that case so the surface's already-scheduled re-paint sees a\n * cache miss and re-renders against the latest state. */\n #invalidations = 0\n readonly #children: Node[] = []\n #state: SignalStore<T>\n #ctx?: MountCtx\n #id?: string\n #actionTargets = new Set<Node>()\n #mountAc?: AbortController\n actions?: NodeActionMap\n type?: string\n protected layout?(ctx: RenderCtx): MaybePromise<Layout | undefined>\n\n constructor(state: State<T>, ...children: Node[]) {\n super()\n this.onEmitError = (err) => {\n const logger = this.logger\n if (logger) logger.error(err)\n // oxlint-disable-next-line no-console\n else console.error(\"Uncaught node event error (node is unmounted, no ctx):\", err)\n }\n // `createStore` gives us per-field reactivity for free: reads\n // inside `_render` subscribe via the rendering Node's tracking\n // ctx, so mutating a read field invalidates exactly this node.\n // Fields nobody reads never allocate a signal — mutation is just\n // a state update with no cascade. Finer-grained than the old\n // \"any write invalidates everything\" Proxy.\n this.#state = createStore(state)\n children.forEach((c) => this.add(c))\n }\n\n withEvents<Events extends {}>(): this & Emitter<Events> {\n return this as unknown as this & Emitter<Events>\n }\n\n withActions<A extends NodeActionMap>(actions: A): this & { actions: A } {\n this.actions = { ...this.actions, ...actions }\n return this as this & { actions: A }\n }\n\n action(id: keyof NonNullable<this[\"actions\"]> & string, opts?: Partial<ActionCtx>): boolean {\n const entry = this.actions?.[id]\n const fn = typeof entry === \"function\" ? entry : entry?.fn\n if (!fn) return false\n fn({ source: \"programmatic\", target: this, ...opts, id, node: this })\n return true\n }\n\n get mountSignal(): AbortSignal {\n this.#mountAc ??= new AbortController()\n return this.#mountAc.signal\n }\n\n get state(): SignalStore<T> & State<T> {\n return this.#state\n }\n\n get children(): readonly Node[] {\n return this.#children\n }\n\n protected get logger() {\n return this.#ctx?.logger.child({\n id: this.#id,\n name: \"node\",\n node: this.type ?? \"unknown\",\n })\n }\n\n protected try<R>(fn: () => R): TryResult<R> {\n const logger = this.logger\n if (!logger) return fn() as TryResult<R>\n return logger.try(fn) as TryResult<R>\n }\n\n /** Fragment protocol — opt-in. When defined, layout containers\n * (Box's row/column allocator) treat this node as a transparent\n * fragment and substitute the returned children for `this` during\n * flex allocation and rendering. The fragment Node still exists in\n * the tree (it's mounted; it bubbles invalidates from its\n * descendants), but layout sees through it.\n *\n * Used by `show`, `errorBoundary`, `suspense`: their children carry\n * their own flex props through to the parent box, can share its\n * `gap`, and a row containing a `show` with multiple flex children\n * allocates per-leaf-slot rather than collapsing into one. */\n protected layoutChildren?(): readonly Node[]\n\n // Make parent readonly\n get parent(): Node | undefined {\n return this.#parent\n }\n\n /** Mount context when this node is attached to a surface — surface\n * identifier plus scoped handles to router / overlay / tree lookups.\n * `undefined` before mount and after unmount. Exposed so widget\n * authors can reach renderer services without closing over a ref. */\n get ctx(): MountCtx | undefined {\n return this.#ctx\n }\n\n get surface(): SurfaceType | undefined {\n return this.#ctx?.surface\n }\n\n get mounted(): boolean {\n return this.#ctx !== undefined\n }\n\n /**\n * Read or set the node's `id`. Called without args, returns the\n * current id. Called with a string, sets it and returns `this` for\n * chaining — useful when building trees inline:\n *\n * ```ts\n * input({...}).id(\"chat-input\").focus().on(\"submit\", ...)\n * ```\n *\n * The id is used by `ctx.getNode(id)` / `Renderer.getNode(id)` for\n * tree lookups, and by the input router as a per-instance scope for\n * keymap bindings (alongside the class-level `type`).\n */\n id(): string | undefined\n id(value: string): this\n id(value?: string): string | undefined | this {\n if (value === undefined) return this.#id\n this.#id = value\n return this\n }\n\n async getLayout(ctx: RenderCtx): Promise<Layout | undefined> {\n if (!this.layout) return\n return this.layout(ctx)\n }\n\n get layoutNodes(): readonly Node[] {\n if (this.layoutChildren === undefined) return [this]\n const ret: Node[] = []\n for (const c of this.with(() => this.layoutChildren?.()) ?? []) ret.push(...c.layoutNodes)\n return ret\n }\n\n addActionTarget(node: Node): this {\n this.#actionTargets.add(node)\n return this\n }\n\n removeActionTarget(node: Node): this {\n this.#actionTargets.delete(node)\n return this\n }\n\n get actionTargets(): Iterable<Node> {\n return this.#actionTargets\n }\n\n invalidate(): this {\n this.#cache = undefined\n // Suppress only when this invalidate originates from *inside this\n // node's own render call stack* — e.g. Markdown mutating its child\n // Text inside its own `_render`. The active render observes the\n // mutation as part of its own logic and produces rows that already\n // reflect it, so we skip the cascade *and* the generation bump:\n // the cache writeback at the end of the render is valid.\n //\n // External mutations (network callbacks, event handlers) and\n // mutations cascading up from a *deeper* render's own call stack\n // (e.g. Text._render mutating something that bubbles to Markdown\n // mid-Markdown-render) run outside *this* node's ALS scope, so\n // `inRenderContextOf(this)` returns `false` and they emit + bump\n // normally — the in-flight render's rows are stale, the cache\n // writeback gets skipped, and the surface schedules a fresh paint.\n if (inRenderContextOf(this)) return this\n this.#invalidations++\n // Always emit and always cascade — surfaces (stream, UI) dedupe\n // via their own `scheduled` flag, and the parent's cache always\n // includes whatever rows we returned last render, so a child\n // mutation always implies a stale parent cache. (We previously\n // skipped the cascade when `hadCache` was false, which was buggy:\n // a node that intentionally doesn't cache — e.g. one whose\n // `_render` mutates a child mid-render and skips the writeback —\n // would silently fail to dirty its parent on the next mutation,\n // pinning the surface to the first render's output.)\n void this.emit(\"invalidate\")\n this.parent?.invalidate()\n return this\n }\n\n with<R>(fn: () => R): R {\n return withActiveNode(this, () => fn())\n }\n\n async render(ctx: RenderCtx): Promise<string[]> {\n // `visible: false` on the state suppresses the render entirely —\n // no `_render` call, no cached rows, zero layout footprint. Opt-in\n // via `state.visible`; absence or `true` is the default shown path.\n // Resolve inside the tracking ctx so a signal accessor read\n // subscribes this node. Async work registered via `createAsync` is\n // fire-and-forget here; surfaces that need a \"wait until settled\"\n // commit (Stream) install a `SuspenseContext` boundary at their\n // append boundary and await it before painting.\n this.#rendering ??= this.with(() => this.#render(ctx))\n\n try {\n return await this.#rendering\n } finally {\n this.#rendering = undefined\n }\n }\n\n async #render(ctx: RenderCtx): Promise<string[]> {\n // Cache the hidden result too, so a later `invalidate()` sees\n // `hadCache === true` and cascades up — otherwise a toggleable\n // panel (autocomplete, modal) whose first paint happened while\n // hidden would swallow the flip-to-visible invalidate.\n if (!(unwrap(this.state.visible) ?? true)) {\n this.#cache = { rows: [], version: ctx.version, width: ctx.width }\n return this.#cache.rows\n }\n // Cache key includes `ctx.width` because rendered output is\n // width-dependent. `version` alone catches state/theme mutations\n // (top-level bumps version on theme change), but a parent passing\n // a different `width` to the same node — common in flex\n // measure-then-allocate passes — needs a fresh render.\n if (this.#cache?.version === ctx.version && this.#cache.width === ctx.width) {\n return this.#cache.rows\n }\n // Capture the invalidation count *before* awaiting `_render`. If\n // it bumps mid-render, the rows we get back are based on stale\n // state — the external mutation that bumped it has already emitted\n // and re-scheduled the surface, so skip caching. The re-paint will\n // run a fresh `_render` against the latest state.\n const stamp = this.#invalidations\n try {\n const rows = await this._render(ctx)\n if (this.#invalidations === stamp) {\n this.#cache = { rows, version: ctx.version, width: ctx.width }\n }\n return rows\n } catch (error) {\n this.logger?.error(error)\n // Don't cache the error result — a later invalidate should\n // re-render and give the user a chance to recover.\n this.#cache = undefined\n return [\n ctx.style.error(\n `[Node render error: ${error instanceof Error ? error.message : String(error)}]`\n ),\n ]\n }\n }\n\n protected abstract _render(ctx: RenderCtx): Promise<string[]> | string[]\n\n add(child: Node): this {\n this.splice(this.#children.length, 0, child)\n return this\n }\n\n remove(child: Node): this {\n const i = this.#children.indexOf(child)\n if (i === -1) return this\n this.splice(i, 1)\n return this\n }\n\n splice(start: number, deleteCount: number, ...items: Node[]): this {\n // Reject self-insertion — a node in its own children list would\n // create a cycle and stack-overflow on traversal.\n const filtered: Node[] = []\n for (const c of items) {\n if (c === this) continue\n filtered.push(c)\n }\n\n // If any item is already a child of this container, pull it out\n // first so we don't end up with duplicates. Adjust `start` when a\n // removal falls before it so the caller-relative index is preserved.\n let adjustedStart = Math.max(0, Math.min(start, this.#children.length))\n for (const c of filtered) {\n if (c.parent !== this) continue\n const i = this.#children.indexOf(c)\n if (i === -1) continue\n this.#children.splice(i, 1)\n if (i < adjustedStart) adjustedStart--\n }\n\n const removed = this.#children.splice(adjustedStart, deleteCount, ...filtered)\n for (const c of removed) {\n void this.emit(\"childremoved\", { child: c })\n if (c.mounted) c.unmount()\n if (c.parent === this) c.#parent = undefined\n }\n for (const c of filtered) {\n // Detach from old parent before rewriting `#parent`, so the old\n // parent's `remove()` path observes a consistent `c.parent === old`\n // and cleans up its own bookkeeping.\n if (c.parent && c.parent !== this) {\n c.unmount()\n c.parent.remove(c)\n }\n c.#parent = this\n void this.emit(\"childadded\", { child: c })\n if (this.#ctx !== undefined) c.mount(this.#ctx)\n }\n this.invalidate()\n return this\n }\n\n clear(): this {\n this.splice(0, this.#children.length)\n return this\n }\n\n mount(ctx: MountCtx): this {\n if (this.#ctx?.surface === ctx.surface) return this\n if (this.#ctx) {\n throw new Error(\n `Node is already mounted on \"${this.#ctx.surface}\" (requested \"${ctx.surface}\"). Unmount first if you meant to move it.`\n )\n }\n // Make sure state.visible is tracked and will trigger invalidate\n withActiveNode(this, () => unwrap(this.state.visible))\n this.#ctx = ctx\n this.#registerActionMeta(ctx)\n void this.emit(\"mount\")\n for (const c of this.#children) c.mount(ctx)\n return this\n }\n\n /** Contribute any `ActionInfo`-shaped entries in `this.actions` to\n * the catalog. The instance `fn` stays on the node (dispatch finds\n * it via the focus-chain walk); only metadata lands in the registry.\n * Uses `extend: false` so defaults don't clobber user overrides\n * that were registered before the widget mounted. */\n #registerActionMeta(ctx: MountCtx): void {\n if (!this.actions) return\n const metas: Record<string, ActionDef> = {}\n let any = false\n for (const [id, entry] of Object.entries(this.actions)) {\n if (typeof entry === \"function\") continue\n const { fn: _fn, ...info } = entry\n metas[id] = info\n any = true\n }\n if (any) ctx.actions.register(metas, { default: true })\n }\n\n unmount(): this {\n if (!this.#ctx) return this\n this.#mountAc?.abort()\n this.#mountAc = undefined\n for (const c of this.#children) c.unmount()\n void this.emit(\"unmount\")\n this.#ctx.input.blur(this)\n this.#ctx = undefined\n return this\n }\n\n /** Become the focused node. Routes through the MountCtx, which emits\n * `blur` on the previous focus and `focus` on this node.\n *\n * When called before the node is mounted (the common case for\n * `input({ focus: true }).on(...)` style setup), the focus is\n * deferred until the next `mount` event. That keeps the API\n * synchronous and side-effect-free at build time — callers don't\n * have to think about ordering. */\n focus(): this {\n if (!this.#ctx) {\n this.once(\"mount\", () => this.focus())\n return this\n }\n this.#ctx.input.focus(this)\n return this\n }\n\n /** Release focus. No-op when not mounted. */\n blur(): this {\n this.#ctx?.input.blur(this)\n return this\n }\n\n get visible() {\n return unwrap(this.state.visible) ?? true\n }\n\n show(): this {\n if (this.visible) return this\n this.state.visible = true\n void this.emit(\"show\")\n return this\n }\n\n hide(): this {\n if (!this.visible) return this\n this.state.visible = false\n void this.emit(\"hide\")\n return this\n }\n\n toggle(): this {\n this.state.visible = !this.visible\n return this\n }\n\n ref<N extends Node>(ref?: Ref<this extends N ? N : never>): this {\n if (ref) ref.value = this as unknown as Exclude<typeof ref.value, undefined>\n return this\n }\n\n isVisible(): boolean {\n return this.visible && (this.parent ? this.parent.isVisible() : true)\n }\n}\n\n/** Runtime type guard for Node.\n *\n * @internal*/\nexport function isNode(x: unknown): x is Node {\n return x instanceof Node\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
- import { i as toHex, r as parseHex } from "./color-DBMQLv4Y.mjs";
1
+ import { i as toHex, r as parseHex } from "./color-Cr1VoL49.mjs";
2
2
  import { clamp } from "@zaly/shared";
3
- //#region src/style/oklch.ts
3
+ // src/style/oklch.ts
4
4
  const cache = /* @__PURE__ */ new Map();
5
5
  function srgbToLinear(c) {
6
6
  return c <= .04045 ? c / 12.92 : ((c + .055) / 1.055) ** 2.4;
@@ -93,7 +93,6 @@ function shiftLightness(hex, delta) {
93
93
  function isDark(hex) {
94
94
  return hexToOklch(hex).L < .6;
95
95
  }
96
- //#endregion
97
96
  export { shiftLightness as n, isDark as t };
98
97
 
99
- //# sourceMappingURL=oklch-Bsh5swbC.mjs.map
98
+ //# sourceMappingURL=oklch-Boh4YHEc.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"oklch-Bsh5swbC.mjs","names":[],"sources":["../src/style/oklch.ts"],"sourcesContent":["/**\n * OKLCH-based tonal scale generation for theme colors.\n *\n * Given a base hex color, `variant(hex, step)` returns a hex variant at\n * the Tailwind-v4-ish step (50..950). The base color's hue and chroma\n * are preserved; only lightness moves. When the resulting color falls\n * outside sRGB, chroma is reduced via binary search (CSS Color Level 4\n * \"MinDE chroma reduction\") until the color fits — that avoids the\n * saturation blowout you get from naive HSL lightening at the extremes.\n *\n * Matrix constants come from Björn Ottosson's reference OKLab spec:\n * https://bottosson.github.io/posts/oklab/\n */\n\nimport type { HexColor } from \"./types.ts\"\n\nimport { clamp } from \"@zaly/shared\"\nimport { parseHex, toHex } from \"./color.ts\"\n\nexport interface OKLCH {\n L: number\n C: number\n h: number\n}\n\nconst cache = new Map<string, HexColor>()\n\n// ---------- conversions ----------\n\nfunction srgbToLinear(c: number): number {\n return c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4\n}\n\nfunction linearToSrgb(c: number): number {\n return c <= 0.0031308 ? c * 12.92 : 1.055 * c ** (1 / 2.4) - 0.055\n}\n\nexport function hexToOklch(hex: HexColor): OKLCH {\n const [r8, g8, b8] = parseHex(hex)\n const r = srgbToLinear(r8 / 255)\n const g = srgbToLinear(g8 / 255)\n const b = srgbToLinear(b8 / 255)\n\n const l = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b\n const m = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b\n const s = 0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b\n const lp = Math.cbrt(l)\n const mp = Math.cbrt(m)\n const sp = Math.cbrt(s)\n const L = 0.2104542553 * lp + 0.793617785 * mp - 0.0040720468 * sp\n const a = 1.9779984951 * lp - 2.428592205 * mp + 0.4505937099 * sp\n const b2 = 0.0259040371 * lp + 0.7827717662 * mp - 0.808675766 * sp\n const C = Math.hypot(a, b2)\n const h = ((Math.atan2(b2, a) * 180) / Math.PI + 360) % 360\n return { C, L, h }\n}\n\n/** Convert OKLCH → linear sRGB (unclamped — callers can test for gamut). */\nfunction oklchToLinearRgb({ L, C, h }: OKLCH): [number, number, number] {\n const hr = (h * Math.PI) / 180\n const a = C * Math.cos(hr)\n const b2 = C * Math.sin(hr)\n const lp = L + 0.3963377774 * a + 0.2158037573 * b2\n const mp = L - 0.1055613458 * a - 0.0638541728 * b2\n const sp = L - 0.0894841775 * a - 1.291485548 * b2\n const l = lp ** 3\n const m = mp ** 3\n const s = sp ** 3\n const r = 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s\n const g = -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s\n const b = -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s\n return [r, g, b]\n}\n\nfunction inGamut([r, g, b]: [number, number, number], eps = 0.001): boolean {\n return r >= -eps && r <= 1 + eps && g >= -eps && g <= 1 + eps && b >= -eps && b <= 1 + eps\n}\n\n/**\n * Convert OKLCH → hex, reducing chroma via binary search when the\n * straightforward conversion falls outside sRGB. The search keeps `L`\n * and `h` fixed and shrinks `C` toward the achromatic axis until the\n * color fits, which is the CSS Color Level 4-recommended behavior.\n */\nexport function oklchToHex(oklch: OKLCH): HexColor {\n let rgb = oklchToLinearRgb(oklch)\n if (!inGamut(rgb)) {\n // Binary-search on C.\n let lo = 0\n let hi = oklch.C\n for (let i = 0; i < 24; i++) {\n const mid = (lo + hi) / 2\n if (inGamut(oklchToLinearRgb({ ...oklch, C: mid }))) lo = mid\n else hi = mid\n }\n rgb = oklchToLinearRgb({ ...oklch, C: lo })\n }\n const [r, g, b] = rgb.map((c) => linearToSrgb(Math.max(0, Math.min(1, c)))) as [\n number,\n number,\n number,\n ]\n return toHex(r * 255, g * 255, b * 255)\n}\n\nexport function shiftLightness(hex: HexColor, delta: number): HexColor {\n const key = `${hex}:${delta}`\n let ret = cache.get(key)\n if (ret) return ret\n\n const okl = hexToOklch(hex)\n const L = clamp(okl.L + delta, 0, 1)\n // Pull chroma toward 0 as L approaches an extreme. Keeps near-white\n // and near-black from going out of gamut.\n const margin = Math.min(L, 1 - L)\n const C = okl.C * Math.min(1, margin / 0.1)\n cache.set(key, (ret = oklchToHex({ ...okl, C, L })))\n return ret\n}\n\nexport function isDark(hex: HexColor): boolean {\n const oklch = hexToOklch(hex)\n return oklch.L < 0.6\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"oklch-Boh4YHEc.mjs","names":[],"sources":["../src/style/oklch.ts"],"sourcesContent":["/**\n * OKLCH-based tonal scale generation for theme colors.\n *\n * Given a base hex color, `variant(hex, step)` returns a hex variant at\n * the Tailwind-v4-ish step (50..950). The base color's hue and chroma\n * are preserved; only lightness moves. When the resulting color falls\n * outside sRGB, chroma is reduced via binary search (CSS Color Level 4\n * \"MinDE chroma reduction\") until the color fits — that avoids the\n * saturation blowout you get from naive HSL lightening at the extremes.\n *\n * Matrix constants come from Björn Ottosson's reference OKLab spec:\n * https://bottosson.github.io/posts/oklab/\n */\n\nimport type { HexColor } from \"./types.ts\"\n\nimport { clamp } from \"@zaly/shared\"\nimport { parseHex, toHex } from \"./color.ts\"\n\nexport interface OKLCH {\n L: number\n C: number\n h: number\n}\n\nconst cache = new Map<string, HexColor>()\n\n// ---------- conversions ----------\n\nfunction srgbToLinear(c: number): number {\n return c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4\n}\n\nfunction linearToSrgb(c: number): number {\n return c <= 0.0031308 ? c * 12.92 : 1.055 * c ** (1 / 2.4) - 0.055\n}\n\nexport function hexToOklch(hex: HexColor): OKLCH {\n const [r8, g8, b8] = parseHex(hex)\n const r = srgbToLinear(r8 / 255)\n const g = srgbToLinear(g8 / 255)\n const b = srgbToLinear(b8 / 255)\n\n const l = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b\n const m = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b\n const s = 0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b\n const lp = Math.cbrt(l)\n const mp = Math.cbrt(m)\n const sp = Math.cbrt(s)\n const L = 0.2104542553 * lp + 0.793617785 * mp - 0.0040720468 * sp\n const a = 1.9779984951 * lp - 2.428592205 * mp + 0.4505937099 * sp\n const b2 = 0.0259040371 * lp + 0.7827717662 * mp - 0.808675766 * sp\n const C = Math.hypot(a, b2)\n const h = ((Math.atan2(b2, a) * 180) / Math.PI + 360) % 360\n return { C, L, h }\n}\n\n/** Convert OKLCH → linear sRGB (unclamped — callers can test for gamut). */\nfunction oklchToLinearRgb({ L, C, h }: OKLCH): [number, number, number] {\n const hr = (h * Math.PI) / 180\n const a = C * Math.cos(hr)\n const b2 = C * Math.sin(hr)\n const lp = L + 0.3963377774 * a + 0.2158037573 * b2\n const mp = L - 0.1055613458 * a - 0.0638541728 * b2\n const sp = L - 0.0894841775 * a - 1.291485548 * b2\n const l = lp ** 3\n const m = mp ** 3\n const s = sp ** 3\n const r = 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s\n const g = -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s\n const b = -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s\n return [r, g, b]\n}\n\nfunction inGamut([r, g, b]: [number, number, number], eps = 0.001): boolean {\n return r >= -eps && r <= 1 + eps && g >= -eps && g <= 1 + eps && b >= -eps && b <= 1 + eps\n}\n\n/**\n * Convert OKLCH → hex, reducing chroma via binary search when the\n * straightforward conversion falls outside sRGB. The search keeps `L`\n * and `h` fixed and shrinks `C` toward the achromatic axis until the\n * color fits, which is the CSS Color Level 4-recommended behavior.\n */\nexport function oklchToHex(oklch: OKLCH): HexColor {\n let rgb = oklchToLinearRgb(oklch)\n if (!inGamut(rgb)) {\n // Binary-search on C.\n let lo = 0\n let hi = oklch.C\n for (let i = 0; i < 24; i++) {\n const mid = (lo + hi) / 2\n if (inGamut(oklchToLinearRgb({ ...oklch, C: mid }))) lo = mid\n else hi = mid\n }\n rgb = oklchToLinearRgb({ ...oklch, C: lo })\n }\n const [r, g, b] = rgb.map((c) => linearToSrgb(Math.max(0, Math.min(1, c)))) as [\n number,\n number,\n number,\n ]\n return toHex(r * 255, g * 255, b * 255)\n}\n\nexport function shiftLightness(hex: HexColor, delta: number): HexColor {\n const key = `${hex}:${delta}`\n let ret = cache.get(key)\n if (ret) return ret\n\n const okl = hexToOklch(hex)\n const L = clamp(okl.L + delta, 0, 1)\n // Pull chroma toward 0 as L approaches an extreme. Keeps near-white\n // and near-black from going out of gamut.\n const margin = Math.min(L, 1 - L)\n const C = okl.C * Math.min(1, margin / 0.1)\n cache.set(key, (ret = oklchToHex({ ...okl, C, L })))\n return ret\n}\n\nexport function isDark(hex: HexColor): boolean {\n const oklch = hexToOklch(hex)\n return oklch.L < 0.6\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import { n as box } from "./box-C_N_7ChP.mjs";
2
- //#region src/widgets/overlay.ts
1
+ import { n as box } from "./box-DToDoXlU.mjs";
2
+ // src/widgets/overlay.ts
3
3
  function overlay(state, ...children) {
4
4
  const node = box({
5
5
  visible: false,
@@ -12,7 +12,6 @@ function overlay(state, ...children) {
12
12
  } });
13
13
  return node;
14
14
  }
15
- //#endregion
16
15
  export { overlay as t };
17
16
 
18
- //# sourceMappingURL=overlay-LvOjG7kn.mjs.map
17
+ //# sourceMappingURL=overlay-CevDWgSF.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"overlay-LvOjG7kn.mjs","names":[],"sources":["../src/widgets/overlay.ts"],"sourcesContent":["import type { Emitter } from \"@zaly/shared\"\nimport type { Node } from \"../core/node.ts\"\nimport type { State } from \"../core/state.ts\"\nimport type { Box, BoxStyle } from \"./box.ts\"\n\nimport { box } from \"./box.ts\"\n\nexport interface OverlayState {\n /** Absolute column (1-based) of the overlay's left edge. */\n x: number\n /** Absolute row (1-based) of the overlay's top edge. */\n y: number\n /** Higher zIndex paints on top. Default: 0. */\n zIndex?: number\n verticalAnchor?: \"top\" | \"center\" | \"bottom\"\n horizontalAnchor?: \"left\" | \"center\" | \"right\"\n relative?: \"screen\" | \"ui\" | \"stream\"\n /** If true, the overlay will be automatically removed from the renderer when closed.\n * When false, the overlay will remain in the renderer but will be hidden.\n * Default: true. */\n removeOnClose?: boolean\n}\n\ntype OverlayEvents = {\n close: {}\n}\n\nexport type Overlay<T extends Node[] = Node[]> = Box<OverlayState> & {\n children: T\n} & Emitter<OverlayEvents>\n\nexport function overlay<T extends Node[] = Node[]>(\n state: State<OverlayState & BoxStyle>,\n ...children: T\n): Overlay<T> {\n const node = box({ visible: false, ...state }, ...children) as Overlay<T>\n node.withActions({\n \"overlay.close\": {\n desc: \"Close overlay\",\n fn: () => ((node.state.removeOnClose ?? true) ? node.ctx?.overlay.remove(node) : node.hide()),\n keys: [\"esc\", \"ctrl-c\"],\n },\n })\n return node\n}\n"],"mappings":";;AA+BA,SAAgB,QACd,OACA,GAAG,UACS;CACZ,MAAM,OAAO,IAAI;EAAE,SAAS;EAAO,GAAG;CAAM,GAAG,GAAG,QAAQ;CAC1D,KAAK,YAAY,EACf,iBAAiB;EACf,MAAM;EACN,UAAY,KAAK,MAAM,iBAAiB,OAAQ,KAAK,KAAK,QAAQ,OAAO,IAAI,IAAI,KAAK,KAAK;EAC3F,MAAM,CAAC,OAAO,QAAQ;CACxB,EACF,CAAC;CACD,OAAO;AACT"}
1
+ {"version":3,"file":"overlay-CevDWgSF.mjs","names":[],"sources":["../src/widgets/overlay.ts"],"sourcesContent":["import type { Emitter } from \"@zaly/shared\"\nimport type { Node } from \"../core/node.ts\"\nimport type { State } from \"../core/state.ts\"\nimport type { Box, BoxStyle } from \"./box.ts\"\n\nimport { box } from \"./box.ts\"\n\nexport interface OverlayState {\n /** Absolute column (1-based) of the overlay's left edge. */\n x: number\n /** Absolute row (1-based) of the overlay's top edge. */\n y: number\n /** Higher zIndex paints on top. Default: 0. */\n zIndex?: number\n verticalAnchor?: \"top\" | \"center\" | \"bottom\"\n horizontalAnchor?: \"left\" | \"center\" | \"right\"\n relative?: \"screen\" | \"ui\" | \"stream\"\n /** If true, the overlay will be automatically removed from the renderer when closed.\n * When false, the overlay will remain in the renderer but will be hidden.\n * Default: true. */\n removeOnClose?: boolean\n}\n\ntype OverlayEvents = {\n close: {}\n}\n\nexport type Overlay<T extends Node[] = Node[]> = Box<OverlayState> & {\n children: T\n} & Emitter<OverlayEvents>\n\nexport function overlay<T extends Node[] = Node[]>(\n state: State<OverlayState & BoxStyle>,\n ...children: T\n): Overlay<T> {\n const node = box({ visible: false, ...state }, ...children) as Overlay<T>\n node.withActions({\n \"overlay.close\": {\n desc: \"Close overlay\",\n fn: () => ((node.state.removeOnClose ?? true) ? node.ctx?.overlay.remove(node) : node.hide()),\n keys: [\"esc\", \"ctrl-c\"],\n },\n })\n return node\n}\n"],"mappings":";;AA+BA,SAAgB,QACd,OACA,GAAG,UACS;CACZ,MAAM,OAAO,IAAI;EAAE,SAAS;EAAO,GAAG;CAAM,GAAG,GAAG,QAAQ;CAC1D,KAAK,YAAY,EACf,iBAAiB;EACf,MAAM;EACN,UAAY,KAAK,MAAM,iBAAiB,OAAQ,KAAK,KAAK,QAAQ,OAAO,IAAI,IAAI,KAAK,KAAK;EAC3F,MAAM,CAAC,OAAO,QAAQ;CACxB,EACF,CAAC;CACD,OAAO;AACT"}
@@ -1,11 +1,11 @@
1
- import { S as unwrap, f as effect, h as memo, s as createProgressive, x as untrack } from "./reactive-f7NpAi69.mjs";
2
- import { t as widget } from "./widget-BkMwhY2p.mjs";
3
- import { t as Input } from "./input-ET2YEMcl.mjs";
4
- import { n as select } from "./select-m7NE6Rwa.mjs";
5
- import { t as tree } from "./tree-Bo1afYhe.mjs";
1
+ import { S as unwrap, f as effect, h as memo, s as createProgressive, x as untrack } from "./reactive-DkP75TPk.mjs";
2
+ import { t as widget } from "./widget-sFveyrZE.mjs";
3
+ import { t as Input } from "./input-BkE4EpBw.mjs";
4
+ import { n as select } from "./select-CUmAFKJh.mjs";
5
+ import { t as tree } from "./tree-D6NvMQx8.mjs";
6
6
  import { sliceAnsi, stripAnsi } from "@zaly/shared/ansi";
7
7
  import { TopK } from "@zaly/shared/minheap";
8
- //#region src/search/score.ts
8
+ // src/search/score.ts
9
9
  const SCORE_MATCH = 16;
10
10
  const SCORE_GAP_START = -3;
11
11
  const SCORE_GAP_EXTENSION = -1;
@@ -121,8 +121,7 @@ function hasPathSepAfter(str, first) {
121
121
  for (let i = first + 1; i < str.length; i++) if (PATH_SEPS.has(str[i])) return true;
122
122
  return false;
123
123
  }
124
- //#endregion
125
- //#region src/search/matcher.ts
124
+ // src/search/matcher.ts
126
125
  var Matcher = class {
127
126
  opts;
128
127
  score;
@@ -392,8 +391,7 @@ function itemField(item, field) {
392
391
  function parseFilePattern(pattern) {
393
392
  return pattern.match(/^(.*[/\\].*):\d*:?:?\d*$/)?.[1] ?? pattern.match(/^(.+\.[a-z_]+):\d*:?:?\d*$/)?.[1];
394
393
  }
395
- //#endregion
396
- //#region src/search/sort.ts
394
+ // src/search/sort.ts
397
395
  function parse(field) {
398
396
  const len = field.startsWith("#");
399
397
  return {
@@ -432,8 +430,7 @@ function compare(a, b) {
432
430
  if (typeof a === "string" && typeof b === "string") return a.localeCompare(b);
433
431
  return String(a).localeCompare(String(b));
434
432
  }
435
- //#endregion
436
- //#region src/search/search.ts
433
+ // src/search/search.ts
437
434
  const BONUS_FRECENCY = 8;
438
435
  const SEARCH_LIMIT = 1e3;
439
436
  const aborted = Symbol("search.aborted");
@@ -541,8 +538,7 @@ var Searcher = class {
541
538
  return this.#matcher.positions(s);
542
539
  }
543
540
  };
544
- //#endregion
545
- //#region src/widgets/picker.ts
541
+ // src/widgets/picker.ts
546
542
  function isTree(props) {
547
543
  return "tree" in props;
548
544
  }
@@ -615,7 +611,6 @@ const picker = widget((props) => {
615
611
  }
616
612
  });
617
613
  });
618
- //#endregion
619
614
  export { picker as t };
620
615
 
621
- //# sourceMappingURL=picker-f6xjgACX.mjs.map
616
+ //# sourceMappingURL=picker-D9WrE3zF.mjs.map