jsmdcui 0.3.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.
Files changed (258) hide show
  1. package/CHANGELOG.md +76 -0
  2. package/LICENSE +22 -0
  3. package/README.md +285 -0
  4. package/package.json +17 -0
  5. package/project_structure.txt +35 -0
  6. package/runmd.mjs +240 -0
  7. package/runtime/colorschemes/atom-dark.micro +33 -0
  8. package/runtime/colorschemes/bubblegum.micro +31 -0
  9. package/runtime/colorschemes/cmc-16.micro +47 -0
  10. package/runtime/colorschemes/cmc-tc.micro +43 -0
  11. package/runtime/colorschemes/darcula.micro +34 -0
  12. package/runtime/colorschemes/default.micro +1 -0
  13. package/runtime/colorschemes/dracula-tc.micro +49 -0
  14. package/runtime/colorschemes/dukedark-tc.micro +38 -0
  15. package/runtime/colorschemes/dukelight-tc.micro +38 -0
  16. package/runtime/colorschemes/dukeubuntu-tc.micro +38 -0
  17. package/runtime/colorschemes/geany.micro +29 -0
  18. package/runtime/colorschemes/gotham.micro +29 -0
  19. package/runtime/colorschemes/gruvbox-tc.micro +29 -0
  20. package/runtime/colorschemes/gruvbox.micro +26 -0
  21. package/runtime/colorschemes/material-tc.micro +36 -0
  22. package/runtime/colorschemes/monokai-dark.micro +28 -0
  23. package/runtime/colorschemes/monokai.micro +34 -0
  24. package/runtime/colorschemes/one-dark.micro +39 -0
  25. package/runtime/colorschemes/railscast.micro +37 -0
  26. package/runtime/colorschemes/simple.micro +33 -0
  27. package/runtime/colorschemes/solarized-tc.micro +31 -0
  28. package/runtime/colorschemes/solarized.micro +30 -0
  29. package/runtime/colorschemes/sunny-day.micro +29 -0
  30. package/runtime/colorschemes/twilight.micro +40 -0
  31. package/runtime/colorschemes/zenburn.micro +30 -0
  32. package/runtime/help/actions.md +161 -0
  33. package/runtime/help/cdp.md +119 -0
  34. package/runtime/help/colors.md +421 -0
  35. package/runtime/help/commands.md +161 -0
  36. package/runtime/help/copypaste.md +149 -0
  37. package/runtime/help/defaultkeys.md +148 -0
  38. package/runtime/help/help.md +285 -0
  39. package/runtime/help/keybindings.md +355 -0
  40. package/runtime/help/linter.md +90 -0
  41. package/runtime/help/options.md +709 -0
  42. package/runtime/help/plugins.md +544 -0
  43. package/runtime/help/tutorial.md +112 -0
  44. package/runtime/jsplugins/cdp/cdp-server.js +1161 -0
  45. package/runtime/jsplugins/cdp/cdp.js +192 -0
  46. package/runtime/jsplugins/chapter/chapter.js +108 -0
  47. package/runtime/jsplugins/diff/diff.js +46 -0
  48. package/runtime/jsplugins/example/example.js +110 -0
  49. package/runtime/jsplugins/linter/linter.js +281 -0
  50. package/runtime/plugins/autoclose/autoclose.lua +75 -0
  51. package/runtime/plugins/ftoptions/ftoptions.lua +17 -0
  52. package/runtime/plugins/literate/README.md +5 -0
  53. package/runtime/plugins/literate/literate.lua +55 -0
  54. package/runtime/plugins/status/help/status.md +21 -0
  55. package/runtime/plugins/status/status.lua +62 -0
  56. package/runtime/syntax/LICENSE +22 -0
  57. package/runtime/syntax/PowerShell.yaml +128 -0
  58. package/runtime/syntax/README.md +63 -0
  59. package/runtime/syntax/ada.yaml +43 -0
  60. package/runtime/syntax/apacheconf.yaml +59 -0
  61. package/runtime/syntax/arduino.yaml +101 -0
  62. package/runtime/syntax/asciidoc.yaml +51 -0
  63. package/runtime/syntax/asm.yaml +123 -0
  64. package/runtime/syntax/ats.yaml +99 -0
  65. package/runtime/syntax/awk.yaml +44 -0
  66. package/runtime/syntax/b.yaml +87 -0
  67. package/runtime/syntax/bat.yaml +57 -0
  68. package/runtime/syntax/c.yaml +60 -0
  69. package/runtime/syntax/caddyfile.yaml +23 -0
  70. package/runtime/syntax/cake.yaml +7 -0
  71. package/runtime/syntax/clojure.yaml +38 -0
  72. package/runtime/syntax/cmake.yaml +42 -0
  73. package/runtime/syntax/coffeescript.yaml +56 -0
  74. package/runtime/syntax/colortest.yaml +19 -0
  75. package/runtime/syntax/conky.yaml +17 -0
  76. package/runtime/syntax/cpp.yaml +91 -0
  77. package/runtime/syntax/crontab.yaml +36 -0
  78. package/runtime/syntax/crystal.yaml +72 -0
  79. package/runtime/syntax/csharp.yaml +52 -0
  80. package/runtime/syntax/css.yaml +44 -0
  81. package/runtime/syntax/csx.yaml +8 -0
  82. package/runtime/syntax/cuda.yaml +68 -0
  83. package/runtime/syntax/cython.yaml +52 -0
  84. package/runtime/syntax/d.yaml +121 -0
  85. package/runtime/syntax/dart.yaml +46 -0
  86. package/runtime/syntax/default.yaml +10 -0
  87. package/runtime/syntax/dockerfile.yaml +36 -0
  88. package/runtime/syntax/dot.yaml +29 -0
  89. package/runtime/syntax/elixir.yaml +30 -0
  90. package/runtime/syntax/elm.yaml +38 -0
  91. package/runtime/syntax/erb.yaml +42 -0
  92. package/runtime/syntax/erlang.yaml +45 -0
  93. package/runtime/syntax/fish.yaml +48 -0
  94. package/runtime/syntax/forth.yaml +34 -0
  95. package/runtime/syntax/fortran.yaml +64 -0
  96. package/runtime/syntax/freebsd-kernel.yaml +14 -0
  97. package/runtime/syntax/fsharp.yaml +48 -0
  98. package/runtime/syntax/gdscript.yaml +61 -0
  99. package/runtime/syntax/gemini.yaml +19 -0
  100. package/runtime/syntax/gentoo-ebuild.yaml +48 -0
  101. package/runtime/syntax/gentoo-etc-portage.yaml +23 -0
  102. package/runtime/syntax/git-commit.yaml +35 -0
  103. package/runtime/syntax/git-config.yaml +14 -0
  104. package/runtime/syntax/git-rebase-todo.yaml +19 -0
  105. package/runtime/syntax/gleam.yaml +69 -0
  106. package/runtime/syntax/glsl.yaml +26 -0
  107. package/runtime/syntax/gnuplot.yaml +15 -0
  108. package/runtime/syntax/go.yaml +62 -0
  109. package/runtime/syntax/godoc.yaml +17 -0
  110. package/runtime/syntax/golo.yaml +73 -0
  111. package/runtime/syntax/gomod.yaml +31 -0
  112. package/runtime/syntax/graphql.yaml +47 -0
  113. package/runtime/syntax/groff.yaml +30 -0
  114. package/runtime/syntax/groovy.yaml +111 -0
  115. package/runtime/syntax/haml.yaml +16 -0
  116. package/runtime/syntax/hare.yaml +52 -0
  117. package/runtime/syntax/haskell.yaml +52 -0
  118. package/runtime/syntax/hc.yaml +52 -0
  119. package/runtime/syntax/html.yaml +70 -0
  120. package/runtime/syntax/html4.yaml +25 -0
  121. package/runtime/syntax/html5.yaml +25 -0
  122. package/runtime/syntax/ini.yaml +23 -0
  123. package/runtime/syntax/inputrc.yaml +14 -0
  124. package/runtime/syntax/java.yaml +37 -0
  125. package/runtime/syntax/javascript.yaml +124 -0
  126. package/runtime/syntax/jinja2.yaml +19 -0
  127. package/runtime/syntax/json.yaml +39 -0
  128. package/runtime/syntax/jsonnet.yaml +92 -0
  129. package/runtime/syntax/julia.yaml +57 -0
  130. package/runtime/syntax/justfile.yaml +40 -0
  131. package/runtime/syntax/keymap.yaml +27 -0
  132. package/runtime/syntax/kickstart.yaml +16 -0
  133. package/runtime/syntax/kotlin.yaml +66 -0
  134. package/runtime/syntax/kvlang.yaml +67 -0
  135. package/runtime/syntax/ledger.yaml +14 -0
  136. package/runtime/syntax/lfe.yaml +17 -0
  137. package/runtime/syntax/lilypond.yaml +26 -0
  138. package/runtime/syntax/lisp.yaml +17 -0
  139. package/runtime/syntax/log.yaml +92 -0
  140. package/runtime/syntax/lua.yaml +111 -0
  141. package/runtime/syntax/mail.yaml +25 -0
  142. package/runtime/syntax/makefile.yaml +38 -0
  143. package/runtime/syntax/man.yaml +12 -0
  144. package/runtime/syntax/markdown.yaml +49 -0
  145. package/runtime/syntax/mc.yaml +23 -0
  146. package/runtime/syntax/meson.yaml +51 -0
  147. package/runtime/syntax/micro.yaml +34 -0
  148. package/runtime/syntax/mpdconf.yaml +13 -0
  149. package/runtime/syntax/msbuild.yaml +6 -0
  150. package/runtime/syntax/nanorc.yaml +16 -0
  151. package/runtime/syntax/nftables.yaml +30 -0
  152. package/runtime/syntax/nginx.yaml +22 -0
  153. package/runtime/syntax/nim.yaml +27 -0
  154. package/runtime/syntax/nix.yaml +32 -0
  155. package/runtime/syntax/nu.yaml +114 -0
  156. package/runtime/syntax/objc.yaml +60 -0
  157. package/runtime/syntax/ocaml.yaml +43 -0
  158. package/runtime/syntax/octave.yaml +83 -0
  159. package/runtime/syntax/odin.yaml +64 -0
  160. package/runtime/syntax/pascal.yaml +45 -0
  161. package/runtime/syntax/patch.yaml +14 -0
  162. package/runtime/syntax/peg.yaml +16 -0
  163. package/runtime/syntax/perl.yaml +58 -0
  164. package/runtime/syntax/php.yaml +60 -0
  165. package/runtime/syntax/pkg-config.yaml +12 -0
  166. package/runtime/syntax/po.yaml +12 -0
  167. package/runtime/syntax/pony.yaml +37 -0
  168. package/runtime/syntax/pov.yaml +21 -0
  169. package/runtime/syntax/privoxy-action.yaml +14 -0
  170. package/runtime/syntax/privoxy-config.yaml +10 -0
  171. package/runtime/syntax/privoxy-filter.yaml +12 -0
  172. package/runtime/syntax/proto.yaml +40 -0
  173. package/runtime/syntax/prql.yaml +84 -0
  174. package/runtime/syntax/puppet.yaml +22 -0
  175. package/runtime/syntax/python2.yaml +60 -0
  176. package/runtime/syntax/python3.yaml +80 -0
  177. package/runtime/syntax/r.yaml +32 -0
  178. package/runtime/syntax/raku.yaml +42 -0
  179. package/runtime/syntax/reST.yaml +18 -0
  180. package/runtime/syntax/renpy.yaml +15 -0
  181. package/runtime/syntax/rpmspec.yaml +43 -0
  182. package/runtime/syntax/ruby.yaml +73 -0
  183. package/runtime/syntax/rust.yaml +78 -0
  184. package/runtime/syntax/sage.yaml +60 -0
  185. package/runtime/syntax/scad.yaml +53 -0
  186. package/runtime/syntax/scala.yaml +33 -0
  187. package/runtime/syntax/sed.yaml +13 -0
  188. package/runtime/syntax/sh.yaml +69 -0
  189. package/runtime/syntax/sls.yaml +15 -0
  190. package/runtime/syntax/smalltalk.yaml +55 -0
  191. package/runtime/syntax/solidity.yaml +41 -0
  192. package/runtime/syntax/sql.yaml +35 -0
  193. package/runtime/syntax/stata.yaml +67 -0
  194. package/runtime/syntax/svelte.yaml +27 -0
  195. package/runtime/syntax/swift.yaml +103 -0
  196. package/runtime/syntax/systemd.yaml +16 -0
  197. package/runtime/syntax/tcl.yaml +18 -0
  198. package/runtime/syntax/terraform.yaml +87 -0
  199. package/runtime/syntax/tex.yaml +32 -0
  200. package/runtime/syntax/toml.yaml +56 -0
  201. package/runtime/syntax/twig.yaml +55 -0
  202. package/runtime/syntax/typescript.yaml +49 -0
  203. package/runtime/syntax/v.yaml +80 -0
  204. package/runtime/syntax/vala.yaml +26 -0
  205. package/runtime/syntax/verilog.yaml +60 -0
  206. package/runtime/syntax/vhdl.yaml +37 -0
  207. package/runtime/syntax/vi.yaml +31 -0
  208. package/runtime/syntax/vue.yaml +64 -0
  209. package/runtime/syntax/xml.yaml +37 -0
  210. package/runtime/syntax/xresources.yaml +14 -0
  211. package/runtime/syntax/yaml.yaml +34 -0
  212. package/runtime/syntax/yum.yaml +12 -0
  213. package/runtime/syntax/zig.yaml +52 -0
  214. package/runtime/syntax/zscript.yaml +72 -0
  215. package/runtime/syntax/zsh.yaml +52 -0
  216. package/single-exe/README.md +80 -0
  217. package/single-exe/assetsHelper.js +90 -0
  218. package/single-exe/assetsLoader.mjs +85 -0
  219. package/single-exe/compiled.js +149 -0
  220. package/single-exe/entry.mjs +9 -0
  221. package/single-exe/packAssets.sh +7 -0
  222. package/src/buffer/backup.js +160 -0
  223. package/src/buffer/buffer.js +126 -0
  224. package/src/buffer/fixed3-codec.js +140 -0
  225. package/src/buffer/loc.js +38 -0
  226. package/src/buffer/message.js +29 -0
  227. package/src/config/clean.js +172 -0
  228. package/src/config/colorscheme.js +99 -0
  229. package/src/config/config.js +122 -0
  230. package/src/config/defaults.js +109 -0
  231. package/src/cui/rpc.mjs +259 -0
  232. package/src/cui/server.mjs +116 -0
  233. package/src/display/ansi-style.js +60 -0
  234. package/src/highlight/highlighter.js +243 -0
  235. package/src/highlight/parser.js +225 -0
  236. package/src/index.js +8009 -0
  237. package/src/lua/engine.js +71 -0
  238. package/src/platform/archive.js +50 -0
  239. package/src/platform/clipboard.js +278 -0
  240. package/src/platform/commands.js +137 -0
  241. package/src/plugins/js-bridge.js +983 -0
  242. package/src/plugins/manager.js +674 -0
  243. package/src/runtime/assets.js +90 -0
  244. package/src/runtime/compiled.js +25 -0
  245. package/src/runtime/encodings.js +10 -0
  246. package/src/runtime/registry.js +134 -0
  247. package/src/screen/cell-buffer.js +81 -0
  248. package/src/screen/events.js +263 -0
  249. package/src/screen/screen.js +142 -0
  250. package/src/screen/vt100.js +571 -0
  251. package/src/shell/shell.js +70 -0
  252. package/testapp.md +55 -0
  253. package/tests/backup.test.js +133 -0
  254. package/tests/cmphex3b64.js +95 -0
  255. package/tests/pty-demo.js +492 -0
  256. package/tests/wv-client.js +96 -0
  257. package/tui +5 -0
  258. package/wui +5 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,76 @@
1
+ # Changelog
2
+
3
+ All notable user-visible changes to jsmdcui are documented here.
4
+
5
+ ## [0.3.0] - 2026-07-16
6
+
7
+ Initial usable release, based on the bunmicro terminal editor.
8
+
9
+ ### Added
10
+
11
+ - Open local Markdown files as interactive, read-only terminal interfaces while
12
+ retaining navigation, selection, search, copy, mouse support, and automatic
13
+ reflow when the terminal is resized.
14
+ - Open HTTP(S) Markdown files in the terminal without generating local
15
+ companion files or executing their `javascript:` links.
16
+ - Start a browser interface with `--wui [FILE.md]`. Browser pages support normal
17
+ links, clickable `javascript:` actions, heading anchors, and task checkboxes
18
+ that can be toggled for the current page session.
19
+ - Build interactive Markdown apps with `js front`, `js back`, and
20
+ `javascript:` links. Frontend actions can use `alert`, `confirm`, `prompt`,
21
+ and call published backend functions through `rpc`.
22
+ - Activate local terminal actions with `Enter`, `Space`, or a left click, with
23
+ visible reporting for synchronous and asynchronous frontend errors.
24
+ - Generate five companion files beside each local Markdown source:
25
+ `.front.js`, `.back.js`, `.html`, `-rpc.js`, and `-server.js`.
26
+ - Print terminal output with `--cat`, print the bundled example source with
27
+ `--testapp.md`, and choose a WUI port with the `PORT` environment variable.
28
+ - Provide the `jsmdcui`, `tui`, and `wui` launchers, Bun scripts for common
29
+ development tasks, bundled runtime assets, and experimental single-executable
30
+ build commands.
31
+ - Retain the normal bunmicro-based editor for non-Markdown buffers, including
32
+ file editing, syntax highlighting, search and replace, undo and redo, tabs,
33
+ split panes, clipboard support, themes, and shell commands.
34
+ - Show this README as the built-in `Ctrl-G` help page.
35
+
36
+ ### Changed
37
+
38
+ - Local `.md` files now enter read-only Markdown UI mode automatically. Starting
39
+ jsmdcui without a file still opens the normal terminal editor.
40
+ - Opening or rendering a local Markdown file regenerates its five companion
41
+ files in terminal, `--cat`, and `--wui` modes.
42
+ - Running `--wui` without a file uses `testapp.md` in the current directory when
43
+ present, otherwise the bundled demo, and writes generated files in the current
44
+ directory.
45
+ - Every WUI start prints a fresh UUID-based page URL. Requests without the
46
+ trailing slash redirect to the canonical page URL.
47
+ - `js front` and `js back` blocks are removed from the displayed Markdown and
48
+ written to their generated modules.
49
+
50
+ ### Fixed
51
+
52
+ - Preserved Markdown ANSI colors in the terminal UI and prevented the rendered
53
+ document background from obscuring the editor theme.
54
+ - Correctly detected links at the active terminal cell and loaded the matching
55
+ generated frontend module.
56
+ - Restored terminal display state after `alert`, `confirm`, and `prompt`.
57
+ - Reported frontend errors instead of letting them crash or silently fail.
58
+ - Corrected generated module paths and imports, supported bundled runtime
59
+ templates, and removed redundant file generation.
60
+ - Made JavaScript plugin commands available through command lookup and
61
+ completion, and added `js`, `py`, and `sh` choices for `eval` completion.
62
+
63
+ ### Security
64
+
65
+ - Browser RPC discovery and calls expose only exported backend functions whose
66
+ exported names do not begin with `_`. This restriction applies to WUI RPC;
67
+ the local TUI imports the backend module directly.
68
+ - WUI pages use a fresh UUID path on each start. This reduces accidental
69
+ discovery but is not authentication or authorization.
70
+ - The WUI has no login or HTTPS, may be reachable through the machine's network
71
+ interfaces, and permits cross-origin RPC responses. Anyone who can reach it
72
+ and obtains the complete URL can call every backend function published by the
73
+ Markdown app.
74
+ - Frontend code runs in the browser, while terminal frontend code and backend
75
+ code can run with the permissions of the jsmdcui process. Only open or serve
76
+ Markdown apps from trusted sources.
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2016-2020: Zachary Yedidia, et al.
4
+ Copyright (c) 2026 Dr. John (醫者小智)
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,285 @@
1
+ # Introduction
2
+ - jsmdcui stands for:
3
+ - JavaScript Markdown Cross-environment User Interface
4
+ - The goal is to use Markdown as a Common UI for Terminals and Web Browsers
5
+ * TUI = Terminal User Interface
6
+ * WUI = Web User Interface
7
+ - .
8
+ - It's based on bunmicro, a terminal text editor derived from the Micro text editor
9
+
10
+ - Original projects:
11
+ - https://github.com/jjtseng93/bunmicro
12
+ - https://github.com/micro-editor/micro
13
+
14
+ ## Quick start
15
+
16
+ jsmdcui requires [Bun](https://bun.com). On Android, install it in Termux:
17
+
18
+ ```sh
19
+ npm install -g bun
20
+ ```
21
+
22
+ On other platforms, follow the [official Bun installation guide](https://bun.com/docs/installation).
23
+
24
+ Choose either of these two ways to run jsmdcui.
25
+
26
+ > **Important:** Opening or rendering a local `.md` file writes or overwrites
27
+ > five generated files beside it. Starting `--wui` without a file writes the
28
+ > five files generated from `testapp.md` in the current directory. The source
29
+ > Markdown is not changed, but you should run the demo in a directory where
30
+ > overwriting generated files is safe.
31
+
32
+ ### Route 1: Run with npx
33
+
34
+ This route requires both `npx` and `bun` on your `PATH`; no source checkout is
35
+ required. Open the normal terminal editor:
36
+
37
+ ```sh
38
+ npx jsmdcui
39
+ ```
40
+
41
+ Or start the default browser demo:
42
+
43
+ ```sh
44
+ npx jsmdcui --wui
45
+ ```
46
+
47
+ If the current directory already contains `testapp.md`, that local file is used
48
+ instead of the bundled demo. To open your own Markdown UI, pass its path:
49
+
50
+ ```sh
51
+ npx jsmdcui app.md
52
+ npx jsmdcui --wui app.md
53
+ ```
54
+
55
+ ### Route 2: Clone the source
56
+
57
+ ```sh
58
+ git clone https://github.com/jjtseng93/jsmdcui.git
59
+ cd jsmdcui
60
+ bun src/index.js testapp.md
61
+ ```
62
+
63
+ The last command opens the included demo in the terminal. Use the arrow keys to
64
+ move, `Enter` or `Space` to activate an item, and `Ctrl-Q` to quit. To open the
65
+ same demo in a browser instead:
66
+
67
+ ```sh
68
+ bun src/index.js --wui testapp.md
69
+ ```
70
+
71
+ After starting `--wui`, open the last printed `http://...` URL in a browser.
72
+ Keep the command running while using the page, and press `Ctrl-C` in that
73
+ terminal to stop the server.
74
+
75
+ The command table below uses the cloned-source form. If you use npx, replace
76
+ `bun src/index.js` with `npx jsmdcui`.
77
+
78
+ | Command | Result |
79
+ | --- | --- |
80
+ | `bun src/index.js app.md` | Render `app.md` as a read-only terminal UI and write five generated files beside it. |
81
+ | `bun src/index.js --cat app.md` | Render the terminal version to stdout, write five generated files beside it, and exit. |
82
+ | `bun src/index.js --testapp.md` | Write the bundled `testapp.md` source to stdout and exit. |
83
+ | `bun src/index.js --wui` | Use local `testapp.md` when present, otherwise use the bundled demo; write five generated files in the current directory, then print and serve a random URL. |
84
+ | `bun src/index.js --wui app.md` | Write five generated files beside `app.md`, then print and serve a random URL. |
85
+ | `PORT=8080 bun src/index.js --wui app.md` | Start the browser UI on another port. |
86
+ | `bun src/index.js` | Open the normal terminal editor with an empty buffer. |
87
+
88
+ Run `npx jsmdcui --help` or `bun src/index.js --help` for all command-line
89
+ options.
90
+
91
+ ## Write a Markdown UI
92
+
93
+ Create `app.md`:
94
+
95
+ ````md
96
+ # My app
97
+
98
+ - [Say hello](javascript:sayHello())
99
+ - [Get server time](javascript:showServerTime())
100
+
101
+ ```js front
102
+ export function sayHello() {
103
+ const name = prompt("Your name:", "World");
104
+ if (name) alert(`Hello, ${name}!`);
105
+ }
106
+
107
+ export async function showServerTime() {
108
+ const time = await rpc.getServerTime();
109
+ alert(time);
110
+ }
111
+ ```
112
+
113
+ ```js back
114
+ export function getServerTime() {
115
+ return new Date().toISOString();
116
+ }
117
+ ```
118
+ ````
119
+
120
+ Open it in either UI:
121
+
122
+ ```sh
123
+ bun src/index.js app.md
124
+ bun src/index.js --wui app.md
125
+ ```
126
+
127
+ The three UI building blocks are:
128
+
129
+ - Regular Markdown provides headings, text, lists, task checkboxes, code, and
130
+ links.
131
+ - A `js front` block contains UI code. Exported functions can use
132
+ `alert`, `confirm`, `prompt`, and the generated `rpc` client.
133
+ - A `js back` block exports trusted backend functions. In the browser WUI,
134
+ `rpc` publishes only exported functions whose exported names do not start
135
+ with `_`. Call a published function from the front end with
136
+ `await rpc.functionName(arg1, arg2)`.
137
+
138
+ > An `_` prefix only hides a function from the browser WUI RPC interface. The
139
+ > local terminal UI imports the backend module directly, so `_` is a naming
140
+ > convention, not authentication or a security boundary. Use a name without
141
+ > `_` for a function that must work through RPC in both UIs.
142
+
143
+ Use a `javascript:` Markdown link to run front-end code:
144
+
145
+ ```md
146
+ [Button label](javascript:exportedFunction())
147
+ ```
148
+
149
+ The front and back code blocks are extracted and are not shown in the rendered
150
+ UI. In the terminal, `rpc` calls the generated backend module directly. In the
151
+ browser, it sends the call to the relative `rpc` endpoint under the printed
152
+ UUID URL (`/<uuid>/rpc`).
153
+
154
+ ## Terminal interaction
155
+
156
+ Markdown files automatically use `mdcui` mode. The rendered buffer is
157
+ read-only, but navigation, selection, search, and copy remain available.
158
+
159
+ | Input | Result |
160
+ | --- | --- |
161
+ | Arrow keys, `Home`, `End`, `PageUp`, `PageDown` | Move through the rendered UI. |
162
+ | `Enter` or `Space` | Activate the cell under the cursor. Put the cursor on a `javascript:` link to run it. |
163
+ | Left click | Move to and activate the clicked cell. A `javascript:` link runs immediately. |
164
+ | Mouse wheel | Scroll three rows at a time. |
165
+ | `Shift` + arrow keys | Select rendered text. |
166
+ | `Ctrl-C` | Copy the selection, or the current line when nothing is selected. |
167
+ | `Ctrl-A` | Select all rendered text. |
168
+ | `Ctrl-F` | Search; use `Ctrl-N` and `Ctrl-P` for the next and previous match. |
169
+ | `Ctrl-G` | Open the editor help. |
170
+ | `Alt-G` | Show or hide the shortcut bar. |
171
+ | `Ctrl-Q` or `Alt-Q` | Close the current UI. |
172
+
173
+ The terminal automatically reflows the Markdown when its width changes.
174
+ Only `javascript:` links execute in the TUI; ordinary web links behave as
175
+ normal links in the browser.
176
+
177
+ ## Browser interaction
178
+
179
+ The WUI uses normal browser mouse and keyboard behavior. Clicking a
180
+ `javascript:` link calls its front-end function, regular links navigate
181
+ normally, and task checkboxes can be toggled. `alert`, `confirm`, and `prompt`
182
+ use the browser's built-in dialogs. Checkbox changes exist only in the current
183
+ page: refreshing does not preserve them and does not update the Markdown file.
184
+
185
+ The WUI uses port `3000` by default and accepts connections through the
186
+ machine's available network interfaces. The printed `localhost` URL is for the
187
+ same machine. From another device on the same network, replace `localhost` with
188
+ the server machine's IP address and keep the same port and full path.
189
+
190
+ Each server start prints a new random path. The old URL stops working after the
191
+ server is stopped or restarted. Keep the process running while using the page,
192
+ and press `Ctrl-C` in its terminal to stop it.
193
+
194
+ ## Generated files
195
+
196
+ Opening a local Markdown UI generates these files beside the source file:
197
+
198
+ ```text
199
+ app.md.front.js
200
+ app.md.back.js
201
+ app.md.html
202
+ app.md-rpc.js
203
+ app.md-server.js
204
+ ```
205
+
206
+ They are regenerated from `app.md`, so edit the Markdown source rather than the
207
+ generated files. The source directory must be writable.
208
+
209
+ ## Security
210
+
211
+ A Markdown UI is an executable application, not a passive document. Starting a
212
+ WUI loads its backend module, loading the page runs its frontend module, and
213
+ activating a local terminal `javascript:` link runs frontend code. Backend and
214
+ terminal code can read or change files, start programs, access the network, and
215
+ read environment data with the permissions of the jsmdcui process. Only open
216
+ or serve Markdown UI files that you trust.
217
+
218
+ ### WUI network exposure
219
+
220
+ > **Warning:** The WUI is intended for local or trusted-network use. It is not
221
+ > a production web server.
222
+
223
+ - There is no login, per-user authorization, or HTTPS. Traffic and results are
224
+ sent over plain HTTP.
225
+ - The server may be reachable from other devices through the machine's network
226
+ interfaces, even though the printed URL says `localhost`.
227
+ - The random path makes the URL harder to guess, but it is not a password.
228
+ Anyone who can reach the server and obtains the complete URL can load the
229
+ page and call every backend function published through WUI RPC, using
230
+ arguments they choose.
231
+ - A backend RPC function runs with the same operating-system permissions as
232
+ jsmdcui. Its code determines what files, commands, network services, or
233
+ secrets a visitor may be able to reach.
234
+ - Do not share the complete URL, forward the port to the public internet, or
235
+ run the WUI on an untrusted network. Use a firewall when needed and stop the
236
+ server with `Ctrl-C` as soon as you finish.
237
+ - Prefixing a backend export with `_` keeps it out of WUI RPC discovery and
238
+ calls, but it does not protect the backend module from trusted local code and
239
+ is not a substitute for authentication.
240
+
241
+ ## Development
242
+
243
+ ```sh
244
+ npm run tui
245
+ npm run wui
246
+ npm run check
247
+ ```
248
+
249
+ `testapp.md` is the main working example.
250
+
251
+ testapp.md
252
+
253
+ ┌──────────────┴──────────────┐
254
+ │ │
255
+ ▼ ▼
256
+ Browser HTML TUI ANSI
257
+ │ │
258
+ javascript:foo() extract javascript:
259
+ │ │
260
+ ▼ ▼
261
+ window.frontFunc() evalFront(frontMod, text)
262
+ │ │
263
+ └──────────────┬──────────────┘
264
+
265
+ testapp.md.front.js
266
+
267
+ rpc.someFunc()
268
+ ┌───────────┴───────────┐
269
+ │ │
270
+ Browser TUI
271
+ │ │
272
+ RPC Proxy client import * as rpc
273
+ │ from back module
274
+ ▼ │
275
+ fetch rpc │
276
+ │ │
277
+ ▼ │
278
+ server.mjs │
279
+ │ │
280
+ ▼ │
281
+ evalBack(backMod, reqjson) │
282
+ │ │
283
+ └───────────┬───────────┘
284
+
285
+ testapp.md.back.js
package/package.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "jsmdcui",
3
+ "version": "0.3.0",
4
+ "description": "Markdown as a Common UI for Terminals and Web Browsers",
5
+ "type": "module",
6
+ "main": "./src/index.js",
7
+ "license": "MIT",
8
+ "bin": {
9
+ "jsmdcui": "./src/index.js"
10
+ },
11
+ "scripts": {
12
+ "start": "bun ./src/index.js",
13
+ "tui": "bun ./src/index.js ./testapp.md",
14
+ "wui": "bun ./runmd.mjs",
15
+ "check": "node --check ./src/index.js"
16
+ }
17
+ }
@@ -0,0 +1,35 @@
1
+ testapp.md
2
+
3
+ ┌──────────────┴──────────────┐
4
+ │ │
5
+ ▼ ▼
6
+ Browser HTML TUI ANSI
7
+ │ │
8
+ javascript:foo() extract javascript:
9
+ │ │
10
+ ▼ ▼
11
+ window.frontFunc() evalFront(frontMod, text)
12
+ │ │
13
+ └──────────────┬──────────────┘
14
+
15
+ testapp.md.front.js
16
+
17
+ rpc.someFunc()
18
+ ┌───────────┴───────────┐
19
+ │ │
20
+ Browser TUI
21
+ │ │
22
+ RPC Proxy client import * as rpc
23
+ │ from back module
24
+ ▼ │
25
+ fetch rpc │
26
+ │ │
27
+ ▼ │
28
+ server.mjs │
29
+ │ │
30
+ ▼ │
31
+ evalBack(backMod, reqjson) │
32
+ │ │
33
+ └───────────┬───────────┘
34
+
35
+ testapp.md.back.js
package/runmd.mjs ADDED
@@ -0,0 +1,240 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import path from 'node:path'
4
+ import { pathToFileURL } from 'node:url'
5
+ import { readInternalAssetText } from './src/runtime/assets.js'
6
+ import { REPO_ROOT } from './single-exe/compiled.js'
7
+
8
+ const csl=console.log
9
+ const mda=Bun.markdown.ansi
10
+ const mdh=Bun.markdown.html
11
+ const jss=JSON.stringify
12
+
13
+
14
+ const TEST_ROW=9
15
+ const TEST_COL=5
16
+
17
+
18
+ function logWroteFile(label,path)
19
+ {
20
+ csl(mda(`- Wrote to ${label} file: ${path}`))
21
+ }
22
+
23
+ async function readTemplate(pathname)
24
+ {
25
+ return readInternalAssetText(pathname) ??
26
+ await Bun.file(path.join(REPO_ROOT, pathname)).text()
27
+ }
28
+
29
+ async function readMarkdownInput(mdpath)
30
+ {
31
+ const file = Bun.file(mdpath);
32
+ if (await file.exists()) return await file.text();
33
+ const assetName = path.basename(mdpath);
34
+ const internalText = readInternalAssetText(assetName);
35
+ if (internalText != null) return internalText;
36
+ const fallbackPath = path.join(REPO_ROOT, assetName);
37
+ const fallback = Bun.file(fallbackPath);
38
+ if (await fallback.exists()) return await fallback.text();
39
+ throw new Error(`md file not found: ${mdpath}`);
40
+ }
41
+
42
+
43
+ export async function main(tuiWidth=30)
44
+ {
45
+ const mdpath = process.argv.filter(i=>i.endsWith('.md'))[0] || 'testapp.md'
46
+
47
+
48
+ // 1. Read markdown file
49
+ console.error('Reading:',mdpath)
50
+ let md = await readMarkdownInput(mdpath)
51
+
52
+ // 2. Extract js files
53
+ md = await extractJs(md,mdpath);
54
+
55
+ // 3. Create Terminal UI
56
+ let tui = createTui(md,tuiWidth)
57
+ csl(mda("\n# TUI"))
58
+ csl(tui)
59
+ csl(mda('## TUI raw'))
60
+ csl(jss(tui))
61
+
62
+
63
+ // 4. Create Web UI
64
+ let wui = await createWui(md,mdpath)
65
+ csl(mda('\n# HTML'))
66
+ csl(wui)
67
+
68
+
69
+ // 5. Get character from point for TUI
70
+ let ch = charFromPoint(tui,TEST_ROW,TEST_COL)
71
+
72
+ csl(mda(
73
+ '# Slicing row,col: '+TEST_ROW+','+TEST_COL
74
+ ))
75
+
76
+ csl(jss(ch))
77
+
78
+
79
+ const serverPath = mdpath + "-server.js"
80
+ const svmod = await import(pathToFileURL(path.resolve(serverPath)).href)
81
+
82
+ csl("\n\n"+mda('# Server'))
83
+ svmod.main();
84
+ }
85
+
86
+
87
+ // Exports
88
+
89
+
90
+ export async function extractJs(md,mdpath)
91
+ {
92
+ const mdb = path.basename(mdpath)
93
+
94
+ let sctagsp = mdpath + ".front.js"
95
+ let sctags = ""
96
+ // Gather frontend script tags
97
+ let reJs = /```js front[^]+?```/g //
98
+ md = md.replace( reJs,
99
+ i=>{
100
+ sctags+=i.slice(11,-3)+'\n\n'
101
+
102
+ return '';
103
+ return i
104
+ .replace('```js front',"<scr"+"ipt>")
105
+ .slice(0,-3)+"</scr"+"ipt>"
106
+ }
107
+ )
108
+
109
+ sctags = "#!/usr/bin/env bun" + `
110
+
111
+ import { rpc as wuiRpcClient } from "./${mdb}-rpc.js";
112
+ let rpc = "${mdb}"
113
+ if(globalThis.process)
114
+ rpc = await import("./"+rpc+".back.js") ;
115
+ else
116
+ rpc = wuiRpcClient ;
117
+
118
+ ` + sctags + `
119
+
120
+ if (typeof window !== "undefined")
121
+ {
122
+ // Browser
123
+ setTimeout( () => {
124
+ import("./${mdb}.front.js").then(mod=>{
125
+ Object.assign(window,mod);
126
+ })
127
+ }, 0 ) ;
128
+ }
129
+ ` ;
130
+
131
+ await Bun.write(sctagsp,sctags);
132
+ logWroteFile("front", sctagsp)
133
+
134
+ sctagsp = mdpath + ".back.js"
135
+ sctags = "#!/usr/bin/env bun\n\n"
136
+ // Gather backend script tags
137
+ reJs = /```js back[^]+?```/g //
138
+ md = md.replace( reJs,
139
+ i=>{
140
+ sctags+=i.slice(10,-3)+'\n\n'
141
+
142
+ return '';
143
+ }
144
+ )
145
+
146
+ await Bun.write(sctagsp,sctags);
147
+ logWroteFile("back", sctagsp)
148
+
149
+ await writeRuntimeFiles(mdpath)
150
+
151
+ return md
152
+ }
153
+
154
+ export async function writeRuntimeFiles(mdpath)
155
+ {
156
+ const mdb = path.basename(mdpath)
157
+ const rpcPath = mdpath + "-rpc.js"
158
+ const serverPath = mdpath + "-server.js"
159
+ const rpcSource = await readTemplate("src/cui/rpc.mjs")
160
+ const serverSource = (await readTemplate("src/cui/server.mjs"))
161
+ .replaceAll("./rpc.mjs", `./${mdb}-rpc.js`)
162
+ .replaceAll("testapp.md", mdb)
163
+
164
+ await Bun.write(rpcPath, rpcSource)
165
+ logWroteFile("rpc", rpcPath)
166
+ await Bun.write(serverPath, serverSource)
167
+ logWroteFile("server", serverPath)
168
+
169
+ return serverPath
170
+ }
171
+
172
+
173
+ export function createTui(md,TERMINAL_WIDTH=30) // ANSI Colors
174
+ {
175
+ md = ( Bun?.markdown?.ansi?.(
176
+ md,{
177
+ hyperlinks:true,
178
+ columns:TERMINAL_WIDTH
179
+ }
180
+ )
181
+ || md )+'' ;
182
+
183
+ return md ;
184
+
185
+ return Bun.wrapAnsi(
186
+ md,
187
+ TERMINAL_WIDTH
188
+ )
189
+ }
190
+
191
+ export async function createWui(md,mdpath) // HTML
192
+ {
193
+
194
+ const opts = {
195
+ headings: { ids: true }
196
+ }
197
+
198
+ md = (Bun?.markdown?.html?.(md,opts) || md)+'' ;
199
+
200
+ // Restore single quotes
201
+ let reHrefs = /href="[^"]*?"/g // "
202
+ md = md.replace( reHrefs,
203
+ i=>i.replaceAll('&#x27;',"'")
204
+ )
205
+
206
+
207
+ md = md.replaceAll(
208
+ 'class="task-list-item-checkbox" disabled',
209
+ 'class="task-list-item-checkbox"'
210
+ )
211
+
212
+ const mdb = path.basename(mdpath);
213
+
214
+
215
+ md += `
216
+
217
+ <scr`+`ipt type="module" src="./${mdb}.front.js">
218
+ </scr`+`ipt>
219
+
220
+ `;
221
+
222
+
223
+ await Bun.write(mdpath+".html",md)
224
+ logWroteFile("html", mdpath+".html")
225
+
226
+ return md;
227
+ }
228
+
229
+ export function charFromPoint(tui,row,col)
230
+ {
231
+ tui = tui.split('\n')[row-1] || ""
232
+ return Bun.sliceAnsi(tui,col-1,col)
233
+ }
234
+
235
+
236
+ if(import.meta.main)
237
+ main().catch(e=>{
238
+ console.error(e?.message || e)
239
+ process.exit(127)
240
+ });