@zaly/tui 0.0.12 → 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/assets/schemas/shiki-language.schema.json +352 -0
- package/assets/schemas/shiki-theme.schema.json +71 -0
- package/assets/schemas/theme.schema.json +226 -1046
- package/assets/themes/andromeeda.json +97 -0
- package/assets/themes/ayu-dark.json +31 -25
- package/assets/themes/ayu-light.json +106 -0
- package/assets/themes/ayu-mirage.json +106 -0
- package/assets/themes/catppuccin-frappe.json +43 -40
- package/assets/themes/catppuccin-latte.json +43 -40
- package/assets/themes/catppuccin-macchiato.json +43 -40
- package/assets/themes/catppuccin-mocha.json +43 -40
- package/assets/themes/dark-plus.json +99 -0
- package/assets/themes/dracula.json +47 -47
- package/assets/themes/everforest-dark.json +28 -26
- package/assets/themes/everforest-light.json +28 -26
- package/assets/themes/github-dark-default.json +107 -0
- package/assets/themes/github-dark-dimmed.json +107 -0
- package/assets/themes/github-light-default.json +107 -0
- package/assets/themes/gruvbox-dark-hard.json +98 -0
- package/assets/themes/gruvbox-dark-medium.json +34 -29
- package/assets/themes/gruvbox-light-medium.json +98 -0
- package/assets/themes/horizon-bright.json +101 -0
- package/assets/themes/horizon.json +101 -0
- package/assets/themes/iterm2/LICENSE +25 -0
- package/assets/themes/iterm2/README.md +28 -0
- package/assets/themes/iterm2/catppuccin-latte.json +140 -0
- package/assets/themes/iterm2/catppuccin-mocha.json +140 -0
- package/assets/themes/iterm2/github-dark.json +140 -0
- package/assets/themes/iterm2/github.json +140 -0
- package/assets/themes/iterm2/gruvbox-dark.json +140 -0
- package/assets/themes/iterm2/gruvbox-light.json +140 -0
- package/assets/themes/iterm2/nord.json +140 -0
- package/assets/themes/iterm2/tokyonight-day.json +140 -0
- package/assets/themes/iterm2/tokyonight-moon.json +140 -0
- package/assets/themes/kanagawa-dragon.json +55 -49
- package/assets/themes/kanagawa-lotus.json +105 -0
- package/assets/themes/kanagawa-wave.json +56 -50
- package/assets/themes/light-plus.json +99 -0
- package/assets/themes/material-theme-lighter.json +102 -0
- package/assets/themes/material-theme-palenight.json +52 -45
- package/assets/themes/material-theme.json +102 -0
- package/assets/themes/min-dark.json +69 -14
- package/assets/themes/min-light.json +68 -17
- package/assets/themes/monokai.json +54 -47
- package/assets/themes/night-owl-light.json +105 -0
- package/assets/themes/night-owl.json +57 -49
- package/assets/themes/nord.json +53 -39
- package/assets/themes/one-dark-pro.json +46 -38
- package/assets/themes/one-light.json +52 -41
- package/assets/themes/poimandres.json +51 -49
- package/assets/themes/rose-pine-dawn.json +99 -0
- package/assets/themes/rose-pine-moon.json +99 -0
- package/assets/themes/rose-pine.json +50 -33
- package/assets/themes/solarized-dark.json +38 -31
- package/assets/themes/solarized-light.json +40 -33
- package/assets/themes/synthwave-84.json +47 -30
- package/assets/themes/tokyonight-day.json +3 -3
- package/assets/themes/tokyonight-moon.json +3 -2
- package/assets/themes/tokyonight-night.json +3 -2
- package/assets/themes/tokyonight-storm.json +3 -2
- package/assets/themes/vesper.json +50 -42
- package/assets/themes/vitesse-black.json +103 -0
- package/assets/themes/vitesse-dark.json +26 -30
- package/assets/themes/vitesse-light.json +26 -30
- 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-Dc4D4uSf.d.mts → actions-B0yyZ-Ww.d.mts} +4 -6
- package/dist/actions-B0yyZ-Ww.d.mts.map +1 -0
- package/dist/{ansi-DZlXaItJ.mjs → ansi-CxgRPLA2.mjs} +4 -33
- package/dist/ansi-CxgRPLA2.mjs.map +1 -0
- package/dist/ansi.d.mts +1 -3
- package/dist/ansi.d.mts.map +1 -1
- package/dist/ansi.mjs +1 -1
- package/dist/{autocomplete-StW1QJZJ.d.mts → autocomplete-wgceuhLr.d.mts} +5 -7
- package/dist/autocomplete-wgceuhLr.d.mts.map +1 -0
- package/dist/{box-BkP4MF82.mjs → box-DToDoXlU.mjs} +8 -10
- package/dist/{box-BkP4MF82.mjs.map → box-DToDoXlU.mjs.map} +1 -1
- package/dist/builder-BdYlK1Ew.mjs +195 -0
- package/dist/builder-BdYlK1Ew.mjs.map +1 -0
- package/dist/{callbacks-BwWTCqPJ.mjs → callbacks-DW8XKYKy.mjs} +9 -13
- package/dist/{callbacks-BwWTCqPJ.mjs.map → callbacks-DW8XKYKy.mjs.map} +1 -1
- package/dist/{client-C2GXKfJ1.mjs → client-BWm6dNZF.mjs} +9 -6
- package/dist/client-BWm6dNZF.mjs.map +1 -0
- 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-Cr1VoL49.mjs +36 -0
- package/dist/color-Cr1VoL49.mjs.map +1 -0
- package/dist/ctx-CYEzzH-7.mjs +1 -0
- package/dist/{ctx-B7BmPUCD.mjs → ctx-DBeFcZt6.mjs} +4 -5
- package/dist/{ctx-B7BmPUCD.mjs.map → ctx-DBeFcZt6.mjs.map} +1 -1
- package/dist/{divider-TSx9NhI_.mjs → divider-qEj5prKE.mjs} +3 -4
- package/dist/{divider-TSx9NhI_.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-7DUYSeNc.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-C1KfXk98.d.mts → github-DC0XemKa.d.mts} +3 -5
- package/dist/github-DC0XemKa.d.mts.map +1 -0
- package/dist/{image-VaT7AN-S.d.mts → image-DdRDv7q6.d.mts} +2 -4
- package/dist/image-DdRDv7q6.d.mts.map +1 -0
- package/dist/image-M--Bw4bs.mjs +177 -0
- package/dist/image-M--Bw4bs.mjs.map +1 -0
- 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 +11 -14
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +12 -14
- package/dist/index.mjs.map +1 -1
- package/dist/{input-BGTTujDN.mjs → input-BkE4EpBw.mjs} +6 -7
- package/dist/{input-BGTTujDN.mjs.map → input-BkE4EpBw.mjs.map} +1 -1
- package/dist/{input-BAYux7qo.d.mts → input-C3kfYoEJ.d.mts} +3 -5
- package/dist/input-C3kfYoEJ.d.mts.map +1 -0
- package/dist/{inspect-jG_W4njH.mjs → inspect-Cli1beu8.mjs} +3 -4
- package/dist/{inspect-jG_W4njH.mjs.map → inspect-Cli1beu8.mjs.map} +1 -1
- package/dist/{inspect-CfNydMgO.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-ByKlM4iI.mjs → log-96b1_J6G.mjs} +6 -7
- package/dist/{log-ByKlM4iI.mjs.map → log-96b1_J6G.mjs.map} +1 -1
- package/dist/{logger-DSQb0sLP.mjs → logger-DwfXp8LF.mjs} +5 -6
- package/dist/{logger-DSQb0sLP.mjs.map → logger-DwfXp8LF.mjs.map} +1 -1
- package/dist/{markdown-CwRmWuU6.mjs → markdown-CgRZBhIX.mjs} +12 -12
- package/dist/markdown-CgRZBhIX.mjs.map +1 -0
- 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-VPuwFCid.mjs +205 -0
- package/dist/math-VPuwFCid.mjs.map +1 -0
- 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-Boh4YHEc.mjs +98 -0
- package/dist/oklch-Boh4YHEc.mjs.map +1 -0
- package/dist/{overlay-BnvOKtGT.mjs → overlay-CevDWgSF.mjs} +3 -4
- package/dist/{overlay-BnvOKtGT.mjs.map → overlay-CevDWgSF.mjs.map} +1 -1
- package/dist/{picker-Dh5IUrTk.mjs → picker-D9WrE3zF.mjs} +11 -16
- package/dist/{picker-Dh5IUrTk.mjs.map → picker-D9WrE3zF.mjs.map} +1 -1
- package/dist/{picker-DQs_IW77.d.mts → picker-DsB6--GN.d.mts} +6 -8
- package/dist/picker-DsB6--GN.d.mts.map +1 -0
- package/dist/{queries-B_lOwEiE.mjs → queries-BIniKnLL.mjs} +68 -7
- package/dist/queries-BIniKnLL.mjs.map +1 -0
- package/dist/{queries-BDX8mcl5.d.mts → queries-Dmd3IjSd.d.mts} +19 -52
- 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-D-suU3g0.mjs → registry-CQU0VwpM.mjs} +205 -65
- package/dist/registry-CQU0VwpM.mjs.map +1 -0
- package/dist/{render-DEIUNLkh.mjs → render-Bp2N2i6J.mjs} +4 -5
- package/dist/{render-DEIUNLkh.mjs.map → render-Bp2N2i6J.mjs.map} +1 -1
- package/dist/{renderer-zDNlAovy.mjs → renderer-B9sAMA10.mjs} +123 -40
- package/dist/renderer-B9sAMA10.mjs.map +1 -0
- package/dist/{renderer-MQue4Da5.mjs → renderer-BYl2pbdx.mjs} +9 -9
- package/dist/renderer-BYl2pbdx.mjs.map +1 -0
- 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-J2KtwxyK.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 +40 -19
- package/dist/shiki/server.mjs.map +1 -1
- package/dist/shiki-BAf9PdIc.mjs +19 -0
- package/dist/shiki-BAf9PdIc.mjs.map +1 -0
- package/dist/shiki-DUUAIE6f.mjs +632 -0
- package/dist/shiki-DUUAIE6f.mjs.map +1 -0
- 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-Cw_EuszU.mjs +108 -0
- package/dist/style-Cw_EuszU.mjs.map +1 -0
- package/dist/style.d.mts +4 -5
- package/dist/style.d.mts.map +1 -1
- package/dist/style.mjs +4 -4
- package/dist/{text-CkQ7Irmt.mjs → text-4arC3odI.mjs} +3 -4
- package/dist/{text-CkQ7Irmt.mjs.map → text-4arC3odI.mjs.map} +1 -1
- package/dist/{text-XvPfweeg.mjs → text-BArwVVkg.mjs} +5 -6
- package/dist/{text-XvPfweeg.mjs.map → text-BArwVVkg.mjs.map} +1 -1
- package/dist/{text-CCYs9RB2.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-B8hb1lPU.mjs +60 -0
- package/dist/theme-B8hb1lPU.mjs.map +1 -0
- package/dist/theme-BeSaXGNY.mjs +1792 -0
- package/dist/theme-BeSaXGNY.mjs.map +1 -0
- package/dist/themes.d.mts +211 -77
- package/dist/themes.d.mts.map +1 -1
- package/dist/themes.mjs +1 -1
- package/dist/{tree-DJl5W_B7.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-D2EYlzmK.d.mts → types-ByMHEdCF.d.mts} +133 -131
- 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-DPqBbB57.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 +4 -4
- package/dist/widgets/code.d.mts.map +1 -1
- package/dist/widgets/code.mjs +12 -12
- package/dist/widgets/code.mjs.map +1 -1
- 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 +38 -16
- 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 +8 -9
- package/dist/widgets/image.mjs.map +1 -1
- 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 +5 -7
- package/assets/themes/dracula-soft.json +0 -100
- package/assets/themes/github-dark.json +0 -104
- package/assets/themes/github-light.json +0 -104
- package/dist/actions-Dc4D4uSf.d.mts.map +0 -1
- package/dist/ansi-DZlXaItJ.mjs.map +0 -1
- package/dist/autocomplete-StW1QJZJ.d.mts.map +0 -1
- package/dist/builder-1z5ewEWK.mjs +0 -386
- package/dist/builder-1z5ewEWK.mjs.map +0 -1
- package/dist/client-C2GXKfJ1.mjs.map +0 -1
- package/dist/client-bzdHIDL2.mjs +0 -1
- package/dist/ctx-D_JgJ8Eo.mjs +0 -1
- package/dist/files-7DUYSeNc.d.mts.map +0 -1
- package/dist/github-C1KfXk98.d.mts.map +0 -1
- package/dist/image-Cm5T-uw-.mjs +0 -108
- package/dist/image-Cm5T-uw-.mjs.map +0 -1
- package/dist/image-VaT7AN-S.d.mts.map +0 -1
- package/dist/input-BAYux7qo.d.mts.map +0 -1
- package/dist/inspect-CfNydMgO.d.mts.map +0 -1
- package/dist/markdown-CwRmWuU6.mjs.map +0 -1
- package/dist/picker-DQs_IW77.d.mts.map +0 -1
- package/dist/queries-BDX8mcl5.d.mts.map +0 -1
- package/dist/queries-B_lOwEiE.mjs.map +0 -1
- package/dist/registry-D-suU3g0.mjs.map +0 -1
- package/dist/renderer-DhqbEVVR.mjs +0 -1
- package/dist/renderer-MQue4Da5.mjs.map +0 -1
- package/dist/renderer-zDNlAovy.mjs.map +0 -1
- package/dist/search-FvGnswRl.d.mts.map +0 -1
- package/dist/select-J2KtwxyK.d.mts.map +0 -1
- package/dist/shiki--ASSYv4_.mjs +0 -12
- package/dist/shiki--ASSYv4_.mjs.map +0 -1
- package/dist/shiki-D7l5--zz.mjs +0 -432
- package/dist/shiki-D7l5--zz.mjs.map +0 -1
- package/dist/text-CCYs9RB2.d.mts.map +0 -1
- package/dist/theme.config-B3Yx--C2.mjs +0 -855
- package/dist/theme.config-B3Yx--C2.mjs.map +0 -1
- package/dist/tree-DJl5W_B7.d.mts.map +0 -1
- package/dist/types-D2EYlzmK.d.mts.map +0 -1
- package/dist/types-D3jk1rtk.d.mts.map +0 -1
- package/dist/widget-DPqBbB57.d.mts.map +0 -1
- package/dist/worker-C01jK2Ip.mjs +0 -1
|
@@ -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";
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { clamp } from "@zaly/shared";
|
|
2
|
+
// src/style/color.ts
|
|
3
|
+
const hexByte = (n) => clamp(Math.round(n), 0, 255).toString(16).padStart(2, "0");
|
|
4
|
+
function toHex(x, y, z) {
|
|
5
|
+
const [r, g, b] = Array.isArray(x) ? x : [
|
|
6
|
+
x,
|
|
7
|
+
y,
|
|
8
|
+
z
|
|
9
|
+
];
|
|
10
|
+
return `#${hexByte(r)}${hexByte(g)}${hexByte(b)}`;
|
|
11
|
+
}
|
|
12
|
+
function parseHex(hex) {
|
|
13
|
+
let s = hex.charCodeAt(0) === 35 ? hex.slice(1) : hex;
|
|
14
|
+
if (s.length === 4) s = s.slice(0, 3);
|
|
15
|
+
if (s.length === 8) s = s.slice(0, 6);
|
|
16
|
+
if (s.length === 3) s = s[0] + s[0] + s[1] + s[1] + s[2] + s[2];
|
|
17
|
+
if (s.length !== 6) throw new Error(`invalid hex color: ${hex}`);
|
|
18
|
+
const n = Number.parseInt(s, 16);
|
|
19
|
+
if (Number.isNaN(n)) throw new Error(`invalid hex color: ${hex}`);
|
|
20
|
+
return [
|
|
21
|
+
n >> 16 & 255,
|
|
22
|
+
n >> 8 & 255,
|
|
23
|
+
n & 255
|
|
24
|
+
];
|
|
25
|
+
}
|
|
26
|
+
function isHexColor(s) {
|
|
27
|
+
return /^#[0-9a-fA-F]{3,8}$/.test(s);
|
|
28
|
+
}
|
|
29
|
+
function blend(c, bg, alpha) {
|
|
30
|
+
const [r1, g1, b1] = parseHex(c);
|
|
31
|
+
const [r2, g2, b2] = parseHex(bg);
|
|
32
|
+
return toHex(r1 * alpha + r2 * (1 - alpha), g1 * alpha + g2 * (1 - alpha), b1 * alpha + b2 * (1 - alpha));
|
|
33
|
+
}
|
|
34
|
+
export { toHex as i, isHexColor as n, parseHex as r, blend as t };
|
|
35
|
+
|
|
36
|
+
//# sourceMappingURL=color-Cr1VoL49.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
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
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flex-Bguo-fUe.mjs","names":[],"sources":["../src/layout/flex.ts"],"sourcesContent":["import type { FlexState } from \"../core/state.ts\"\n\nimport { sliceAnsi, stringWidth } from \"@zaly/shared/ansi\"\nimport { clamp, resolveSize } from \"./size.ts\"\n\n/**\n * Items participating in a row-direction allocation. Extends the\n * `Flexible` mixin with the node's intrinsic content sizes (from\n * `Node.layout()` or fallback measurement). Box constructs these\n * before calling `allocateRow`.\n *\n * @internal\n */\nexport interface RowItem extends FlexState {\n /** Measured natural (max-content) width — used as the flex-basis\n * when no fixed `width` is set. Falls back to 0 when omitted. */\n natural?: number\n /** Intrinsic min-content width — the smallest the node can render\n * without breaking content. Acts as a shrink floor below which the\n * allocator won't go (combined with `Flexible.minWidth`). */\n intrinsicMin?: number\n}\n\nexport interface AllocateOpts {\n contentWidth: number\n gap: number\n}\n\n/**\n * Allocate widths to row-direction children with CSS-style flex\n * semantics. Each item starts at its **basis**:\n *\n * - `width: <number | \"N%\">` → resolved fixed width.\n * - `width: \"fill\"` → 0 (claim slack, ignore intrinsic).\n * - any other non-fixed spec → `natural` (content size, 0 when not\n * provided).\n *\n * Three branches:\n *\n * - `slack > 0`: distribute by `flexGrow`. Items without grow weight\n * stay at basis; leftover slack is *not* smeared onto siblings.\n * - `slack < 0` (deficit): distribute the shortfall by\n * `basis * flexShrink` (CSS shrink default = 1). Each item is\n * clamped at its `minWidth` floor so content doesn't break.\n * - `slack == 0`: items take their basis as-is.\n *\n * `min`/`max` (combined with `intrinsicMin`) clamp per-item; rounding\n * remainder lands on the last grower / shrinker.\n *\n * @internal\n */\nexport function allocateRow(items: readonly RowItem[], opts: AllocateOpts): number[] {\n const { contentWidth } = opts\n const gapTotal = opts.gap * Math.max(0, items.length - 1)\n const available = contentWidth - gapTotal\n\n const bases: number[] = Array.from({ length: items.length })\n const grows: number[] = Array.from({ length: items.length })\n const shrinks: number[] = Array.from({ length: items.length })\n // Effective minimum per item — the larger of the user-set `minWidth`\n // and the node's intrinsic `min-content`. Shrink can't go below this.\n const mins: number[] = Array.from({ length: items.length })\n let baseSum = 0\n let growSum = 0\n\n for (let i = 0; i < items.length; i++) {\n const item = items[i]\n if (isFixedWidth(item.width)) {\n bases[i] = resolveSize(item.width, contentWidth) ?? 0\n grows[i] = 0\n shrinks[i] = 0 // Fixed-width items never shrink — author asked for an exact size.\n } else if (item.width === \"fill\") {\n // `fill` claims slack and ignores intrinsic basis (CSS `flex: 1 1 0`).\n bases[i] = 0\n grows[i] = item.flexGrow ?? 1\n shrinks[i] = item.flexShrink ?? 1\n } else {\n bases[i] = item.natural ?? 0\n grows[i] = item.flexGrow ?? 0\n shrinks[i] = item.flexShrink ?? 1\n }\n const intrinsicMin = item.intrinsicMin ?? 0\n const userMin = resolveSize(item.minWidth, contentWidth) ?? 0\n mins[i] = Math.max(intrinsicMin, userMin)\n baseSum += bases[i]\n growSum += grows[i]\n }\n\n const slack = available - baseSum\n const widths: number[] = Array.from({ length: items.length })\n let allocated = 0\n let lastGrowIndex = -1\n let lastShrinkIndex = -1\n\n if (slack >= 0) {\n // Grow phase — distribute slack among items that asked for it.\n for (let i = 0; i < items.length; i++) {\n const item = items[i]\n let w = bases[i]\n if (growSum > 0 && grows[i] > 0) {\n w += Math.floor((slack * grows[i]) / growSum)\n lastGrowIndex = i\n }\n w = clamp(w, { available: contentWidth, max: item.maxWidth, min: mins[i] })\n widths[i] = w\n allocated += w\n }\n // Tail grower absorbs rounding remainder and any over/underflow\n // from min/max clamps. We aim at `baseSum + slack` (i.e. exactly\n // `available`) only when growers exist; otherwise leftover is real\n // slack the parent will pad as end-of-row space.\n if (lastGrowIndex !== -1) {\n const target = baseSum + slack\n if (allocated !== target) {\n widths[lastGrowIndex] = Math.max(0, widths[lastGrowIndex] + (target - allocated))\n }\n }\n } else {\n // Shrink phase — distribute the deficit weighted by `basis * shrink`.\n // Items at their `mins[i]` floor stop shrinking; the remaining\n // deficit re-routes to the others. Iterate until stable or no\n // shrinkable items remain.\n for (let i = 0; i < items.length; i++) widths[i] = bases[i]\n let deficit = -slack // positive\n const frozen: boolean[] = Array.from({ length: items.length }, () => false)\n // Cap loop at items.length+1 — each pass freezes at least one item\n // or fully resolves the deficit. Bounded, no risk of looping.\n for (let pass = 0; pass <= items.length && deficit > 0; pass++) {\n let weightSum = 0\n for (let i = 0; i < items.length; i++) {\n if (!frozen[i]) weightSum += widths[i] * shrinks[i]\n }\n if (weightSum === 0) break // nothing left can shrink\n let froze = false\n for (let i = 0; i < items.length; i++) {\n if (frozen[i]) continue\n const share = Math.floor((deficit * widths[i] * shrinks[i]) / weightSum)\n const next = widths[i] - share\n if (next <= mins[i]) {\n deficit -= widths[i] - mins[i]\n widths[i] = mins[i]\n frozen[i] = true\n froze = true\n } else {\n widths[i] = next\n lastShrinkIndex = i\n }\n }\n if (!froze) {\n // Apply remaining deficit proportionally one more time without\n // freezing — the round above didn't hit any floor.\n deficit = 0\n }\n }\n for (let i = 0; i < items.length; i++) {\n widths[i] = clamp(widths[i], {\n available: contentWidth,\n max: items[i].maxWidth,\n min: mins[i],\n })\n allocated += widths[i]\n }\n // Soak any rounding-shaped over/underflow into the last shrinker so\n // the row sums exactly to `available` (or to the minSum floor when\n // mins force overflow).\n if (lastShrinkIndex !== -1) {\n const target = Math.max(\n available,\n mins.reduce((a, b) => a + b, 0)\n )\n if (allocated !== target) {\n const adj = target - allocated\n widths[lastShrinkIndex] = Math.max(mins[lastShrinkIndex], widths[lastShrinkIndex] + adj)\n }\n }\n }\n\n return widths\n}\n\n/**\n * Whether a `Flexible.width` spec is a fixed numeric size (number or\n * `Pct`). `\"fill\"`/`\"fit\"`/undefined are non-fixed and resolve from\n * surrounding context (slack / measurement).\n *\n * @internal\n */\nexport function isFixedWidth(w: FlexState[\"width\"]): boolean {\n return typeof w === \"number\" || (typeof w === \"string\" && w.endsWith(\"%\"))\n}\n\nexport interface ZipOpts {\n widths: readonly number[]\n gap: number\n}\n\n/**\n * Zip pre-rendered child rows horizontally. Shorter children are padded with\n * blank rows of their allocated width. Rows of each child must already be\n * padded to the corresponding width in `widths`.\n *\n * @internal\n */\nexport function zipRow(children: readonly (readonly string[])[], opts: ZipOpts): string[] {\n if (children.length === 0) return []\n const gapStr = \" \".repeat(opts.gap)\n const height = children.reduce((h, rows) => Math.max(h, rows.length), 0)\n const out: string[] = []\n for (let r = 0; r < height; r++) {\n let line = \"\"\n for (let c = 0; c < children.length; c++) {\n if (c > 0) line += gapStr\n const rows = children[c]\n line += r < rows.length ? rows[r] : \" \".repeat(opts.widths[c])\n }\n out.push(line)\n }\n return out\n}\n\nexport interface StackOpts {\n gap: number\n width: number\n}\n\n/**\n * Stack pre-rendered child rows vertically. `gap` blank rows of `width`\n * spaces separate each sibling pair that *contributes rows*. Children\n * that emit no rows (e.g. `visible: false`, falsy `show()` branch) are\n * skipped along with the gap that would have surrounded them — no\n * stray blank bands. Caller must ensure each child's rows are already\n * padded to `width`.\n *\n * @internal\n */\nexport function stackColumn(children: readonly (readonly string[])[], opts: StackOpts): string[] {\n const out: string[] = []\n const blank = \" \".repeat(opts.width)\n let any = false\n for (const child of children) {\n if (child.length === 0) continue\n if (any) for (let g = 0; g < opts.gap; g++) out.push(blank)\n out.push(...child)\n any = true\n }\n return out\n}\n\n/**\n * Pad a row to `width` cells without injecting a `RESET`. Used by the\n * row/column zip-and-pad steps — the parent `Box`'s outer\n * `reapplyStyle` chain handles re-emitting any ambient SGR after inner\n * `[0m`s, so the pad cells stay inside the active style. Over-wide\n * rows are clipped via `sliceAnsi`, preserving SGR state.\n *\n * Distinct from `padOrClip` in `style/ansi.ts`, which is for\n * standalone (non-Box-wrapped) paths and *does* inject a `RESET`\n * before the pad.\n *\n * @internal\n */\nexport function padRow(row: string, width: number): string {\n const w = stringWidth(row)\n if (w === width) return row\n if (w < width) return row + \" \".repeat(width - w)\n return sliceAnsi(row, 0, width)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"flex-CYp4etqn.mjs","names":[],"sources":["../src/layout/flex.ts"],"sourcesContent":["import type { FlexState } from \"../core/state.ts\"\n\nimport { sliceAnsi, stringWidth } from \"@zaly/shared/ansi\"\nimport { clamp, resolveSize } from \"./size.ts\"\n\n/**\n * Items participating in a row-direction allocation. Extends the\n * `Flexible` mixin with the node's intrinsic content sizes (from\n * `Node.layout()` or fallback measurement). Box constructs these\n * before calling `allocateRow`.\n *\n * @internal\n */\nexport interface RowItem extends FlexState {\n /** Measured natural (max-content) width — used as the flex-basis\n * when no fixed `width` is set. Falls back to 0 when omitted. */\n natural?: number\n /** Intrinsic min-content width — the smallest the node can render\n * without breaking content. Acts as a shrink floor below which the\n * allocator won't go (combined with `Flexible.minWidth`). */\n intrinsicMin?: number\n}\n\nexport interface AllocateOpts {\n contentWidth: number\n gap: number\n}\n\n/**\n * Allocate widths to row-direction children with CSS-style flex\n * semantics. Each item starts at its **basis**:\n *\n * - `width: <number | \"N%\">` → resolved fixed width.\n * - `width: \"fill\"` → 0 (claim slack, ignore intrinsic).\n * - any other non-fixed spec → `natural` (content size, 0 when not\n * provided).\n *\n * Three branches:\n *\n * - `slack > 0`: distribute by `flexGrow`. Items without grow weight\n * stay at basis; leftover slack is *not* smeared onto siblings.\n * - `slack < 0` (deficit): distribute the shortfall by\n * `basis * flexShrink` (CSS shrink default = 1). Each item is\n * clamped at its `minWidth` floor so content doesn't break.\n * - `slack == 0`: items take their basis as-is.\n *\n * `min`/`max` (combined with `intrinsicMin`) clamp per-item; rounding\n * remainder lands on the last grower / shrinker.\n *\n * @internal\n */\nexport function allocateRow(items: readonly RowItem[], opts: AllocateOpts): number[] {\n const { contentWidth } = opts\n const gapTotal = opts.gap * Math.max(0, items.length - 1)\n const available = contentWidth - gapTotal\n\n const bases: number[] = Array.from({ length: items.length })\n const grows: number[] = Array.from({ length: items.length })\n const shrinks: number[] = Array.from({ length: items.length })\n // Effective minimum per item — the larger of the user-set `minWidth`\n // and the node's intrinsic `min-content`. Shrink can't go below this.\n const mins: number[] = Array.from({ length: items.length })\n let baseSum = 0\n let growSum = 0\n\n for (let i = 0; i < items.length; i++) {\n const item = items[i]\n if (isFixedWidth(item.width)) {\n bases[i] = resolveSize(item.width, contentWidth) ?? 0\n grows[i] = 0\n shrinks[i] = 0 // Fixed-width items never shrink — author asked for an exact size.\n } else if (item.width === \"fill\") {\n // `fill` claims slack and ignores intrinsic basis (CSS `flex: 1 1 0`).\n bases[i] = 0\n grows[i] = item.flexGrow ?? 1\n shrinks[i] = item.flexShrink ?? 1\n } else {\n bases[i] = item.natural ?? 0\n grows[i] = item.flexGrow ?? 0\n shrinks[i] = item.flexShrink ?? 1\n }\n const intrinsicMin = item.intrinsicMin ?? 0\n const userMin = resolveSize(item.minWidth, contentWidth) ?? 0\n mins[i] = Math.max(intrinsicMin, userMin)\n baseSum += bases[i]\n growSum += grows[i]\n }\n\n const slack = available - baseSum\n const widths: number[] = Array.from({ length: items.length })\n let allocated = 0\n let lastGrowIndex = -1\n let lastShrinkIndex = -1\n\n if (slack >= 0) {\n // Grow phase — distribute slack among items that asked for it.\n for (let i = 0; i < items.length; i++) {\n const item = items[i]\n let w = bases[i]\n if (growSum > 0 && grows[i] > 0) {\n w += Math.floor((slack * grows[i]) / growSum)\n lastGrowIndex = i\n }\n w = clamp(w, { available: contentWidth, max: item.maxWidth, min: mins[i] })\n widths[i] = w\n allocated += w\n }\n // Tail grower absorbs rounding remainder and any over/underflow\n // from min/max clamps. We aim at `baseSum + slack` (i.e. exactly\n // `available`) only when growers exist; otherwise leftover is real\n // slack the parent will pad as end-of-row space.\n if (lastGrowIndex !== -1) {\n const target = baseSum + slack\n if (allocated !== target) {\n widths[lastGrowIndex] = Math.max(0, widths[lastGrowIndex] + (target - allocated))\n }\n }\n } else {\n // Shrink phase — distribute the deficit weighted by `basis * shrink`.\n // Items at their `mins[i]` floor stop shrinking; the remaining\n // deficit re-routes to the others. Iterate until stable or no\n // shrinkable items remain.\n for (let i = 0; i < items.length; i++) widths[i] = bases[i]\n let deficit = -slack // positive\n const frozen: boolean[] = Array.from({ length: items.length }, () => false)\n // Cap loop at items.length+1 — each pass freezes at least one item\n // or fully resolves the deficit. Bounded, no risk of looping.\n for (let pass = 0; pass <= items.length && deficit > 0; pass++) {\n let weightSum = 0\n for (let i = 0; i < items.length; i++) {\n if (!frozen[i]) weightSum += widths[i] * shrinks[i]\n }\n if (weightSum === 0) break // nothing left can shrink\n let froze = false\n for (let i = 0; i < items.length; i++) {\n if (frozen[i]) continue\n const share = Math.floor((deficit * widths[i] * shrinks[i]) / weightSum)\n const next = widths[i] - share\n if (next <= mins[i]) {\n deficit -= widths[i] - mins[i]\n widths[i] = mins[i]\n frozen[i] = true\n froze = true\n } else {\n widths[i] = next\n lastShrinkIndex = i\n }\n }\n if (!froze) {\n // Apply remaining deficit proportionally one more time without\n // freezing — the round above didn't hit any floor.\n deficit = 0\n }\n }\n for (let i = 0; i < items.length; i++) {\n widths[i] = clamp(widths[i], {\n available: contentWidth,\n max: items[i].maxWidth,\n min: mins[i],\n })\n allocated += widths[i]\n }\n // Soak any rounding-shaped over/underflow into the last shrinker so\n // the row sums exactly to `available` (or to the minSum floor when\n // mins force overflow).\n if (lastShrinkIndex !== -1) {\n const target = Math.max(\n available,\n mins.reduce((a, b) => a + b, 0)\n )\n if (allocated !== target) {\n const adj = target - allocated\n widths[lastShrinkIndex] = Math.max(mins[lastShrinkIndex], widths[lastShrinkIndex] + adj)\n }\n }\n }\n\n return widths\n}\n\n/**\n * Whether a `Flexible.width` spec is a fixed numeric size (number or\n * `Pct`). `\"fill\"`/`\"fit\"`/undefined are non-fixed and resolve from\n * surrounding context (slack / measurement).\n *\n * @internal\n */\nexport function isFixedWidth(w: FlexState[\"width\"]): boolean {\n return typeof w === \"number\" || (typeof w === \"string\" && w.endsWith(\"%\"))\n}\n\nexport interface ZipOpts {\n widths: readonly number[]\n gap: number\n}\n\n/**\n * Zip pre-rendered child rows horizontally. Shorter children are padded with\n * blank rows of their allocated width. Rows of each child must already be\n * padded to the corresponding width in `widths`.\n *\n * @internal\n */\nexport function zipRow(children: readonly (readonly string[])[], opts: ZipOpts): string[] {\n if (children.length === 0) return []\n const gapStr = \" \".repeat(opts.gap)\n const height = children.reduce((h, rows) => Math.max(h, rows.length), 0)\n const out: string[] = []\n for (let r = 0; r < height; r++) {\n let line = \"\"\n for (let c = 0; c < children.length; c++) {\n if (c > 0) line += gapStr\n const rows = children[c]\n line += r < rows.length ? rows[r] : \" \".repeat(opts.widths[c])\n }\n out.push(line)\n }\n return out\n}\n\nexport interface StackOpts {\n gap: number\n width: number\n}\n\n/**\n * Stack pre-rendered child rows vertically. `gap` blank rows of `width`\n * spaces separate each sibling pair that *contributes rows*. Children\n * that emit no rows (e.g. `visible: false`, falsy `show()` branch) are\n * skipped along with the gap that would have surrounded them — no\n * stray blank bands. Caller must ensure each child's rows are already\n * padded to `width`.\n *\n * @internal\n */\nexport function stackColumn(children: readonly (readonly string[])[], opts: StackOpts): string[] {\n const out: string[] = []\n const blank = \" \".repeat(opts.width)\n let any = false\n for (const child of children) {\n if (child.length === 0) continue\n if (any) for (let g = 0; g < opts.gap; g++) out.push(blank)\n out.push(...child)\n any = true\n }\n return out\n}\n\n/**\n * Pad a row to `width` cells without injecting a `RESET`. Used by the\n * row/column zip-and-pad steps — the parent `Box`'s outer\n * `reapplyStyle` chain handles re-emitting any ambient SGR after inner\n * `[0m`s, so the pad cells stay inside the active style. Over-wide\n * rows are clipped via `sliceAnsi`, preserving SGR state.\n *\n * Distinct from `padOrClip` in `style/ansi.ts`, which is for\n * standalone (non-Box-wrapped) paths and *does* inject a `RESET`\n * before the pad.\n *\n * @internal\n */\nexport function padRow(row: string, width: number): string {\n const w = stringWidth(row)\n if (w === width) return row\n if (w < width) return row + \" \".repeat(width - w)\n return sliceAnsi(row, 0, width)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAmDA,SAAgB,YAAY,OAA2B,MAA8B;CACnF,MAAM,EAAE,iBAAiB;CAEzB,MAAM,YAAY,eADD,KAAK,MAAM,KAAK,IAAI,GAAG,MAAM,SAAS,CAAC;CAGxD,MAAM,QAAkB,MAAM,KAAK,EAAE,QAAQ,MAAM,OAAO,CAAC;CAC3D,MAAM,QAAkB,MAAM,KAAK,EAAE,QAAQ,MAAM,OAAO,CAAC;CAC3D,MAAM,UAAoB,MAAM,KAAK,EAAE,QAAQ,MAAM,OAAO,CAAC;CAG7D,MAAM,OAAiB,MAAM,KAAK,EAAE,QAAQ,MAAM,OAAO,CAAC;CAC1D,IAAI,UAAU;CACd,IAAI,UAAU;CAEd,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACrC,MAAM,OAAO,MAAM;EACnB,IAAI,aAAa,KAAK,KAAK,GAAG;GAC5B,MAAM,KAAK,YAAY,KAAK,OAAO,YAAY,KAAK;GACpD,MAAM,KAAK;GACX,QAAQ,KAAK;EACf,OAAO,IAAI,KAAK,UAAU,QAAQ;GAEhC,MAAM,KAAK;GACX,MAAM,KAAK,KAAK,YAAY;GAC5B,QAAQ,KAAK,KAAK,cAAc;EAClC,OAAO;GACL,MAAM,KAAK,KAAK,WAAW;GAC3B,MAAM,KAAK,KAAK,YAAY;GAC5B,QAAQ,KAAK,KAAK,cAAc;EAClC;EACA,MAAM,eAAe,KAAK,gBAAgB;EAC1C,MAAM,UAAU,YAAY,KAAK,UAAU,YAAY,KAAK;EAC5D,KAAK,KAAK,KAAK,IAAI,cAAc,OAAO;EACxC,WAAW,MAAM;EACjB,WAAW,MAAM;CACnB;CAEA,MAAM,QAAQ,YAAY;CAC1B,MAAM,SAAmB,MAAM,KAAK,EAAE,QAAQ,MAAM,OAAO,CAAC;CAC5D,IAAI,YAAY;CAChB,IAAI,gBAAgB;CACpB,IAAI,kBAAkB;CAEtB,IAAI,SAAS,GAAG;EAEd,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;GACrC,MAAM,OAAO,MAAM;GACnB,IAAI,IAAI,MAAM;GACd,IAAI,UAAU,KAAK,MAAM,KAAK,GAAG;IAC/B,KAAK,KAAK,MAAO,QAAQ,MAAM,KAAM,OAAO;IAC5C,gBAAgB;GAClB;GACA,IAAI,MAAM,GAAG;IAAE,WAAW;IAAc,KAAK,KAAK;IAAU,KAAK,KAAK;GAAG,CAAC;GAC1E,OAAO,KAAK;GACZ,aAAa;EACf;EAKA,IAAI,kBAAkB,IAAI;GACxB,MAAM,SAAS,UAAU;GACzB,IAAI,cAAc,QAChB,OAAO,iBAAiB,KAAK,IAAI,GAAG,OAAO,kBAAkB,SAAS,UAAU;EAEpF;CACF,OAAO;EAKL,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,OAAO,KAAK,MAAM;EACzD,IAAI,UAAU,CAAC;EACf,MAAM,SAAoB,MAAM,KAAK,EAAE,QAAQ,MAAM,OAAO,SAAS,KAAK;EAG1E,KAAK,IAAI,OAAO,GAAG,QAAQ,MAAM,UAAU,UAAU,GAAG,QAAQ;GAC9D,IAAI,YAAY;GAChB,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAChC,IAAI,CAAC,OAAO,IAAI,aAAa,OAAO,KAAK,QAAQ;GAEnD,IAAI,cAAc,GAAG;GACrB,IAAI,QAAQ;GACZ,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;IACrC,IAAI,OAAO,IAAI;IACf,MAAM,QAAQ,KAAK,MAAO,UAAU,OAAO,KAAK,QAAQ,KAAM,SAAS;IACvE,MAAM,OAAO,OAAO,KAAK;IACzB,IAAI,QAAQ,KAAK,IAAI;KACnB,WAAW,OAAO,KAAK,KAAK;KAC5B,OAAO,KAAK,KAAK;KACjB,OAAO,KAAK;KACZ,QAAQ;IACV,OAAO;KACL,OAAO,KAAK;KACZ,kBAAkB;IACpB;GACF;GACA,IAAI,CAAC,OAGH,UAAU;EAEd;EACA,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;GACrC,OAAO,KAAK,MAAM,OAAO,IAAI;IAC3B,WAAW;IACX,KAAK,MAAM,EAAE,CAAC;IACd,KAAK,KAAK;GACZ,CAAC;GACD,aAAa,OAAO;EACtB;EAIA,IAAI,oBAAoB,IAAI;GAC1B,MAAM,SAAS,KAAK,IAClB,WACA,KAAK,QAAQ,GAAG,MAAM,IAAI,GAAG,CAAC,CAChC;GACA,IAAI,cAAc,QAAQ;IACxB,MAAM,MAAM,SAAS;IACrB,OAAO,mBAAmB,KAAK,IAAI,KAAK,kBAAkB,OAAO,mBAAmB,GAAG;GACzF;EACF;CACF;CAEA,OAAO;AACT;;;;;;;;AASA,SAAgB,aAAa,GAAgC;CAC3D,OAAO,OAAO,MAAM,YAAa,OAAO,MAAM,YAAY,EAAE,SAAS,GAAG;AAC1E;;;;;;;;AAcA,SAAgB,OAAO,UAA0C,MAAyB;CACxF,IAAI,SAAS,WAAW,GAAG,OAAO,CAAC;CACnC,MAAM,SAAS,IAAI,OAAO,KAAK,GAAG;CAClC,MAAM,SAAS,SAAS,QAAQ,GAAG,SAAS,KAAK,IAAI,GAAG,KAAK,MAAM,GAAG,CAAC;CACvE,MAAM,MAAgB,CAAC;CACvB,KAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,KAAK;EAC/B,IAAI,OAAO;EACX,KAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;GACxC,IAAI,IAAI,GAAG,QAAQ;GACnB,MAAM,OAAO,SAAS;GACtB,QAAQ,IAAI,KAAK,SAAS,KAAK,KAAK,IAAI,OAAO,KAAK,OAAO,EAAE;EAC/D;EACA,IAAI,KAAK,IAAI;CACf;CACA,OAAO;AACT;;;;;;;;;;;AAiBA,SAAgB,YAAY,UAA0C,MAA2B;CAC/F,MAAM,MAAgB,CAAC;CACvB,MAAM,QAAQ,IAAI,OAAO,KAAK,KAAK;CACnC,IAAI,MAAM;CACV,KAAK,MAAM,SAAS,UAAU;EAC5B,IAAI,MAAM,WAAW,GAAG;EACxB,IAAI,KAAK,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK;EAC1D,IAAI,KAAK,GAAG,KAAK;EACjB,MAAM;CACR;CACA,OAAO;AACT;;;;;;;;;;;;;;AAeA,SAAgB,OAAO,KAAa,OAAuB;CACzD,MAAM,IAAI,YAAY,GAAG;CACzB,IAAI,MAAM,OAAO,OAAO;CACxB,IAAI,IAAI,OAAO,OAAO,MAAM,IAAI,OAAO,QAAQ,CAAC;CAChD,OAAO,UAAU,KAAK,GAAG,KAAK;AAChC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { stringWidth } from "@zaly/shared/ansi";
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
|
-
|
|
3
|
+
// src/widgets/completions/github.ts
|
|
4
4
|
/**
|
|
5
5
|
* Completion source for GitHub issues and pull requests from the repo
|
|
6
6
|
* at `cwd`. Shells out to the `gh` CLI on first use, caches the result,
|
|
@@ -131,7 +131,6 @@ function runGh(cwd, args) {
|
|
|
131
131
|
}
|
|
132
132
|
});
|
|
133
133
|
}
|
|
134
|
-
//#endregion
|
|
135
134
|
export { githubSource as t };
|
|
136
135
|
|
|
137
|
-
//# sourceMappingURL=github-
|
|
136
|
+
//# sourceMappingURL=github-CLveT9mq.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"github-
|
|
1
|
+
{"version":3,"file":"github-CLveT9mq.mjs","names":[],"sources":["../src/widgets/completions/github.ts"],"sourcesContent":["import type { ScoredItem } from \"../../search/matcher.ts\"\nimport type { CompletionSource } from \"../autocomplete.ts\"\nimport type { PickerItem } from \"../picker.ts\"\nimport type { OptionRender } from \"../select.ts\"\n\nimport { stringWidth } from \"@zaly/shared/ansi\"\nimport { spawn } from \"node:child_process\"\n\n/** A GitHub issue or pull request returned by `githubSource`. Shape\n * mirrors the `gh` CLI's `--json number,title,state,author,url`\n * output, with `type` added to distinguish issues from PRs. */\nexport type GithubItem = PickerItem & {\n number: number\n title: string\n /** `\"open\"` / `\"closed\"` for issues; `\"open\"` / `\"closed\"` / `\"merged\"`\n * for PRs. Kept as a string so we don't trip if gh evolves. */\n state: string\n type: \"issue\" | \"pr\"\n url: string\n author?: { login?: string }\n}\n\nexport type GithubState = \"open\" | \"closed\" | \"all\"\n\nexport type GithubFetcher = (cwd: string, state: GithubState) => Promise<GithubItem[]>\n\nexport interface GithubSourceOptions {\n /** Working directory used to locate the repo. `gh` resolves the repo\n * from the git remote of this dir. Default: `process.cwd()`. */\n cwd?: string\n /** Trigger regex. Default: `/(?<=^|\\s)#/` — `#` preceded by start-of-\n * string or whitespace (same lookbehind trick as `filesSource`). */\n trigger?: RegExp\n /** Prepended to the accepted value so the trigger character stays in\n * the input. Default: `\"#\"`. */\n prefix?: string\n /** Which issues / PRs to include. Default: `\"open\"`. */\n state?: GithubState\n /** Per-kind cap passed to `gh`. Default: `50`. */\n limit?: number\n /** Override the fetch implementation. Mainly useful for tests — the\n * default shells out to `gh`. The fetcher is called at most once per\n * `githubSource` instance; results are cached thereafter. */\n fetcher?: GithubFetcher\n}\n\n/**\n * Completion source for GitHub issues and pull requests from the repo\n * at `cwd`. Shells out to the `gh` CLI on first use, caches the result,\n * and fuzzy-filters locally on subsequent calls so typing stays snappy.\n *\n * Accepts insert `#123 ` into the input — ready for a markdown-style\n * reference in a chat message or commit subject.\n *\n * ```ts\n * autocomplete({\n * input: \"chat-input\",\n * sources: {\n * gh: githubSource(),\n * },\n * })\n * ```\n *\n * Silently returns no items when `gh` isn't installed, the user isn't\n * authenticated, or the directory isn't a GitHub repo — so it's safe\n * to wire up unconditionally.\n */\nexport function githubSource(opts: GithubSourceOptions = {}): CompletionSource<GithubItem> {\n const cwd = opts.cwd ?? process.cwd()\n const trigger = opts.trigger ?? /(?<=^|\\s)#/\n const prefix = opts.prefix ?? \"#\"\n const state: GithubState = opts.state ?? \"open\"\n const limit = opts.limit ?? 50\n const fetcher = opts.fetcher ?? defaultFetcher(limit)\n\n // Lazy, single-fetch cache. The Promise is reused by concurrent\n // callers so rapid keystrokes while the first fetch is in flight\n // don't spawn duplicate `gh` processes.\n let cache: Promise<GithubItem[]> | undefined\n\n return {\n accept: (item) => `${prefix}${item.number} `,\n async complete(_query: string, match): Promise<ScoredItem<GithubItem>[]> {\n cache ??= fetcher(cwd, state).catch(() => [])\n const items = await cache\n const out: ScoredItem<GithubItem>[] = []\n for (const item of items) {\n // Match on \"#<num> <title>\" so users can type either digits or\n // words and both hit. Keeps ranking implicit — source order\n // (freshest first from gh) wins when scores tie.\n const target = `#${item.number} ${item.title}`\n const score = match(target)\n if (!score) continue\n out.push({ ...item, score })\n }\n return out\n },\n render: defaultRender,\n triggers: [trigger],\n }\n}\n\nconst defaultRender: OptionRender<GithubItem> = (item, ctx) => {\n const { style } = ctx\n const num = `#${item.number}`\n const stateSlot = stateStyleSlot(item.state)\n const typeLabel = item.type === \"pr\" ? \"pr\" : \"issue\"\n const author = item.author?.login ? `@${item.author.login}` : \"\"\n\n // Layout: [#num] [state/type] title ............. @author\n // All chrome is left-aligned; author is the right-column hint.\n const lead = `${style.add(\"optionName\")(num)} ${style.add(stateSlot)(`${item.state}·${typeLabel}`)} ${item.title}`\n\n if (author === \"\") return lead\n const gap = 2\n const leadW = stringWidth(lead)\n const hintW = stringWidth(author)\n const padded = ctx.width - leadW - hintW - gap\n if (padded < 1) return lead\n return lead + \" \".repeat(padded + gap) + style.add(\"optionDesc\")(author)\n}\n\nfunction stateStyleSlot(state: string) {\n if (state === \"open\") return \"success\"\n if (state === \"merged\") return \"accent\"\n // \"closed\" and anything unknown → dim.\n return \"muted\"\n}\n\n/** Default fetcher — shells out to `gh issue list` + `gh pr list` in\n * parallel. Either failing is swallowed so a repo with only issues\n * (or only PRs) still works. */\nfunction defaultFetcher(limit: number): GithubFetcher {\n const fields = \"number,title,state,author,url\"\n return async (cwd, state) => {\n const [issues, prs] = await Promise.all([\n runGh(cwd, [\"issue\", \"list\", \"--state\", state, \"--json\", fields, \"--limit\", String(limit)]),\n runGh(cwd, [\"pr\", \"list\", \"--state\", state, \"--json\", fields, \"--limit\", String(limit)]),\n ])\n return [\n ...parse(issues).map((x) => Object.assign(x, { type: \"issue\" as const })),\n ...parse(prs).map((x) => Object.assign(x, { type: \"pr\" as const })),\n ].map((item) => Object.assign(item, { text: `#${item.number} ${item.title}` }))\n }\n}\n\nfunction parse(out: string): Omit<GithubItem, \"type\">[] {\n try {\n return JSON.parse(out) as Omit<GithubItem, \"type\">[]\n } catch {\n return []\n }\n}\n\nfunction runGh(cwd: string, args: string[]): Promise<string> {\n return new Promise((resolve) => {\n try {\n const child = spawn(\"gh\", args, { cwd, stdio: [\"ignore\", \"pipe\", \"ignore\"] })\n let stdout = \"\"\n child.stdout.on(\"data\", (chunk: Buffer) => {\n stdout += chunk.toString(\"utf8\")\n })\n child.on(\"close\", (code) => {\n resolve(code === 0 ? stdout : \"\")\n })\n child.on(\"error\", () => {\n resolve(\"\")\n })\n } catch {\n resolve(\"\")\n }\n })\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { n as PickerItem } from "./picker-
|
|
2
|
-
import { o as CompletionSource } from "./autocomplete-
|
|
3
|
-
//#region src/widgets/completions/github.d.ts
|
|
1
|
+
import { n as PickerItem } from "./picker-DsB6--GN.mjs";
|
|
2
|
+
import { o as CompletionSource } from "./autocomplete-wgceuhLr.mjs";
|
|
4
3
|
/** A GitHub issue or pull request returned by `githubSource`. Shape
|
|
5
4
|
* mirrors the `gh` CLI's `--json number,title,state,author,url`
|
|
6
5
|
* output, with `type` added to distinguish issues from PRs. */
|
|
@@ -59,6 +58,5 @@ interface GithubSourceOptions {
|
|
|
59
58
|
* to wire up unconditionally.
|
|
60
59
|
*/
|
|
61
60
|
declare function githubSource(opts?: GithubSourceOptions): CompletionSource<GithubItem>;
|
|
62
|
-
//#endregion
|
|
63
61
|
export { githubSource as a, GithubState as i, GithubItem as n, GithubSourceOptions as r, GithubFetcher as t };
|
|
64
|
-
//# sourceMappingURL=github-
|
|
62
|
+
//# sourceMappingURL=github-DC0XemKa.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-DC0XemKa.d.mts","names":[],"sources":["../src/widgets/completions/github.ts"],"mappings":";;;;;KAWY,aAAa;EACvB;EACA;;;EAGA;EACA;EACA;EACA;IAAW;;;KAGD;KAEA,iBAAiB,aAAa,OAAO,gBAAgB,QAAQ;UAExD;;;EAGf;;;EAGA,UAAU;;;EAGV;;EAEA,QAAQ;;EAER;;;;EAIA,UAAU;;;;;;;;;;;;;;;;;;;;;;;iBAwBI,aAAa,OAAM,sBAA2B,iBAAiB"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
//#region src/widgets/image.d.ts
|
|
1
|
+
import { I as RenderCtx, X as Layout, h as Node } from "./queries-Dmd3IjSd.mjs";
|
|
3
2
|
interface ImageState {
|
|
4
3
|
/**
|
|
5
4
|
* Path to an image file. PNG passes through with zero decoding; other
|
|
@@ -32,6 +31,5 @@ declare class Image extends Node<ImageState> {
|
|
|
32
31
|
}
|
|
33
32
|
declare function image(src: string, style?: Omit<ImageState, "src">): Image;
|
|
34
33
|
declare function image(state: ImageState): Image;
|
|
35
|
-
//#endregion
|
|
36
34
|
export { ImageState as n, image as r, Image as t };
|
|
37
|
-
//# sourceMappingURL=image-
|
|
35
|
+
//# sourceMappingURL=image-DdRDv7q6.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-DdRDv7q6.d.mts","names":[],"sources":["../src/widgets/image.ts"],"mappings":";UAWiB;;;;;;;EAOf;;EAEA;;;;;EAKA;;EAEA;;;;;EAKA;;cAGW,cAAc,KAAK;;EAI9B,YAAY,OAAO;MAOf;EAIK,UAAU;YAIH,QAAQ,KAAK,YAAY;;iBAuC3B,MAAM,aAAa,QAAQ,KAAK,qBAAqB;iBACrD,MAAM,OAAO,aAAa"}
|