jsmdcui 0.8.0 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +129 -0
- package/README.md +141 -1
- package/cdp-maze.js +141 -0
- package/demos/maze.md +6 -1
- package/llm-maze.txt +94 -0
- package/package.json +7 -5
- package/runmd.mjs +20 -11
- package/runtime/help/cdp.md +5 -0
- package/runtime/help/help.md +141 -1
- package/runtime/jsplugins/cdp/cdp-server.js +26 -2
- package/runtime/jsplugins/cdp/cdp.js +55 -49
- package/runtime/jsplugins/chapter/chapter.js +2 -1
- package/runtime/jsplugins/example/example.js +2 -0
- package/single-exe/README.md +268 -45
- package/single-exe/compiled.js +11 -6
- package/single-exe/entry.mjs +4 -3
- package/single-exe/packAssets.sh +1 -1
- package/src/cui/kitty-debug.mjs +1 -1
- package/src/index.js +208 -74
- package/src/lua/engine.js +1 -4
- package/src/plugins/js-bridge.js +43 -0
- package/tests/cat-markdown.test.js +59 -0
- package/tests/compiled-runtime.test.js +8 -0
- package/tests/demo.test.js +81 -2
- package/tests/kitty-demo.md +1 -1
- package/demo.resized.jpg +0 -0
- package/src/runtime/compiled.js +0 -25
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,135 @@
|
|
|
2
2
|
|
|
3
3
|
All notable user-visible changes to jsmdcui are documented here.
|
|
4
4
|
|
|
5
|
+
## [0.10.1] - 2026-07-25
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Resolve and prepare `--demo` and `--demo-*` selections before entering WUI
|
|
10
|
+
mode, so combinations such as `--wui --demo-todo` serve the selected demo
|
|
11
|
+
instead of falling back to `testapp.md`.
|
|
12
|
+
|
|
13
|
+
## [0.10.0] - 2026-07-24
|
|
14
|
+
|
|
15
|
+
This update adds distribution-oriented defaults for shipping jsmdcui as a text
|
|
16
|
+
editor, terminal application, or browser application; makes WUI startup and
|
|
17
|
+
demo replacement explicit; and improves command-line control over rendering,
|
|
18
|
+
syntax highlighting, and single-executable builds.
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- Add `--mdcui` and `--tui` as aliases for `-encoding mdcui`, allowing an
|
|
23
|
+
explicit terminal Markdown UI request to override an editor-oriented
|
|
24
|
+
distribution default.
|
|
25
|
+
- Add `--overwrite-demo` as a modifier for `--demo` and the automatically
|
|
26
|
+
discovered `--demo-*` commands. It replaces an existing local demo with the
|
|
27
|
+
bundled copy before opening it.
|
|
28
|
+
- Add `--print-ui` for WUI launches. The generated TUI preview, raw ANSI, and
|
|
29
|
+
HTML are printed only when this flag is present.
|
|
30
|
+
- Add the `src/MDCUI_DEFAULT_EDIT` distribution marker. When present, Markdown
|
|
31
|
+
files open as editable UTF-8 text by default while `--mdcui` remains
|
|
32
|
+
available explicitly.
|
|
33
|
+
- Add presence-based single-executable build constants:
|
|
34
|
+
`MDCUI_DEFAULT_EDIT`, `MDCUI_DEFAULT_DEMO`, and
|
|
35
|
+
`MDCUI_DEFAULT_DEMO_WUI` select an editor, no-argument TUI demo, or
|
|
36
|
+
no-argument WUI demo default. `MDCUI_OVERWRITE_DEMO` optionally makes a
|
|
37
|
+
bundled demo replace its local copy.
|
|
38
|
+
- Add distribution documentation for shipping a customized root
|
|
39
|
+
`testapp.md` as a standalone terminal or browser application.
|
|
40
|
+
- Add regression coverage for the `--mdcui` alias, forced CLI filetypes,
|
|
41
|
+
non-overwriting and overwriting demos, and WUI demo replacement.
|
|
42
|
+
|
|
43
|
+
### Changed
|
|
44
|
+
|
|
45
|
+
- Parse `--wui` through the normal command-line argument flow, so it can appear
|
|
46
|
+
before or after the Markdown filename.
|
|
47
|
+
- Make WUI startup quieter by default. It continues to report file generation
|
|
48
|
+
and the server URL, while full generated UI output is opt-in through
|
|
49
|
+
`--print-ui`.
|
|
50
|
+
- Make direct `runmd.mjs` launches recognize `--overwrite-demo` and
|
|
51
|
+
`--print-ui`. Demo replacement is limited to the implicit `testapp.md`;
|
|
52
|
+
explicitly named Markdown files are protected from replacement.
|
|
53
|
+
- Forward every extra argument after `--build-exe`, or after the target passed
|
|
54
|
+
to `--build-for`, to the underlying `bun build` command.
|
|
55
|
+
- Align the `start`, `tui`, and `wui` package scripts with the repository
|
|
56
|
+
launchers, and add a `clean` package script.
|
|
57
|
+
- Recommend choosing only one of the three `MDCUI_DEFAULT_*` distribution
|
|
58
|
+
modes per build, with `MDCUI_OVERWRITE_DEMO` treated as an optional modifier.
|
|
59
|
+
- Show effective `MDCUI_*` distribution settings in `--version` output,
|
|
60
|
+
including `MDCUI_DEFAULT_EDIT` enabled through either its build constant or
|
|
61
|
+
the `src/MDCUI_DEFAULT_EDIT` marker.
|
|
62
|
+
|
|
63
|
+
### Fixed
|
|
64
|
+
|
|
65
|
+
- Make `-filetype <name>` actually override automatic syntax detection in
|
|
66
|
+
editor buffers and `--cat`, including input read from stdin.
|
|
67
|
+
|
|
68
|
+
## [0.9.0] - 2026-07-21
|
|
69
|
+
|
|
70
|
+
This update expands Chrome DevTools Protocol automation for the terminal UI,
|
|
71
|
+
adds a complete maze-solving example, and makes the optional Bun single-file
|
|
72
|
+
executable bootstrap easier to reuse and more reliable across platforms.
|
|
73
|
+
|
|
74
|
+
### Added
|
|
75
|
+
|
|
76
|
+
- Add CDP automation support for reading the rendered ANSI document, activating
|
|
77
|
+
a 1-based terminal-buffer cell, and sending keyboard input through the TUI's
|
|
78
|
+
normal input parser and event pipeline. Key presses support Alt, Ctrl, Meta,
|
|
79
|
+
Shift, navigation keys, editing keys, text, and auto-repeat without emitting
|
|
80
|
+
duplicate input for paired CDP `keyDown` and `char` events.
|
|
81
|
+
- Add `micro.getAllAnsiText()` and `micro.clickBufferCell(x, y)` to the JS
|
|
82
|
+
plugin bridge. MDCUI cell clicks now activate the same callbacks as mouse
|
|
83
|
+
input, while clicks in ordinary buffers retain the previous `goto` behavior.
|
|
84
|
+
- Add `cdp-maze.js` and `llm-maze.txt`, a documented Bun.WebView example that
|
|
85
|
+
reads the maze from the running TUI, solves it with breadth-first search, and
|
|
86
|
+
completes it by clicking controls and sending arrow-key events through CDP.
|
|
87
|
+
- Add `--cdp-maze` to open the bundled maze, start a local CDP server on port
|
|
88
|
+
9222, and automatically import and run the maze solver after a three-second
|
|
89
|
+
delay. The completed result is returned to the main program and displayed in
|
|
90
|
+
the TUI status message.
|
|
91
|
+
- Add `--export-cdp-maze` to write or overwrite `./cdp-maze.js` from the
|
|
92
|
+
bundled asset first, with a source-tree fallback, then exit. Include the
|
|
93
|
+
solver in the single-executable asset archive.
|
|
94
|
+
- Add a dedicated CDP automation section to the README and bundled help,
|
|
95
|
+
covering command-line and command-prompt startup, bind-address safety,
|
|
96
|
+
connection methods, and the maze solver workflow.
|
|
97
|
+
- Add a reusable single-file executable guide covering asset packing, embedded
|
|
98
|
+
and external resource fallbacks, build commands, cross-compilation, Node.js
|
|
99
|
+
compatibility, and adapting the bootstrap to another project.
|
|
100
|
+
|
|
101
|
+
### Changed
|
|
102
|
+
|
|
103
|
+
- Move responsibility for awaiting bundled assets into the Bun-only
|
|
104
|
+
`single-exe/entry.mjs` bootstrap, then dynamically import the regular main
|
|
105
|
+
module. This keeps the main module free of Bun-specific asset-loader state
|
|
106
|
+
and preserves its uncompiled Node.js execution path.
|
|
107
|
+
- Detect Bun's compiled virtual paths on both POSIX (`/$bunfs/`) and Windows
|
|
108
|
+
(`B:/~BUN`) when resolving single-executable resources, and consolidate
|
|
109
|
+
compiled-runtime handling in `single-exe/compiled.js`.
|
|
110
|
+
- When initially launched with Node.js, restart under Bun with inherited stdio
|
|
111
|
+
and propagate its exit status instead of replacing the current process with
|
|
112
|
+
`process.execve()`.
|
|
113
|
+
- Display the maze completion message inside the game instead of opening a
|
|
114
|
+
blocking alert, and include reset instructions in the result.
|
|
115
|
+
- Export the maze solver as a reusable `runCdpMaze()` function while retaining
|
|
116
|
+
direct `bun cdp-maze.js` execution. Suppress its console result while stdin
|
|
117
|
+
is in terminal raw mode so TUI rendering is not corrupted.
|
|
118
|
+
- Replace the manual two-process maze automation instructions with the single
|
|
119
|
+
`--cdp-maze` workflow in the README and bundled help.
|
|
120
|
+
- Update the homepage and Kitty test images to use the smaller current demo and
|
|
121
|
+
maze screenshots; exclude the maze screenshot from the npm package.
|
|
122
|
+
- Document theme preview from the TUI command prompt and state Bun 1.3.12 as
|
|
123
|
+
the minimum required version.
|
|
124
|
+
|
|
125
|
+
### Fixed
|
|
126
|
+
|
|
127
|
+
- Make CDP `view.click()` activate MDCUI links and controls at the requested
|
|
128
|
+
buffer coordinate instead of only moving the cursor there.
|
|
129
|
+
- Allow CDP input dispatch without an explicit session ID by selecting the
|
|
130
|
+
implicit target, matching other single-target CDP operations.
|
|
131
|
+
- Wait until the embedded asset archive is ready before starting the compiled
|
|
132
|
+
application, preventing startup from racing bundled resource loading.
|
|
133
|
+
|
|
5
134
|
## [0.8.0] - 2026-07-20
|
|
6
135
|
|
|
7
136
|
This update adds portable inline keydown handling to Markdown text controls,
|
package/README.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|

|
|
2
2
|
|
|
3
|
+
- Maze Game
|
|
4
|
+
* The TUI can be controlled through the Chrome DevTools Protocol (CDP). Codex successfully solved this maze using it.
|
|
5
|
+
|
|
6
|
+

|
|
7
|
+
|
|
3
8
|
# Demo App
|
|
4
9
|
## My app
|
|
5
10
|
|
|
@@ -36,7 +41,8 @@ npx jsmdcui@latest README.md
|
|
|
36
41
|
|
|
37
42
|
## Quick start
|
|
38
43
|
|
|
39
|
-
jsmdcui requires [Bun](https://bun.com).
|
|
44
|
+
jsmdcui requires [Bun ≥ 1.3.12](https://bun.com).
|
|
45
|
+
On Android, install it in Termux:
|
|
40
46
|
|
|
41
47
|
```sh
|
|
42
48
|
npm install -g bun
|
|
@@ -94,6 +100,13 @@ npx jsmdcui --demo-imgtool-zh
|
|
|
94
100
|
npx jsmdcui --demo-maze
|
|
95
101
|
```
|
|
96
102
|
|
|
103
|
+
To watch jsmdcui start the maze with a local CDP server and solve it
|
|
104
|
+
automatically after three seconds, run:
|
|
105
|
+
|
|
106
|
+
```sh
|
|
107
|
+
npx jsmdcui --cdp-maze
|
|
108
|
+
```
|
|
109
|
+
|
|
97
110
|
```sh
|
|
98
111
|
# WUI(Web User Interface) Demo
|
|
99
112
|
npx jsmdcui --wui
|
|
@@ -147,14 +160,19 @@ bun src/index.js --wui testapp.md
|
|
|
147
160
|
| `bun src/index.js --cat app.md` | Render the terminal version to stdout, write five generated files beside it, and exit. |
|
|
148
161
|
| `bun src/index.js --testapp.md` | Write the bundled `testapp.md` source to stdout and exit. |
|
|
149
162
|
| `bun src/index.js --export-readme` | Write or overwrite `./README.md` with the bundled README source and exit. |
|
|
163
|
+
| `bun src/index.js --export-cdp-maze` | Write or overwrite `./cdp-maze.js` with the bundled CDP maze solver and exit. |
|
|
150
164
|
| `bun src/index.js --demo-list` | List `testapp.md` and every bundled `demos/*.md` example with its command-line option, then exit. |
|
|
151
165
|
| `bun src/index.js --demo` | Use local `testapp.md` when present, otherwise write the bundled demo; open it in the terminal UI and write five generated files beside it. |
|
|
166
|
+
| `bun src/index.js --overwrite-demo --demo` | Replace an existing local `testapp.md` with the bundled copy before opening it. `--overwrite-demo` can modify any `--demo-*` option. |
|
|
152
167
|
| `bun src/index.js --demo-<filename>` | Load `demos/<filename>.md`; preserve an existing local copy or write the bundled copy, then open it and generate its five companion files. New files added under `demos/` work automatically. |
|
|
153
168
|
| `bun src/index.js --demo-imgtool` | Compatibility alias for `--demo-image-processor`. |
|
|
154
169
|
| `bun src/index.js --demo-imgtool-zh` | Compatibility alias for `--demo-image-processor.zh-TW`. |
|
|
170
|
+
| `bun src/index.js --cdp-maze` | Load the maze demo, start CDP on `127.0.0.1:9222`, and run the bundled solver after three seconds. |
|
|
155
171
|
| `bun src/index.js --allow-url URL.md` | Download HTTP(S) Markdown to the current directory and, with Kitty mode enabled, download its HTTP(S) images; write 5 generated files and allow embedded code to run. Only use trusted URLs. |
|
|
156
172
|
| `bun src/index.js --wui` | Use local `testapp.md` when present, otherwise write the bundled demo; write five generated files in the current directory, then print and serve a random URL. |
|
|
157
173
|
| `bun src/index.js --wui app.md` | Write five generated files beside `app.md`, then print and serve a random URL. |
|
|
174
|
+
| `bun src/index.js --wui --demo-<filename>` | Load the selected bundled demo and serve it as a Web UI. |
|
|
175
|
+
| `bun src/index.js --wui --print-ui app.md` | Also print the generated TUI, raw ANSI, and HTML before starting the WUI server. |
|
|
158
176
|
| `PORT=8080 bun src/index.js --wui app.md` | Start the browser UI on another port. |
|
|
159
177
|
| `bun src/index.js` | Open the normal terminal editor with an empty buffer. |
|
|
160
178
|
|
|
@@ -537,6 +555,10 @@ selection, search, and copy remain available.
|
|
|
537
555
|
| `Alt-G` | Show or hide the shortcut bar. |
|
|
538
556
|
| `Ctrl-Q` or `Alt-Q` | Close the current UI. |
|
|
539
557
|
|
|
558
|
+
To preview another color theme, press `Ctrl-E` or click on `€`, type `theme `, then use `Tab`
|
|
559
|
+
and the arrow keys to browse the available themes. Press `Enter` to switch to
|
|
560
|
+
the selected theme, or `Esc` to cancel and restore the previous one.
|
|
561
|
+
|
|
540
562
|
The terminal automatically reflows the Markdown when its width changes.
|
|
541
563
|
Only `javascript:` links execute in the TUI; ordinary web links behave as
|
|
542
564
|
normal links in the browser.
|
|
@@ -554,6 +576,63 @@ supported HTTP(S) images with Kitty graphics, combine the options:
|
|
|
554
576
|
bun src/index.js --kitty --allow-url https://example.com/app.md
|
|
555
577
|
```
|
|
556
578
|
|
|
579
|
+
### CDP control for TUI automation
|
|
580
|
+
|
|
581
|
+
The terminal UI can expose a Chrome DevTools Protocol (CDP) server, so another
|
|
582
|
+
Bun process can inspect the terminal buffer, click buffer coordinates, and send
|
|
583
|
+
real keyboard events to the running TUI.
|
|
584
|
+
|
|
585
|
+
Start CDP from the command line when launching jsmdcui:
|
|
586
|
+
|
|
587
|
+
```sh
|
|
588
|
+
bun src/index.js --remote-debugging-port=9222 demos/maze.md
|
|
589
|
+
bun src/index.js --remote-debugging-port=9222 --remote-debugging-address=127.0.0.1 demos/maze.md
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
Or start it from inside the TUI editor command prompt(Ctrl-E or €):
|
|
593
|
+
|
|
594
|
+
```text
|
|
595
|
+
Ctrl-E cdp
|
|
596
|
+
Ctrl-E cdp 9000
|
|
597
|
+
Ctrl-E cdp --address=127.0.0.1
|
|
598
|
+
Ctrl-E cdp 9000 --public
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
The default bind address is `127.0.0.1` and the default port is `9222`.
|
|
602
|
+
Use `--public` or `--address=0.0.0.0` only on a trusted network.
|
|
603
|
+
|
|
604
|
+
Once CDP is running, control the TUI with `Bun.WebView`. For more info, enter
|
|
605
|
+
jsmdcui and use `Ctrl-E` or `€` → `help cdp`.
|
|
606
|
+
|
|
607
|
+
The cli flag `--cdp-maze` is a combination of
|
|
608
|
+
1. Start the demos/maze.md
|
|
609
|
+
2. Start a local CDP server
|
|
610
|
+
3. Wait three seconds
|
|
611
|
+
4. Run the solver cdp-maze.js automatically
|
|
612
|
+
|
|
613
|
+
The solver was
|
|
614
|
+
generated from the `llm-maze.txt` instructions; it focuses the maze controls,
|
|
615
|
+
resets the game, reads the maze from the TUI, solves it with breadth-first
|
|
616
|
+
search, and sends arrow-key input until the maze is escaped:
|
|
617
|
+
|
|
618
|
+
```sh
|
|
619
|
+
npx jsmdcui@latest --cdp-maze
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
From the source tree, run:
|
|
623
|
+
|
|
624
|
+
```sh
|
|
625
|
+
bun src/index.js --cdp-maze
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
Useful automation methods used by `cdp-maze.js`:
|
|
629
|
+
|
|
630
|
+
- `view.evaluate(js)`
|
|
631
|
+
- `view.evaluate("micro.getAllText()")`
|
|
632
|
+
- `view.evaluate("micro.getAllAnsiText()")`
|
|
633
|
+
- `view.click(column, line)`
|
|
634
|
+
- `view.press(key, options)`
|
|
635
|
+
|
|
557
636
|
## Browser interaction
|
|
558
637
|
|
|
559
638
|
The WUI uses normal browser mouse and keyboard behavior. Clicking a
|
|
@@ -666,6 +745,67 @@ or serve Markdown UI files that you trust.
|
|
|
666
745
|
calls, but it does not protect the backend module from trusted local code and
|
|
667
746
|
is not a substitute for authentication.
|
|
668
747
|
|
|
748
|
+
## Distribution
|
|
749
|
+
|
|
750
|
+
### Text editor distribution
|
|
751
|
+
|
|
752
|
+
Distributions intended primarily as text editors can include an empty
|
|
753
|
+
`src/MDCUI_DEFAULT_EDIT` file. When this marker exists, opening a `.md` file uses the
|
|
754
|
+
normal editable UTF-8 view instead of automatically entering `mdcui` mode.
|
|
755
|
+
Markdown UI support remains available explicitly with `--mdcui` or `--tui`;
|
|
756
|
+
both are equivalent to `-encoding mdcui`.
|
|
757
|
+
|
|
758
|
+
### Build-time distribution constants
|
|
759
|
+
|
|
760
|
+
Single-file distributions can define one of these default modes:
|
|
761
|
+
|
|
762
|
+
- `MDCUI_DEFAULT_EDIT`: open files as editable text by default.
|
|
763
|
+
- `MDCUI_DEFAULT_DEMO`: add `--demo` when launched without arguments.
|
|
764
|
+
- `MDCUI_DEFAULT_DEMO_WUI`: add `--wui` when launched without arguments.
|
|
765
|
+
|
|
766
|
+
Choose only one of the three default-mode constants for a distribution.
|
|
767
|
+
`MDCUI_OVERWRITE_DEMO` is an optional modifier for the demo or WUI mode.
|
|
768
|
+
These are presence-based constants, so their build values do not require
|
|
769
|
+
shell-quoted strings:
|
|
770
|
+
|
|
771
|
+
```sh
|
|
772
|
+
bun src/index.js --build-exe --define MDCUI_DEFAULT_EDIT=true
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
### Ship `testapp.md` as a standalone application
|
|
776
|
+
|
|
777
|
+
To turn your finished Markdown UI into a standalone executable, save it as the
|
|
778
|
+
repository-root `testapp.md`, then build with both demo constants:
|
|
779
|
+
|
|
780
|
+
```sh
|
|
781
|
+
bun src/index.js --build-exe \
|
|
782
|
+
--define MDCUI_DEFAULT_DEMO=true \
|
|
783
|
+
--define MDCUI_OVERWRITE_DEMO=true
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
The build first packs `testapp.md` into the executable and writes the resulting
|
|
787
|
+
`mdcui` binary in the current directory. In that binary,
|
|
788
|
+
`MDCUI_DEFAULT_DEMO` adds `--demo` when the user launches it without arguments,
|
|
789
|
+
and `MDCUI_OVERWRITE_DEMO` adds `--overwrite-demo`. Consequently, running
|
|
790
|
+
`./mdcui` writes the bundled application to `./testapp.md`, replacing an older
|
|
791
|
+
copy, and starts it as the terminal UI.
|
|
792
|
+
|
|
793
|
+
To make a no-argument launch start the browser UI instead, build with
|
|
794
|
+
`MDCUI_DEFAULT_DEMO_WUI`:
|
|
795
|
+
|
|
796
|
+
```sh
|
|
797
|
+
bun src/index.js --build-exe \
|
|
798
|
+
--define MDCUI_DEFAULT_DEMO_WUI=true \
|
|
799
|
+
--define MDCUI_OVERWRITE_DEMO=true
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
This adds `--wui` when the executable is launched without arguments.
|
|
803
|
+
|
|
804
|
+
You can rename and distribute the resulting binary. It contains the Bun
|
|
805
|
+
runtime, jsmdcui, the packed runtime assets, and your `testapp.md`; the target
|
|
806
|
+
directory must remain writable because launching the application creates
|
|
807
|
+
`testapp.md` and its generated companion files there.
|
|
808
|
+
|
|
669
809
|
## Development
|
|
670
810
|
|
|
671
811
|
```sh
|
package/cdp-maze.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
// Generated from llm-maze.txt instructions.
|
|
4
|
+
// Start the maze TUI first:
|
|
5
|
+
// bun src/index.js --remote-debugging-port=9222 demos/maze.md
|
|
6
|
+
// Then run:
|
|
7
|
+
// bun cdp-maze.js
|
|
8
|
+
|
|
9
|
+
const DEFAULT_CDP_URL = "ws://127.0.0.1:9222/devtools/browser/cdp-server";
|
|
10
|
+
|
|
11
|
+
const dirs = [
|
|
12
|
+
[1, 0, "ArrowDown"],
|
|
13
|
+
[-1, 0, "ArrowUp"],
|
|
14
|
+
[0, 1, "ArrowRight"],
|
|
15
|
+
[0, -1, "ArrowLeft"],
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
function parseMaze(text) {
|
|
19
|
+
return text.trimEnd().split("\n").map(line => {
|
|
20
|
+
const cells = [];
|
|
21
|
+
|
|
22
|
+
for (let i = 0; i < line.length;) {
|
|
23
|
+
const ch = String.fromCodePoint(line.codePointAt(i));
|
|
24
|
+
|
|
25
|
+
if (ch === "🧱" || ch === "😀") {
|
|
26
|
+
cells.push(ch);
|
|
27
|
+
i += ch.length;
|
|
28
|
+
} else if (line[i] === " ") {
|
|
29
|
+
cells.push(" ");
|
|
30
|
+
i += 2;
|
|
31
|
+
} else {
|
|
32
|
+
cells.push(ch);
|
|
33
|
+
i += ch.length;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return cells;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function solveMaze(grid) {
|
|
42
|
+
let start;
|
|
43
|
+
|
|
44
|
+
for (let row = 0; row < grid.length; row++) {
|
|
45
|
+
for (let col = 0; col < grid[row].length; col++) {
|
|
46
|
+
if (grid[row][col] === "😀") start = [row, col];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!start) throw new Error("Maze start not found");
|
|
51
|
+
|
|
52
|
+
const queue = [{ row: start[0], col: start[1], path: [] }];
|
|
53
|
+
const seen = new Set([start.join(",")]);
|
|
54
|
+
|
|
55
|
+
while (queue.length) {
|
|
56
|
+
const { row, col, path } = queue.shift();
|
|
57
|
+
|
|
58
|
+
for (const [dr, dc, key] of dirs) {
|
|
59
|
+
const nextRow = row + dr;
|
|
60
|
+
const nextCol = col + dc;
|
|
61
|
+
|
|
62
|
+
if (
|
|
63
|
+
nextRow < 0 ||
|
|
64
|
+
nextRow >= grid.length ||
|
|
65
|
+
nextCol < 0 ||
|
|
66
|
+
nextCol >= grid[row].length
|
|
67
|
+
) {
|
|
68
|
+
if (path.length > 0) return path.concat(key);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (nextCol >= grid[nextRow].length) continue;
|
|
73
|
+
if (grid[nextRow][nextCol] === "🧱") continue;
|
|
74
|
+
|
|
75
|
+
const id = `${nextRow},${nextCol}`;
|
|
76
|
+
if (seen.has(id)) continue;
|
|
77
|
+
|
|
78
|
+
seen.add(id);
|
|
79
|
+
queue.push({
|
|
80
|
+
row: nextRow,
|
|
81
|
+
col: nextCol,
|
|
82
|
+
path: path.concat(key),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
throw new Error("No path found");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export async function runCdpMaze(cdpUrl = DEFAULT_CDP_URL) {
|
|
91
|
+
const view = new Bun.WebView({
|
|
92
|
+
backend: {
|
|
93
|
+
type: "chrome",
|
|
94
|
+
url: cdpUrl,
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
const control = await view.evaluate(`
|
|
100
|
+
(() => {
|
|
101
|
+
const lines = micro.getAllText().split("\\n");
|
|
102
|
+
const y = lines.findIndex(line =>
|
|
103
|
+
line.includes("Put the cursor here")
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
x: lines[y].indexOf("Put the cursor here") + 1,
|
|
108
|
+
y: y + 1,
|
|
109
|
+
};
|
|
110
|
+
})()
|
|
111
|
+
`);
|
|
112
|
+
|
|
113
|
+
await view.click(control.x, control.y);
|
|
114
|
+
await view.press("r", { modifiers: ["Control"] });
|
|
115
|
+
await Bun.sleep(100);
|
|
116
|
+
|
|
117
|
+
const mazeText = await view.evaluate(`$("#character").val()`);
|
|
118
|
+
const path = solveMaze(parseMaze(mazeText));
|
|
119
|
+
|
|
120
|
+
for (const key of path) {
|
|
121
|
+
await view.press(key);
|
|
122
|
+
await Bun.sleep(100);
|
|
123
|
+
|
|
124
|
+
const status = await view.evaluate(`$("#last-key").val()`);
|
|
125
|
+
if (status.includes("Escaped the maze")) break;
|
|
126
|
+
if (status.includes("Wall")) {
|
|
127
|
+
throw new Error(`Hit a wall after ${key}: ${status}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const result = await view.evaluate(`$("#last-key").val()`);
|
|
132
|
+
if (!process.stdin.isRaw) console.log(result);
|
|
133
|
+
return result;
|
|
134
|
+
} finally {
|
|
135
|
+
view.close();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (import.meta.main) {
|
|
140
|
+
await runCdpMaze(Bun.argv[2] ?? DEFAULT_CDP_URL);
|
|
141
|
+
}
|
package/demos/maze.md
CHANGED
|
@@ -126,7 +126,8 @@ export function handle(event) {
|
|
|
126
126
|
if (row === GOAL.row && col === GOAL.col) {
|
|
127
127
|
completed = true;
|
|
128
128
|
$('#last-key').val('🎉🏆🌟 Escaped the maze! / 成功走出迷宮!🌟🏆🎉');
|
|
129
|
-
|
|
129
|
+
$('#character').val([
|
|
130
|
+
'',
|
|
130
131
|
'🎆🎇🎉 恭 喜 過 關 ! 🎉🎇🎆',
|
|
131
132
|
'',
|
|
132
133
|
'🥳 你成功逃出迷宮了! 🥳',
|
|
@@ -135,6 +136,10 @@ export function handle(event) {
|
|
|
135
136
|
'🎈 🎊 🎁 🪩 🎁 🎊 🎈',
|
|
136
137
|
'',
|
|
137
138
|
'🚀 ESCAPED THE MAZE! 🚀',
|
|
139
|
+
'',
|
|
140
|
+
'Press Ctrl-R to reset',
|
|
141
|
+
'按 Ctrl-R 重新開始',
|
|
142
|
+
''
|
|
138
143
|
].join('\n'));
|
|
139
144
|
} else {
|
|
140
145
|
const label = event.key === ' ' ? 'Space' : event.key;
|
package/llm-maze.txt
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
你正在透過 Bun.WebView 操作 jsmdcui TUI App。
|
|
2
|
+
|
|
3
|
+
CDP 連線位址:
|
|
4
|
+
|
|
5
|
+
ws://127.0.0.1:9222/devtools/browser/cdp-server
|
|
6
|
+
|
|
7
|
+
可用操作:
|
|
8
|
+
|
|
9
|
+
1. 取得完整純文字 UI:
|
|
10
|
+
|
|
11
|
+
await view.evaluate("micro.getAllText()")
|
|
12
|
+
|
|
13
|
+
2. 取得包含 ANSI 樣式與 OSC 8 link 的完整 UI:
|
|
14
|
+
|
|
15
|
+
await view.evaluate("micro.getAllAnsiText()")
|
|
16
|
+
|
|
17
|
+
3. 點擊 buffer 座標:
|
|
18
|
+
|
|
19
|
+
await view.click(column, line)
|
|
20
|
+
|
|
21
|
+
column 與 line 都是 1-based。
|
|
22
|
+
這是整份 buffer 的座標,不是螢幕 pixel。
|
|
23
|
+
不支援 selector click。
|
|
24
|
+
|
|
25
|
+
4. 發送真正的鍵盤事件:
|
|
26
|
+
|
|
27
|
+
await view.press("ArrowUp")
|
|
28
|
+
await view.press("ArrowDown")
|
|
29
|
+
await view.press("ArrowLeft")
|
|
30
|
+
await view.press("ArrowRight")
|
|
31
|
+
await view.press("r", { modifiers: ["Control"] })
|
|
32
|
+
|
|
33
|
+
5. 每次 click 或 press 後,重新取得 UI 快照確認結果。
|
|
34
|
+
|
|
35
|
+
Maze 操作方式:
|
|
36
|
+
|
|
37
|
+
先在 micro.getAllText() 中找到包含 "Put the cursor here" 的行,計算該文字的
|
|
38
|
+
1-based column/line,然後 click 該位置,讓 Maze controls 取得焦點。
|
|
39
|
+
|
|
40
|
+
Maze controls 取得焦點後:
|
|
41
|
+
|
|
42
|
+
- ArrowUp:上
|
|
43
|
+
- ArrowDown:下
|
|
44
|
+
- ArrowLeft:左
|
|
45
|
+
- ArrowRight:右
|
|
46
|
+
- Ctrl-R:重新開始
|
|
47
|
+
|
|
48
|
+
每次移動後可讀取狀態:
|
|
49
|
+
|
|
50
|
+
await view.evaluate("$('#last-key').val()")
|
|
51
|
+
|
|
52
|
+
取得完整迷宮畫面:
|
|
53
|
+
|
|
54
|
+
await view.evaluate("$('#character').val()")
|
|
55
|
+
|
|
56
|
+
看到狀態包含 "Escaped the maze" 代表成功。抵達終點時不會呼叫 alert;大型
|
|
57
|
+
慶祝內容會直接顯示在 character 區域,並提示使用 Ctrl-R 重新開始。
|
|
58
|
+
|
|
59
|
+
初始化與聚焦 controls 的範例:
|
|
60
|
+
|
|
61
|
+
const view = new Bun.WebView({
|
|
62
|
+
backend: {
|
|
63
|
+
type: "chrome",
|
|
64
|
+
url: "ws://127.0.0.1:9222/devtools/browser/cdp-server",
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const cell = await view.evaluate(`
|
|
69
|
+
(() => {
|
|
70
|
+
const lines = micro.getAllText().split("\\n");
|
|
71
|
+
const y = lines.findIndex(line =>
|
|
72
|
+
line.includes("Put the cursor here")
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
x: lines[y].indexOf("Put the cursor here") + 1,
|
|
77
|
+
y: y + 1,
|
|
78
|
+
};
|
|
79
|
+
})()
|
|
80
|
+
`);
|
|
81
|
+
|
|
82
|
+
await view.click(cell.x, cell.y);
|
|
83
|
+
await view.press("r", { modifiers: ["Control"] });
|
|
84
|
+
|
|
85
|
+
alert 注意事項:
|
|
86
|
+
|
|
87
|
+
Maze 抵達終點不會觸發 alert。不過 jsmdcui 其他操作若呼叫同步 alert(),CDP
|
|
88
|
+
operation 會等待使用者直接在實際 terminal 按 Enter 關閉;alert 阻塞期間,無法
|
|
89
|
+
再透過 view.press("Enter") 關閉它。無人值守自動化應避免觸發同步 alert,或預先
|
|
90
|
+
提供替代的 dialog handling。
|
|
91
|
+
|
|
92
|
+
操作完成後呼叫:
|
|
93
|
+
|
|
94
|
+
view.close();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jsmdcui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"description": "Markdown as a Common UI for Terminals and Web Browsers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -9,9 +9,11 @@
|
|
|
9
9
|
"jsmdcui": "./src/index.js"
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
|
-
"start": "bun ./src/index.js",
|
|
13
|
-
"tui": "bun ./
|
|
14
|
-
"wui": "bun ./
|
|
15
|
-
"
|
|
12
|
+
"start": "bun ./src/index.js --demo",
|
|
13
|
+
"tui": "bun ./tui",
|
|
14
|
+
"wui": "bun ./wui",
|
|
15
|
+
"edit": "bun ./edit",
|
|
16
|
+
"check": "bun ./src/index.js --check",
|
|
17
|
+
"clean": "bun ./clean.sh"
|
|
16
18
|
}
|
|
17
19
|
}
|
package/runmd.mjs
CHANGED
|
@@ -29,10 +29,10 @@ async function readTemplate(pathname)
|
|
|
29
29
|
await Bun.file(path.join(REPO_ROOT, pathname)).text()
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
export async function readMarkdownInput(mdpath)
|
|
32
|
+
export async function readMarkdownInput(mdpath, overwriteDemo = false)
|
|
33
33
|
{
|
|
34
34
|
const file = Bun.file(mdpath);
|
|
35
|
-
if (await file.exists()) return await file.text();
|
|
35
|
+
if (!overwriteDemo && await file.exists()) return await file.text();
|
|
36
36
|
const assetName = path.basename(mdpath);
|
|
37
37
|
const internalText = readInternalAssetText(assetName);
|
|
38
38
|
if (internalText != null) {
|
|
@@ -50,30 +50,39 @@ export async function readMarkdownInput(mdpath)
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
export async function main(tuiWidth=30
|
|
53
|
+
export async function main(tuiWidth=30, {
|
|
54
|
+
mdpath: requestedMdpath = null,
|
|
55
|
+
overwriteDemo = process.argv.includes("--overwrite-demo"),
|
|
56
|
+
printUi = process.argv.includes("--print-ui"),
|
|
57
|
+
} = {})
|
|
54
58
|
{
|
|
55
|
-
const
|
|
59
|
+
const explicitMdpath = process.argv.find(i=>i.endsWith('.md'))
|
|
60
|
+
const mdpath = requestedMdpath || explicitMdpath || 'testapp.md'
|
|
56
61
|
|
|
57
62
|
|
|
58
63
|
// 1. Read markdown file
|
|
59
64
|
console.error('Reading:',mdpath)
|
|
60
|
-
let md = await readMarkdownInput(mdpath)
|
|
65
|
+
let md = await readMarkdownInput(mdpath, overwriteDemo && !requestedMdpath && !explicitMdpath)
|
|
61
66
|
|
|
62
67
|
// 2. Extract js files
|
|
63
68
|
md = await extractJs(md,mdpath);
|
|
64
69
|
|
|
65
70
|
// 3. Create Terminal UI
|
|
66
71
|
let tui = createTui(md,tuiWidth)
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
72
|
+
if (printUi) {
|
|
73
|
+
cse(mda("\n# TUI"))
|
|
74
|
+
cse(tui)
|
|
75
|
+
cse(mda('## TUI raw'))
|
|
76
|
+
cse(jss(tui))
|
|
77
|
+
}
|
|
71
78
|
|
|
72
79
|
|
|
73
80
|
// 4. Create Web UI
|
|
74
81
|
let wui = await createWui(md,mdpath)
|
|
75
|
-
|
|
76
|
-
|
|
82
|
+
if (printUi) {
|
|
83
|
+
cse(mda('\n# HTML'))
|
|
84
|
+
cse(wui)
|
|
85
|
+
}
|
|
77
86
|
|
|
78
87
|
|
|
79
88
|
/*
|
package/runtime/help/cdp.md
CHANGED
|
@@ -22,6 +22,11 @@
|
|
|
22
22
|
// Once running, status bar shows: CDP@<port> server running
|
|
23
23
|
// Running cdp again shows: CDP@<port> already running
|
|
24
24
|
//
|
|
25
|
+
// ── Maze demo from the source tree ─────────────────────────────────────────
|
|
26
|
+
//
|
|
27
|
+
// Start the maze, local CDP server, and automatic solver together:
|
|
28
|
+
// bun src/index.js --cdp-maze
|
|
29
|
+
//
|
|
25
30
|
// ── Connecting ─────────────────────────────────────────────────────────────
|
|
26
31
|
//
|
|
27
32
|
// Bun.WebView:
|