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