jsmdcui 0.9.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 +63 -0
- package/README.md +64 -0
- package/package.json +7 -5
- package/runmd.mjs +20 -11
- package/runtime/help/help.md +64 -0
- package/single-exe/README.md +45 -0
- package/single-exe/compiled.js +5 -3
- package/src/index.js +164 -66
- package/tests/cat-markdown.test.js +59 -0
- package/tests/demo.test.js +41 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,69 @@
|
|
|
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
|
+
|
|
5
68
|
## [0.9.0] - 2026-07-21
|
|
6
69
|
|
|
7
70
|
This update expands Chrome DevTools Protocol automation for the terminal UI,
|
package/README.md
CHANGED
|
@@ -163,6 +163,7 @@ bun src/index.js --wui testapp.md
|
|
|
163
163
|
| `bun src/index.js --export-cdp-maze` | Write or overwrite `./cdp-maze.js` with the bundled CDP maze solver and exit. |
|
|
164
164
|
| `bun src/index.js --demo-list` | List `testapp.md` and every bundled `demos/*.md` example with its command-line option, then exit. |
|
|
165
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. |
|
|
166
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. |
|
|
167
168
|
| `bun src/index.js --demo-imgtool` | Compatibility alias for `--demo-image-processor`. |
|
|
168
169
|
| `bun src/index.js --demo-imgtool-zh` | Compatibility alias for `--demo-image-processor.zh-TW`. |
|
|
@@ -170,6 +171,8 @@ bun src/index.js --wui testapp.md
|
|
|
170
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. |
|
|
171
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. |
|
|
172
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. |
|
|
173
176
|
| `PORT=8080 bun src/index.js --wui app.md` | Start the browser UI on another port. |
|
|
174
177
|
| `bun src/index.js` | Open the normal terminal editor with an empty buffer. |
|
|
175
178
|
|
|
@@ -742,6 +745,67 @@ or serve Markdown UI files that you trust.
|
|
|
742
745
|
calls, but it does not protect the backend module from trusted local code and
|
|
743
746
|
is not a substitute for authentication.
|
|
744
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
|
+
|
|
745
809
|
## Development
|
|
746
810
|
|
|
747
811
|
```sh
|
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/help.md
CHANGED
|
@@ -163,6 +163,7 @@ bun src/index.js --wui testapp.md
|
|
|
163
163
|
| `bun src/index.js --export-cdp-maze` | Write or overwrite `./cdp-maze.js` with the bundled CDP maze solver and exit. |
|
|
164
164
|
| `bun src/index.js --demo-list` | List `testapp.md` and every bundled `demos/*.md` example with its command-line option, then exit. |
|
|
165
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. |
|
|
166
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. |
|
|
167
168
|
| `bun src/index.js --demo-imgtool` | Compatibility alias for `--demo-image-processor`. |
|
|
168
169
|
| `bun src/index.js --demo-imgtool-zh` | Compatibility alias for `--demo-image-processor.zh-TW`. |
|
|
@@ -170,6 +171,8 @@ bun src/index.js --wui testapp.md
|
|
|
170
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. |
|
|
171
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. |
|
|
172
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. |
|
|
173
176
|
| `PORT=8080 bun src/index.js --wui app.md` | Start the browser UI on another port. |
|
|
174
177
|
| `bun src/index.js` | Open the normal terminal editor with an empty buffer. |
|
|
175
178
|
|
|
@@ -742,6 +745,67 @@ or serve Markdown UI files that you trust.
|
|
|
742
745
|
calls, but it does not protect the backend module from trusted local code and
|
|
743
746
|
is not a substitute for authentication.
|
|
744
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
|
+
|
|
745
809
|
## Development
|
|
746
810
|
|
|
747
811
|
```sh
|
package/single-exe/README.md
CHANGED
|
@@ -31,6 +31,51 @@ executable to the current directory. To pass a Bun compilation target, use:
|
|
|
31
31
|
bun ./src/index.js --build-for <target>
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
Any additional arguments after `--build-exe`, or after the target passed to
|
|
35
|
+
`--build-for`, are forwarded to the `bun build` command:
|
|
36
|
+
|
|
37
|
+
```shell
|
|
38
|
+
bun ./src/index.js --build-exe --sourcemap
|
|
39
|
+
bun ./src/index.js --build-for <target> --sourcemap
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The following presence-based build constants select distribution defaults:
|
|
43
|
+
|
|
44
|
+
- `MDCUI_DEFAULT_EDIT`: open files as editable text by default.
|
|
45
|
+
- `MDCUI_DEFAULT_DEMO`: add `--demo` when launched without arguments.
|
|
46
|
+
- `MDCUI_DEFAULT_DEMO_WUI`: add `--wui` when launched without arguments.
|
|
47
|
+
|
|
48
|
+
Choose only one of these three default-mode constants for each build.
|
|
49
|
+
`MDCUI_OVERWRITE_DEMO` is an optional modifier for a demo or WUI build.
|
|
50
|
+
|
|
51
|
+
For example, build a default text editor with:
|
|
52
|
+
|
|
53
|
+
```shell
|
|
54
|
+
bun ./src/index.js --build-exe \
|
|
55
|
+
--define MDCUI_DEFAULT_EDIT=true
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
To build a bundled terminal demo that replaces its local copy:
|
|
59
|
+
|
|
60
|
+
```shell
|
|
61
|
+
bun ./src/index.js --build-exe \
|
|
62
|
+
--define MDCUI_DEFAULT_DEMO=true \
|
|
63
|
+
--define MDCUI_OVERWRITE_DEMO=true
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`MDCUI_DEFAULT_DEMO=true` makes an executable launched without arguments behave
|
|
67
|
+
as if `--demo` was passed. `MDCUI_OVERWRITE_DEMO=true` makes every selected demo
|
|
68
|
+
replace an existing local copy; it does not select a demo by itself.
|
|
69
|
+
|
|
70
|
+
For a browser UI that automatically adds `--wui` when launched without
|
|
71
|
+
arguments, use `MDCUI_DEFAULT_DEMO_WUI` instead:
|
|
72
|
+
|
|
73
|
+
```shell
|
|
74
|
+
bun ./src/index.js --build-exe \
|
|
75
|
+
--define MDCUI_DEFAULT_DEMO_WUI=true \
|
|
76
|
+
--define MDCUI_OVERWRITE_DEMO=true
|
|
77
|
+
```
|
|
78
|
+
|
|
34
79
|
To perform the same steps manually, run these commands from `single-exe/`:
|
|
35
80
|
|
|
36
81
|
```shell
|
package/single-exe/compiled.js
CHANGED
|
@@ -38,10 +38,11 @@ export function getDirnameFromUrl(importMetaUrl) {
|
|
|
38
38
|
return dirname(fileURLToPath(importMetaUrl));
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
export async function buildExecutable(target = "",build_outfile="single.exe") {
|
|
41
|
+
export async function buildExecutable(target = "",build_outfile="single.exe", bunArgs = []) {
|
|
42
42
|
|
|
43
43
|
const outfile = resolve(process.cwd(), build_outfile);
|
|
44
44
|
const normalizedTarget = String(target || "").trim();
|
|
45
|
+
const extraBunArgs = Array.from(bunArgs ?? [], String);
|
|
45
46
|
if(!globalThis.Bun || IS_COMPILED)
|
|
46
47
|
{
|
|
47
48
|
console.log("Build exe can only be run by Bun in the source tree");
|
|
@@ -71,6 +72,7 @@ export async function buildExecutable(target = "",build_outfile="single.exe") {
|
|
|
71
72
|
`--outfile=${outfile}`,
|
|
72
73
|
`--metafile-md=${outfile}.meta.md`,
|
|
73
74
|
...(normalizedTarget ? [`--target=${normalizedTarget}`] : []),
|
|
75
|
+
...extraBunArgs,
|
|
74
76
|
],
|
|
75
77
|
},
|
|
76
78
|
];
|
|
@@ -145,8 +147,8 @@ export async function buildEarlyExit(argv,build_outfile) {
|
|
|
145
147
|
console.error("Missing target value for --build-for");
|
|
146
148
|
process.exit(2);
|
|
147
149
|
}
|
|
148
|
-
process.exit(await buildExe(target,build_outfile));
|
|
150
|
+
process.exit(await buildExe(target, build_outfile, argv.slice(buildForIndex + 2)));
|
|
149
151
|
}
|
|
150
152
|
|
|
151
|
-
process.exit(await buildExe(null,build_outfile));
|
|
153
|
+
process.exit(await buildExe(null, build_outfile, argv.slice(buildExeIndex + 1)));
|
|
152
154
|
}
|
package/src/index.js
CHANGED
|
@@ -181,6 +181,11 @@ const VERSION = pkg.version;
|
|
|
181
181
|
const SINGLE_EXE_DIR = resolve(REPO_ROOT, "single-exe");
|
|
182
182
|
const SINGLE_EXE_ENTRY = resolve(SINGLE_EXE_DIR, "entry.mjs");
|
|
183
183
|
const DEFAULT_BUILD_OUTFILE = "mdcui";
|
|
184
|
+
const MDCUI_DEFAULT_EDIT_ENABLED = typeof MDCUI_DEFAULT_EDIT !== "undefined";
|
|
185
|
+
const MDCUI_DEFAULT_DEMO_ENABLED = typeof MDCUI_DEFAULT_DEMO !== "undefined";
|
|
186
|
+
const MDCUI_DEFAULT_DEMO_WUI_ENABLED = typeof MDCUI_DEFAULT_DEMO_WUI !== "undefined";
|
|
187
|
+
const MDCUI_OVERWRITE_DEMO_ENABLED = typeof MDCUI_OVERWRITE_DEMO !== "undefined";
|
|
188
|
+
let defaultEdit = false;
|
|
184
189
|
const decoder = new TextDecoder();
|
|
185
190
|
let _activeTtyStream = null; // set in App.start() for use by the global error handler
|
|
186
191
|
|
|
@@ -340,7 +345,7 @@ function normalizeEncodingLabel(encoding = "utf-8") {
|
|
|
340
345
|
|
|
341
346
|
function encodingForPath(pathOrUrl, encoding = DEFAULT_SETTINGS.encoding, inferMdcui = true) {
|
|
342
347
|
const normalized = normalizeEncodingLabel(encoding);
|
|
343
|
-
if (normalized !== "utf-8" || !inferMdcui) return normalized;
|
|
348
|
+
if (normalized !== "utf-8" || !inferMdcui || defaultEdit) return normalized;
|
|
344
349
|
let pathname = String(pathOrUrl ?? "").replace(/[?#].*$/, "");
|
|
345
350
|
try {
|
|
346
351
|
if (isHttpUrl(pathname)) pathname = new URL(pathname).pathname;
|
|
@@ -1092,6 +1097,8 @@ function parseArgs(argv) {
|
|
|
1092
1097
|
clean: false,
|
|
1093
1098
|
check: false,
|
|
1094
1099
|
cat: false,
|
|
1100
|
+
wui: false,
|
|
1101
|
+
printUi: false,
|
|
1095
1102
|
docs: false,
|
|
1096
1103
|
exportReadme: false,
|
|
1097
1104
|
exportCdpMaze: false,
|
|
@@ -1099,6 +1106,7 @@ function parseArgs(argv) {
|
|
|
1099
1106
|
testapp: false,
|
|
1100
1107
|
demoList: false,
|
|
1101
1108
|
demo: null,
|
|
1109
|
+
overwriteDemo: false,
|
|
1102
1110
|
allowUrl: false,
|
|
1103
1111
|
buildExe: false,
|
|
1104
1112
|
buildFor: "",
|
|
@@ -1122,6 +1130,8 @@ function parseArgs(argv) {
|
|
|
1122
1130
|
else if (arg === "-clean") flags.clean = true;
|
|
1123
1131
|
else if (arg === "--check") flags.check = true;
|
|
1124
1132
|
else if (arg === "--cat" || arg === "-cat" || arg === "--ccat" || arg === "-ccat" || arg === "--bat" || arg === "-bat" || arg === "--glow" || arg === "-glow") flags.cat = true;
|
|
1133
|
+
else if (arg === "--wui") flags.wui = true;
|
|
1134
|
+
else if (arg === "--print-ui") flags.printUi = true;
|
|
1125
1135
|
else if (arg === "--xxd" || arg === "--hexdump") {
|
|
1126
1136
|
flags.cat = true;
|
|
1127
1137
|
flags.settings.set("encoding", "hex3");
|
|
@@ -1138,12 +1148,16 @@ function parseArgs(argv) {
|
|
|
1138
1148
|
else if (arg === "--edit") {
|
|
1139
1149
|
flags.settings.set("encoding", "utf-8");
|
|
1140
1150
|
}
|
|
1151
|
+
else if (arg === "--mdcui" || arg === "--tui") {
|
|
1152
|
+
flags.settings.set("encoding", "mdcui");
|
|
1153
|
+
}
|
|
1141
1154
|
else if (arg === "--docs" || arg === "--readme") flags.docs = true;
|
|
1142
1155
|
else if (arg === "--export-readme") flags.exportReadme = true;
|
|
1143
1156
|
else if (arg === "--export-cdp-maze") flags.exportCdpMaze = true;
|
|
1144
1157
|
else if (arg === "--changelog") flags.changelog = true;
|
|
1145
1158
|
else if (arg === "--testapp.md") flags.testapp = true;
|
|
1146
1159
|
else if (arg === "--demo-list") flags.demoList = true;
|
|
1160
|
+
else if (arg === "--overwrite-demo") flags.overwriteDemo = true;
|
|
1147
1161
|
else if (arg === "--cdp-maze") {
|
|
1148
1162
|
flags.cdpMaze = true;
|
|
1149
1163
|
flags.demo = { option: arg, filename: "maze.md", asset: "demos/maze.md" };
|
|
@@ -1195,17 +1209,56 @@ function parseArgs(argv) {
|
|
|
1195
1209
|
function usage() {
|
|
1196
1210
|
return [
|
|
1197
1211
|
`Usage:
|
|
1198
|
-
${pkg.name} [OPTIONS] [FILE
|
|
1212
|
+
${pkg.name} [OPTIONS] [FILE]
|
|
1213
|
+
|
|
1214
|
+
Execute .md:
|
|
1215
|
+
${pkg.name} [FILE.md]
|
|
1199
1216
|
${pkg.name} --wui [FILE.md]
|
|
1217
|
+
Develop .md:
|
|
1218
|
+
${pkg.name} --edit [FILE.md]
|
|
1219
|
+
${pkg.name} --check <FILE.md>
|
|
1200
1220
|
|
|
1201
1221
|
Modes:
|
|
1202
|
-
--
|
|
1203
|
-
|
|
1204
|
-
|
|
1222
|
+
--tui, --mdcui, -encoding mdcui
|
|
1223
|
+
.md files use this by default
|
|
1224
|
+
Execute a Markdown App
|
|
1225
|
+
Create a Terminal UI
|
|
1226
|
+
Generate & overwrite
|
|
1227
|
+
.front.js, .back.js,
|
|
1228
|
+
.html, -rpc.js, -server.js
|
|
1229
|
+
beside the .md file
|
|
1230
|
+
|
|
1205
1231
|
--wui [FILE.md]
|
|
1206
|
-
|
|
1207
|
-
|
|
1232
|
+
Execute a Markdown App
|
|
1233
|
+
Create a Web UI
|
|
1234
|
+
Start a web server
|
|
1235
|
+
Generate & overwrite
|
|
1236
|
+
.front.js, .back.js,
|
|
1237
|
+
.html, -rpc.js, -server.js
|
|
1238
|
+
beside the .md file
|
|
1239
|
+
|
|
1240
|
+
Without FILE.md,
|
|
1241
|
+
default to ./testapp.md
|
|
1208
1242
|
If ./testapp.md is missing, write the bundled demo there first
|
|
1243
|
+
Combine with any --demo option to start that demo as a Web UI
|
|
1244
|
+
|
|
1245
|
+
--print-ui
|
|
1246
|
+
Must be combined with --wui
|
|
1247
|
+
Print the generated TUI, raw ANSI, and HTML before starting the server
|
|
1248
|
+
|
|
1249
|
+
--kitty
|
|
1250
|
+
Display Markdown images with Kitty graphics and the jsgotty MIME extension
|
|
1251
|
+
--kitty-compat
|
|
1252
|
+
Display Markdown images with Kitty graphics without the non-standard MIME U field
|
|
1253
|
+
|
|
1254
|
+
--edit
|
|
1255
|
+
Open files as editable UTF-8 text
|
|
1256
|
+
Override .md mdcui detection
|
|
1257
|
+
|
|
1258
|
+
--check <FILE.md>
|
|
1259
|
+
Check heading and fenced-block IDs for collisions, print details, and exit
|
|
1260
|
+
Exits 0 when IDs are unique, 1 on collisions, and 2 on usage/read errors
|
|
1261
|
+
|
|
1209
1262
|
--cat, --ccat, --bat, --glow
|
|
1210
1263
|
Render file(s) and write to stdout, then exit (.md uses mdcui/createTui)
|
|
1211
1264
|
A local .md file also writes or overwrites five generated files beside it
|
|
@@ -1214,15 +1267,6 @@ Modes:
|
|
|
1214
1267
|
--hex3, --hex3gz, --hex3zst
|
|
1215
1268
|
Set -encoding hex3, hex3gz, or hex3zst for this session
|
|
1216
1269
|
hex3 shows raw bytes; gz/zst variants compress the same hex3 view
|
|
1217
|
-
--edit
|
|
1218
|
-
Open files as editable UTF-8 text, overriding .md mdcui detection
|
|
1219
|
-
-encoding mdcui
|
|
1220
|
-
Render Markdown through runmd.mjs#createTui; .md files use this automatically
|
|
1221
|
-
Writes .front.js, .back.js, .html, -rpc.js, and -server.js beside the .md file
|
|
1222
|
-
--kitty
|
|
1223
|
-
Display Markdown images with Kitty graphics and the jsgotty MIME extension
|
|
1224
|
-
--kitty-compat
|
|
1225
|
-
Display Markdown images with Kitty graphics without the non-standard MIME U field
|
|
1226
1270
|
|
|
1227
1271
|
Settings:
|
|
1228
1272
|
-SETTING VALUE
|
|
@@ -1230,54 +1274,73 @@ Settings:
|
|
|
1230
1274
|
-options
|
|
1231
1275
|
List all setting names and defaults, then exit.
|
|
1232
1276
|
|
|
1233
|
-
CDP:
|
|
1277
|
+
CDP(Chrome DevTools Protocol):
|
|
1234
1278
|
--cdp-maze
|
|
1235
1279
|
Open the maze demo, start CDP on localhost:9222, and solve it automatically
|
|
1280
|
+
--export-cdp-maze
|
|
1281
|
+
Write or overwrite ./cdp-maze.js with the bundled CDP maze solver & exit
|
|
1282
|
+
|
|
1236
1283
|
--remote-debugging-port=PORT
|
|
1237
1284
|
Start CDP (Chrome DevTools Protocol) server on PORT at launch
|
|
1238
1285
|
--remote-debugging-address=ADDRESS
|
|
1239
1286
|
Bind CDP server to ADDRESS (default: 127.0.0.1); use 0.0.0.0 for all interfaces
|
|
1240
1287
|
|
|
1241
1288
|
Information:
|
|
1242
|
-
|
|
1289
|
+
--help, -h, -help
|
|
1243
1290
|
Show this help & exit
|
|
1244
|
-
|
|
1291
|
+
--version, -V, -version
|
|
1245
1292
|
Show version+backend info & exit
|
|
1246
|
-
|
|
1293
|
+
|
|
1294
|
+
--readme, --docs
|
|
1247
1295
|
Show ${pkg.name}'s README.md & exit
|
|
1248
1296
|
--export-readme
|
|
1249
1297
|
Write or overwrite ./README.md with the bundled README.md & exit
|
|
1250
|
-
|
|
1251
|
-
Write or overwrite ./cdp-maze.js with the bundled CDP maze solver & exit
|
|
1298
|
+
|
|
1252
1299
|
--changelog
|
|
1253
1300
|
Show CHANGELOG.md & exit
|
|
1254
|
-
|
|
1301
|
+
--profile, -profile
|
|
1255
1302
|
Print startup performance profile and exit
|
|
1256
1303
|
|
|
1257
1304
|
Demo:
|
|
1258
1305
|
--testapp.md
|
|
1259
|
-
|
|
1306
|
+
Print the bundled testapp.md to stdout & exit
|
|
1260
1307
|
--demo-list
|
|
1261
|
-
List the bundled demos
|
|
1308
|
+
List the bundled demos & exit
|
|
1309
|
+
|
|
1262
1310
|
--demo
|
|
1263
|
-
|
|
1311
|
+
Execute an existing ./testapp.md
|
|
1312
|
+
Or write the bundled demo if missing
|
|
1264
1313
|
Open it in the TUI and write 5 generated files beside it
|
|
1314
|
+
|
|
1265
1315
|
--demo-<filename>
|
|
1266
|
-
|
|
1316
|
+
Execute an existing <filename>.md
|
|
1317
|
+
Or write the bundled demos/<filename>.md if missing
|
|
1267
1318
|
Open it in the TUI and write 5 generated files beside it
|
|
1268
1319
|
For example: --demo-select, --demo-todo, or --demo-todo-zh
|
|
1320
|
+
|
|
1269
1321
|
--demo-imgtool
|
|
1270
1322
|
Alias for --demo-image-processor
|
|
1271
1323
|
--demo-imgtool-zh
|
|
1272
1324
|
Alias for --demo-image-processor.zh-TW
|
|
1273
1325
|
|
|
1326
|
+
--overwrite-demo
|
|
1327
|
+
Overwrite an existing local demo with the bundled copy; combine with any --demo option
|
|
1328
|
+
|
|
1274
1329
|
Remote Markdown:
|
|
1275
1330
|
--allow-url
|
|
1276
1331
|
Download HTTP(S) Markdown and, with Kitty mode, its HTTP(S) images; allow its code to run
|
|
1277
1332
|
|
|
1278
|
-
Experimental:
|
|
1279
|
-
--build-exe
|
|
1280
|
-
|
|
1333
|
+
Experimental single-exe:
|
|
1334
|
+
--build-exe [BUN_ARGS...]
|
|
1335
|
+
Build a Bun single-file executable & exit
|
|
1336
|
+
--build-for <target> [BUN_ARGS...]
|
|
1337
|
+
Build a Bun single-file executable for target & exit
|
|
1338
|
+
|
|
1339
|
+
[BUN_ARGS...]
|
|
1340
|
+
--define MDCUI_XXX=true
|
|
1341
|
+
More info in --readme
|
|
1342
|
+
https://bun.com/docs/bundler/executables
|
|
1343
|
+
`
|
|
1281
1344
|
].join("\n");
|
|
1282
1345
|
}
|
|
1283
1346
|
|
|
@@ -8073,19 +8136,42 @@ function printDemoList() {
|
|
|
8073
8136
|
console.log(" --demo-imgtool-zh --demo-image-processor.zh-TW");
|
|
8074
8137
|
}
|
|
8075
8138
|
|
|
8076
|
-
async function
|
|
8077
|
-
if (
|
|
8078
|
-
|
|
8079
|
-
|
|
8080
|
-
|
|
8081
|
-
return;
|
|
8139
|
+
async function prepareDemo(flags, rawFiles) {
|
|
8140
|
+
if (!flags.demo) return true;
|
|
8141
|
+
if (flags.demo.error) {
|
|
8142
|
+
console.error(`Invalid demo option ${flags.demo.option}: ${flags.demo.error}`);
|
|
8143
|
+
process.exitCode = 2;
|
|
8144
|
+
return false;
|
|
8145
|
+
}
|
|
8146
|
+
let demoSource;
|
|
8147
|
+
try {
|
|
8148
|
+
demoSource = await bundledMarkdownSource(flags.demo.asset);
|
|
8149
|
+
} catch {
|
|
8150
|
+
console.error(`Unknown demo ${flags.demo.option}: ${flags.demo.asset} was not found`);
|
|
8151
|
+
process.exitCode = 2;
|
|
8152
|
+
return false;
|
|
8153
|
+
}
|
|
8154
|
+
const demoPath = resolve(flags.demo.filename);
|
|
8155
|
+
if (flags.overwriteDemo || !(await Bun.file(demoPath).exists())) {
|
|
8156
|
+
await Bun.write(demoPath, demoSource);
|
|
8082
8157
|
}
|
|
8158
|
+
rawFiles.splice(0, rawFiles.length, demoPath);
|
|
8159
|
+
return true;
|
|
8160
|
+
}
|
|
8083
8161
|
|
|
8162
|
+
async function main() {
|
|
8084
8163
|
addCheckpoint("Argument Parsing");
|
|
8085
8164
|
|
|
8086
8165
|
await buildEarlyExit(null,DEFAULT_BUILD_OUTFILE)
|
|
8166
|
+
defaultEdit = await Bun.file(join(REPO_ROOT, "src", "MDCUI_DEFAULT_EDIT")).exists();
|
|
8087
8167
|
|
|
8088
|
-
const
|
|
8168
|
+
const runtimeArgs = process.argv.slice(2);
|
|
8169
|
+
const noRuntimeArgs = runtimeArgs.length === 0;
|
|
8170
|
+
if (MDCUI_DEFAULT_EDIT_ENABLED) runtimeArgs.unshift("--edit");
|
|
8171
|
+
if (noRuntimeArgs && MDCUI_DEFAULT_DEMO_ENABLED) runtimeArgs.push("--demo");
|
|
8172
|
+
if (noRuntimeArgs && MDCUI_DEFAULT_DEMO_WUI_ENABLED) runtimeArgs.push("--wui");
|
|
8173
|
+
if (MDCUI_OVERWRITE_DEMO_ENABLED) runtimeArgs.push("--overwrite-demo");
|
|
8174
|
+
const { flags, files: rawFiles } = parseArgs(runtimeArgs);
|
|
8089
8175
|
kittyImageMode = flags.kittyMode;
|
|
8090
8176
|
allowRemoteKittyImages = flags.allowUrl;
|
|
8091
8177
|
|
|
@@ -8096,7 +8182,7 @@ async function main() {
|
|
|
8096
8182
|
if (flags.version) {
|
|
8097
8183
|
const ttsCmd = detectTtsCmd();
|
|
8098
8184
|
console.log(pkg.name+":",pkg.description)
|
|
8099
|
-
console.log("
|
|
8185
|
+
console.log(" Made by: Dr. John (醫者小智)")
|
|
8100
8186
|
console.log("")
|
|
8101
8187
|
console.log("Version:", VERSION);
|
|
8102
8188
|
console.log("Runtime:", `Bun ${Bun.version}`);
|
|
@@ -8104,6 +8190,11 @@ async function main() {
|
|
|
8104
8190
|
console.log("Http client:",detectHttpBackend());
|
|
8105
8191
|
console.log("TTS:", ttsCmd ? ttsCmd.cmd[0] : "not found");
|
|
8106
8192
|
console.log({SUPPORTED_ENCODING_LABELS})
|
|
8193
|
+
console.log("Distribution settings:");
|
|
8194
|
+
console.log(" MDCUI_DEFAULT_EDIT:", (MDCUI_DEFAULT_EDIT_ENABLED || defaultEdit) ? "enabled" : "disabled");
|
|
8195
|
+
console.log(" MDCUI_DEFAULT_DEMO:", MDCUI_DEFAULT_DEMO_ENABLED ? "enabled" : "disabled");
|
|
8196
|
+
console.log(" MDCUI_DEFAULT_DEMO_WUI:", MDCUI_DEFAULT_DEMO_WUI_ENABLED ? "enabled" : "disabled");
|
|
8197
|
+
console.log(" MDCUI_OVERWRITE_DEMO:", MDCUI_OVERWRITE_DEMO_ENABLED ? "enabled" : "disabled");
|
|
8107
8198
|
const clipboard = new ClipboardManager();
|
|
8108
8199
|
let osc52Available = false;
|
|
8109
8200
|
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
@@ -8118,6 +8209,16 @@ async function main() {
|
|
|
8118
8209
|
console.log("Clipboard:", backends);
|
|
8119
8210
|
return;
|
|
8120
8211
|
}
|
|
8212
|
+
if (flags.wui) {
|
|
8213
|
+
if (!(await prepareDemo(flags, rawFiles))) return;
|
|
8214
|
+
const runmd = await import("../runmd.mjs");
|
|
8215
|
+
await runmd.main(30, {
|
|
8216
|
+
mdpath: rawFiles[0] ?? null,
|
|
8217
|
+
overwriteDemo: flags.overwriteDemo && rawFiles.length === 0,
|
|
8218
|
+
printUi: flags.printUi,
|
|
8219
|
+
});
|
|
8220
|
+
return;
|
|
8221
|
+
}
|
|
8121
8222
|
if (flags.check) {
|
|
8122
8223
|
if (rawFiles.length !== 1) {
|
|
8123
8224
|
console.error("Usage: jsmdcui --check FILE.md");
|
|
@@ -8162,24 +8263,7 @@ async function main() {
|
|
|
8162
8263
|
return;
|
|
8163
8264
|
}
|
|
8164
8265
|
if (flags.demo) {
|
|
8165
|
-
if (flags
|
|
8166
|
-
console.error(`Invalid demo option ${flags.demo.option}: ${flags.demo.error}`);
|
|
8167
|
-
process.exitCode = 2;
|
|
8168
|
-
return;
|
|
8169
|
-
}
|
|
8170
|
-
let demoSource;
|
|
8171
|
-
try {
|
|
8172
|
-
demoSource = await bundledMarkdownSource(flags.demo.asset);
|
|
8173
|
-
} catch {
|
|
8174
|
-
console.error(`Unknown demo ${flags.demo.option}: ${flags.demo.asset} was not found`);
|
|
8175
|
-
process.exitCode = 2;
|
|
8176
|
-
return;
|
|
8177
|
-
}
|
|
8178
|
-
const demoPath = resolve(flags.demo.filename);
|
|
8179
|
-
if (!(await Bun.file(demoPath).exists())) {
|
|
8180
|
-
await Bun.write(demoPath, demoSource);
|
|
8181
|
-
}
|
|
8182
|
-
rawFiles.splice(0, rawFiles.length, demoPath);
|
|
8266
|
+
if (!(await prepareDemo(flags, rawFiles))) return;
|
|
8183
8267
|
}
|
|
8184
8268
|
if (flags.options) {
|
|
8185
8269
|
for (const [key, value] of Object.entries(defaultAllSettings()).sort(([a], [b]) => a.localeCompare(b))) {
|
|
@@ -8192,6 +8276,7 @@ async function main() {
|
|
|
8192
8276
|
const config = await new Config({ configDir: flags.configDir }).init();
|
|
8193
8277
|
config.applyCliSettings(flags.settings);
|
|
8194
8278
|
const encodingExplicit = flags.settings.has("encoding") || Object.hasOwn(config.parsedSettings, "encoding");
|
|
8279
|
+
const inputFiletype = flags.settings.has("filetype") ? String(flags.settings.get("filetype")) : null;
|
|
8195
8280
|
syncEditorSettings(config);
|
|
8196
8281
|
|
|
8197
8282
|
addCheckpoint("Runtime Registry Init");
|
|
@@ -8203,7 +8288,14 @@ async function main() {
|
|
|
8203
8288
|
const syntaxDefinitions = await loadSyntaxDefinitions(runtime);
|
|
8204
8289
|
|
|
8205
8290
|
if (flags.cat) {
|
|
8206
|
-
await catFiles(
|
|
8291
|
+
await catFiles(
|
|
8292
|
+
rawFiles,
|
|
8293
|
+
colorscheme,
|
|
8294
|
+
syntaxDefinitions,
|
|
8295
|
+
config.getGlobalOption("encoding"),
|
|
8296
|
+
!encodingExplicit,
|
|
8297
|
+
inputFiletype,
|
|
8298
|
+
);
|
|
8207
8299
|
return;
|
|
8208
8300
|
}
|
|
8209
8301
|
|
|
@@ -8268,6 +8360,7 @@ async function main() {
|
|
|
8268
8360
|
runtime,
|
|
8269
8361
|
jsPlugins,
|
|
8270
8362
|
encodingExplicit,
|
|
8363
|
+
inputFiletype,
|
|
8271
8364
|
allowUrl: flags.allowUrl,
|
|
8272
8365
|
kittyMode: flags.kittyMode,
|
|
8273
8366
|
};
|
|
@@ -8725,9 +8818,12 @@ function getSelectionText(buf, selection) {
|
|
|
8725
8818
|
|
|
8726
8819
|
function attachSyntax(buffer, context, path, text) {
|
|
8727
8820
|
buffer._syntaxContext = context;
|
|
8728
|
-
const
|
|
8821
|
+
const explicitFiletype = context.inputFiletype;
|
|
8822
|
+
const def = explicitFiletype == null
|
|
8823
|
+
? detectBufferSyntax(context.syntaxDefinitions, path, text)
|
|
8824
|
+
: context.syntaxDefinitions?.find((candidate) => candidate.filetype === explicitFiletype) ?? null;
|
|
8729
8825
|
buffer.syntaxDefinition = def;
|
|
8730
|
-
buffer.filetype = def?.filetype ?? "unknown";
|
|
8826
|
+
buffer.filetype = explicitFiletype == null ? (def?.filetype ?? "unknown") : String(explicitFiletype);
|
|
8731
8827
|
buffer.Settings.filetype = buffer.filetype;
|
|
8732
8828
|
buffer.highlighter = def ? new Highlighter(def, context.syntaxDefinitions ?? []) : null;
|
|
8733
8829
|
buffer._highlightCache = null;
|
|
@@ -8805,7 +8901,7 @@ function syncEditorSettings(config) {
|
|
|
8805
8901
|
}
|
|
8806
8902
|
}
|
|
8807
8903
|
|
|
8808
|
-
async function catFiles(files, colorscheme, syntaxDefinitions, encoding = DEFAULT_SETTINGS.encoding, inferMdcui = true) {
|
|
8904
|
+
async function catFiles(files, colorscheme, syntaxDefinitions, encoding = DEFAULT_SETTINGS.encoding, inferMdcui = true, filetype = null) {
|
|
8809
8905
|
const targets = files.length > 0 ? files.map((f) => ({ path: f, stdin: false })) : [{ path: null, stdin: true }];
|
|
8810
8906
|
for (const { path: filePath, stdin } of targets) {
|
|
8811
8907
|
let content;
|
|
@@ -8836,7 +8932,7 @@ async function catFiles(files, colorscheme, syntaxDefinitions, encoding = DEFAUL
|
|
|
8836
8932
|
process.stdout.write(ansiContent ?? content);
|
|
8837
8933
|
if (!(ansiContent ?? content).endsWith("\n")) process.stdout.write("\n");
|
|
8838
8934
|
continue;
|
|
8839
|
-
} else if (effectivePath && /\.md$/i.test(effectivePath)) {
|
|
8935
|
+
} else if (filetype == null && effectivePath && /\.md$/i.test(effectivePath)) {
|
|
8840
8936
|
process.stdout.write(
|
|
8841
8937
|
Bun.markdown.ansi(content,{
|
|
8842
8938
|
hyperlinks:true
|
|
@@ -8845,11 +8941,13 @@ async function catFiles(files, colorscheme, syntaxDefinitions, encoding = DEFAUL
|
|
|
8845
8941
|
continue;
|
|
8846
8942
|
}
|
|
8847
8943
|
const lines = normalizeBufferText(content).split("\n");
|
|
8848
|
-
const def =
|
|
8849
|
-
|
|
8850
|
-
|
|
8851
|
-
|
|
8852
|
-
|
|
8944
|
+
const def = filetype == null
|
|
8945
|
+
? detectSyntax(syntaxDefinitions, {
|
|
8946
|
+
path: effectivePath ?? "",
|
|
8947
|
+
firstLine: lines[0] ?? "",
|
|
8948
|
+
lines: lines.slice(0, 50),
|
|
8949
|
+
})
|
|
8950
|
+
: syntaxDefinitions.find((candidate) => candidate.filetype === filetype) ?? null;
|
|
8853
8951
|
const highlighter = def ? new Highlighter(def, syntaxDefinitions) : null;
|
|
8854
8952
|
if (!highlighter) {
|
|
8855
8953
|
process.stdout.write(content);
|
|
@@ -78,3 +78,62 @@ test("--edit overrides the .md mdcui default with utf8", async () => {
|
|
|
78
78
|
await rm(dir, { recursive: true, force: true });
|
|
79
79
|
}
|
|
80
80
|
});
|
|
81
|
+
|
|
82
|
+
test("--mdcui and --tui are equivalent to -encoding mdcui", async () => {
|
|
83
|
+
const dir = await mkdtemp(join(tmpdir(), "jsmdcui-cat-mdcui-"));
|
|
84
|
+
const markdownPath = join(dir, "sample.md");
|
|
85
|
+
await writeFile(markdownPath, "# Heading\n\n- one\n- two\n");
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
const flag = Bun.spawnSync([bunBin, tui, "-cat", "--mdcui", markdownPath], {
|
|
89
|
+
cwd: dir,
|
|
90
|
+
stdout: "pipe",
|
|
91
|
+
stderr: "pipe",
|
|
92
|
+
});
|
|
93
|
+
const encoding = Bun.spawnSync([bunBin, tui, "-cat", "-encoding", "mdcui", markdownPath], {
|
|
94
|
+
cwd: dir,
|
|
95
|
+
stdout: "pipe",
|
|
96
|
+
stderr: "pipe",
|
|
97
|
+
});
|
|
98
|
+
const tuiFlag = Bun.spawnSync([bunBin, tui, "-cat", "--tui", markdownPath], {
|
|
99
|
+
cwd: dir,
|
|
100
|
+
stdout: "pipe",
|
|
101
|
+
stderr: "pipe",
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
expect(flag.exitCode).toBe(0);
|
|
105
|
+
expect(encoding.exitCode).toBe(0);
|
|
106
|
+
expect(tuiFlag.exitCode).toBe(0);
|
|
107
|
+
expect(flag.stdout.toString()).toBe(encoding.stdout.toString());
|
|
108
|
+
expect(tuiFlag.stdout.toString()).toBe(encoding.stdout.toString());
|
|
109
|
+
} finally {
|
|
110
|
+
await rm(dir, { recursive: true, force: true });
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("-filetype forces syntax highlighting independently of the filename", async () => {
|
|
115
|
+
const dir = await mkdtemp(join(tmpdir(), "jsmdcui-cat-filetype-"));
|
|
116
|
+
const textPath = join(dir, "sample.txt");
|
|
117
|
+
await writeFile(textPath, "const answer = 42;\n");
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
const automatic = Bun.spawnSync([bunBin, tui, "-cat", textPath], {
|
|
121
|
+
cwd: dir,
|
|
122
|
+
stdout: "pipe",
|
|
123
|
+
stderr: "pipe",
|
|
124
|
+
});
|
|
125
|
+
const javascript = Bun.spawnSync([bunBin, tui, "-cat", "-filetype", "javascript", textPath], {
|
|
126
|
+
cwd: dir,
|
|
127
|
+
stdout: "pipe",
|
|
128
|
+
stderr: "pipe",
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
expect(automatic.exitCode).toBe(0);
|
|
132
|
+
expect(javascript.exitCode).toBe(0);
|
|
133
|
+
expect(automatic.stdout.toString()).not.toContain("\x1b[");
|
|
134
|
+
expect(javascript.stdout.toString()).toContain("\x1b[");
|
|
135
|
+
expect(Bun.stripANSI(javascript.stdout.toString()).trimEnd()).toBe("const answer = 42;");
|
|
136
|
+
} finally {
|
|
137
|
+
await rm(dir, { recursive: true, force: true });
|
|
138
|
+
}
|
|
139
|
+
});
|
package/tests/demo.test.js
CHANGED
|
@@ -2,6 +2,7 @@ import { expect, test } from "bun:test";
|
|
|
2
2
|
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
+
import { readMarkdownInput } from "../runmd.mjs";
|
|
5
6
|
|
|
6
7
|
const tui = join(import.meta.dir, "..", "tui");
|
|
7
8
|
const bunBin = Bun.which("bun") || process.argv0;
|
|
@@ -14,8 +15,8 @@ test("--help describes the non-overwriting demo behavior", () => {
|
|
|
14
15
|
|
|
15
16
|
expect(result.exitCode).toBe(0);
|
|
16
17
|
const output = result.stdout.toString();
|
|
17
|
-
expect(output).toContain("
|
|
18
|
-
expect(output).toContain("
|
|
18
|
+
expect(output).toContain("Execute an existing ./testapp.md");
|
|
19
|
+
expect(output).toContain("Or write the bundled demo if missing");
|
|
19
20
|
expect(output).toContain("--demo-<filename>");
|
|
20
21
|
expect(output).toContain("demos/<filename>.md");
|
|
21
22
|
expect(output.match(/Open it in the TUI and write 5 generated files beside it/g)?.length).toBe(2);
|
|
@@ -97,6 +98,44 @@ test("--demo preserves an existing testapp.md", async () => {
|
|
|
97
98
|
}
|
|
98
99
|
});
|
|
99
100
|
|
|
101
|
+
test("--overwrite-demo replaces an existing demo with the bundled copy", async () => {
|
|
102
|
+
const dir = await mkdtemp(join(tmpdir(), "jsmdcui-demo-overwrite-"));
|
|
103
|
+
const existing = "# Replace my demo\n";
|
|
104
|
+
try {
|
|
105
|
+
await writeFile(join(dir, "testapp.md"), existing);
|
|
106
|
+
const result = Bun.spawnSync(
|
|
107
|
+
[bunBin, tui, "--overwrite-demo", "--demo", "-cat", "-encoding", "utf8"],
|
|
108
|
+
{
|
|
109
|
+
cwd: dir,
|
|
110
|
+
stdout: "pipe",
|
|
111
|
+
stderr: "pipe",
|
|
112
|
+
},
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
expect(result.exitCode).toBe(0);
|
|
116
|
+
const written = await readFile(join(dir, "testapp.md"), "utf8");
|
|
117
|
+
expect(written).toContain("計算機");
|
|
118
|
+
expect(written).not.toBe(existing);
|
|
119
|
+
expect(Bun.stripANSI(result.stdout.toString())).toContain("計算機");
|
|
120
|
+
} finally {
|
|
121
|
+
await rm(dir, { recursive: true, force: true });
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("WUI demo loading can overwrite an existing testapp.md", async () => {
|
|
126
|
+
const dir = await mkdtemp(join(tmpdir(), "jsmdcui-wui-demo-overwrite-"));
|
|
127
|
+
const demoPath = join(dir, "testapp.md");
|
|
128
|
+
try {
|
|
129
|
+
await writeFile(demoPath, "# Replace my WUI demo\n");
|
|
130
|
+
const source = await readMarkdownInput(demoPath, true);
|
|
131
|
+
|
|
132
|
+
expect(source).toContain("計算機");
|
|
133
|
+
expect(await readFile(demoPath, "utf8")).toBe(source);
|
|
134
|
+
} finally {
|
|
135
|
+
await rm(dir, { recursive: true, force: true });
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
100
139
|
test("--demo-<filename> automatically loads a matching demos file", async () => {
|
|
101
140
|
const dir = await mkdtemp(join(tmpdir(), "jsmdcui-demo-generic-"));
|
|
102
141
|
try {
|