buninu 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (435) hide show
  1. package/.bashrc +11 -0
  2. package/ARCHITECTURE.md +452 -0
  3. package/LICENSE +21 -0
  4. package/README.md +121 -0
  5. package/apps/bun/make-bunBin.sh +84 -0
  6. package/apps/jsgotty/LICENSE +23 -0
  7. package/apps/jsgotty/LICENSES/Apache-2.0-zmodem.js.txt +201 -0
  8. package/apps/jsgotty/NOTICE +21 -0
  9. package/apps/jsgotty/README.en.md +290 -0
  10. package/apps/jsgotty/README.md +292 -0
  11. package/apps/jsgotty/client.mjs +700 -0
  12. package/apps/jsgotty/env.sh +2 -0
  13. package/apps/jsgotty/gotty.js +3325 -0
  14. package/apps/jsgotty/node_modules/.package-lock.json +35 -0
  15. package/apps/jsgotty/node_modules/node-addon-api/LICENSE.md +9 -0
  16. package/apps/jsgotty/node_modules/node-addon-api/README.md +319 -0
  17. package/apps/jsgotty/node_modules/node-addon-api/common.gypi +20 -0
  18. package/apps/jsgotty/node_modules/node-addon-api/except.gypi +25 -0
  19. package/apps/jsgotty/node_modules/node-addon-api/index.js +12 -0
  20. package/apps/jsgotty/node_modules/node-addon-api/napi-inl.deprecated.h +186 -0
  21. package/apps/jsgotty/node_modules/node-addon-api/napi-inl.h +6607 -0
  22. package/apps/jsgotty/node_modules/node-addon-api/napi.h +3201 -0
  23. package/apps/jsgotty/node_modules/node-addon-api/node_addon_api.gyp +32 -0
  24. package/apps/jsgotty/node_modules/node-addon-api/node_api.gyp +9 -0
  25. package/apps/jsgotty/node_modules/node-addon-api/noexcept.gypi +26 -0
  26. package/apps/jsgotty/node_modules/node-addon-api/nothing.c +0 -0
  27. package/apps/jsgotty/node_modules/node-addon-api/package-support.json +21 -0
  28. package/apps/jsgotty/node_modules/node-addon-api/package.json +480 -0
  29. package/apps/jsgotty/node_modules/node-addon-api/tools/README.md +73 -0
  30. package/apps/jsgotty/node_modules/node-addon-api/tools/check-napi.js +99 -0
  31. package/apps/jsgotty/node_modules/node-addon-api/tools/clang-format.js +71 -0
  32. package/apps/jsgotty/node_modules/node-addon-api/tools/conversion.js +301 -0
  33. package/apps/jsgotty/node_modules/node-addon-api/tools/eslint-format.js +79 -0
  34. package/apps/jsgotty/node_modules/ws/LICENSE +20 -0
  35. package/apps/jsgotty/node_modules/ws/README.md +548 -0
  36. package/apps/jsgotty/node_modules/ws/browser.js +8 -0
  37. package/apps/jsgotty/node_modules/ws/index.js +22 -0
  38. package/apps/jsgotty/node_modules/ws/lib/buffer-util.js +131 -0
  39. package/apps/jsgotty/node_modules/ws/lib/constants.js +19 -0
  40. package/apps/jsgotty/node_modules/ws/lib/event-target.js +292 -0
  41. package/apps/jsgotty/node_modules/ws/lib/extension.js +203 -0
  42. package/apps/jsgotty/node_modules/ws/lib/limiter.js +55 -0
  43. package/apps/jsgotty/node_modules/ws/lib/permessage-deflate.js +528 -0
  44. package/apps/jsgotty/node_modules/ws/lib/receiver.js +760 -0
  45. package/apps/jsgotty/node_modules/ws/lib/sender.js +607 -0
  46. package/apps/jsgotty/node_modules/ws/lib/stream.js +161 -0
  47. package/apps/jsgotty/node_modules/ws/lib/subprotocol.js +62 -0
  48. package/apps/jsgotty/node_modules/ws/lib/validation.js +152 -0
  49. package/apps/jsgotty/node_modules/ws/lib/websocket-server.js +562 -0
  50. package/apps/jsgotty/node_modules/ws/lib/websocket.js +1407 -0
  51. package/apps/jsgotty/node_modules/ws/package.json +70 -0
  52. package/apps/jsgotty/node_modules/ws/wrapper.mjs +21 -0
  53. package/apps/jsgotty/package-lock.json +46 -0
  54. package/apps/jsgotty/package.json +13 -0
  55. package/apps/jsgotty/patches/windowsPtyAgent.js +326 -0
  56. package/apps/jsgotty/patches/windowsTerminal.js +204 -0
  57. package/apps/jsgotty/rz.js +199 -0
  58. package/apps/jsgotty/single-exe/README.md +80 -0
  59. package/apps/jsgotty/single-exe/assetsHelper.js +90 -0
  60. package/apps/jsgotty/single-exe/assetsLoader.mjs +85 -0
  61. package/apps/jsgotty/single-exe/compiled.js +138 -0
  62. package/apps/jsgotty/single-exe/entry.mjs +9 -0
  63. package/apps/jsgotty/single-exe/packAssets.sh +7 -0
  64. package/apps/jsgotty/static/css/index.css +13 -0
  65. package/apps/jsgotty/static/css/kitty.css +61 -0
  66. package/apps/jsgotty/static/css/xterm.css +218 -0
  67. package/apps/jsgotty/static/css/xterm_customize.css +32 -0
  68. package/apps/jsgotty/static/favicon.ico +0 -0
  69. package/apps/jsgotty/static/help.md +125 -0
  70. package/apps/jsgotty/static/icon.svg +66 -0
  71. package/apps/jsgotty/static/icon_192.png +0 -0
  72. package/apps/jsgotty/static/index.html +164 -0
  73. package/apps/jsgotty/static/js/gotty.js +31537 -0
  74. package/apps/jsgotty/static/js/gotty.js.LICENSE.txt +7 -0
  75. package/apps/jsgotty/static/js/gotty.js.map +1 -0
  76. package/apps/jsgotty/static/js/gotty.licenses.txt +650 -0
  77. package/apps/jsgotty/static/js/kitty.js +1045 -0
  78. package/apps/jsgotty/static/js/windows_bridge.js +194 -0
  79. package/apps/jsgotty/static/js/xterm-addon-unicode11.js +2 -0
  80. package/apps/jsgotty/static/js/zmodem.js +1 -0
  81. package/apps/jsgotty/static/manifest.json +15 -0
  82. package/apps/jsgotty/sz.js +273 -0
  83. package/apps/jsgotty/tests/kitty-order.test.js +58 -0
  84. package/apps/jsgotty/viu.mjs +393 -0
  85. package/apps/jsgotty/wgotty +62 -0
  86. package/apps/jsgotty/windows-bridge-node.js +60 -0
  87. package/apps/jsgotty/zmodem-node.js +260 -0
  88. package/apps/jsmdcui/CHANGELOG.md +1048 -0
  89. package/apps/jsmdcui/LICENSE +22 -0
  90. package/apps/jsmdcui/README.md +1269 -0
  91. package/apps/jsmdcui/cdp-maze.js +141 -0
  92. package/apps/jsmdcui/clean.sh +9 -0
  93. package/apps/jsmdcui/demos/basic.jpg +0 -0
  94. package/apps/jsmdcui/demos/chinese.md +32 -0
  95. package/apps/jsmdcui/demos/event.md +170 -0
  96. package/apps/jsmdcui/demos/good.jpg +0 -0
  97. package/apps/jsmdcui/demos/image-processor.md +366 -0
  98. package/apps/jsmdcui/demos/image-processor.zh-TW.md +366 -0
  99. package/apps/jsmdcui/demos/img-change.md +73 -0
  100. package/apps/jsmdcui/demos/imgtool-zh.md +298 -0
  101. package/apps/jsmdcui/demos/imgtool.md +298 -0
  102. package/apps/jsmdcui/demos/maze.md +149 -0
  103. package/apps/jsmdcui/demos/reactive.md +40 -0
  104. package/apps/jsmdcui/demos/reader.md +513 -0
  105. package/apps/jsmdcui/demos/select.md +63 -0
  106. package/apps/jsmdcui/demos/todo-zh.md +190 -0
  107. package/apps/jsmdcui/demos/todo.md +191 -0
  108. package/apps/jsmdcui/edit +9 -0
  109. package/apps/jsmdcui/jsmdcui.sh +5 -0
  110. package/apps/jsmdcui/llm-maze.txt +94 -0
  111. package/apps/jsmdcui/package.json +19 -0
  112. package/apps/jsmdcui/project_structure.txt +35 -0
  113. package/apps/jsmdcui/runmd.mjs +1140 -0
  114. package/apps/jsmdcui/runtime/colorschemes/atom-dark.micro +33 -0
  115. package/apps/jsmdcui/runtime/colorschemes/bubblegum.micro +31 -0
  116. package/apps/jsmdcui/runtime/colorschemes/cmc-16.micro +47 -0
  117. package/apps/jsmdcui/runtime/colorschemes/cmc-tc.micro +43 -0
  118. package/apps/jsmdcui/runtime/colorschemes/darcula.micro +34 -0
  119. package/apps/jsmdcui/runtime/colorschemes/default.micro +1 -0
  120. package/apps/jsmdcui/runtime/colorschemes/dracula-tc.micro +49 -0
  121. package/apps/jsmdcui/runtime/colorschemes/dukedark-tc.micro +38 -0
  122. package/apps/jsmdcui/runtime/colorschemes/dukelight-tc.micro +38 -0
  123. package/apps/jsmdcui/runtime/colorschemes/dukeubuntu-tc.micro +38 -0
  124. package/apps/jsmdcui/runtime/colorschemes/geany.micro +29 -0
  125. package/apps/jsmdcui/runtime/colorschemes/gotham.micro +29 -0
  126. package/apps/jsmdcui/runtime/colorschemes/gruvbox-tc.micro +29 -0
  127. package/apps/jsmdcui/runtime/colorschemes/gruvbox.micro +26 -0
  128. package/apps/jsmdcui/runtime/colorschemes/material-tc.micro +36 -0
  129. package/apps/jsmdcui/runtime/colorschemes/monokai-dark.micro +28 -0
  130. package/apps/jsmdcui/runtime/colorschemes/monokai.micro +34 -0
  131. package/apps/jsmdcui/runtime/colorschemes/one-dark.micro +39 -0
  132. package/apps/jsmdcui/runtime/colorschemes/railscast.micro +37 -0
  133. package/apps/jsmdcui/runtime/colorschemes/simple.micro +33 -0
  134. package/apps/jsmdcui/runtime/colorschemes/solarized-tc.micro +31 -0
  135. package/apps/jsmdcui/runtime/colorschemes/solarized.micro +30 -0
  136. package/apps/jsmdcui/runtime/colorschemes/sunny-day.micro +29 -0
  137. package/apps/jsmdcui/runtime/colorschemes/twilight.micro +40 -0
  138. package/apps/jsmdcui/runtime/colorschemes/zenburn.micro +30 -0
  139. package/apps/jsmdcui/runtime/help/actions.md +161 -0
  140. package/apps/jsmdcui/runtime/help/cdp.md +124 -0
  141. package/apps/jsmdcui/runtime/help/colors.md +421 -0
  142. package/apps/jsmdcui/runtime/help/commands.md +161 -0
  143. package/apps/jsmdcui/runtime/help/copypaste.md +149 -0
  144. package/apps/jsmdcui/runtime/help/defaultkeys.md +148 -0
  145. package/apps/jsmdcui/runtime/help/help.md +1269 -0
  146. package/apps/jsmdcui/runtime/help/keybindings.md +355 -0
  147. package/apps/jsmdcui/runtime/help/linter.md +90 -0
  148. package/apps/jsmdcui/runtime/help/options.md +709 -0
  149. package/apps/jsmdcui/runtime/help/plugins.md +544 -0
  150. package/apps/jsmdcui/runtime/help/tutorial.md +112 -0
  151. package/apps/jsmdcui/runtime/jsplugins/cdp/cdp-server.js +1185 -0
  152. package/apps/jsmdcui/runtime/jsplugins/cdp/cdp.js +201 -0
  153. package/apps/jsmdcui/runtime/jsplugins/chapter/chapter.js +111 -0
  154. package/apps/jsmdcui/runtime/jsplugins/diff/diff.js +46 -0
  155. package/apps/jsmdcui/runtime/jsplugins/example/example.js +117 -0
  156. package/apps/jsmdcui/runtime/jsplugins/linter/linter.js +281 -0
  157. package/apps/jsmdcui/runtime/plugins/autoclose/autoclose.lua +75 -0
  158. package/apps/jsmdcui/runtime/plugins/ftoptions/ftoptions.lua +17 -0
  159. package/apps/jsmdcui/runtime/plugins/literate/README.md +5 -0
  160. package/apps/jsmdcui/runtime/plugins/literate/literate.lua +55 -0
  161. package/apps/jsmdcui/runtime/plugins/status/help/status.md +21 -0
  162. package/apps/jsmdcui/runtime/plugins/status/status.lua +62 -0
  163. package/apps/jsmdcui/runtime/syntax/LICENSE +22 -0
  164. package/apps/jsmdcui/runtime/syntax/PowerShell.yaml +128 -0
  165. package/apps/jsmdcui/runtime/syntax/README.md +63 -0
  166. package/apps/jsmdcui/runtime/syntax/ada.yaml +43 -0
  167. package/apps/jsmdcui/runtime/syntax/apacheconf.yaml +59 -0
  168. package/apps/jsmdcui/runtime/syntax/arduino.yaml +101 -0
  169. package/apps/jsmdcui/runtime/syntax/asciidoc.yaml +51 -0
  170. package/apps/jsmdcui/runtime/syntax/asm.yaml +123 -0
  171. package/apps/jsmdcui/runtime/syntax/ats.yaml +99 -0
  172. package/apps/jsmdcui/runtime/syntax/awk.yaml +44 -0
  173. package/apps/jsmdcui/runtime/syntax/b.yaml +87 -0
  174. package/apps/jsmdcui/runtime/syntax/bat.yaml +57 -0
  175. package/apps/jsmdcui/runtime/syntax/c.yaml +60 -0
  176. package/apps/jsmdcui/runtime/syntax/caddyfile.yaml +23 -0
  177. package/apps/jsmdcui/runtime/syntax/cake.yaml +7 -0
  178. package/apps/jsmdcui/runtime/syntax/clojure.yaml +38 -0
  179. package/apps/jsmdcui/runtime/syntax/cmake.yaml +42 -0
  180. package/apps/jsmdcui/runtime/syntax/coffeescript.yaml +56 -0
  181. package/apps/jsmdcui/runtime/syntax/colortest.yaml +19 -0
  182. package/apps/jsmdcui/runtime/syntax/conky.yaml +17 -0
  183. package/apps/jsmdcui/runtime/syntax/cpp.yaml +91 -0
  184. package/apps/jsmdcui/runtime/syntax/crontab.yaml +36 -0
  185. package/apps/jsmdcui/runtime/syntax/crystal.yaml +72 -0
  186. package/apps/jsmdcui/runtime/syntax/csharp.yaml +52 -0
  187. package/apps/jsmdcui/runtime/syntax/css.yaml +44 -0
  188. package/apps/jsmdcui/runtime/syntax/csx.yaml +8 -0
  189. package/apps/jsmdcui/runtime/syntax/cuda.yaml +68 -0
  190. package/apps/jsmdcui/runtime/syntax/cython.yaml +52 -0
  191. package/apps/jsmdcui/runtime/syntax/d.yaml +121 -0
  192. package/apps/jsmdcui/runtime/syntax/dart.yaml +46 -0
  193. package/apps/jsmdcui/runtime/syntax/default.yaml +10 -0
  194. package/apps/jsmdcui/runtime/syntax/dockerfile.yaml +36 -0
  195. package/apps/jsmdcui/runtime/syntax/dot.yaml +29 -0
  196. package/apps/jsmdcui/runtime/syntax/elixir.yaml +30 -0
  197. package/apps/jsmdcui/runtime/syntax/elm.yaml +38 -0
  198. package/apps/jsmdcui/runtime/syntax/erb.yaml +42 -0
  199. package/apps/jsmdcui/runtime/syntax/erlang.yaml +45 -0
  200. package/apps/jsmdcui/runtime/syntax/fish.yaml +48 -0
  201. package/apps/jsmdcui/runtime/syntax/forth.yaml +34 -0
  202. package/apps/jsmdcui/runtime/syntax/fortran.yaml +64 -0
  203. package/apps/jsmdcui/runtime/syntax/freebsd-kernel.yaml +14 -0
  204. package/apps/jsmdcui/runtime/syntax/fsharp.yaml +48 -0
  205. package/apps/jsmdcui/runtime/syntax/gdscript.yaml +61 -0
  206. package/apps/jsmdcui/runtime/syntax/gemini.yaml +19 -0
  207. package/apps/jsmdcui/runtime/syntax/gentoo-ebuild.yaml +48 -0
  208. package/apps/jsmdcui/runtime/syntax/gentoo-etc-portage.yaml +23 -0
  209. package/apps/jsmdcui/runtime/syntax/git-commit.yaml +35 -0
  210. package/apps/jsmdcui/runtime/syntax/git-config.yaml +14 -0
  211. package/apps/jsmdcui/runtime/syntax/git-rebase-todo.yaml +19 -0
  212. package/apps/jsmdcui/runtime/syntax/gleam.yaml +69 -0
  213. package/apps/jsmdcui/runtime/syntax/glsl.yaml +26 -0
  214. package/apps/jsmdcui/runtime/syntax/gnuplot.yaml +15 -0
  215. package/apps/jsmdcui/runtime/syntax/go.yaml +62 -0
  216. package/apps/jsmdcui/runtime/syntax/godoc.yaml +17 -0
  217. package/apps/jsmdcui/runtime/syntax/golo.yaml +73 -0
  218. package/apps/jsmdcui/runtime/syntax/gomod.yaml +31 -0
  219. package/apps/jsmdcui/runtime/syntax/graphql.yaml +47 -0
  220. package/apps/jsmdcui/runtime/syntax/groff.yaml +30 -0
  221. package/apps/jsmdcui/runtime/syntax/groovy.yaml +111 -0
  222. package/apps/jsmdcui/runtime/syntax/haml.yaml +16 -0
  223. package/apps/jsmdcui/runtime/syntax/hare.yaml +52 -0
  224. package/apps/jsmdcui/runtime/syntax/haskell.yaml +52 -0
  225. package/apps/jsmdcui/runtime/syntax/hc.yaml +52 -0
  226. package/apps/jsmdcui/runtime/syntax/html.yaml +70 -0
  227. package/apps/jsmdcui/runtime/syntax/html4.yaml +25 -0
  228. package/apps/jsmdcui/runtime/syntax/html5.yaml +25 -0
  229. package/apps/jsmdcui/runtime/syntax/ini.yaml +23 -0
  230. package/apps/jsmdcui/runtime/syntax/inputrc.yaml +14 -0
  231. package/apps/jsmdcui/runtime/syntax/java.yaml +37 -0
  232. package/apps/jsmdcui/runtime/syntax/javascript.yaml +124 -0
  233. package/apps/jsmdcui/runtime/syntax/jinja2.yaml +19 -0
  234. package/apps/jsmdcui/runtime/syntax/json.yaml +39 -0
  235. package/apps/jsmdcui/runtime/syntax/jsonnet.yaml +92 -0
  236. package/apps/jsmdcui/runtime/syntax/julia.yaml +57 -0
  237. package/apps/jsmdcui/runtime/syntax/justfile.yaml +40 -0
  238. package/apps/jsmdcui/runtime/syntax/keymap.yaml +27 -0
  239. package/apps/jsmdcui/runtime/syntax/kickstart.yaml +16 -0
  240. package/apps/jsmdcui/runtime/syntax/kotlin.yaml +66 -0
  241. package/apps/jsmdcui/runtime/syntax/kvlang.yaml +67 -0
  242. package/apps/jsmdcui/runtime/syntax/ledger.yaml +14 -0
  243. package/apps/jsmdcui/runtime/syntax/lfe.yaml +17 -0
  244. package/apps/jsmdcui/runtime/syntax/lilypond.yaml +26 -0
  245. package/apps/jsmdcui/runtime/syntax/lisp.yaml +17 -0
  246. package/apps/jsmdcui/runtime/syntax/log.yaml +92 -0
  247. package/apps/jsmdcui/runtime/syntax/lua.yaml +111 -0
  248. package/apps/jsmdcui/runtime/syntax/mail.yaml +25 -0
  249. package/apps/jsmdcui/runtime/syntax/makefile.yaml +38 -0
  250. package/apps/jsmdcui/runtime/syntax/man.yaml +12 -0
  251. package/apps/jsmdcui/runtime/syntax/markdown.yaml +64 -0
  252. package/apps/jsmdcui/runtime/syntax/mc.yaml +23 -0
  253. package/apps/jsmdcui/runtime/syntax/meson.yaml +51 -0
  254. package/apps/jsmdcui/runtime/syntax/micro.yaml +34 -0
  255. package/apps/jsmdcui/runtime/syntax/mpdconf.yaml +13 -0
  256. package/apps/jsmdcui/runtime/syntax/msbuild.yaml +6 -0
  257. package/apps/jsmdcui/runtime/syntax/nanorc.yaml +16 -0
  258. package/apps/jsmdcui/runtime/syntax/nftables.yaml +30 -0
  259. package/apps/jsmdcui/runtime/syntax/nginx.yaml +22 -0
  260. package/apps/jsmdcui/runtime/syntax/nim.yaml +27 -0
  261. package/apps/jsmdcui/runtime/syntax/nix.yaml +32 -0
  262. package/apps/jsmdcui/runtime/syntax/nu.yaml +114 -0
  263. package/apps/jsmdcui/runtime/syntax/objc.yaml +60 -0
  264. package/apps/jsmdcui/runtime/syntax/ocaml.yaml +43 -0
  265. package/apps/jsmdcui/runtime/syntax/octave.yaml +83 -0
  266. package/apps/jsmdcui/runtime/syntax/odin.yaml +64 -0
  267. package/apps/jsmdcui/runtime/syntax/pascal.yaml +45 -0
  268. package/apps/jsmdcui/runtime/syntax/patch.yaml +14 -0
  269. package/apps/jsmdcui/runtime/syntax/peg.yaml +16 -0
  270. package/apps/jsmdcui/runtime/syntax/perl.yaml +58 -0
  271. package/apps/jsmdcui/runtime/syntax/php.yaml +60 -0
  272. package/apps/jsmdcui/runtime/syntax/pkg-config.yaml +12 -0
  273. package/apps/jsmdcui/runtime/syntax/po.yaml +12 -0
  274. package/apps/jsmdcui/runtime/syntax/pony.yaml +37 -0
  275. package/apps/jsmdcui/runtime/syntax/pov.yaml +21 -0
  276. package/apps/jsmdcui/runtime/syntax/privoxy-action.yaml +14 -0
  277. package/apps/jsmdcui/runtime/syntax/privoxy-config.yaml +10 -0
  278. package/apps/jsmdcui/runtime/syntax/privoxy-filter.yaml +12 -0
  279. package/apps/jsmdcui/runtime/syntax/proto.yaml +40 -0
  280. package/apps/jsmdcui/runtime/syntax/prql.yaml +84 -0
  281. package/apps/jsmdcui/runtime/syntax/puppet.yaml +22 -0
  282. package/apps/jsmdcui/runtime/syntax/python2.yaml +60 -0
  283. package/apps/jsmdcui/runtime/syntax/python3.yaml +80 -0
  284. package/apps/jsmdcui/runtime/syntax/r.yaml +32 -0
  285. package/apps/jsmdcui/runtime/syntax/raku.yaml +42 -0
  286. package/apps/jsmdcui/runtime/syntax/reST.yaml +18 -0
  287. package/apps/jsmdcui/runtime/syntax/renpy.yaml +15 -0
  288. package/apps/jsmdcui/runtime/syntax/rpmspec.yaml +43 -0
  289. package/apps/jsmdcui/runtime/syntax/ruby.yaml +73 -0
  290. package/apps/jsmdcui/runtime/syntax/rust.yaml +78 -0
  291. package/apps/jsmdcui/runtime/syntax/sage.yaml +60 -0
  292. package/apps/jsmdcui/runtime/syntax/scad.yaml +53 -0
  293. package/apps/jsmdcui/runtime/syntax/scala.yaml +33 -0
  294. package/apps/jsmdcui/runtime/syntax/sed.yaml +13 -0
  295. package/apps/jsmdcui/runtime/syntax/sh.yaml +69 -0
  296. package/apps/jsmdcui/runtime/syntax/sls.yaml +15 -0
  297. package/apps/jsmdcui/runtime/syntax/smalltalk.yaml +55 -0
  298. package/apps/jsmdcui/runtime/syntax/solidity.yaml +41 -0
  299. package/apps/jsmdcui/runtime/syntax/sql.yaml +35 -0
  300. package/apps/jsmdcui/runtime/syntax/stata.yaml +67 -0
  301. package/apps/jsmdcui/runtime/syntax/svelte.yaml +27 -0
  302. package/apps/jsmdcui/runtime/syntax/swift.yaml +103 -0
  303. package/apps/jsmdcui/runtime/syntax/systemd.yaml +16 -0
  304. package/apps/jsmdcui/runtime/syntax/tcl.yaml +18 -0
  305. package/apps/jsmdcui/runtime/syntax/terraform.yaml +87 -0
  306. package/apps/jsmdcui/runtime/syntax/tex.yaml +32 -0
  307. package/apps/jsmdcui/runtime/syntax/toml.yaml +56 -0
  308. package/apps/jsmdcui/runtime/syntax/twig.yaml +55 -0
  309. package/apps/jsmdcui/runtime/syntax/typescript.yaml +49 -0
  310. package/apps/jsmdcui/runtime/syntax/v.yaml +80 -0
  311. package/apps/jsmdcui/runtime/syntax/vala.yaml +26 -0
  312. package/apps/jsmdcui/runtime/syntax/verilog.yaml +60 -0
  313. package/apps/jsmdcui/runtime/syntax/vhdl.yaml +37 -0
  314. package/apps/jsmdcui/runtime/syntax/vi.yaml +31 -0
  315. package/apps/jsmdcui/runtime/syntax/vue.yaml +64 -0
  316. package/apps/jsmdcui/runtime/syntax/xml.yaml +37 -0
  317. package/apps/jsmdcui/runtime/syntax/xresources.yaml +14 -0
  318. package/apps/jsmdcui/runtime/syntax/yaml.yaml +34 -0
  319. package/apps/jsmdcui/runtime/syntax/yum.yaml +12 -0
  320. package/apps/jsmdcui/runtime/syntax/zig.yaml +52 -0
  321. package/apps/jsmdcui/runtime/syntax/zscript.yaml +72 -0
  322. package/apps/jsmdcui/runtime/syntax/zsh.yaml +52 -0
  323. package/apps/jsmdcui/single-exe/README.md +473 -0
  324. package/apps/jsmdcui/single-exe/assetsHelper.js +191 -0
  325. package/apps/jsmdcui/single-exe/assetsLoader.mjs +85 -0
  326. package/apps/jsmdcui/single-exe/compiled.js +212 -0
  327. package/apps/jsmdcui/single-exe/entry.mjs +10 -0
  328. package/apps/jsmdcui/single-exe/packAssets.sh +7 -0
  329. package/apps/jsmdcui/src/MDCUI_DEFAULT_EDIT +0 -0
  330. package/apps/jsmdcui/src/buffer/backup.js +165 -0
  331. package/apps/jsmdcui/src/buffer/buffer.js +126 -0
  332. package/apps/jsmdcui/src/buffer/fixed3-codec.js +140 -0
  333. package/apps/jsmdcui/src/buffer/loc.js +38 -0
  334. package/apps/jsmdcui/src/buffer/message.js +29 -0
  335. package/apps/jsmdcui/src/build-args.js +40 -0
  336. package/apps/jsmdcui/src/config/clean.js +172 -0
  337. package/apps/jsmdcui/src/config/colorscheme.js +99 -0
  338. package/apps/jsmdcui/src/config/config.js +125 -0
  339. package/apps/jsmdcui/src/config/defaults.js +109 -0
  340. package/apps/jsmdcui/src/cui/fence-events.mjs +144 -0
  341. package/apps/jsmdcui/src/cui/heading-ids.mjs +1568 -0
  342. package/apps/jsmdcui/src/cui/id-collision.mjs +111 -0
  343. package/apps/jsmdcui/src/cui/identity.mjs +26 -0
  344. package/apps/jsmdcui/src/cui/kitty-debug.mjs +25 -0
  345. package/apps/jsmdcui/src/cui/kitty-images.mjs +280 -0
  346. package/apps/jsmdcui/src/cui/kitty-mode.mjs +6 -0
  347. package/apps/jsmdcui/src/cui/rerender-queue.mjs +66 -0
  348. package/apps/jsmdcui/src/cui/rpc.mjs +1525 -0
  349. package/apps/jsmdcui/src/cui/server.mjs +128 -0
  350. package/apps/jsmdcui/src/cui/table-render.mjs +286 -0
  351. package/apps/jsmdcui/src/cui/table-row-edit.mjs +226 -0
  352. package/apps/jsmdcui/src/cui/task-checkbox.mjs +60 -0
  353. package/apps/jsmdcui/src/cui/template-components.mjs +253 -0
  354. package/apps/jsmdcui/src/cui/text-block-resize.mjs +177 -0
  355. package/apps/jsmdcui/src/cui/tui-links.mjs +560 -0
  356. package/apps/jsmdcui/src/display/ansi-style.js +60 -0
  357. package/apps/jsmdcui/src/highlight/highlighter.js +243 -0
  358. package/apps/jsmdcui/src/highlight/parser.js +225 -0
  359. package/apps/jsmdcui/src/index.js +9573 -0
  360. package/apps/jsmdcui/src/lua/engine.js +68 -0
  361. package/apps/jsmdcui/src/platform/archive.js +50 -0
  362. package/apps/jsmdcui/src/platform/clipboard.js +278 -0
  363. package/apps/jsmdcui/src/platform/commands.js +221 -0
  364. package/apps/jsmdcui/src/platform/terminal.js +5 -0
  365. package/apps/jsmdcui/src/plugins/js-bridge.js +3377 -0
  366. package/apps/jsmdcui/src/plugins/manager.js +674 -0
  367. package/apps/jsmdcui/src/runtime/encodings.js +10 -0
  368. package/apps/jsmdcui/src/runtime/registry.js +134 -0
  369. package/apps/jsmdcui/src/screen/cell-buffer.js +81 -0
  370. package/apps/jsmdcui/src/screen/events.js +263 -0
  371. package/apps/jsmdcui/src/screen/screen.js +249 -0
  372. package/apps/jsmdcui/src/screen/vt100.js +571 -0
  373. package/apps/jsmdcui/src/shell/shell.js +70 -0
  374. package/apps/jsmdcui/testapp.md +78 -0
  375. package/apps/jsmdcui/tests/assets-helper.test.js +150 -0
  376. package/apps/jsmdcui/tests/backup.test.js +158 -0
  377. package/apps/jsmdcui/tests/cat-markdown.test.js +139 -0
  378. package/apps/jsmdcui/tests/cmphex3b64.js +95 -0
  379. package/apps/jsmdcui/tests/compiled-runtime.test.js +139 -0
  380. package/apps/jsmdcui/tests/config-settings.test.js +52 -0
  381. package/apps/jsmdcui/tests/demo.test.js +447 -0
  382. package/apps/jsmdcui/tests/docs-sync.test.js +14 -0
  383. package/apps/jsmdcui/tests/fence-events.test.js +1293 -0
  384. package/apps/jsmdcui/tests/heading-ids.test.js +559 -0
  385. package/apps/jsmdcui/tests/heading-list-selector.test.js +2393 -0
  386. package/apps/jsmdcui/tests/id-collision.test.js +514 -0
  387. package/apps/jsmdcui/tests/js-plugin-prompts.test.js +110 -0
  388. package/apps/jsmdcui/tests/key-event.md +10 -0
  389. package/apps/jsmdcui/tests/kitty-demo.md +184 -0
  390. package/apps/jsmdcui/tests/kitty-images.test.js +288 -0
  391. package/apps/jsmdcui/tests/kitty-mode.test.js +14 -0
  392. package/apps/jsmdcui/tests/platform-commands.test.js +18 -0
  393. package/apps/jsmdcui/tests/platform-terminal.test.js +8 -0
  394. package/apps/jsmdcui/tests/pty-demo.js +492 -0
  395. package/apps/jsmdcui/tests/rerender-queue.test.js +130 -0
  396. package/apps/jsmdcui/tests/table-render.test.js +150 -0
  397. package/apps/jsmdcui/tests/table-row-edit.test.js +138 -0
  398. package/apps/jsmdcui/tests/task-checkbox.test.js +33 -0
  399. package/apps/jsmdcui/tests/template-components.test.js +422 -0
  400. package/apps/jsmdcui/tests/text-block-resize.test.js +222 -0
  401. package/apps/jsmdcui/tests/textarea.md +8 -0
  402. package/apps/jsmdcui/tests/tui-links.test.js +298 -0
  403. package/apps/jsmdcui/tests/wui-responsive-images.test.js +60 -0
  404. package/apps/jsmdcui/tests/wui.test.js +108 -0
  405. package/apps/jsmdcui/tests/wv-client.js +96 -0
  406. package/apps/jsmdcui/tui +7 -0
  407. package/apps/jsmdcui/wui +9 -0
  408. package/apps/musl-la/LICENSES/Apache-2.0_LLVM_Exceptions_license.txt +311 -0
  409. package/apps/musl-la/LICENSES/Apache-2.0_license.txt +177 -0
  410. package/apps/musl-la/LICENSES/BSD-3-Clause_license.txt +43 -0
  411. package/apps/musl-la/LICENSES/GPL-3.0.txt +674 -0
  412. package/apps/musl-la/LICENSES/LGPL-3.0.txt +165 -0
  413. package/apps/musl-la/LICENSES/MIT.txt +21 -0
  414. package/apps/musl-la/LICENSES/curl_license.txt +22 -0
  415. package/apps/musl-la/LICENSES/gcc_runtime_exception.txt +73 -0
  416. package/apps/musl-la/LICENSES/zlib_license.txt +22 -0
  417. package/apps/musl-la/LICENSE_musl.txt +193 -0
  418. package/apps/musl-la/NOTICE +56 -0
  419. package/apps/musl-la/ldmusl +0 -0
  420. package/apps/musl-la/libgcc_s.so.1 +0 -0
  421. package/apps/musl-la/libstdc++.so.6 +0 -0
  422. package/apps/musl-la/musl-la.sh +5 -0
  423. package/bin/bun.sh +57 -0
  424. package/bin/glow.sh +5 -0
  425. package/bin/init.js +395 -0
  426. package/bin/jmi.sh +5 -0
  427. package/bin/jsgotty.sh +5 -0
  428. package/bin/libsh-loader.c +135 -0
  429. package/bin/libsh-loader.so +0 -0
  430. package/bin/multicall.sh +90 -0
  431. package/bin/shc.js +5 -0
  432. package/bin/switch_to_android.sh +9 -0
  433. package/bin/switch_to_linux.sh +7 -0
  434. package/icon.png +0 -0
  435. package/package.json +31 -0
@@ -0,0 +1,149 @@
1
+ ```text#character
2
+ 🧱😀🧱🧱🧱🧱🧱🧱🧱🧱🧱🧱
3
+ 🧱 🧱 🧱
4
+ 🧱 🧱🧱🧱 🧱 🧱🧱🧱🧱
5
+ 🧱 🧱 🧱 🧱
6
+ 🧱🧱🧱 🧱🧱🧱🧱🧱 🧱🧱
7
+ 🧱 🧱 🧱 🧱
8
+ 🧱 🧱🧱🧱 🧱 🧱 🧱🧱
9
+ 🧱 🧱 🧱 🧱
10
+ 🧱 🧱🧱🧱🧱🧱🧱🧱 🧱🧱
11
+ 🧱 🧱 🧱
12
+ 🧱 🧱 🧱🧱🧱 🧱🧱🧱🧱
13
+ 🧱 🧱 🧱
14
+ 🧱🧱🧱🧱🧱🧱🧱🧱🧱 🧱🧱
15
+ ```
16
+
17
+ ```text#controls @keydown.prevent="handle(event)"
18
+ Put the cursor here, then press:
19
+ 將遊標放在這裡,然後按:
20
+
21
+ L't half of QWERTY ← move left
22
+ R't half of QWERTY → move right
23
+ T / Y ↑ move up
24
+ Space ↓ move down
25
+
26
+ 鍵盤左半邊 ← 向左移動
27
+ 鍵盤右半邊 → 向右移動
28
+ T / Y 向上移動
29
+ 空白鍵向下移動
30
+
31
+ Arrow keys ←↑→↓ / 正常方向移動
32
+ Ctrl-R Reset maze / 重新開始
33
+
34
+ Do not hit 🧱 / 不能撞牆
35
+ ```
36
+
37
+ ```text#last-key
38
+ Waiting for input / 等待輸入
39
+ ```
40
+
41
+ [🔄 Reset maze / 重新開始](javascript:reset())
42
+
43
+ ```js front
44
+ const LEFT_KEYS = new Set([
45
+ ...'`12345qwerasdfgzxcvb',
46
+ 'escape', 'tab',
47
+ ]);
48
+ const RIGHT_KEYS = new Set([
49
+ ...'67890-=uiop[]\\hjkl;\'nm,./',
50
+ 'backspace', 'enter', 'delete',
51
+ 'home', 'end', 'pageup', 'pagedown',
52
+ ]);
53
+ const MAZE = [
54
+ '# ##########',
55
+ '# # #',
56
+ '# ### # ####',
57
+ '# # # #',
58
+ '### ##### ##',
59
+ '# # # #',
60
+ '# ### # # ##',
61
+ '# # # #',
62
+ '# ####### ##',
63
+ '# # #',
64
+ '# # ### ####',
65
+ '# # #',
66
+ '######### ##',
67
+ ];
68
+ const GOAL = { row: MAZE.length - 1, col: 9 };
69
+ const player = { row: 0, col: 1 };
70
+ let completed = false;
71
+
72
+ function drawMaze() {
73
+ const screen = MAZE.map((line, row) => [...line].map((cell, col) => {
74
+ if (row === player.row && col === player.col) return '😀';
75
+ return cell === '#' ? '🧱' : ' ';
76
+ }).join('')).join('\n');
77
+ $('#character').val(screen);
78
+ }
79
+
80
+ export function reset() {
81
+ player.row = 0;
82
+ player.col = 1;
83
+ completed = false;
84
+ drawMaze();
85
+ $('#last-key').val('Waiting for input / 等待輸入');
86
+ }
87
+
88
+ export function handle(event) {
89
+ const key = String(event.key ?? '').toLowerCase();
90
+ if (event.ctrlKey && key === 'r') {
91
+ reset();
92
+ return;
93
+ }
94
+ if (completed) return;
95
+
96
+ let row = player.row;
97
+ let col = player.col;
98
+ let direction;
99
+
100
+ if (key === 't' || key === 'y' || key === 'arrowup') {
101
+ row--;
102
+ direction = 'up / 上';
103
+ } else if (key === ' ' || key === 'arrowdown') {
104
+ row++;
105
+ direction = 'down / 下';
106
+ } else if (key === 'arrowleft' || LEFT_KEYS.has(key)) {
107
+ col--;
108
+ direction = 'left / 左';
109
+ } else if (key === 'arrowright' || RIGHT_KEYS.has(key)) {
110
+ col++;
111
+ direction = 'right / 右';
112
+ } else {
113
+ $('#last-key').val(`Ignored / 忽略:${event.key}`);
114
+ return;
115
+ }
116
+
117
+ if (MAZE[row]?.[col] !== ' ') {
118
+ $('#last-key').val(`${event.key === ' ' ? 'Space' : event.key} → 🧱 Wall / 撞牆`);
119
+ return;
120
+ }
121
+
122
+ player.row = row;
123
+ player.col = col;
124
+ drawMaze();
125
+
126
+ if (row === GOAL.row && col === GOAL.col) {
127
+ completed = true;
128
+ $('#last-key').val('🎉🏆🌟 Escaped the maze! / 成功走出迷宮!🌟🏆🎉');
129
+ $('#character').val([
130
+ '',
131
+ '🎆🎇🎉 恭 喜 過 關 ! 🎉🎇🎆',
132
+ '',
133
+ '🥳 你成功逃出迷宮了! 🥳',
134
+ '👑 🏆 ⭐ 🌟 💫 🌈 🦄',
135
+ '👏👏👏 AMAZING! 👏👏👏',
136
+ '🎈 🎊 🎁 🪩 🎁 🎊 🎈',
137
+ '',
138
+ '🚀 ESCAPED THE MAZE! 🚀',
139
+ '',
140
+ 'Press Ctrl-R to reset',
141
+ '按 Ctrl-R 重新開始',
142
+ ''
143
+ ].join('\n'));
144
+ } else {
145
+ const label = event.key === ' ' ? 'Space' : event.key;
146
+ $('#last-key').val(`${label} → ${direction} (${row}, ${col})`);
147
+ }
148
+ }
149
+ ```
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env jsmdcui
2
+
3
+ # Reactive Template Demo / 響應式模板示範
4
+
5
+ Type a name below. After you stop typing for 200 ms, the template updates in
6
+ both the TUI and WUI.
7
+
8
+ 在下方輸入名字;停止輸入 200 毫秒後,TUI 與 WUI 中的模板都會自動更新。
9
+
10
+ ```text#reactive-name @input="updateReactiveName(event)"
11
+ Johnny
12
+ ```
13
+
14
+ ## Reactive Result
15
+
16
+ ````md template
17
+ ---
18
+ name: Johnny
19
+ ---
20
+ 恭喜你,**${data.name}**,成就金仙!
21
+
22
+ Congratulations, **${data.name}**! You have attained the Golden Immortal realm.
23
+ ````
24
+
25
+ ```js front
26
+ let reactiveNameTimer = null;
27
+
28
+ export function updateReactiveName(event) {
29
+
30
+ const name = $(event.target).val().trim() || '無名道友 / Nameless Daoist';
31
+
32
+
33
+ clearTimeout(reactiveNameTimer);
34
+ reactiveNameTimer = setTimeout(() => {
35
+
36
+ $('#reactive-result').data('name', name);
37
+
38
+ }, 200);
39
+ }
40
+ ```
@@ -0,0 +1,513 @@
1
+ #!/usr/bin/env jsmdcui
2
+
3
+ # File List Reader / 檔案列表閱讀器
4
+
5
+ Enter a local directory or web page URL, then press Enter or choose **List**.
6
+ Tab completes local directories. 輸入本機目錄或網頁網址後按 Enter 或選擇
7
+ 「列出」;Tab 可補全本機目錄。
8
+
9
+ ```text#directory @keydown="directoryKey(event)"
10
+ .
11
+ ```
12
+
13
+ - [List files or links / 列出檔案或連結](javascript:listDirectory())
14
+
15
+ ## File List / 檔案列表
16
+
17
+ ````js template
18
+ ---
19
+ filelist: []
20
+ listPage: 0
21
+ ---
22
+ const size = 10
23
+ const files = Array.isArray(data.filelist) ? data.filelist : []
24
+ const pages = Math.max(1, Math.ceil(files.length / size))
25
+ const page = Math.max(0, Math.min(Number(data.listPage) || 0, pages - 1))
26
+ let markdown = `Page ${page + 1} / ${pages} · ${files.length} files\n\n`
27
+ for (const file of files.slice(page * size, page * size + size)) {
28
+ const label = String(file.name).replaceAll('\\', '\\\\').replaceAll('[', '\\[').replaceAll(']', '\\]')
29
+ const value = encodeURIComponent(String(file.value))
30
+ markdown += `- [${label}](javascript:selectFile(decodeURIComponent('${value}')))\n`
31
+ }
32
+ if (!files.length) markdown += '- *(No files / 沒有檔案)*\n'
33
+ return markdown
34
+ ````
35
+
36
+ ## Selected File/URL 已選檔案/網址
37
+
38
+ ```text#selected
39
+
40
+ ```
41
+
42
+ ## Controls / 控制
43
+
44
+ | Previous page | Page | Go | Next page | Clear selection |
45
+ | --- | --- | --- | --- | --- |
46
+ | [上一頁](javascript:changeListPage(-1)) | 1 | [跳轉](javascript:jumpListPage(this)) | [下一頁](javascript:changeListPage(1)) | [清除已選](javascript:clearSelection()) |
47
+ | [🚀載入](javascript:loadSelected()) | [⏮上一章](javascript:loadRelative(-1)) | [⏭下一章](javascript:loadRelative(1)) | [⏹停止朗讀](javascript:stopReading()) | [📢朗讀](javascript:readFromCurrentPage()) |
48
+
49
+ ## Document Reader / 文件閱讀器
50
+ - asdf = prev page
51
+ - hjkl = next page
52
+
53
+ ### More info click 'M' 更多說明按'M'
54
+
55
+ Focus the reader and use the left half of QWERTY, Left/Up, or PageUp for the
56
+ previous page. Use the right half of QWERTY, Right/Down, or PageDown for the
57
+ next page.
58
+
59
+ 將遊標放在閱讀框;QWERTY 左半邊、左/上方向鍵或 PageUp 切到上一頁,右半邊、
60
+ 右/下方向鍵或 PageDown 切到下一頁。
61
+
62
+ To replace text while loading, use `replace.json`. For a local file, place it
63
+ beside that file; for a URL, place it in `process.cwd()`. It is parsed as JSON5
64
+ and must contain an array of two-item arrays. The first item is passed to
65
+ `new RegExp(target, "g")`; the second item is inserted as literal replacement
66
+ text. Invalid or unparseable rules are ignored.
67
+
68
+ 載入本機檔案時,可在檔案同一目錄放置 `replace.json`;載入 URL 時則使用
69
+ `process.cwd()` 下的 `replace.json`。檔案會以 JSON5 解析,格式為二項陣列所組成
70
+ 的陣列;第一項會傳給 `new RegExp(target, "g")`,第二項是純文字取代結果。
71
+ 無效或無法解析時會忽略規則並正常載入原文。
72
+
73
+ ```json5
74
+ [
75
+ ["old\\s+name", "new name"],
76
+ ["第[零〇]章", "序章"],
77
+ ]
78
+ ```
79
+
80
+ Button translations / 按鈕翻譯:
81
+
82
+ - `上一頁` — Previous page
83
+ - `跳轉` — Go to the entered page
84
+ - `下一頁` — Next page
85
+ - `清除已選` — Clear selection
86
+ - `🚀載入` — Load the selected file or URL
87
+ - `⏮上一章` — Previous chapter
88
+ - `⏭下一章` — Next chapter
89
+ - `⏹停止朗讀` — Stop reading aloud
90
+ - `📢朗讀` — Start reading aloud from the current page
91
+ - `速度` — Speech speed
92
+ - `音高` — Speech pitch
93
+ - `套用` — Apply
94
+
95
+ ### Document content
96
+
97
+ ```textarea#reader @keydown.prevent="readerKey(event)"
98
+
99
+
100
+
101
+
102
+
103
+
104
+
105
+
106
+
107
+
108
+
109
+
110
+ ```
111
+
112
+ ### Reader controls / 閱讀控制
113
+
114
+ | Prev page / Speed | Page | Go / Pitch | Next page |
115
+ | --- | --- | --- | --- |
116
+ | [上一頁](javascript:previousReaderPage()) | 1 | [跳轉](javascript:jumpReaderPage(this)) | [下一頁](javascript:nextReaderPage()) |
117
+ | 速度: [1.5](javascript:applySpeechSetting(this,'speed',false)) | [套用 / Apply](javascript:applySpeechSetting(this,'speed')) | 音高: [1.0](javascript:applySpeechSetting(this,'pitch',false)) | [套用 / Apply](javascript:applySpeechSetting(this,'pitch')) |
118
+
119
+ ```text#reader-status
120
+ Not loaded / 尚未載入
121
+ ```
122
+
123
+ ```js front
124
+ const PAGE_ROWS = 12;
125
+ const PAGE_COLUMNS = 64;
126
+ const LEFT_KEYS = new Set([
127
+ ...'`12345qwerasdfgzxcvb',
128
+ 'escape', 'tab',
129
+ ]);
130
+ const RIGHT_KEYS = new Set([
131
+ ...'67890-=uiop[]\\hjkl;\'nm,./',
132
+ 'backspace', 'enter', 'delete', 'home', 'end',
133
+ ]);
134
+
135
+ let pages = [''];
136
+ let pageIndex = 0;
137
+ let selectedIndex = -1;
138
+ let readingRevision = 0;
139
+ let loadedSource = '';
140
+ let speechSpeed = 1.5;
141
+ let speechPitch = 1.0;
142
+
143
+ function message(text) {
144
+ $('#reader-status').val(String(text));
145
+ }
146
+
147
+ function fileState() {
148
+ return $('#file-list').data();
149
+ }
150
+
151
+ function pageCount() {
152
+ return Math.max(1, Math.ceil((fileState().filelist?.length || 0) / 10));
153
+ }
154
+
155
+ function selectedValue() {
156
+ return $('#selected').val().trim();
157
+ }
158
+
159
+ function wrapText(text) {
160
+ const rows = [];
161
+ for (const rawLine of String(text ?? '').replace(/\r\n?/g, '\n').split('\n')) {
162
+ const characters = [...rawLine];
163
+ if (!characters.length) {
164
+ rows.push('');
165
+ continue;
166
+ }
167
+ for (let offset = 0; offset < characters.length; offset += PAGE_COLUMNS)
168
+ rows.push(characters.slice(offset, offset + PAGE_COLUMNS).join(''));
169
+ }
170
+ if (!rows.length) rows.push('');
171
+ const result = [];
172
+ for (let offset = 0; offset < rows.length; offset += PAGE_ROWS)
173
+ result.push(rows.slice(offset, offset + PAGE_ROWS).join('\n'));
174
+ return result.length ? result : [''];
175
+ }
176
+
177
+ function showReaderPage(nextPage = pageIndex) {
178
+ pageIndex = Math.max(0, Math.min(Number(nextPage) || 0, pages.length - 1));
179
+ const rows = String(pages[pageIndex] ?? '').split('\n');
180
+ while (rows.length < PAGE_ROWS) rows.push('');
181
+ $('#reader').val(rows.slice(0, PAGE_ROWS).join('\n'));
182
+ $('#reader-controls').cell(1, 1).text(String(pageIndex + 1));
183
+ message(`${loadedSource || 'Reader'} · Page ${pageIndex + 1} / ${pages.length}`);
184
+ }
185
+
186
+ function trailingNumber(text) {
187
+ const match = String(text ?? '').trim().match(/(-?(?:\d+(?:\.\d*)?|\.\d+))\s*$/u);
188
+ return match ? Number(match[1]) : NaN;
189
+ }
190
+
191
+ export function jumpReaderPage(link) {
192
+ const value = trailingNumber($(link).parent()?.left()?.text());
193
+ if (!Number.isFinite(value)) {
194
+ message('Invalid page number / 頁碼無效');
195
+ return;
196
+ }
197
+ showReaderPage(Math.trunc(value) - 1);
198
+ }
199
+
200
+ export function applySpeechSetting(link, kind, fromLeft = true) {
201
+ const cell = $(link).parent();
202
+ const value = trailingNumber((fromLeft ? cell?.left() : cell)?.text());
203
+ if (!Number.isFinite(value) || value <= 0) {
204
+ message(`Invalid ${kind} / ${kind === 'speed' ? '速度' : '音高'}無效`);
205
+ return;
206
+ }
207
+ if (kind === 'speed') speechSpeed = value;
208
+ else speechPitch = value;
209
+ message(`${kind === 'speed' ? 'Speed / 速度' : 'Pitch / 音高'}: ${value}`);
210
+ }
211
+
212
+ function sentences(text) {
213
+ return String(text ?? '')
214
+ .split(/(?<=[.!?。!?…;;])\s*|[\r\n]+/u)
215
+ .map(value => value.trim())
216
+ .filter(Boolean);
217
+ }
218
+
219
+ export async function onMdcuiLoad() {
220
+ $("#more-info-click-m-m").hide();
221
+ await listDirectory();
222
+ showReaderPage(0);
223
+ }
224
+
225
+ export function directoryKey(event) {
226
+ if (event.key === 'Tab') {
227
+ event.preventDefault();
228
+ return completeDirectory();
229
+ }
230
+ if (event.key === 'Enter') {
231
+ event.preventDefault();
232
+ return listDirectory();
233
+ }
234
+ }
235
+
236
+ export async function completeDirectory() {
237
+ try {
238
+ const result = await rpc.completeReaderDirectory($('#directory').val());
239
+ if (result.matches > 0) $('#directory').val(result.directory);
240
+ message(result.matches > 0
241
+ ? `Directory completion: ${result.matches} match${result.matches === 1 ? '' : 'es'}`
242
+ : 'No matching directory / 找不到符合的目錄');
243
+ } catch (error) {
244
+ message(`Completion failed: ${error?.message || error}`);
245
+ }
246
+ }
247
+
248
+ export async function listDirectory() {
249
+ try {
250
+ const result = await rpc.listReaderDirectory($('#directory').val().trim());
251
+ $('#directory').val(result.directory);
252
+ selectedIndex = -1;
253
+ $('#file-list').data({ filelist: result.files, listPage: 0 });
254
+ $('#controls').cell(1, 1).text('1');
255
+ message(`Listed ${result.files.length} files from ${result.directory}`);
256
+ } catch (error) {
257
+ message(`List failed: ${error?.message || error}`);
258
+ }
259
+ }
260
+
261
+ export function selectFile(value) {
262
+ const files = fileState().filelist || [];
263
+ selectedIndex = files.findIndex(file => file.value === value);
264
+ $('#selected').val(value);
265
+ if (selectedIndex >= 0) setListPage(Math.floor(selectedIndex / 10));
266
+ message(`Selected: ${value}`);
267
+ }
268
+
269
+ function setListPage(nextPage) {
270
+ const data = fileState();
271
+ const next = Math.max(0, Math.min(Number(nextPage) || 0, pageCount() - 1));
272
+ $('#file-list').data('listPage', next);
273
+ $('#controls').cell(1, 1).text(String(next + 1));
274
+ }
275
+
276
+ export function changeListPage(delta) {
277
+ setListPage((Number(fileState().listPage) || 0) + delta);
278
+ }
279
+
280
+ export function jumpListPage(link) {
281
+ const value = trailingNumber($(link).parent()?.left()?.text());
282
+ if (!Number.isFinite(value)) {
283
+ message('Invalid list page number / 檔案列表頁碼無效');
284
+ return;
285
+ }
286
+ setListPage(Math.trunc(value) - 1);
287
+ }
288
+
289
+ export function selectRelative(delta) {
290
+ const files = fileState().filelist || [];
291
+ if (!files.length) return;
292
+ if (selectedIndex < 0) {
293
+ const current = selectedValue();
294
+ selectedIndex = files.findIndex(file => file.value === current);
295
+ }
296
+ selectedIndex = Math.max(0, Math.min(
297
+ selectedIndex < 0 ? (delta > 0 ? 0 : files.length - 1) : selectedIndex + delta,
298
+ files.length - 1,
299
+ ));
300
+ selectFile(files[selectedIndex].value);
301
+ }
302
+
303
+ export function clearSelection() {
304
+ selectedIndex = -1;
305
+ $('#selected').val('');
306
+ message('Selection cleared / 已清除選取');
307
+ }
308
+
309
+ function numberedSibling(value, delta) {
310
+ const source = String(value ?? '');
311
+ const suffixAt = source.search(/[?#]/u);
312
+ const path = suffixAt < 0 ? source : source.slice(0, suffixAt);
313
+ const suffix = suffixAt < 0 ? '' : source.slice(suffixAt);
314
+ const separatorAt = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\'));
315
+ const directory = path.slice(0, separatorAt + 1);
316
+ const filename = path.slice(separatorAt + 1);
317
+ const matches = [...filename.matchAll(/\d+/gu)];
318
+ const match = matches.at(-1);
319
+ if (!match) return null;
320
+ const current = BigInt(match[0]);
321
+ const next = current + BigInt(delta);
322
+ if (next < 0n) return null;
323
+ const replacement = String(next).padStart(match[0].length, '0');
324
+ return directory
325
+ + filename.slice(0, match.index)
326
+ + replacement
327
+ + filename.slice(match.index + match[0].length)
328
+ + suffix;
329
+ }
330
+
331
+ export async function loadSelected() {
332
+ const value = selectedValue();
333
+ if (!value) {
334
+ message('Load failed: select a file or enter a URL first');
335
+ return;
336
+ }
337
+ stopReading();
338
+ try {
339
+ const result = await rpc.loadReaderResource(value);
340
+ loadedSource = result.source;
341
+ pages = wrapText(result.text);
342
+ pageIndex = 0;
343
+ showReaderPage(0);
344
+ } catch (error) {
345
+ message(`Load failed: ${error?.message || error}`);
346
+ }
347
+ }
348
+
349
+ export async function loadRelative(delta) {
350
+ const numbered = numberedSibling(selectedValue(), delta);
351
+ if (numbered) selectFile(numbered);
352
+ else selectRelative(delta);
353
+ await loadSelected();
354
+ }
355
+
356
+ export function previousReaderPage() {
357
+ showReaderPage(pageIndex - 1);
358
+ }
359
+
360
+ export function nextReaderPage() {
361
+ showReaderPage(pageIndex + 1);
362
+ }
363
+
364
+ export function readerKey(event) {
365
+ const key = String(event.key ?? '').toLowerCase();
366
+ if (key === 'pageup' || key === 'arrowleft' || key === 'arrowup' || LEFT_KEYS.has(key)) {
367
+ previousReaderPage();
368
+ } else if (
369
+ key === 'pagedown' || key === 'arrowright' || key === 'arrowdown' || RIGHT_KEYS.has(key)
370
+ ) {
371
+ nextReaderPage();
372
+ }
373
+ }
374
+
375
+ export function stopReading() {
376
+ readingRevision++;
377
+ $.tts.stop?.();
378
+ message('Reading stopped / 已停止朗讀');
379
+ }
380
+
381
+ export function readFromCurrentPage() {
382
+ const revision = ++readingRevision;
383
+ void runReading(revision);
384
+ }
385
+
386
+ async function runReading(revision) {
387
+ for (let current = pageIndex; current < pages.length; current++) {
388
+ if (revision !== readingRevision) return;
389
+ showReaderPage(current);
390
+ for (const sentence of sentences(pages[current])) {
391
+ if (revision !== readingRevision) return;
392
+ const error = await $.tts(sentence, speechPitch, speechSpeed);
393
+ if (error) {
394
+ message(error);
395
+ return;
396
+ }
397
+ }
398
+ }
399
+ if (revision === readingRevision)
400
+ message(`Reading complete: ${loadedSource}`);
401
+ }
402
+ ```
403
+
404
+ ```js back
405
+ import { readdir } from 'node:fs/promises';
406
+ import { basename, dirname, resolve, sep } from 'node:path';
407
+
408
+ export async function completeReaderDirectory(input) {
409
+ const raw = String(input ?? '').trim();
410
+ if (/^https?:\/\//iu.test(raw)) return { directory: raw, matches: 0 };
411
+ const resolved = resolve(raw || process.cwd());
412
+ const endsWithSeparator = /[\\/]$/u.test(raw);
413
+ const parent = endsWithSeparator ? resolved : dirname(resolved);
414
+ const prefix = endsWithSeparator ? '' : basename(resolved);
415
+ const entries = await readdir(parent, { withFileTypes: true });
416
+ const matches = entries
417
+ .filter(entry => entry.isDirectory() && entry.name.startsWith(prefix))
418
+ .map(entry => entry.name)
419
+ .sort((left, right) => left.localeCompare(right));
420
+ if (!matches.length) return { directory: raw, matches: 0 };
421
+ let common = matches[0];
422
+ for (const name of matches.slice(1)) {
423
+ let length = 0;
424
+ while (length < common.length && common[length] === name[length]) length++;
425
+ common = common.slice(0, length);
426
+ }
427
+ let directory = common ? resolve(parent, common) : `${resolved}${sep}`;
428
+ if (matches.length === 1) directory += sep;
429
+ return { directory, matches: matches.length };
430
+ }
431
+
432
+ export async function listReaderDirectory(input) {
433
+ const value = String(input ?? '').trim();
434
+ if (/^https?:\/\//iu.test(value)) {
435
+ const response = await fetch(value);
436
+ if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
437
+ const directory = response.url || value;
438
+ const links = [];
439
+ let currentLink = null;
440
+ await new HTMLRewriter()
441
+ .on('a[href]', {
442
+ element(element) {
443
+ const href = element.getAttribute('href')?.trim();
444
+ currentLink = href ? { href, text: '' } : null;
445
+ if (currentLink) links.push(currentLink);
446
+ },
447
+ text(chunk) {
448
+ if (currentLink) currentLink.text += chunk.text;
449
+ },
450
+ })
451
+ .transform(response)
452
+ .text();
453
+ const seen = new Set();
454
+ const files = [];
455
+ for (const link of links) {
456
+ let url;
457
+ try { url = new URL(link.href, directory); } catch { continue; }
458
+ if (!/^https?:$/iu.test(url.protocol) || seen.has(url.href)) continue;
459
+ seen.add(url.href);
460
+ files.push({ name: link.text.trim() || link.href, value: url.href });
461
+ }
462
+ return { directory, files };
463
+ }
464
+ const directory = resolve(value || process.cwd());
465
+ const entries = await readdir(directory, { withFileTypes: true });
466
+ const files = entries
467
+ .filter(entry => entry.isFile())
468
+ .map(entry => ({
469
+ name: entry.name,
470
+ value: resolve(directory, entry.name),
471
+ }))
472
+ .sort((left, right) => left.name.localeCompare(right.name));
473
+ return { directory, files };
474
+ }
475
+
476
+ async function applyReaderReplacements(text, directory) {
477
+ const replacementFile = Bun.file(resolve(directory, 'replace.json'));
478
+ if (!await replacementFile.exists()) return text;
479
+ try {
480
+ const rules = Bun.JSON5.parse(await replacementFile.text());
481
+ if (!Array.isArray(rules)) return text;
482
+ for (const rule of rules) {
483
+ if (!Array.isArray(rule) || rule.length !== 2) continue;
484
+ try {
485
+ const pattern = new RegExp(String(rule[0]), 'g');
486
+ const replacement = String(rule[1]);
487
+ text = text.replace(pattern, () => replacement);
488
+ } catch {}
489
+ }
490
+ } catch {}
491
+ return text;
492
+ }
493
+
494
+ export async function loadReaderResource(input) {
495
+ const value = String(input ?? '').trim();
496
+ if (!value) throw new Error('Select a file or enter a URL first');
497
+ if (/^https?:\/\//iu.test(value)) {
498
+ const response = await fetch(value);
499
+ if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
500
+ return {
501
+ source: response.url || value,
502
+ text: await applyReaderReplacements(await response.text(), process.cwd()),
503
+ };
504
+ }
505
+ const source = resolve(value);
506
+ const file = Bun.file(source);
507
+ if (!await file.exists()) throw new Error(`File not found: ${source}`);
508
+ return {
509
+ source,
510
+ text: await applyReaderReplacements(await file.text(), dirname(source)),
511
+ };
512
+ }
513
+ ```