buninu-linux 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/LICENSES/Bun-LICENSE.md +84 -0
- package/LICENSES/GCC-Runtime-Library-Exception-3.1.txt +73 -0
- package/LICENSES/GPL-2.0.txt +338 -0
- package/LICENSES/GPL-3.0.txt +674 -0
- package/LICENSES/LGPL-2.1.txt +501 -0
- package/LICENSES/Linux-syscall-note.txt +25 -0
- package/LICENSES/musl-COPYRIGHT.txt +193 -0
- package/NOTICE.md +184 -0
- package/README.md +537 -0
- package/build-image.sh +44 -0
- package/build-uki.sh +75 -0
- package/fetch-alpine.sh +80 -0
- package/fetch-bun.sh +41 -0
- package/fetch-plus-build.sh +11 -0
- package/hello/build-hello.sh +14 -0
- package/hello/hello.c +50 -0
- package/index.js +235 -0
- package/initramfs/bin/sh +3 -0
- package/initramfs/buninu/.bashrc +11 -0
- package/initramfs/buninu/ARCHITECTURE.md +452 -0
- package/initramfs/buninu/CHANGELOG.md +580 -0
- package/initramfs/buninu/LICENSE +21 -0
- package/initramfs/buninu/README.md +1161 -0
- package/initramfs/buninu/apps/bun/make-bunBin.sh +99 -0
- package/initramfs/buninu/apps/bunmsh/CHANGELOG.md +493 -0
- package/initramfs/buninu/apps/bunmsh/LICENSE +21 -0
- package/initramfs/buninu/apps/bunmsh/LICENSE-MICRO +58 -0
- package/initramfs/buninu/apps/bunmsh/LICENSE-MKSH +295 -0
- package/initramfs/buninu/apps/bunmsh/PORTING.md +192 -0
- package/initramfs/buninu/apps/bunmsh/README.md +1042 -0
- package/initramfs/buninu/apps/bunmsh/bunmsh +7 -0
- package/initramfs/buninu/apps/bunmsh/bunshell.md +61 -0
- package/initramfs/buninu/apps/bunmsh/help/README.md +2237 -0
- package/initramfs/buninu/apps/bunmsh/help/alias.md +22 -0
- package/initramfs/buninu/apps/bunmsh/help/basename.md +38 -0
- package/initramfs/buninu/apps/bunmsh/help/builtin.md +25 -0
- package/initramfs/buninu/apps/bunmsh/help/bun.md +27 -0
- package/initramfs/buninu/apps/bunmsh/help/bunmsh.md +47 -0
- package/initramfs/buninu/apps/bunmsh/help/cat.md +31 -0
- package/initramfs/buninu/apps/bunmsh/help/catfancy.md +35 -0
- package/initramfs/buninu/apps/bunmsh/help/cd.md +22 -0
- package/initramfs/buninu/apps/bunmsh/help/chmod.md +27 -0
- package/initramfs/buninu/apps/bunmsh/help/clear.md +21 -0
- package/initramfs/buninu/apps/bunmsh/help/colon.md +22 -0
- package/initramfs/buninu/apps/bunmsh/help/command.md +27 -0
- package/initramfs/buninu/apps/bunmsh/help/cp.md +28 -0
- package/initramfs/buninu/apps/bunmsh/help/curl.md +181 -0
- package/initramfs/buninu/apps/bunmsh/help/cut.md +25 -0
- package/initramfs/buninu/apps/bunmsh/help/date.md +25 -0
- package/initramfs/buninu/apps/bunmsh/help/dirname.md +25 -0
- package/initramfs/buninu/apps/bunmsh/help/echo.md +25 -0
- package/initramfs/buninu/apps/bunmsh/help/env.md +27 -0
- package/initramfs/buninu/apps/bunmsh/help/eval.md +22 -0
- package/initramfs/buninu/apps/bunmsh/help/exec.md +22 -0
- package/initramfs/buninu/apps/bunmsh/help/exit.md +21 -0
- package/initramfs/buninu/apps/bunmsh/help/export.md +22 -0
- package/initramfs/buninu/apps/bunmsh/help/false.md +22 -0
- package/initramfs/buninu/apps/bunmsh/help/find.md +33 -0
- package/initramfs/buninu/apps/bunmsh/help/getopts.md +28 -0
- package/initramfs/buninu/apps/bunmsh/help/grep.md +34 -0
- package/initramfs/buninu/apps/bunmsh/help/head.md +27 -0
- package/initramfs/buninu/apps/bunmsh/help/kill.md +43 -0
- package/initramfs/buninu/apps/bunmsh/help/ln.md +28 -0
- package/initramfs/buninu/apps/bunmsh/help/ls.md +37 -0
- package/initramfs/buninu/apps/bunmsh/help/lsbun.md +29 -0
- package/initramfs/buninu/apps/bunmsh/help/lsfancy.md +37 -0
- package/initramfs/buninu/apps/bunmsh/help/md5sum.md +21 -0
- package/initramfs/buninu/apps/bunmsh/help/mkdir.md +27 -0
- package/initramfs/buninu/apps/bunmsh/help/mktemp.md +26 -0
- package/initramfs/buninu/apps/bunmsh/help/mv.md +26 -0
- package/initramfs/buninu/apps/bunmsh/help/print.md +30 -0
- package/initramfs/buninu/apps/bunmsh/help/printf.md +25 -0
- package/initramfs/buninu/apps/bunmsh/help/pspa.md +38 -0
- package/initramfs/buninu/apps/bunmsh/help/pspac.md +57 -0
- package/initramfs/buninu/apps/bunmsh/help/pwd.md +21 -0
- package/initramfs/buninu/apps/bunmsh/help/read.md +27 -0
- package/initramfs/buninu/apps/bunmsh/help/readonly.md +27 -0
- package/initramfs/buninu/apps/bunmsh/help/realpath.md +21 -0
- package/initramfs/buninu/apps/bunmsh/help/rm.md +33 -0
- package/initramfs/buninu/apps/bunmsh/help/rmdir.md +27 -0
- package/initramfs/buninu/apps/bunmsh/help/sed.md +29 -0
- package/initramfs/buninu/apps/bunmsh/help/seq.md +29 -0
- package/initramfs/buninu/apps/bunmsh/help/serve.md +50 -0
- package/initramfs/buninu/apps/bunmsh/help/set.md +26 -0
- package/initramfs/buninu/apps/bunmsh/help/sha256sum.md +21 -0
- package/initramfs/buninu/apps/bunmsh/help/shift.md +23 -0
- package/initramfs/buninu/apps/bunmsh/help/sleep.md +25 -0
- package/initramfs/buninu/apps/bunmsh/help/sort.md +29 -0
- package/initramfs/buninu/apps/bunmsh/help/source.md +23 -0
- package/initramfs/buninu/apps/bunmsh/help/tab.md +30 -0
- package/initramfs/buninu/apps/bunmsh/help/tac.md +29 -0
- package/initramfs/buninu/apps/bunmsh/help/tail.md +27 -0
- package/initramfs/buninu/apps/bunmsh/help/tee.md +25 -0
- package/initramfs/buninu/apps/bunmsh/help/test.md +30 -0
- package/initramfs/buninu/apps/bunmsh/help/time.md +29 -0
- package/initramfs/buninu/apps/bunmsh/help/touch.md +26 -0
- package/initramfs/buninu/apps/bunmsh/help/tr.md +26 -0
- package/initramfs/buninu/apps/bunmsh/help/true.md +22 -0
- package/initramfs/buninu/apps/bunmsh/help/type.md +21 -0
- package/initramfs/buninu/apps/bunmsh/help/umask.md +22 -0
- package/initramfs/buninu/apps/bunmsh/help/unalias.md +28 -0
- package/initramfs/buninu/apps/bunmsh/help/uname.md +31 -0
- package/initramfs/buninu/apps/bunmsh/help/unset.md +28 -0
- package/initramfs/buninu/apps/bunmsh/help/wc.md +27 -0
- package/initramfs/buninu/apps/bunmsh/help/whence.md +26 -0
- package/initramfs/buninu/apps/bunmsh/help/which.md +41 -0
- package/initramfs/buninu/apps/bunmsh/help/yes.md +23 -0
- package/initramfs/buninu/apps/bunmsh/index.js +7 -0
- package/initramfs/buninu/apps/bunmsh/package.json +30 -0
- package/initramfs/buninu/apps/bunmsh/serve.js +468 -0
- package/initramfs/buninu/apps/bunmsh/single-exe/README.md +402 -0
- package/initramfs/buninu/apps/bunmsh/single-exe/assetsHelper.js +371 -0
- package/initramfs/buninu/apps/bunmsh/single-exe/assetsLoader.mjs +85 -0
- package/initramfs/buninu/apps/bunmsh/single-exe/assetsPacker.js +218 -0
- package/initramfs/buninu/apps/bunmsh/single-exe/compiled.js +527 -0
- package/initramfs/buninu/apps/bunmsh/single-exe/entry.mjs +17 -0
- package/initramfs/buninu/apps/bunmsh/src/cat-fancy.js +117 -0
- package/initramfs/buninu/apps/bunmsh/src/cat-operands.js +31 -0
- package/initramfs/buninu/apps/bunmsh/src/completion.js +363 -0
- package/initramfs/buninu/apps/bunmsh/src/curl.js +1191 -0
- package/initramfs/buninu/apps/bunmsh/src/environment.js +61 -0
- package/initramfs/buninu/apps/bunmsh/src/fancy-ls.js +186 -0
- package/initramfs/buninu/apps/bunmsh/src/find.js +119 -0
- package/initramfs/buninu/apps/bunmsh/src/history.js +198 -0
- package/initramfs/buninu/apps/bunmsh/src/main.js +850 -0
- package/initramfs/buninu/apps/bunmsh/src/mouse.js +103 -0
- package/initramfs/buninu/apps/bunmsh/src/shell.js +3932 -0
- package/initramfs/buninu/apps/bunmsh/test/catfancy.test.js +102 -0
- package/initramfs/buninu/apps/bunmsh/test/curl.test.js +335 -0
- package/initramfs/buninu/apps/bunmsh/test/reference-source.sh +1 -0
- package/initramfs/buninu/apps/bunmsh/test/reference.test.js +817 -0
- package/initramfs/buninu/apps/bunmsh/test/serve/README.md +3 -0
- package/initramfs/buninu/apps/bunmsh/test/serve/bad.json +1 -0
- package/initramfs/buninu/apps/bunmsh/test/serve/data.json +1 -0
- package/initramfs/buninu/apps/bunmsh/test/serve/data.json5 +1 -0
- package/initramfs/buninu/apps/bunmsh/test/serve/data.jsonc +2 -0
- package/initramfs/buninu/apps/bunmsh/test/serve/data.jsonl +2 -0
- package/initramfs/buninu/apps/bunmsh/test/serve/data.ndjson +1 -0
- package/initramfs/buninu/apps/bunmsh/test/serve/data.toml +3 -0
- package/initramfs/buninu/apps/bunmsh/test/serve/data.ts +15 -0
- package/initramfs/buninu/apps/bunmsh/test/serve/data.xml +1 -0
- package/initramfs/buninu/apps/bunmsh/test/serve/data.yaml +3 -0
- package/initramfs/buninu/apps/bunmsh/test/serve/data.yml +1 -0
- package/initramfs/buninu/apps/bunmsh/test/serve.test.js +241 -0
- package/initramfs/buninu/apps/bunmsh/test/shell.test.js +2092 -0
- package/initramfs/buninu/apps/bunmsh/test/shell1.test.js +1087 -0
- package/initramfs/buninu/apps/bunmsh/test/shell2.test.js +457 -0
- package/initramfs/buninu/apps/bunmsh/test/shell3.test.js +741 -0
- package/initramfs/buninu/apps/bunmsh/test/t.sh +12 -0
- package/initramfs/buninu/apps/bunx/bunx.js +172 -0
- package/initramfs/buninu/apps/cmdlist +48 -0
- package/initramfs/buninu/apps/jsgotty/LICENSE +23 -0
- package/initramfs/buninu/apps/jsgotty/LICENSES/Apache-2.0-zmodem.js.txt +201 -0
- package/initramfs/buninu/apps/jsgotty/NOTICE +21 -0
- package/initramfs/buninu/apps/jsgotty/README.en.md +294 -0
- package/initramfs/buninu/apps/jsgotty/README.md +296 -0
- package/initramfs/buninu/apps/jsgotty/client.mjs +700 -0
- package/initramfs/buninu/apps/jsgotty/env.sh +2 -0
- package/initramfs/buninu/apps/jsgotty/gotty.js +3334 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/.package-lock.json +35 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/LICENSE.md +9 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/README.md +319 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/common.gypi +20 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/except.gypi +25 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/index.js +12 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/napi-inl.deprecated.h +186 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/napi-inl.h +6607 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/napi.h +3201 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/node_addon_api.gyp +32 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/node_api.gyp +9 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/noexcept.gypi +26 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/nothing.c +0 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/package-support.json +21 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/package.json +480 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/tools/README.md +73 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/tools/check-napi.js +99 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/tools/clang-format.js +71 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/tools/conversion.js +301 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/tools/eslint-format.js +79 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/ws/LICENSE +20 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/ws/README.md +548 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/ws/browser.js +8 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/ws/index.js +22 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/ws/lib/buffer-util.js +131 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/ws/lib/constants.js +19 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/ws/lib/event-target.js +292 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/ws/lib/extension.js +203 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/ws/lib/limiter.js +55 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/ws/lib/permessage-deflate.js +528 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/ws/lib/receiver.js +760 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/ws/lib/sender.js +607 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/ws/lib/stream.js +161 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/ws/lib/subprotocol.js +62 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/ws/lib/validation.js +152 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/ws/lib/websocket-server.js +562 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/ws/lib/websocket.js +1407 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/ws/package.json +70 -0
- package/initramfs/buninu/apps/jsgotty/node_modules/ws/wrapper.mjs +21 -0
- package/initramfs/buninu/apps/jsgotty/package-lock.json +46 -0
- package/initramfs/buninu/apps/jsgotty/package.json +19 -0
- package/initramfs/buninu/apps/jsgotty/patches/windowsPtyAgent.js +326 -0
- package/initramfs/buninu/apps/jsgotty/patches/windowsTerminal.js +204 -0
- package/initramfs/buninu/apps/jsgotty/rz.js +199 -0
- package/initramfs/buninu/apps/jsgotty/single-exe/README.md +384 -0
- package/initramfs/buninu/apps/jsgotty/single-exe/assetsHelper.js +371 -0
- package/initramfs/buninu/apps/jsgotty/single-exe/assetsLoader.mjs +85 -0
- package/initramfs/buninu/apps/jsgotty/single-exe/assetsPacker.js +218 -0
- package/initramfs/buninu/apps/jsgotty/single-exe/compiled.js +498 -0
- package/initramfs/buninu/apps/jsgotty/single-exe/entry.mjs +23 -0
- package/initramfs/buninu/apps/jsgotty/single-exe/packAssets.sh +7 -0
- package/initramfs/buninu/apps/jsgotty/static/css/index.css +13 -0
- package/initramfs/buninu/apps/jsgotty/static/css/kitty.css +61 -0
- package/initramfs/buninu/apps/jsgotty/static/css/xterm.css +218 -0
- package/initramfs/buninu/apps/jsgotty/static/css/xterm_customize.css +35 -0
- package/initramfs/buninu/apps/jsgotty/static/favicon.ico +0 -0
- package/initramfs/buninu/apps/jsgotty/static/help.md +125 -0
- package/initramfs/buninu/apps/jsgotty/static/icon.svg +66 -0
- package/initramfs/buninu/apps/jsgotty/static/icon_192.png +0 -0
- package/initramfs/buninu/apps/jsgotty/static/index.html +164 -0
- package/initramfs/buninu/apps/jsgotty/static/js/gotty.js +31537 -0
- package/initramfs/buninu/apps/jsgotty/static/js/gotty.js.LICENSE.txt +7 -0
- package/initramfs/buninu/apps/jsgotty/static/js/gotty.js.map +1 -0
- package/initramfs/buninu/apps/jsgotty/static/js/gotty.licenses.txt +650 -0
- package/initramfs/buninu/apps/jsgotty/static/js/kitty.js +1101 -0
- package/initramfs/buninu/apps/jsgotty/static/js/windows_bridge.js +194 -0
- package/initramfs/buninu/apps/jsgotty/static/js/xterm-addon-unicode11.js +2 -0
- package/initramfs/buninu/apps/jsgotty/static/js/zmodem.js +1 -0
- package/initramfs/buninu/apps/jsgotty/static/manifest.json +15 -0
- package/initramfs/buninu/apps/jsgotty/sz.js +273 -0
- package/initramfs/buninu/apps/jsgotty/tests/kitty-order.test.js +58 -0
- package/initramfs/buninu/apps/jsgotty/tests/websocket-origin.test.js +54 -0
- package/initramfs/buninu/apps/jsgotty/viu.mjs +422 -0
- package/initramfs/buninu/apps/jsgotty/wgotty +62 -0
- package/initramfs/buninu/apps/jsgotty/windows-bridge-node.js +60 -0
- package/initramfs/buninu/apps/jsgotty/zmodem-node.js +262 -0
- package/initramfs/buninu/apps/jsmdcui/CHANGELOG.md +1118 -0
- package/initramfs/buninu/apps/jsmdcui/LICENSE +22 -0
- package/initramfs/buninu/apps/jsmdcui/README.md +1283 -0
- package/initramfs/buninu/apps/jsmdcui/cdp-maze.js +141 -0
- package/initramfs/buninu/apps/jsmdcui/clean.sh +9 -0
- package/initramfs/buninu/apps/jsmdcui/demos/basic.jpg +0 -0
- package/initramfs/buninu/apps/jsmdcui/demos/chinese.md +32 -0
- package/initramfs/buninu/apps/jsmdcui/demos/event.md +170 -0
- package/initramfs/buninu/apps/jsmdcui/demos/good.jpg +0 -0
- package/initramfs/buninu/apps/jsmdcui/demos/image-processor.md +366 -0
- package/initramfs/buninu/apps/jsmdcui/demos/image-processor.zh-TW.md +366 -0
- package/initramfs/buninu/apps/jsmdcui/demos/img-change.md +73 -0
- package/initramfs/buninu/apps/jsmdcui/demos/imgtool-zh.md +298 -0
- package/initramfs/buninu/apps/jsmdcui/demos/imgtool.md +298 -0
- package/initramfs/buninu/apps/jsmdcui/demos/maze.md +149 -0
- package/initramfs/buninu/apps/jsmdcui/demos/reactive.md +40 -0
- package/initramfs/buninu/apps/jsmdcui/demos/reader.md +513 -0
- package/initramfs/buninu/apps/jsmdcui/demos/select.md +63 -0
- package/initramfs/buninu/apps/jsmdcui/demos/todo-zh.md +190 -0
- package/initramfs/buninu/apps/jsmdcui/demos/todo.md +191 -0
- package/initramfs/buninu/apps/jsmdcui/edit +9 -0
- package/initramfs/buninu/apps/jsmdcui/jsmdcui.sh +16 -0
- package/initramfs/buninu/apps/jsmdcui/llm-maze.txt +94 -0
- package/initramfs/buninu/apps/jsmdcui/package.json +29 -0
- package/initramfs/buninu/apps/jsmdcui/project_structure.txt +35 -0
- package/initramfs/buninu/apps/jsmdcui/runmd.mjs +1140 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/atom-dark.micro +33 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/bubblegum.micro +31 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/cmc-16.micro +47 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/cmc-tc.micro +43 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/darcula.micro +34 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/default.micro +1 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/dracula-tc.micro +49 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/dukedark-tc.micro +38 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/dukelight-tc.micro +38 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/dukeubuntu-tc.micro +38 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/geany.micro +29 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/gotham.micro +29 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/gruvbox-tc.micro +29 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/gruvbox.micro +26 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/material-tc.micro +36 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/monokai-dark.micro +28 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/monokai.micro +34 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/one-dark.micro +39 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/railscast.micro +37 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/simple.micro +33 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/solarized-tc.micro +31 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/solarized.micro +30 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/sunny-day.micro +29 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/twilight.micro +40 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/zenburn.micro +30 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/help/actions.md +161 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/help/cdp.md +130 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/help/colors.md +421 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/help/commands.md +161 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/help/copypaste.md +149 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/help/defaultkeys.md +148 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/help/help.md +1283 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/help/keybindings.md +355 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/help/linter.md +90 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/help/options.md +709 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/help/plugins.md +544 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/help/tutorial.md +112 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/jsplugins/cdp/cdp-server.js +1263 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/jsplugins/cdp/cdp.js +203 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/jsplugins/chapter/chapter.js +111 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/jsplugins/diff/diff.js +46 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/jsplugins/example/example.js +117 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/jsplugins/linter/linter.js +281 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/plugins/autoclose/autoclose.lua +75 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/plugins/ftoptions/ftoptions.lua +17 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/plugins/literate/README.md +5 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/plugins/literate/literate.lua +55 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/plugins/status/help/status.md +21 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/plugins/status/status.lua +62 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/LICENSE +22 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/PowerShell.yaml +128 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/README.md +63 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/ada.yaml +43 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/apacheconf.yaml +59 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/arduino.yaml +101 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/asciidoc.yaml +51 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/asm.yaml +123 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/ats.yaml +99 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/awk.yaml +44 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/b.yaml +87 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/bat.yaml +57 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/c.yaml +60 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/caddyfile.yaml +23 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/cake.yaml +7 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/clojure.yaml +38 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/cmake.yaml +42 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/coffeescript.yaml +56 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/colortest.yaml +19 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/conky.yaml +17 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/cpp.yaml +91 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/crontab.yaml +36 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/crystal.yaml +72 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/csharp.yaml +52 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/css.yaml +44 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/csx.yaml +8 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/cuda.yaml +68 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/cython.yaml +52 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/d.yaml +121 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/dart.yaml +46 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/default.yaml +10 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/dockerfile.yaml +36 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/dot.yaml +29 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/elixir.yaml +30 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/elm.yaml +38 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/erb.yaml +42 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/erlang.yaml +45 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/fish.yaml +48 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/forth.yaml +34 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/fortran.yaml +64 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/freebsd-kernel.yaml +14 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/fsharp.yaml +48 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/gdscript.yaml +61 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/gemini.yaml +19 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/gentoo-ebuild.yaml +48 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/gentoo-etc-portage.yaml +23 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/git-commit.yaml +35 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/git-config.yaml +14 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/git-rebase-todo.yaml +19 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/gleam.yaml +69 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/glsl.yaml +26 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/gnuplot.yaml +15 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/go.yaml +62 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/godoc.yaml +17 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/golo.yaml +73 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/gomod.yaml +31 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/graphql.yaml +47 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/groff.yaml +30 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/groovy.yaml +111 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/haml.yaml +16 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/hare.yaml +52 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/haskell.yaml +52 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/hc.yaml +52 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/html.yaml +70 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/html4.yaml +25 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/html5.yaml +25 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/ini.yaml +23 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/inputrc.yaml +14 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/java.yaml +37 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/javascript.yaml +124 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/jinja2.yaml +19 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/json.yaml +39 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/jsonnet.yaml +92 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/julia.yaml +57 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/justfile.yaml +40 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/keymap.yaml +27 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/kickstart.yaml +16 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/kotlin.yaml +66 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/kvlang.yaml +67 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/ledger.yaml +14 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/lfe.yaml +17 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/lilypond.yaml +26 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/lisp.yaml +17 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/log.yaml +92 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/lua.yaml +111 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/mail.yaml +25 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/makefile.yaml +38 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/man.yaml +12 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/markdown.yaml +65 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/mc.yaml +23 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/meson.yaml +51 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/micro.yaml +34 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/mpdconf.yaml +13 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/msbuild.yaml +6 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/nanorc.yaml +16 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/nftables.yaml +30 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/nginx.yaml +22 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/nim.yaml +27 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/nix.yaml +32 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/nu.yaml +114 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/objc.yaml +60 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/ocaml.yaml +43 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/octave.yaml +83 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/odin.yaml +64 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/pascal.yaml +45 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/patch.yaml +14 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/peg.yaml +16 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/perl.yaml +58 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/php.yaml +60 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/pkg-config.yaml +12 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/po.yaml +12 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/pony.yaml +37 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/pov.yaml +21 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/privoxy-action.yaml +14 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/privoxy-config.yaml +10 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/privoxy-filter.yaml +12 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/proto.yaml +40 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/prql.yaml +84 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/puppet.yaml +22 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/python2.yaml +60 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/python3.yaml +80 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/r.yaml +32 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/raku.yaml +42 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/reST.yaml +18 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/renpy.yaml +15 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/rpmspec.yaml +43 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/ruby.yaml +73 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/rust.yaml +78 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/sage.yaml +60 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/scad.yaml +53 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/scala.yaml +33 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/sed.yaml +13 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/sh.yaml +69 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/sls.yaml +15 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/smalltalk.yaml +55 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/solidity.yaml +41 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/sql.yaml +35 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/stata.yaml +67 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/svelte.yaml +27 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/swift.yaml +103 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/systemd.yaml +16 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/tcl.yaml +18 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/terraform.yaml +87 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/tex.yaml +32 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/toml.yaml +56 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/twig.yaml +55 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/typescript.yaml +49 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/v.yaml +80 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/vala.yaml +26 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/verilog.yaml +60 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/vhdl.yaml +37 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/vi.yaml +31 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/vue.yaml +64 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/xml.yaml +37 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/xresources.yaml +14 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/yaml.yaml +34 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/yum.yaml +12 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/zig.yaml +52 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/zscript.yaml +72 -0
- package/initramfs/buninu/apps/jsmdcui/runtime/syntax/zsh.yaml +52 -0
- package/initramfs/buninu/apps/jsmdcui/single-exe/README.md +396 -0
- package/initramfs/buninu/apps/jsmdcui/single-exe/assetsHelper.js +371 -0
- package/initramfs/buninu/apps/jsmdcui/single-exe/assetsLoader.mjs +85 -0
- package/initramfs/buninu/apps/jsmdcui/single-exe/assetsPacker.js +218 -0
- package/initramfs/buninu/apps/jsmdcui/single-exe/compiled.js +498 -0
- package/initramfs/buninu/apps/jsmdcui/single-exe/entry.mjs +17 -0
- package/initramfs/buninu/apps/jsmdcui/single-exe/packAssets.sh +7 -0
- package/initramfs/buninu/apps/jsmdcui/src/MDCUI_DEFAULT_EDIT +0 -0
- package/initramfs/buninu/apps/jsmdcui/src/buffer/backup.js +165 -0
- package/initramfs/buninu/apps/jsmdcui/src/buffer/buffer.js +126 -0
- package/initramfs/buninu/apps/jsmdcui/src/buffer/fixed3-codec.js +140 -0
- package/initramfs/buninu/apps/jsmdcui/src/buffer/loc.js +38 -0
- package/initramfs/buninu/apps/jsmdcui/src/buffer/message.js +29 -0
- package/initramfs/buninu/apps/jsmdcui/src/build-args.js +40 -0
- package/initramfs/buninu/apps/jsmdcui/src/config/clean.js +172 -0
- package/initramfs/buninu/apps/jsmdcui/src/config/colorscheme.js +99 -0
- package/initramfs/buninu/apps/jsmdcui/src/config/config.js +125 -0
- package/initramfs/buninu/apps/jsmdcui/src/config/defaults.js +109 -0
- package/initramfs/buninu/apps/jsmdcui/src/cui/fence-events.mjs +144 -0
- package/initramfs/buninu/apps/jsmdcui/src/cui/heading-ids.mjs +1568 -0
- package/initramfs/buninu/apps/jsmdcui/src/cui/id-collision.mjs +111 -0
- package/initramfs/buninu/apps/jsmdcui/src/cui/identity.mjs +26 -0
- package/initramfs/buninu/apps/jsmdcui/src/cui/kitty-debug.mjs +25 -0
- package/initramfs/buninu/apps/jsmdcui/src/cui/kitty-images.mjs +280 -0
- package/initramfs/buninu/apps/jsmdcui/src/cui/kitty-mode.mjs +6 -0
- package/initramfs/buninu/apps/jsmdcui/src/cui/rerender-queue.mjs +66 -0
- package/initramfs/buninu/apps/jsmdcui/src/cui/rpc.mjs +1554 -0
- package/initramfs/buninu/apps/jsmdcui/src/cui/server.mjs +128 -0
- package/initramfs/buninu/apps/jsmdcui/src/cui/table-render.mjs +286 -0
- package/initramfs/buninu/apps/jsmdcui/src/cui/table-row-edit.mjs +226 -0
- package/initramfs/buninu/apps/jsmdcui/src/cui/task-checkbox.mjs +60 -0
- package/initramfs/buninu/apps/jsmdcui/src/cui/template-components.mjs +253 -0
- package/initramfs/buninu/apps/jsmdcui/src/cui/text-block-resize.mjs +177 -0
- package/initramfs/buninu/apps/jsmdcui/src/cui/tui-links.mjs +560 -0
- package/initramfs/buninu/apps/jsmdcui/src/display/ansi-style.js +60 -0
- package/initramfs/buninu/apps/jsmdcui/src/highlight/highlighter.js +243 -0
- package/initramfs/buninu/apps/jsmdcui/src/highlight/parser.js +225 -0
- package/initramfs/buninu/apps/jsmdcui/src/index.js +9586 -0
- package/initramfs/buninu/apps/jsmdcui/src/lua/engine.js +68 -0
- package/initramfs/buninu/apps/jsmdcui/src/platform/archive.js +50 -0
- package/initramfs/buninu/apps/jsmdcui/src/platform/clipboard.js +278 -0
- package/initramfs/buninu/apps/jsmdcui/src/platform/commands.js +221 -0
- package/initramfs/buninu/apps/jsmdcui/src/platform/terminal.js +5 -0
- package/initramfs/buninu/apps/jsmdcui/src/plugins/js-bridge.js +3377 -0
- package/initramfs/buninu/apps/jsmdcui/src/plugins/manager.js +674 -0
- package/initramfs/buninu/apps/jsmdcui/src/runtime/encodings.js +10 -0
- package/initramfs/buninu/apps/jsmdcui/src/runtime/registry.js +134 -0
- package/initramfs/buninu/apps/jsmdcui/src/screen/cell-buffer.js +81 -0
- package/initramfs/buninu/apps/jsmdcui/src/screen/events.js +263 -0
- package/initramfs/buninu/apps/jsmdcui/src/screen/screen.js +249 -0
- package/initramfs/buninu/apps/jsmdcui/src/screen/vt100.js +571 -0
- package/initramfs/buninu/apps/jsmdcui/src/shell/shell.js +70 -0
- package/initramfs/buninu/apps/jsmdcui/testapp.md +78 -0
- package/initramfs/buninu/apps/jsmdcui/tests/assets-helper.test.js +150 -0
- package/initramfs/buninu/apps/jsmdcui/tests/backup.test.js +158 -0
- package/initramfs/buninu/apps/jsmdcui/tests/cat-markdown.test.js +139 -0
- package/initramfs/buninu/apps/jsmdcui/tests/cdp-security.test.js +32 -0
- package/initramfs/buninu/apps/jsmdcui/tests/cdp-websocket-test.html +46 -0
- package/initramfs/buninu/apps/jsmdcui/tests/cmphex3b64.js +95 -0
- package/initramfs/buninu/apps/jsmdcui/tests/compiled-runtime.test.js +139 -0
- package/initramfs/buninu/apps/jsmdcui/tests/config-settings.test.js +52 -0
- package/initramfs/buninu/apps/jsmdcui/tests/demo.test.js +448 -0
- package/initramfs/buninu/apps/jsmdcui/tests/docs-sync.test.js +14 -0
- package/initramfs/buninu/apps/jsmdcui/tests/fence-events.test.js +1293 -0
- package/initramfs/buninu/apps/jsmdcui/tests/heading-ids.test.js +559 -0
- package/initramfs/buninu/apps/jsmdcui/tests/heading-list-selector.test.js +2393 -0
- package/initramfs/buninu/apps/jsmdcui/tests/id-collision.test.js +514 -0
- package/initramfs/buninu/apps/jsmdcui/tests/jmigt.js +16 -0
- package/initramfs/buninu/apps/jsmdcui/tests/js-plugin-prompts.test.js +110 -0
- package/initramfs/buninu/apps/jsmdcui/tests/key-event.md +10 -0
- package/initramfs/buninu/apps/jsmdcui/tests/kitty-demo.md +184 -0
- package/initramfs/buninu/apps/jsmdcui/tests/kitty-images.test.js +288 -0
- package/initramfs/buninu/apps/jsmdcui/tests/kitty-mode.test.js +14 -0
- package/initramfs/buninu/apps/jsmdcui/tests/platform-commands.test.js +18 -0
- package/initramfs/buninu/apps/jsmdcui/tests/platform-terminal.test.js +8 -0
- package/initramfs/buninu/apps/jsmdcui/tests/pty-demo.js +492 -0
- package/initramfs/buninu/apps/jsmdcui/tests/rerender-queue.test.js +130 -0
- package/initramfs/buninu/apps/jsmdcui/tests/table-render.test.js +150 -0
- package/initramfs/buninu/apps/jsmdcui/tests/table-row-edit.test.js +138 -0
- package/initramfs/buninu/apps/jsmdcui/tests/task-checkbox.test.js +33 -0
- package/initramfs/buninu/apps/jsmdcui/tests/template-components.test.js +422 -0
- package/initramfs/buninu/apps/jsmdcui/tests/text-block-resize.test.js +222 -0
- package/initramfs/buninu/apps/jsmdcui/tests/textarea.md +8 -0
- package/initramfs/buninu/apps/jsmdcui/tests/tui-links.test.js +298 -0
- package/initramfs/buninu/apps/jsmdcui/tests/wui-responsive-images.test.js +60 -0
- package/initramfs/buninu/apps/jsmdcui/tests/wui.test.js +108 -0
- package/initramfs/buninu/apps/jsmdcui/tests/wv-client.js +96 -0
- package/initramfs/buninu/apps/jsmdcui/tui +7 -0
- package/initramfs/buninu/apps/jsmdcui/wui +9 -0
- package/initramfs/buninu/apps/musl-la/LICENSES/GPL-3.0.txt +674 -0
- package/initramfs/buninu/apps/musl-la/LICENSES/gcc_runtime_exception.txt +73 -0
- package/initramfs/buninu/apps/musl-la/LICENSE_musl.txt +193 -0
- package/initramfs/buninu/apps/musl-la/NOTICE +41 -0
- package/initramfs/buninu/apps/musl-la/ld-musl-aarch64.so.1 +0 -0
- package/initramfs/buninu/apps/musl-la/libgcc_s.so.1 +0 -0
- package/initramfs/buninu/apps/musl-la/libstdc++.so.6 +0 -0
- package/initramfs/buninu/apps/musl-la/musl-la.sh +36 -0
- package/initramfs/buninu/apps/native-bridge/native-bridge.js +199 -0
- package/initramfs/buninu/apps/native-bridge/package.json +8 -0
- package/initramfs/buninu/apps/tts/tts.js +214 -0
- package/initramfs/buninu/apps/xclip/xclip.js +121 -0
- package/initramfs/buninu/apps/xdg-open/xdg-open.js +151 -0
- package/initramfs/buninu/bin/bun.bat +37 -0
- package/initramfs/buninu/bin/bun.sh +57 -0
- package/initramfs/buninu/bin/buninu-help.bat +4 -0
- package/initramfs/buninu/bin/buninu-help.sh +7 -0
- package/initramfs/buninu/bin/bunmsh.bat +3 -0
- package/initramfs/buninu/bin/bunmsh.sh +5 -0
- package/initramfs/buninu/bin/bunx.bat +3 -0
- package/initramfs/buninu/bin/glow.bat +3 -0
- package/initramfs/buninu/bin/glow.sh +5 -0
- package/initramfs/buninu/bin/init.js +779 -0
- package/initramfs/buninu/bin/install.js +570 -0
- package/initramfs/buninu/bin/jmi.bat +3 -0
- package/initramfs/buninu/bin/jmi.sh +5 -0
- package/initramfs/buninu/bin/jsgotty.bat +3 -0
- package/initramfs/buninu/bin/jsgotty.sh +5 -0
- package/initramfs/buninu/bin/jsmdcui.bat +11 -0
- package/initramfs/buninu/bin/libsh-loader.c +135 -0
- package/initramfs/buninu/bin/libsh-loader.so +0 -0
- package/initramfs/buninu/bin/multicall.sh +103 -0
- package/initramfs/buninu/bin/native-bridge.bat +3 -0
- package/initramfs/buninu/bin/rz.bat +3 -0
- package/initramfs/buninu/bin/rz.sh +5 -0
- package/initramfs/buninu/bin/shc.js +5 -0
- package/initramfs/buninu/bin/showimg.bat +3 -0
- package/initramfs/buninu/bin/showimg.sh +5 -0
- package/initramfs/buninu/bin/switch_to_android.sh +9 -0
- package/initramfs/buninu/bin/switch_to_linux.sh +7 -0
- package/initramfs/buninu/bin/sz.bat +3 -0
- package/initramfs/buninu/bin/sz.sh +5 -0
- package/initramfs/buninu/bin/tts.bat +3 -0
- package/initramfs/buninu/bin/xclip.bat +3 -0
- package/initramfs/buninu/bin/xdg-open.bat +3 -0
- package/initramfs/buninu/demo-server.js +64 -0
- package/initramfs/buninu/icon.png +0 -0
- package/initramfs/buninu/package.json +59 -0
- package/initramfs/etc/resolv.conf +1 -0
- package/initramfs/init.js +339 -0
- package/initramfs/lib/ld-musl-x86_64.so.1 +0 -0
- package/initramfs/lib/libc.musl-x86_64.so.1 +0 -0
- package/initramfs/lib/libgcc_s.so.1 +0 -0
- package/initramfs/lib/libstdc++.so.6 +0 -0
- package/initramfs/usr/bin/env +7 -0
- package/package.json +42 -0
- package/run-qemu.sh +44 -0
- package/scripts/fetch.sh +26 -0
- package/scripts/pack-initramfs.sh +41 -0
- package/test/build-init-bootstrap.sh +23 -0
- package/test/init-bootstrap.c +55 -0
|
@@ -0,0 +1,3377 @@
|
|
|
1
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
2
|
+
import { readdir } from "node:fs/promises";
|
|
3
|
+
import { dirname, join, basename, extname } from "node:path";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { assetPath, hasInternalAssets, listInternalAssetDirs, listInternalAssetPaths, readInternalAssetBytes } from "../../single-exe/assetsHelper.js";
|
|
7
|
+
import { isMdcuiEncoding } from "../runtime/encodings.js";
|
|
8
|
+
import { newMessage, newMessageAtLine, MTError, MTWarning, MTInfo } from "../buffer/message.js";
|
|
9
|
+
import { Loc } from "../buffer/loc.js";
|
|
10
|
+
import { renderMarkdownWithHeadingIds } from "../cui/heading-ids.mjs";
|
|
11
|
+
import { isMdcuiId, parseMdcuiIdentity, parseMdcuiIdSelector } from "../cui/identity.mjs";
|
|
12
|
+
import { updateAnsiTaskCheckbox } from "../cui/task-checkbox.mjs";
|
|
13
|
+
import {
|
|
14
|
+
colorAnsiPlainRange,
|
|
15
|
+
replaceAnsiPlainRange,
|
|
16
|
+
replaceAnsiPlainRangePreservingControls,
|
|
17
|
+
} from "../cui/table-row-edit.mjs";
|
|
18
|
+
import { renderTuiComponentMarkdown } from "../cui/template-components.mjs";
|
|
19
|
+
import { markTuiTableStripeStyles } from "../cui/table-render.mjs";
|
|
20
|
+
import { refreshTuiLinkIndex } from "../cui/tui-links.mjs";
|
|
21
|
+
|
|
22
|
+
// ── Action registry ──────────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
const ACTIONS = new Map();
|
|
25
|
+
const INTERNAL_JSPLUGIN_STAGE_ROOT = join(tmpdir(), "bunmicro-jsplugins");
|
|
26
|
+
let internalJsPluginStagePromise = null;
|
|
27
|
+
|
|
28
|
+
function reg(name, fn) { ACTIONS.set(name, fn); }
|
|
29
|
+
|
|
30
|
+
function stageInternalJsPlugins() {
|
|
31
|
+
if (!internalJsPluginStagePromise) {
|
|
32
|
+
internalJsPluginStagePromise = _stageInternalJsPlugins().catch((error) => {
|
|
33
|
+
console.error("# failed to stage internal JS plugins");
|
|
34
|
+
console.error(error);
|
|
35
|
+
return null;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return internalJsPluginStagePromise;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function _stageInternalJsPlugins() {
|
|
42
|
+
const prefix = assetPath("runtime", "jsplugins");
|
|
43
|
+
const paths = listInternalAssetPaths(prefix);
|
|
44
|
+
if (paths.length === 0) return null;
|
|
45
|
+
|
|
46
|
+
mkdirSync(INTERNAL_JSPLUGIN_STAGE_ROOT, { recursive: true });
|
|
47
|
+
await Bun.write(join(INTERNAL_JSPLUGIN_STAGE_ROOT, "package.json"), JSON.stringify({ type: "module" }));
|
|
48
|
+
|
|
49
|
+
for (const assetPathName of paths) {
|
|
50
|
+
const bytes = readInternalAssetBytes(assetPathName);
|
|
51
|
+
if (!bytes) continue;
|
|
52
|
+
const stagedPath = join(INTERNAL_JSPLUGIN_STAGE_ROOT, ...assetPathName.split("/"));
|
|
53
|
+
mkdirSync(dirname(stagedPath), { recursive: true });
|
|
54
|
+
await Bun.write(stagedPath, bytes);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return INTERNAL_JSPLUGIN_STAGE_ROOT;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function _actIndentStr(buf) {
|
|
61
|
+
if (buf?.Settings?.tabstospaces) return " ".repeat(buf?.Settings?.tabsize ?? 4);
|
|
62
|
+
return "\t";
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function _actExtendSel(app, moveFn) {
|
|
66
|
+
const pane = app.pane;
|
|
67
|
+
const buf = app.buffer;
|
|
68
|
+
if (!pane || !buf) return;
|
|
69
|
+
const anchor = pane.selection?.start ?? { ...buf.cursor };
|
|
70
|
+
moveFn(buf);
|
|
71
|
+
const end = { ...buf.cursor };
|
|
72
|
+
const same = anchor?.x === end?.x && anchor?.y === end?.y;
|
|
73
|
+
pane.selection = same ? null : { start: anchor, end };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function _actSelBounds(sel) {
|
|
77
|
+
const a = sel.start, b = sel.end;
|
|
78
|
+
const first = (a.y < b.y || (a.y === b.y && a.x <= b.x)) ? a : b;
|
|
79
|
+
const last = first === a ? b : a;
|
|
80
|
+
return { first, last };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function registerBuiltinActions() {
|
|
84
|
+
// Cursor movement
|
|
85
|
+
reg("CursorUp", (app) => { app.pane && (app.pane.selection = null); app.buffer?._moveUpVisual?.() ?? app.buffer?.moveUp(); });
|
|
86
|
+
reg("CursorDown", (app) => { app.pane && (app.pane.selection = null); app.buffer?._moveDownVisual?.() ?? app.buffer?.moveDown(); });
|
|
87
|
+
reg("CursorLeft", (app) => { app.pane && (app.pane.selection = null); app.buffer?.moveLeft(); });
|
|
88
|
+
reg("CursorRight", (app) => { app.pane && (app.pane.selection = null); app.buffer?.moveRight(); });
|
|
89
|
+
reg("WordRight", (app) => { app.pane && (app.pane.selection = null); app.buffer?.moveWordRight(); });
|
|
90
|
+
reg("WordLeft", (app) => { app.pane && (app.pane.selection = null); app.buffer?.moveWordLeft(); });
|
|
91
|
+
reg("CursorWordRight", (app) => { app.pane && (app.pane.selection = null); app.buffer?.moveWordRight(); });
|
|
92
|
+
reg("CursorWordLeft", (app) => { app.pane && (app.pane.selection = null); app.buffer?.moveWordLeft(); });
|
|
93
|
+
reg("StartOfLine", (app) => { app.pane && (app.pane.selection = null); app.buffer?.moveHome(); });
|
|
94
|
+
reg("StartOfText", (app) => { app.pane && (app.pane.selection = null); app.buffer?.moveStartOfText(); });
|
|
95
|
+
reg("StartOfTextToggle", (app) => { app.pane && (app.pane.selection = null); app.buffer?.moveStartOfTextToggle(); });
|
|
96
|
+
reg("EndOfLine", (app) => { app.pane && (app.pane.selection = null); app.buffer?.moveEnd(); });
|
|
97
|
+
reg("CursorStart", (app) => { app.pane && (app.pane.selection = null); app.buffer?.moveStartOfBuffer(); app.scrollCursorToBoundary?.(app.pane, "start"); });
|
|
98
|
+
reg("CursorEnd", (app) => { app.pane && (app.pane.selection = null); app.buffer?.moveEndOfBuffer(); app.scrollCursorToBoundary?.(app.pane, "end"); });
|
|
99
|
+
reg("ParagraphPrevious", (app) => { app.pane && (app.pane.selection = null); app.buffer?.paragraphPrevious(); });
|
|
100
|
+
reg("ParagraphNext", (app) => { app.pane && (app.pane.selection = null); app.buffer?.paragraphNext(); });
|
|
101
|
+
reg("PageUp", (app) => { app.pane && (app.pane.selection = null); app.pageScroll?.(app.pane, -1); });
|
|
102
|
+
reg("PageDown", (app) => { app.pane && (app.pane.selection = null); app.pageScroll?.(app.pane, 1); });
|
|
103
|
+
|
|
104
|
+
// Selection — extend
|
|
105
|
+
reg("SelectUp", (app) => _actExtendSel(app, (buf) => buf._moveUpVisual?.() ?? buf.moveUp?.()));
|
|
106
|
+
reg("SelectDown", (app) => _actExtendSel(app, (buf) => buf._moveDownVisual?.() ?? buf.moveDown?.()));
|
|
107
|
+
reg("SelectLeft", (app) => _actExtendSel(app, (buf) => buf.moveLeft?.()));
|
|
108
|
+
reg("SelectRight", (app) => _actExtendSel(app, (buf) => buf.moveRight?.()));
|
|
109
|
+
reg("SelectWordRight", (app) => _actExtendSel(app, (buf) => buf.moveWordRight?.()));
|
|
110
|
+
reg("SelectWordLeft", (app) => _actExtendSel(app, (buf) => buf.moveWordLeft?.()));
|
|
111
|
+
reg("SelectToStartOfText", (app) => _actExtendSel(app, (buf) => buf.moveStartOfText?.()));
|
|
112
|
+
reg("SelectToStartOfTextToggle", (app) => _actExtendSel(app, (buf) => buf.moveStartOfTextToggle?.()));
|
|
113
|
+
reg("SelectToStartOfLine", (app) => _actExtendSel(app, (buf) => buf.moveHome?.()));
|
|
114
|
+
reg("SelectToEndOfLine", (app) => _actExtendSel(app, (buf) => buf.moveEnd?.()));
|
|
115
|
+
reg("SelectToStart", (app) => _actExtendSel(app, (buf) => buf.moveStartOfBuffer?.()));
|
|
116
|
+
reg("SelectToEnd", (app) => _actExtendSel(app, (buf) => buf.moveEndOfBuffer?.()));
|
|
117
|
+
reg("SelectPageUp", (app) => app.cursorPage?.(app.pane, -1, { select: true }));
|
|
118
|
+
reg("SelectPageDown", (app) => app.cursorPage?.(app.pane, 1, { select: true }));
|
|
119
|
+
reg("SelectToParagraphPrevious", (app) => _actExtendSel(app, (buf) => buf.paragraphPrevious?.()));
|
|
120
|
+
reg("SelectToParagraphNext", (app) => _actExtendSel(app, (buf) => buf.paragraphNext?.()));
|
|
121
|
+
|
|
122
|
+
// Selection — whole-range
|
|
123
|
+
reg("SelectAll", (app) => {
|
|
124
|
+
const buf = app.buffer;
|
|
125
|
+
const pane = app.pane;
|
|
126
|
+
if (!buf || !pane) return;
|
|
127
|
+
const end = { x: buf.lines.at(-1)?.length ?? 0, y: buf.lines.length - 1 };
|
|
128
|
+
pane.selection = { start: { x: 0, y: 0 }, end };
|
|
129
|
+
buf.cursor = { ...end };
|
|
130
|
+
});
|
|
131
|
+
reg("SelectLine", (app) => {
|
|
132
|
+
const buf = app.buffer;
|
|
133
|
+
const pane = app.pane;
|
|
134
|
+
if (!buf || !pane) return;
|
|
135
|
+
const y = buf.cursor.y;
|
|
136
|
+
pane.selection = { start: { x: 0, y }, end: { x: buf.lines[y]?.length ?? 0, y } };
|
|
137
|
+
buf.cursor = { ...pane.selection.end };
|
|
138
|
+
});
|
|
139
|
+
reg("Deselect", (app) => { if (app.pane) app.pane.selection = null; });
|
|
140
|
+
|
|
141
|
+
// Indent/Outdent with selection support
|
|
142
|
+
reg("IndentSelection", (app) => {
|
|
143
|
+
const buf = app.buffer;
|
|
144
|
+
const pane = app.pane;
|
|
145
|
+
if (!buf) return;
|
|
146
|
+
if (!pane?.selection) { buf.insertTab?.(); return; }
|
|
147
|
+
buf.pushUndo?.();
|
|
148
|
+
const indent = _actIndentStr(buf);
|
|
149
|
+
const { first, last } = _actSelBounds(pane.selection);
|
|
150
|
+
for (let y = first.y; y <= last.y; y++) {
|
|
151
|
+
if ((buf.lines[y] ?? "").length > 0) buf.lines[y] = indent + (buf.lines[y] ?? "");
|
|
152
|
+
}
|
|
153
|
+
buf.invalidateHighlightFrom?.(first.y, { force: first.y !== last.y });
|
|
154
|
+
pane.selection = {
|
|
155
|
+
start: { ...pane.selection.start, x: pane.selection.start.x > 0 ? pane.selection.start.x + indent.length : pane.selection.start.x },
|
|
156
|
+
end: { ...pane.selection.end, x: pane.selection.end.x + indent.length },
|
|
157
|
+
};
|
|
158
|
+
buf.cursor = { ...buf.cursor, x: buf.cursor.x + indent.length };
|
|
159
|
+
buf.ensureCursor?.();
|
|
160
|
+
buf.modified = true;
|
|
161
|
+
});
|
|
162
|
+
reg("OutdentSelection", (app) => {
|
|
163
|
+
const buf = app.buffer;
|
|
164
|
+
const pane = app.pane;
|
|
165
|
+
if (!buf) return;
|
|
166
|
+
if (!pane?.selection) {
|
|
167
|
+
// outdent current line
|
|
168
|
+
const indent = _actIndentStr(buf);
|
|
169
|
+
const line = buf.lines[buf.cursor.y] ?? "";
|
|
170
|
+
buf.pushUndo?.();
|
|
171
|
+
let n = 0;
|
|
172
|
+
if (line.startsWith(indent)) n = indent.length;
|
|
173
|
+
else if (line.startsWith("\t")) n = 1;
|
|
174
|
+
else { while (n < indent.length && line[n] === ' ') n++; }
|
|
175
|
+
if (n > 0) {
|
|
176
|
+
buf.lines[buf.cursor.y] = line.slice(n);
|
|
177
|
+
buf.cursor.x = Math.max(0, buf.cursor.x - n);
|
|
178
|
+
buf.invalidateHighlightFrom?.(buf.cursor.y);
|
|
179
|
+
buf.modified = true;
|
|
180
|
+
}
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
buf.pushUndo?.();
|
|
184
|
+
const indent = _actIndentStr(buf);
|
|
185
|
+
const { first, last } = _actSelBounds(pane.selection);
|
|
186
|
+
for (let y = first.y; y <= last.y; y++) {
|
|
187
|
+
const line = buf.lines[y] ?? "";
|
|
188
|
+
let n = 0;
|
|
189
|
+
if (line.startsWith(indent)) n = indent.length;
|
|
190
|
+
else if (line.startsWith("\t")) n = 1;
|
|
191
|
+
else { while (n < indent.length && line[n] === ' ') n++; }
|
|
192
|
+
if (n > 0) buf.lines[y] = line.slice(n);
|
|
193
|
+
}
|
|
194
|
+
buf.invalidateHighlightFrom?.(first.y, { force: first.y !== last.y });
|
|
195
|
+
pane.selection = {
|
|
196
|
+
start: { ...pane.selection.start, x: Math.max(0, pane.selection.start.x - indent.length) },
|
|
197
|
+
end: { ...pane.selection.end, x: Math.max(0, pane.selection.end.x - indent.length) },
|
|
198
|
+
};
|
|
199
|
+
buf.cursor = { ...buf.cursor, x: Math.max(0, buf.cursor.x - indent.length) };
|
|
200
|
+
buf.ensureCursor?.();
|
|
201
|
+
buf.modified = true;
|
|
202
|
+
});
|
|
203
|
+
reg("IndentLine", (app) => {
|
|
204
|
+
const buf = app.buffer;
|
|
205
|
+
if (!buf || app.pane?.selection) return;
|
|
206
|
+
buf.pushUndo?.();
|
|
207
|
+
const indent = _actIndentStr(buf);
|
|
208
|
+
buf.lines[buf.cursor.y] = indent + (buf.lines[buf.cursor.y] ?? "");
|
|
209
|
+
buf.cursor.x += indent.length;
|
|
210
|
+
buf.invalidateHighlightFrom?.(buf.cursor.y);
|
|
211
|
+
buf.modified = true;
|
|
212
|
+
});
|
|
213
|
+
reg("OutdentLine", (app) => {
|
|
214
|
+
const buf = app.buffer;
|
|
215
|
+
if (!buf || app.pane?.selection) return;
|
|
216
|
+
const indent = _actIndentStr(buf);
|
|
217
|
+
const line = buf.lines[buf.cursor.y] ?? "";
|
|
218
|
+
buf.pushUndo?.();
|
|
219
|
+
let n = 0;
|
|
220
|
+
if (line.startsWith(indent)) n = indent.length;
|
|
221
|
+
else if (line.startsWith("\t")) n = 1;
|
|
222
|
+
else { while (n < indent.length && line[n] === ' ') n++; }
|
|
223
|
+
if (n > 0) {
|
|
224
|
+
buf.lines[buf.cursor.y] = line.slice(n);
|
|
225
|
+
buf.cursor.x = Math.max(0, buf.cursor.x - n);
|
|
226
|
+
buf.invalidateHighlightFrom?.(buf.cursor.y);
|
|
227
|
+
buf.modified = true;
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
// Aliases for OutdentSelection / OutdentLine
|
|
231
|
+
reg("DedentSelection", (app) => ACTIONS.get("OutdentSelection")(app));
|
|
232
|
+
reg("UnindentSelection", (app) => ACTIONS.get("OutdentSelection")(app));
|
|
233
|
+
reg("DedentLine", (app) => ACTIONS.get("OutdentLine")(app));
|
|
234
|
+
reg("UnindentLine", (app) => ACTIONS.get("OutdentLine")(app));
|
|
235
|
+
|
|
236
|
+
// Editing
|
|
237
|
+
reg("Backspace", (app) => app.buffer?.backspace());
|
|
238
|
+
reg("Delete", (app) => app.buffer?.deleteForward());
|
|
239
|
+
reg("InsertNewline", (app) => app.buffer?.newline());
|
|
240
|
+
reg("InsertTab", (app) => app.buffer?.insertTab());
|
|
241
|
+
reg("Undo", (app) => app.buffer?.undo());
|
|
242
|
+
reg("Redo", (app) => app.buffer?.redo());
|
|
243
|
+
reg("DeleteWordLeft", (app) => { app.buffer?.pushUndo?.(); app.buffer?.moveWordLeft && (() => { const start = {...app.buffer.cursor}; app.buffer.moveWordLeft(); const end = {...app.buffer.cursor}; if (start.y !== end.y || start.x !== end.x) { app.buffer.lines[end.y] = (app.buffer.lines[end.y] ?? "").slice(0, end.x) + (app.buffer.lines[start.y] ?? "").slice(start.x); app.buffer.invalidateHighlightFrom?.(end.y); app.buffer.modified = true; } })(); });
|
|
244
|
+
reg("DeleteWordRight", (app) => { app.buffer?.pushUndo?.(); if (app.buffer?.moveWordRight) { const start = {...app.buffer.cursor}; app.buffer.moveWordRight(); const end = {...app.buffer.cursor}; if (start.y !== end.y || start.x !== end.x) { app.buffer.lines[start.y] = (app.buffer.lines[start.y] ?? "").slice(0, start.x) + (app.buffer.lines[end.y] ?? "").slice(end.x); app.buffer.cursor = {...start}; app.buffer.invalidateHighlightFrom?.(start.y); app.buffer.modified = true; } } });
|
|
245
|
+
|
|
246
|
+
// Line operations
|
|
247
|
+
reg("MoveLinesUp", (app) => {
|
|
248
|
+
const buf = app.buffer;
|
|
249
|
+
const pane = app.pane;
|
|
250
|
+
if (!buf) return;
|
|
251
|
+
buf.pushUndo?.();
|
|
252
|
+
if (pane?.selection) {
|
|
253
|
+
const { first, last } = _actSelBounds(pane.selection);
|
|
254
|
+
if (first.y === 0) return;
|
|
255
|
+
const moved = buf.lines.splice(first.y - 1, 1)[0];
|
|
256
|
+
buf.lines.splice(last.y, 0, moved);
|
|
257
|
+
pane.selection = {
|
|
258
|
+
start: { ...pane.selection.start, y: pane.selection.start.y - 1 },
|
|
259
|
+
end: { ...pane.selection.end, y: pane.selection.end.y - 1 },
|
|
260
|
+
};
|
|
261
|
+
buf.cursor = { ...buf.cursor, y: buf.cursor.y - 1 };
|
|
262
|
+
buf.invalidateHighlightFrom?.(first.y - 1, { force: true });
|
|
263
|
+
} else {
|
|
264
|
+
if (buf.cursor.y === 0) return;
|
|
265
|
+
const y = buf.cursor.y;
|
|
266
|
+
[buf.lines[y - 1], buf.lines[y]] = [buf.lines[y], buf.lines[y - 1]];
|
|
267
|
+
buf.cursor.y--;
|
|
268
|
+
buf.invalidateHighlightFrom?.(y - 1, { force: true });
|
|
269
|
+
}
|
|
270
|
+
buf.modified = true;
|
|
271
|
+
});
|
|
272
|
+
reg("MoveLinesDown", (app) => {
|
|
273
|
+
const buf = app.buffer;
|
|
274
|
+
const pane = app.pane;
|
|
275
|
+
if (!buf) return;
|
|
276
|
+
buf.pushUndo?.();
|
|
277
|
+
if (pane?.selection) {
|
|
278
|
+
const { first, last } = _actSelBounds(pane.selection);
|
|
279
|
+
if (last.y >= buf.lines.length - 1) return;
|
|
280
|
+
const moved = buf.lines.splice(last.y + 1, 1)[0];
|
|
281
|
+
buf.lines.splice(first.y, 0, moved);
|
|
282
|
+
pane.selection = {
|
|
283
|
+
start: { ...pane.selection.start, y: pane.selection.start.y + 1 },
|
|
284
|
+
end: { ...pane.selection.end, y: pane.selection.end.y + 1 },
|
|
285
|
+
};
|
|
286
|
+
buf.cursor = { ...buf.cursor, y: buf.cursor.y + 1 };
|
|
287
|
+
buf.invalidateHighlightFrom?.(first.y, { force: true });
|
|
288
|
+
} else {
|
|
289
|
+
if (buf.cursor.y >= buf.lines.length - 1) return;
|
|
290
|
+
const y = buf.cursor.y;
|
|
291
|
+
[buf.lines[y], buf.lines[y + 1]] = [buf.lines[y + 1], buf.lines[y]];
|
|
292
|
+
buf.cursor.y++;
|
|
293
|
+
buf.invalidateHighlightFrom?.(y, { force: true });
|
|
294
|
+
}
|
|
295
|
+
buf.modified = true;
|
|
296
|
+
});
|
|
297
|
+
reg("DuplicateLine", (app) => {
|
|
298
|
+
const buf = app.buffer;
|
|
299
|
+
if (!buf) return;
|
|
300
|
+
const line = buf.lines[buf.cursor.y];
|
|
301
|
+
buf.lines.splice(buf.cursor.y + 1, 0, line);
|
|
302
|
+
buf.cursor.y++;
|
|
303
|
+
buf.invalidateHighlightFrom?.(buf.cursor.y, { force: true });
|
|
304
|
+
buf.modified = true;
|
|
305
|
+
});
|
|
306
|
+
reg("DeleteLine", (app) => app.buffer?.cutLine());
|
|
307
|
+
|
|
308
|
+
// Clipboard — delegate to handleCommand so clipboard manager is used
|
|
309
|
+
reg("Copy", (app) => app.handleCommand?.("copy"));
|
|
310
|
+
reg("CopyLine",(app) => app.handleCommand?.("copy"));
|
|
311
|
+
reg("Cut", (app) => app.handleCommand?.("cut"));
|
|
312
|
+
reg("Paste", (app) => app.handleCommand?.("paste"));
|
|
313
|
+
reg("CutLine", (app) => app.handleCommand?.("cutline"));
|
|
314
|
+
|
|
315
|
+
// Comment
|
|
316
|
+
reg("ToggleComment", (app) => app.toggleComment?.());
|
|
317
|
+
|
|
318
|
+
// File / tab
|
|
319
|
+
reg("Save", async (app) => app.save?.());
|
|
320
|
+
reg("SaveAs", (app) => app.openCommandMode?.());
|
|
321
|
+
reg("Quit", async (app) => app.quit?.());
|
|
322
|
+
reg("AddTab", async (app) => app.addTab?.());
|
|
323
|
+
reg("NextTab", (app) => app.nextTab?.());
|
|
324
|
+
reg("PrevTab", (app) => app.previousTab?.());
|
|
325
|
+
reg("PreviousTab", (app) => app.previousTab?.());
|
|
326
|
+
|
|
327
|
+
// View / search
|
|
328
|
+
reg("Find", (app) => app.handleCommand?.("find"));
|
|
329
|
+
reg("CommandMode", (app) => app.openCommandMode?.());
|
|
330
|
+
reg("ShellMode", (app) => app.openShellMode?.());
|
|
331
|
+
reg("ToggleHelp", (app) => app.toggleHelp?.());
|
|
332
|
+
reg("ToggleRuler", (app) => {
|
|
333
|
+
const buf = app.buffer; if (!buf) return;
|
|
334
|
+
buf.Settings = buf.Settings ?? {};
|
|
335
|
+
buf.Settings.ruler = !(buf.Settings.ruler ?? true);
|
|
336
|
+
app.message = buf.Settings.ruler ? "Enabled ruler" : "Disabled ruler";
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
// Scroll without moving cursor
|
|
340
|
+
reg("ScrollUp", (app) => { if (app.buffer) app.buffer.scroll.y = Math.max(0, (app.buffer.scroll.y ?? 0) - 3); });
|
|
341
|
+
reg("ScrollDown", (app) => { if (app.buffer) app.buffer.scroll.y = (app.buffer.scroll.y ?? 0) + 3; });
|
|
342
|
+
|
|
343
|
+
// Start / End — move cursor + scroll to buffer boundary
|
|
344
|
+
reg("Start", (app) => { app.pane && (app.pane.selection = null); app.buffer?._lastVisX != null && (app.buffer._lastVisX = null); app.buffer?.moveStartOfBuffer(); app.scrollCursorToBoundary?.(app.pane, "start"); });
|
|
345
|
+
reg("End", (app) => { app.pane && (app.pane.selection = null); app.buffer?._lastVisX != null && (app.buffer._lastVisX = null); app.buffer?.moveEndOfBuffer(); app.scrollCursorToBoundary?.(app.pane, "end"); });
|
|
346
|
+
|
|
347
|
+
// Page aliases
|
|
348
|
+
reg("CursorPageUp", (app) => app.cursorPage?.(app.pane, -1));
|
|
349
|
+
reg("CursorPageDown", (app) => app.cursorPage?.(app.pane, 1));
|
|
350
|
+
reg("HalfPageUp", (app) => app.cursorPage?.(app.pane, -1, { amount: Math.max(1, Math.floor((app.pane?.h ?? 24) / 2)) }));
|
|
351
|
+
reg("HalfPageDown", (app) => app.cursorPage?.(app.pane, 1, { amount: Math.max(1, Math.floor((app.pane?.h ?? 24) / 2)) }));
|
|
352
|
+
|
|
353
|
+
// Cursor-to-view-boundary
|
|
354
|
+
reg("CursorToViewTop", (app) => {
|
|
355
|
+
const buf = app.buffer; if (!buf) return;
|
|
356
|
+
app.pane && (app.pane.selection = null);
|
|
357
|
+
buf.cursor.y = Math.min(buf.lines.length - 1, Math.max(0, buf.scroll.y ?? 0));
|
|
358
|
+
buf.ensureCursor?.();
|
|
359
|
+
});
|
|
360
|
+
reg("CursorToViewCenter", (app) => {
|
|
361
|
+
const buf = app.buffer; if (!buf) return;
|
|
362
|
+
app.pane && (app.pane.selection = null);
|
|
363
|
+
buf.cursor.y = Math.min(buf.lines.length - 1, Math.max(0, (buf.scroll.y ?? 0) + Math.floor((app.pane?.h ?? 24) / 2)));
|
|
364
|
+
buf.ensureCursor?.();
|
|
365
|
+
});
|
|
366
|
+
reg("CursorToViewBottom", (app) => {
|
|
367
|
+
const buf = app.buffer; if (!buf) return;
|
|
368
|
+
app.pane && (app.pane.selection = null);
|
|
369
|
+
buf.cursor.y = Math.min(buf.lines.length - 1, Math.max(0, (buf.scroll.y ?? 0) + (app.pane?.h ?? 24) - 1));
|
|
370
|
+
buf.ensureCursor?.();
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
// Center — scroll so cursor is vertically centered
|
|
374
|
+
reg("Center", (app) => {
|
|
375
|
+
const buf = app.buffer; if (!buf) return;
|
|
376
|
+
buf.scroll.y = Math.max(0, buf.cursor.y - Math.floor((app.pane?.h ?? 24) / 2));
|
|
377
|
+
buf.scroll.row = 0;
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
// Search
|
|
381
|
+
reg("FindNext", (app) => { app.buffer?.searchNext?.(); });
|
|
382
|
+
reg("FindPrevious", (app) => { app.buffer?.searchPrev?.(); });
|
|
383
|
+
reg("FindLiteral", (app) => { app.buffer?.searchNext?.(); });
|
|
384
|
+
reg("ToggleHighlightSearch", (app) => {
|
|
385
|
+
const buf = app.buffer; if (!buf) return;
|
|
386
|
+
buf.Settings = buf.Settings ?? {};
|
|
387
|
+
buf.Settings.hlsearch = !(buf.Settings.hlsearch ?? false);
|
|
388
|
+
app.message = buf.Settings.hlsearch ? "Enabled search highlight" : "Disabled search highlight";
|
|
389
|
+
});
|
|
390
|
+
reg("UnhighlightSearch", (app) => { if (app.buffer) { app.buffer.searchPattern = ""; } });
|
|
391
|
+
reg("ResetSearch", (app) => { if (app.buffer) { app.buffer.searchPattern = ""; } });
|
|
392
|
+
|
|
393
|
+
// Diff navigation (requires app.diffNext/diffPrevious added to App class)
|
|
394
|
+
reg("DiffNext", (app) => app.diffNext?.());
|
|
395
|
+
reg("DiffPrevious", (app) => app.diffPrevious?.());
|
|
396
|
+
|
|
397
|
+
// Duplicate selection or line
|
|
398
|
+
reg("Duplicate", (app) => {
|
|
399
|
+
const buf = app.buffer; const pane = app.pane; if (!buf) return;
|
|
400
|
+
buf.pushUndo?.();
|
|
401
|
+
if (pane?.selection) {
|
|
402
|
+
const { first, last } = _actSelBounds(pane.selection);
|
|
403
|
+
const selLines = buf.lines;
|
|
404
|
+
const getText = () => {
|
|
405
|
+
if (first.y === last.y) return (selLines[first.y] ?? "").slice(first.x, last.x);
|
|
406
|
+
const parts = [(selLines[first.y] ?? "").slice(first.x)];
|
|
407
|
+
for (let i = first.y + 1; i < last.y; i++) parts.push(selLines[i] ?? "");
|
|
408
|
+
parts.push((selLines[last.y] ?? "").slice(0, last.x));
|
|
409
|
+
return parts.join("\n");
|
|
410
|
+
};
|
|
411
|
+
const selText = getText();
|
|
412
|
+
const parts = selText.split("\n");
|
|
413
|
+
const line = buf.lines[last.y] ?? "";
|
|
414
|
+
const right = line.slice(last.x);
|
|
415
|
+
if (parts.length === 1) {
|
|
416
|
+
buf.lines[last.y] = line.slice(0, last.x) + parts[0] + right;
|
|
417
|
+
buf.cursor = { y: last.y, x: last.x + parts[0].length };
|
|
418
|
+
buf.invalidateHighlightFrom?.(last.y);
|
|
419
|
+
} else {
|
|
420
|
+
buf.lines[last.y] = line.slice(0, last.x) + parts[0];
|
|
421
|
+
buf.lines.splice(last.y + 1, 0, ...parts.slice(1, -1), parts.at(-1) + right);
|
|
422
|
+
buf.cursor = { y: last.y + parts.length - 1, x: parts.at(-1).length };
|
|
423
|
+
buf.invalidateHighlightFrom?.(last.y, { force: true });
|
|
424
|
+
}
|
|
425
|
+
pane.selection = null;
|
|
426
|
+
buf.modified = true;
|
|
427
|
+
} else {
|
|
428
|
+
const lineText = buf.lines[buf.cursor.y] ?? "";
|
|
429
|
+
buf.lines.splice(buf.cursor.y + 1, 0, lineText);
|
|
430
|
+
buf.invalidateHighlightFrom?.(buf.cursor.y, { force: true });
|
|
431
|
+
buf.cursor = { y: buf.cursor.y + 1, x: lineText.length };
|
|
432
|
+
buf.modified = true;
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
// Retab — re-indent all lines to match tabstospaces/tabsize setting
|
|
437
|
+
reg("Retab", (app) => {
|
|
438
|
+
const buf = app.buffer; if (!buf) return;
|
|
439
|
+
const tabsize = Math.max(1, buf.Settings?.tabsize ?? 4);
|
|
440
|
+
const toSpaces = buf.Settings?.tabstospaces ?? false;
|
|
441
|
+
buf.pushUndo?.();
|
|
442
|
+
for (let y = 0; y < buf.lines.length; y++) {
|
|
443
|
+
const line = buf.lines[y];
|
|
444
|
+
let i = 0; let col = 0;
|
|
445
|
+
while (i < line.length && (line[i] === " " || line[i] === "\t")) {
|
|
446
|
+
if (line[i] === "\t") col = Math.floor(col / tabsize) * tabsize + tabsize;
|
|
447
|
+
else col++;
|
|
448
|
+
i++;
|
|
449
|
+
}
|
|
450
|
+
if (i === 0) continue;
|
|
451
|
+
const newIndent = toSpaces ? " ".repeat(col) : "\t".repeat(Math.floor(col / tabsize)) + " ".repeat(col % tabsize);
|
|
452
|
+
if (newIndent !== line.slice(0, i)) {
|
|
453
|
+
buf.lines[y] = newIndent + line.slice(i);
|
|
454
|
+
buf.invalidateHighlightFrom?.(y);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
buf.modified = true;
|
|
458
|
+
buf.ensureCursor?.();
|
|
459
|
+
app.message = `Retabbed (${toSpaces ? "spaces" : "tabs"}, size ${tabsize})`;
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
// Autocomplete
|
|
463
|
+
reg("Autocomplete", (app) => { if (app.buffer?.acHas) app.buffer.cycleAutocomplete?.(true); else app.buffer?.startBufferComplete?.(); });
|
|
464
|
+
reg("CycleAutocompleteBack", (app) => { app.buffer?.cycleAutocomplete?.(false); });
|
|
465
|
+
|
|
466
|
+
// Tab navigation
|
|
467
|
+
reg("FirstTab", (app) => app.setActiveTab?.(0));
|
|
468
|
+
reg("LastTab", (app) => app.setActiveTab?.((app.tabs?.length ?? 1) - 1));
|
|
469
|
+
|
|
470
|
+
// Split pane navigation
|
|
471
|
+
reg("NextSplit", (app) => { const panes = app.tab?.panes(); if (panes?.length > 1) app.tab.activePane = panes[(panes.indexOf(app.tab.activePane) + 1) % panes.length]; });
|
|
472
|
+
reg("PreviousSplit", (app) => { const panes = app.tab?.panes(); if (panes?.length > 1) app.tab.activePane = panes[(panes.indexOf(app.tab.activePane) - 1 + panes.length) % panes.length]; });
|
|
473
|
+
reg("FirstSplit", (app) => { const panes = app.tab?.panes(); if (panes?.length) app.tab.activePane = panes[0]; });
|
|
474
|
+
reg("LastSplit", (app) => { const panes = app.tab?.panes(); if (panes?.length) app.tab.activePane = panes[panes.length - 1]; });
|
|
475
|
+
|
|
476
|
+
// Split actions (delegate to handleCommand for buffer opening)
|
|
477
|
+
reg("VSplitAction", async (app) => app.handleCommand?.("vsplit"));
|
|
478
|
+
reg("HSplitAction", async (app) => app.handleCommand?.("hsplit"));
|
|
479
|
+
reg("Unsplit", async (app) => {
|
|
480
|
+
if ((app.tab?.panes().length ?? 0) > 1)
|
|
481
|
+
await app.closePane?.(app.pane);
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
// File operations
|
|
485
|
+
reg("OpenFile", (app) => app.openCommandMode?.("open "));
|
|
486
|
+
reg("SaveAll", async (app) => {
|
|
487
|
+
let saved = 0;
|
|
488
|
+
for (const tab of (app.tabs ?? [])) {
|
|
489
|
+
for (const pane of (tab.panes?.() ?? [])) {
|
|
490
|
+
if (pane?.buffer?.modified) {
|
|
491
|
+
try { await pane.buffer.save?.(); saved++; } catch {}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
app.message = saved > 0 ? `Saved ${saved} file${saved === 1 ? "" : "s"}` : "Nothing to save";
|
|
496
|
+
});
|
|
497
|
+
reg("JumpLine", (app) => app.openCommandMode?.("goto "));
|
|
498
|
+
reg("JumpToMatchingBrace", (app) => app.jumpToMatchingBrace?.());
|
|
499
|
+
|
|
500
|
+
// Quit actions
|
|
501
|
+
reg("ForceQuit", async (app) => app.stop?.(0));
|
|
502
|
+
reg("QuitAll", async (app) => {
|
|
503
|
+
for (const tab of (app.tabs ?? []))
|
|
504
|
+
for (const pane of (tab.panes?.() ?? []))
|
|
505
|
+
if (pane?.buffer?.modified) try { await pane.buffer.save?.(); } catch {}
|
|
506
|
+
await app.stop?.(0);
|
|
507
|
+
});
|
|
508
|
+
reg("Escape", (app) => app._dispatchInput?.(new TextEncoder().encode("\x1b")));
|
|
509
|
+
|
|
510
|
+
// Toggle settings
|
|
511
|
+
reg("ToggleDiffGutter", (app) => {
|
|
512
|
+
const buf = app.buffer; if (!buf) return;
|
|
513
|
+
buf.Settings = buf.Settings ?? {};
|
|
514
|
+
buf.Settings.diffgutter = !(buf.Settings.diffgutter ?? false);
|
|
515
|
+
app.message = buf.Settings.diffgutter ? "Enabled diff gutter" : "Disabled diff gutter";
|
|
516
|
+
});
|
|
517
|
+
reg("ToggleKeyMenu", (app) => { app.keymenu = !(app.keymenu ?? false); });
|
|
518
|
+
reg("ToggleOverwriteMode", (app) => {
|
|
519
|
+
const buf = app.buffer; if (!buf) return;
|
|
520
|
+
buf._overwrite = !buf._overwrite;
|
|
521
|
+
app.message = buf._overwrite ? "Overwrite mode on" : "Overwrite mode off";
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
// Paste from primary selection (X11/Wayland middle-click clipboard)
|
|
525
|
+
reg("PastePrimary", (app) => {
|
|
526
|
+
const pasted = app.clipboard?.read?.("primary");
|
|
527
|
+
if (!pasted) return;
|
|
528
|
+
const buf = app.buffer; if (!buf) return;
|
|
529
|
+
buf.pushUndo?.();
|
|
530
|
+
if (app.pane?.selection) _deleteSel(buf, app.pane);
|
|
531
|
+
buf.insert?.(pasted);
|
|
532
|
+
app.message = "Pasted from primary selection";
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
// Status/info
|
|
536
|
+
reg("ClearInfo", (app) => { app.message = ""; if (app.buffer) app.buffer.message = ""; });
|
|
537
|
+
reg("ClearStatus", (app) => { app.message = ""; if (app.buffer) app.buffer.message = ""; });
|
|
538
|
+
reg("None", () => {});
|
|
539
|
+
|
|
540
|
+
// SubWord — stub: treated as word movement (no sub-word segmentation implemented)
|
|
541
|
+
reg("SubWordLeft", (app) => { app.pane && (app.pane.selection = null); app.buffer?.moveWordLeft?.(); });
|
|
542
|
+
reg("SubWordRight", (app) => { app.pane && (app.pane.selection = null); app.buffer?.moveWordRight?.(); });
|
|
543
|
+
reg("SelectSubWordLeft", (app) => _actExtendSel(app, (buf) => buf.moveWordLeft?.()));
|
|
544
|
+
reg("SelectSubWordRight", (app) => _actExtendSel(app, (buf) => buf.moveWordRight?.()));
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// Register all built-in actions at module load time
|
|
548
|
+
registerBuiltinActions();
|
|
549
|
+
|
|
550
|
+
// ── Public action API ────────────────────────────────────────────────────────
|
|
551
|
+
|
|
552
|
+
const EDIT_LOCKED_ACTIONS = new Set([
|
|
553
|
+
"IndentSelection", "OutdentSelection", "IndentLine", "OutdentLine",
|
|
554
|
+
"DedentSelection", "UnindentSelection", "DedentLine", "UnindentLine",
|
|
555
|
+
"Backspace", "Delete", "InsertNewline", "InsertTab", "Undo", "Redo",
|
|
556
|
+
"DeleteWordLeft", "DeleteWordRight",
|
|
557
|
+
"MoveLinesUp", "MoveLinesDown", "DuplicateLine", "DeleteLine",
|
|
558
|
+
"Cut", "Paste", "CutLine", "ToggleComment", "Duplicate", "Retab", "PastePrimary",
|
|
559
|
+
]);
|
|
560
|
+
|
|
561
|
+
export async function runAction(name, app) {
|
|
562
|
+
const fn = ACTIONS.get(name);
|
|
563
|
+
if (!fn) return false;
|
|
564
|
+
if (app?.buffer?.isEditLocked?.() && EDIT_LOCKED_ACTIONS.has(name)) return true;
|
|
565
|
+
await fn(app);
|
|
566
|
+
return true;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
export function listActions() {
|
|
570
|
+
return [...ACTIONS.keys()].sort();
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// ── JsPluginManager ──────────────────────────────────────────────────────────
|
|
574
|
+
|
|
575
|
+
export class JsPluginManager {
|
|
576
|
+
constructor() {
|
|
577
|
+
this._hooks = new Map(); // hookName → fn[]
|
|
578
|
+
this.commands = new Map();
|
|
579
|
+
this._loaded = []; // { path, name, error? }
|
|
580
|
+
this._app = null;
|
|
581
|
+
this._ctx = null;
|
|
582
|
+
// registerBuiltinActions() already called at module load time
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
setApp(app) { this._app = app; }
|
|
586
|
+
setContext(ctx) { this._ctx = ctx; }
|
|
587
|
+
|
|
588
|
+
// Register a hook handler from a JS plugin
|
|
589
|
+
on(hookName, fn) {
|
|
590
|
+
if (!this._hooks.has(hookName)) this._hooks.set(hookName, []);
|
|
591
|
+
this._hooks.get(hookName).push(fn);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// Dispatch a hook to all JS handlers (fire-and-forget style like Lua run)
|
|
595
|
+
async run(hookName, ...args) {
|
|
596
|
+
for (const fn of (this._hooks.get(hookName) ?? [])) {
|
|
597
|
+
try { await fn(...args); } catch (e) { console.error(`[jsplugin] ${hookName}:`, e.message); }
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
async runBool(hookName, ...args) {
|
|
602
|
+
let ok = true;
|
|
603
|
+
for (const fn of (this._hooks.get(hookName) ?? [])) {
|
|
604
|
+
try {
|
|
605
|
+
if (await fn(...args) === false) ok = false;
|
|
606
|
+
} catch (e) { console.error(`[jsplugin] ${hookName}:`, e.message); }
|
|
607
|
+
}
|
|
608
|
+
return ok;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// Scan and load all JS plugins from given directories
|
|
612
|
+
async loadFrom(dirs) {
|
|
613
|
+
for (const { dir, builtin } of dirs) {
|
|
614
|
+
if (builtin && hasInternalAssets()) {
|
|
615
|
+
const loadedFromAssets = await this._loadFromInternalAssets(dir, builtin);
|
|
616
|
+
if (loadedFromAssets) continue;
|
|
617
|
+
}
|
|
618
|
+
if (!existsSync(dir)) continue;
|
|
619
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
620
|
+
for (const entry of entries) {
|
|
621
|
+
if (!entry.isDirectory()) continue;
|
|
622
|
+
const plugDir = join(dir, entry.name);
|
|
623
|
+
const mainJs = join(plugDir, `${entry.name}.js`);
|
|
624
|
+
if (!existsSync(mainJs)) continue;
|
|
625
|
+
await this._loadFile(mainJs, entry.name, builtin);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
async _loadFromInternalAssets(dir, builtin) {
|
|
631
|
+
const prefix = assetPath("runtime", "jsplugins");
|
|
632
|
+
const stageRoot = await stageInternalJsPlugins();
|
|
633
|
+
if (!stageRoot) return false;
|
|
634
|
+
|
|
635
|
+
const pluginNames = listInternalAssetDirs(prefix);
|
|
636
|
+
if (pluginNames.length === 0) return false;
|
|
637
|
+
|
|
638
|
+
let loadedAny = false;
|
|
639
|
+
for (const pluginName of pluginNames) {
|
|
640
|
+
const stagedMainPath = join(stageRoot, prefix, pluginName, `${pluginName}.js`);
|
|
641
|
+
if (!existsSync(stagedMainPath)) continue;
|
|
642
|
+
try {
|
|
643
|
+
await import(pathToFileURL(stagedMainPath).href);
|
|
644
|
+
this._loaded.push({ path: assetPath(prefix, pluginName, `${pluginName}.js`), name: pluginName, builtin, loaded: true });
|
|
645
|
+
loadedAny = true;
|
|
646
|
+
} catch (e) {
|
|
647
|
+
this._loaded.push({ path: assetPath(prefix, pluginName, `${pluginName}.js`), name: pluginName, builtin, loaded: false, error: e.message });
|
|
648
|
+
console.error(`[jsplugin] failed to load ${pluginName}: ${e.message}`);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
return loadedAny;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
async _loadFile(path, name, builtin) {
|
|
655
|
+
try {
|
|
656
|
+
await import(path);
|
|
657
|
+
this._loaded.push({ path, name, builtin, loaded: true });
|
|
658
|
+
} catch (e) {
|
|
659
|
+
this._loaded.push({ path, name, builtin, loaded: false, error: e.message });
|
|
660
|
+
console.error(`[jsplugin] failed to load ${name}: ${e.message}`);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
list() { return this._loaded; }
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
// ── Selection helpers (used by micro.getSelection / micro.putSelection) ──────
|
|
668
|
+
|
|
669
|
+
function _selBounds(sel) {
|
|
670
|
+
const a = sel.start, b = sel.end;
|
|
671
|
+
const first = (a.y < b.y || (a.y === b.y && a.x <= b.x)) ? a : b;
|
|
672
|
+
const last = first === a ? b : a;
|
|
673
|
+
return { first, last };
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
function _selText(buf, sel) {
|
|
677
|
+
const { first, last } = _selBounds(sel);
|
|
678
|
+
if (first.y === last.y) return buf.lines[first.y]?.slice(first.x, last.x) ?? "";
|
|
679
|
+
const parts = [buf.lines[first.y]?.slice(first.x) ?? ""];
|
|
680
|
+
for (let i = first.y + 1; i < last.y; i++) parts.push(buf.lines[i] ?? "");
|
|
681
|
+
parts.push(buf.lines[last.y]?.slice(0, last.x) ?? "");
|
|
682
|
+
return parts.join("\n");
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
function _deleteSel(buf, pane) {
|
|
686
|
+
const sel = pane.selection;
|
|
687
|
+
if (!sel) return;
|
|
688
|
+
const { first, last } = _selBounds(sel);
|
|
689
|
+
if (first.y === last.y) {
|
|
690
|
+
buf.lines[first.y] = (buf.lines[first.y] ?? "").slice(0, first.x) + (buf.lines[first.y] ?? "").slice(last.x);
|
|
691
|
+
} else {
|
|
692
|
+
const a = (buf.lines[first.y] ?? "").slice(0, first.x);
|
|
693
|
+
const b = (buf.lines[last.y] ?? "").slice(last.x);
|
|
694
|
+
buf.lines.splice(first.y, last.y - first.y + 1, a + b);
|
|
695
|
+
}
|
|
696
|
+
buf.invalidateHighlightFrom?.(first.y, { force: first.y !== last.y });
|
|
697
|
+
buf.cursor = { x: first.x, y: first.y };
|
|
698
|
+
pane.selection = null;
|
|
699
|
+
buf.modified = true;
|
|
700
|
+
buf.ensureCursor?.();
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// ── mdcui block selector ────────────────────────────────────────────────────
|
|
704
|
+
|
|
705
|
+
function _parseBlockIdentity(input, { selector = false } = {}) {
|
|
706
|
+
return parseMdcuiIdentity(input, { selector });
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
function _blockHeader(line) {
|
|
710
|
+
const text = String(line ?? "");
|
|
711
|
+
const framed = text.match(/^(\s*)(┌─|╭─|\+-)\s*(\S+)\s*$/);
|
|
712
|
+
if (framed) {
|
|
713
|
+
const identity = _parseBlockIdentity(framed[3]);
|
|
714
|
+
return identity
|
|
715
|
+
? { kind: "framed", indent: framed[1], bodyMarker: framed[2] === "+-" ? "|" : "│", ...identity }
|
|
716
|
+
: null;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
const fenced = text.match(/^(\s*)(`{3,})\s*(\S+)\s*$/);
|
|
720
|
+
if (fenced) {
|
|
721
|
+
const identity = _parseBlockIdentity(fenced[3]);
|
|
722
|
+
return identity
|
|
723
|
+
? { kind: "fenced", indent: fenced[1], fenceLength: fenced[2].length, ...identity }
|
|
724
|
+
: null;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
return null;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
function _matchesBlock(header, selector) {
|
|
731
|
+
if (selector.tag && header.tag !== selector.tag) return false;
|
|
732
|
+
if (selector.id && header.id !== selector.id) return false;
|
|
733
|
+
return selector.classes.every((name) => header.classes.includes(name));
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
function _findBlock(lines, selector) {
|
|
737
|
+
for (let start = 0; start < lines.length; start++) {
|
|
738
|
+
const header = _blockHeader(lines[start]);
|
|
739
|
+
if (!header || !_matchesBlock(header, selector)) continue;
|
|
740
|
+
|
|
741
|
+
for (let y = start + 1; y < lines.length; y++) {
|
|
742
|
+
const line = String(lines[y] ?? "");
|
|
743
|
+
const rest = line.startsWith(header.indent)
|
|
744
|
+
? line.slice(header.indent.length)
|
|
745
|
+
: line;
|
|
746
|
+
|
|
747
|
+
if (header.kind === "fenced") {
|
|
748
|
+
const closing = rest.match(/^(`{3,})\s*$/);
|
|
749
|
+
if (closing && closing[1].length >= header.fenceLength)
|
|
750
|
+
return { start, end: y, header };
|
|
751
|
+
continue;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
if (/^(?:└─|╰─|\+-)\s*$/.test(rest))
|
|
755
|
+
return { start, end: y, header };
|
|
756
|
+
}
|
|
757
|
+
return { start, end: lines.length, header };
|
|
758
|
+
}
|
|
759
|
+
return null;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
export function findTuiBlockAtLine(lines, lineIndex) {
|
|
763
|
+
return findTuiBlockInIndex(buildTuiBlockIndex(lines), lineIndex);
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
export function buildTuiBlockIndex(lines, declarations = null) {
|
|
767
|
+
const blocks = [];
|
|
768
|
+
for (let start = 0; start < lines.length; start++) {
|
|
769
|
+
const header = _blockHeader(lines[start]);
|
|
770
|
+
if (!header) continue;
|
|
771
|
+
let end = lines.length;
|
|
772
|
+
for (let y = start + 1; y < lines.length; y++) {
|
|
773
|
+
const line = String(lines[y] ?? "");
|
|
774
|
+
const rest = line.startsWith(header.indent)
|
|
775
|
+
? line.slice(header.indent.length)
|
|
776
|
+
: line;
|
|
777
|
+
if (header.kind === "fenced") {
|
|
778
|
+
const closing = rest.match(/^(`{3,})\s*$/);
|
|
779
|
+
if (!closing || closing[1].length < header.fenceLength) continue;
|
|
780
|
+
} else if (!/^(?:└─|╰─|\+-)\s*$/.test(rest)) {
|
|
781
|
+
continue;
|
|
782
|
+
}
|
|
783
|
+
end = y;
|
|
784
|
+
break;
|
|
785
|
+
}
|
|
786
|
+
const declaration = header.id ? declarations?.get(header.id) : null;
|
|
787
|
+
if (
|
|
788
|
+
declarations == null
|
|
789
|
+
|| (
|
|
790
|
+
declaration?.tag === header.tag
|
|
791
|
+
&& declaration.events?.size > 0
|
|
792
|
+
)
|
|
793
|
+
) blocks.push({ start, end, header });
|
|
794
|
+
start = Math.max(start, end);
|
|
795
|
+
}
|
|
796
|
+
return blocks;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
export function findTuiBlockInIndex(blocks, lineIndex) {
|
|
800
|
+
let low = 0;
|
|
801
|
+
let high = blocks.length - 1;
|
|
802
|
+
while (low <= high) {
|
|
803
|
+
const middle = (low + high) >> 1;
|
|
804
|
+
const block = blocks[middle];
|
|
805
|
+
if (lineIndex <= block.start) high = middle - 1;
|
|
806
|
+
else if (lineIndex >= block.end) low = middle + 1;
|
|
807
|
+
else return block;
|
|
808
|
+
}
|
|
809
|
+
return null;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
function _blockValue(lines, selector) {
|
|
813
|
+
const block = _findBlock(lines, selector);
|
|
814
|
+
if (!block) return undefined;
|
|
815
|
+
const value = [];
|
|
816
|
+
for (let y = block.start + 1; y < block.end; y++) {
|
|
817
|
+
const line = String(lines[y] ?? "");
|
|
818
|
+
const rest = line.startsWith(block.header.indent)
|
|
819
|
+
? line.slice(block.header.indent.length)
|
|
820
|
+
: line;
|
|
821
|
+
if (block.header.kind === "fenced") value.push(rest);
|
|
822
|
+
else {
|
|
823
|
+
const body = rest.match(/^(?:│|\|)(?: ?)(.*)$/);
|
|
824
|
+
value.push(body ? body[1] : rest);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
return value.join("\n");
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
function _headingSelectorId(input) {
|
|
831
|
+
if (input !== null && typeof input === "object") {
|
|
832
|
+
const id = String(input.id ?? "");
|
|
833
|
+
return isMdcuiId(id) ? id : null;
|
|
834
|
+
}
|
|
835
|
+
return parseMdcuiIdSelector(input);
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
function _sameHeadingContainer(left, right) {
|
|
839
|
+
const leftPath = left?.containerPath ?? [];
|
|
840
|
+
const rightPath = right?.containerPath ?? [];
|
|
841
|
+
return (
|
|
842
|
+
leftPath.length === rightPath.length
|
|
843
|
+
&& leftPath.every((container, index) => container === rightPath[index])
|
|
844
|
+
);
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
function _headingContainerContains(containerHeading, candidate) {
|
|
848
|
+
const containerPath = containerHeading?.containerPath ?? [];
|
|
849
|
+
const candidatePath = candidate?.containerPath ?? [];
|
|
850
|
+
return (
|
|
851
|
+
candidatePath.length >= containerPath.length
|
|
852
|
+
&& containerPath.every(
|
|
853
|
+
(container, index) => container === candidatePath[index],
|
|
854
|
+
)
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
function _tuiSourceHeadings(buffer) {
|
|
859
|
+
const markdown = buffer?._mdcuiTuiSourceText;
|
|
860
|
+
if (markdown == null || typeof Bun?.markdown?.html !== "function") return [];
|
|
861
|
+
const cached = buffer?._mdcuiSourceHeadingIndex;
|
|
862
|
+
if (cached?.source === markdown) return cached.headings;
|
|
863
|
+
|
|
864
|
+
const result = renderMarkdownWithHeadingIds(markdown).headings.map(
|
|
865
|
+
heading => ({ ...heading }),
|
|
866
|
+
);
|
|
867
|
+
|
|
868
|
+
for (const heading of result) {
|
|
869
|
+
heading.endOrdinal = result.length;
|
|
870
|
+
for (
|
|
871
|
+
let ordinal = heading.ordinal + 1;
|
|
872
|
+
ordinal < result.length;
|
|
873
|
+
ordinal++
|
|
874
|
+
) {
|
|
875
|
+
const candidate = result[ordinal];
|
|
876
|
+
if (
|
|
877
|
+
!_headingContainerContains(heading, candidate)
|
|
878
|
+
|| (
|
|
879
|
+
_sameHeadingContainer(heading, candidate)
|
|
880
|
+
&& candidate.level <= heading.level
|
|
881
|
+
)
|
|
882
|
+
) {
|
|
883
|
+
heading.endOrdinal = ordinal;
|
|
884
|
+
break;
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
if (buffer && typeof buffer === "object") {
|
|
890
|
+
buffer._mdcuiSourceHeadingIndex = {
|
|
891
|
+
source: markdown,
|
|
892
|
+
headings: result,
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
return result;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
function _findHeading(buffer, selector) {
|
|
899
|
+
const id = _headingSelectorId(selector);
|
|
900
|
+
if (!id) return undefined;
|
|
901
|
+
return _tuiSourceHeadings(buffer).find(heading => heading.id === id);
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
let _headingAnsiPrefixes;
|
|
905
|
+
function _tuiHeadingAnsiPrefixes() {
|
|
906
|
+
if (_headingAnsiPrefixes) return _headingAnsiPrefixes;
|
|
907
|
+
const marker = "MDCUI_HEADING_LINE_PROBE";
|
|
908
|
+
_headingAnsiPrefixes = new Map();
|
|
909
|
+
for (let level = 1; level <= 6; level++) {
|
|
910
|
+
const rendered = String(Bun.markdown.ansi(
|
|
911
|
+
`${"#".repeat(level)} ${marker}`,
|
|
912
|
+
{ hyperlinks: true, columns: 80 },
|
|
913
|
+
));
|
|
914
|
+
const line = rendered.split("\n").find((item) => item.includes(marker));
|
|
915
|
+
if (line) _headingAnsiPrefixes.set(level, line.slice(0, line.indexOf(marker)));
|
|
916
|
+
}
|
|
917
|
+
return _headingAnsiPrefixes;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
function _headingTuiLine(buffer, heading) {
|
|
921
|
+
if (!heading) return 0;
|
|
922
|
+
const exact = _tuiHeadingRowIndex(buffer).byOrdinal.get(heading.ordinal);
|
|
923
|
+
return exact?.id === heading.id && exact.level === heading.level
|
|
924
|
+
? exact.row + 1
|
|
925
|
+
: 0;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
function _hiddenTuiHeadingAncestors(buffer, heading) {
|
|
929
|
+
if (!heading || !(buffer?._mdcuiIdStore instanceof Map)) return [];
|
|
930
|
+
|
|
931
|
+
const hiddenAncestors = [];
|
|
932
|
+
for (const [id, record] of buffer._mdcuiIdStore) {
|
|
933
|
+
const state = record?.headingVisibility;
|
|
934
|
+
if (
|
|
935
|
+
state?.hidden &&
|
|
936
|
+
Number.isInteger(state.ordinal) &&
|
|
937
|
+
state.ordinal < heading.ordinal
|
|
938
|
+
) hiddenAncestors.push({ id, ordinal: state.ordinal });
|
|
939
|
+
}
|
|
940
|
+
if (hiddenAncestors.length === 0) return [];
|
|
941
|
+
|
|
942
|
+
const sourceHeadings = _tuiSourceHeadings(buffer);
|
|
943
|
+
const result = [];
|
|
944
|
+
for (const hidden of hiddenAncestors) {
|
|
945
|
+
const ancestor = sourceHeadings[hidden.ordinal];
|
|
946
|
+
if (!ancestor || ancestor.id !== hidden.id) continue;
|
|
947
|
+
|
|
948
|
+
if (heading.ordinal < ancestor.endOrdinal)
|
|
949
|
+
result.push({ id: hidden.id, heading: ancestor });
|
|
950
|
+
}
|
|
951
|
+
return result.sort((a, b) => a.heading.ordinal - b.heading.ordinal);
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
function _headingHasHiddenTuiAncestor(buffer, heading) {
|
|
955
|
+
return _hiddenTuiHeadingAncestors(buffer, heading).length > 0;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
function _tuiHeadingLines(buffer) {
|
|
959
|
+
const ansiText = buffer?._mdcuiAnsiText;
|
|
960
|
+
if (typeof ansiText !== "string") return [];
|
|
961
|
+
const prefixes = _tuiHeadingAnsiPrefixes();
|
|
962
|
+
const tuiHeadings = [];
|
|
963
|
+
for (const [lineIndex, line] of ansiText.split("\n").entries()) {
|
|
964
|
+
for (const [level, prefix] of prefixes) {
|
|
965
|
+
const prefixIndex = line.indexOf(prefix);
|
|
966
|
+
if (prefixIndex < 0) continue;
|
|
967
|
+
const visiblePrefix = Bun.stripANSI(line.slice(0, prefixIndex));
|
|
968
|
+
if (!/^[\s│|]*$/u.test(visiblePrefix)) continue;
|
|
969
|
+
tuiHeadings.push({
|
|
970
|
+
level,
|
|
971
|
+
line: lineIndex + 1,
|
|
972
|
+
column: visiblePrefix.length,
|
|
973
|
+
prefix: visiblePrefix,
|
|
974
|
+
});
|
|
975
|
+
break;
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
return tuiHeadings;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
function _visibleTuiSourceHeadings(buffer, sourceHeadings) {
|
|
982
|
+
const hidden = new Set();
|
|
983
|
+
if (buffer?._mdcuiIdStore instanceof Map) {
|
|
984
|
+
for (const [id, record] of buffer._mdcuiIdStore) {
|
|
985
|
+
const state = record?.headingVisibility;
|
|
986
|
+
const heading = sourceHeadings[state?.ordinal];
|
|
987
|
+
if (
|
|
988
|
+
state?.hidden
|
|
989
|
+
&& Number.isInteger(state.ordinal)
|
|
990
|
+
&& heading?.id === id
|
|
991
|
+
) hidden.add(state.ordinal);
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
const visible = [];
|
|
996
|
+
let concealedUntil = 0;
|
|
997
|
+
for (const heading of sourceHeadings) {
|
|
998
|
+
if (heading.ordinal < concealedUntil) continue;
|
|
999
|
+
visible.push(heading);
|
|
1000
|
+
if (hidden.has(heading.ordinal))
|
|
1001
|
+
concealedUntil = heading.endOrdinal;
|
|
1002
|
+
}
|
|
1003
|
+
return visible;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
function _replaceTuiHeadingRowEntries(buffer, index, entries) {
|
|
1007
|
+
entries.sort((a, b) => a.row - b.row);
|
|
1008
|
+
index.entries = entries;
|
|
1009
|
+
index.byRow.clear();
|
|
1010
|
+
index.byOrdinal.clear();
|
|
1011
|
+
for (const [position, entry] of entries.entries()) {
|
|
1012
|
+
entry.position = position;
|
|
1013
|
+
index.byRow.set(entry.row, entry);
|
|
1014
|
+
index.byOrdinal.set(entry.ordinal, entry);
|
|
1015
|
+
}
|
|
1016
|
+
index.ansiText = buffer?._mdcuiAnsiText;
|
|
1017
|
+
index.lines = buffer?.lines;
|
|
1018
|
+
index.lineCount = buffer?.lines?.length ?? 0;
|
|
1019
|
+
return index;
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
function _buildTuiHeadingRowIndex(buffer) {
|
|
1023
|
+
const sourceHeadings = _tuiSourceHeadings(buffer);
|
|
1024
|
+
const renderedHeadings = _tuiHeadingLines(buffer);
|
|
1025
|
+
const visibleHeadings = _visibleTuiSourceHeadings(buffer, sourceHeadings);
|
|
1026
|
+
const valid = (
|
|
1027
|
+
visibleHeadings.length === renderedHeadings.length
|
|
1028
|
+
&& visibleHeadings.every((heading, index) =>
|
|
1029
|
+
heading.level === renderedHeadings[index].level
|
|
1030
|
+
)
|
|
1031
|
+
);
|
|
1032
|
+
const index = {
|
|
1033
|
+
source: buffer?._mdcuiTuiSourceText,
|
|
1034
|
+
ansiText: buffer?._mdcuiAnsiText,
|
|
1035
|
+
lines: buffer?.lines,
|
|
1036
|
+
lineCount: buffer?.lines?.length ?? 0,
|
|
1037
|
+
valid,
|
|
1038
|
+
entries: [],
|
|
1039
|
+
byRow: new Map(),
|
|
1040
|
+
byOrdinal: new Map(),
|
|
1041
|
+
};
|
|
1042
|
+
if (valid) {
|
|
1043
|
+
_replaceTuiHeadingRowEntries(
|
|
1044
|
+
buffer,
|
|
1045
|
+
index,
|
|
1046
|
+
visibleHeadings.map((heading, position) => ({
|
|
1047
|
+
...heading,
|
|
1048
|
+
row: renderedHeadings[position].line - 1,
|
|
1049
|
+
column: renderedHeadings[position].column,
|
|
1050
|
+
prefix: renderedHeadings[position].prefix,
|
|
1051
|
+
})),
|
|
1052
|
+
);
|
|
1053
|
+
}
|
|
1054
|
+
if (buffer && typeof buffer === "object")
|
|
1055
|
+
buffer._mdcuiHeadingRowIndex = index;
|
|
1056
|
+
return index;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
function _tuiHeadingRowIndex(buffer) {
|
|
1060
|
+
const cached = buffer?._mdcuiHeadingRowIndex;
|
|
1061
|
+
if (
|
|
1062
|
+
cached
|
|
1063
|
+
&& cached.source === buffer?._mdcuiTuiSourceText
|
|
1064
|
+
&& cached.ansiText === buffer?._mdcuiAnsiText
|
|
1065
|
+
&& cached.lines === buffer?.lines
|
|
1066
|
+
&& cached.lineCount === (buffer?.lines?.length ?? 0)
|
|
1067
|
+
) return cached;
|
|
1068
|
+
return _buildTuiHeadingRowIndex(buffer);
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
export function indexTuiHeadingRows(buffer) {
|
|
1072
|
+
return _tuiHeadingRowIndex(buffer);
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
export function navigateTuiHeadingFragment(buffer, href) {
|
|
1076
|
+
const value = String(href ?? "");
|
|
1077
|
+
if (!value.startsWith("#") || value.length === 1) return false;
|
|
1078
|
+
|
|
1079
|
+
let id;
|
|
1080
|
+
try {
|
|
1081
|
+
id = decodeURIComponent(value.slice(1));
|
|
1082
|
+
} catch {
|
|
1083
|
+
return false;
|
|
1084
|
+
}
|
|
1085
|
+
if (!id) return false;
|
|
1086
|
+
|
|
1087
|
+
const heading = _tuiSourceHeadings(buffer).find(item => item.id === id);
|
|
1088
|
+
if (!heading) return false;
|
|
1089
|
+
const rendered = _tuiHeadingRowIndex(buffer).byOrdinal.get(heading.ordinal);
|
|
1090
|
+
if (
|
|
1091
|
+
!rendered
|
|
1092
|
+
|| rendered.id !== heading.id
|
|
1093
|
+
|| rendered.level !== heading.level
|
|
1094
|
+
) return false;
|
|
1095
|
+
|
|
1096
|
+
buffer.cursor = {
|
|
1097
|
+
x: rendered.column,
|
|
1098
|
+
y: rendered.row,
|
|
1099
|
+
};
|
|
1100
|
+
buffer.allowCursorOffscreen = false;
|
|
1101
|
+
buffer.ensureCursor?.();
|
|
1102
|
+
return true;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
function _cachedTuiHeadingRowIndex(buffer) {
|
|
1106
|
+
const cached = buffer?._mdcuiHeadingRowIndex;
|
|
1107
|
+
return (
|
|
1108
|
+
cached?.valid
|
|
1109
|
+
&& cached.source === buffer?._mdcuiTuiSourceText
|
|
1110
|
+
&& cached.ansiText === buffer?._mdcuiAnsiText
|
|
1111
|
+
&& cached.lines === buffer?.lines
|
|
1112
|
+
&& cached.lineCount === (buffer?.lines?.length ?? 0)
|
|
1113
|
+
) ? cached : null;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
function _tuiRenderedContainerPrefix(line) {
|
|
1117
|
+
const text = String(line ?? "");
|
|
1118
|
+
const prefix = text.match(/^[ \t│|]*/u)?.[0] ?? "";
|
|
1119
|
+
let quoteDepth = 0;
|
|
1120
|
+
for (const character of prefix) {
|
|
1121
|
+
if (character === "│" || character === "|") quoteDepth++;
|
|
1122
|
+
}
|
|
1123
|
+
return {
|
|
1124
|
+
contentColumn: prefix.length,
|
|
1125
|
+
empty: prefix.length === text.length,
|
|
1126
|
+
quoteDepth,
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
function _tuiRenderedTaskCheckbox(line, expectedQuoteDepth = null) {
|
|
1131
|
+
// Matching the heading's quote depth keeps table-cell borders from looking
|
|
1132
|
+
// like task-list container markers.
|
|
1133
|
+
const match = String(line ?? "").match(
|
|
1134
|
+
/^((?:[ \t]*[│|])*[ \t]*)([☐☒])(?:[ \t]+|$)(.*)$/u,
|
|
1135
|
+
);
|
|
1136
|
+
if (!match) return null;
|
|
1137
|
+
const quoteDepth = [...match[1]]
|
|
1138
|
+
.filter(character => character === "│" || character === "|")
|
|
1139
|
+
.length;
|
|
1140
|
+
if (
|
|
1141
|
+
Number.isInteger(expectedQuoteDepth)
|
|
1142
|
+
&& quoteDepth !== expectedQuoteDepth
|
|
1143
|
+
) return null;
|
|
1144
|
+
return {
|
|
1145
|
+
checked: match[2] === "☒",
|
|
1146
|
+
column: match[1].length,
|
|
1147
|
+
prefix: match[1],
|
|
1148
|
+
quoteDepth,
|
|
1149
|
+
value: match[3],
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
function _headingCheckboxValue(buffer, heading, id) {
|
|
1154
|
+
const list = _tuiHeadingTaskList(buffer, heading);
|
|
1155
|
+
if (!list) return id.startsWith("select") ? null : [];
|
|
1156
|
+
const selected = [];
|
|
1157
|
+
for (const item of list.items) {
|
|
1158
|
+
const checkbox = _tuiRenderedTaskCheckbox(
|
|
1159
|
+
buffer.lines[item.start],
|
|
1160
|
+
list.quoteDepth,
|
|
1161
|
+
);
|
|
1162
|
+
if (!checkbox?.checked) continue;
|
|
1163
|
+
if (id.startsWith("select")) return item.value;
|
|
1164
|
+
selected.push(item.value);
|
|
1165
|
+
}
|
|
1166
|
+
return id.startsWith("select") ? null : selected;
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
function _tuiIdStore(buffer) {
|
|
1170
|
+
if (!(buffer?._mdcuiIdStore instanceof Map))
|
|
1171
|
+
buffer._mdcuiIdStore = new Map();
|
|
1172
|
+
return buffer._mdcuiIdStore;
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
function _tuiIdRecord(buffer, id) {
|
|
1176
|
+
const store = _tuiIdStore(buffer);
|
|
1177
|
+
let record = store.get(id);
|
|
1178
|
+
if (!record) {
|
|
1179
|
+
record = {};
|
|
1180
|
+
store.set(id, record);
|
|
1181
|
+
}
|
|
1182
|
+
return record;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
function _tuiUserData(buffer, id) {
|
|
1186
|
+
if (!buffer || !id) return undefined;
|
|
1187
|
+
const record = _tuiIdRecord(buffer, id);
|
|
1188
|
+
if (!record.data || typeof record.data !== "object")
|
|
1189
|
+
record.data = Object.create(null);
|
|
1190
|
+
return record.data;
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
function _removeTuiUserData(buffer, id, keys) {
|
|
1194
|
+
if (!buffer || !id) return;
|
|
1195
|
+
const store = _tuiIdStore(buffer);
|
|
1196
|
+
const record = store.get(id);
|
|
1197
|
+
if (!record?.data) return;
|
|
1198
|
+
if (keys.length === 0) delete record.data;
|
|
1199
|
+
else {
|
|
1200
|
+
for (const key of keys) delete record.data[key];
|
|
1201
|
+
}
|
|
1202
|
+
if (Object.keys(record).length === 0) store.delete(id);
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
const _renderingTuiComponentRecords = new WeakSet();
|
|
1206
|
+
|
|
1207
|
+
export function markdownImageSignature(markdown) {
|
|
1208
|
+
return [...String(markdown ?? "").matchAll(/!\[[^\]]*\]\(([^)]+)\)/gu)]
|
|
1209
|
+
.map((match) => match[1].trim())
|
|
1210
|
+
.join("\0");
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
function _renderTuiHeadingComponents(buffer, id) {
|
|
1214
|
+
const record = buffer?._mdcuiIdStore?.get(id);
|
|
1215
|
+
if (!record || _renderingTuiComponentRecords.has(record) || !Array.isArray(record.components))
|
|
1216
|
+
return false;
|
|
1217
|
+
let changed = false;
|
|
1218
|
+
_renderingTuiComponentRecords.add(record);
|
|
1219
|
+
try {
|
|
1220
|
+
for (const component of record.components) {
|
|
1221
|
+
if (!component || typeof component.render !== "function") continue;
|
|
1222
|
+
const rendered = String(component.render.call(component, record.data) ?? "");
|
|
1223
|
+
if (rendered === component.last) continue;
|
|
1224
|
+
const oldImageSignature = markdownImageSignature(component.last);
|
|
1225
|
+
const newImageSignature = markdownImageSignature(rendered);
|
|
1226
|
+
// Replacing a component removes its old per-line Kitty metadata. Even if
|
|
1227
|
+
// the destinations are unchanged, prepare the frame again whenever the
|
|
1228
|
+
// changed component contains an image.
|
|
1229
|
+
const imagesAffected = oldImageSignature !== newImageSignature
|
|
1230
|
+
|| oldImageSignature !== "";
|
|
1231
|
+
const start = buffer.lines.findIndex(line =>
|
|
1232
|
+
String(line ?? "").includes(component.marker?.start)
|
|
1233
|
+
);
|
|
1234
|
+
const end = buffer.lines.findIndex((line, index) =>
|
|
1235
|
+
index > start && String(line ?? "").includes(component.marker?.end)
|
|
1236
|
+
);
|
|
1237
|
+
if (start < 0 || end < 0) continue;
|
|
1238
|
+
const replacement = renderTuiComponentMarkdown(
|
|
1239
|
+
component,
|
|
1240
|
+
rendered,
|
|
1241
|
+
buffer._mdcuiRenderWidth || 80,
|
|
1242
|
+
);
|
|
1243
|
+
if (!replacement) continue;
|
|
1244
|
+
const styled = buffer._parseAnsiStyledText?.(replacement.ansi.join("\n"));
|
|
1245
|
+
if (styled?.styleLines)
|
|
1246
|
+
markTuiTableStripeStyles(styled.styleLines, replacement.lines);
|
|
1247
|
+
spliceTuiBufferLines(buffer, start + 1, end - start - 1, replacement.lines, {
|
|
1248
|
+
ansi: replacement.ansi,
|
|
1249
|
+
styles: styled?.styleLines,
|
|
1250
|
+
markModified: false,
|
|
1251
|
+
});
|
|
1252
|
+
component.last = rendered;
|
|
1253
|
+
if (imagesAffected) {
|
|
1254
|
+
buffer._mdcuiImagesDirty = true;
|
|
1255
|
+
buffer._mdcuiImagesRevision = (buffer._mdcuiImagesRevision ?? 0) + 1;
|
|
1256
|
+
}
|
|
1257
|
+
changed = true;
|
|
1258
|
+
}
|
|
1259
|
+
if (changed) refreshTuiLinkIndex(buffer);
|
|
1260
|
+
} finally {
|
|
1261
|
+
_renderingTuiComponentRecords.delete(record);
|
|
1262
|
+
}
|
|
1263
|
+
return changed;
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
function _takeTuiTaskListAnchors(buffer, start, end) {
|
|
1267
|
+
const anchors = [];
|
|
1268
|
+
const stored = buffer?._mdcuiHeadingTaskListAnchors;
|
|
1269
|
+
if (!(stored instanceof Map)) return anchors;
|
|
1270
|
+
|
|
1271
|
+
for (const [ordinal, anchor] of stored) {
|
|
1272
|
+
if (!anchor || anchor.index < start || anchor.index >= end) continue;
|
|
1273
|
+
anchors.push({
|
|
1274
|
+
ordinal,
|
|
1275
|
+
anchor: { ...anchor, index: anchor.index - start },
|
|
1276
|
+
});
|
|
1277
|
+
stored.delete(ordinal);
|
|
1278
|
+
}
|
|
1279
|
+
return anchors;
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
function _restoreTuiTaskListAnchors(buffer, start, anchors) {
|
|
1283
|
+
if (!Array.isArray(anchors) || anchors.length === 0) return;
|
|
1284
|
+
if (!(buffer._mdcuiHeadingTaskListAnchors instanceof Map))
|
|
1285
|
+
buffer._mdcuiHeadingTaskListAnchors = new Map();
|
|
1286
|
+
|
|
1287
|
+
for (const entry of anchors) {
|
|
1288
|
+
if (!entry?.anchor || !Number.isInteger(entry.anchor.index)) continue;
|
|
1289
|
+
buffer._mdcuiHeadingTaskListAnchors.set(entry.ordinal, {
|
|
1290
|
+
...entry.anchor,
|
|
1291
|
+
index: start + entry.anchor.index,
|
|
1292
|
+
});
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
export function clearTuiSourceDependentState(buffer) {
|
|
1297
|
+
if (!buffer || typeof buffer !== "object") return;
|
|
1298
|
+
|
|
1299
|
+
if (buffer._mdcuiIdStore instanceof Map) {
|
|
1300
|
+
for (const [id, record] of buffer._mdcuiIdStore) {
|
|
1301
|
+
if (!record || typeof record !== "object") {
|
|
1302
|
+
buffer._mdcuiIdStore.delete(id);
|
|
1303
|
+
continue;
|
|
1304
|
+
}
|
|
1305
|
+
delete record.headingVisibility;
|
|
1306
|
+
if (Object.keys(record).length === 0)
|
|
1307
|
+
buffer._mdcuiIdStore.delete(id);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
delete buffer._mdcuiMutationMacros;
|
|
1312
|
+
delete buffer._mdcuiDirtyTableCells;
|
|
1313
|
+
buffer._mdcuiReplayingMutations = false;
|
|
1314
|
+
buffer._mdcuiHeadingTaskListAnchors = null;
|
|
1315
|
+
buffer._mdcuiRerenderMismatch = null;
|
|
1316
|
+
buffer._mdcuiFenceBlockIndex = null;
|
|
1317
|
+
buffer._mdcuiControlBlockIndex = null;
|
|
1318
|
+
buffer._mdcuiSourceHeadingIndex = null;
|
|
1319
|
+
buffer._mdcuiHeadingRowIndex = null;
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
function _tuiHeadingContainerEndRow(buffer, heading) {
|
|
1323
|
+
const lines = buffer?.lines;
|
|
1324
|
+
if (
|
|
1325
|
+
!Array.isArray(lines)
|
|
1326
|
+
|| !Array.isArray(heading?.containerPath)
|
|
1327
|
+
|| heading.containerPath.length === 0
|
|
1328
|
+
) return lines?.length ?? 0;
|
|
1329
|
+
|
|
1330
|
+
const prefix = String(heading.prefix ?? "");
|
|
1331
|
+
if (!prefix) return lines.length;
|
|
1332
|
+
const required = _tuiRenderedContainerPrefix(prefix);
|
|
1333
|
+
|
|
1334
|
+
for (let row = heading.row + 1; row < lines.length; row++) {
|
|
1335
|
+
const candidate = _tuiRenderedContainerPrefix(lines[row]);
|
|
1336
|
+
if (candidate.quoteDepth < required.quoteDepth) return row;
|
|
1337
|
+
if (candidate.empty) continue;
|
|
1338
|
+
if (candidate.contentColumn < required.contentColumn) return row;
|
|
1339
|
+
}
|
|
1340
|
+
return lines.length;
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
function _hideTuiHeadingSection(buffer, heading, id) {
|
|
1344
|
+
if (!buffer || !heading || !id || !Array.isArray(buffer.lines)) return false;
|
|
1345
|
+
const store = _tuiIdStore(buffer);
|
|
1346
|
+
if (store.get(id)?.headingVisibility?.hidden) return true;
|
|
1347
|
+
|
|
1348
|
+
const headingIndex = _tuiHeadingRowIndex(buffer);
|
|
1349
|
+
const renderedHeading = headingIndex.byOrdinal.get(heading.ordinal);
|
|
1350
|
+
if (
|
|
1351
|
+
!headingIndex.valid
|
|
1352
|
+
|| renderedHeading?.id !== id
|
|
1353
|
+
|| renderedHeading.level !== heading.level
|
|
1354
|
+
) return false;
|
|
1355
|
+
|
|
1356
|
+
let next = null;
|
|
1357
|
+
const sectionHeadings = [];
|
|
1358
|
+
for (
|
|
1359
|
+
let position = renderedHeading.position + 1;
|
|
1360
|
+
position < headingIndex.entries.length;
|
|
1361
|
+
position++
|
|
1362
|
+
) {
|
|
1363
|
+
const candidate = headingIndex.entries[position];
|
|
1364
|
+
if (
|
|
1365
|
+
!_headingContainerContains(renderedHeading, candidate)
|
|
1366
|
+
|| (
|
|
1367
|
+
_sameHeadingContainer(renderedHeading, candidate)
|
|
1368
|
+
&& candidate.level <= heading.level
|
|
1369
|
+
)
|
|
1370
|
+
) {
|
|
1371
|
+
next = candidate;
|
|
1372
|
+
break;
|
|
1373
|
+
}
|
|
1374
|
+
sectionHeadings.push(candidate);
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
// Starting after the zero-based heading row keeps the heading itself and
|
|
1378
|
+
// removes only its generated section body.
|
|
1379
|
+
const start = renderedHeading.row + 1;
|
|
1380
|
+
const end = Math.min(
|
|
1381
|
+
next ? next.row : buffer.lines.length,
|
|
1382
|
+
_tuiHeadingContainerEndRow(buffer, renderedHeading),
|
|
1383
|
+
);
|
|
1384
|
+
const deleteCount = Math.max(0, end - start);
|
|
1385
|
+
const ansiLines = typeof buffer._mdcuiAnsiText === "string"
|
|
1386
|
+
? buffer._mdcuiAnsiText.split("\n")
|
|
1387
|
+
: null;
|
|
1388
|
+
const segment = {
|
|
1389
|
+
lines: buffer.lines.slice(start, end),
|
|
1390
|
+
styles: Array.isArray(buffer._ansiStyleLines)
|
|
1391
|
+
? buffer._ansiStyleLines.slice(start, end)
|
|
1392
|
+
: null,
|
|
1393
|
+
ansi: ansiLines?.slice(start, end) ?? null,
|
|
1394
|
+
images: Array.isArray(buffer._mdcuiImages)
|
|
1395
|
+
? buffer._mdcuiImages
|
|
1396
|
+
.filter(image => image.line >= start && image.line < end)
|
|
1397
|
+
.map(image => ({ ...image, line: image.line - start }))
|
|
1398
|
+
: [],
|
|
1399
|
+
anchors: _takeTuiTaskListAnchors(buffer, start, end),
|
|
1400
|
+
headings: sectionHeadings
|
|
1401
|
+
.filter(candidate => candidate.row < end)
|
|
1402
|
+
.map((candidate) => ({
|
|
1403
|
+
...candidate,
|
|
1404
|
+
row: candidate.row - start,
|
|
1405
|
+
})),
|
|
1406
|
+
};
|
|
1407
|
+
|
|
1408
|
+
const modified = buffer.modified;
|
|
1409
|
+
spliceTuiBufferLines(buffer, start, deleteCount, []);
|
|
1410
|
+
buffer.modified = modified;
|
|
1411
|
+
_tuiIdRecord(buffer, id).headingVisibility = {
|
|
1412
|
+
hidden: true,
|
|
1413
|
+
ordinal: heading.ordinal,
|
|
1414
|
+
segment,
|
|
1415
|
+
};
|
|
1416
|
+
return true;
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
function _showTuiHeadingSection(buffer, heading, id) {
|
|
1420
|
+
if (!buffer || !heading || !id || !Array.isArray(buffer.lines)) return false;
|
|
1421
|
+
const store = _tuiIdStore(buffer);
|
|
1422
|
+
const record = store.get(id);
|
|
1423
|
+
const state = record?.headingVisibility;
|
|
1424
|
+
if (!state?.hidden) return true;
|
|
1425
|
+
|
|
1426
|
+
const headingLine = _headingTuiLine(buffer, heading);
|
|
1427
|
+
if (!headingLine) return false;
|
|
1428
|
+
const segment = state.segment;
|
|
1429
|
+
const modified = buffer.modified;
|
|
1430
|
+
spliceTuiBufferLines(buffer, headingLine, 0, segment.lines, {
|
|
1431
|
+
styles: segment.styles ?? undefined,
|
|
1432
|
+
ansi: segment.ansi ?? undefined,
|
|
1433
|
+
headings: segment.headings ?? undefined,
|
|
1434
|
+
});
|
|
1435
|
+
if (Array.isArray(buffer._mdcuiImages) && segment.images.length > 0) {
|
|
1436
|
+
buffer._mdcuiImages.push(
|
|
1437
|
+
...segment.images.map(image => ({ ...image, line: headingLine + image.line })),
|
|
1438
|
+
);
|
|
1439
|
+
buffer._mdcuiImages.sort((a, b) => a.line - b.line);
|
|
1440
|
+
}
|
|
1441
|
+
_restoreTuiTaskListAnchors(buffer, headingLine, segment.anchors);
|
|
1442
|
+
buffer.modified = modified;
|
|
1443
|
+
delete record.headingVisibility;
|
|
1444
|
+
if (Object.keys(record).length === 0) store.delete(id);
|
|
1445
|
+
return true;
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
function _changeTuiHeadingSectionVisibility(buffer, heading, id, action) {
|
|
1449
|
+
if (!buffer || !heading || !id) return false;
|
|
1450
|
+
const expandedAncestors = [];
|
|
1451
|
+
try {
|
|
1452
|
+
for (const ancestor of _hiddenTuiHeadingAncestors(buffer, heading)) {
|
|
1453
|
+
if (!_showTuiHeadingSection(buffer, ancestor.heading, ancestor.id))
|
|
1454
|
+
return false;
|
|
1455
|
+
expandedAncestors.push(ancestor);
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
if (action === "show")
|
|
1459
|
+
return _showTuiHeadingSection(buffer, heading, id);
|
|
1460
|
+
if (action === "hide")
|
|
1461
|
+
return _hideTuiHeadingSection(buffer, heading, id);
|
|
1462
|
+
if (action === "toggle") {
|
|
1463
|
+
const hidden = _tuiIdStore(buffer).get(id)?.headingVisibility?.hidden;
|
|
1464
|
+
return hidden
|
|
1465
|
+
? _showTuiHeadingSection(buffer, heading, id)
|
|
1466
|
+
: _hideTuiHeadingSection(buffer, heading, id);
|
|
1467
|
+
}
|
|
1468
|
+
return false;
|
|
1469
|
+
} finally {
|
|
1470
|
+
for (const ancestor of expandedAncestors.reverse())
|
|
1471
|
+
_hideTuiHeadingSection(buffer, ancestor.heading, ancestor.id);
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
export function toggleTuiHeadingAt(buffer, y, x) {
|
|
1476
|
+
if (!buffer || !Array.isArray(buffer.lines)) return false;
|
|
1477
|
+
const row = Math.trunc(Number(y));
|
|
1478
|
+
const column = Math.trunc(Number(x));
|
|
1479
|
+
if (row < 0 || row >= buffer.lines.length) return false;
|
|
1480
|
+
const heading = _tuiHeadingRowIndex(buffer).byRow.get(row);
|
|
1481
|
+
if (!heading?.id || column !== heading.column) return false;
|
|
1482
|
+
|
|
1483
|
+
const state = _tuiIdStore(buffer).get(heading.id)?.headingVisibility;
|
|
1484
|
+
return state?.hidden
|
|
1485
|
+
? _showTuiHeadingSection(buffer, heading, heading.id)
|
|
1486
|
+
: _hideTuiHeadingSection(buffer, heading, heading.id);
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
function _tuiHeadingTaskList(buffer, heading) {
|
|
1490
|
+
if (!heading || _headingHasHiddenTuiAncestor(buffer, heading)) return null;
|
|
1491
|
+
const savedAnchor = buffer?._mdcuiHeadingTaskListAnchors?.get(heading.ordinal);
|
|
1492
|
+
if (savedAnchor) {
|
|
1493
|
+
return {
|
|
1494
|
+
first: savedAnchor.index,
|
|
1495
|
+
end: savedAnchor.index,
|
|
1496
|
+
indent: savedAnchor.indent,
|
|
1497
|
+
items: [],
|
|
1498
|
+
quoteDepth: _tuiRenderedContainerPrefix(savedAnchor.indent).quoteDepth,
|
|
1499
|
+
};
|
|
1500
|
+
}
|
|
1501
|
+
const headingIndex = _tuiHeadingRowIndex(buffer);
|
|
1502
|
+
const renderedHeading = headingIndex.byOrdinal.get(heading.ordinal);
|
|
1503
|
+
if (
|
|
1504
|
+
!renderedHeading
|
|
1505
|
+
|| renderedHeading.id !== heading.id
|
|
1506
|
+
|| !Array.isArray(buffer?.lines)
|
|
1507
|
+
) return null;
|
|
1508
|
+
|
|
1509
|
+
const headingLine = renderedHeading.row + 1;
|
|
1510
|
+
const nextHeading = headingIndex.entries[renderedHeading.position + 1];
|
|
1511
|
+
const endLine = Math.min(
|
|
1512
|
+
nextHeading?.row ?? buffer.lines.length,
|
|
1513
|
+
_tuiHeadingContainerEndRow(buffer, renderedHeading),
|
|
1514
|
+
);
|
|
1515
|
+
const quoteDepth = _tuiRenderedContainerPrefix(
|
|
1516
|
+
renderedHeading.prefix,
|
|
1517
|
+
).quoteDepth;
|
|
1518
|
+
let first = -1;
|
|
1519
|
+
let indent = "";
|
|
1520
|
+
|
|
1521
|
+
for (let y = headingLine; y < endLine; y++) {
|
|
1522
|
+
const checkbox = _tuiRenderedTaskCheckbox(buffer.lines[y], quoteDepth);
|
|
1523
|
+
if (!checkbox) continue;
|
|
1524
|
+
first = y;
|
|
1525
|
+
indent = checkbox.prefix;
|
|
1526
|
+
break;
|
|
1527
|
+
}
|
|
1528
|
+
if (first < 0) return null;
|
|
1529
|
+
|
|
1530
|
+
let end = endLine;
|
|
1531
|
+
let pendingBlank = -1;
|
|
1532
|
+
for (let y = first + 1; y < endLine; y++) {
|
|
1533
|
+
const line = String(buffer.lines[y] ?? "");
|
|
1534
|
+
const containerPrefix = _tuiRenderedContainerPrefix(line);
|
|
1535
|
+
if (containerPrefix.empty) {
|
|
1536
|
+
if (pendingBlank < 0) pendingBlank = y;
|
|
1537
|
+
continue;
|
|
1538
|
+
}
|
|
1539
|
+
const checkbox = _tuiRenderedTaskCheckbox(line, quoteDepth);
|
|
1540
|
+
if (checkbox || containerPrefix.contentColumn > indent.length) {
|
|
1541
|
+
pendingBlank = -1;
|
|
1542
|
+
continue;
|
|
1543
|
+
}
|
|
1544
|
+
end = pendingBlank >= 0 ? pendingBlank : y;
|
|
1545
|
+
break;
|
|
1546
|
+
}
|
|
1547
|
+
if (end === endLine && pendingBlank >= 0) end = pendingBlank;
|
|
1548
|
+
|
|
1549
|
+
const items = [];
|
|
1550
|
+
for (let y = first; y < end; y++) {
|
|
1551
|
+
const checkbox = _tuiRenderedTaskCheckbox(buffer.lines[y], quoteDepth);
|
|
1552
|
+
if (checkbox?.prefix === indent) {
|
|
1553
|
+
items.push({
|
|
1554
|
+
start: y,
|
|
1555
|
+
end,
|
|
1556
|
+
value: checkbox.value.trim(),
|
|
1557
|
+
});
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
for (let index = 0; index < items.length - 1; index++)
|
|
1561
|
+
items[index].end = items[index + 1].start;
|
|
1562
|
+
|
|
1563
|
+
return { first, end, indent, items, quoteDepth };
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
function _tuiHeadingImageSources(buffer, heading) {
|
|
1567
|
+
if (!heading || _headingHasHiddenTuiAncestor(buffer, heading)) return [];
|
|
1568
|
+
const headingIndex = _tuiHeadingRowIndex(buffer);
|
|
1569
|
+
const renderedHeading = headingIndex.byOrdinal.get(heading.ordinal);
|
|
1570
|
+
if (!renderedHeading || renderedHeading.id !== heading.id) return [];
|
|
1571
|
+
const ansiLines = String(buffer?._mdcuiAnsiText ?? "").split("\n");
|
|
1572
|
+
const nextHeading = headingIndex.entries[renderedHeading.position + 1];
|
|
1573
|
+
const endLine = Math.min(
|
|
1574
|
+
nextHeading?.row ?? ansiLines.length,
|
|
1575
|
+
_tuiHeadingContainerEndRow(buffer, renderedHeading),
|
|
1576
|
+
);
|
|
1577
|
+
const sources = [];
|
|
1578
|
+
const oscImage = /\x1b\]8;;([^\x1b]*)\x1b\\(?=[^\n]*📷)/gu;
|
|
1579
|
+
for (let line = renderedHeading.row + 1; line < endLine; line++) {
|
|
1580
|
+
for (const match of String(ansiLines[line] ?? "").matchAll(oscImage))
|
|
1581
|
+
sources.push(match[1]);
|
|
1582
|
+
}
|
|
1583
|
+
return sources;
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
function _tuiHeadingTable(buffer, heading) {
|
|
1587
|
+
if (!heading || _headingHasHiddenTuiAncestor(buffer, heading)) return null;
|
|
1588
|
+
const headingIndex = _tuiHeadingRowIndex(buffer);
|
|
1589
|
+
const renderedHeading = headingIndex.byOrdinal.get(heading.ordinal);
|
|
1590
|
+
if (
|
|
1591
|
+
!renderedHeading
|
|
1592
|
+
|| renderedHeading.id !== heading.id
|
|
1593
|
+
|| !Array.isArray(buffer?.lines)
|
|
1594
|
+
) return null;
|
|
1595
|
+
|
|
1596
|
+
const nextHeading = headingIndex.entries[renderedHeading.position + 1];
|
|
1597
|
+
const endLine = Math.min(
|
|
1598
|
+
nextHeading?.row ?? buffer.lines.length,
|
|
1599
|
+
_tuiHeadingContainerEndRow(buffer, renderedHeading),
|
|
1600
|
+
);
|
|
1601
|
+
let top = -1;
|
|
1602
|
+
let opening = -1;
|
|
1603
|
+
let closing = -1;
|
|
1604
|
+
for (let y = renderedHeading.row + 1; y < endLine; y++) {
|
|
1605
|
+
const line = String(buffer.lines[y] ?? "");
|
|
1606
|
+
const start = line.indexOf("┌");
|
|
1607
|
+
const end = line.lastIndexOf("┐");
|
|
1608
|
+
if (
|
|
1609
|
+
start >= 0
|
|
1610
|
+
&& end > start
|
|
1611
|
+
&& /^┌─+(?:┬─+)*┐$/u.test(line.slice(start, end + 1))
|
|
1612
|
+
) {
|
|
1613
|
+
top = y;
|
|
1614
|
+
opening = start;
|
|
1615
|
+
closing = end;
|
|
1616
|
+
break;
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
if (top < 0) return null;
|
|
1620
|
+
|
|
1621
|
+
const rows = [];
|
|
1622
|
+
let visualLines = [];
|
|
1623
|
+
for (let y = top + 1; y < endLine; y++) {
|
|
1624
|
+
const line = String(buffer.lines[y] ?? "");
|
|
1625
|
+
const frame = line.slice(opening, closing + 1);
|
|
1626
|
+
if (/^└─+(?:┴─+)*┘$/u.test(frame)) {
|
|
1627
|
+
if (visualLines.length) rows.push(visualLines);
|
|
1628
|
+
return { top, bottom: y, opening, closing, rows };
|
|
1629
|
+
}
|
|
1630
|
+
if (/^├─+(?:┼─+)*┤$/u.test(frame)) {
|
|
1631
|
+
if (visualLines.length) rows.push(visualLines);
|
|
1632
|
+
visualLines = [];
|
|
1633
|
+
continue;
|
|
1634
|
+
}
|
|
1635
|
+
if (/^│ .* │$/u.test(frame)) {
|
|
1636
|
+
const separators = [];
|
|
1637
|
+
for (let index = opening; index <= closing; index++) {
|
|
1638
|
+
if (line[index] === "│") separators.push(index);
|
|
1639
|
+
}
|
|
1640
|
+
if (separators.length >= 2) visualLines.push({ y, separators });
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
return null;
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
function _tuiTableCell(table, row, col) {
|
|
1647
|
+
const rowIndex = Number(row);
|
|
1648
|
+
const columnIndex = Number(col);
|
|
1649
|
+
if (
|
|
1650
|
+
!Number.isInteger(rowIndex)
|
|
1651
|
+
|| !Number.isInteger(columnIndex)
|
|
1652
|
+
|| rowIndex < 0
|
|
1653
|
+
|| columnIndex < 0
|
|
1654
|
+
) return null;
|
|
1655
|
+
const visualLines = table?.rows?.[rowIndex];
|
|
1656
|
+
if (
|
|
1657
|
+
!Array.isArray(visualLines)
|
|
1658
|
+
|| visualLines.length === 0
|
|
1659
|
+
|| visualLines.some(line => columnIndex + 1 >= line.separators.length)
|
|
1660
|
+
) return null;
|
|
1661
|
+
return { visualLines, columnIndex };
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
function _tuiTableCellText(buffer, table, row, col) {
|
|
1665
|
+
const cell = _tuiTableCell(table, row, col);
|
|
1666
|
+
if (!cell) return "";
|
|
1667
|
+
return cell.visualLines.map(({ y, separators }) => {
|
|
1668
|
+
const start = separators[cell.columnIndex] + 2;
|
|
1669
|
+
const end = separators[cell.columnIndex + 1] - 1;
|
|
1670
|
+
return String(buffer.lines[y] ?? "").slice(start, end).trimEnd();
|
|
1671
|
+
}).join("");
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
function _setTuiTableCellCheckbox(buffer, heading, row, col, checked) {
|
|
1675
|
+
const table = _tuiHeadingTable(buffer, heading);
|
|
1676
|
+
const cell = _tuiTableCell(table, row, col);
|
|
1677
|
+
if (!cell) return false;
|
|
1678
|
+
let target = null;
|
|
1679
|
+
for (const { y, separators } of cell.visualLines) {
|
|
1680
|
+
const start = separators[cell.columnIndex] + 2;
|
|
1681
|
+
const end = separators[cell.columnIndex + 1] - 1;
|
|
1682
|
+
const line = String(buffer.lines[y] ?? "");
|
|
1683
|
+
const offset = line.slice(start, end).search(/[☐☒]/u);
|
|
1684
|
+
if (offset >= 0) {
|
|
1685
|
+
target = { y, x: start + offset };
|
|
1686
|
+
break;
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
if (!target) return false;
|
|
1690
|
+
const glyph = checked ? "☒" : "☐";
|
|
1691
|
+
if (buffer.lines[target.y][target.x] === glyph) return true;
|
|
1692
|
+
if (!buffer._mdcuiReplayingMutations) buffer.pushUndo?.(true);
|
|
1693
|
+
buffer.lines[target.y] =
|
|
1694
|
+
buffer.lines[target.y].slice(0, target.x)
|
|
1695
|
+
+ glyph
|
|
1696
|
+
+ buffer.lines[target.y].slice(target.x + 1);
|
|
1697
|
+
if (typeof buffer._mdcuiAnsiText === "string") {
|
|
1698
|
+
const ansiLines = buffer._mdcuiAnsiText.split("\n");
|
|
1699
|
+
ansiLines[target.y] = updateAnsiTaskCheckbox(
|
|
1700
|
+
ansiLines[target.y] ?? "",
|
|
1701
|
+
target.x,
|
|
1702
|
+
checked,
|
|
1703
|
+
);
|
|
1704
|
+
if (checked) {
|
|
1705
|
+
ansiLines[target.y] = colorAnsiPlainRange(
|
|
1706
|
+
ansiLines[target.y],
|
|
1707
|
+
target.x,
|
|
1708
|
+
target.x + 1,
|
|
1709
|
+
"☒",
|
|
1710
|
+
);
|
|
1711
|
+
}
|
|
1712
|
+
buffer._mdcuiAnsiText = ansiLines.join("\n");
|
|
1713
|
+
}
|
|
1714
|
+
const styles = buffer._ansiStyleLines?.[target.y];
|
|
1715
|
+
if (Array.isArray(styles)) {
|
|
1716
|
+
const base = styles[target.x + 1] ?? styles[target.x - 1] ?? null;
|
|
1717
|
+
styles[target.x] = checked
|
|
1718
|
+
? { ...(base ?? {}), fg: "green" }
|
|
1719
|
+
: (base ? { ...base } : null);
|
|
1720
|
+
}
|
|
1721
|
+
if (!(buffer._mdcuiDirtyTableCells instanceof Set))
|
|
1722
|
+
buffer._mdcuiDirtyTableCells = new Set();
|
|
1723
|
+
buffer._mdcuiDirtyTableCells.add(`${heading.id}\0${row}\0${col}`);
|
|
1724
|
+
buffer._mdcuiLinkIndex = null;
|
|
1725
|
+
buffer.invalidateHighlightFrom?.(target.y);
|
|
1726
|
+
buffer.modified = true;
|
|
1727
|
+
return true;
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
const _tableCellSegmenter = typeof Intl.Segmenter === "function"
|
|
1731
|
+
? new Intl.Segmenter(undefined, { granularity: "grapheme" })
|
|
1732
|
+
: null;
|
|
1733
|
+
|
|
1734
|
+
function _tuiCellGraphemes(value) {
|
|
1735
|
+
const text = String(value ?? "").replace(/\r\n?|\n/gu, "");
|
|
1736
|
+
return _tableCellSegmenter
|
|
1737
|
+
? [..._tableCellSegmenter.segment(text)].map(item => item.segment)
|
|
1738
|
+
: Array.from(text);
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1741
|
+
function _tuiStringWidth(value) {
|
|
1742
|
+
return typeof globalThis.Bun?.stringWidth === "function"
|
|
1743
|
+
? Bun.stringWidth(String(value ?? ""))
|
|
1744
|
+
: Array.from(String(value ?? "")).length;
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
function _tuiReplacementStyles(line, styles, start, end, replacement) {
|
|
1748
|
+
const source = String(line ?? "").slice(start, end);
|
|
1749
|
+
const sourceStyles = Array.isArray(styles) ? styles.slice(start, end) : [];
|
|
1750
|
+
const sourceGraphemes = _tableCellSegmenter
|
|
1751
|
+
? [..._tableCellSegmenter.segment(source)].map(item => ({
|
|
1752
|
+
text: item.segment,
|
|
1753
|
+
index: item.index,
|
|
1754
|
+
}))
|
|
1755
|
+
: Array.from(source).map((text, index) => ({ text, index }));
|
|
1756
|
+
const styleByColumn = [];
|
|
1757
|
+
for (const item of sourceGraphemes) {
|
|
1758
|
+
const style = sourceStyles[item.index] ?? null;
|
|
1759
|
+
const width = Math.max(1, _tuiStringWidth(item.text));
|
|
1760
|
+
for (let column = 0; column < width; column++) styleByColumn.push(style);
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
const result = [];
|
|
1764
|
+
let column = 0;
|
|
1765
|
+
const replacementGraphemes = _tableCellSegmenter
|
|
1766
|
+
? [..._tableCellSegmenter.segment(replacement)].map(item => item.segment)
|
|
1767
|
+
: Array.from(replacement);
|
|
1768
|
+
for (const grapheme of replacementGraphemes) {
|
|
1769
|
+
const style = styleByColumn[
|
|
1770
|
+
Math.min(column, Math.max(0, styleByColumn.length - 1))
|
|
1771
|
+
] ?? null;
|
|
1772
|
+
for (let index = 0; index < grapheme.length; index++) result.push(style);
|
|
1773
|
+
column += Math.max(1, _tuiStringWidth(grapheme));
|
|
1774
|
+
}
|
|
1775
|
+
return result;
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
function _setTuiTableCell(buffer, heading, row, col, value) {
|
|
1779
|
+
const table = _tuiHeadingTable(buffer, heading);
|
|
1780
|
+
const cell = _tuiTableCell(table, row, col);
|
|
1781
|
+
if (!cell) return false;
|
|
1782
|
+
|
|
1783
|
+
const graphemes = _tuiCellGraphemes(value);
|
|
1784
|
+
let grapheme = 0;
|
|
1785
|
+
const replacements = [];
|
|
1786
|
+
for (const { y, separators } of cell.visualLines) {
|
|
1787
|
+
const start = separators[cell.columnIndex] + 2;
|
|
1788
|
+
const end = separators[cell.columnIndex + 1] - 1;
|
|
1789
|
+
const capacity = _tuiStringWidth(
|
|
1790
|
+
String(buffer.lines[y] ?? "").slice(start, end),
|
|
1791
|
+
);
|
|
1792
|
+
let text = "";
|
|
1793
|
+
let width = 0;
|
|
1794
|
+
while (grapheme < graphemes.length) {
|
|
1795
|
+
const next = graphemes[grapheme];
|
|
1796
|
+
const nextWidth = _tuiStringWidth(next);
|
|
1797
|
+
if (nextWidth <= 0) {
|
|
1798
|
+
text += next;
|
|
1799
|
+
grapheme++;
|
|
1800
|
+
continue;
|
|
1801
|
+
}
|
|
1802
|
+
if (width + nextWidth > capacity) break;
|
|
1803
|
+
text += next;
|
|
1804
|
+
width += nextWidth;
|
|
1805
|
+
grapheme++;
|
|
1806
|
+
}
|
|
1807
|
+
replacements.push({
|
|
1808
|
+
y,
|
|
1809
|
+
start,
|
|
1810
|
+
end,
|
|
1811
|
+
text: text + " ".repeat(Math.max(0, capacity - width)),
|
|
1812
|
+
});
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
const before = _tuiTableCellText(buffer, table, row, col);
|
|
1816
|
+
if (before === replacements.map(item => item.text.trimEnd()).join(""))
|
|
1817
|
+
return true;
|
|
1818
|
+
if (!buffer._mdcuiReplayingMutations) buffer.pushUndo?.(true);
|
|
1819
|
+
const ansiLines = typeof buffer._mdcuiAnsiText === "string"
|
|
1820
|
+
? buffer._mdcuiAnsiText.split("\n")
|
|
1821
|
+
: null;
|
|
1822
|
+
for (const replacement of replacements) {
|
|
1823
|
+
const oldLine = String(buffer.lines[replacement.y] ?? "");
|
|
1824
|
+
const styleLine = buffer._ansiStyleLines?.[replacement.y];
|
|
1825
|
+
if (Array.isArray(styleLine)) {
|
|
1826
|
+
styleLine.splice(
|
|
1827
|
+
replacement.start,
|
|
1828
|
+
replacement.end - replacement.start,
|
|
1829
|
+
..._tuiReplacementStyles(
|
|
1830
|
+
oldLine,
|
|
1831
|
+
styleLine,
|
|
1832
|
+
replacement.start,
|
|
1833
|
+
replacement.end,
|
|
1834
|
+
replacement.text,
|
|
1835
|
+
),
|
|
1836
|
+
);
|
|
1837
|
+
}
|
|
1838
|
+
if (ansiLines) {
|
|
1839
|
+
ansiLines[replacement.y] = replaceAnsiPlainRangePreservingControls(
|
|
1840
|
+
ansiLines[replacement.y] ?? "",
|
|
1841
|
+
replacement.start,
|
|
1842
|
+
replacement.end,
|
|
1843
|
+
replacement.text,
|
|
1844
|
+
);
|
|
1845
|
+
}
|
|
1846
|
+
buffer.lines[replacement.y] =
|
|
1847
|
+
oldLine.slice(0, replacement.start)
|
|
1848
|
+
+ replacement.text
|
|
1849
|
+
+ oldLine.slice(replacement.end);
|
|
1850
|
+
buffer.invalidateHighlightFrom?.(replacement.y);
|
|
1851
|
+
}
|
|
1852
|
+
if (ansiLines) buffer._mdcuiAnsiText = ansiLines.join("\n");
|
|
1853
|
+
buffer._mdcuiLinkIndex = null;
|
|
1854
|
+
if (!buffer._mdcuiReplayingMutations) {
|
|
1855
|
+
if (!(buffer._mdcuiDirtyTableCells instanceof Set))
|
|
1856
|
+
buffer._mdcuiDirtyTableCells = new Set();
|
|
1857
|
+
buffer._mdcuiDirtyTableCells.add(`${heading.id}\0${row}\0${col}`);
|
|
1858
|
+
}
|
|
1859
|
+
buffer.modified = true;
|
|
1860
|
+
return true;
|
|
1861
|
+
}
|
|
1862
|
+
|
|
1863
|
+
function _normalizedSpliceRange(length, argumentCount, start, deleteCount) {
|
|
1864
|
+
if (argumentCount === 0) return { start: 0, deleteCount: 0 };
|
|
1865
|
+
let relativeStart = Number(start);
|
|
1866
|
+
if (Number.isNaN(relativeStart)) relativeStart = 0;
|
|
1867
|
+
relativeStart = Math.trunc(relativeStart);
|
|
1868
|
+
const actualStart = relativeStart < 0
|
|
1869
|
+
? Math.max(length + relativeStart, 0)
|
|
1870
|
+
: Math.min(relativeStart, length);
|
|
1871
|
+
if (argumentCount === 1) return { start: actualStart, deleteCount: length - actualStart };
|
|
1872
|
+
let requestedDelete = Number(deleteCount);
|
|
1873
|
+
if (Number.isNaN(requestedDelete)) requestedDelete = 0;
|
|
1874
|
+
requestedDelete = Math.max(0, Math.trunc(requestedDelete));
|
|
1875
|
+
return {
|
|
1876
|
+
start: actualStart,
|
|
1877
|
+
deleteCount: Math.min(requestedDelete, length - actualStart),
|
|
1878
|
+
};
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
function _normalizedTuiTaskItem(input) {
|
|
1882
|
+
const item = input && typeof input === "object"
|
|
1883
|
+
? { value: input.value ?? input.label ?? "", checked: Boolean(input.checked) }
|
|
1884
|
+
: { value: input ?? "", checked: false };
|
|
1885
|
+
return {
|
|
1886
|
+
value: String(item.value).replace(/\r?\n/g, " "),
|
|
1887
|
+
checked: item.checked,
|
|
1888
|
+
};
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
function _snapshotTuiHeadingListMutationArgs(method, args) {
|
|
1892
|
+
let snapshot;
|
|
1893
|
+
if (method === "splice") {
|
|
1894
|
+
snapshot = [];
|
|
1895
|
+
if (args.length > 0) snapshot.push(Number(args[0]));
|
|
1896
|
+
if (args.length > 1) snapshot.push(Number(args[1]));
|
|
1897
|
+
snapshot.push(...args.slice(2).map(_normalizedTuiTaskItem));
|
|
1898
|
+
} else if (method === "push" || method === "unshift") {
|
|
1899
|
+
snapshot = args.map(_normalizedTuiTaskItem);
|
|
1900
|
+
} else {
|
|
1901
|
+
snapshot = [];
|
|
1902
|
+
}
|
|
1903
|
+
for (const item of snapshot) {
|
|
1904
|
+
if (item && typeof item === "object") Object.freeze(item);
|
|
1905
|
+
}
|
|
1906
|
+
return Object.freeze(snapshot);
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
function _tuiTaskItemReplacement(indent, inputs) {
|
|
1910
|
+
const normalized = inputs.map(_normalizedTuiTaskItem);
|
|
1911
|
+
return {
|
|
1912
|
+
lines: normalized.map((item) =>
|
|
1913
|
+
`${indent}${item.checked ? "☒" : "☐"} ${item.value}`,
|
|
1914
|
+
),
|
|
1915
|
+
styles: normalized.map((item) => {
|
|
1916
|
+
const styles = [];
|
|
1917
|
+
if (item.checked) {
|
|
1918
|
+
styles[indent.length] = { fg: "green" };
|
|
1919
|
+
styles[indent.length + 1] = { fg: "green" };
|
|
1920
|
+
}
|
|
1921
|
+
return styles;
|
|
1922
|
+
}),
|
|
1923
|
+
ansi: normalized.map((item) =>
|
|
1924
|
+
`${indent}\x1b[${item.checked ? "32" : "2"}m${item.checked ? "☒" : "☐"} \x1b[0m${item.value}`,
|
|
1925
|
+
),
|
|
1926
|
+
};
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
function _mutateTuiHeadingList(buffer, heading, method, args, resolvedList = null) {
|
|
1930
|
+
const list = resolvedList ?? _tuiHeadingTaskList(buffer, heading);
|
|
1931
|
+
if (!list) {
|
|
1932
|
+
if (method === "push" || method === "unshift") return 0;
|
|
1933
|
+
if (method === "splice") return [];
|
|
1934
|
+
return undefined;
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
if (method === "splice") {
|
|
1938
|
+
const range = _normalizedSpliceRange(list.items.length, args.length, args[0], args[1]);
|
|
1939
|
+
const removedItems = list.items.slice(range.start, range.start + range.deleteCount);
|
|
1940
|
+
const removed = removedItems.map((item) => item.value);
|
|
1941
|
+
const replacement = _tuiTaskItemReplacement(list.indent, args.slice(2));
|
|
1942
|
+
if (range.deleteCount === 0 && replacement.lines.length === 0) return [];
|
|
1943
|
+
const start = list.items[range.start]?.start ?? list.end;
|
|
1944
|
+
const end = removedItems.at(-1)?.end ?? start;
|
|
1945
|
+
if (range.deleteCount === list.items.length && replacement.lines.length === 0 && list.items.length > 0) {
|
|
1946
|
+
if (!(buffer._mdcuiHeadingTaskListAnchors instanceof Map))
|
|
1947
|
+
buffer._mdcuiHeadingTaskListAnchors = new Map();
|
|
1948
|
+
buffer._mdcuiHeadingTaskListAnchors.set(heading.ordinal, { index: start, indent: list.indent });
|
|
1949
|
+
}
|
|
1950
|
+
spliceTuiBufferLines(buffer, start, end - start, replacement.lines, {
|
|
1951
|
+
styles: replacement.styles,
|
|
1952
|
+
ansi: replacement.ansi,
|
|
1953
|
+
});
|
|
1954
|
+
if (replacement.lines.length > 0)
|
|
1955
|
+
buffer._mdcuiHeadingTaskListAnchors?.delete(heading.ordinal);
|
|
1956
|
+
return removed;
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1959
|
+
if (method === "pop" || method === "shift") {
|
|
1960
|
+
const item = method === "pop" ? list.items.at(-1) : list.items[0];
|
|
1961
|
+
if (!item) return undefined;
|
|
1962
|
+
if (list.items.length === 1) {
|
|
1963
|
+
if (!(buffer._mdcuiHeadingTaskListAnchors instanceof Map))
|
|
1964
|
+
buffer._mdcuiHeadingTaskListAnchors = new Map();
|
|
1965
|
+
buffer._mdcuiHeadingTaskListAnchors.set(heading.ordinal, {
|
|
1966
|
+
index: item.start,
|
|
1967
|
+
indent: list.indent,
|
|
1968
|
+
});
|
|
1969
|
+
}
|
|
1970
|
+
spliceTuiBufferLines(buffer, item.start, item.end - item.start, []);
|
|
1971
|
+
return item.value;
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
const replacement = _tuiTaskItemReplacement(list.indent, args);
|
|
1975
|
+
if (replacement.lines.length === 0) return list.items.length;
|
|
1976
|
+
const start = method === "push" ? list.end : list.first;
|
|
1977
|
+
spliceTuiBufferLines(buffer, start, 0, replacement.lines, {
|
|
1978
|
+
styles: replacement.styles,
|
|
1979
|
+
ansi: replacement.ansi,
|
|
1980
|
+
});
|
|
1981
|
+
if (replacement.lines.length > 0) buffer._mdcuiHeadingTaskListAnchors?.delete(heading.ordinal);
|
|
1982
|
+
return list.items.length + replacement.lines.length;
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1985
|
+
function _recordTuiHeadingListMutation(buffer, selector, method, args) {
|
|
1986
|
+
if (!buffer || buffer._mdcuiReplayingMutations) return;
|
|
1987
|
+
if (!Array.isArray(args)) return;
|
|
1988
|
+
if (!Array.isArray(buffer._mdcuiMutationMacros))
|
|
1989
|
+
buffer._mdcuiMutationMacros = [];
|
|
1990
|
+
buffer._mdcuiMutationMacros.push({
|
|
1991
|
+
selector: String(selector),
|
|
1992
|
+
method,
|
|
1993
|
+
args,
|
|
1994
|
+
});
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
function _mutateAndRecordTuiHeadingList(buffer, heading, selector, method, args, failureValue) {
|
|
1998
|
+
if (!buffer || !heading) return failureValue;
|
|
1999
|
+
if (
|
|
2000
|
+
_headingHasHiddenTuiAncestor(buffer, heading) ||
|
|
2001
|
+
_tuiIdStore(buffer).get(heading.id)?.headingVisibility?.hidden
|
|
2002
|
+
)
|
|
2003
|
+
return failureValue;
|
|
2004
|
+
const list = _tuiHeadingTaskList(buffer, heading);
|
|
2005
|
+
if (!list) return failureValue;
|
|
2006
|
+
const snapshotArgs = _snapshotTuiHeadingListMutationArgs(method, args);
|
|
2007
|
+
const before = Array.isArray(buffer.lines) ? buffer.lines.join("\n") : "";
|
|
2008
|
+
const result = _mutateTuiHeadingList(buffer, heading, method, snapshotArgs, list);
|
|
2009
|
+
const after = Array.isArray(buffer.lines) ? buffer.lines.join("\n") : "";
|
|
2010
|
+
if (after !== before)
|
|
2011
|
+
_recordTuiHeadingListMutation(buffer, selector, method, snapshotArgs);
|
|
2012
|
+
return result;
|
|
2013
|
+
}
|
|
2014
|
+
|
|
2015
|
+
export function replayTuiMutationMacros(buffer) {
|
|
2016
|
+
if (!buffer || !Array.isArray(buffer._mdcuiMutationMacros)) return;
|
|
2017
|
+
buffer._mdcuiReplayingMutations = true;
|
|
2018
|
+
try {
|
|
2019
|
+
for (const macro of buffer._mdcuiMutationMacros) {
|
|
2020
|
+
if (
|
|
2021
|
+
!macro
|
|
2022
|
+
|| !["push", "pop", "shift", "unshift", "splice"].includes(macro.method)
|
|
2023
|
+
) continue;
|
|
2024
|
+
let args;
|
|
2025
|
+
if (Array.isArray(macro.args)) args = macro.args;
|
|
2026
|
+
else {
|
|
2027
|
+
try {
|
|
2028
|
+
args = JSON.parse(macro.argsJson);
|
|
2029
|
+
} catch {
|
|
2030
|
+
continue;
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
if (!Array.isArray(args)) continue;
|
|
2034
|
+
const heading = _findHeading(buffer, macro.selector);
|
|
2035
|
+
if (heading) _mutateTuiHeadingList(buffer, heading, macro.method, args);
|
|
2036
|
+
}
|
|
2037
|
+
} finally {
|
|
2038
|
+
buffer._mdcuiReplayingMutations = false;
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
|
|
2042
|
+
function _tuiBlockSignature(block) {
|
|
2043
|
+
const header = block.header;
|
|
2044
|
+
return JSON.stringify([
|
|
2045
|
+
header.kind,
|
|
2046
|
+
header.tag,
|
|
2047
|
+
header.id,
|
|
2048
|
+
header.classes,
|
|
2049
|
+
]);
|
|
2050
|
+
}
|
|
2051
|
+
|
|
2052
|
+
function _captureTuiFenceBlocks(buffer) {
|
|
2053
|
+
const ansiLines = typeof buffer?._mdcuiAnsiText === "string"
|
|
2054
|
+
? buffer._mdcuiAnsiText.split("\n")
|
|
2055
|
+
: null;
|
|
2056
|
+
return buildTuiBlockIndex(buffer?.lines ?? []).map((block) => ({
|
|
2057
|
+
signature: _tuiBlockSignature(block),
|
|
2058
|
+
lines: buffer.lines.slice(block.start + 1, block.end),
|
|
2059
|
+
styles: Array.isArray(buffer._ansiStyleLines)
|
|
2060
|
+
? buffer._ansiStyleLines.slice(block.start + 1, block.end)
|
|
2061
|
+
: null,
|
|
2062
|
+
ansi: ansiLines?.slice(block.start + 1, block.end) ?? null,
|
|
2063
|
+
}));
|
|
2064
|
+
}
|
|
2065
|
+
|
|
2066
|
+
function _restoreTuiFenceBlocks(buffer, snapshots) {
|
|
2067
|
+
if (!Array.isArray(snapshots) || snapshots.length === 0) return;
|
|
2068
|
+
const blocks = buildTuiBlockIndex(buffer?.lines ?? []);
|
|
2069
|
+
if (
|
|
2070
|
+
blocks.length !== snapshots.length
|
|
2071
|
+
|| blocks.some((block, index) => _tuiBlockSignature(block) !== snapshots[index].signature)
|
|
2072
|
+
) {
|
|
2073
|
+
buffer._mdcuiRerenderMismatch = {
|
|
2074
|
+
...(buffer._mdcuiRerenderMismatch ?? {}),
|
|
2075
|
+
fenceBlocks: { before: snapshots.length, after: blocks.length },
|
|
2076
|
+
};
|
|
2077
|
+
return;
|
|
2078
|
+
}
|
|
2079
|
+
for (let index = blocks.length - 1; index >= 0; index--) {
|
|
2080
|
+
const block = blocks[index];
|
|
2081
|
+
const snapshot = snapshots[index];
|
|
2082
|
+
spliceTuiBufferLines(
|
|
2083
|
+
buffer,
|
|
2084
|
+
block.start + 1,
|
|
2085
|
+
Math.max(0, block.end - block.start - 1),
|
|
2086
|
+
snapshot.lines,
|
|
2087
|
+
{
|
|
2088
|
+
styles: snapshot.styles ?? undefined,
|
|
2089
|
+
ansi: snapshot.ansi ?? undefined,
|
|
2090
|
+
},
|
|
2091
|
+
);
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
function _tuiCheckboxRows(buffer) {
|
|
2096
|
+
const rows = [];
|
|
2097
|
+
const ansiLines = typeof buffer?._mdcuiAnsiText === "string"
|
|
2098
|
+
? buffer._mdcuiAnsiText.split("\n")
|
|
2099
|
+
: null;
|
|
2100
|
+
for (let y = 0; y < (buffer?.lines?.length ?? 0); y++) {
|
|
2101
|
+
const line = String(buffer.lines[y] ?? "");
|
|
2102
|
+
for (const match of line.matchAll(/[☐☒]/gu)) {
|
|
2103
|
+
const style = buffer._ansiStyleLines?.[y]?.[match.index] ?? null;
|
|
2104
|
+
const column = globalThis.Bun?.stringWidth
|
|
2105
|
+
? Bun.stringWidth(line.slice(0, match.index))
|
|
2106
|
+
: match.index;
|
|
2107
|
+
rows.push({
|
|
2108
|
+
y,
|
|
2109
|
+
x: match.index,
|
|
2110
|
+
checked: match[0] === "☒",
|
|
2111
|
+
style: style ? { ...style } : null,
|
|
2112
|
+
ansi: ansiLines && globalThis.Bun?.sliceAnsi
|
|
2113
|
+
? Bun.sliceAnsi(ansiLines[y] ?? "", column, column + 1)
|
|
2114
|
+
: null,
|
|
2115
|
+
});
|
|
2116
|
+
}
|
|
2117
|
+
}
|
|
2118
|
+
return rows;
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
function _restoreTuiCheckboxStates(buffer, states) {
|
|
2122
|
+
const rows = _tuiCheckboxRows(buffer);
|
|
2123
|
+
if (rows.length !== states.length) {
|
|
2124
|
+
buffer._mdcuiRerenderMismatch = {
|
|
2125
|
+
...(buffer._mdcuiRerenderMismatch ?? {}),
|
|
2126
|
+
checkboxes: { before: states.length, after: rows.length },
|
|
2127
|
+
};
|
|
2128
|
+
return;
|
|
2129
|
+
}
|
|
2130
|
+
const ansiLines = typeof buffer._mdcuiAnsiText === "string"
|
|
2131
|
+
? buffer._mdcuiAnsiText.split("\n")
|
|
2132
|
+
: null;
|
|
2133
|
+
for (let index = 0; index < rows.length; index++) {
|
|
2134
|
+
const row = rows[index];
|
|
2135
|
+
const state = states[index];
|
|
2136
|
+
const checked = typeof state === "object" && state !== null
|
|
2137
|
+
? Boolean(state.checked)
|
|
2138
|
+
: Boolean(state);
|
|
2139
|
+
const glyph = checked ? "☒" : "☐";
|
|
2140
|
+
const styleLine = buffer._ansiStyleLines?.[row.y];
|
|
2141
|
+
if (Array.isArray(styleLine)) {
|
|
2142
|
+
const style = typeof state === "object" && state !== null
|
|
2143
|
+
? state.style
|
|
2144
|
+
: (checked ? { fg: "green" } : null);
|
|
2145
|
+
styleLine[row.x] = style ? { ...style } : null;
|
|
2146
|
+
}
|
|
2147
|
+
if (ansiLines) {
|
|
2148
|
+
if (
|
|
2149
|
+
typeof state === "object"
|
|
2150
|
+
&& state !== null
|
|
2151
|
+
&& typeof state.ansi === "string"
|
|
2152
|
+
&& globalThis.Bun?.sliceAnsi
|
|
2153
|
+
) {
|
|
2154
|
+
const line = String(buffer.lines[row.y] ?? "");
|
|
2155
|
+
const column = globalThis.Bun?.stringWidth
|
|
2156
|
+
? Bun.stringWidth(line.slice(0, row.x))
|
|
2157
|
+
: row.x;
|
|
2158
|
+
ansiLines[row.y] =
|
|
2159
|
+
Bun.sliceAnsi(ansiLines[row.y], 0, column)
|
|
2160
|
+
+ state.ansi
|
|
2161
|
+
+ Bun.sliceAnsi(ansiLines[row.y], column + 1);
|
|
2162
|
+
} else {
|
|
2163
|
+
ansiLines[row.y] = updateAnsiTaskCheckbox(ansiLines[row.y], row.x, checked);
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
if (buffer.lines[row.y][row.x] !== glyph) {
|
|
2167
|
+
buffer.lines[row.y] =
|
|
2168
|
+
buffer.lines[row.y].slice(0, row.x) + glyph + buffer.lines[row.y].slice(row.x + 1);
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
if (ansiLines) buffer._mdcuiAnsiText = ansiLines.join("\n");
|
|
2172
|
+
}
|
|
2173
|
+
|
|
2174
|
+
export function tuiCheckboxRerenderMismatchMessage(buffer) {
|
|
2175
|
+
const mismatch = buffer?._mdcuiRerenderMismatch?.checkboxes;
|
|
2176
|
+
if (!mismatch) return "";
|
|
2177
|
+
const name = String(buffer?.name ?? "").trim();
|
|
2178
|
+
const location = name ? ` in ${name}` : "";
|
|
2179
|
+
return `Checkbox state restore skipped${location}: count changed from ${mismatch.before} to ${mismatch.after}`;
|
|
2180
|
+
}
|
|
2181
|
+
|
|
2182
|
+
function _captureTuiTables(buffer) {
|
|
2183
|
+
const snapshots = [];
|
|
2184
|
+
const dirty = buffer?._mdcuiDirtyTableCells;
|
|
2185
|
+
if (!(dirty instanceof Set) || dirty.size === 0) return snapshots;
|
|
2186
|
+
const ansiLines = typeof buffer?._mdcuiAnsiText === "string"
|
|
2187
|
+
? buffer._mdcuiAnsiText.split("\n")
|
|
2188
|
+
: null;
|
|
2189
|
+
for (const heading of _tuiSourceHeadings(buffer)) {
|
|
2190
|
+
const table = _tuiHeadingTable(buffer, heading);
|
|
2191
|
+
if (!table) continue;
|
|
2192
|
+
snapshots.push({
|
|
2193
|
+
id: heading.id,
|
|
2194
|
+
cells: table.rows.map((visualLines, row) => {
|
|
2195
|
+
const columns = visualLines[0]?.separators?.length - 1;
|
|
2196
|
+
return Array.from(
|
|
2197
|
+
{ length: Math.max(0, columns) },
|
|
2198
|
+
(_, col) => {
|
|
2199
|
+
if (!dirty.has(`${heading.id}\0${row}\0${col}`)) return null;
|
|
2200
|
+
const ansi = [];
|
|
2201
|
+
const styles = [];
|
|
2202
|
+
for (const { y, separators } of visualLines) {
|
|
2203
|
+
const start = separators[col] + 2;
|
|
2204
|
+
const end = separators[col + 1] - 1;
|
|
2205
|
+
const content = String(buffer.lines[y] ?? "")
|
|
2206
|
+
.slice(start, end).trimEnd();
|
|
2207
|
+
const width = _tuiStringWidth(content);
|
|
2208
|
+
if (
|
|
2209
|
+
width > 0
|
|
2210
|
+
&& ansiLines
|
|
2211
|
+
&& typeof globalThis.Bun?.sliceAnsi === "function"
|
|
2212
|
+
) {
|
|
2213
|
+
const displayStart = _tuiStringWidth(
|
|
2214
|
+
String(buffer.lines[y] ?? "").slice(0, start),
|
|
2215
|
+
);
|
|
2216
|
+
ansi.push(Bun.sliceAnsi(
|
|
2217
|
+
ansiLines[y] ?? "",
|
|
2218
|
+
displayStart,
|
|
2219
|
+
displayStart + width,
|
|
2220
|
+
));
|
|
2221
|
+
} else {
|
|
2222
|
+
ansi.push(content);
|
|
2223
|
+
}
|
|
2224
|
+
if (Array.isArray(buffer._ansiStyleLines?.[y])) {
|
|
2225
|
+
styles.push(
|
|
2226
|
+
...buffer._ansiStyleLines[y]
|
|
2227
|
+
.slice(start, start + content.length)
|
|
2228
|
+
.map(style => style ? { ...style } : null),
|
|
2229
|
+
);
|
|
2230
|
+
} else {
|
|
2231
|
+
styles.push(...Array.from(
|
|
2232
|
+
{ length: content.length },
|
|
2233
|
+
() => null,
|
|
2234
|
+
));
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
return {
|
|
2238
|
+
text: _tuiTableCellText(buffer, table, row, col),
|
|
2239
|
+
ansi: ansi.join(""),
|
|
2240
|
+
styles,
|
|
2241
|
+
};
|
|
2242
|
+
},
|
|
2243
|
+
);
|
|
2244
|
+
}),
|
|
2245
|
+
});
|
|
2246
|
+
}
|
|
2247
|
+
return snapshots;
|
|
2248
|
+
}
|
|
2249
|
+
|
|
2250
|
+
function _restoreTuiTableCellAnsi(buffer, table, row, col, state) {
|
|
2251
|
+
if (!state || typeof state !== "object") return;
|
|
2252
|
+
const cell = _tuiTableCell(table, row, col);
|
|
2253
|
+
if (!cell) return;
|
|
2254
|
+
const ansiLines = typeof buffer._mdcuiAnsiText === "string"
|
|
2255
|
+
? buffer._mdcuiAnsiText.split("\n")
|
|
2256
|
+
: null;
|
|
2257
|
+
let displayOffset = 0;
|
|
2258
|
+
let characterOffset = 0;
|
|
2259
|
+
|
|
2260
|
+
for (const { y, separators } of cell.visualLines) {
|
|
2261
|
+
const start = separators[col] + 2;
|
|
2262
|
+
const end = separators[col + 1] - 1;
|
|
2263
|
+
const content = String(buffer.lines[y] ?? "")
|
|
2264
|
+
.slice(start, end).trimEnd();
|
|
2265
|
+
const width = _tuiStringWidth(content);
|
|
2266
|
+
if (
|
|
2267
|
+
ansiLines
|
|
2268
|
+
&& typeof state.ansi === "string"
|
|
2269
|
+
&& typeof globalThis.Bun?.sliceAnsi === "function"
|
|
2270
|
+
) {
|
|
2271
|
+
ansiLines[y] = replaceAnsiPlainRange(
|
|
2272
|
+
ansiLines[y] ?? "",
|
|
2273
|
+
start,
|
|
2274
|
+
start + content.length,
|
|
2275
|
+
Bun.sliceAnsi(
|
|
2276
|
+
state.ansi,
|
|
2277
|
+
displayOffset,
|
|
2278
|
+
displayOffset + width,
|
|
2279
|
+
),
|
|
2280
|
+
);
|
|
2281
|
+
}
|
|
2282
|
+
const styleLine = buffer._ansiStyleLines?.[y];
|
|
2283
|
+
if (Array.isArray(styleLine) && Array.isArray(state.styles)) {
|
|
2284
|
+
styleLine.splice(
|
|
2285
|
+
start,
|
|
2286
|
+
content.length,
|
|
2287
|
+
...state.styles
|
|
2288
|
+
.slice(characterOffset, characterOffset + content.length)
|
|
2289
|
+
.map(style => style ? { ...style } : null),
|
|
2290
|
+
);
|
|
2291
|
+
}
|
|
2292
|
+
displayOffset += width;
|
|
2293
|
+
characterOffset += content.length;
|
|
2294
|
+
}
|
|
2295
|
+
if (ansiLines) buffer._mdcuiAnsiText = ansiLines.join("\n");
|
|
2296
|
+
}
|
|
2297
|
+
|
|
2298
|
+
function _restoreTuiTables(buffer, snapshots) {
|
|
2299
|
+
if (!Array.isArray(snapshots)) return;
|
|
2300
|
+
const headings = new Map(
|
|
2301
|
+
_tuiSourceHeadings(buffer).map(heading => [heading.id, heading]),
|
|
2302
|
+
);
|
|
2303
|
+
const resolved = snapshots.map((snapshot) => {
|
|
2304
|
+
const heading = headings.get(snapshot.id);
|
|
2305
|
+
const table = _tuiHeadingTable(buffer, heading);
|
|
2306
|
+
return { snapshot, heading, table };
|
|
2307
|
+
});
|
|
2308
|
+
const mismatch = resolved.some(({ snapshot, table }) =>
|
|
2309
|
+
!table
|
|
2310
|
+
|| table.rows.length !== snapshot.cells.length
|
|
2311
|
+
|| snapshot.cells.some((cells, row) =>
|
|
2312
|
+
(table.rows[row]?.[0]?.separators?.length - 1) !== cells.length
|
|
2313
|
+
)
|
|
2314
|
+
);
|
|
2315
|
+
if (mismatch) {
|
|
2316
|
+
buffer._mdcuiRerenderMismatch = {
|
|
2317
|
+
...(buffer._mdcuiRerenderMismatch ?? {}),
|
|
2318
|
+
tableCells: {
|
|
2319
|
+
before: snapshots.length,
|
|
2320
|
+
after: resolved.filter(item => item.table).length,
|
|
2321
|
+
},
|
|
2322
|
+
};
|
|
2323
|
+
return;
|
|
2324
|
+
}
|
|
2325
|
+
|
|
2326
|
+
const replaying = buffer._mdcuiReplayingMutations;
|
|
2327
|
+
buffer._mdcuiReplayingMutations = true;
|
|
2328
|
+
try {
|
|
2329
|
+
for (const { snapshot, heading } of resolved) {
|
|
2330
|
+
for (let row = 0; row < snapshot.cells.length; row++) {
|
|
2331
|
+
for (let col = 0; col < snapshot.cells[row].length; col++) {
|
|
2332
|
+
const state = snapshot.cells[row][col];
|
|
2333
|
+
if (state == null) continue;
|
|
2334
|
+
_setTuiTableCell(
|
|
2335
|
+
buffer,
|
|
2336
|
+
heading,
|
|
2337
|
+
row,
|
|
2338
|
+
col,
|
|
2339
|
+
typeof state === "object" && state !== null
|
|
2340
|
+
? state.text
|
|
2341
|
+
: state,
|
|
2342
|
+
);
|
|
2343
|
+
const table = _tuiHeadingTable(buffer, heading);
|
|
2344
|
+
_restoreTuiTableCellAnsi(buffer, table, row, col, state);
|
|
2345
|
+
}
|
|
2346
|
+
}
|
|
2347
|
+
}
|
|
2348
|
+
} finally {
|
|
2349
|
+
buffer._mdcuiReplayingMutations = replaying;
|
|
2350
|
+
}
|
|
2351
|
+
}
|
|
2352
|
+
|
|
2353
|
+
export function captureTuiRerenderState(buffer) {
|
|
2354
|
+
const headings = new Map(_tuiSourceHeadings(buffer).map((heading) => [heading.id, heading]));
|
|
2355
|
+
const hiddenHeadings = [];
|
|
2356
|
+
if (buffer?._mdcuiIdStore instanceof Map) {
|
|
2357
|
+
for (const [id, record] of buffer._mdcuiIdStore) {
|
|
2358
|
+
if (!record?.headingVisibility?.hidden) continue;
|
|
2359
|
+
const heading = headings.get(id);
|
|
2360
|
+
if (heading) hiddenHeadings.push({ id, level: heading.level, ordinal: heading.ordinal });
|
|
2361
|
+
}
|
|
2362
|
+
}
|
|
2363
|
+
|
|
2364
|
+
// Outer sections must be shown first so nested heading handles become visible.
|
|
2365
|
+
for (const item of hiddenHeadings.slice().sort((a, b) =>
|
|
2366
|
+
a.level - b.level || a.ordinal - b.ordinal
|
|
2367
|
+
)) {
|
|
2368
|
+
_showTuiHeadingSection(buffer, headings.get(item.id), item.id);
|
|
2369
|
+
}
|
|
2370
|
+
|
|
2371
|
+
return {
|
|
2372
|
+
hiddenHeadings,
|
|
2373
|
+
tableCells: _captureTuiTables(buffer),
|
|
2374
|
+
checkboxStates: _tuiCheckboxRows(buffer).map(({ checked, style, ansi }) => ({
|
|
2375
|
+
checked,
|
|
2376
|
+
style,
|
|
2377
|
+
ansi,
|
|
2378
|
+
})),
|
|
2379
|
+
fenceBlocks: _captureTuiFenceBlocks(buffer),
|
|
2380
|
+
};
|
|
2381
|
+
}
|
|
2382
|
+
|
|
2383
|
+
export function restoreTuiRerenderState(buffer, snapshot) {
|
|
2384
|
+
if (!buffer || !snapshot) return;
|
|
2385
|
+
buffer._mdcuiRerenderMismatch = null;
|
|
2386
|
+
replayTuiMutationMacros(buffer);
|
|
2387
|
+
_restoreTuiTables(buffer, snapshot.tableCells);
|
|
2388
|
+
_restoreTuiFenceBlocks(buffer, snapshot.fenceBlocks);
|
|
2389
|
+
_restoreTuiCheckboxStates(buffer, snapshot.checkboxStates ?? []);
|
|
2390
|
+
|
|
2391
|
+
restoreTuiHiddenHeadings(buffer, snapshot.hiddenHeadings);
|
|
2392
|
+
}
|
|
2393
|
+
|
|
2394
|
+
export function restoreTuiHiddenHeadings(buffer, hiddenHeadings) {
|
|
2395
|
+
const headings = new Map(_tuiSourceHeadings(buffer).map((heading) => [heading.id, heading]));
|
|
2396
|
+
// Nested sections are hidden first; an outer hide may then safely contain them.
|
|
2397
|
+
for (const item of (hiddenHeadings ?? []).slice().sort((a, b) =>
|
|
2398
|
+
b.level - a.level || b.ordinal - a.ordinal
|
|
2399
|
+
)) {
|
|
2400
|
+
_hideTuiHeadingSection(buffer, headings.get(item.id), item.id);
|
|
2401
|
+
}
|
|
2402
|
+
}
|
|
2403
|
+
|
|
2404
|
+
export function spliceTuiBufferLines(buffer, start, deleteCount, replacement, replacementMeta = {}) {
|
|
2405
|
+
const oldCursor = buffer.cursor ? { ...buffer.cursor } : null;
|
|
2406
|
+
const headingIndex = _cachedTuiHeadingRowIndex(buffer);
|
|
2407
|
+
const oldEnd = start + deleteCount;
|
|
2408
|
+
const delta = replacement.length - deleteCount;
|
|
2409
|
+
buffer._mdcuiFenceBlockIndex = null;
|
|
2410
|
+
buffer._mdcuiControlBlockIndex = null;
|
|
2411
|
+
buffer.lines.splice(start, deleteCount, ...replacement);
|
|
2412
|
+
|
|
2413
|
+
if (Array.isArray(buffer._ansiStyleLines)) {
|
|
2414
|
+
const template = buffer._ansiStyleLines[start] ?? null;
|
|
2415
|
+
buffer._ansiStyleLines.splice(
|
|
2416
|
+
start,
|
|
2417
|
+
deleteCount,
|
|
2418
|
+
...(replacementMeta.styles ?? replacement.map(() => template)),
|
|
2419
|
+
);
|
|
2420
|
+
}
|
|
2421
|
+
|
|
2422
|
+
if (typeof buffer._mdcuiAnsiText === "string") {
|
|
2423
|
+
const ansiLines = buffer._mdcuiAnsiText.split("\n");
|
|
2424
|
+
ansiLines.splice(start, deleteCount, ...(replacementMeta.ansi ?? replacement));
|
|
2425
|
+
buffer._mdcuiAnsiText = ansiLines.join("\n");
|
|
2426
|
+
}
|
|
2427
|
+
if (Array.isArray(buffer._mdcuiImages)) {
|
|
2428
|
+
buffer._mdcuiImages = buffer._mdcuiImages
|
|
2429
|
+
.filter((image) => image.line < start || image.line >= start + deleteCount)
|
|
2430
|
+
.map((image) => image.line >= start + deleteCount ? { ...image, line: image.line + delta } : image);
|
|
2431
|
+
}
|
|
2432
|
+
if (buffer._mdcuiHeadingTaskListAnchors instanceof Map) {
|
|
2433
|
+
for (const anchor of buffer._mdcuiHeadingTaskListAnchors.values()) {
|
|
2434
|
+
if (anchor.index >= oldEnd) anchor.index += delta;
|
|
2435
|
+
else if (anchor.index >= start) anchor.index = start;
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2438
|
+
if (headingIndex) {
|
|
2439
|
+
const entries = headingIndex.entries
|
|
2440
|
+
.filter((heading) => heading.row < start || heading.row >= oldEnd)
|
|
2441
|
+
.map((heading) => heading.row >= oldEnd
|
|
2442
|
+
? { ...heading, row: heading.row + delta }
|
|
2443
|
+
: heading
|
|
2444
|
+
);
|
|
2445
|
+
for (const heading of replacementMeta.headings ?? []) {
|
|
2446
|
+
if (
|
|
2447
|
+
!heading
|
|
2448
|
+
|| !Number.isInteger(heading.row)
|
|
2449
|
+
|| heading.row < 0
|
|
2450
|
+
|| heading.row >= replacement.length
|
|
2451
|
+
) continue;
|
|
2452
|
+
entries.push({ ...heading, row: start + heading.row });
|
|
2453
|
+
}
|
|
2454
|
+
_replaceTuiHeadingRowEntries(buffer, headingIndex, entries);
|
|
2455
|
+
}
|
|
2456
|
+
|
|
2457
|
+
buffer.invalidateHighlightFrom?.(start, { force: replacement.length !== deleteCount });
|
|
2458
|
+
if (oldCursor) {
|
|
2459
|
+
if (oldCursor.y >= oldEnd) {
|
|
2460
|
+
buffer.cursor.y = oldCursor.y + replacement.length - deleteCount;
|
|
2461
|
+
} else if (oldCursor.y >= start) {
|
|
2462
|
+
const relativeY = Math.min(oldCursor.y - start, Math.max(0, replacement.length - 1));
|
|
2463
|
+
buffer.cursor.y = start + relativeY;
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
if (replacementMeta.markModified !== false) buffer.modified = true;
|
|
2467
|
+
buffer.ensureCursor?.();
|
|
2468
|
+
}
|
|
2469
|
+
|
|
2470
|
+
export function insertTuiTextareaNewline(buffer, block) {
|
|
2471
|
+
if (!buffer || block?.header?.tag !== "textarea") return false;
|
|
2472
|
+
const prefix = block.header.indent + block.header.bodyMarker + " ";
|
|
2473
|
+
const line = String(buffer.lines?.[buffer.cursor.y] ?? "");
|
|
2474
|
+
const left = line.slice(0, buffer.cursor.x);
|
|
2475
|
+
const right = line.slice(buffer.cursor.x);
|
|
2476
|
+
buffer.lines[buffer.cursor.y] = left;
|
|
2477
|
+
spliceTuiBufferLines(buffer, buffer.cursor.y + 1, 0, [prefix + right]);
|
|
2478
|
+
buffer.cursor.y++;
|
|
2479
|
+
buffer.cursor.x = prefix.length;
|
|
2480
|
+
buffer.ensureCursor?.();
|
|
2481
|
+
return true;
|
|
2482
|
+
}
|
|
2483
|
+
|
|
2484
|
+
export function mergeTuiTextareaBackward(buffer, block) {
|
|
2485
|
+
if (!buffer || block?.header?.tag !== "textarea") return false;
|
|
2486
|
+
const prefix = block.header.indent + block.header.bodyMarker + " ";
|
|
2487
|
+
if (buffer.cursor.x > prefix.length || buffer.cursor.y <= block.start + 1) return false;
|
|
2488
|
+
const previousY = buffer.cursor.y - 1;
|
|
2489
|
+
const previousLength = String(buffer.lines?.[previousY] ?? "").length;
|
|
2490
|
+
const line = String(buffer.lines?.[buffer.cursor.y] ?? "");
|
|
2491
|
+
const body = line.startsWith(prefix) ? line.slice(prefix.length) : line;
|
|
2492
|
+
buffer.lines[previousY] += body;
|
|
2493
|
+
spliceTuiBufferLines(buffer, buffer.cursor.y, 1, []);
|
|
2494
|
+
buffer.cursor.y = previousY;
|
|
2495
|
+
buffer.cursor.x = previousLength;
|
|
2496
|
+
buffer.ensureCursor?.();
|
|
2497
|
+
return true;
|
|
2498
|
+
}
|
|
2499
|
+
|
|
2500
|
+
export function mergeTuiTextareaForward(buffer, block) {
|
|
2501
|
+
if (!buffer || block?.header?.tag !== "textarea") return false;
|
|
2502
|
+
const line = String(buffer.lines?.[buffer.cursor.y] ?? "");
|
|
2503
|
+
if (buffer.cursor.x < line.length || buffer.cursor.y >= block.end - 1) return false;
|
|
2504
|
+
const prefix = block.header.indent + block.header.bodyMarker + " ";
|
|
2505
|
+
const nextLine = String(buffer.lines?.[buffer.cursor.y + 1] ?? "");
|
|
2506
|
+
const body = nextLine.startsWith(prefix) ? nextLine.slice(prefix.length) : nextLine;
|
|
2507
|
+
buffer.lines[buffer.cursor.y] += body;
|
|
2508
|
+
spliceTuiBufferLines(buffer, buffer.cursor.y + 1, 1, []);
|
|
2509
|
+
buffer.ensureCursor?.();
|
|
2510
|
+
return true;
|
|
2511
|
+
}
|
|
2512
|
+
|
|
2513
|
+
function _setBlockValue(buffer, selector, value) {
|
|
2514
|
+
const lines = buffer.lines;
|
|
2515
|
+
const block = _findBlock(lines, selector);
|
|
2516
|
+
if (!block) return false;
|
|
2517
|
+
|
|
2518
|
+
const normalizedValue = String(value ?? "").replace(/\r\n?/g, "\n");
|
|
2519
|
+
if ((_blockValue(lines, selector) ?? "") === normalizedValue) return true;
|
|
2520
|
+
buffer.pushUndo?.(true);
|
|
2521
|
+
const values = normalizedValue.split("\n");
|
|
2522
|
+
const contentStart = block.start + 1;
|
|
2523
|
+
const capacity = Math.max(0, block.end - contentStart);
|
|
2524
|
+
|
|
2525
|
+
if (block.header.kind === "fenced") {
|
|
2526
|
+
const replacement = values.map((line) => block.header.indent + line);
|
|
2527
|
+
spliceTuiBufferLines(buffer, contentStart, capacity, replacement);
|
|
2528
|
+
return true;
|
|
2529
|
+
}
|
|
2530
|
+
|
|
2531
|
+
const rowPrefix = block.header.indent + block.header.bodyMarker + " ";
|
|
2532
|
+
const replacement = values.map((line) => rowPrefix + line);
|
|
2533
|
+
spliceTuiBufferLines(buffer, contentStart, capacity, replacement);
|
|
2534
|
+
return true;
|
|
2535
|
+
}
|
|
2536
|
+
|
|
2537
|
+
export function createTuiSelector(getBuffer, requestRender = null, requestScroll = null) {
|
|
2538
|
+
return function $(selectorInput) {
|
|
2539
|
+
const objectSelectorId = selectorInput !== null && typeof selectorInput === "object"
|
|
2540
|
+
? _headingSelectorId(selectorInput)
|
|
2541
|
+
: null;
|
|
2542
|
+
const selector = objectSelectorId ? `#${objectSelectorId}` : selectorInput;
|
|
2543
|
+
const objectTarget = !objectSelectorId
|
|
2544
|
+
&& selectorInput !== null
|
|
2545
|
+
&& typeof selectorInput === "object"
|
|
2546
|
+
? selectorInput
|
|
2547
|
+
: null;
|
|
2548
|
+
const parsedSelector = objectTarget
|
|
2549
|
+
? null
|
|
2550
|
+
: _parseBlockIdentity(selector, { selector: true });
|
|
2551
|
+
const selectorId = _headingSelectorId(selector) ?? parsedSelector?.id ?? null;
|
|
2552
|
+
const selection = {
|
|
2553
|
+
id: selectorId ?? "",
|
|
2554
|
+
html() {
|
|
2555
|
+
try {
|
|
2556
|
+
if (objectTarget && "innerHTML" in objectTarget)
|
|
2557
|
+
return String(objectTarget.innerHTML ?? "");
|
|
2558
|
+
return _findHeading(getBuffer?.(), selector)?.html ?? "";
|
|
2559
|
+
} catch {
|
|
2560
|
+
return "";
|
|
2561
|
+
}
|
|
2562
|
+
},
|
|
2563
|
+
line() {
|
|
2564
|
+
try {
|
|
2565
|
+
const buffer = getBuffer?.();
|
|
2566
|
+
return _headingTuiLine(buffer, _findHeading(buffer, selector));
|
|
2567
|
+
} catch {
|
|
2568
|
+
return 0;
|
|
2569
|
+
}
|
|
2570
|
+
},
|
|
2571
|
+
scrollIntoView(...args) {
|
|
2572
|
+
const value = args[0];
|
|
2573
|
+
const buffer = getBuffer?.();
|
|
2574
|
+
const heading = _findHeading(buffer, selector);
|
|
2575
|
+
if (!buffer || !heading?.id) return undefined;
|
|
2576
|
+
if (!navigateTuiHeadingFragment(buffer, `#${encodeURIComponent(heading.id)}`))
|
|
2577
|
+
return undefined;
|
|
2578
|
+
const block = args.length === 0 || value === true || value === -1
|
|
2579
|
+
? "start"
|
|
2580
|
+
: value === false || value === 1
|
|
2581
|
+
? "end"
|
|
2582
|
+
: "center";
|
|
2583
|
+
requestScroll?.({ buffer, block });
|
|
2584
|
+
return undefined;
|
|
2585
|
+
},
|
|
2586
|
+
text(...args) {
|
|
2587
|
+
try {
|
|
2588
|
+
const buffer = getBuffer?.();
|
|
2589
|
+
const heading = _findHeading(buffer, selector);
|
|
2590
|
+
if (!objectTarget || (heading && !("textContent" in objectTarget))) {
|
|
2591
|
+
if (args.length > 0) return selection;
|
|
2592
|
+
const line = _headingTuiLine(buffer, heading);
|
|
2593
|
+
return line > 0 ? String(buffer.lines?.[line - 1] ?? "").trim() : "";
|
|
2594
|
+
}
|
|
2595
|
+
if (args.length > 0) {
|
|
2596
|
+
objectTarget.textContent = String(args[0] ?? "");
|
|
2597
|
+
return selection;
|
|
2598
|
+
}
|
|
2599
|
+
return String(objectTarget.textContent ?? "");
|
|
2600
|
+
} catch {
|
|
2601
|
+
return args.length > 0 ? selection : "";
|
|
2602
|
+
}
|
|
2603
|
+
},
|
|
2604
|
+
parent() {
|
|
2605
|
+
try {
|
|
2606
|
+
return objectTarget && typeof objectTarget.parent === "function"
|
|
2607
|
+
? objectTarget.parent()
|
|
2608
|
+
: null;
|
|
2609
|
+
} catch {
|
|
2610
|
+
return null;
|
|
2611
|
+
}
|
|
2612
|
+
},
|
|
2613
|
+
cell(row, col) {
|
|
2614
|
+
const makeCellSelection = (cellRow, cellCol) => {
|
|
2615
|
+
const normalizedRow = Number(cellRow);
|
|
2616
|
+
const normalizedCol = Number(cellCol);
|
|
2617
|
+
const cellSelection = {
|
|
2618
|
+
get row() {
|
|
2619
|
+
return normalizedRow;
|
|
2620
|
+
},
|
|
2621
|
+
get col() {
|
|
2622
|
+
return normalizedCol;
|
|
2623
|
+
},
|
|
2624
|
+
text(...args) {
|
|
2625
|
+
try {
|
|
2626
|
+
const buffer = getBuffer?.();
|
|
2627
|
+
const heading = _findHeading(buffer, selector);
|
|
2628
|
+
const table = _tuiHeadingTable(buffer, heading);
|
|
2629
|
+
if (args.length === 0) {
|
|
2630
|
+
return _tuiTableCellText(
|
|
2631
|
+
buffer,
|
|
2632
|
+
table,
|
|
2633
|
+
normalizedRow,
|
|
2634
|
+
normalizedCol,
|
|
2635
|
+
);
|
|
2636
|
+
}
|
|
2637
|
+
_setTuiTableCell(
|
|
2638
|
+
buffer,
|
|
2639
|
+
heading,
|
|
2640
|
+
normalizedRow,
|
|
2641
|
+
normalizedCol,
|
|
2642
|
+
args[0],
|
|
2643
|
+
);
|
|
2644
|
+
requestRender?.();
|
|
2645
|
+
} catch {}
|
|
2646
|
+
return args.length > 0 ? cellSelection : "";
|
|
2647
|
+
},
|
|
2648
|
+
val(...args) {
|
|
2649
|
+
try {
|
|
2650
|
+
const buffer = getBuffer?.();
|
|
2651
|
+
const heading = _findHeading(buffer, selector);
|
|
2652
|
+
if (args.length > 0) {
|
|
2653
|
+
_setTuiTableCellCheckbox(
|
|
2654
|
+
buffer,
|
|
2655
|
+
heading,
|
|
2656
|
+
normalizedRow,
|
|
2657
|
+
normalizedCol,
|
|
2658
|
+
Boolean(args[0]),
|
|
2659
|
+
);
|
|
2660
|
+
requestRender?.();
|
|
2661
|
+
return cellSelection;
|
|
2662
|
+
}
|
|
2663
|
+
const table = _tuiHeadingTable(buffer, heading);
|
|
2664
|
+
const text = _tuiTableCellText(
|
|
2665
|
+
buffer,
|
|
2666
|
+
table,
|
|
2667
|
+
normalizedRow,
|
|
2668
|
+
normalizedCol,
|
|
2669
|
+
);
|
|
2670
|
+
const checkbox = /[☐☒]/u.exec(text)?.[0];
|
|
2671
|
+
return checkbox ? checkbox === "☒" : text;
|
|
2672
|
+
} catch {
|
|
2673
|
+
return args.length > 0 ? cellSelection : "";
|
|
2674
|
+
}
|
|
2675
|
+
},
|
|
2676
|
+
left() {
|
|
2677
|
+
return neighborCell(0, -1);
|
|
2678
|
+
},
|
|
2679
|
+
lt() {
|
|
2680
|
+
return neighborCell(0, -1);
|
|
2681
|
+
},
|
|
2682
|
+
right() {
|
|
2683
|
+
return neighborCell(0, 1);
|
|
2684
|
+
},
|
|
2685
|
+
rt() {
|
|
2686
|
+
return neighborCell(0, 1);
|
|
2687
|
+
},
|
|
2688
|
+
up() {
|
|
2689
|
+
return neighborCell(-1, 0);
|
|
2690
|
+
},
|
|
2691
|
+
down() {
|
|
2692
|
+
return neighborCell(1, 0);
|
|
2693
|
+
},
|
|
2694
|
+
dn() {
|
|
2695
|
+
return neighborCell(1, 0);
|
|
2696
|
+
},
|
|
2697
|
+
};
|
|
2698
|
+
const neighborCell = (rowDelta, colDelta) => {
|
|
2699
|
+
try {
|
|
2700
|
+
const buffer = getBuffer?.();
|
|
2701
|
+
const heading = _findHeading(buffer, selector);
|
|
2702
|
+
const table = _tuiHeadingTable(buffer, heading);
|
|
2703
|
+
if (
|
|
2704
|
+
!_tuiTableCell(
|
|
2705
|
+
table,
|
|
2706
|
+
normalizedRow,
|
|
2707
|
+
normalizedCol,
|
|
2708
|
+
)
|
|
2709
|
+
) return null;
|
|
2710
|
+
const nextRow = normalizedRow + rowDelta;
|
|
2711
|
+
if (nextRow < 0 || nextRow >= table.rows.length) return null;
|
|
2712
|
+
const columnCount =
|
|
2713
|
+
table.rows[nextRow]?.[0]?.separators?.length - 1;
|
|
2714
|
+
if (!Number.isInteger(columnCount) || columnCount < 1)
|
|
2715
|
+
return null;
|
|
2716
|
+
const nextCol = normalizedCol + colDelta;
|
|
2717
|
+
if (nextCol < 0 || nextCol >= columnCount) return null;
|
|
2718
|
+
return makeCellSelection(nextRow, nextCol);
|
|
2719
|
+
} catch {
|
|
2720
|
+
return null;
|
|
2721
|
+
}
|
|
2722
|
+
};
|
|
2723
|
+
return cellSelection;
|
|
2724
|
+
};
|
|
2725
|
+
return makeCellSelection(row, col);
|
|
2726
|
+
},
|
|
2727
|
+
img(index = 0) {
|
|
2728
|
+
const normalizedIndex = Math.trunc(Number(index));
|
|
2729
|
+
return {
|
|
2730
|
+
get src() {
|
|
2731
|
+
try {
|
|
2732
|
+
if (!Number.isInteger(normalizedIndex) || normalizedIndex < 0) return "";
|
|
2733
|
+
const buffer = getBuffer?.();
|
|
2734
|
+
return _tuiHeadingImageSources(
|
|
2735
|
+
buffer,
|
|
2736
|
+
_findHeading(buffer, selector),
|
|
2737
|
+
)[normalizedIndex] ?? "";
|
|
2738
|
+
} catch {
|
|
2739
|
+
return "";
|
|
2740
|
+
}
|
|
2741
|
+
},
|
|
2742
|
+
};
|
|
2743
|
+
},
|
|
2744
|
+
show() {
|
|
2745
|
+
try {
|
|
2746
|
+
const buffer = getBuffer?.();
|
|
2747
|
+
const id = _headingSelectorId(selector);
|
|
2748
|
+
if (_changeTuiHeadingSectionVisibility(
|
|
2749
|
+
buffer, _findHeading(buffer, selector), id, "show",
|
|
2750
|
+
)) requestRender?.();
|
|
2751
|
+
} catch {}
|
|
2752
|
+
return selection;
|
|
2753
|
+
},
|
|
2754
|
+
hide() {
|
|
2755
|
+
try {
|
|
2756
|
+
const buffer = getBuffer?.();
|
|
2757
|
+
const id = _headingSelectorId(selector);
|
|
2758
|
+
if (_changeTuiHeadingSectionVisibility(
|
|
2759
|
+
buffer, _findHeading(buffer, selector), id, "hide",
|
|
2760
|
+
)) requestRender?.();
|
|
2761
|
+
} catch {}
|
|
2762
|
+
return selection;
|
|
2763
|
+
},
|
|
2764
|
+
toggle() {
|
|
2765
|
+
try {
|
|
2766
|
+
const buffer = getBuffer?.();
|
|
2767
|
+
const id = _headingSelectorId(selector);
|
|
2768
|
+
const heading = _findHeading(buffer, selector);
|
|
2769
|
+
if (_changeTuiHeadingSectionVisibility(buffer, heading, id, "toggle"))
|
|
2770
|
+
requestRender?.();
|
|
2771
|
+
} catch {}
|
|
2772
|
+
return selection;
|
|
2773
|
+
},
|
|
2774
|
+
data(...args) {
|
|
2775
|
+
try {
|
|
2776
|
+
const buffer = getBuffer?.();
|
|
2777
|
+
const data = _tuiUserData(buffer, selectorId);
|
|
2778
|
+
if (!data) return args.length === 0 ? undefined : selection;
|
|
2779
|
+
if (args.length === 0) return data;
|
|
2780
|
+
if (args.length === 1) {
|
|
2781
|
+
if (args[0] && typeof args[0] === "object") {
|
|
2782
|
+
Object.assign(data, args[0]);
|
|
2783
|
+
if (_renderTuiHeadingComponents(buffer, selectorId))
|
|
2784
|
+
requestRender?.({ imagesDirty: buffer._mdcuiImagesDirty === true });
|
|
2785
|
+
return selection;
|
|
2786
|
+
}
|
|
2787
|
+
return data[String(args[0])];
|
|
2788
|
+
}
|
|
2789
|
+
data[String(args[0])] = args[1];
|
|
2790
|
+
if (_renderTuiHeadingComponents(buffer, selectorId))
|
|
2791
|
+
requestRender?.({ imagesDirty: buffer._mdcuiImagesDirty === true });
|
|
2792
|
+
return selection;
|
|
2793
|
+
} catch {
|
|
2794
|
+
return args.length <= 1 ? undefined : selection;
|
|
2795
|
+
}
|
|
2796
|
+
},
|
|
2797
|
+
removeData(...keys) {
|
|
2798
|
+
try {
|
|
2799
|
+
const buffer = getBuffer?.();
|
|
2800
|
+
const normalized = keys
|
|
2801
|
+
.flatMap(key => Array.isArray(key) ? key : String(key).split(/\s+/))
|
|
2802
|
+
.filter(Boolean)
|
|
2803
|
+
.map(String);
|
|
2804
|
+
_removeTuiUserData(buffer, selectorId, normalized);
|
|
2805
|
+
} catch {}
|
|
2806
|
+
return selection;
|
|
2807
|
+
},
|
|
2808
|
+
val(...args) {
|
|
2809
|
+
try {
|
|
2810
|
+
const buffer = getBuffer?.();
|
|
2811
|
+
const heading = _findHeading(buffer, selector);
|
|
2812
|
+
if (objectTarget && (!heading || "value" in objectTarget || "textContent" in objectTarget)) {
|
|
2813
|
+
if (args.length > 0) {
|
|
2814
|
+
const value = String(args[0] ?? "");
|
|
2815
|
+
if ("value" in objectTarget) objectTarget.value = value;
|
|
2816
|
+
else objectTarget.textContent = value;
|
|
2817
|
+
return selection;
|
|
2818
|
+
}
|
|
2819
|
+
return "value" in objectTarget
|
|
2820
|
+
? String(objectTarget.value ?? "")
|
|
2821
|
+
: String(objectTarget.textContent ?? "");
|
|
2822
|
+
}
|
|
2823
|
+
if (!buffer) return args.length > 0 ? selection : "";
|
|
2824
|
+
const headingId = _headingSelectorId(selector);
|
|
2825
|
+
if (heading && headingId) {
|
|
2826
|
+
if (args.length > 0) return selection;
|
|
2827
|
+
return _headingCheckboxValue(buffer, heading, headingId);
|
|
2828
|
+
}
|
|
2829
|
+
if (!parsedSelector) return args.length > 0 ? selection : "";
|
|
2830
|
+
const lines = Array.isArray(buffer.lines)
|
|
2831
|
+
? buffer.lines
|
|
2832
|
+
: String(buffer).replace(/\r\n?/g, "\n").split("\n");
|
|
2833
|
+
if (args.length > 0) {
|
|
2834
|
+
if (Array.isArray(buffer.lines))
|
|
2835
|
+
_setBlockValue(buffer, parsedSelector, args[0]);
|
|
2836
|
+
// 跟 .data() 一樣主動要求重繪;不然從 setInterval/Promise
|
|
2837
|
+
// 之類非按鍵觸發的情境呼叫 .val(value),畫面只會靜靜地在
|
|
2838
|
+
// buffer 裡更新,要等下一次真正觸發 render() 的操作才會補上。
|
|
2839
|
+
requestRender?.();
|
|
2840
|
+
return selection;
|
|
2841
|
+
}
|
|
2842
|
+
return _blockValue(lines, parsedSelector) ?? "";
|
|
2843
|
+
} catch {
|
|
2844
|
+
return args.length > 0 ? selection : "";
|
|
2845
|
+
}
|
|
2846
|
+
},
|
|
2847
|
+
push(...items) {
|
|
2848
|
+
try {
|
|
2849
|
+
const buffer = getBuffer?.();
|
|
2850
|
+
const heading = _findHeading(buffer, selector);
|
|
2851
|
+
const result = _mutateAndRecordTuiHeadingList(
|
|
2852
|
+
buffer, heading, selector, "push", items, 0,
|
|
2853
|
+
);
|
|
2854
|
+
requestRender?.();
|
|
2855
|
+
return result;
|
|
2856
|
+
} catch {
|
|
2857
|
+
return 0;
|
|
2858
|
+
}
|
|
2859
|
+
},
|
|
2860
|
+
pop() {
|
|
2861
|
+
try {
|
|
2862
|
+
const buffer = getBuffer?.();
|
|
2863
|
+
const heading = _findHeading(buffer, selector);
|
|
2864
|
+
const result = _mutateAndRecordTuiHeadingList(
|
|
2865
|
+
buffer, heading, selector, "pop", [], undefined,
|
|
2866
|
+
);
|
|
2867
|
+
requestRender?.();
|
|
2868
|
+
return result;
|
|
2869
|
+
} catch {
|
|
2870
|
+
return undefined;
|
|
2871
|
+
}
|
|
2872
|
+
},
|
|
2873
|
+
shift() {
|
|
2874
|
+
try {
|
|
2875
|
+
const buffer = getBuffer?.();
|
|
2876
|
+
const heading = _findHeading(buffer, selector);
|
|
2877
|
+
const result = _mutateAndRecordTuiHeadingList(
|
|
2878
|
+
buffer, heading, selector, "shift", [], undefined,
|
|
2879
|
+
);
|
|
2880
|
+
requestRender?.();
|
|
2881
|
+
return result;
|
|
2882
|
+
} catch {
|
|
2883
|
+
return undefined;
|
|
2884
|
+
}
|
|
2885
|
+
},
|
|
2886
|
+
unshift(...items) {
|
|
2887
|
+
try {
|
|
2888
|
+
const buffer = getBuffer?.();
|
|
2889
|
+
const heading = _findHeading(buffer, selector);
|
|
2890
|
+
const result = _mutateAndRecordTuiHeadingList(
|
|
2891
|
+
buffer, heading, selector, "unshift", items, 0,
|
|
2892
|
+
);
|
|
2893
|
+
requestRender?.();
|
|
2894
|
+
return result;
|
|
2895
|
+
} catch {
|
|
2896
|
+
return 0;
|
|
2897
|
+
}
|
|
2898
|
+
},
|
|
2899
|
+
splice(...args) {
|
|
2900
|
+
try {
|
|
2901
|
+
const buffer = getBuffer?.();
|
|
2902
|
+
const heading = _findHeading(buffer, selector);
|
|
2903
|
+
const result = _mutateAndRecordTuiHeadingList(
|
|
2904
|
+
buffer, heading, selector, "splice", args, [],
|
|
2905
|
+
);
|
|
2906
|
+
requestRender?.();
|
|
2907
|
+
return result;
|
|
2908
|
+
} catch {
|
|
2909
|
+
return [];
|
|
2910
|
+
}
|
|
2911
|
+
},
|
|
2912
|
+
slice(...args) {
|
|
2913
|
+
try {
|
|
2914
|
+
const buffer = getBuffer?.();
|
|
2915
|
+
const heading = _findHeading(buffer, selector);
|
|
2916
|
+
const list = heading ? _tuiHeadingTaskList(buffer, heading) : null;
|
|
2917
|
+
if (!list) return [];
|
|
2918
|
+
return list.items.map((item) => ({
|
|
2919
|
+
value: item.value,
|
|
2920
|
+
checked: Boolean(
|
|
2921
|
+
_tuiRenderedTaskCheckbox(
|
|
2922
|
+
buffer.lines[item.start],
|
|
2923
|
+
list.quoteDepth,
|
|
2924
|
+
)?.checked,
|
|
2925
|
+
),
|
|
2926
|
+
})).slice(...args);
|
|
2927
|
+
} catch {
|
|
2928
|
+
return [];
|
|
2929
|
+
}
|
|
2930
|
+
},
|
|
2931
|
+
};
|
|
2932
|
+
return selection;
|
|
2933
|
+
};
|
|
2934
|
+
}
|
|
2935
|
+
|
|
2936
|
+
export function tuiTableCellAtPosition(buffer, lineIndex, characterIndex) {
|
|
2937
|
+
const row = Math.trunc(Number(lineIndex));
|
|
2938
|
+
const column = Math.trunc(Number(characterIndex));
|
|
2939
|
+
if (
|
|
2940
|
+
!Array.isArray(buffer?.lines)
|
|
2941
|
+
|| !Number.isInteger(row)
|
|
2942
|
+
|| !Number.isInteger(column)
|
|
2943
|
+
|| row < 0
|
|
2944
|
+
|| row >= buffer.lines.length
|
|
2945
|
+
|| column < 0
|
|
2946
|
+
) return null;
|
|
2947
|
+
|
|
2948
|
+
for (const heading of _tuiSourceHeadings(buffer)) {
|
|
2949
|
+
const table = _tuiHeadingTable(buffer, heading);
|
|
2950
|
+
if (!table || row <= table.top || row >= table.bottom) continue;
|
|
2951
|
+
for (let rowIndex = 0; rowIndex < table.rows.length; rowIndex++) {
|
|
2952
|
+
const visual = table.rows[rowIndex].find(item => item.y === row);
|
|
2953
|
+
if (!visual) continue;
|
|
2954
|
+
for (
|
|
2955
|
+
let colIndex = 0;
|
|
2956
|
+
colIndex + 1 < visual.separators.length;
|
|
2957
|
+
colIndex++
|
|
2958
|
+
) {
|
|
2959
|
+
if (
|
|
2960
|
+
column > visual.separators[colIndex]
|
|
2961
|
+
&& column < visual.separators[colIndex + 1]
|
|
2962
|
+
) {
|
|
2963
|
+
return createTuiSelector(() => buffer)({ id: heading.id })
|
|
2964
|
+
.cell(rowIndex, colIndex);
|
|
2965
|
+
}
|
|
2966
|
+
}
|
|
2967
|
+
return null;
|
|
2968
|
+
}
|
|
2969
|
+
}
|
|
2970
|
+
return null;
|
|
2971
|
+
}
|
|
2972
|
+
|
|
2973
|
+
export function markTuiTableCellDirtyAtPosition(buffer, lineIndex, characterIndex) {
|
|
2974
|
+
const row = Math.trunc(Number(lineIndex));
|
|
2975
|
+
const column = Math.trunc(Number(characterIndex));
|
|
2976
|
+
if (!Array.isArray(buffer?.lines) || row < 0 || column < 0) return false;
|
|
2977
|
+
for (const heading of _tuiSourceHeadings(buffer)) {
|
|
2978
|
+
const table = _tuiHeadingTable(buffer, heading);
|
|
2979
|
+
if (!table || row <= table.top || row >= table.bottom) continue;
|
|
2980
|
+
for (let rowIndex = 0; rowIndex < table.rows.length; rowIndex++) {
|
|
2981
|
+
const visual = table.rows[rowIndex].find(item => item.y === row);
|
|
2982
|
+
if (!visual) continue;
|
|
2983
|
+
for (let colIndex = 0; colIndex + 1 < visual.separators.length; colIndex++) {
|
|
2984
|
+
if (column <= visual.separators[colIndex] || column >= visual.separators[colIndex + 1])
|
|
2985
|
+
continue;
|
|
2986
|
+
if (!(buffer._mdcuiDirtyTableCells instanceof Set))
|
|
2987
|
+
buffer._mdcuiDirtyTableCells = new Set();
|
|
2988
|
+
buffer._mdcuiDirtyTableCells.add(`${heading.id}\0${rowIndex}\0${colIndex}`);
|
|
2989
|
+
return true;
|
|
2990
|
+
}
|
|
2991
|
+
}
|
|
2992
|
+
}
|
|
2993
|
+
return false;
|
|
2994
|
+
}
|
|
2995
|
+
|
|
2996
|
+
// ── micro global object ───────────────────────────────────────────────────────
|
|
2997
|
+
|
|
2998
|
+
export function buildMicroGlobal(jsManager) {
|
|
2999
|
+
const getApp = () => jsManager._app;
|
|
3000
|
+
const getCtx = () => jsManager._ctx;
|
|
3001
|
+
const $ = createTuiSelector(
|
|
3002
|
+
() => getApp()?.buffer,
|
|
3003
|
+
({ imagesDirty = false } = {}) => {
|
|
3004
|
+
const app = getApp();
|
|
3005
|
+
if (!app?._started) return;
|
|
3006
|
+
if (imagesDirty) void app.refreshMdcuiKittyImages?.(app.buffer);
|
|
3007
|
+
else app.render?.();
|
|
3008
|
+
},
|
|
3009
|
+
({ buffer, block }) => {
|
|
3010
|
+
const app = getApp();
|
|
3011
|
+
const pane = app?.pane;
|
|
3012
|
+
if (!app?._started || pane?.buffer !== buffer) return;
|
|
3013
|
+
app.scrollCursorToAlignment?.(pane, block);
|
|
3014
|
+
app.render?.();
|
|
3015
|
+
},
|
|
3016
|
+
);
|
|
3017
|
+
$.tts = async (text, pitch, speed) => {
|
|
3018
|
+
const app = getApp();
|
|
3019
|
+
if (!app?.runTts) return;
|
|
3020
|
+
const parsedPitch = Number(pitch);
|
|
3021
|
+
const parsedSpeed = Number(speed);
|
|
3022
|
+
if (Number.isFinite(parsedPitch) && parsedPitch > 0)
|
|
3023
|
+
Bun.env.TTS_PITCH = String(parsedPitch);
|
|
3024
|
+
if (Number.isFinite(parsedSpeed) && parsedSpeed > 0)
|
|
3025
|
+
Bun.env.TTS_SPEED = String(parsedSpeed);
|
|
3026
|
+
return app.runTts(String(text ?? ""), {
|
|
3027
|
+
trackBuffer: false,
|
|
3028
|
+
});
|
|
3029
|
+
};
|
|
3030
|
+
$.tts.stop = () => getApp()?.stopTts?.() ?? false;
|
|
3031
|
+
|
|
3032
|
+
// Converts cmd args to a safe command string for handleCommand
|
|
3033
|
+
function buildCmdString(name, args) {
|
|
3034
|
+
if (args.length === 0) return String(name);
|
|
3035
|
+
const parts = args.map(a => {
|
|
3036
|
+
const s = String(a);
|
|
3037
|
+
return /[\s"'\\]/.test(s) || s === "" ? JSON.stringify(s) : s;
|
|
3038
|
+
});
|
|
3039
|
+
return `${name} ${parts.join(" ")}`;
|
|
3040
|
+
}
|
|
3041
|
+
|
|
3042
|
+
const micro = {
|
|
3043
|
+
// ── Hook registration ──────────────────────────────────────────
|
|
3044
|
+
on(hookName, fn) {
|
|
3045
|
+
jsManager.on(hookName, fn);
|
|
3046
|
+
},
|
|
3047
|
+
|
|
3048
|
+
// ── Current pane access ───────────────────────────────────────
|
|
3049
|
+
CurPane() {
|
|
3050
|
+
const app = getApp();
|
|
3051
|
+
return app?.buffer ? _makePaneAPI(app.buffer, app) : null;
|
|
3052
|
+
},
|
|
3053
|
+
|
|
3054
|
+
// ── Option access ─────────────────────────────────────────────
|
|
3055
|
+
GetOption: (name) => getCtx()?.config?.getGlobalOption(name),
|
|
3056
|
+
SetOption: (name, value) => getCtx()?.config?.setGlobalOptionNative(name, value),
|
|
3057
|
+
|
|
3058
|
+
// ── Messaging ─────────────────────────────────────────────────
|
|
3059
|
+
Log: (...args) => console.log(...args),
|
|
3060
|
+
TermMessage: (msg) => { const app = getApp(); if (app) { app.message = String(msg); if (app._started) app.render?.(); } },
|
|
3061
|
+
alert: (msg) => { const app = getApp(); return app ? app.protectedAlert(msg) : console.log(String(msg)); },
|
|
3062
|
+
confirm: (msg) => { const app = getApp(); return app ? app.protectedConfirm(msg) : false; },
|
|
3063
|
+
prompt: (msg, defaultValue = "") => {
|
|
3064
|
+
const app = getApp();
|
|
3065
|
+
return app ? app.protectedPrompt(msg, defaultValue) : defaultValue;
|
|
3066
|
+
},
|
|
3067
|
+
|
|
3068
|
+
// ── Buffer line access (1-based line numbers; omit → cursor line) ─
|
|
3069
|
+
|
|
3070
|
+
// Returns text of line n (1-based). Omit n to use cursor line.
|
|
3071
|
+
getLine(lineNumber) {
|
|
3072
|
+
const app = getApp();
|
|
3073
|
+
if (!app?.buffer) return "";
|
|
3074
|
+
const buf = app.buffer;
|
|
3075
|
+
const y = lineNumber != null ? Number(lineNumber) - 1 : buf.cursor.y;
|
|
3076
|
+
return buf.lines[y] ?? "";
|
|
3077
|
+
},
|
|
3078
|
+
|
|
3079
|
+
// Replaces line n (1-based) with text. Text may contain newlines → line expands.
|
|
3080
|
+
putLine(text, lineNumber) {
|
|
3081
|
+
const app = getApp();
|
|
3082
|
+
if (!app?.buffer) return;
|
|
3083
|
+
const buf = app.buffer;
|
|
3084
|
+
const y = lineNumber != null ? Number(lineNumber) - 1 : buf.cursor.y;
|
|
3085
|
+
if (y < 0 || y >= buf.lines.length) return;
|
|
3086
|
+
buf.pushUndo?.();
|
|
3087
|
+
buf._mdcuiFenceBlockIndex = null;
|
|
3088
|
+
const parts = String(text).replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
|
|
3089
|
+
buf.lines.splice(y, 1, ...parts);
|
|
3090
|
+
buf.invalidateHighlightFrom?.(y, { force: parts.length > 1 });
|
|
3091
|
+
buf.modified = true;
|
|
3092
|
+
buf.ensureCursor?.();
|
|
3093
|
+
app.render?.();
|
|
3094
|
+
},
|
|
3095
|
+
|
|
3096
|
+
// Deletes line n (1-based). If the buffer has only one line, clears it instead.
|
|
3097
|
+
delLine(lineNumber) {
|
|
3098
|
+
const app = getApp();
|
|
3099
|
+
if (!app?.buffer) return;
|
|
3100
|
+
const buf = app.buffer;
|
|
3101
|
+
const y = lineNumber != null ? Number(lineNumber) - 1 : buf.cursor.y;
|
|
3102
|
+
if (y < 0 || y >= buf.lines.length) return;
|
|
3103
|
+
buf.pushUndo?.();
|
|
3104
|
+
buf._mdcuiFenceBlockIndex = null;
|
|
3105
|
+
if (buf.lines.length === 1) {
|
|
3106
|
+
buf.lines[0] = "";
|
|
3107
|
+
} else {
|
|
3108
|
+
buf.lines.splice(y, 1);
|
|
3109
|
+
}
|
|
3110
|
+
buf.invalidateHighlightFrom?.(y, { force: true });
|
|
3111
|
+
buf.modified = true;
|
|
3112
|
+
buf.ensureCursor?.();
|
|
3113
|
+
app.render?.();
|
|
3114
|
+
},
|
|
3115
|
+
|
|
3116
|
+
// Returns an array of line strings from line `from` to `to` (1-based, inclusive).
|
|
3117
|
+
// Omit both to return all lines.
|
|
3118
|
+
getLines(from, to) {
|
|
3119
|
+
const app = getApp();
|
|
3120
|
+
if (!app?.buffer) return [];
|
|
3121
|
+
const buf = app.buffer;
|
|
3122
|
+
const start = from != null ? Number(from) - 1 : 0;
|
|
3123
|
+
const end = to != null ? Number(to) - 1 : buf.lines.length - 1;
|
|
3124
|
+
return buf.lines.slice(Math.max(0, start), Math.min(buf.lines.length, end + 1));
|
|
3125
|
+
},
|
|
3126
|
+
|
|
3127
|
+
// Returns total number of lines.
|
|
3128
|
+
getLinesCount() {
|
|
3129
|
+
const app = getApp();
|
|
3130
|
+
return app?.buffer?.lines.length ?? 0;
|
|
3131
|
+
},
|
|
3132
|
+
|
|
3133
|
+
// Returns the entire buffer content as a single string (lines joined by "\n").
|
|
3134
|
+
getAllText() {
|
|
3135
|
+
const app = getApp();
|
|
3136
|
+
return app?.buffer?.lines.join("\n") ?? "";
|
|
3137
|
+
},
|
|
3138
|
+
|
|
3139
|
+
// Returns the rendered ANSI document when available, otherwise plain text.
|
|
3140
|
+
getAllAnsiText() {
|
|
3141
|
+
const buffer = getApp()?.buffer;
|
|
3142
|
+
return typeof buffer?._mdcuiAnsiText === "string"
|
|
3143
|
+
? buffer._mdcuiAnsiText
|
|
3144
|
+
: buffer?.lines.join("\n") ?? "";
|
|
3145
|
+
},
|
|
3146
|
+
|
|
3147
|
+
// Activates a 1-based rendered-buffer cell in MDCUI; otherwise falls back to goto.
|
|
3148
|
+
async clickBufferCell(column = 1, line = 1) {
|
|
3149
|
+
const app = getApp();
|
|
3150
|
+
const buffer = app?.buffer;
|
|
3151
|
+
if (!app || !buffer) return false;
|
|
3152
|
+
|
|
3153
|
+
const targetLine = Math.max(1, Math.trunc(Number(line)) || 1);
|
|
3154
|
+
const targetColumn = Math.max(1, Math.trunc(Number(column)) || 1);
|
|
3155
|
+
if (!isMdcuiEncoding(buffer.encoding ?? buffer.Settings?.encoding)) {
|
|
3156
|
+
await app.handleCommand(`goto ${targetLine}:${targetColumn}`);
|
|
3157
|
+
app.render?.();
|
|
3158
|
+
return true;
|
|
3159
|
+
}
|
|
3160
|
+
|
|
3161
|
+
const y = Math.min(targetLine - 1, Math.max(0, buffer.lines.length - 1));
|
|
3162
|
+
const x = Math.min(targetColumn - 1, String(buffer.lines[y] ?? "").length);
|
|
3163
|
+
buffer.cursor = { x, y };
|
|
3164
|
+
const handled = await app.handleMdcuiCellCallback(buffer, y, x, "mouse");
|
|
3165
|
+
app.render?.();
|
|
3166
|
+
return handled;
|
|
3167
|
+
},
|
|
3168
|
+
|
|
3169
|
+
// Sends terminal input through the App's normal parser and event pipeline.
|
|
3170
|
+
async _dispatchRawInput(raw) {
|
|
3171
|
+
const app = getApp();
|
|
3172
|
+
if (!app) return false;
|
|
3173
|
+
const bytes = typeof raw === "string"
|
|
3174
|
+
? new TextEncoder().encode(raw)
|
|
3175
|
+
: raw;
|
|
3176
|
+
await app._dispatchInput(bytes);
|
|
3177
|
+
app.render?.();
|
|
3178
|
+
return true;
|
|
3179
|
+
},
|
|
3180
|
+
|
|
3181
|
+
// Replaces the entire buffer content with text (may contain newlines).
|
|
3182
|
+
putAllText(text) {
|
|
3183
|
+
const app = getApp();
|
|
3184
|
+
if (!app?.buffer) return;
|
|
3185
|
+
const buf = app.buffer;
|
|
3186
|
+
buf.pushUndo?.();
|
|
3187
|
+
buf._mdcuiFenceBlockIndex = null;
|
|
3188
|
+
buf.lines = String(text).replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
|
|
3189
|
+
if (buf.lines.length === 0) buf.lines = [""];
|
|
3190
|
+
buf.invalidateHighlightFrom?.(0, { force: true });
|
|
3191
|
+
buf.modified = true;
|
|
3192
|
+
buf.ensureCursor?.();
|
|
3193
|
+
app.render?.();
|
|
3194
|
+
},
|
|
3195
|
+
|
|
3196
|
+
// ── Selection access ──────────────────────────────────────────────
|
|
3197
|
+
|
|
3198
|
+
// Returns the currently selected text, or "" if nothing is selected.
|
|
3199
|
+
getSelection() {
|
|
3200
|
+
const app = getApp();
|
|
3201
|
+
if (!app?.buffer || !app.pane?.selection) return "";
|
|
3202
|
+
return _selText(app.buffer, app.pane.selection);
|
|
3203
|
+
},
|
|
3204
|
+
|
|
3205
|
+
// Replaces the active selection with text; if nothing selected, inserts at cursor.
|
|
3206
|
+
putSelection(text) {
|
|
3207
|
+
const app = getApp();
|
|
3208
|
+
if (!app?.buffer) return;
|
|
3209
|
+
const buf = app.buffer;
|
|
3210
|
+
buf.pushUndo?.();
|
|
3211
|
+
if (app.pane?.selection) _deleteSel(buf, app.pane);
|
|
3212
|
+
buf.insert(String(text));
|
|
3213
|
+
app.render?.();
|
|
3214
|
+
},
|
|
3215
|
+
|
|
3216
|
+
// ── Register custom command (shows up in Ctrl+E Tab completion) ──
|
|
3217
|
+
MakeCommand(name, fn) {
|
|
3218
|
+
if (!name || typeof fn !== "function") return;
|
|
3219
|
+
jsManager.commands.set(String(name), fn);
|
|
3220
|
+
},
|
|
3221
|
+
|
|
3222
|
+
// ── cmd proxy: micro.cmd.save("file.txt") ─────────────────────
|
|
3223
|
+
// Each property is a function that calls handleCommand on the current pane.
|
|
3224
|
+
cmd: new Proxy({}, {
|
|
3225
|
+
get(_, name) {
|
|
3226
|
+
if (typeof name !== "string") return undefined;
|
|
3227
|
+
return async (...args) => {
|
|
3228
|
+
const app = getApp();
|
|
3229
|
+
if (!app) return;
|
|
3230
|
+
const result = await app.handleCommand(buildCmdString(name, args));
|
|
3231
|
+
app.render?.();
|
|
3232
|
+
return result;
|
|
3233
|
+
};
|
|
3234
|
+
},
|
|
3235
|
+
}),
|
|
3236
|
+
|
|
3237
|
+
// ── action proxy: micro.action.MoveLinesUp() ──────────────────
|
|
3238
|
+
// Each property is an async function that runs a named editor action.
|
|
3239
|
+
// micro.shell.COMMAND(...args) — runs COMMAND with args via Ctrl-B interactive shell
|
|
3240
|
+
// e.g. micro.shell.ls('-l') → runInteractiveShell("ls -l")
|
|
3241
|
+
shell: new Proxy({}, {
|
|
3242
|
+
get(_, cmd) {
|
|
3243
|
+
if (typeof cmd !== "string") return undefined;
|
|
3244
|
+
return (...args) => {
|
|
3245
|
+
const app = getApp();
|
|
3246
|
+
if (!app?.runInteractiveShell) return;
|
|
3247
|
+
return app.runInteractiveShell([cmd, ...args.map(String)]);
|
|
3248
|
+
};
|
|
3249
|
+
},
|
|
3250
|
+
}),
|
|
3251
|
+
|
|
3252
|
+
action: new Proxy({}, {
|
|
3253
|
+
get(_, name) {
|
|
3254
|
+
if (typeof name !== "string") return undefined;
|
|
3255
|
+
return async (...args) => {
|
|
3256
|
+
const app = getApp();
|
|
3257
|
+
if (!app) return;
|
|
3258
|
+
const fn = ACTIONS.get(name);
|
|
3259
|
+
if (fn) {
|
|
3260
|
+
await fn(app, ...args);
|
|
3261
|
+
} else {
|
|
3262
|
+
// Fallback: try as a method on the current buffer
|
|
3263
|
+
const buf = app.buffer;
|
|
3264
|
+
if (buf && typeof buf[name] === "function") {
|
|
3265
|
+
await buf[name](...args);
|
|
3266
|
+
} else {
|
|
3267
|
+
console.warn(`[micro.action] unknown action: ${name}`);
|
|
3268
|
+
return;
|
|
3269
|
+
}
|
|
3270
|
+
}
|
|
3271
|
+
app.render?.();
|
|
3272
|
+
};
|
|
3273
|
+
},
|
|
3274
|
+
}),
|
|
3275
|
+
|
|
3276
|
+
// ── Runtime info ──────────────────────────────────────────────
|
|
3277
|
+
OS: process.platform,
|
|
3278
|
+
Version: "0.1.0-bun",
|
|
3279
|
+
|
|
3280
|
+
// ── Internal: register an action from a JS plugin ─────────────
|
|
3281
|
+
RegisterAction(name, fn) {
|
|
3282
|
+
ACTIONS.set(name, fn);
|
|
3283
|
+
},
|
|
3284
|
+
|
|
3285
|
+
// ── Trigger editor re-render ──────────────────────────────────
|
|
3286
|
+
render() {
|
|
3287
|
+
getApp()?.render?.();
|
|
3288
|
+
},
|
|
3289
|
+
|
|
3290
|
+
// ── Append to lintLog (displayed via :lintlog command) ────────
|
|
3291
|
+
pushLintLog(msg) {
|
|
3292
|
+
const plugins = getCtx()?.plugins;
|
|
3293
|
+
if (plugins) { plugins.lintLog ??= []; plugins.lintLog.push(String(msg)); }
|
|
3294
|
+
},
|
|
3295
|
+
|
|
3296
|
+
// ── Buffer message factories ──────────────────────────────────
|
|
3297
|
+
// micro.buffer.newMessage(owner, msg, {x,y}, {x,y}, severity)
|
|
3298
|
+
// micro.buffer.newMessageAtLine(owner, msg, lineNum, severity)
|
|
3299
|
+
// micro.buffer.MTError / MTWarning / MTInfo
|
|
3300
|
+
// micro.buffer.Loc(x, y)
|
|
3301
|
+
buffer: {
|
|
3302
|
+
newMessage,
|
|
3303
|
+
newMessageAtLine,
|
|
3304
|
+
Loc: (x, y) => new Loc(x, y),
|
|
3305
|
+
MTError,
|
|
3306
|
+
MTWarning,
|
|
3307
|
+
MTInfo,
|
|
3308
|
+
},
|
|
3309
|
+
};
|
|
3310
|
+
|
|
3311
|
+
globalThis.micro = micro;
|
|
3312
|
+
globalThis.$ = $;
|
|
3313
|
+
return micro;
|
|
3314
|
+
}
|
|
3315
|
+
|
|
3316
|
+
// ── Pane / Buffer API returned by CurPane() ──────────────────────────────────
|
|
3317
|
+
|
|
3318
|
+
function _makePaneAPI(buffer, app) {
|
|
3319
|
+
return {
|
|
3320
|
+
get Buf() { return _makeBufAPI(buffer); },
|
|
3321
|
+
get Cursor() { return _makeCursorAPI(buffer); },
|
|
3322
|
+
CursorLocation: () => app?.formatCursorLocation?.(buffer) ?? "+1.0:1",
|
|
3323
|
+
AbsoluteCursorLocation: () => app?.formatAbsoluteCursorLocation?.(buffer) ?? "+1:1",
|
|
3324
|
+
|
|
3325
|
+
Save: async () => app?.save?.(),
|
|
3326
|
+
Quit: async () => app?.quit?.(),
|
|
3327
|
+
Backspace: () => buffer.backspace(),
|
|
3328
|
+
Delete: () => buffer.deleteForward(),
|
|
3329
|
+
CursorLeft: () => buffer.moveLeft(),
|
|
3330
|
+
CursorRight: () => buffer.moveRight(),
|
|
3331
|
+
CursorUp: () => buffer.moveUp(),
|
|
3332
|
+
CursorDown: () => buffer.moveDown(),
|
|
3333
|
+
StartOfLine: () => buffer.moveHome(),
|
|
3334
|
+
EndOfLine: () => buffer.moveEnd(),
|
|
3335
|
+
InsertNewline: () => buffer.newline(),
|
|
3336
|
+
InsertTab: () => buffer.insertTab(),
|
|
3337
|
+
Insert: (text) => { buffer.pushUndo?.(); buffer.insert(text); app?.render?.(); },
|
|
3338
|
+
HandleCommand: (cmd) => app?.handleCommand?.(cmd),
|
|
3339
|
+
|
|
3340
|
+
// Run a named action on this pane
|
|
3341
|
+
RunAction: async (name, ...args) => {
|
|
3342
|
+
const fn = ACTIONS.get(name);
|
|
3343
|
+
if (fn) { await fn(app, ...args); app.render?.(); }
|
|
3344
|
+
},
|
|
3345
|
+
};
|
|
3346
|
+
}
|
|
3347
|
+
|
|
3348
|
+
function _makeBufAPI(buffer) {
|
|
3349
|
+
return {
|
|
3350
|
+
get Path() { return buffer.path ?? ""; },
|
|
3351
|
+
get AbsPath() { return buffer.AbsPath ?? buffer.path ?? ""; },
|
|
3352
|
+
get Type() { return buffer.Type; },
|
|
3353
|
+
get Settings() { return buffer.Settings; },
|
|
3354
|
+
get Modified() { return buffer.modified; },
|
|
3355
|
+
get MdcuiModuleSource() {
|
|
3356
|
+
return global.MDCUI_MAIN && buffer._useBundledMdcuiModules
|
|
3357
|
+
? "embedded"
|
|
3358
|
+
: "external";
|
|
3359
|
+
},
|
|
3360
|
+
|
|
3361
|
+
Line: (n) => buffer.Line(n),
|
|
3362
|
+
LinesNum: () => buffer.LinesNum(),
|
|
3363
|
+
FileType: () => buffer.FileType(),
|
|
3364
|
+
SetOption: (opt, val) => buffer.SetOption(opt, val),
|
|
3365
|
+
Insert: (loc, text) => buffer.Insert(loc, text),
|
|
3366
|
+
GetActiveCursor: () => _makeCursorAPI(buffer),
|
|
3367
|
+
};
|
|
3368
|
+
}
|
|
3369
|
+
|
|
3370
|
+
function _makeCursorAPI(buffer) {
|
|
3371
|
+
return {
|
|
3372
|
+
get X() { return buffer.cursor.x; },
|
|
3373
|
+
set X(v) { buffer.cursor.x = v; buffer.ensureCursor?.(); },
|
|
3374
|
+
get Y() { return buffer.cursor.y; },
|
|
3375
|
+
set Y(v) { buffer.cursor.y = v; buffer.ensureCursor?.(); },
|
|
3376
|
+
};
|
|
3377
|
+
}
|