@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.
- package/dist/{actions-DEIKF2ts.mjs → actions-AY6avfEV.mjs} +2 -3
- package/dist/{actions-DEIKF2ts.mjs.map → actions-AY6avfEV.mjs.map} +1 -1
- package/dist/{actions--ODlgCQ5.d.mts → actions-B0yyZ-Ww.d.mts} +4 -6
- package/dist/actions-B0yyZ-Ww.d.mts.map +1 -0
- package/dist/{ansi-CMv71xVr.mjs → ansi-CxgRPLA2.mjs} +3 -4
- package/dist/{ansi-CMv71xVr.mjs.map → ansi-CxgRPLA2.mjs.map} +1 -1
- package/dist/ansi.d.mts +1 -3
- package/dist/ansi.d.mts.map +1 -1
- package/dist/ansi.mjs +1 -1
- package/dist/{autocomplete-CD30kSe_.d.mts → autocomplete-wgceuhLr.d.mts} +5 -7
- package/dist/autocomplete-wgceuhLr.d.mts.map +1 -0
- package/dist/{box-C_N_7ChP.mjs → box-DToDoXlU.mjs} +8 -10
- package/dist/{box-C_N_7ChP.mjs.map → box-DToDoXlU.mjs.map} +1 -1
- package/dist/{builder-Chwtk0nQ.mjs → builder-BdYlK1Ew.mjs} +5 -6
- package/dist/{builder-Chwtk0nQ.mjs.map → builder-BdYlK1Ew.mjs.map} +1 -1
- package/dist/{callbacks-DLecWn7G.mjs → callbacks-DW8XKYKy.mjs} +8 -12
- package/dist/{callbacks-DLecWn7G.mjs.map → callbacks-DW8XKYKy.mjs.map} +1 -1
- package/dist/{client-D7qz4TZt.mjs → client-BWm6dNZF.mjs} +4 -5
- package/dist/{client-D7qz4TZt.mjs.map → client-BWm6dNZF.mjs.map} +1 -1
- package/dist/client-C69qWhMG.mjs +1 -0
- package/dist/{clipboard-CNPBTX7j.mjs → clipboard-UEoX9sup.mjs} +2 -3
- package/dist/{clipboard-CNPBTX7j.mjs.map → clipboard-UEoX9sup.mjs.map} +1 -1
- package/dist/clipboard.d.mts +0 -2
- package/dist/clipboard.d.mts.map +1 -1
- package/dist/clipboard.mjs +1 -1
- package/dist/{color-DBMQLv4Y.mjs → color-Cr1VoL49.mjs} +2 -3
- package/dist/{color-DBMQLv4Y.mjs.map → color-Cr1VoL49.mjs.map} +1 -1
- package/dist/ctx-CYEzzH-7.mjs +1 -0
- package/dist/{ctx-BaQ2hP5u.mjs → ctx-DBeFcZt6.mjs} +4 -5
- package/dist/{ctx-BaQ2hP5u.mjs.map → ctx-DBeFcZt6.mjs.map} +1 -1
- package/dist/{divider-BlNNN2OS.mjs → divider-qEj5prKE.mjs} +3 -4
- package/dist/{divider-BlNNN2OS.mjs.map → divider-qEj5prKE.mjs.map} +1 -1
- package/dist/{files-BvThQpOf.mjs → files-C3ZnFpvq.mjs} +3 -4
- package/dist/{files-BvThQpOf.mjs.map → files-C3ZnFpvq.mjs.map} +1 -1
- package/dist/{files-B_5HDBPp.d.mts → files-CdiDnwA_.d.mts} +3 -5
- package/dist/files-CdiDnwA_.d.mts.map +1 -0
- package/dist/{flex-Bguo-fUe.mjs → flex-CYp4etqn.mjs} +3 -4
- package/dist/{flex-Bguo-fUe.mjs.map → flex-CYp4etqn.mjs.map} +1 -1
- package/dist/{github-Cy2hfWJ7.mjs → github-CLveT9mq.mjs} +2 -3
- package/dist/{github-Cy2hfWJ7.mjs.map → github-CLveT9mq.mjs.map} +1 -1
- package/dist/{github-CyRwUQ3y.d.mts → github-DC0XemKa.d.mts} +3 -5
- package/dist/github-DC0XemKa.d.mts.map +1 -0
- package/dist/{image-BLls2F4t.d.mts → image-DdRDv7q6.d.mts} +2 -4
- package/dist/image-DdRDv7q6.d.mts.map +1 -0
- package/dist/{image-Cqtj3yRA.mjs → image-M--Bw4bs.mjs} +6 -7
- package/dist/{image-Cqtj3yRA.mjs.map → image-M--Bw4bs.mjs.map} +1 -1
- package/dist/image.d.mts +1 -3
- package/dist/image.d.mts.map +1 -1
- package/dist/image.mjs +1 -1
- package/dist/index.d.mts +3 -15
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +11 -14
- package/dist/index.mjs.map +1 -1
- package/dist/{input-ET2YEMcl.mjs → input-BkE4EpBw.mjs} +6 -7
- package/dist/{input-ET2YEMcl.mjs.map → input-BkE4EpBw.mjs.map} +1 -1
- package/dist/{input-NuD8TH8N.d.mts → input-C3kfYoEJ.d.mts} +3 -5
- package/dist/input-C3kfYoEJ.d.mts.map +1 -0
- package/dist/{inspect-Cxzs-W0g.mjs → inspect-Cli1beu8.mjs} +3 -4
- package/dist/{inspect-Cxzs-W0g.mjs.map → inspect-Cli1beu8.mjs.map} +1 -1
- package/dist/{inspect-B1uXxdKC.d.mts → inspect-hSl3U2BD.d.mts} +2 -6
- package/dist/inspect-hSl3U2BD.d.mts.map +1 -0
- package/dist/kitty-0sJ8axU_.mjs +1 -0
- package/dist/{kitty-JbCJC0iQ.mjs → kitty-BH_eK6lz.mjs} +2 -3
- package/dist/{kitty-JbCJC0iQ.mjs.map → kitty-BH_eK6lz.mjs.map} +1 -1
- package/dist/{log-B4djzy2m.mjs → log-96b1_J6G.mjs} +6 -7
- package/dist/{log-B4djzy2m.mjs.map → log-96b1_J6G.mjs.map} +1 -1
- package/dist/{logger-CvJCITzP.mjs → logger-DwfXp8LF.mjs} +5 -6
- package/dist/{logger-CvJCITzP.mjs.map → logger-DwfXp8LF.mjs.map} +1 -1
- package/dist/{markdown-D3ZtRn6C.mjs → markdown-CgRZBhIX.mjs} +11 -12
- package/dist/{markdown-D3ZtRn6C.mjs.map → markdown-CgRZBhIX.mjs.map} +1 -1
- package/dist/markdown.d.mts +2 -4
- package/dist/markdown.d.mts.map +1 -1
- package/dist/markdown.mjs +1 -1
- package/dist/math-Dk8hxbmz.mjs +1 -0
- package/dist/{math-Dd7a1irv.mjs → math-VPuwFCid.mjs} +1 -2
- package/dist/{math-Dd7a1irv.mjs.map → math-VPuwFCid.mjs.map} +1 -1
- package/dist/md.bun.d.mts +1 -3
- package/dist/md.bun.d.mts.map +1 -1
- package/dist/md.bun.mjs +2 -3
- package/dist/md.node.d.mts +1 -3
- package/dist/md.node.d.mts.map +1 -1
- package/dist/md.node.mjs +2 -3
- package/dist/{node-BZ7f6HzU.mjs → node-CXNobGaP.mjs} +3 -4
- package/dist/{node-BZ7f6HzU.mjs.map → node-CXNobGaP.mjs.map} +1 -1
- package/dist/{oklch-Bsh5swbC.mjs → oklch-Boh4YHEc.mjs} +3 -4
- package/dist/{oklch-Bsh5swbC.mjs.map → oklch-Boh4YHEc.mjs.map} +1 -1
- package/dist/{overlay-LvOjG7kn.mjs → overlay-CevDWgSF.mjs} +3 -4
- package/dist/{overlay-LvOjG7kn.mjs.map → overlay-CevDWgSF.mjs.map} +1 -1
- package/dist/{picker-f6xjgACX.mjs → picker-D9WrE3zF.mjs} +11 -16
- package/dist/{picker-f6xjgACX.mjs.map → picker-D9WrE3zF.mjs.map} +1 -1
- package/dist/{picker-B-OxKKnD.d.mts → picker-DsB6--GN.d.mts} +6 -8
- package/dist/picker-DsB6--GN.d.mts.map +1 -0
- package/dist/{queries-BUkBxkuM.mjs → queries-BIniKnLL.mjs} +3 -5
- package/dist/{queries-BUkBxkuM.mjs.map → queries-BIniKnLL.mjs.map} +1 -1
- package/dist/{queries-C75-wG1B.d.mts → queries-Dmd3IjSd.d.mts} +3 -51
- package/dist/queries-Dmd3IjSd.d.mts.map +1 -0
- package/dist/{reactive-f7NpAi69.mjs → reactive-DkP75TPk.mjs} +2 -3
- package/dist/{reactive-f7NpAi69.mjs.map → reactive-DkP75TPk.mjs.map} +1 -1
- package/dist/{registry-DUWpSPoo.mjs → registry-CQU0VwpM.mjs} +9 -14
- package/dist/{registry-DUWpSPoo.mjs.map → registry-CQU0VwpM.mjs.map} +1 -1
- package/dist/{render-B7LfNM5K.mjs → render-Bp2N2i6J.mjs} +4 -5
- package/dist/{render-B7LfNM5K.mjs.map → render-Bp2N2i6J.mjs.map} +1 -1
- package/dist/{renderer-BLsd-wrG.mjs → renderer-B9sAMA10.mjs} +25 -38
- package/dist/{renderer-BLsd-wrG.mjs.map → renderer-B9sAMA10.mjs.map} +1 -1
- package/dist/{renderer-aAtkFgll.mjs → renderer-BYl2pbdx.mjs} +3 -4
- package/dist/{renderer-aAtkFgll.mjs.map → renderer-BYl2pbdx.mjs.map} +1 -1
- package/dist/renderer-Ctz0hv5o.mjs +1 -0
- package/dist/{search-FvGnswRl.d.mts → search-DsHzamic.d.mts} +1 -9
- package/dist/search-DsHzamic.d.mts.map +1 -0
- package/dist/{select-BKE5LzdN.d.mts → select-BZCFXdjp.d.mts} +3 -5
- package/dist/select-BZCFXdjp.d.mts.map +1 -0
- package/dist/{select-m7NE6Rwa.mjs → select-CUmAFKJh.mjs} +5 -6
- package/dist/{select-m7NE6Rwa.mjs.map → select-CUmAFKJh.mjs.map} +1 -1
- package/dist/services/logger.d.mts +1 -1
- package/dist/services/logger.mjs +1 -1
- package/dist/services/notifier.d.mts +1 -3
- package/dist/services/notifier.d.mts.map +1 -1
- package/dist/services/notifier.mjs +3 -4
- package/dist/services/picker.d.mts +5 -7
- package/dist/services/picker.d.mts.map +1 -1
- package/dist/services/picker.mjs +8 -9
- package/dist/services/prompt.d.mts +4 -6
- package/dist/services/prompt.d.mts.map +1 -1
- package/dist/services/prompt.mjs +7 -8
- package/dist/shiki/server.mjs +6 -8
- package/dist/shiki/server.mjs.map +1 -1
- package/dist/{shiki-CJauRusM.mjs → shiki-BAf9PdIc.mjs} +4 -5
- package/dist/{shiki-CJauRusM.mjs.map → shiki-BAf9PdIc.mjs.map} +1 -1
- package/dist/{shiki-B4U0s8jT.mjs → shiki-DUUAIE6f.mjs} +4 -7
- package/dist/{shiki-B4U0s8jT.mjs.map → shiki-DUUAIE6f.mjs.map} +1 -1
- package/dist/{show-DR2x1YFc.mjs → show-D5XNXgZf.mjs} +4 -5
- package/dist/{show-DR2x1YFc.mjs.map → show-D5XNXgZf.mjs.map} +1 -1
- package/dist/{size-D8JogYLy.mjs → size-BHRJuRk9.mjs} +2 -3
- package/dist/{size-D8JogYLy.mjs.map → size-BHRJuRk9.mjs.map} +1 -1
- package/dist/{style-DslRHzI4.mjs → style-Cw_EuszU.mjs} +5 -6
- package/dist/{style-DslRHzI4.mjs.map → style-Cw_EuszU.mjs.map} +1 -1
- package/dist/style.d.mts +2 -4
- package/dist/style.d.mts.map +1 -1
- package/dist/style.mjs +3 -3
- package/dist/{text-BoJdKXLU.mjs → text-4arC3odI.mjs} +3 -4
- package/dist/{text-BoJdKXLU.mjs.map → text-4arC3odI.mjs.map} +1 -1
- package/dist/{text-DPrhma4x.mjs → text-BArwVVkg.mjs} +5 -6
- package/dist/{text-DPrhma4x.mjs.map → text-BArwVVkg.mjs.map} +1 -1
- package/dist/{text-CmxbA8lk.d.mts → text-DdkJXgoC.d.mts} +3 -5
- package/dist/text-DdkJXgoC.d.mts.map +1 -0
- package/dist/text.d.mts +1 -1
- package/dist/text.mjs +1 -1
- package/dist/{theme-C8LkcoaK.mjs → theme-B8hb1lPU.mjs} +3 -4
- package/dist/{theme-C8LkcoaK.mjs.map → theme-B8hb1lPU.mjs.map} +1 -1
- package/dist/{theme-DVX8dvGG.mjs → theme-BeSaXGNY.mjs} +3 -5
- package/dist/{theme-DVX8dvGG.mjs.map → theme-BeSaXGNY.mjs.map} +1 -1
- package/dist/themes.d.mts +1 -7
- package/dist/themes.d.mts.map +1 -1
- package/dist/themes.mjs +1 -1
- package/dist/{tree-CTvSpF9b.d.mts → tree-Berzv8Rz.d.mts} +3 -5
- package/dist/tree-Berzv8Rz.d.mts.map +1 -0
- package/dist/{tree-Bo1afYhe.mjs → tree-D6NvMQx8.mjs} +5 -6
- package/dist/{tree-Bo1afYhe.mjs.map → tree-D6NvMQx8.mjs.map} +1 -1
- package/dist/{types-C9ceMsbJ.d.mts → types-ByMHEdCF.d.mts} +1 -7
- package/dist/types-ByMHEdCF.d.mts.map +1 -0
- package/dist/{types-D3jk1rtk.d.mts → types-CUVUJPz6.d.mts} +1 -3
- package/dist/types-CUVUJPz6.d.mts.map +1 -0
- package/dist/{utils-3M4D_AXZ.mjs → utils-DHXAegVW.mjs} +1 -2
- package/dist/{utils-3M4D_AXZ.mjs.map → utils-DHXAegVW.mjs.map} +1 -1
- package/dist/{widget-BkMwhY2p.mjs → widget-sFveyrZE.mjs} +3 -4
- package/dist/{widget-BkMwhY2p.mjs.map → widget-sFveyrZE.mjs.map} +1 -1
- package/dist/{widget-DlF32PRp.d.mts → widget-sPJaKVU2.d.mts} +2 -4
- package/dist/widget-sPJaKVU2.d.mts.map +1 -0
- package/dist/widgets/autocomplete.d.mts +1 -1
- package/dist/widgets/autocomplete.mjs +5 -6
- package/dist/widgets/box.d.mts +1 -1
- package/dist/widgets/box.mjs +1 -1
- package/dist/widgets/code.d.mts +3 -5
- package/dist/widgets/code.d.mts.map +1 -1
- package/dist/widgets/code.mjs +10 -11
- package/dist/widgets/completions/actions.d.mts +1 -1
- package/dist/widgets/completions/actions.mjs +1 -1
- package/dist/widgets/completions/files.d.mts +1 -1
- package/dist/widgets/completions/files.mjs +1 -1
- package/dist/widgets/completions/github.d.mts +1 -1
- package/dist/widgets/completions/github.mjs +1 -1
- package/dist/widgets/completions/index.d.mts +3 -3
- package/dist/widgets/completions/index.mjs +3 -3
- package/dist/widgets/diff.d.mts +2 -4
- package/dist/widgets/diff.d.mts.map +1 -1
- package/dist/widgets/diff.mjs +7 -8
- package/dist/widgets/divider.d.mts +2 -4
- package/dist/widgets/divider.d.mts.map +1 -1
- package/dist/widgets/divider.mjs +1 -1
- package/dist/widgets/image.d.mts +1 -1
- package/dist/widgets/image.mjs +5 -6
- package/dist/widgets/input.d.mts +1 -1
- package/dist/widgets/input.mjs +1 -1
- package/dist/widgets/log.d.mts +1 -1
- package/dist/widgets/log.mjs +1 -1
- package/dist/widgets/markdown.d.mts +5 -7
- package/dist/widgets/markdown.d.mts.map +1 -1
- package/dist/widgets/markdown.mjs +1 -1
- package/dist/widgets/overlay.d.mts +1 -1
- package/dist/widgets/overlay.mjs +1 -1
- package/dist/widgets/picker.d.mts +1 -1
- package/dist/widgets/picker.mjs +1 -1
- package/dist/widgets/progress.d.mts +2 -4
- package/dist/widgets/progress.d.mts.map +1 -1
- package/dist/widgets/progress.mjs +4 -5
- package/dist/widgets/select.d.mts +1 -1
- package/dist/widgets/select.mjs +1 -1
- package/dist/widgets/show.d.mts +1 -3
- package/dist/widgets/show.d.mts.map +1 -1
- package/dist/widgets/show.mjs +1 -1
- package/dist/widgets/spinner.d.mts +2 -4
- package/dist/widgets/spinner.d.mts.map +1 -1
- package/dist/widgets/spinner.mjs +3 -4
- package/dist/widgets/text.d.mts +1 -1
- package/dist/widgets/text.mjs +1 -1
- package/dist/widgets/tree.d.mts +1 -1
- package/dist/widgets/tree.mjs +1 -1
- package/dist/widgets/widget.d.mts +1 -1
- package/dist/widgets/widget.mjs +1 -1
- package/dist/{worker-T6SdPzUH.mjs → worker-CtEowucz.mjs} +2 -3
- package/dist/{worker-T6SdPzUH.mjs.map → worker-CtEowucz.mjs.map} +1 -1
- package/dist/worker-Mqi380Et.mjs +1 -0
- package/package.json +3 -3
- package/dist/actions--ODlgCQ5.d.mts.map +0 -1
- package/dist/autocomplete-CD30kSe_.d.mts.map +0 -1
- package/dist/client-CNGt-dOt.mjs +0 -1
- package/dist/ctx-CndlTp1T.mjs +0 -1
- package/dist/files-B_5HDBPp.d.mts.map +0 -1
- package/dist/github-CyRwUQ3y.d.mts.map +0 -1
- package/dist/image-BLls2F4t.d.mts.map +0 -1
- package/dist/input-NuD8TH8N.d.mts.map +0 -1
- package/dist/inspect-B1uXxdKC.d.mts.map +0 -1
- package/dist/kitty-DKQYsQfH.mjs +0 -1
- package/dist/math-CU0wmC9N.mjs +0 -1
- package/dist/picker-B-OxKKnD.d.mts.map +0 -1
- package/dist/queries-C75-wG1B.d.mts.map +0 -1
- package/dist/renderer-YHbR7JZ4.mjs +0 -1
- package/dist/search-FvGnswRl.d.mts.map +0 -1
- package/dist/select-BKE5LzdN.d.mts.map +0 -1
- package/dist/text-CmxbA8lk.d.mts.map +0 -1
- package/dist/tree-CTvSpF9b.d.mts.map +0 -1
- package/dist/types-C9ceMsbJ.d.mts.map +0 -1
- package/dist/types-D3jk1rtk.d.mts.map +0 -1
- package/dist/widget-DlF32PRp.d.mts.map +0 -1
- package/dist/worker-C01jK2Ip.mjs +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"callbacks-DLecWn7G.mjs","names":[],"sources":["../src/markdown/code.ts","../src/markdown/list.ts","../src/markdown/table.ts","../src/markdown/callbacks.ts"],"sourcesContent":["import type { MarkdownCtx } from \"./renderer.ts\"\nimport type { MdCallbacks } from \"./types.ts\"\n\nimport { stringWidth } from \"@zaly/shared/ansi\"\nimport { formatText } from \"../layout/text.ts\"\n\n/**\n * Build the `code` callback. The returned closure captures ctx/style/highlighter\n * and renders each fenced block: syntax-highlight (if available), pad lines to\n * widest-content + 1, apply `mdCodeBlock` as a bg-only backdrop, and prefix an\n * optional title line from `title=\"...\"`.\n */\nexport function createCodeCallback(ctx: MarkdownCtx): NonNullable<MdCallbacks[\"code\"]> {\n const s = ctx.style\n return (text, meta) => {\n text = text.replace(/\\n+$/, \"\") // trim to avoid extra blank lines after the block\n const highlighted = ctx.highlight?.(text, meta?.language) ?? text\n\n // For highlighted output the per-token fgs would clash with the slot's\n // fg; override fg with `\"inherit\"` so only bg + attrs wrap each row\n // and shiki's per-token colors show through on top of the backdrop.\n const wrap = highlighted === text ? s.mdCodeBlock : s.mdCodeBlock.fg(\"inherit\")\n const hpad = 2\n const maxWidth = ctx.width\n const lines = formatText(`\\n${highlighted}\\n`, {\n width: maxWidth - hpad * 2,\n wrap: \"word\",\n })\n let width = Math.max(...lines.map(stringWidth)) + hpad * 2\n width = Math.min(width, maxWidth) // leave room for the border\n // Left-align with a fixed `hpad` of leading whitespace; pad the\n // right to fill the bg out to `width`. Centering would push short\n // lines toward the middle, which reads as visually misaligned code.\n // Lines wider than the available room (width − hpad) drop the\n // leading pad so the bg ends at the cap rather than overshooting.\n const padded = lines.map((line) => {\n const w = stringWidth(line)\n if (w >= width - hpad) return wrap(line)\n return wrap(`${\" \".repeat(hpad)}${line}${\" \".repeat(width - w - hpad)}`)\n })\n const titleLine = meta?.title === undefined ? \"\" : `${s.mdCodeBlockTitle(meta.title)}\\n`\n // Leading `\\n` guards against raw-text siblings that don't emit a\n // paragraph-wrap (e.g. some inline-only list items where the parser\n // drops the paragraph). Containers (`listItem`, document) collapse\n // `\\n{3,}` → `\\n\\n` so paragraph + code doesn't double up.\n return `\\n${titleLine}${padded.join(\"\\n\")}\\n\\n`\n }\n}\n","// oxlint-disable no-multi-assign\nimport type { RenderCtx } from \"../core/ctx.ts\"\nimport type { MdCallbacks } from \"./types.ts\"\n\nimport { splitAnsi, stringWidth } from \"@zaly/shared/ansi\"\n\ninterface ListState {\n block: boolean\n items: string[]\n /** Formatted output, set when the list closes. The parent `listItem`\n * pulls it via `stack[depth+1]` and splices it onto its own body. */\n rendered?: string\n}\n\nconst BULLETS = [\"●\", \"○\", \"◆\", \"◇\"] as const\n\ntype ListCallbacks = Pick<MdCallbacks, \"list\" | \"listItem\">\n\n/**\n * Stack-based list renderer. Each depth has its own `ListState`. Inner\n * lists return `\"\"` and stash their formatted output on `state.rendered`;\n * the parent `listItem` pulls + splices, so inner output never reaches\n * the parent's `children` string. That keeps each level's loose-vs-tight\n * verdict independent — `children.endsWith(\"\\n\\n\")` is then the per-item\n * loose signal (parser only emits `<p>` for items in loose lists).\n *\n * Nested list lands at the end of its parent's body — `text, list, more`\n * is rare in real markdown and not worth the extra plumbing.\n */\nexport function createListCallbacks(ctx: RenderCtx): ListCallbacks {\n const s = ctx.style\n // Lazy-pushed on the first `listItem` at a given depth; consumed\n // either by the parent `listItem` (nested) or by `list(0)` (top).\n const stack: (ListState | undefined)[] = []\n\n return {\n list: (_children, meta) => {\n const state = stack[meta.depth]\n if (state === undefined || state.items.length === 0) return \"\"\n state.rendered = state.items.join(state.block ? \"\\n\\n\" : \"\\n\")\n if (meta.depth > 0) return \"\"\n // Top-level: defensive stack reset + block-contract `\\n\\n` trailer.\n stack.length = 0\n return `${state.rendered}\\n\\n`\n },\n\n listItem: (children, meta) => {\n let marker = s.mdListBullet(\n meta.ordered ? `${(meta.start ?? 1) + meta.index}.` : BULLETS[meta.depth % BULLETS.length]\n )\n if (meta.checked !== undefined) {\n const ref = meta.checked ? \"mdListChecked\" : \"mdListUnchecked\"\n marker += ` ${s.add(ref)(meta.checked ? \"[x]\" : \"[ ]\")}`\n }\n const indent = \" \".repeat(stringWidth(marker))\n // Pull nested list output (if any) off the stack.\n const nested = stack[meta.depth + 1]\n stack[meta.depth + 1] = undefined\n // Trailing `\\n\\n` = loose signal. Nested output is in `rendered`,\n // not `children`, so it can't pollute this check.\n const block = children.endsWith(\"\\n\\n\")\n const trimmed = children.replace(/\\s*\\n+$/, \"\").replace(/\\n{3,}/g, \"\\n\\n\")\n // Splice nested after the prose; loose item adds a blank-row gap.\n let body = trimmed\n if (nested?.rendered !== undefined) {\n const sep = block ? \"\\n\\n\" : \"\\n\"\n body = trimmed === \"\" ? nested.rendered : `${trimmed}${sep}${nested.rendered}`\n }\n const lines = splitAnsi(body)\n const content = lines\n .map((line, l) => {\n if (l === 0) return `${marker} ${line}`\n // Bare empty lines so loose `\\n\\n` separators stay clean.\n return line === \"\" ? \"\" : `${indent}${line}`\n })\n .join(\"\\n\")\n\n const state = (stack[meta.depth] ??= { block: false, items: [] })\n state.block ||= block\n state.items.push(content)\n return \"\"\n },\n }\n}\n","import type { RenderCtx } from \"../core/ctx.ts\"\nimport type { Layout } from \"../core/state.ts\"\nimport type { RowItem } from \"../layout/flex.ts\"\nimport type { MdCallbacks } from \"./types.ts\"\n\nimport { stringWidth } from \"@zaly/shared/ansi\"\nimport { allocateRow } from \"../layout/flex.ts\"\nimport { calcLayout, formatText } from \"../layout/text.ts\"\n\ntype CellAlign = \"left\" | \"center\" | \"right\"\n\ninterface TableCell {\n text: string\n align: CellAlign | undefined\n}\n\ninterface TableState {\n header: TableCell[][]\n body: TableCell[][]\n pending: TableCell[]\n pendingIsHeader: boolean\n}\n\ntype TableCallbacks = Pick<MdCallbacks, \"table\" | \"tbody\" | \"td\" | \"th\" | \"thead\" | \"tr\">\n\n/**\n * Build the table-related callbacks. They share a closure-local\n * `TableState` accumulator: `th`/`td` push cells, `tr` commits rows into\n * header or body, and `table` runs the final layout + reset. Tables can't\n * nest in markdown, so a single top-level slot is enough.\n */\nexport function createTableCallbacks(ctx: RenderCtx): TableCallbacks {\n const { style } = ctx\n let state: TableState | undefined\n const begin = (): TableState =>\n (state ??= { body: [], header: [], pending: [], pendingIsHeader: false })\n\n return {\n table: () => {\n // Children of th/td/tr/thead/tbody are empty strings; all content is\n // in `state`. Format, reset, emit.\n if (state === undefined) return \"\"\n const out = formatTable(state, ctx)\n state = undefined\n return `${out}\\n\\n`\n },\n\n tbody: () => \"\",\n\n td: (children, meta) => {\n const t = begin()\n t.pending.push({ align: meta?.align, text: children })\n t.pendingIsHeader = false\n return \"\"\n },\n\n th: (children, meta) => {\n const t = begin()\n t.pending.push({ align: meta?.align, text: style.mdTableHeader(children) })\n t.pendingIsHeader = true\n return \"\"\n },\n\n thead: () => \"\",\n\n tr: () => {\n if (state === undefined || state.pending.length === 0) return \"\"\n if (state.pendingIsHeader) state.header.push(state.pending)\n else state.body.push(state.pending)\n state.pending = []\n return \"\"\n },\n }\n}\n\nfunction formatTable(state: TableState, ctx: RenderCtx): string {\n const cols: Layout[] = []\n const cells = [...state.header, ...state.body]\n const colCount = Math.max(...cells.map((r) => r.length))\n\n // 1. Calculate min-content and max-content for each column\n for (let c = 0; c < colCount; c++) {\n for (const row of cells) {\n const cell = row[c] as TableCell | undefined\n if (!cell) continue // Safety guard for ragged rows\n const l = calcLayout(cell.text)\n cols[c] ??= { ...l }\n cols[c].width = Math.max(cols[c].width, l.width)\n cols[c].minWidth = Math.max(cols[c].minWidth, l.minWidth)\n }\n }\n const chromeWidth = 3 * colCount + 1\n\n const items: RowItem[] = cols.map((c) => ({\n flex: 1,\n intrinsicMin: c.minWidth,\n natural: c.width,\n }))\n\n const widths = allocateRow(items, { contentWidth: ctx.width - chromeWidth, gap: 0 })\n\n // 4. Render output string using the exact final Column Widths\n // Pre-style each piece of chrome once. Each column span on a rule row is\n // `widths[i] + 2` dashes (one cell of padding on each side of the cell).\n const style = ctx.style\n const v = style.mdTable(\"│\")\n const ruleRow = (left: string, mid: string, right: string): string =>\n style.mdTable(left + widths.map((w) => \"─\".repeat(w + 2)).join(mid) + right)\n const top = ruleRow(\"┌\", \"┬\", \"┐\")\n const sep = ruleRow(\"├\", \"┼\", \"┤\")\n const bot = ruleRow(\"└\", \"┴\", \"┘\")\n\n const renderRow = (row: TableCell[]): string => {\n const formatted = row.map((cell, c) =>\n formatText(cell.text, { indent: false, width: widths[c], wrapBg: false })\n )\n const rows: string[] = []\n const rowCount = Math.max(...formatted.map((r) => r.length))\n for (let r = 0; r < rowCount; r++) {\n const line: string[] = []\n for (let c = 0; c < colCount; c++) {\n const t = formatted[c]?.[r] ?? \"\"\n line.push(padCell(t, widths[c], row[c]?.align))\n }\n rows.push(`${v} ${line.join(` ${v} `)} ${v}`)\n }\n\n return rows.join(\"\\n\")\n }\n\n const lines: string[] = [top]\n for (const row of state.header) lines.push(renderRow(row))\n if (state.header.length > 0 && state.body.length > 0) lines.push(sep)\n for (const row of state.body) lines.push(renderRow(row))\n lines.push(bot)\n return lines.join(\"\\n\")\n}\n\nfunction padCell(text: string, width: number, align: CellAlign | undefined): string {\n const slack = Math.max(0, width - stringWidth(text))\n if (align === \"right\") return \" \".repeat(slack) + text\n if (align === \"center\") {\n const left = Math.floor(slack / 2)\n return \" \".repeat(left) + text + \" \".repeat(slack - left)\n }\n return text + \" \".repeat(slack)\n}\n","import type { AnyStyle, ThemeKey } from \"../style/types.ts\"\nimport type { MarkdownCtx } from \"./renderer.ts\"\nimport type { MdCallbacks } from \"./types.ts\"\n\nimport { stringWidth } from \"@zaly/shared/ansi\"\nimport { capitalize } from \"../layout/text.ts\"\nimport { hyperlink } from \"../style/ansi.ts\"\nimport { createCodeCallback } from \"./code.ts\"\nimport { createListCallbacks } from \"./list.ts\"\nimport { createTableCallbacks } from \"./table.ts\"\n\nconst icons = {\n hr: \"─\",\n quote: \"┃\",\n} as const\n\ntype AlertType = \"NOTE\" | \"TIP\" | \"WARNING\" | \"IMPORTANT\" | \"CAUTION\"\nconst alerts: Record<AlertType, { icon: string; style: AnyStyle }> = {\n CAUTION: { icon: \"✖ \", style: \"error\" },\n IMPORTANT: { icon: \"‼\", style: \"syntaxConstant\" },\n NOTE: { icon: \"ℹ \", style: \"info\" },\n TIP: { icon: \"🛈 \", style: \"success\" },\n WARNING: { icon: \"⚠\", style: \"warn\" },\n}\n\nconst alertRe = /^\\s*\\[!(NOTE|TIP|WARNING|IMPORTANT|CAUTION)\\]\\s*$/\n\n/**\n * Build the `MdCallbacks` that drive the theme-aware rendering. Exposed so\n * callers can invoke `renderMarkdown` directly when they need the string\n * output without the wrapping `Markdown` node (e.g. to embed markdown inside\n * a custom `Text` content function).\n * @internal\n */\nexport function createCallbacks(ctx: MarkdownCtx): MdCallbacks {\n const s = ctx.style\n\n return {\n blockquote: (children) => {\n const lines = children.replace(/\\n+$/, \"\").split(\"\\n\")\n let lineStyle: AnyStyle = \"mdQuote\"\n\n const m = lines.length >= 1 ? lines[0].match(alertRe) : undefined\n if (m) {\n const alertType = m[1] as AlertType\n const alert = alerts[alertType]\n lineStyle = alert.style\n const name = capitalize(alertType.toLowerCase())\n lines[0] = s.add(lineStyle)(`${alert.icon} ${name}`)\n }\n\n // Inner blocks (typically a paragraph) end with trailing newlines; if\n // we prefix those empty lines with \"│ \" they render as styled empty\n // rows between the last line of the quote and the block separator.\n // Trim first so the quote stops cleanly.\n const prefixed = lines.map((line) => `${s.add(lineStyle)(icons.quote)} ${line}`).join(\"\\n\")\n return `${prefixed}\\n\\n`\n },\n\n code: createCodeCallback(ctx),\n\n // Image markers use code spans for parser protection, not code styling.\n codespan: (text) => (/^<img id=\\d+>$/.test(text) ? text : s.mdCode(text)),\n\n emphasis: (children) => s.mdItalic(children),\n\n heading: (children, { level }) => {\n const width = ctx.width\n const padded = children\n .split(\"\\n\")\n .map((line) => line + \" \".repeat(Math.max(0, width - stringWidth(line))))\n .join(\"\\n\")\n return `${s.add(`mdHeading${level}` as ThemeKey)(padded)}\\n\\n`\n },\n\n hr: () => `${s.mdHr(icons.hr.repeat(ctx.width))}\\n\\n`,\n\n html: (children) => children,\n\n link: (children, { href }) => hyperlink(href, s.mdLink(children)),\n\n paragraph: (children) => `${children}\\n\\n`,\n\n strikethrough: (children) => s.mdStrikethrough(children),\n\n strong: (children) => s.mdBold(children),\n\n ...createListCallbacks(ctx),\n ...createTableCallbacks(ctx),\n }\n}\n"],"mappings":";;;;;;;;;;;AAYA,SAAgB,mBAAmB,KAAoD;CACrF,MAAM,IAAI,IAAI;CACd,QAAQ,MAAM,SAAS;EACrB,OAAO,KAAK,QAAQ,QAAQ,EAAE;EAC9B,MAAM,cAAc,IAAI,YAAY,MAAM,MAAM,QAAQ,KAAK;EAK7D,MAAM,OAAO,gBAAgB,OAAO,EAAE,cAAc,EAAE,YAAY,GAAG,SAAS;EAC9E,MAAM,OAAO;EACb,MAAM,WAAW,IAAI;EACrB,MAAM,QAAQ,WAAW,KAAK,YAAY,KAAK;GAC7C,OAAO,WAAW;GAClB,MAAM;EACR,CAAC;EACD,IAAI,QAAQ,KAAK,IAAI,GAAG,MAAM,IAAI,WAAW,CAAC,IAAI;EAClD,QAAQ,KAAK,IAAI,OAAO,QAAQ;EAMhC,MAAM,SAAS,MAAM,KAAK,SAAS;GACjC,MAAM,IAAI,YAAY,IAAI;GAC1B,IAAI,KAAK,QAAQ,MAAM,OAAO,KAAK,IAAI;GACvC,OAAO,KAAK,GAAG,IAAI,OAAO,IAAI,IAAI,OAAO,IAAI,OAAO,QAAQ,IAAI,IAAI,GAAG;EACzE,CAAC;EAMD,OAAO,KALW,MAAM,UAAU,KAAA,IAAY,KAAK,GAAG,EAAE,iBAAiB,KAAK,KAAK,EAAE,MAK7D,OAAO,KAAK,IAAI,EAAE;CAC5C;AACF;;;ACjCA,MAAM,UAAU;CAAC;CAAK;CAAK;CAAK;AAAG;;;;;;;;;;;;AAenC,SAAgB,oBAAoB,KAA+B;CACjE,MAAM,IAAI,IAAI;CAGd,MAAM,QAAmC,CAAC;CAE1C,OAAO;EACL,OAAO,WAAW,SAAS;GACzB,MAAM,QAAQ,MAAM,KAAK;GACzB,IAAI,UAAU,KAAA,KAAa,MAAM,MAAM,WAAW,GAAG,OAAO;GAC5D,MAAM,WAAW,MAAM,MAAM,KAAK,MAAM,QAAQ,SAAS,IAAI;GAC7D,IAAI,KAAK,QAAQ,GAAG,OAAO;GAE3B,MAAM,SAAS;GACf,OAAO,GAAG,MAAM,SAAS;EAC3B;EAEA,WAAW,UAAU,SAAS;GAC5B,IAAI,SAAS,EAAE,aACb,KAAK,UAAU,IAAI,KAAK,SAAS,KAAK,KAAK,MAAM,KAAK,QAAQ,KAAK,QAAQ,QAAQ,OACrF;GACA,IAAI,KAAK,YAAY,KAAA,GAAW;IAC9B,MAAM,MAAM,KAAK,UAAU,kBAAkB;IAC7C,UAAU,IAAI,EAAE,IAAI,GAAG,CAAC,CAAC,KAAK,UAAU,QAAQ,KAAK;GACvD;GACA,MAAM,SAAS,KAAK,OAAO,YAAY,MAAM,CAAC;GAE9C,MAAM,SAAS,MAAM,KAAK,QAAQ;GAClC,MAAM,KAAK,QAAQ,KAAK,KAAA;GAGxB,MAAM,QAAQ,SAAS,SAAS,MAAM;GACtC,MAAM,UAAU,SAAS,QAAQ,WAAW,EAAE,CAAC,CAAC,QAAQ,WAAW,MAAM;GAEzE,IAAI,OAAO;GACX,IAAI,QAAQ,aAAa,KAAA,GAEvB,OAAO,YAAY,KAAK,OAAO,WAAW,GAAG,UADjC,QAAQ,SAAS,OACgC,OAAO;GAGtE,MAAM,UADQ,UAAU,IACJ,CAAC,CAClB,KAAK,MAAM,MAAM;IAChB,IAAI,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG;IAEjC,OAAO,SAAS,KAAK,KAAK,GAAG,SAAS;GACxC,CAAC,CAAC,CACD,KAAK,IAAI;GAEZ,MAAM,QAAS,MAAM,KAAK,WAAW;IAAE,OAAO;IAAO,OAAO,CAAC;GAAE;GAC/D,MAAM,UAAU;GAChB,MAAM,MAAM,KAAK,OAAO;GACxB,OAAO;EACT;CACF;AACF;;;;;;;;;ACpDA,SAAgB,qBAAqB,KAAgC;CACnE,MAAM,EAAE,UAAU;CAClB,IAAI;CACJ,MAAM,cACH,UAAU;EAAE,MAAM,CAAC;EAAG,QAAQ,CAAC;EAAG,SAAS,CAAC;EAAG,iBAAiB;CAAM;CAEzE,OAAO;EACL,aAAa;GAGX,IAAI,UAAU,KAAA,GAAW,OAAO;GAChC,MAAM,MAAM,YAAY,OAAO,GAAG;GAClC,QAAQ,KAAA;GACR,OAAO,GAAG,IAAI;EAChB;EAEA,aAAa;EAEb,KAAK,UAAU,SAAS;GACtB,MAAM,IAAI,MAAM;GAChB,EAAE,QAAQ,KAAK;IAAE,OAAO,MAAM;IAAO,MAAM;GAAS,CAAC;GACrD,EAAE,kBAAkB;GACpB,OAAO;EACT;EAEA,KAAK,UAAU,SAAS;GACtB,MAAM,IAAI,MAAM;GAChB,EAAE,QAAQ,KAAK;IAAE,OAAO,MAAM;IAAO,MAAM,MAAM,cAAc,QAAQ;GAAE,CAAC;GAC1E,EAAE,kBAAkB;GACpB,OAAO;EACT;EAEA,aAAa;EAEb,UAAU;GACR,IAAI,UAAU,KAAA,KAAa,MAAM,QAAQ,WAAW,GAAG,OAAO;GAC9D,IAAI,MAAM,iBAAiB,MAAM,OAAO,KAAK,MAAM,OAAO;QACrD,MAAM,KAAK,KAAK,MAAM,OAAO;GAClC,MAAM,UAAU,CAAC;GACjB,OAAO;EACT;CACF;AACF;AAEA,SAAS,YAAY,OAAmB,KAAwB;CAC9D,MAAM,OAAiB,CAAC;CACxB,MAAM,QAAQ,CAAC,GAAG,MAAM,QAAQ,GAAG,MAAM,IAAI;CAC7C,MAAM,WAAW,KAAK,IAAI,GAAG,MAAM,KAAK,MAAM,EAAE,MAAM,CAAC;CAGvD,KAAK,IAAI,IAAI,GAAG,IAAI,UAAU,KAC5B,KAAK,MAAM,OAAO,OAAO;EACvB,MAAM,OAAO,IAAI;EACjB,IAAI,CAAC,MAAM;EACX,MAAM,IAAI,WAAW,KAAK,IAAI;EAC9B,KAAK,OAAO,EAAE,GAAG,EAAE;EACnB,KAAK,EAAE,CAAC,QAAQ,KAAK,IAAI,KAAK,EAAE,CAAC,OAAO,EAAE,KAAK;EAC/C,KAAK,EAAE,CAAC,WAAW,KAAK,IAAI,KAAK,EAAE,CAAC,UAAU,EAAE,QAAQ;CAC1D;CAEF,MAAM,cAAc,IAAI,WAAW;CAEnC,MAAM,QAAmB,KAAK,KAAK,OAAO;EACxC,MAAM;EACN,cAAc,EAAE;EAChB,SAAS,EAAE;CACb,EAAE;CAEF,MAAM,SAAS,YAAY,OAAO;EAAE,cAAc,IAAI,QAAQ;EAAa,KAAK;CAAE,CAAC;CAKnF,MAAM,QAAQ,IAAI;CAClB,MAAM,IAAI,MAAM,QAAQ,GAAG;CAC3B,MAAM,WAAW,MAAc,KAAa,UAC1C,MAAM,QAAQ,OAAO,OAAO,KAAK,MAAM,IAAI,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,KAAK;CAC7E,MAAM,MAAM,QAAQ,KAAK,KAAK,GAAG;CACjC,MAAM,MAAM,QAAQ,KAAK,KAAK,GAAG;CACjC,MAAM,MAAM,QAAQ,KAAK,KAAK,GAAG;CAEjC,MAAM,aAAa,QAA6B;EAC9C,MAAM,YAAY,IAAI,KAAK,MAAM,MAC/B,WAAW,KAAK,MAAM;GAAE,QAAQ;GAAO,OAAO,OAAO;GAAI,QAAQ;EAAM,CAAC,CAC1E;EACA,MAAM,OAAiB,CAAC;EACxB,MAAM,WAAW,KAAK,IAAI,GAAG,UAAU,KAAK,MAAM,EAAE,MAAM,CAAC;EAC3D,KAAK,IAAI,IAAI,GAAG,IAAI,UAAU,KAAK;GACjC,MAAM,OAAiB,CAAC;GACxB,KAAK,IAAI,IAAI,GAAG,IAAI,UAAU,KAAK;IACjC,MAAM,IAAI,UAAU,EAAE,GAAG,MAAM;IAC/B,KAAK,KAAK,QAAQ,GAAG,OAAO,IAAI,IAAI,EAAE,EAAE,KAAK,CAAC;GAChD;GACA,KAAK,KAAK,GAAG,EAAE,GAAG,KAAK,KAAK,IAAI,EAAE,EAAE,EAAE,GAAG,GAAG;EAC9C;EAEA,OAAO,KAAK,KAAK,IAAI;CACvB;CAEA,MAAM,QAAkB,CAAC,GAAG;CAC5B,KAAK,MAAM,OAAO,MAAM,QAAQ,MAAM,KAAK,UAAU,GAAG,CAAC;CACzD,IAAI,MAAM,OAAO,SAAS,KAAK,MAAM,KAAK,SAAS,GAAG,MAAM,KAAK,GAAG;CACpE,KAAK,MAAM,OAAO,MAAM,MAAM,MAAM,KAAK,UAAU,GAAG,CAAC;CACvD,MAAM,KAAK,GAAG;CACd,OAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,QAAQ,MAAc,OAAe,OAAsC;CAClF,MAAM,QAAQ,KAAK,IAAI,GAAG,QAAQ,YAAY,IAAI,CAAC;CACnD,IAAI,UAAU,SAAS,OAAO,IAAI,OAAO,KAAK,IAAI;CAClD,IAAI,UAAU,UAAU;EACtB,MAAM,OAAO,KAAK,MAAM,QAAQ,CAAC;EACjC,OAAO,IAAI,OAAO,IAAI,IAAI,OAAO,IAAI,OAAO,QAAQ,IAAI;CAC1D;CACA,OAAO,OAAO,IAAI,OAAO,KAAK;AAChC;;;ACvIA,MAAM,QAAQ;CACZ,IAAI;CACJ,OAAO;AACT;AAGA,MAAM,SAA+D;CACnE,SAAS;EAAE,MAAM;EAAM,OAAO;CAAQ;CACtC,WAAW;EAAE,MAAM;EAAK,OAAO;CAAiB;CAChD,MAAM;EAAE,MAAM;EAAM,OAAO;CAAO;CAClC,KAAK;EAAE,MAAM;EAAO,OAAO;CAAU;CACrC,SAAS;EAAE,MAAM;EAAK,OAAO;CAAO;AACtC;AAEA,MAAM,UAAU;;;;;;;;AAShB,SAAgB,gBAAgB,KAA+B;CAC7D,MAAM,IAAI,IAAI;CAEd,OAAO;EACL,aAAa,aAAa;GACxB,MAAM,QAAQ,SAAS,QAAQ,QAAQ,EAAE,CAAC,CAAC,MAAM,IAAI;GACrD,IAAI,YAAsB;GAE1B,MAAM,IAAI,MAAM,UAAU,IAAI,MAAM,EAAE,CAAC,MAAM,OAAO,IAAI,KAAA;GACxD,IAAI,GAAG;IACL,MAAM,YAAY,EAAE;IACpB,MAAM,QAAQ,OAAO;IACrB,YAAY,MAAM;IAClB,MAAM,OAAO,WAAW,UAAU,YAAY,CAAC;IAC/C,MAAM,KAAK,EAAE,IAAI,SAAS,CAAC,CAAC,GAAG,MAAM,KAAK,GAAG,MAAM;GACrD;GAOA,OAAO,GADU,MAAM,KAAK,SAAS,GAAG,EAAE,IAAI,SAAS,CAAC,CAAC,MAAM,KAAK,EAAE,GAAG,MAAM,CAAC,CAAC,KAAK,IACrE,EAAE;EACrB;EAEA,MAAM,mBAAmB,GAAG;EAG5B,WAAW,SAAU,iBAAiB,KAAK,IAAI,IAAI,OAAO,EAAE,OAAO,IAAI;EAEvE,WAAW,aAAa,EAAE,SAAS,QAAQ;EAE3C,UAAU,UAAU,EAAE,YAAY;GAChC,MAAM,QAAQ,IAAI;GAClB,MAAM,SAAS,SACZ,MAAM,IAAI,CAAC,CACX,KAAK,SAAS,OAAO,IAAI,OAAO,KAAK,IAAI,GAAG,QAAQ,YAAY,IAAI,CAAC,CAAC,CAAC,CAAC,CACxE,KAAK,IAAI;GACZ,OAAO,GAAG,EAAE,IAAI,YAAY,OAAmB,CAAC,CAAC,MAAM,EAAE;EAC3D;EAEA,UAAU,GAAG,EAAE,KAAK,MAAM,GAAG,OAAO,IAAI,KAAK,CAAC,EAAE;EAEhD,OAAO,aAAa;EAEpB,OAAO,UAAU,EAAE,WAAW,UAAU,MAAM,EAAE,OAAO,QAAQ,CAAC;EAEhE,YAAY,aAAa,GAAG,SAAS;EAErC,gBAAgB,aAAa,EAAE,gBAAgB,QAAQ;EAEvD,SAAS,aAAa,EAAE,OAAO,QAAQ;EAEvC,GAAG,oBAAoB,GAAG;EAC1B,GAAG,qBAAqB,GAAG;CAC7B;AACF"}
|
|
1
|
+
{"version":3,"file":"callbacks-DW8XKYKy.mjs","names":[],"sources":["../src/markdown/code.ts","../src/markdown/list.ts","../src/markdown/table.ts","../src/markdown/callbacks.ts"],"sourcesContent":["import type { MarkdownCtx } from \"./renderer.ts\"\nimport type { MdCallbacks } from \"./types.ts\"\n\nimport { stringWidth } from \"@zaly/shared/ansi\"\nimport { formatText } from \"../layout/text.ts\"\n\n/**\n * Build the `code` callback. The returned closure captures ctx/style/highlighter\n * and renders each fenced block: syntax-highlight (if available), pad lines to\n * widest-content + 1, apply `mdCodeBlock` as a bg-only backdrop, and prefix an\n * optional title line from `title=\"...\"`.\n */\nexport function createCodeCallback(ctx: MarkdownCtx): NonNullable<MdCallbacks[\"code\"]> {\n const s = ctx.style\n return (text, meta) => {\n text = text.replace(/\\n+$/, \"\") // trim to avoid extra blank lines after the block\n const highlighted = ctx.highlight?.(text, meta?.language) ?? text\n\n // For highlighted output the per-token fgs would clash with the slot's\n // fg; override fg with `\"inherit\"` so only bg + attrs wrap each row\n // and shiki's per-token colors show through on top of the backdrop.\n const wrap = highlighted === text ? s.mdCodeBlock : s.mdCodeBlock.fg(\"inherit\")\n const hpad = 2\n const maxWidth = ctx.width\n const lines = formatText(`\\n${highlighted}\\n`, {\n width: maxWidth - hpad * 2,\n wrap: \"word\",\n })\n let width = Math.max(...lines.map(stringWidth)) + hpad * 2\n width = Math.min(width, maxWidth) // leave room for the border\n // Left-align with a fixed `hpad` of leading whitespace; pad the\n // right to fill the bg out to `width`. Centering would push short\n // lines toward the middle, which reads as visually misaligned code.\n // Lines wider than the available room (width − hpad) drop the\n // leading pad so the bg ends at the cap rather than overshooting.\n const padded = lines.map((line) => {\n const w = stringWidth(line)\n if (w >= width - hpad) return wrap(line)\n return wrap(`${\" \".repeat(hpad)}${line}${\" \".repeat(width - w - hpad)}`)\n })\n const titleLine = meta?.title === undefined ? \"\" : `${s.mdCodeBlockTitle(meta.title)}\\n`\n // Leading `\\n` guards against raw-text siblings that don't emit a\n // paragraph-wrap (e.g. some inline-only list items where the parser\n // drops the paragraph). Containers (`listItem`, document) collapse\n // `\\n{3,}` → `\\n\\n` so paragraph + code doesn't double up.\n return `\\n${titleLine}${padded.join(\"\\n\")}\\n\\n`\n }\n}\n","// oxlint-disable no-multi-assign\nimport type { RenderCtx } from \"../core/ctx.ts\"\nimport type { MdCallbacks } from \"./types.ts\"\n\nimport { splitAnsi, stringWidth } from \"@zaly/shared/ansi\"\n\ninterface ListState {\n block: boolean\n items: string[]\n /** Formatted output, set when the list closes. The parent `listItem`\n * pulls it via `stack[depth+1]` and splices it onto its own body. */\n rendered?: string\n}\n\nconst BULLETS = [\"●\", \"○\", \"◆\", \"◇\"] as const\n\ntype ListCallbacks = Pick<MdCallbacks, \"list\" | \"listItem\">\n\n/**\n * Stack-based list renderer. Each depth has its own `ListState`. Inner\n * lists return `\"\"` and stash their formatted output on `state.rendered`;\n * the parent `listItem` pulls + splices, so inner output never reaches\n * the parent's `children` string. That keeps each level's loose-vs-tight\n * verdict independent — `children.endsWith(\"\\n\\n\")` is then the per-item\n * loose signal (parser only emits `<p>` for items in loose lists).\n *\n * Nested list lands at the end of its parent's body — `text, list, more`\n * is rare in real markdown and not worth the extra plumbing.\n */\nexport function createListCallbacks(ctx: RenderCtx): ListCallbacks {\n const s = ctx.style\n // Lazy-pushed on the first `listItem` at a given depth; consumed\n // either by the parent `listItem` (nested) or by `list(0)` (top).\n const stack: (ListState | undefined)[] = []\n\n return {\n list: (_children, meta) => {\n const state = stack[meta.depth]\n if (state === undefined || state.items.length === 0) return \"\"\n state.rendered = state.items.join(state.block ? \"\\n\\n\" : \"\\n\")\n if (meta.depth > 0) return \"\"\n // Top-level: defensive stack reset + block-contract `\\n\\n` trailer.\n stack.length = 0\n return `${state.rendered}\\n\\n`\n },\n\n listItem: (children, meta) => {\n let marker = s.mdListBullet(\n meta.ordered ? `${(meta.start ?? 1) + meta.index}.` : BULLETS[meta.depth % BULLETS.length]\n )\n if (meta.checked !== undefined) {\n const ref = meta.checked ? \"mdListChecked\" : \"mdListUnchecked\"\n marker += ` ${s.add(ref)(meta.checked ? \"[x]\" : \"[ ]\")}`\n }\n const indent = \" \".repeat(stringWidth(marker))\n // Pull nested list output (if any) off the stack.\n const nested = stack[meta.depth + 1]\n stack[meta.depth + 1] = undefined\n // Trailing `\\n\\n` = loose signal. Nested output is in `rendered`,\n // not `children`, so it can't pollute this check.\n const block = children.endsWith(\"\\n\\n\")\n const trimmed = children.replace(/\\s*\\n+$/, \"\").replace(/\\n{3,}/g, \"\\n\\n\")\n // Splice nested after the prose; loose item adds a blank-row gap.\n let body = trimmed\n if (nested?.rendered !== undefined) {\n const sep = block ? \"\\n\\n\" : \"\\n\"\n body = trimmed === \"\" ? nested.rendered : `${trimmed}${sep}${nested.rendered}`\n }\n const lines = splitAnsi(body)\n const content = lines\n .map((line, l) => {\n if (l === 0) return `${marker} ${line}`\n // Bare empty lines so loose `\\n\\n` separators stay clean.\n return line === \"\" ? \"\" : `${indent}${line}`\n })\n .join(\"\\n\")\n\n const state = (stack[meta.depth] ??= { block: false, items: [] })\n state.block ||= block\n state.items.push(content)\n return \"\"\n },\n }\n}\n","import type { RenderCtx } from \"../core/ctx.ts\"\nimport type { Layout } from \"../core/state.ts\"\nimport type { RowItem } from \"../layout/flex.ts\"\nimport type { MdCallbacks } from \"./types.ts\"\n\nimport { stringWidth } from \"@zaly/shared/ansi\"\nimport { allocateRow } from \"../layout/flex.ts\"\nimport { calcLayout, formatText } from \"../layout/text.ts\"\n\ntype CellAlign = \"left\" | \"center\" | \"right\"\n\ninterface TableCell {\n text: string\n align: CellAlign | undefined\n}\n\ninterface TableState {\n header: TableCell[][]\n body: TableCell[][]\n pending: TableCell[]\n pendingIsHeader: boolean\n}\n\ntype TableCallbacks = Pick<MdCallbacks, \"table\" | \"tbody\" | \"td\" | \"th\" | \"thead\" | \"tr\">\n\n/**\n * Build the table-related callbacks. They share a closure-local\n * `TableState` accumulator: `th`/`td` push cells, `tr` commits rows into\n * header or body, and `table` runs the final layout + reset. Tables can't\n * nest in markdown, so a single top-level slot is enough.\n */\nexport function createTableCallbacks(ctx: RenderCtx): TableCallbacks {\n const { style } = ctx\n let state: TableState | undefined\n const begin = (): TableState =>\n (state ??= { body: [], header: [], pending: [], pendingIsHeader: false })\n\n return {\n table: () => {\n // Children of th/td/tr/thead/tbody are empty strings; all content is\n // in `state`. Format, reset, emit.\n if (state === undefined) return \"\"\n const out = formatTable(state, ctx)\n state = undefined\n return `${out}\\n\\n`\n },\n\n tbody: () => \"\",\n\n td: (children, meta) => {\n const t = begin()\n t.pending.push({ align: meta?.align, text: children })\n t.pendingIsHeader = false\n return \"\"\n },\n\n th: (children, meta) => {\n const t = begin()\n t.pending.push({ align: meta?.align, text: style.mdTableHeader(children) })\n t.pendingIsHeader = true\n return \"\"\n },\n\n thead: () => \"\",\n\n tr: () => {\n if (state === undefined || state.pending.length === 0) return \"\"\n if (state.pendingIsHeader) state.header.push(state.pending)\n else state.body.push(state.pending)\n state.pending = []\n return \"\"\n },\n }\n}\n\nfunction formatTable(state: TableState, ctx: RenderCtx): string {\n const cols: Layout[] = []\n const cells = [...state.header, ...state.body]\n const colCount = Math.max(...cells.map((r) => r.length))\n\n // 1. Calculate min-content and max-content for each column\n for (let c = 0; c < colCount; c++) {\n for (const row of cells) {\n const cell = row[c] as TableCell | undefined\n if (!cell) continue // Safety guard for ragged rows\n const l = calcLayout(cell.text)\n cols[c] ??= { ...l }\n cols[c].width = Math.max(cols[c].width, l.width)\n cols[c].minWidth = Math.max(cols[c].minWidth, l.minWidth)\n }\n }\n const chromeWidth = 3 * colCount + 1\n\n const items: RowItem[] = cols.map((c) => ({\n flex: 1,\n intrinsicMin: c.minWidth,\n natural: c.width,\n }))\n\n const widths = allocateRow(items, { contentWidth: ctx.width - chromeWidth, gap: 0 })\n\n // 4. Render output string using the exact final Column Widths\n // Pre-style each piece of chrome once. Each column span on a rule row is\n // `widths[i] + 2` dashes (one cell of padding on each side of the cell).\n const style = ctx.style\n const v = style.mdTable(\"│\")\n const ruleRow = (left: string, mid: string, right: string): string =>\n style.mdTable(left + widths.map((w) => \"─\".repeat(w + 2)).join(mid) + right)\n const top = ruleRow(\"┌\", \"┬\", \"┐\")\n const sep = ruleRow(\"├\", \"┼\", \"┤\")\n const bot = ruleRow(\"└\", \"┴\", \"┘\")\n\n const renderRow = (row: TableCell[]): string => {\n const formatted = row.map((cell, c) =>\n formatText(cell.text, { indent: false, width: widths[c], wrapBg: false })\n )\n const rows: string[] = []\n const rowCount = Math.max(...formatted.map((r) => r.length))\n for (let r = 0; r < rowCount; r++) {\n const line: string[] = []\n for (let c = 0; c < colCount; c++) {\n const t = formatted[c]?.[r] ?? \"\"\n line.push(padCell(t, widths[c], row[c]?.align))\n }\n rows.push(`${v} ${line.join(` ${v} `)} ${v}`)\n }\n\n return rows.join(\"\\n\")\n }\n\n const lines: string[] = [top]\n for (const row of state.header) lines.push(renderRow(row))\n if (state.header.length > 0 && state.body.length > 0) lines.push(sep)\n for (const row of state.body) lines.push(renderRow(row))\n lines.push(bot)\n return lines.join(\"\\n\")\n}\n\nfunction padCell(text: string, width: number, align: CellAlign | undefined): string {\n const slack = Math.max(0, width - stringWidth(text))\n if (align === \"right\") return \" \".repeat(slack) + text\n if (align === \"center\") {\n const left = Math.floor(slack / 2)\n return \" \".repeat(left) + text + \" \".repeat(slack - left)\n }\n return text + \" \".repeat(slack)\n}\n","import type { AnyStyle, ThemeKey } from \"../style/types.ts\"\nimport type { MarkdownCtx } from \"./renderer.ts\"\nimport type { MdCallbacks } from \"./types.ts\"\n\nimport { stringWidth } from \"@zaly/shared/ansi\"\nimport { capitalize } from \"../layout/text.ts\"\nimport { hyperlink } from \"../style/ansi.ts\"\nimport { createCodeCallback } from \"./code.ts\"\nimport { createListCallbacks } from \"./list.ts\"\nimport { createTableCallbacks } from \"./table.ts\"\n\nconst icons = {\n hr: \"─\",\n quote: \"┃\",\n} as const\n\ntype AlertType = \"NOTE\" | \"TIP\" | \"WARNING\" | \"IMPORTANT\" | \"CAUTION\"\nconst alerts: Record<AlertType, { icon: string; style: AnyStyle }> = {\n CAUTION: { icon: \"✖ \", style: \"error\" },\n IMPORTANT: { icon: \"‼\", style: \"syntaxConstant\" },\n NOTE: { icon: \"ℹ \", style: \"info\" },\n TIP: { icon: \"🛈 \", style: \"success\" },\n WARNING: { icon: \"⚠\", style: \"warn\" },\n}\n\nconst alertRe = /^\\s*\\[!(NOTE|TIP|WARNING|IMPORTANT|CAUTION)\\]\\s*$/\n\n/**\n * Build the `MdCallbacks` that drive the theme-aware rendering. Exposed so\n * callers can invoke `renderMarkdown` directly when they need the string\n * output without the wrapping `Markdown` node (e.g. to embed markdown inside\n * a custom `Text` content function).\n * @internal\n */\nexport function createCallbacks(ctx: MarkdownCtx): MdCallbacks {\n const s = ctx.style\n\n return {\n blockquote: (children) => {\n const lines = children.replace(/\\n+$/, \"\").split(\"\\n\")\n let lineStyle: AnyStyle = \"mdQuote\"\n\n const m = lines.length >= 1 ? lines[0].match(alertRe) : undefined\n if (m) {\n const alertType = m[1] as AlertType\n const alert = alerts[alertType]\n lineStyle = alert.style\n const name = capitalize(alertType.toLowerCase())\n lines[0] = s.add(lineStyle)(`${alert.icon} ${name}`)\n }\n\n // Inner blocks (typically a paragraph) end with trailing newlines; if\n // we prefix those empty lines with \"│ \" they render as styled empty\n // rows between the last line of the quote and the block separator.\n // Trim first so the quote stops cleanly.\n const prefixed = lines.map((line) => `${s.add(lineStyle)(icons.quote)} ${line}`).join(\"\\n\")\n return `${prefixed}\\n\\n`\n },\n\n code: createCodeCallback(ctx),\n\n // Image markers use code spans for parser protection, not code styling.\n codespan: (text) => (/^<img id=\\d+>$/.test(text) ? text : s.mdCode(text)),\n\n emphasis: (children) => s.mdItalic(children),\n\n heading: (children, { level }) => {\n const width = ctx.width\n const padded = children\n .split(\"\\n\")\n .map((line) => line + \" \".repeat(Math.max(0, width - stringWidth(line))))\n .join(\"\\n\")\n return `${s.add(`mdHeading${level}` as ThemeKey)(padded)}\\n\\n`\n },\n\n hr: () => `${s.mdHr(icons.hr.repeat(ctx.width))}\\n\\n`,\n\n html: (children) => children,\n\n link: (children, { href }) => hyperlink(href, s.mdLink(children)),\n\n paragraph: (children) => `${children}\\n\\n`,\n\n strikethrough: (children) => s.mdStrikethrough(children),\n\n strong: (children) => s.mdBold(children),\n\n ...createListCallbacks(ctx),\n ...createTableCallbacks(ctx),\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { t as isShikiLang } from "./shiki-
|
|
1
|
+
import { t as isShikiLang } from "./shiki-DUUAIE6f.mjs";
|
|
2
2
|
import { hash, toError } from "@zaly/shared";
|
|
3
3
|
import { hasColors } from "@zaly/shared/env";
|
|
4
|
-
|
|
4
|
+
// src/shiki/client.ts
|
|
5
5
|
function isJob(s) {
|
|
6
6
|
return "key" in s && "lang" in s;
|
|
7
7
|
}
|
|
@@ -95,7 +95,7 @@ var ShikiWorkerClient = class {
|
|
|
95
95
|
async #createWorker() {
|
|
96
96
|
if (this.#worker) return this.#worker;
|
|
97
97
|
this.#workerPromise ??= (async () => {
|
|
98
|
-
const { createWorker } = await import("./worker-
|
|
98
|
+
const { createWorker } = await import("./worker-Mqi380Et.mjs");
|
|
99
99
|
const worker = await createWorker();
|
|
100
100
|
const ready = Promise.withResolvers();
|
|
101
101
|
let started = false;
|
|
@@ -189,7 +189,6 @@ var ShikiWorkerClient = class {
|
|
|
189
189
|
}
|
|
190
190
|
};
|
|
191
191
|
const shikiWorker = new ShikiWorkerClient();
|
|
192
|
-
//#endregion
|
|
193
192
|
export { shikiWorker as n, ShikiWorkerClient as t };
|
|
194
193
|
|
|
195
|
-
//# sourceMappingURL=client-
|
|
194
|
+
//# sourceMappingURL=client-BWm6dNZF.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-D7qz4TZt.mjs","names":[],"sources":["../src/shiki/client.ts"],"sourcesContent":["import type {\n ShikiJob,\n ShikiOpts,\n ShikiRequest,\n ShikiResult,\n ShikiWorkerMessage,\n ShikiWorkerRequest,\n} from \"./types.ts\"\nimport type { WorkerInstance } from \"./worker.ts\"\n\nimport { hash, toError } from \"@zaly/shared\"\nimport { hasColors } from \"@zaly/shared/env\"\nimport { isShikiLang } from \"../schemas/shiki.ts\"\n\nfunction isJob(s: ShikiJob | ShikiResult): s is ShikiJob {\n return \"key\" in s && \"lang\" in s\n}\n\nconst MAX_CACHE_SIZE = 100\n// Max jobs posted to the worker. Worker still processes serially.\nconst MAX_RUNNING = 2\nconst REQUEST_TIMEOUT = 30_000\n\nexport class ShikiWorkerClient {\n #id = 0\n #cache = new Map<string, ShikiJob>()\n #queue = new Map<number, ShikiJob>()\n #running = new Map<number, ShikiJob>()\n #results: ShikiResult[] = []\n #timeouts = new Map<number, ReturnType<typeof setTimeout>>()\n #worker?: WorkerInstance<ShikiWorkerRequest, ShikiWorkerMessage>\n #workerPromise?: Promise<WorkerInstance<ShikiWorkerRequest, ShikiWorkerMessage>>\n #timeout: number\n #updateScheduled: Promise<void> | undefined = undefined\n #updatePending = false\n\n constructor(opts: { timeout?: number } = {}) {\n this.#timeout = opts.timeout ?? REQUEST_TIMEOUT\n }\n\n key(req: ShikiRequest): string {\n return req.key ?? hash(JSON.stringify([req.lang, req.code, req.theme ?? \"default\"]))\n }\n\n toJob(req: ShikiRequest): ShikiJob | ShikiResult {\n const id = ++this.#id\n const key = this.key(req)\n if (!hasColors) return { error: \"terminal does not support colors\", id, key, value: req.code }\n if (!isShikiLang(req.lang))\n return { error: `unsupported language: ${req.lang}`, id, key, value: req.code }\n const cached = this.#cache.get(key)\n if (cached) {\n this.#cache.delete(key)\n this.#cache.set(key, cached) // bump to end of LRU\n return cached\n }\n while (this.#cache.size > MAX_CACHE_SIZE) this.#cache.delete(this.#cache.keys().next().value!)\n const ret: ShikiJob = { ...req, id, key, lang: req.lang, ...Promise.withResolvers() }\n this.#cache.set(key, ret)\n return ret\n }\n\n highlight(code: string, lang: string, opts: ShikiOpts = {}): Promise<ShikiResult> {\n const req: ShikiRequest = { code, lang, signal: opts.signal, theme: opts.theme }\n const job = this.toJob(req)\n if (!isJob(job)) return Promise.resolve(job)\n if (!job.scheduled) {\n this.#queue.set(job.id, job)\n job.signal?.addEventListener(\"abort\", () => this.update(), { once: true })\n this.update()\n }\n return job.promise\n }\n\n dispose(): void {\n const error = new Error(\"Shiki worker disposed\")\n this.#rejectAll(error)\n }\n\n async #request(job: ShikiJob): Promise<void> {\n job.scheduled = true\n this.#running.set(job.id, job)\n const worker = await this.#createWorker()\n const { signal: _s, promise: _prom, resolve: _res, reject: _rej, ...msg } = job\n const timeout = setTimeout(\n () =>\n this.#fail(\n new Error(`Shiki worker timed out after ${this.#timeout}ms (${job.lang}, ${job.key})`)\n ),\n this.#timeout\n )\n timeout.unref()\n this.#timeouts.set(job.id, timeout)\n // oxlint-disable-next-line unicorn/require-post-message-target-origin\n worker.postMessage(msg)\n }\n\n async #createWorker(): Promise<WorkerInstance<ShikiWorkerRequest, ShikiWorkerMessage>> {\n if (this.#worker) return this.#worker\n this.#workerPromise ??= (async () => {\n const { createWorker } = await import(\"./worker.ts\")\n const worker = await createWorker<ShikiWorkerRequest, ShikiWorkerMessage>()\n const ready = Promise.withResolvers<void>()\n let started = false\n const timeout = setTimeout(\n () => ready.reject(new Error(`Shiki worker did not start within ${this.#timeout}ms`)),\n this.#timeout\n )\n timeout.unref()\n worker.on(\"message\", (event) => {\n if (event.type === \"ready\") {\n started = true\n ready.resolve()\n return\n }\n this.#clearTimeout(event.id)\n this.#running.delete(event.id)\n this.#results.push(event)\n this.update()\n })\n worker.on(\"error\", (error) => {\n if (started) this.#fail(error)\n else ready.reject(error)\n })\n try {\n await ready.promise\n return worker\n } catch (error) {\n worker.terminate()\n throw error\n } finally {\n clearTimeout(timeout)\n }\n })()\n return (this.#worker = await this.#workerPromise)\n }\n\n update() {\n this.#updatePending = true\n if (this.#updateScheduled) return\n this.#updateScheduled = (async () => {\n while (this.#updatePending) {\n this.#updatePending = false\n // oxlint-disable-next-line no-await-in-loop\n await this.#update()\n }\n })()\n .catch((error) => this.#fail(error))\n .finally(() => {\n this.#updateScheduled = undefined\n if (this.#updatePending) this.update()\n })\n }\n\n async #update() {\n // Resolve pending results\n while (this.#results.length) {\n const result = this.#results.shift()!\n const job = this.#queue.get(result.id)\n if (job) {\n if (result.error) {\n const error = new Error(result.error)\n // oxlint-disable-next-line no-console\n console.error(\"Shiki highlighting failed:\", error)\n job.reject(error)\n } else job.resolve(result)\n\n // oxlint-disable-next-line no-await-in-loop\n await new Promise((resolve) => setImmediate(resolve)) // yield to event loop between batches\n this.#queue.delete(job.id)\n }\n }\n\n // Clean up aborted jobs\n for (const job of this.#queue.values()) {\n if (job.signal?.aborted && !job.scheduled) {\n job.reject(new Error(\"Job aborted\"))\n this.#queue.delete(job.id)\n this.#cache.delete(job.key)\n }\n }\n\n if (this.#running.size >= MAX_RUNNING || this.#queue.size === 0) return\n\n // Schedule new jobs up to concurrency limit, prioritizing the most recently requested ones (LIFO)\n const todo = [...this.#queue.values()]\n .filter((j) => !j.scheduled)\n .slice(-(MAX_RUNNING - this.#running.size))\n await Promise.all(todo.map((j) => this.#request(j)))\n }\n\n #clearTimeout(id: number): void {\n const timeout = this.#timeouts.get(id)\n if (timeout) clearTimeout(timeout)\n this.#timeouts.delete(id)\n }\n\n #fail(error: unknown): void {\n error = toError(error)\n // oxlint-disable-next-line no-console\n console.error(\"Shiki worker failed:\", error)\n this.#rejectAll(error)\n }\n\n #rejectAll(error: unknown): void {\n for (const { reject } of this.#queue.values()) reject(error)\n this.#cache.clear()\n this.#queue.clear()\n this.#running.clear()\n this.#results = []\n for (const timeout of this.#timeouts.values()) clearTimeout(timeout)\n this.#timeouts.clear()\n this.#updatePending = false\n this.#worker?.terminate()\n this.#worker = undefined\n this.#workerPromise = undefined\n }\n}\n\nexport const shikiWorker = new ShikiWorkerClient()\n"],"mappings":";;;;AAcA,SAAS,MAAM,GAA0C;CACvD,OAAO,SAAS,KAAK,UAAU;AACjC;AAEA,MAAM,iBAAiB;AAEvB,MAAM,cAAc;AACpB,MAAM,kBAAkB;AAExB,IAAa,oBAAb,MAA+B;CAC7B,MAAM;CACN,yBAAS,IAAI,IAAsB;CACnC,yBAAS,IAAI,IAAsB;CACnC,2BAAW,IAAI,IAAsB;CACrC,WAA0B,CAAC;CAC3B,4BAAY,IAAI,IAA2C;CAC3D;CACA;CACA;CACA,mBAA8C,KAAA;CAC9C,iBAAiB;CAEjB,YAAY,OAA6B,CAAC,GAAG;EAC3C,KAAK,WAAW,KAAK,WAAW;CAClC;CAEA,IAAI,KAA2B;EAC7B,OAAO,IAAI,OAAO,KAAK,KAAK,UAAU;GAAC,IAAI;GAAM,IAAI;GAAM,IAAI,SAAS;EAAS,CAAC,CAAC;CACrF;CAEA,MAAM,KAA2C;EAC/C,MAAM,KAAK,EAAE,KAAK;EAClB,MAAM,MAAM,KAAK,IAAI,GAAG;EACxB,IAAI,CAAC,WAAW,OAAO;GAAE,OAAO;GAAoC;GAAI;GAAK,OAAO,IAAI;EAAK;EAC7F,IAAI,CAAC,YAAY,IAAI,IAAI,GACvB,OAAO;GAAE,OAAO,yBAAyB,IAAI;GAAQ;GAAI;GAAK,OAAO,IAAI;EAAK;EAChF,MAAM,SAAS,KAAK,OAAO,IAAI,GAAG;EAClC,IAAI,QAAQ;GACV,KAAK,OAAO,OAAO,GAAG;GACtB,KAAK,OAAO,IAAI,KAAK,MAAM;GAC3B,OAAO;EACT;EACA,OAAO,KAAK,OAAO,OAAO,gBAAgB,KAAK,OAAO,OAAO,KAAK,OAAO,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,KAAM;EAC7F,MAAM,MAAgB;GAAE,GAAG;GAAK;GAAI;GAAK,MAAM,IAAI;GAAM,GAAG,QAAQ,cAAc;EAAE;EACpF,KAAK,OAAO,IAAI,KAAK,GAAG;EACxB,OAAO;CACT;CAEA,UAAU,MAAc,MAAc,OAAkB,CAAC,GAAyB;EAChF,MAAM,MAAoB;GAAE;GAAM;GAAM,QAAQ,KAAK;GAAQ,OAAO,KAAK;EAAM;EAC/E,MAAM,MAAM,KAAK,MAAM,GAAG;EAC1B,IAAI,CAAC,MAAM,GAAG,GAAG,OAAO,QAAQ,QAAQ,GAAG;EAC3C,IAAI,CAAC,IAAI,WAAW;GAClB,KAAK,OAAO,IAAI,IAAI,IAAI,GAAG;GAC3B,IAAI,QAAQ,iBAAiB,eAAe,KAAK,OAAO,GAAG,EAAE,MAAM,KAAK,CAAC;GACzE,KAAK,OAAO;EACd;EACA,OAAO,IAAI;CACb;CAEA,UAAgB;EACd,MAAM,wBAAQ,IAAI,MAAM,uBAAuB;EAC/C,KAAK,WAAW,KAAK;CACvB;CAEA,MAAM,SAAS,KAA8B;EAC3C,IAAI,YAAY;EAChB,KAAK,SAAS,IAAI,IAAI,IAAI,GAAG;EAC7B,MAAM,SAAS,MAAM,KAAK,cAAc;EACxC,MAAM,EAAE,QAAQ,IAAI,SAAS,OAAO,SAAS,MAAM,QAAQ,MAAM,GAAG,QAAQ;EAC5E,MAAM,UAAU,iBAEZ,KAAK,sBACH,IAAI,MAAM,gCAAgC,KAAK,SAAS,MAAM,IAAI,KAAK,IAAI,IAAI,IAAI,EAAE,CACvF,GACF,KAAK,QACP;EACA,QAAQ,MAAM;EACd,KAAK,UAAU,IAAI,IAAI,IAAI,OAAO;EAElC,OAAO,YAAY,GAAG;CACxB;CAEA,MAAM,gBAAiF;EACrF,IAAI,KAAK,SAAS,OAAO,KAAK;EAC9B,KAAK,oBAAoB,YAAY;GACnC,MAAM,EAAE,iBAAiB,MAAM,OAAO;GACtC,MAAM,SAAS,MAAM,aAAqD;GAC1E,MAAM,QAAQ,QAAQ,cAAoB;GAC1C,IAAI,UAAU;GACd,MAAM,UAAU,iBACR,MAAM,uBAAO,IAAI,MAAM,qCAAqC,KAAK,SAAS,GAAG,CAAC,GACpF,KAAK,QACP;GACA,QAAQ,MAAM;GACd,OAAO,GAAG,YAAY,UAAU;IAC9B,IAAI,MAAM,SAAS,SAAS;KAC1B,UAAU;KACV,MAAM,QAAQ;KACd;IACF;IACA,KAAK,cAAc,MAAM,EAAE;IAC3B,KAAK,SAAS,OAAO,MAAM,EAAE;IAC7B,KAAK,SAAS,KAAK,KAAK;IACxB,KAAK,OAAO;GACd,CAAC;GACD,OAAO,GAAG,UAAU,UAAU;IAC5B,IAAI,SAAS,KAAK,MAAM,KAAK;SACxB,MAAM,OAAO,KAAK;GACzB,CAAC;GACD,IAAI;IACF,MAAM,MAAM;IACZ,OAAO;GACT,SAAS,OAAO;IACd,OAAO,UAAU;IACjB,MAAM;GACR,UAAU;IACR,aAAa,OAAO;GACtB;EACF,EAAA,CAAG;EACH,OAAQ,KAAK,UAAU,MAAM,KAAK;CACpC;CAEA,SAAS;EACP,KAAK,iBAAiB;EACtB,IAAI,KAAK,kBAAkB;EAC3B,KAAK,oBAAoB,YAAY;GACnC,OAAO,KAAK,gBAAgB;IAC1B,KAAK,iBAAiB;IAEtB,MAAM,KAAK,QAAQ;GACrB;EACF,EAAA,CAAG,CAAC,CACD,OAAO,UAAU,KAAK,MAAM,KAAK,CAAC,CAAC,CACnC,cAAc;GACb,KAAK,mBAAmB,KAAA;GACxB,IAAI,KAAK,gBAAgB,KAAK,OAAO;EACvC,CAAC;CACL;CAEA,MAAM,UAAU;EAEd,OAAO,KAAK,SAAS,QAAQ;GAC3B,MAAM,SAAS,KAAK,SAAS,MAAM;GACnC,MAAM,MAAM,KAAK,OAAO,IAAI,OAAO,EAAE;GACrC,IAAI,KAAK;IACP,IAAI,OAAO,OAAO;KAChB,MAAM,QAAQ,IAAI,MAAM,OAAO,KAAK;KAEpC,QAAQ,MAAM,8BAA8B,KAAK;KACjD,IAAI,OAAO,KAAK;IAClB,OAAO,IAAI,QAAQ,MAAM;IAGzB,MAAM,IAAI,SAAS,YAAY,aAAa,OAAO,CAAC;IACpD,KAAK,OAAO,OAAO,IAAI,EAAE;GAC3B;EACF;EAGA,KAAK,MAAM,OAAO,KAAK,OAAO,OAAO,GACnC,IAAI,IAAI,QAAQ,WAAW,CAAC,IAAI,WAAW;GACzC,IAAI,uBAAO,IAAI,MAAM,aAAa,CAAC;GACnC,KAAK,OAAO,OAAO,IAAI,EAAE;GACzB,KAAK,OAAO,OAAO,IAAI,GAAG;EAC5B;EAGF,IAAI,KAAK,SAAS,QAAQ,eAAe,KAAK,OAAO,SAAS,GAAG;EAGjE,MAAM,OAAO,CAAC,GAAG,KAAK,OAAO,OAAO,CAAC,CAAC,CACnC,QAAQ,MAAM,CAAC,EAAE,SAAS,CAAC,CAC3B,MAAM,EAAE,cAAc,KAAK,SAAS,KAAK;EAC5C,MAAM,QAAQ,IAAI,KAAK,KAAK,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC;CACrD;CAEA,cAAc,IAAkB;EAC9B,MAAM,UAAU,KAAK,UAAU,IAAI,EAAE;EACrC,IAAI,SAAS,aAAa,OAAO;EACjC,KAAK,UAAU,OAAO,EAAE;CAC1B;CAEA,MAAM,OAAsB;EAC1B,QAAQ,QAAQ,KAAK;EAErB,QAAQ,MAAM,wBAAwB,KAAK;EAC3C,KAAK,WAAW,KAAK;CACvB;CAEA,WAAW,OAAsB;EAC/B,KAAK,MAAM,EAAE,YAAY,KAAK,OAAO,OAAO,GAAG,OAAO,KAAK;EAC3D,KAAK,OAAO,MAAM;EAClB,KAAK,OAAO,MAAM;EAClB,KAAK,SAAS,MAAM;EACpB,KAAK,WAAW,CAAC;EACjB,KAAK,MAAM,WAAW,KAAK,UAAU,OAAO,GAAG,aAAa,OAAO;EACnE,KAAK,UAAU,MAAM;EACrB,KAAK,iBAAiB;EACtB,KAAK,SAAS,UAAU;EACxB,KAAK,UAAU,KAAA;EACf,KAAK,iBAAiB,KAAA;CACxB;AACF;AAEA,MAAa,cAAc,IAAI,kBAAkB"}
|
|
1
|
+
{"version":3,"file":"client-BWm6dNZF.mjs","names":[],"sources":["../src/shiki/client.ts"],"sourcesContent":["import type {\n ShikiJob,\n ShikiOpts,\n ShikiRequest,\n ShikiResult,\n ShikiWorkerMessage,\n ShikiWorkerRequest,\n} from \"./types.ts\"\nimport type { WorkerInstance } from \"./worker.ts\"\n\nimport { hash, toError } from \"@zaly/shared\"\nimport { hasColors } from \"@zaly/shared/env\"\nimport { isShikiLang } from \"../schemas/shiki.ts\"\n\nfunction isJob(s: ShikiJob | ShikiResult): s is ShikiJob {\n return \"key\" in s && \"lang\" in s\n}\n\nconst MAX_CACHE_SIZE = 100\n// Max jobs posted to the worker. Worker still processes serially.\nconst MAX_RUNNING = 2\nconst REQUEST_TIMEOUT = 30_000\n\nexport class ShikiWorkerClient {\n #id = 0\n #cache = new Map<string, ShikiJob>()\n #queue = new Map<number, ShikiJob>()\n #running = new Map<number, ShikiJob>()\n #results: ShikiResult[] = []\n #timeouts = new Map<number, ReturnType<typeof setTimeout>>()\n #worker?: WorkerInstance<ShikiWorkerRequest, ShikiWorkerMessage>\n #workerPromise?: Promise<WorkerInstance<ShikiWorkerRequest, ShikiWorkerMessage>>\n #timeout: number\n #updateScheduled: Promise<void> | undefined = undefined\n #updatePending = false\n\n constructor(opts: { timeout?: number } = {}) {\n this.#timeout = opts.timeout ?? REQUEST_TIMEOUT\n }\n\n key(req: ShikiRequest): string {\n return req.key ?? hash(JSON.stringify([req.lang, req.code, req.theme ?? \"default\"]))\n }\n\n toJob(req: ShikiRequest): ShikiJob | ShikiResult {\n const id = ++this.#id\n const key = this.key(req)\n if (!hasColors) return { error: \"terminal does not support colors\", id, key, value: req.code }\n if (!isShikiLang(req.lang))\n return { error: `unsupported language: ${req.lang}`, id, key, value: req.code }\n const cached = this.#cache.get(key)\n if (cached) {\n this.#cache.delete(key)\n this.#cache.set(key, cached) // bump to end of LRU\n return cached\n }\n while (this.#cache.size > MAX_CACHE_SIZE) this.#cache.delete(this.#cache.keys().next().value!)\n const ret: ShikiJob = { ...req, id, key, lang: req.lang, ...Promise.withResolvers() }\n this.#cache.set(key, ret)\n return ret\n }\n\n highlight(code: string, lang: string, opts: ShikiOpts = {}): Promise<ShikiResult> {\n const req: ShikiRequest = { code, lang, signal: opts.signal, theme: opts.theme }\n const job = this.toJob(req)\n if (!isJob(job)) return Promise.resolve(job)\n if (!job.scheduled) {\n this.#queue.set(job.id, job)\n job.signal?.addEventListener(\"abort\", () => this.update(), { once: true })\n this.update()\n }\n return job.promise\n }\n\n dispose(): void {\n const error = new Error(\"Shiki worker disposed\")\n this.#rejectAll(error)\n }\n\n async #request(job: ShikiJob): Promise<void> {\n job.scheduled = true\n this.#running.set(job.id, job)\n const worker = await this.#createWorker()\n const { signal: _s, promise: _prom, resolve: _res, reject: _rej, ...msg } = job\n const timeout = setTimeout(\n () =>\n this.#fail(\n new Error(`Shiki worker timed out after ${this.#timeout}ms (${job.lang}, ${job.key})`)\n ),\n this.#timeout\n )\n timeout.unref()\n this.#timeouts.set(job.id, timeout)\n // oxlint-disable-next-line unicorn/require-post-message-target-origin\n worker.postMessage(msg)\n }\n\n async #createWorker(): Promise<WorkerInstance<ShikiWorkerRequest, ShikiWorkerMessage>> {\n if (this.#worker) return this.#worker\n this.#workerPromise ??= (async () => {\n const { createWorker } = await import(\"./worker.ts\")\n const worker = await createWorker<ShikiWorkerRequest, ShikiWorkerMessage>()\n const ready = Promise.withResolvers<void>()\n let started = false\n const timeout = setTimeout(\n () => ready.reject(new Error(`Shiki worker did not start within ${this.#timeout}ms`)),\n this.#timeout\n )\n timeout.unref()\n worker.on(\"message\", (event) => {\n if (event.type === \"ready\") {\n started = true\n ready.resolve()\n return\n }\n this.#clearTimeout(event.id)\n this.#running.delete(event.id)\n this.#results.push(event)\n this.update()\n })\n worker.on(\"error\", (error) => {\n if (started) this.#fail(error)\n else ready.reject(error)\n })\n try {\n await ready.promise\n return worker\n } catch (error) {\n worker.terminate()\n throw error\n } finally {\n clearTimeout(timeout)\n }\n })()\n return (this.#worker = await this.#workerPromise)\n }\n\n update() {\n this.#updatePending = true\n if (this.#updateScheduled) return\n this.#updateScheduled = (async () => {\n while (this.#updatePending) {\n this.#updatePending = false\n // oxlint-disable-next-line no-await-in-loop\n await this.#update()\n }\n })()\n .catch((error) => this.#fail(error))\n .finally(() => {\n this.#updateScheduled = undefined\n if (this.#updatePending) this.update()\n })\n }\n\n async #update() {\n // Resolve pending results\n while (this.#results.length) {\n const result = this.#results.shift()!\n const job = this.#queue.get(result.id)\n if (job) {\n if (result.error) {\n const error = new Error(result.error)\n // oxlint-disable-next-line no-console\n console.error(\"Shiki highlighting failed:\", error)\n job.reject(error)\n } else job.resolve(result)\n\n // oxlint-disable-next-line no-await-in-loop\n await new Promise((resolve) => setImmediate(resolve)) // yield to event loop between batches\n this.#queue.delete(job.id)\n }\n }\n\n // Clean up aborted jobs\n for (const job of this.#queue.values()) {\n if (job.signal?.aborted && !job.scheduled) {\n job.reject(new Error(\"Job aborted\"))\n this.#queue.delete(job.id)\n this.#cache.delete(job.key)\n }\n }\n\n if (this.#running.size >= MAX_RUNNING || this.#queue.size === 0) return\n\n // Schedule new jobs up to concurrency limit, prioritizing the most recently requested ones (LIFO)\n const todo = [...this.#queue.values()]\n .filter((j) => !j.scheduled)\n .slice(-(MAX_RUNNING - this.#running.size))\n await Promise.all(todo.map((j) => this.#request(j)))\n }\n\n #clearTimeout(id: number): void {\n const timeout = this.#timeouts.get(id)\n if (timeout) clearTimeout(timeout)\n this.#timeouts.delete(id)\n }\n\n #fail(error: unknown): void {\n error = toError(error)\n // oxlint-disable-next-line no-console\n console.error(\"Shiki worker failed:\", error)\n this.#rejectAll(error)\n }\n\n #rejectAll(error: unknown): void {\n for (const { reject } of this.#queue.values()) reject(error)\n this.#cache.clear()\n this.#queue.clear()\n this.#running.clear()\n this.#results = []\n for (const timeout of this.#timeouts.values()) clearTimeout(timeout)\n this.#timeouts.clear()\n this.#updatePending = false\n this.#worker?.terminate()\n this.#worker = undefined\n this.#workerPromise = undefined\n }\n}\n\nexport const shikiWorker = new ShikiWorkerClient()\n"],"mappings":";;;;AAcA,SAAS,MAAM,GAA0C;CACvD,OAAO,SAAS,KAAK,UAAU;AACjC;AAEA,MAAM,iBAAiB;AAEvB,MAAM,cAAc;AACpB,MAAM,kBAAkB;AAExB,IAAa,oBAAb,MAA+B;CAC7B,MAAM;CACN,yBAAS,IAAI,IAAsB;CACnC,yBAAS,IAAI,IAAsB;CACnC,2BAAW,IAAI,IAAsB;CACrC,WAA0B,CAAC;CAC3B,4BAAY,IAAI,IAA2C;CAC3D;CACA;CACA;CACA,mBAA8C,KAAA;CAC9C,iBAAiB;CAEjB,YAAY,OAA6B,CAAC,GAAG;EAC3C,KAAK,WAAW,KAAK,WAAW;CAClC;CAEA,IAAI,KAA2B;EAC7B,OAAO,IAAI,OAAO,KAAK,KAAK,UAAU;GAAC,IAAI;GAAM,IAAI;GAAM,IAAI,SAAS;EAAS,CAAC,CAAC;CACrF;CAEA,MAAM,KAA2C;EAC/C,MAAM,KAAK,EAAE,KAAK;EAClB,MAAM,MAAM,KAAK,IAAI,GAAG;EACxB,IAAI,CAAC,WAAW,OAAO;GAAE,OAAO;GAAoC;GAAI;GAAK,OAAO,IAAI;EAAK;EAC7F,IAAI,CAAC,YAAY,IAAI,IAAI,GACvB,OAAO;GAAE,OAAO,yBAAyB,IAAI;GAAQ;GAAI;GAAK,OAAO,IAAI;EAAK;EAChF,MAAM,SAAS,KAAK,OAAO,IAAI,GAAG;EAClC,IAAI,QAAQ;GACV,KAAK,OAAO,OAAO,GAAG;GACtB,KAAK,OAAO,IAAI,KAAK,MAAM;GAC3B,OAAO;EACT;EACA,OAAO,KAAK,OAAO,OAAO,gBAAgB,KAAK,OAAO,OAAO,KAAK,OAAO,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,KAAM;EAC7F,MAAM,MAAgB;GAAE,GAAG;GAAK;GAAI;GAAK,MAAM,IAAI;GAAM,GAAG,QAAQ,cAAc;EAAE;EACpF,KAAK,OAAO,IAAI,KAAK,GAAG;EACxB,OAAO;CACT;CAEA,UAAU,MAAc,MAAc,OAAkB,CAAC,GAAyB;EAChF,MAAM,MAAoB;GAAE;GAAM;GAAM,QAAQ,KAAK;GAAQ,OAAO,KAAK;EAAM;EAC/E,MAAM,MAAM,KAAK,MAAM,GAAG;EAC1B,IAAI,CAAC,MAAM,GAAG,GAAG,OAAO,QAAQ,QAAQ,GAAG;EAC3C,IAAI,CAAC,IAAI,WAAW;GAClB,KAAK,OAAO,IAAI,IAAI,IAAI,GAAG;GAC3B,IAAI,QAAQ,iBAAiB,eAAe,KAAK,OAAO,GAAG,EAAE,MAAM,KAAK,CAAC;GACzE,KAAK,OAAO;EACd;EACA,OAAO,IAAI;CACb;CAEA,UAAgB;EACd,MAAM,wBAAQ,IAAI,MAAM,uBAAuB;EAC/C,KAAK,WAAW,KAAK;CACvB;CAEA,MAAM,SAAS,KAA8B;EAC3C,IAAI,YAAY;EAChB,KAAK,SAAS,IAAI,IAAI,IAAI,GAAG;EAC7B,MAAM,SAAS,MAAM,KAAK,cAAc;EACxC,MAAM,EAAE,QAAQ,IAAI,SAAS,OAAO,SAAS,MAAM,QAAQ,MAAM,GAAG,QAAQ;EAC5E,MAAM,UAAU,iBAEZ,KAAK,sBACH,IAAI,MAAM,gCAAgC,KAAK,SAAS,MAAM,IAAI,KAAK,IAAI,IAAI,IAAI,EAAE,CACvF,GACF,KAAK,QACP;EACA,QAAQ,MAAM;EACd,KAAK,UAAU,IAAI,IAAI,IAAI,OAAO;EAElC,OAAO,YAAY,GAAG;CACxB;CAEA,MAAM,gBAAiF;EACrF,IAAI,KAAK,SAAS,OAAO,KAAK;EAC9B,KAAK,oBAAoB,YAAY;GACnC,MAAM,EAAE,iBAAiB,MAAM,OAAO;GACtC,MAAM,SAAS,MAAM,aAAqD;GAC1E,MAAM,QAAQ,QAAQ,cAAoB;GAC1C,IAAI,UAAU;GACd,MAAM,UAAU,iBACR,MAAM,uBAAO,IAAI,MAAM,qCAAqC,KAAK,SAAS,GAAG,CAAC,GACpF,KAAK,QACP;GACA,QAAQ,MAAM;GACd,OAAO,GAAG,YAAY,UAAU;IAC9B,IAAI,MAAM,SAAS,SAAS;KAC1B,UAAU;KACV,MAAM,QAAQ;KACd;IACF;IACA,KAAK,cAAc,MAAM,EAAE;IAC3B,KAAK,SAAS,OAAO,MAAM,EAAE;IAC7B,KAAK,SAAS,KAAK,KAAK;IACxB,KAAK,OAAO;GACd,CAAC;GACD,OAAO,GAAG,UAAU,UAAU;IAC5B,IAAI,SAAS,KAAK,MAAM,KAAK;SACxB,MAAM,OAAO,KAAK;GACzB,CAAC;GACD,IAAI;IACF,MAAM,MAAM;IACZ,OAAO;GACT,SAAS,OAAO;IACd,OAAO,UAAU;IACjB,MAAM;GACR,UAAU;IACR,aAAa,OAAO;GACtB;EACF,EAAA,CAAG;EACH,OAAQ,KAAK,UAAU,MAAM,KAAK;CACpC;CAEA,SAAS;EACP,KAAK,iBAAiB;EACtB,IAAI,KAAK,kBAAkB;EAC3B,KAAK,oBAAoB,YAAY;GACnC,OAAO,KAAK,gBAAgB;IAC1B,KAAK,iBAAiB;IAEtB,MAAM,KAAK,QAAQ;GACrB;EACF,EAAA,CAAG,CAAC,CACD,OAAO,UAAU,KAAK,MAAM,KAAK,CAAC,CAAC,CACnC,cAAc;GACb,KAAK,mBAAmB,KAAA;GACxB,IAAI,KAAK,gBAAgB,KAAK,OAAO;EACvC,CAAC;CACL;CAEA,MAAM,UAAU;EAEd,OAAO,KAAK,SAAS,QAAQ;GAC3B,MAAM,SAAS,KAAK,SAAS,MAAM;GACnC,MAAM,MAAM,KAAK,OAAO,IAAI,OAAO,EAAE;GACrC,IAAI,KAAK;IACP,IAAI,OAAO,OAAO;KAChB,MAAM,QAAQ,IAAI,MAAM,OAAO,KAAK;KAEpC,QAAQ,MAAM,8BAA8B,KAAK;KACjD,IAAI,OAAO,KAAK;IAClB,OAAO,IAAI,QAAQ,MAAM;IAGzB,MAAM,IAAI,SAAS,YAAY,aAAa,OAAO,CAAC;IACpD,KAAK,OAAO,OAAO,IAAI,EAAE;GAC3B;EACF;EAGA,KAAK,MAAM,OAAO,KAAK,OAAO,OAAO,GACnC,IAAI,IAAI,QAAQ,WAAW,CAAC,IAAI,WAAW;GACzC,IAAI,uBAAO,IAAI,MAAM,aAAa,CAAC;GACnC,KAAK,OAAO,OAAO,IAAI,EAAE;GACzB,KAAK,OAAO,OAAO,IAAI,GAAG;EAC5B;EAGF,IAAI,KAAK,SAAS,QAAQ,eAAe,KAAK,OAAO,SAAS,GAAG;EAGjE,MAAM,OAAO,CAAC,GAAG,KAAK,OAAO,OAAO,CAAC,CAAC,CACnC,QAAQ,MAAM,CAAC,EAAE,SAAS,CAAC,CAC3B,MAAM,EAAE,cAAc,KAAK,SAAS,KAAK;EAC5C,MAAM,QAAQ,IAAI,KAAK,KAAK,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC;CACrD;CAEA,cAAc,IAAkB;EAC9B,MAAM,UAAU,KAAK,UAAU,IAAI,EAAE;EACrC,IAAI,SAAS,aAAa,OAAO;EACjC,KAAK,UAAU,OAAO,EAAE;CAC1B;CAEA,MAAM,OAAsB;EAC1B,QAAQ,QAAQ,KAAK;EAErB,QAAQ,MAAM,wBAAwB,KAAK;EAC3C,KAAK,WAAW,KAAK;CACvB;CAEA,WAAW,OAAsB;EAC/B,KAAK,MAAM,EAAE,YAAY,KAAK,OAAO,OAAO,GAAG,OAAO,KAAK;EAC3D,KAAK,OAAO,MAAM;EAClB,KAAK,OAAO,MAAM;EAClB,KAAK,SAAS,MAAM;EACpB,KAAK,WAAW,CAAC;EACjB,KAAK,MAAM,WAAW,KAAK,UAAU,OAAO,GAAG,aAAa,OAAO;EACnE,KAAK,UAAU,MAAM;EACrB,KAAK,iBAAiB;EACtB,KAAK,SAAS,UAAU;EACxB,KAAK,UAAU,KAAA;EACf,KAAK,iBAAiB,KAAA;CACxB;AACF;AAEA,MAAa,cAAc,IAAI,kBAAkB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { n as shikiWorker } from "./client-BWm6dNZF.mjs";
|
|
@@ -5,7 +5,7 @@ import { join } from "pathe";
|
|
|
5
5
|
import { Spawn, spawnText, spawnWithInput, which } from "@zaly/shared/process";
|
|
6
6
|
import { tmpdir } from "node:os";
|
|
7
7
|
import { platform } from "node:process";
|
|
8
|
-
|
|
8
|
+
// src/input/clipboard.ts
|
|
9
9
|
let cached;
|
|
10
10
|
let cacheResolved = false;
|
|
11
11
|
/** Detect + cache the clipboard provider for this process. Returns
|
|
@@ -400,7 +400,6 @@ function writeTempPath(prefix, ext) {
|
|
|
400
400
|
const dir = mkdtempSync(join(tmpdir(), prefix));
|
|
401
401
|
return join(dir, `clip${ext}`);
|
|
402
402
|
}
|
|
403
|
-
//#endregion
|
|
404
403
|
export { clipboard as t };
|
|
405
404
|
|
|
406
|
-
//# sourceMappingURL=clipboard-
|
|
405
|
+
//# sourceMappingURL=clipboard-UEoX9sup.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clipboard-CNPBTX7j.mjs","names":[],"sources":["../src/input/clipboard.ts"],"sourcesContent":["import { safeStat } from \"@zaly/shared\"\nimport { isSSH } from \"@zaly/shared/env\"\nimport { Spawn, spawnText, spawnWithInput, which } from \"@zaly/shared/process\"\nimport { mkdtempSync, writeFileSync } from \"node:fs\"\nimport { tmpdir } from \"node:os\"\nimport { platform } from \"node:process\"\nimport { join } from \"pathe\"\n\n/**\n * Read text or image content from the system clipboard.\n *\n * Agent CLIs need this to make `ctrl-v` \"just work\" — most terminals\n * don't forward clipboard paste events (they only forward typed keys),\n * so the app has to query the OS clipboard itself via the platform's\n * native tool. The detection order mirrors Neovim's clipboard provider:\n *\n * 1. macOS → `pbpaste` + `osascript` (for images)\n * 2. Wayland → `wl-paste`\n * 3. X11 (xsel) → `xsel`\n * 4. X11 (xclip) → `xclip`\n * 5. WSL → `win32yank.exe`\n * 6. Windows → PowerShell `Get-Clipboard`\n *\n * The chosen provider is cached per process. Spawn primitives live in\n * `@zaly/shared/process` — this file is just glue plus the per-platform\n * AppleScript/PowerShell snippets.\n */\n\n// ---- types -----------------------------------------------------------\n\n/** Image bytes on the clipboard, already written to a temp PNG. */\nexport interface ClipboardImage {\n kind: \"image\"\n /** Absolute path to a temporary PNG file. Caller takes ownership. */\n path: string\n /** MIME type. PNG only for now; kept for future formats. */\n type: \"image/png\"\n}\n\n/** One or more file references on the clipboard — what you get when\n * the user copies a file in their file manager and pastes. */\nexport interface ClipboardFiles {\n kind: \"files\"\n /** Absolute filesystem paths. */\n paths: string[]\n}\n\n/** Plain text on the clipboard — the common case. */\nexport interface ClipboardText {\n kind: \"text\"\n text: string\n}\n\n/** Anything the clipboard can hold that this library surfaces. */\nexport type ClipboardContent = ClipboardImage | ClipboardFiles | ClipboardText\n\n/** One of the content kinds `ClipboardContent` can take. Derived from\n * the union so adding a new variant doesn't require updating this\n * alias separately. */\nexport type ClipboardKind = ClipboardContent[\"kind\"]\n\n/** Internal provider contract. Each backend owns its own \"probe types,\n * pick best, fetch once\" logic — on platforms like Wayland and X11\n * that can enumerate available types, this avoids doing three or four\n * probe round-trips for every paste.\n *\n * `read(kind?)` takes an optional hint: when specified, the backend\n * can skip type enumeration and fetch that kind directly (fast path\n * for callers that already know what they want). When omitted, the\n * backend returns the richest available content. `writeText` is\n * optional since not every backend supports writes. */\ninterface Provider {\n readonly id: string\n read: (kind?: ClipboardKind) => Promise<ClipboardContent | undefined>\n writeText?: (text: string) => Promise<boolean>\n}\n\n// ---- public API ------------------------------------------------------\n\nlet cached: Provider | undefined\nlet cacheResolved = false\n\n/** Detect + cache the clipboard provider for this process. Returns\n * `undefined` when no provider is available. */\nfunction getClipboard(): Provider | undefined {\n if (cacheResolved) return cached\n cached = detect()\n cacheResolved = true\n return cached\n}\n\n/**\n * Read the system clipboard, returning the richest content available.\n *\n * Priority order (decided by each backend internally):\n * 1. **image** — raw image bytes on the clipboard (e.g. a screenshot\n * from `cmd-shift-4`, or an image copied from a web page).\n * 2. **files** — file URIs (e.g. the user copied a PDF / video in\n * their file manager).\n * 3. **text** — plain text.\n *\n * Resolves to `undefined` when the clipboard is empty, no provider is\n * available, or every read fails. Never throws — clipboard access is\n * best-effort.\n */\nasync function readClipboard(): Promise<ClipboardContent | undefined>\nasync function readClipboard(kind: \"text\"): Promise<ClipboardText | undefined>\nasync function readClipboard(kind: \"image\"): Promise<ClipboardImage | undefined>\nasync function readClipboard(kind: \"files\"): Promise<ClipboardFiles | undefined>\nasync function readClipboard(kind?: ClipboardKind): Promise<ClipboardContent | undefined> {\n const p = getClipboard()\n if (!p) return undefined\n try {\n return await p.read(kind)\n } catch {\n return undefined\n }\n}\n\n/** Reset the cached provider — mostly for tests that want to force a\n * re-probe. Also useful if the environment changes (e.g. Wayland\n * display started after process boot). */\nfunction resetClipboardCache(): void {\n cached = undefined\n cacheResolved = false\n}\n\n/**\n * Copy text to the system clipboard.\n *\n * Policy:\n * - **SSH session** → always OSC 52. Native tools on the remote box\n * would write to the remote clipboard, which isn't what the user\n * wants. OSC 52 routes through the local terminal.\n * - **Native tool available** → use it (fast, silent, no prompts).\n * - **Neither** → OSC 52 anyway, fire-and-forget.\n *\n * Resolves to `true` on best-effort delivery; `false` only when we had\n * nothing to try. OSC 52 writes are silent by design — the terminal\n * never confirms receipt — so `true` means \"we emitted\" not \"the\n * user's clipboard definitely has it\".\n */\nasync function writeClipboardText(text: string): Promise<boolean> {\n if (isSSH) {\n writeOsc52(text)\n return true\n }\n const p = getClipboard()\n if (p?.writeText) {\n try {\n if (await p.writeText(text)) return true\n } catch {\n /* fall through to OSC 52 */\n }\n }\n writeOsc52(text)\n return true\n}\n\n/**\n * Namespace export for readable call sites:\n *\n * ```ts\n * import { clipboard } from \"@zaly/tui\"\n *\n * await clipboard.read()\n * await clipboard.read(\"text\")\n * await clipboard.write(\"hello\")\n * clipboard.reset()\n * ```\n */\nexport const clipboard = {\n read: readClipboard,\n reset: resetClipboardCache,\n write: writeClipboardText,\n}\n\n/** Emit an OSC 52 clipboard write. Fire-and-forget; some terminals\n * ignore or prompt on this, and there's no response channel. */\nfunction writeOsc52(text: string): void {\n const b64 = Buffer.from(text, \"utf8\").toString(\"base64\")\n // `\\x1b]52;c;<base64>\\x07` — `c` targets the system clipboard.\n process.stdout.write(`\\x1b]52;c;${b64}\\x07`)\n}\n\n// ---- detection -------------------------------------------------------\n\nfunction detect(): Provider | undefined {\n if (platform === \"darwin\" && which(\"pbpaste\")) return macos()\n\n // Wayland before X11 — XWayland means both vars can be set on a\n // Wayland session, and wl-paste is the right tool there.\n if (process.env.WAYLAND_DISPLAY && which(\"wl-paste\")) return wayland()\n\n // X11: prefer xsel (nicer selection handling, per Neovim), then xclip.\n if (process.env.DISPLAY) {\n if (which(\"xsel\")) return xsel()\n if (which(\"xclip\")) return xclip()\n }\n\n // WSL exposes Windows' clipboard via win32yank if the user installed it.\n if (which(\"win32yank.exe\")) return wsl()\n\n // Plain Windows: PowerShell is always present on supported versions.\n if (platform === \"win32\") return windows()\n\n return undefined\n}\n\n// ---- backends --------------------------------------------------------\n\nfunction macos(): Provider {\n return {\n id: \"pbpaste\",\n read: async (kind) => {\n // macOS has no cheap \"list types\" call, so when no kind is\n // specified we probe in richness order: image → files → text.\n // Each AppleScript is wrapped in `try…on error` so a miss is\n // an empty string, not a spawn failure.\n if (kind === \"image\") return macosImage()\n if (kind === \"files\") return macosFiles()\n if (kind === \"text\") return macosText()\n const img = await macosImage()\n if (img) return img\n const files = await macosFiles()\n if (files) return files\n return macosText()\n },\n writeText: (text) => spawnWithInput(\"pbcopy\", [], text),\n }\n}\n\nasync function macosText(): Promise<ClipboardText | undefined> {\n const text = await spawnText(\"pbpaste\", [])\n return text === undefined ? undefined : { kind: \"text\", text }\n}\n\nasync function macosImage(): Promise<ClipboardImage | undefined> {\n const script = `\n try\n set png to (the clipboard as «class PNGf»)\n set b64 to do shell script \"base64\" with input (png as string)\n return b64\n on error\n return \"\"\n end try\n `\n const out = await spawnText(\"osascript\", [\"-e\", script])\n if (!out) return undefined\n return writeTempPng(Buffer.from(out.trim(), \"base64\"))\n}\n\nasync function macosFiles(): Promise<ClipboardFiles | undefined> {\n const script = `\n try\n set theClipboard to the clipboard as «class furl»\n return POSIX path of theClipboard\n on error\n try\n set theList to the clipboard as list\n set posix to \"\"\n repeat with item_ in theList\n try\n set posix to posix & (POSIX path of item_) & linefeed\n end try\n end repeat\n return posix\n on error\n return \"\"\n end try\n end try\n `\n const out = await spawnText(\"osascript\", [\"-e\", script])\n if (!out) return undefined\n const paths = out\n .split(/\\r?\\n/)\n .map((s) => s.trim())\n .filter((s) => s !== \"\")\n return paths.length > 0 ? { kind: \"files\", paths } : undefined\n}\n\nfunction wayland(): Provider {\n return {\n id: \"wl-paste\",\n read: async (kind) => {\n if (kind === \"image\") return waylandImage()\n if (kind === \"files\") return waylandFiles()\n if (kind === \"text\") return waylandText()\n // Richest-first: one `--list-types` call decides everything.\n const types = await spawnText(\"wl-paste\", [\"--list-types\"])\n if (!types) return undefined\n if (/\\bimage\\/png\\b/.test(types)) {\n const img = await waylandImage()\n if (img) return img\n }\n if (/\\btext\\/uri-list\\b/.test(types)) {\n const files = await waylandFiles()\n if (files) return files\n }\n return waylandText()\n },\n writeText: (text) => spawnWithInput(\"wl-copy\", [\"--type\", \"text/plain\"], text),\n }\n}\n\nasync function waylandImage(): Promise<ClipboardImage | undefined> {\n const r = await new Spawn(\"wl-paste\", [\"--type\", \"image/png\"]).result\n return r.code === 0 && r.stdout.length > 0 ? writeTempPng(r.stdout) : undefined\n}\n\nasync function waylandFiles(): Promise<ClipboardFiles | undefined> {\n const paths = parseUriList(await spawnText(\"wl-paste\", [\"--type\", \"text/uri-list\"]))\n return paths.length > 0 ? { kind: \"files\", paths } : undefined\n}\n\nasync function waylandText(): Promise<ClipboardText | undefined> {\n const text = await spawnText(\"wl-paste\", [\"--no-newline\"])\n return text === undefined ? undefined : { kind: \"text\", text }\n}\n\nfunction xsel(): Provider {\n // xsel doesn't support MIME-typed targets (no image or file-list\n // reads). When xclip is also installed, delegate non-text reads to\n // it and keep xsel for text only.\n const xclipFallback = which(\"xclip\")\n return {\n id: xclipFallback ? \"xsel+xclip\" : \"xsel\",\n read: async (kind) => {\n if (kind === \"text\") return xselText()\n if (xclipFallback) {\n if (kind === \"image\") return xclipImage()\n if (kind === \"files\") return xclipFiles()\n const rich = await xclipRead()\n if (rich && rich.kind !== \"text\") return rich\n }\n return xselText()\n },\n writeText: (text) => spawnWithInput(\"xsel\", [\"--nodetach\", \"-i\", \"-b\"], text),\n }\n}\n\nfunction xclip(): Provider {\n return {\n id: \"xclip\",\n read: (kind) => {\n if (kind === \"image\") return xclipImage()\n if (kind === \"files\") return xclipFiles()\n if (kind === \"text\") return xclipText()\n return xclipRead()\n },\n writeText: (text) => spawnWithInput(\"xclip\", [\"-quiet\", \"-i\", \"-selection\", \"clipboard\"], text),\n }\n}\n\nasync function xclipRead(): Promise<ClipboardContent | undefined> {\n // One TARGETS call covers the whole decision tree.\n const targets = await spawnText(\"xclip\", [\"-selection\", \"clipboard\", \"-t\", \"TARGETS\", \"-o\"])\n if (!targets) return undefined\n if (/\\bimage\\/png\\b/.test(targets)) {\n const img = await xclipImage()\n if (img) return img\n }\n if (/\\btext\\/uri-list\\b/.test(targets)) {\n const files = await xclipFiles()\n if (files) return files\n }\n return xclipText()\n}\n\nasync function xclipImage(): Promise<ClipboardImage | undefined> {\n const r = await new Spawn(\"xclip\", [\"-selection\", \"clipboard\", \"-t\", \"image/png\", \"-o\"]).result\n return r.code === 0 && r.stdout.length > 0 ? writeTempPng(r.stdout) : undefined\n}\n\nasync function xclipFiles(): Promise<ClipboardFiles | undefined> {\n const paths = parseUriList(\n await spawnText(\"xclip\", [\"-selection\", \"clipboard\", \"-t\", \"text/uri-list\", \"-o\"])\n )\n return paths.length > 0 ? { kind: \"files\", paths } : undefined\n}\n\nasync function xclipText(): Promise<ClipboardText | undefined> {\n const text = await spawnText(\"xclip\", [\"-o\", \"-selection\", \"clipboard\"])\n return text === undefined ? undefined : { kind: \"text\", text }\n}\n\nasync function xselText(): Promise<ClipboardText | undefined> {\n const text = await spawnText(\"xsel\", [\"-o\", \"-b\"])\n return text === undefined ? undefined : { kind: \"text\", text }\n}\n\nfunction wsl(): Provider {\n // win32yank is text-only; image / file paste from WSL would need a\n // different shim, so we only expose text here. `kind === \"image\"`\n // or `\"files\"` always resolves to undefined.\n return {\n id: \"win32yank\",\n read: async (kind) => {\n if (kind === \"image\" || kind === \"files\") return undefined\n const text = await spawnText(\"win32yank.exe\", [\"-o\", \"--lf\"])\n return text === undefined ? undefined : { kind: \"text\", text }\n },\n writeText: (text) => spawnWithInput(\"win32yank.exe\", [\"-i\", \"--crlf\"], text),\n }\n}\n\nfunction windows(): Provider {\n return {\n id: \"powershell\",\n read: async (kind) => {\n if (kind === \"image\") return windowsImage()\n if (kind === \"files\") return windowsFiles()\n if (kind === \"text\") return windowsText()\n const img = await windowsImage()\n if (img) return img\n const files = await windowsFiles()\n if (files) return files\n return windowsText()\n },\n writeText: (text) => spawnWithInput(\"clip\", [], text),\n }\n}\n\nasync function windowsText(): Promise<ClipboardText | undefined> {\n const text = await spawnText(\"powershell\", [\"-NoProfile\", \"-NoLogo\", \"-Command\", \"Get-Clipboard\"])\n return text === undefined ? undefined : { kind: \"text\", text }\n}\n\nasync function windowsImage(): Promise<ClipboardImage | undefined> {\n // PowerShell: fetch image from clipboard, save as PNG to a temp path.\n // Nothing on clipboard → the `if` skips and the file stays empty.\n const tmp = writeTempPath(\"zaly-clip-\", \".png\")\n const script = [\n \"Add-Type -AssemblyName System.Windows.Forms\",\n \"$img = [System.Windows.Forms.Clipboard]::GetImage()\",\n `if ($img) { $img.Save('${tmp.replace(/\\\\/g, String.raw`\\\\`)}') }`,\n ].join(\"; \")\n await spawnText(\"powershell\", [\"-NoProfile\", \"-NoLogo\", \"-Command\", script])\n if ((safeStat(tmp)?.size ?? 0) > 0) return { kind: \"image\", path: tmp, type: \"image/png\" }\n return undefined\n}\n\nasync function windowsFiles(): Promise<ClipboardFiles | undefined> {\n const out = await spawnText(\"powershell\", [\n \"-NoProfile\",\n \"-NoLogo\",\n \"-Command\",\n \"Get-Clipboard -Format FileDropList | ForEach-Object { $_.FullName }\",\n ])\n if (!out) return undefined\n const paths = out\n .split(/\\r?\\n/)\n .map((s) => s.trim())\n .filter((s) => s !== \"\")\n return paths.length > 0 ? { kind: \"files\", paths } : undefined\n}\n\n// ---- temp files + URI list parsing ----------------------------------\n\nfunction writeTempPng(bytes: Buffer): ClipboardImage {\n const path = writeTempPath(\"zaly-clip-\", \".png\")\n writeFileSync(path, bytes)\n return { kind: \"image\", path, type: \"image/png\" }\n}\n\n/** Parse a `text/uri-list` blob (RFC 2483): `file://…\\r\\n` lines, skip\n * blanks and comment lines (`#…`), decode percent-encoding. Returns\n * absolute filesystem paths. Non-`file://` URIs are dropped — we\n * can't hand a remote URL off as an attachment path. */\nfunction parseUriList(raw: string | undefined): string[] {\n if (!raw) return []\n const out: string[] = []\n for (const line of raw.split(/\\r?\\n/)) {\n const s = line.trim()\n if (s === \"\" || s.startsWith(\"#\")) continue\n if (!s.startsWith(\"file://\")) continue\n try {\n let path = decodeURIComponent(s.slice(\"file://\".length))\n // `file:///C:/foo` → `/C:/foo` after the slice. Strip the leading\n // slash so we end up with a real Windows path.\n if (/^\\/[A-Za-z]:\\//.test(path)) path = path.slice(1)\n out.push(path)\n } catch {\n /* malformed URI — skip */\n }\n }\n return out\n}\n\nfunction writeTempPath(prefix: string, ext: string): string {\n const dir = mkdtempSync(join(tmpdir(), prefix))\n return join(dir, `clip${ext}`)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"clipboard-UEoX9sup.mjs","names":[],"sources":["../src/input/clipboard.ts"],"sourcesContent":["import { safeStat } from \"@zaly/shared\"\nimport { isSSH } from \"@zaly/shared/env\"\nimport { Spawn, spawnText, spawnWithInput, which } from \"@zaly/shared/process\"\nimport { mkdtempSync, writeFileSync } from \"node:fs\"\nimport { tmpdir } from \"node:os\"\nimport { platform } from \"node:process\"\nimport { join } from \"pathe\"\n\n/**\n * Read text or image content from the system clipboard.\n *\n * Agent CLIs need this to make `ctrl-v` \"just work\" — most terminals\n * don't forward clipboard paste events (they only forward typed keys),\n * so the app has to query the OS clipboard itself via the platform's\n * native tool. The detection order mirrors Neovim's clipboard provider:\n *\n * 1. macOS → `pbpaste` + `osascript` (for images)\n * 2. Wayland → `wl-paste`\n * 3. X11 (xsel) → `xsel`\n * 4. X11 (xclip) → `xclip`\n * 5. WSL → `win32yank.exe`\n * 6. Windows → PowerShell `Get-Clipboard`\n *\n * The chosen provider is cached per process. Spawn primitives live in\n * `@zaly/shared/process` — this file is just glue plus the per-platform\n * AppleScript/PowerShell snippets.\n */\n\n// ---- types -----------------------------------------------------------\n\n/** Image bytes on the clipboard, already written to a temp PNG. */\nexport interface ClipboardImage {\n kind: \"image\"\n /** Absolute path to a temporary PNG file. Caller takes ownership. */\n path: string\n /** MIME type. PNG only for now; kept for future formats. */\n type: \"image/png\"\n}\n\n/** One or more file references on the clipboard — what you get when\n * the user copies a file in their file manager and pastes. */\nexport interface ClipboardFiles {\n kind: \"files\"\n /** Absolute filesystem paths. */\n paths: string[]\n}\n\n/** Plain text on the clipboard — the common case. */\nexport interface ClipboardText {\n kind: \"text\"\n text: string\n}\n\n/** Anything the clipboard can hold that this library surfaces. */\nexport type ClipboardContent = ClipboardImage | ClipboardFiles | ClipboardText\n\n/** One of the content kinds `ClipboardContent` can take. Derived from\n * the union so adding a new variant doesn't require updating this\n * alias separately. */\nexport type ClipboardKind = ClipboardContent[\"kind\"]\n\n/** Internal provider contract. Each backend owns its own \"probe types,\n * pick best, fetch once\" logic — on platforms like Wayland and X11\n * that can enumerate available types, this avoids doing three or four\n * probe round-trips for every paste.\n *\n * `read(kind?)` takes an optional hint: when specified, the backend\n * can skip type enumeration and fetch that kind directly (fast path\n * for callers that already know what they want). When omitted, the\n * backend returns the richest available content. `writeText` is\n * optional since not every backend supports writes. */\ninterface Provider {\n readonly id: string\n read: (kind?: ClipboardKind) => Promise<ClipboardContent | undefined>\n writeText?: (text: string) => Promise<boolean>\n}\n\n// ---- public API ------------------------------------------------------\n\nlet cached: Provider | undefined\nlet cacheResolved = false\n\n/** Detect + cache the clipboard provider for this process. Returns\n * `undefined` when no provider is available. */\nfunction getClipboard(): Provider | undefined {\n if (cacheResolved) return cached\n cached = detect()\n cacheResolved = true\n return cached\n}\n\n/**\n * Read the system clipboard, returning the richest content available.\n *\n * Priority order (decided by each backend internally):\n * 1. **image** — raw image bytes on the clipboard (e.g. a screenshot\n * from `cmd-shift-4`, or an image copied from a web page).\n * 2. **files** — file URIs (e.g. the user copied a PDF / video in\n * their file manager).\n * 3. **text** — plain text.\n *\n * Resolves to `undefined` when the clipboard is empty, no provider is\n * available, or every read fails. Never throws — clipboard access is\n * best-effort.\n */\nasync function readClipboard(): Promise<ClipboardContent | undefined>\nasync function readClipboard(kind: \"text\"): Promise<ClipboardText | undefined>\nasync function readClipboard(kind: \"image\"): Promise<ClipboardImage | undefined>\nasync function readClipboard(kind: \"files\"): Promise<ClipboardFiles | undefined>\nasync function readClipboard(kind?: ClipboardKind): Promise<ClipboardContent | undefined> {\n const p = getClipboard()\n if (!p) return undefined\n try {\n return await p.read(kind)\n } catch {\n return undefined\n }\n}\n\n/** Reset the cached provider — mostly for tests that want to force a\n * re-probe. Also useful if the environment changes (e.g. Wayland\n * display started after process boot). */\nfunction resetClipboardCache(): void {\n cached = undefined\n cacheResolved = false\n}\n\n/**\n * Copy text to the system clipboard.\n *\n * Policy:\n * - **SSH session** → always OSC 52. Native tools on the remote box\n * would write to the remote clipboard, which isn't what the user\n * wants. OSC 52 routes through the local terminal.\n * - **Native tool available** → use it (fast, silent, no prompts).\n * - **Neither** → OSC 52 anyway, fire-and-forget.\n *\n * Resolves to `true` on best-effort delivery; `false` only when we had\n * nothing to try. OSC 52 writes are silent by design — the terminal\n * never confirms receipt — so `true` means \"we emitted\" not \"the\n * user's clipboard definitely has it\".\n */\nasync function writeClipboardText(text: string): Promise<boolean> {\n if (isSSH) {\n writeOsc52(text)\n return true\n }\n const p = getClipboard()\n if (p?.writeText) {\n try {\n if (await p.writeText(text)) return true\n } catch {\n /* fall through to OSC 52 */\n }\n }\n writeOsc52(text)\n return true\n}\n\n/**\n * Namespace export for readable call sites:\n *\n * ```ts\n * import { clipboard } from \"@zaly/tui\"\n *\n * await clipboard.read()\n * await clipboard.read(\"text\")\n * await clipboard.write(\"hello\")\n * clipboard.reset()\n * ```\n */\nexport const clipboard = {\n read: readClipboard,\n reset: resetClipboardCache,\n write: writeClipboardText,\n}\n\n/** Emit an OSC 52 clipboard write. Fire-and-forget; some terminals\n * ignore or prompt on this, and there's no response channel. */\nfunction writeOsc52(text: string): void {\n const b64 = Buffer.from(text, \"utf8\").toString(\"base64\")\n // `\\x1b]52;c;<base64>\\x07` — `c` targets the system clipboard.\n process.stdout.write(`\\x1b]52;c;${b64}\\x07`)\n}\n\n// ---- detection -------------------------------------------------------\n\nfunction detect(): Provider | undefined {\n if (platform === \"darwin\" && which(\"pbpaste\")) return macos()\n\n // Wayland before X11 — XWayland means both vars can be set on a\n // Wayland session, and wl-paste is the right tool there.\n if (process.env.WAYLAND_DISPLAY && which(\"wl-paste\")) return wayland()\n\n // X11: prefer xsel (nicer selection handling, per Neovim), then xclip.\n if (process.env.DISPLAY) {\n if (which(\"xsel\")) return xsel()\n if (which(\"xclip\")) return xclip()\n }\n\n // WSL exposes Windows' clipboard via win32yank if the user installed it.\n if (which(\"win32yank.exe\")) return wsl()\n\n // Plain Windows: PowerShell is always present on supported versions.\n if (platform === \"win32\") return windows()\n\n return undefined\n}\n\n// ---- backends --------------------------------------------------------\n\nfunction macos(): Provider {\n return {\n id: \"pbpaste\",\n read: async (kind) => {\n // macOS has no cheap \"list types\" call, so when no kind is\n // specified we probe in richness order: image → files → text.\n // Each AppleScript is wrapped in `try…on error` so a miss is\n // an empty string, not a spawn failure.\n if (kind === \"image\") return macosImage()\n if (kind === \"files\") return macosFiles()\n if (kind === \"text\") return macosText()\n const img = await macosImage()\n if (img) return img\n const files = await macosFiles()\n if (files) return files\n return macosText()\n },\n writeText: (text) => spawnWithInput(\"pbcopy\", [], text),\n }\n}\n\nasync function macosText(): Promise<ClipboardText | undefined> {\n const text = await spawnText(\"pbpaste\", [])\n return text === undefined ? undefined : { kind: \"text\", text }\n}\n\nasync function macosImage(): Promise<ClipboardImage | undefined> {\n const script = `\n try\n set png to (the clipboard as «class PNGf»)\n set b64 to do shell script \"base64\" with input (png as string)\n return b64\n on error\n return \"\"\n end try\n `\n const out = await spawnText(\"osascript\", [\"-e\", script])\n if (!out) return undefined\n return writeTempPng(Buffer.from(out.trim(), \"base64\"))\n}\n\nasync function macosFiles(): Promise<ClipboardFiles | undefined> {\n const script = `\n try\n set theClipboard to the clipboard as «class furl»\n return POSIX path of theClipboard\n on error\n try\n set theList to the clipboard as list\n set posix to \"\"\n repeat with item_ in theList\n try\n set posix to posix & (POSIX path of item_) & linefeed\n end try\n end repeat\n return posix\n on error\n return \"\"\n end try\n end try\n `\n const out = await spawnText(\"osascript\", [\"-e\", script])\n if (!out) return undefined\n const paths = out\n .split(/\\r?\\n/)\n .map((s) => s.trim())\n .filter((s) => s !== \"\")\n return paths.length > 0 ? { kind: \"files\", paths } : undefined\n}\n\nfunction wayland(): Provider {\n return {\n id: \"wl-paste\",\n read: async (kind) => {\n if (kind === \"image\") return waylandImage()\n if (kind === \"files\") return waylandFiles()\n if (kind === \"text\") return waylandText()\n // Richest-first: one `--list-types` call decides everything.\n const types = await spawnText(\"wl-paste\", [\"--list-types\"])\n if (!types) return undefined\n if (/\\bimage\\/png\\b/.test(types)) {\n const img = await waylandImage()\n if (img) return img\n }\n if (/\\btext\\/uri-list\\b/.test(types)) {\n const files = await waylandFiles()\n if (files) return files\n }\n return waylandText()\n },\n writeText: (text) => spawnWithInput(\"wl-copy\", [\"--type\", \"text/plain\"], text),\n }\n}\n\nasync function waylandImage(): Promise<ClipboardImage | undefined> {\n const r = await new Spawn(\"wl-paste\", [\"--type\", \"image/png\"]).result\n return r.code === 0 && r.stdout.length > 0 ? writeTempPng(r.stdout) : undefined\n}\n\nasync function waylandFiles(): Promise<ClipboardFiles | undefined> {\n const paths = parseUriList(await spawnText(\"wl-paste\", [\"--type\", \"text/uri-list\"]))\n return paths.length > 0 ? { kind: \"files\", paths } : undefined\n}\n\nasync function waylandText(): Promise<ClipboardText | undefined> {\n const text = await spawnText(\"wl-paste\", [\"--no-newline\"])\n return text === undefined ? undefined : { kind: \"text\", text }\n}\n\nfunction xsel(): Provider {\n // xsel doesn't support MIME-typed targets (no image or file-list\n // reads). When xclip is also installed, delegate non-text reads to\n // it and keep xsel for text only.\n const xclipFallback = which(\"xclip\")\n return {\n id: xclipFallback ? \"xsel+xclip\" : \"xsel\",\n read: async (kind) => {\n if (kind === \"text\") return xselText()\n if (xclipFallback) {\n if (kind === \"image\") return xclipImage()\n if (kind === \"files\") return xclipFiles()\n const rich = await xclipRead()\n if (rich && rich.kind !== \"text\") return rich\n }\n return xselText()\n },\n writeText: (text) => spawnWithInput(\"xsel\", [\"--nodetach\", \"-i\", \"-b\"], text),\n }\n}\n\nfunction xclip(): Provider {\n return {\n id: \"xclip\",\n read: (kind) => {\n if (kind === \"image\") return xclipImage()\n if (kind === \"files\") return xclipFiles()\n if (kind === \"text\") return xclipText()\n return xclipRead()\n },\n writeText: (text) => spawnWithInput(\"xclip\", [\"-quiet\", \"-i\", \"-selection\", \"clipboard\"], text),\n }\n}\n\nasync function xclipRead(): Promise<ClipboardContent | undefined> {\n // One TARGETS call covers the whole decision tree.\n const targets = await spawnText(\"xclip\", [\"-selection\", \"clipboard\", \"-t\", \"TARGETS\", \"-o\"])\n if (!targets) return undefined\n if (/\\bimage\\/png\\b/.test(targets)) {\n const img = await xclipImage()\n if (img) return img\n }\n if (/\\btext\\/uri-list\\b/.test(targets)) {\n const files = await xclipFiles()\n if (files) return files\n }\n return xclipText()\n}\n\nasync function xclipImage(): Promise<ClipboardImage | undefined> {\n const r = await new Spawn(\"xclip\", [\"-selection\", \"clipboard\", \"-t\", \"image/png\", \"-o\"]).result\n return r.code === 0 && r.stdout.length > 0 ? writeTempPng(r.stdout) : undefined\n}\n\nasync function xclipFiles(): Promise<ClipboardFiles | undefined> {\n const paths = parseUriList(\n await spawnText(\"xclip\", [\"-selection\", \"clipboard\", \"-t\", \"text/uri-list\", \"-o\"])\n )\n return paths.length > 0 ? { kind: \"files\", paths } : undefined\n}\n\nasync function xclipText(): Promise<ClipboardText | undefined> {\n const text = await spawnText(\"xclip\", [\"-o\", \"-selection\", \"clipboard\"])\n return text === undefined ? undefined : { kind: \"text\", text }\n}\n\nasync function xselText(): Promise<ClipboardText | undefined> {\n const text = await spawnText(\"xsel\", [\"-o\", \"-b\"])\n return text === undefined ? undefined : { kind: \"text\", text }\n}\n\nfunction wsl(): Provider {\n // win32yank is text-only; image / file paste from WSL would need a\n // different shim, so we only expose text here. `kind === \"image\"`\n // or `\"files\"` always resolves to undefined.\n return {\n id: \"win32yank\",\n read: async (kind) => {\n if (kind === \"image\" || kind === \"files\") return undefined\n const text = await spawnText(\"win32yank.exe\", [\"-o\", \"--lf\"])\n return text === undefined ? undefined : { kind: \"text\", text }\n },\n writeText: (text) => spawnWithInput(\"win32yank.exe\", [\"-i\", \"--crlf\"], text),\n }\n}\n\nfunction windows(): Provider {\n return {\n id: \"powershell\",\n read: async (kind) => {\n if (kind === \"image\") return windowsImage()\n if (kind === \"files\") return windowsFiles()\n if (kind === \"text\") return windowsText()\n const img = await windowsImage()\n if (img) return img\n const files = await windowsFiles()\n if (files) return files\n return windowsText()\n },\n writeText: (text) => spawnWithInput(\"clip\", [], text),\n }\n}\n\nasync function windowsText(): Promise<ClipboardText | undefined> {\n const text = await spawnText(\"powershell\", [\"-NoProfile\", \"-NoLogo\", \"-Command\", \"Get-Clipboard\"])\n return text === undefined ? undefined : { kind: \"text\", text }\n}\n\nasync function windowsImage(): Promise<ClipboardImage | undefined> {\n // PowerShell: fetch image from clipboard, save as PNG to a temp path.\n // Nothing on clipboard → the `if` skips and the file stays empty.\n const tmp = writeTempPath(\"zaly-clip-\", \".png\")\n const script = [\n \"Add-Type -AssemblyName System.Windows.Forms\",\n \"$img = [System.Windows.Forms.Clipboard]::GetImage()\",\n `if ($img) { $img.Save('${tmp.replace(/\\\\/g, String.raw`\\\\`)}') }`,\n ].join(\"; \")\n await spawnText(\"powershell\", [\"-NoProfile\", \"-NoLogo\", \"-Command\", script])\n if ((safeStat(tmp)?.size ?? 0) > 0) return { kind: \"image\", path: tmp, type: \"image/png\" }\n return undefined\n}\n\nasync function windowsFiles(): Promise<ClipboardFiles | undefined> {\n const out = await spawnText(\"powershell\", [\n \"-NoProfile\",\n \"-NoLogo\",\n \"-Command\",\n \"Get-Clipboard -Format FileDropList | ForEach-Object { $_.FullName }\",\n ])\n if (!out) return undefined\n const paths = out\n .split(/\\r?\\n/)\n .map((s) => s.trim())\n .filter((s) => s !== \"\")\n return paths.length > 0 ? { kind: \"files\", paths } : undefined\n}\n\n// ---- temp files + URI list parsing ----------------------------------\n\nfunction writeTempPng(bytes: Buffer): ClipboardImage {\n const path = writeTempPath(\"zaly-clip-\", \".png\")\n writeFileSync(path, bytes)\n return { kind: \"image\", path, type: \"image/png\" }\n}\n\n/** Parse a `text/uri-list` blob (RFC 2483): `file://…\\r\\n` lines, skip\n * blanks and comment lines (`#…`), decode percent-encoding. Returns\n * absolute filesystem paths. Non-`file://` URIs are dropped — we\n * can't hand a remote URL off as an attachment path. */\nfunction parseUriList(raw: string | undefined): string[] {\n if (!raw) return []\n const out: string[] = []\n for (const line of raw.split(/\\r?\\n/)) {\n const s = line.trim()\n if (s === \"\" || s.startsWith(\"#\")) continue\n if (!s.startsWith(\"file://\")) continue\n try {\n let path = decodeURIComponent(s.slice(\"file://\".length))\n // `file:///C:/foo` → `/C:/foo` after the slice. Strip the leading\n // slash so we end up with a real Windows path.\n if (/^\\/[A-Za-z]:\\//.test(path)) path = path.slice(1)\n out.push(path)\n } catch {\n /* malformed URI — skip */\n }\n }\n return out\n}\n\nfunction writeTempPath(prefix: string, ext: string): string {\n const dir = mkdtempSync(join(tmpdir(), prefix))\n return join(dir, `clip${ext}`)\n}\n"],"mappings":"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"}
|
package/dist/clipboard.d.mts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/input/clipboard.d.ts
|
|
2
1
|
/**
|
|
3
2
|
* Read text or image content from the system clipboard.
|
|
4
3
|
*
|
|
@@ -99,6 +98,5 @@ export declare const clipboard: {
|
|
|
99
98
|
reset: typeof resetClipboardCache;
|
|
100
99
|
write: typeof writeClipboardText;
|
|
101
100
|
};
|
|
102
|
-
//#endregion
|
|
103
101
|
export {};
|
|
104
102
|
//# sourceMappingURL=clipboard.d.mts.map
|
package/dist/clipboard.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clipboard.d.mts","names":[],"sources":["../src/input/clipboard.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"clipboard.d.mts","names":[],"sources":["../src/input/clipboard.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;iBA+BiB;EACf;;EAEA;;EAEA;;;;iBAKe;EACf;;EAEA;;;iBAIe;EACf;EACA;;;YAIU,mBAAmB,iBAAiB,iBAAiB;;;;YAKrD,gBAAgB;;;;;;;;;;;;;;;iBA8Cb,iBAAiB,QAAQ;iBACzB,cAAc,eAAe,QAAQ;iBACrC,cAAc,gBAAgB,QAAQ;iBACtC,cAAc,gBAAgB,QAAQ;;;;iBAc5C;;;;;;;;;;;;;;;;iBAoBM,mBAAmB,eAAe;;;;;;;;;;;;;qBA6BpC;EACX,aAAI;EACJ,cAAK;EACL,cAAK"}
|
package/dist/clipboard.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { t as clipboard } from "./clipboard-
|
|
1
|
+
export { t as clipboard } from "./clipboard-UEoX9sup.mjs";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { clamp } from "@zaly/shared";
|
|
2
|
-
|
|
2
|
+
// src/style/color.ts
|
|
3
3
|
const hexByte = (n) => clamp(Math.round(n), 0, 255).toString(16).padStart(2, "0");
|
|
4
4
|
function toHex(x, y, z) {
|
|
5
5
|
const [r, g, b] = Array.isArray(x) ? x : [
|
|
@@ -31,7 +31,6 @@ function blend(c, bg, alpha) {
|
|
|
31
31
|
const [r2, g2, b2] = parseHex(bg);
|
|
32
32
|
return toHex(r1 * alpha + r2 * (1 - alpha), g1 * alpha + g2 * (1 - alpha), b1 * alpha + b2 * (1 - alpha));
|
|
33
33
|
}
|
|
34
|
-
//#endregion
|
|
35
34
|
export { toHex as i, isHexColor as n, parseHex as r, blend as t };
|
|
36
35
|
|
|
37
|
-
//# sourceMappingURL=color-
|
|
36
|
+
//# sourceMappingURL=color-Cr1VoL49.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"color-
|
|
1
|
+
{"version":3,"file":"color-Cr1VoL49.mjs","names":[],"sources":["../src/style/color.ts"],"sourcesContent":["import type { HexColor, RGB } from \"./types.ts\"\n\nimport { clamp } from \"@zaly/shared\"\n\nconst hexByte = (n: number): string => clamp(Math.round(n), 0, 255).toString(16).padStart(2, \"0\")\n\nexport function toHex(c: RGB): HexColor\nexport function toHex(r: number, g: number, b: number): HexColor\nexport function toHex(x: number | RGB, y?: number, z?: number): HexColor {\n const [r, g, b] = Array.isArray(x) ? x : [x, y!, z!]\n return `#${hexByte(r)}${hexByte(g)}${hexByte(b)}`\n}\n\nexport function parseHex(hex: HexColor): RGB {\n let s = hex.charCodeAt(0) === 0x23 ? hex.slice(1) : hex // strip leading #\n if (s.length === 4) s = s.slice(0, 3) // strip alpha\n if (s.length === 8) s = s.slice(0, 6) // strip alpha\n if (s.length === 3) {\n // Expand \"abc\" → \"aabbcc\" without allocating an array.\n s = s[0] + s[0] + s[1] + s[1] + s[2] + s[2]\n }\n if (s.length !== 6) throw new Error(`invalid hex color: ${hex}`)\n const n = Number.parseInt(s, 16)\n if (Number.isNaN(n)) throw new Error(`invalid hex color: ${hex}`)\n return [(n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff]\n}\n\nexport function isHexColor(s: string): s is HexColor {\n return /^#[0-9a-fA-F]{3,8}$/.test(s)\n}\n\nexport function blend(c: HexColor, bg: HexColor, alpha: number): HexColor {\n const [r1, g1, b1] = parseHex(c)\n const [r2, g2, b2] = parseHex(bg)\n const r = r1 * alpha + r2 * (1 - alpha)\n const g = g1 * alpha + g2 * (1 - alpha)\n const b = b1 * alpha + b2 * (1 - alpha)\n return toHex(r, g, b)\n}\n"],"mappings":";;AAIA,MAAM,WAAW,MAAsB,MAAM,KAAK,MAAM,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG;AAIhG,SAAgB,MAAM,GAAiB,GAAY,GAAsB;CACvE,MAAM,CAAC,GAAG,GAAG,KAAK,MAAM,QAAQ,CAAC,IAAI,IAAI;EAAC;EAAG;EAAI;CAAE;CACnD,OAAO,IAAI,QAAQ,CAAC,IAAI,QAAQ,CAAC,IAAI,QAAQ,CAAC;AAChD;AAEA,SAAgB,SAAS,KAAoB;CAC3C,IAAI,IAAI,IAAI,WAAW,CAAC,MAAM,KAAO,IAAI,MAAM,CAAC,IAAI;CACpD,IAAI,EAAE,WAAW,GAAG,IAAI,EAAE,MAAM,GAAG,CAAC;CACpC,IAAI,EAAE,WAAW,GAAG,IAAI,EAAE,MAAM,GAAG,CAAC;CACpC,IAAI,EAAE,WAAW,GAEf,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE;CAE3C,IAAI,EAAE,WAAW,GAAG,MAAM,IAAI,MAAM,sBAAsB,KAAK;CAC/D,MAAM,IAAI,OAAO,SAAS,GAAG,EAAE;CAC/B,IAAI,OAAO,MAAM,CAAC,GAAG,MAAM,IAAI,MAAM,sBAAsB,KAAK;CAChE,OAAO;EAAE,KAAK,KAAM;EAAO,KAAK,IAAK;EAAM,IAAI;CAAI;AACrD;AAEA,SAAgB,WAAW,GAA0B;CACnD,OAAO,sBAAsB,KAAK,CAAC;AACrC;AAEA,SAAgB,MAAM,GAAa,IAAc,OAAyB;CACxE,MAAM,CAAC,IAAI,IAAI,MAAM,SAAS,CAAC;CAC/B,MAAM,CAAC,IAAI,IAAI,MAAM,SAAS,EAAE;CAIhC,OAAO,MAHG,KAAK,QAAQ,MAAM,IAAI,QACvB,KAAK,QAAQ,MAAM,IAAI,QACvB,KAAK,QAAQ,MAAM,IAAI,MACb;AACtB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { t as createCtx } from "./ctx-DBeFcZt6.mjs";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { t as defaultTheme } from "./registry-
|
|
2
|
-
import { t as styleBuilder } from "./builder-
|
|
3
|
-
|
|
1
|
+
import { t as defaultTheme } from "./registry-CQU0VwpM.mjs";
|
|
2
|
+
import { t as styleBuilder } from "./builder-BdYlK1Ew.mjs";
|
|
3
|
+
// src/core/ctx.ts
|
|
4
4
|
/**
|
|
5
5
|
* Current terminal width in cells. Returns `undefined` when stdout isn't a
|
|
6
6
|
* TTY (piped output, tests, non-Node/Bun runtimes) — callers should fall
|
|
@@ -37,7 +37,6 @@ function createCtx(opts = {}) {
|
|
|
37
37
|
...opts
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
|
-
//#endregion
|
|
41
40
|
export { createCtx as t };
|
|
42
41
|
|
|
43
|
-
//# sourceMappingURL=ctx-
|
|
42
|
+
//# sourceMappingURL=ctx-DBeFcZt6.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ctx-
|
|
1
|
+
{"version":3,"file":"ctx-DBeFcZt6.mjs","names":[],"sources":["../src/core/ctx.ts"],"sourcesContent":["import type { Emitter } from \"@zaly/shared\"\nimport type { Logger } from \"@zaly/shared/logger\"\nimport type { Actions } from \"../input/actions.ts\"\nimport type { TerminalQueries } from \"../input/queries.ts\"\nimport type { InputRouterEvents } from \"../input/router.ts\"\nimport type { SurfaceType } from \"../renderer/renderer.ts\"\nimport type { StyleBuilder } from \"../style/builder.ts\"\nimport type { Theme } from \"../themes/types.ts\"\nimport type { Overlay } from \"../widgets/overlay.ts\"\nimport type { Node } from \"./node.ts\"\n\nimport { styleBuilder } from \"../style/builder.ts\"\nimport { defaultTheme } from \"../themes/registry.ts\"\n\n/**\n * Passed to every `render(ctx)` call. Width flows in; height emerges from\n * the returned row count. Theme is ambient — children share the parent's.\n *\n * `style` is a theme-bound chainable builder made available to components so\n * they can produce inline-styled strings without re-binding the theme.\n *\n * `version` is a monotonic cache-key bumped by the `Renderer` whenever any\n * identity-relevant ctx field changes (resize, theme swap). Node caches\n * compare this integer — no hashing, no string keys.\n */\nexport interface RenderCtx {\n width: number\n style: StyleBuilder\n version: number\n /** Queue a side-channel ANSI payload (e.g. KGP image transmit) for\n * the renderer to flush before the next paint. Use this to keep\n * side-effecting bytes out of cached row strings — what `_render`\n * returns should be safe to reuse across paints, and transmit\n * bytes shouldn't be. */\n readonly transmit: (seq: string) => void\n}\n\n/**\n * Context handed to a node when it mounts onto a surface. Gives widget\n * authors a scoped handle to the services they legitimately need —\n * focus, overlays, tree lookups, out-of-band repaints — without\n * exposing the full Renderer.\n *\n * MountCtx is *per-lifetime*: set once when `mount()` is called and\n * cleared on `unmount()`. Distinct from `RenderCtx` (per-tick, carries\n * width/theme/version). If a node moves between surfaces, it receives\n * a fresh MountCtx via the subsequent `mount()` call.\n */\nexport interface MountCtx {\n /** Which surface owns this subtree. */\n readonly surface: SurfaceType\n\n /** Overlay capabilities — open/close an Overlay node from inside a\n * widget (e.g. a confirm dialog, autocomplete popover, tooltip). */\n readonly overlay: {\n readonly add: (o: () => Overlay) => void\n readonly remove: (o: Overlay) => void\n }\n\n /** Input capabilities — a narrow slice of the router. We deliberately\n * don't expose the full `InputRouter`: `setKeymap` / `dispatch` are\n * app-level concerns. Widgets legitimately need to install direct\n * global key bindings (autocomplete) and move focus. */\n readonly input: {\n readonly terminalFocus: boolean\n readonly events: Emitter<InputRouterEvents>\n readonly bind: Actions[\"bind\"]\n /** Move focus to `node`. Mirrors `router.focus(node)`. */\n readonly focus: (node: Node) => void\n /** Clear focus. */\n readonly blur: (node: Node) => void\n readonly queries: TerminalQueries\n }\n\n /** Action registry — widgets and plugins can `register` catalog\n * entries (with desc + default keys + optional `fn`) and `dispatch`\n * actions by id. The Router feeds key-triggered dispatches through\n * here; programmatic callers do the same. */\n readonly actions: Actions\n\n /** Look up a node anywhere in the tree by its `id`. First match\n * wins; `undefined` when nothing matches. Same semantics as\n * `Renderer.getNode`. */\n readonly getNode: (id: string) => Node | undefined\n /** Find every node matching a predicate. Strings match `node.type`;\n * pass a function for richer predicates. Same semantics as\n * `Renderer.findNode`. */\n readonly findNode: (match: string | ((n: Node) => boolean)) => Node[]\n\n logger: Logger\n}\n\n/**\n * Current terminal width in cells. Returns `undefined` when stdout isn't a\n * TTY (piped output, tests, non-Node/Bun runtimes) — callers should fall\n * back to a sensible default when this is `undefined`.\n *\n * @internal\n */\nfunction termWidth(): number | undefined {\n if (typeof process === \"undefined\") return undefined\n const cols = process.stdout.columns\n return typeof cols === \"number\" && cols > 0 ? cols : undefined\n}\n\n/**\n * Build a fresh `RenderCtx` from a resolved theme + width. Binds a `style`\n * builder to the theme so components can use `ctx.style.primary(...)`\n * without constructing their own.\n *\n * Width is clamped to the terminal's current column count (falling back to\n * 80 when stdout isn't a TTY). No width passed → terminal width is used\n * directly; an explicit `width` wider than the terminal is capped so\n * components never produce rows that wrap the display. Narrower explicit\n * widths (e.g. for fixed-layout tests) pass through unchanged.\n *\n * Dynamic theme loading (from a string name) lives in `themes/loadTheme`\n * — resolve the name first, then pass the `Theme` object here.\n */\nexport function createCtx(opts: Partial<RenderCtx> & { theme?: Theme } = {}): RenderCtx {\n const tw = termWidth() ?? 80\n return {\n style: opts.style ?? styleBuilder(opts.theme ?? defaultTheme),\n transmit: (data) => process.stdout.write(data),\n version: opts.version ?? 0,\n width: Math.min(opts.width ?? tw, tw),\n ...opts,\n }\n}\n"],"mappings":";;;;;;;;;;AAmGA,SAAS,YAAgC;CACvC,IAAI,OAAO,YAAY,aAAa,OAAO,KAAA;CAC3C,MAAM,OAAO,QAAQ,OAAO;CAC5B,OAAO,OAAO,SAAS,YAAY,OAAO,IAAI,OAAO,KAAA;AACvD;;;;;;;;;;;;;;;AAgBA,SAAgB,UAAU,OAA+C,CAAC,GAAc;CACtF,MAAM,KAAK,UAAU,KAAK;CAC1B,OAAO;EACL,OAAO,KAAK,SAAS,aAAa,KAAK,SAAS,YAAY;EAC5D,WAAW,SAAS,QAAQ,OAAO,MAAM,IAAI;EAC7C,SAAS,KAAK,WAAW;EACzB,OAAO,KAAK,IAAI,KAAK,SAAS,IAAI,EAAE;EACpC,GAAG;CACL;AACF"}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { n as text } from "./text-
|
|
2
|
-
|
|
1
|
+
import { n as text } from "./text-BArwVVkg.mjs";
|
|
2
|
+
// src/widgets/divider.ts
|
|
3
3
|
function divider(state = { style: "divider" }) {
|
|
4
4
|
return text((ctx) => (state.char ?? "─").repeat(state.length ?? ctx.width), state);
|
|
5
5
|
}
|
|
6
|
-
//#endregion
|
|
7
6
|
export { divider as t };
|
|
8
7
|
|
|
9
|
-
//# sourceMappingURL=divider-
|
|
8
|
+
//# sourceMappingURL=divider-qEj5prKE.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"divider-
|
|
1
|
+
{"version":3,"file":"divider-qEj5prKE.mjs","names":[],"sources":["../src/widgets/divider.ts"],"sourcesContent":["import type { Style } from \"../style/types.ts\"\nimport type { Text } from \"./text.ts\"\n\nimport { text } from \"./text.ts\"\n\nexport type DividerState = Style<false> & {\n char?: string\n length?: number\n}\n\nexport function divider(state: DividerState = { style: \"divider\" }): Text {\n return text((ctx) => (state.char ?? \"─\").repeat(state.length ?? ctx.width), state)\n}\n"],"mappings":";;AAUA,SAAgB,QAAQ,QAAsB,EAAE,OAAO,UAAU,GAAS;CACxE,OAAO,MAAM,SAAS,MAAM,QAAQ,IAAA,CAAK,OAAO,MAAM,UAAU,IAAI,KAAK,GAAG,KAAK;AACnF"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as createIterable, y as signal } from "./reactive-
|
|
1
|
+
import { a as createIterable, y as signal } from "./reactive-DkP75TPk.mjs";
|
|
2
2
|
import { normPath } from "@zaly/shared";
|
|
3
|
-
|
|
3
|
+
// src/widgets/completions/files.ts
|
|
4
4
|
/** Completion source for files. Uses `@zaly/shared/find` under the hood, so
|
|
5
5
|
* supports all the same options and respects .gitignore by default. */
|
|
6
6
|
function filesSource(opts = {}) {
|
|
@@ -36,7 +36,6 @@ function filesSource(opts = {}) {
|
|
|
36
36
|
triggers: [trigger]
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
|
-
//#endregion
|
|
40
39
|
export { filesSource as t };
|
|
41
40
|
|
|
42
|
-
//# sourceMappingURL=files-
|
|
41
|
+
//# sourceMappingURL=files-C3ZnFpvq.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"files-
|
|
1
|
+
{"version":3,"file":"files-C3ZnFpvq.mjs","names":[],"sources":["../src/widgets/completions/files.ts"],"sourcesContent":["import type { FindOptions } from \"@zaly/shared/find\"\nimport type { Progressive } from \"../../core/reactive.ts\"\nimport type { CompletionSource } from \"../autocomplete.ts\"\nimport type { PickerItem } from \"../picker.ts\"\n\nimport { normPath } from \"@zaly/shared\"\nimport { createIterable, signal } from \"../../core/reactive.ts\"\n\nexport interface FilesSourceOptions extends FindOptions {\n /** Base directory relative queries resolve against. Default:\n * `process.cwd()`. */\n cwd?: string\n /** Trigger regex. Default: `/(?<=^|\\s)@/` — `@` preceded by start-of-\n * string or whitespace. Lookbehind keeps `match.start` on the `@`\n * itself so the leading space isn't eaten on insertion. */\n trigger?: RegExp\n /** Prefix prepended to accepted values so the trigger character stays\n * in the input after a pick (letting users keep typing to drill into\n * subdirectories). Default: `\"@\"` — matches the default trigger.\n * When customizing `trigger`, set this to the character your trigger\n * represents. */\n prefix?: string\n refreshInterval?: number\n}\n\ntype File = PickerItem & { file: string }\n\n/** Completion source for files. Uses `@zaly/shared/find` under the hood, so\n * supports all the same options and respects .gitignore by default. */\nexport function filesSource(opts: FilesSourceOptions = {}): CompletionSource<File> {\n const cwd = normPath(opts.cwd)\n const trigger = opts.trigger ?? /(?<=^|\\s)@/\n const prefix = opts.prefix ?? \"@\"\n let results: Progressive<readonly File[]> | undefined\n const [gen, setGen] = signal(0)\n let lastRefresh = 0\n return {\n accept: (item) => {\n // Dirs keep the popup open (trigger still matches, user can\n // drill in). Files close it — a trailing space makes `#detect`\n // see whitespace in the query and bail.\n const v = item.file\n return `${prefix}${v}${v.endsWith(\"/\") ? \"\" : \" \"}`\n },\n get complete() {\n const now = performance.now()\n if (!results?.loading() && now - lastRefresh > (opts.refreshInterval ?? 10_000)) {\n setGen((g) => g + 1) // Refresh results\n lastRefresh = now\n }\n return (results ??= createIterable<File>(async function* iterFiles() {\n gen() // Re-run when `gen` changes\n const { find } = await import(\"@zaly/shared/find\")\n for await (const f of find({ ...opts, cwd })) {\n const ff = Array.isArray(f) ? f : [f]\n yield ff.map((file) => ({ file, text: file }))\n }\n }))\n },\n triggers: [trigger],\n }\n}\n"],"mappings":";;;;;AA6BA,SAAgB,YAAY,OAA2B,CAAC,GAA2B;CACjF,MAAM,MAAM,SAAS,KAAK,GAAG;CAC7B,MAAM,UAAU,KAAK,WAAW;CAChC,MAAM,SAAS,KAAK,UAAU;CAC9B,IAAI;CACJ,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC;CAC9B,IAAI,cAAc;CAClB,OAAO;EACL,SAAS,SAAS;GAIhB,MAAM,IAAI,KAAK;GACf,OAAO,GAAG,SAAS,IAAI,EAAE,SAAS,GAAG,IAAI,KAAK;EAChD;EACA,IAAI,WAAW;GACb,MAAM,MAAM,YAAY,IAAI;GAC5B,IAAI,CAAC,SAAS,QAAQ,KAAK,MAAM,eAAe,KAAK,mBAAmB,MAAS;IAC/E,QAAQ,MAAM,IAAI,CAAC;IACnB,cAAc;GAChB;GACA,OAAQ,YAAY,eAAqB,gBAAgB,YAAY;IACnE,IAAI;IACJ,MAAM,EAAE,SAAS,MAAM,OAAO;IAC9B,WAAW,MAAM,KAAK,KAAK;KAAE,GAAG;KAAM;IAAI,CAAC,GAEzC,OADW,MAAM,QAAQ,CAAC,IAAI,IAAI,CAAC,CAAC,EAAA,CAC3B,KAAK,UAAU;KAAE;KAAM,MAAM;IAAK,EAAE;GAEjD,CAAC;EACH;EACA,UAAU,CAAC,OAAO;CACpB;AACF"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { n as PickerItem } from "./picker-
|
|
2
|
-
import { o as CompletionSource } from "./autocomplete-
|
|
1
|
+
import { n as PickerItem } from "./picker-DsB6--GN.mjs";
|
|
2
|
+
import { o as CompletionSource } from "./autocomplete-wgceuhLr.mjs";
|
|
3
3
|
import { FindOptions } from "@zaly/shared/find";
|
|
4
|
-
//#region src/widgets/completions/files.d.ts
|
|
5
4
|
interface FilesSourceOptions extends FindOptions {
|
|
6
5
|
/** Base directory relative queries resolve against. Default:
|
|
7
6
|
* `process.cwd()`. */
|
|
@@ -24,6 +23,5 @@ type File = PickerItem & {
|
|
|
24
23
|
/** Completion source for files. Uses `@zaly/shared/find` under the hood, so
|
|
25
24
|
* supports all the same options and respects .gitignore by default. */
|
|
26
25
|
declare function filesSource(opts?: FilesSourceOptions): CompletionSource<File>;
|
|
27
|
-
//#endregion
|
|
28
26
|
export { filesSource as n, FilesSourceOptions as t };
|
|
29
|
-
//# sourceMappingURL=files-
|
|
27
|
+
//# sourceMappingURL=files-CdiDnwA_.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files-CdiDnwA_.d.mts","names":[],"sources":["../src/widgets/completions/files.ts"],"mappings":";;;UAQiB,2BAA2B;;;EAG1C;;;;EAIA,UAAU;;;;;;EAMV;EACA;;KAGG,OAAO;EAAe;;;;iBAIX,YAAY,OAAM,qBAA0B,iBAAiB"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { n as resolveSize, t as clamp } from "./size-
|
|
1
|
+
import { n as resolveSize, t as clamp } from "./size-BHRJuRk9.mjs";
|
|
2
2
|
import { sliceAnsi, stringWidth } from "@zaly/shared/ansi";
|
|
3
|
-
|
|
3
|
+
// src/layout/flex.ts
|
|
4
4
|
/**
|
|
5
5
|
* Allocate widths to row-direction children with CSS-style flex
|
|
6
6
|
* semantics. Each item starts at its **basis**:
|
|
@@ -196,7 +196,6 @@ function padRow(row, width) {
|
|
|
196
196
|
if (w < width) return row + " ".repeat(width - w);
|
|
197
197
|
return sliceAnsi(row, 0, width);
|
|
198
198
|
}
|
|
199
|
-
//#endregion
|
|
200
199
|
export { zipRow as a, stackColumn as i, isFixedWidth as n, padRow as r, allocateRow as t };
|
|
201
200
|
|
|
202
|
-
//# sourceMappingURL=flex-
|
|
201
|
+
//# sourceMappingURL=flex-CYp4etqn.mjs.map
|