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/src/index.js
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
|
-
let mainPromise = globalThis.assetsLoaderPromise ||
|
|
4
|
-
Promise.resolve();
|
|
5
|
-
|
|
6
3
|
const jsStart = globalThis.Bun ? Bun.nanoseconds() : Date.now() * 1e6;
|
|
7
4
|
const checkpoints = [
|
|
8
5
|
{ name: "Bun Engine Boot", time: 0 },
|
|
@@ -147,7 +144,12 @@ if(!globalThis.Bun)
|
|
|
147
144
|
//console.log(process.argv)
|
|
148
145
|
|
|
149
146
|
console.error('Ran by node, changed to run by bun')
|
|
150
|
-
|
|
147
|
+
const launched = child_process.spawnSync(bunbinary, process.argv.slice(1), {
|
|
148
|
+
stdio: "inherit",
|
|
149
|
+
env: process.env,
|
|
150
|
+
});
|
|
151
|
+
if (launched.error) throw launched.error;
|
|
152
|
+
process.exit(launched.status ?? 1);
|
|
151
153
|
}
|
|
152
154
|
catch(e){
|
|
153
155
|
console.log(`
|
|
@@ -179,6 +181,11 @@ const VERSION = pkg.version;
|
|
|
179
181
|
const SINGLE_EXE_DIR = resolve(REPO_ROOT, "single-exe");
|
|
180
182
|
const SINGLE_EXE_ENTRY = resolve(SINGLE_EXE_DIR, "entry.mjs");
|
|
181
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;
|
|
182
189
|
const decoder = new TextDecoder();
|
|
183
190
|
let _activeTtyStream = null; // set in App.start() for use by the global error handler
|
|
184
191
|
|
|
@@ -338,7 +345,7 @@ function normalizeEncodingLabel(encoding = "utf-8") {
|
|
|
338
345
|
|
|
339
346
|
function encodingForPath(pathOrUrl, encoding = DEFAULT_SETTINGS.encoding, inferMdcui = true) {
|
|
340
347
|
const normalized = normalizeEncodingLabel(encoding);
|
|
341
|
-
if (normalized !== "utf-8" || !inferMdcui) return normalized;
|
|
348
|
+
if (normalized !== "utf-8" || !inferMdcui || defaultEdit) return normalized;
|
|
342
349
|
let pathname = String(pathOrUrl ?? "").replace(/[?#].*$/, "");
|
|
343
350
|
try {
|
|
344
351
|
if (isHttpUrl(pathname)) pathname = new URL(pathname).pathname;
|
|
@@ -1090,12 +1097,16 @@ function parseArgs(argv) {
|
|
|
1090
1097
|
clean: false,
|
|
1091
1098
|
check: false,
|
|
1092
1099
|
cat: false,
|
|
1100
|
+
wui: false,
|
|
1101
|
+
printUi: false,
|
|
1093
1102
|
docs: false,
|
|
1094
1103
|
exportReadme: false,
|
|
1104
|
+
exportCdpMaze: false,
|
|
1095
1105
|
changelog: false,
|
|
1096
1106
|
testapp: false,
|
|
1097
1107
|
demoList: false,
|
|
1098
1108
|
demo: null,
|
|
1109
|
+
overwriteDemo: false,
|
|
1099
1110
|
allowUrl: false,
|
|
1100
1111
|
buildExe: false,
|
|
1101
1112
|
buildFor: "",
|
|
@@ -1103,6 +1114,7 @@ function parseArgs(argv) {
|
|
|
1103
1114
|
debug: false,
|
|
1104
1115
|
profile: false,
|
|
1105
1116
|
plugin: "",
|
|
1117
|
+
cdpMaze: false,
|
|
1106
1118
|
cdpPort: 0,
|
|
1107
1119
|
cdpAddress: "",
|
|
1108
1120
|
kittyMode: "off",
|
|
@@ -1118,6 +1130,8 @@ function parseArgs(argv) {
|
|
|
1118
1130
|
else if (arg === "-clean") flags.clean = true;
|
|
1119
1131
|
else if (arg === "--check") flags.check = true;
|
|
1120
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;
|
|
1121
1135
|
else if (arg === "--xxd" || arg === "--hexdump") {
|
|
1122
1136
|
flags.cat = true;
|
|
1123
1137
|
flags.settings.set("encoding", "hex3");
|
|
@@ -1134,11 +1148,20 @@ function parseArgs(argv) {
|
|
|
1134
1148
|
else if (arg === "--edit") {
|
|
1135
1149
|
flags.settings.set("encoding", "utf-8");
|
|
1136
1150
|
}
|
|
1151
|
+
else if (arg === "--mdcui" || arg === "--tui") {
|
|
1152
|
+
flags.settings.set("encoding", "mdcui");
|
|
1153
|
+
}
|
|
1137
1154
|
else if (arg === "--docs" || arg === "--readme") flags.docs = true;
|
|
1138
1155
|
else if (arg === "--export-readme") flags.exportReadme = true;
|
|
1156
|
+
else if (arg === "--export-cdp-maze") flags.exportCdpMaze = true;
|
|
1139
1157
|
else if (arg === "--changelog") flags.changelog = true;
|
|
1140
1158
|
else if (arg === "--testapp.md") flags.testapp = true;
|
|
1141
1159
|
else if (arg === "--demo-list") flags.demoList = true;
|
|
1160
|
+
else if (arg === "--overwrite-demo") flags.overwriteDemo = true;
|
|
1161
|
+
else if (arg === "--cdp-maze") {
|
|
1162
|
+
flags.cdpMaze = true;
|
|
1163
|
+
flags.demo = { option: arg, filename: "maze.md", asset: "demos/maze.md" };
|
|
1164
|
+
}
|
|
1142
1165
|
else if (arg === "--demo") {
|
|
1143
1166
|
flags.demo = { option: arg, filename: "testapp.md", asset: "testapp.md" };
|
|
1144
1167
|
}
|
|
@@ -1178,23 +1201,64 @@ function parseArgs(argv) {
|
|
|
1178
1201
|
}
|
|
1179
1202
|
}
|
|
1180
1203
|
|
|
1204
|
+
if (flags.cdpMaze && !flags.cdpPort) flags.cdpPort = 9222;
|
|
1205
|
+
|
|
1181
1206
|
return { flags, files };
|
|
1182
1207
|
}
|
|
1183
1208
|
|
|
1184
1209
|
function usage() {
|
|
1185
1210
|
return [
|
|
1186
1211
|
`Usage:
|
|
1187
|
-
${pkg.name} [OPTIONS] [FILE
|
|
1212
|
+
${pkg.name} [OPTIONS] [FILE]
|
|
1213
|
+
|
|
1214
|
+
Execute .md:
|
|
1215
|
+
${pkg.name} [FILE.md]
|
|
1188
1216
|
${pkg.name} --wui [FILE.md]
|
|
1217
|
+
Develop .md:
|
|
1218
|
+
${pkg.name} --edit [FILE.md]
|
|
1219
|
+
${pkg.name} --check <FILE.md>
|
|
1189
1220
|
|
|
1190
1221
|
Modes:
|
|
1191
|
-
--
|
|
1192
|
-
|
|
1193
|
-
|
|
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
|
+
|
|
1194
1231
|
--wui [FILE.md]
|
|
1195
|
-
|
|
1196
|
-
|
|
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
|
|
1197
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
|
+
|
|
1198
1262
|
--cat, --ccat, --bat, --glow
|
|
1199
1263
|
Render file(s) and write to stdout, then exit (.md uses mdcui/createTui)
|
|
1200
1264
|
A local .md file also writes or overwrites five generated files beside it
|
|
@@ -1203,15 +1267,6 @@ Modes:
|
|
|
1203
1267
|
--hex3, --hex3gz, --hex3zst
|
|
1204
1268
|
Set -encoding hex3, hex3gz, or hex3zst for this session
|
|
1205
1269
|
hex3 shows raw bytes; gz/zst variants compress the same hex3 view
|
|
1206
|
-
--edit
|
|
1207
|
-
Open files as editable UTF-8 text, overriding .md mdcui detection
|
|
1208
|
-
-encoding mdcui
|
|
1209
|
-
Render Markdown through runmd.mjs#createTui; .md files use this automatically
|
|
1210
|
-
Writes .front.js, .back.js, .html, -rpc.js, and -server.js beside the .md file
|
|
1211
|
-
--kitty
|
|
1212
|
-
Display Markdown images with Kitty graphics and the jsgotty MIME extension
|
|
1213
|
-
--kitty-compat
|
|
1214
|
-
Display Markdown images with Kitty graphics without the non-standard MIME U field
|
|
1215
1270
|
|
|
1216
1271
|
Settings:
|
|
1217
1272
|
-SETTING VALUE
|
|
@@ -1219,50 +1274,73 @@ Settings:
|
|
|
1219
1274
|
-options
|
|
1220
1275
|
List all setting names and defaults, then exit.
|
|
1221
1276
|
|
|
1222
|
-
CDP:
|
|
1277
|
+
CDP(Chrome DevTools Protocol):
|
|
1278
|
+
--cdp-maze
|
|
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
|
+
|
|
1223
1283
|
--remote-debugging-port=PORT
|
|
1224
1284
|
Start CDP (Chrome DevTools Protocol) server on PORT at launch
|
|
1225
1285
|
--remote-debugging-address=ADDRESS
|
|
1226
1286
|
Bind CDP server to ADDRESS (default: 127.0.0.1); use 0.0.0.0 for all interfaces
|
|
1227
1287
|
|
|
1228
1288
|
Information:
|
|
1229
|
-
|
|
1289
|
+
--help, -h, -help
|
|
1230
1290
|
Show this help & exit
|
|
1231
|
-
|
|
1291
|
+
--version, -V, -version
|
|
1232
1292
|
Show version+backend info & exit
|
|
1233
|
-
|
|
1293
|
+
|
|
1294
|
+
--readme, --docs
|
|
1234
1295
|
Show ${pkg.name}'s README.md & exit
|
|
1235
1296
|
--export-readme
|
|
1236
1297
|
Write or overwrite ./README.md with the bundled README.md & exit
|
|
1298
|
+
|
|
1237
1299
|
--changelog
|
|
1238
1300
|
Show CHANGELOG.md & exit
|
|
1239
|
-
|
|
1301
|
+
--profile, -profile
|
|
1240
1302
|
Print startup performance profile and exit
|
|
1241
1303
|
|
|
1242
1304
|
Demo:
|
|
1243
1305
|
--testapp.md
|
|
1244
|
-
|
|
1306
|
+
Print the bundled testapp.md to stdout & exit
|
|
1245
1307
|
--demo-list
|
|
1246
|
-
List the bundled demos
|
|
1308
|
+
List the bundled demos & exit
|
|
1309
|
+
|
|
1247
1310
|
--demo
|
|
1248
|
-
|
|
1311
|
+
Execute an existing ./testapp.md
|
|
1312
|
+
Or write the bundled demo if missing
|
|
1249
1313
|
Open it in the TUI and write 5 generated files beside it
|
|
1314
|
+
|
|
1250
1315
|
--demo-<filename>
|
|
1251
|
-
|
|
1316
|
+
Execute an existing <filename>.md
|
|
1317
|
+
Or write the bundled demos/<filename>.md if missing
|
|
1252
1318
|
Open it in the TUI and write 5 generated files beside it
|
|
1253
1319
|
For example: --demo-select, --demo-todo, or --demo-todo-zh
|
|
1320
|
+
|
|
1254
1321
|
--demo-imgtool
|
|
1255
1322
|
Alias for --demo-image-processor
|
|
1256
1323
|
--demo-imgtool-zh
|
|
1257
1324
|
Alias for --demo-image-processor.zh-TW
|
|
1258
1325
|
|
|
1326
|
+
--overwrite-demo
|
|
1327
|
+
Overwrite an existing local demo with the bundled copy; combine with any --demo option
|
|
1328
|
+
|
|
1259
1329
|
Remote Markdown:
|
|
1260
1330
|
--allow-url
|
|
1261
1331
|
Download HTTP(S) Markdown and, with Kitty mode, its HTTP(S) images; allow its code to run
|
|
1262
1332
|
|
|
1263
|
-
Experimental:
|
|
1264
|
-
--build-exe
|
|
1265
|
-
|
|
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
|
+
`
|
|
1266
1344
|
].join("\n");
|
|
1267
1345
|
}
|
|
1268
1346
|
|
|
@@ -8003,6 +8081,12 @@ async function exportReadme() {
|
|
|
8003
8081
|
console.log(`Wrote ${readmePath}`);
|
|
8004
8082
|
}
|
|
8005
8083
|
|
|
8084
|
+
async function exportCdpMaze() {
|
|
8085
|
+
const outputPath = resolve("cdp-maze.js");
|
|
8086
|
+
await Bun.write(outputPath, await bundledMarkdownSource("cdp-maze.js"));
|
|
8087
|
+
console.log(`Wrote ${outputPath}`);
|
|
8088
|
+
}
|
|
8089
|
+
|
|
8006
8090
|
async function printChangelogDocs() {
|
|
8007
8091
|
const changelog = readInternalAssetText("CHANGELOG.md") ?? await Bun.file(join(REPO_ROOT, "CHANGELOG.md")).text();
|
|
8008
8092
|
process.stdout.write(Bun.markdown.ansi(changelog, { hyperlinks: true }));
|
|
@@ -8052,19 +8136,42 @@ function printDemoList() {
|
|
|
8052
8136
|
console.log(" --demo-imgtool-zh --demo-image-processor.zh-TW");
|
|
8053
8137
|
}
|
|
8054
8138
|
|
|
8055
|
-
async function
|
|
8056
|
-
if (
|
|
8057
|
-
|
|
8058
|
-
|
|
8059
|
-
|
|
8060
|
-
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;
|
|
8061
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);
|
|
8157
|
+
}
|
|
8158
|
+
rawFiles.splice(0, rawFiles.length, demoPath);
|
|
8159
|
+
return true;
|
|
8160
|
+
}
|
|
8062
8161
|
|
|
8162
|
+
async function main() {
|
|
8063
8163
|
addCheckpoint("Argument Parsing");
|
|
8064
8164
|
|
|
8065
8165
|
await buildEarlyExit(null,DEFAULT_BUILD_OUTFILE)
|
|
8166
|
+
defaultEdit = await Bun.file(join(REPO_ROOT, "src", "MDCUI_DEFAULT_EDIT")).exists();
|
|
8066
8167
|
|
|
8067
|
-
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);
|
|
8068
8175
|
kittyImageMode = flags.kittyMode;
|
|
8069
8176
|
allowRemoteKittyImages = flags.allowUrl;
|
|
8070
8177
|
|
|
@@ -8075,7 +8182,7 @@ async function main() {
|
|
|
8075
8182
|
if (flags.version) {
|
|
8076
8183
|
const ttsCmd = detectTtsCmd();
|
|
8077
8184
|
console.log(pkg.name+":",pkg.description)
|
|
8078
|
-
console.log("
|
|
8185
|
+
console.log(" Made by: Dr. John (醫者小智)")
|
|
8079
8186
|
console.log("")
|
|
8080
8187
|
console.log("Version:", VERSION);
|
|
8081
8188
|
console.log("Runtime:", `Bun ${Bun.version}`);
|
|
@@ -8083,6 +8190,11 @@ async function main() {
|
|
|
8083
8190
|
console.log("Http client:",detectHttpBackend());
|
|
8084
8191
|
console.log("TTS:", ttsCmd ? ttsCmd.cmd[0] : "not found");
|
|
8085
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");
|
|
8086
8198
|
const clipboard = new ClipboardManager();
|
|
8087
8199
|
let osc52Available = false;
|
|
8088
8200
|
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
@@ -8097,6 +8209,16 @@ async function main() {
|
|
|
8097
8209
|
console.log("Clipboard:", backends);
|
|
8098
8210
|
return;
|
|
8099
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
|
+
}
|
|
8100
8222
|
if (flags.check) {
|
|
8101
8223
|
if (rawFiles.length !== 1) {
|
|
8102
8224
|
console.error("Usage: jsmdcui --check FILE.md");
|
|
@@ -8124,6 +8246,10 @@ async function main() {
|
|
|
8124
8246
|
await exportReadme();
|
|
8125
8247
|
return;
|
|
8126
8248
|
}
|
|
8249
|
+
if (flags.exportCdpMaze) {
|
|
8250
|
+
await exportCdpMaze();
|
|
8251
|
+
return;
|
|
8252
|
+
}
|
|
8127
8253
|
if (flags.changelog) {
|
|
8128
8254
|
await printChangelogDocs();
|
|
8129
8255
|
return;
|
|
@@ -8137,24 +8263,7 @@ async function main() {
|
|
|
8137
8263
|
return;
|
|
8138
8264
|
}
|
|
8139
8265
|
if (flags.demo) {
|
|
8140
|
-
if (flags
|
|
8141
|
-
console.error(`Invalid demo option ${flags.demo.option}: ${flags.demo.error}`);
|
|
8142
|
-
process.exitCode = 2;
|
|
8143
|
-
return;
|
|
8144
|
-
}
|
|
8145
|
-
let demoSource;
|
|
8146
|
-
try {
|
|
8147
|
-
demoSource = await bundledMarkdownSource(flags.demo.asset);
|
|
8148
|
-
} catch {
|
|
8149
|
-
console.error(`Unknown demo ${flags.demo.option}: ${flags.demo.asset} was not found`);
|
|
8150
|
-
process.exitCode = 2;
|
|
8151
|
-
return;
|
|
8152
|
-
}
|
|
8153
|
-
const demoPath = resolve(flags.demo.filename);
|
|
8154
|
-
if (!(await Bun.file(demoPath).exists())) {
|
|
8155
|
-
await Bun.write(demoPath, demoSource);
|
|
8156
|
-
}
|
|
8157
|
-
rawFiles.splice(0, rawFiles.length, demoPath);
|
|
8266
|
+
if (!(await prepareDemo(flags, rawFiles))) return;
|
|
8158
8267
|
}
|
|
8159
8268
|
if (flags.options) {
|
|
8160
8269
|
for (const [key, value] of Object.entries(defaultAllSettings()).sort(([a], [b]) => a.localeCompare(b))) {
|
|
@@ -8167,6 +8276,7 @@ async function main() {
|
|
|
8167
8276
|
const config = await new Config({ configDir: flags.configDir }).init();
|
|
8168
8277
|
config.applyCliSettings(flags.settings);
|
|
8169
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;
|
|
8170
8280
|
syncEditorSettings(config);
|
|
8171
8281
|
|
|
8172
8282
|
addCheckpoint("Runtime Registry Init");
|
|
@@ -8178,7 +8288,14 @@ async function main() {
|
|
|
8178
8288
|
const syntaxDefinitions = await loadSyntaxDefinitions(runtime);
|
|
8179
8289
|
|
|
8180
8290
|
if (flags.cat) {
|
|
8181
|
-
await catFiles(
|
|
8291
|
+
await catFiles(
|
|
8292
|
+
rawFiles,
|
|
8293
|
+
colorscheme,
|
|
8294
|
+
syntaxDefinitions,
|
|
8295
|
+
config.getGlobalOption("encoding"),
|
|
8296
|
+
!encodingExplicit,
|
|
8297
|
+
inputFiletype,
|
|
8298
|
+
);
|
|
8182
8299
|
return;
|
|
8183
8300
|
}
|
|
8184
8301
|
|
|
@@ -8243,6 +8360,7 @@ async function main() {
|
|
|
8243
8360
|
runtime,
|
|
8244
8361
|
jsPlugins,
|
|
8245
8362
|
encodingExplicit,
|
|
8363
|
+
inputFiletype,
|
|
8246
8364
|
allowUrl: flags.allowUrl,
|
|
8247
8365
|
kittyMode: flags.kittyMode,
|
|
8248
8366
|
};
|
|
@@ -8370,6 +8488,23 @@ async function main() {
|
|
|
8370
8488
|
if (flags.cdpAddress) cdpArgs.push(`--address=${flags.cdpAddress}`);
|
|
8371
8489
|
await app.handleCommand(`cdp ${cdpArgs.join(" ")}`);
|
|
8372
8490
|
}
|
|
8491
|
+
|
|
8492
|
+
if (flags.cdpMaze) {
|
|
8493
|
+
const solverHost = !flags.cdpAddress || flags.cdpAddress === "0.0.0.0"
|
|
8494
|
+
? "127.0.0.1"
|
|
8495
|
+
: flags.cdpAddress;
|
|
8496
|
+
const solverUrl = `ws://${solverHost}:${flags.cdpPort}/devtools/browser/cdp-server`;
|
|
8497
|
+
setTimeout(async () => {
|
|
8498
|
+
try {
|
|
8499
|
+
const { runCdpMaze } = await import("../cdp-maze.js");
|
|
8500
|
+
app.message = await runCdpMaze(solverUrl);
|
|
8501
|
+
app.render?.();
|
|
8502
|
+
} catch (error) {
|
|
8503
|
+
app.message = `CDP maze solver failed: ${error?.message || error}`;
|
|
8504
|
+
app.render?.();
|
|
8505
|
+
}
|
|
8506
|
+
}, 3000);
|
|
8507
|
+
}
|
|
8373
8508
|
|
|
8374
8509
|
if (flags.profile) {
|
|
8375
8510
|
addCheckpoint("Clipboard Probing");
|
|
@@ -8683,9 +8818,12 @@ function getSelectionText(buf, selection) {
|
|
|
8683
8818
|
|
|
8684
8819
|
function attachSyntax(buffer, context, path, text) {
|
|
8685
8820
|
buffer._syntaxContext = context;
|
|
8686
|
-
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;
|
|
8687
8825
|
buffer.syntaxDefinition = def;
|
|
8688
|
-
buffer.filetype = def?.filetype ?? "unknown";
|
|
8826
|
+
buffer.filetype = explicitFiletype == null ? (def?.filetype ?? "unknown") : String(explicitFiletype);
|
|
8689
8827
|
buffer.Settings.filetype = buffer.filetype;
|
|
8690
8828
|
buffer.highlighter = def ? new Highlighter(def, context.syntaxDefinitions ?? []) : null;
|
|
8691
8829
|
buffer._highlightCache = null;
|
|
@@ -8763,7 +8901,7 @@ function syncEditorSettings(config) {
|
|
|
8763
8901
|
}
|
|
8764
8902
|
}
|
|
8765
8903
|
|
|
8766
|
-
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) {
|
|
8767
8905
|
const targets = files.length > 0 ? files.map((f) => ({ path: f, stdin: false })) : [{ path: null, stdin: true }];
|
|
8768
8906
|
for (const { path: filePath, stdin } of targets) {
|
|
8769
8907
|
let content;
|
|
@@ -8794,7 +8932,7 @@ async function catFiles(files, colorscheme, syntaxDefinitions, encoding = DEFAUL
|
|
|
8794
8932
|
process.stdout.write(ansiContent ?? content);
|
|
8795
8933
|
if (!(ansiContent ?? content).endsWith("\n")) process.stdout.write("\n");
|
|
8796
8934
|
continue;
|
|
8797
|
-
} else if (effectivePath && /\.md$/i.test(effectivePath)) {
|
|
8935
|
+
} else if (filetype == null && effectivePath && /\.md$/i.test(effectivePath)) {
|
|
8798
8936
|
process.stdout.write(
|
|
8799
8937
|
Bun.markdown.ansi(content,{
|
|
8800
8938
|
hyperlinks:true
|
|
@@ -8803,11 +8941,13 @@ async function catFiles(files, colorscheme, syntaxDefinitions, encoding = DEFAUL
|
|
|
8803
8941
|
continue;
|
|
8804
8942
|
}
|
|
8805
8943
|
const lines = normalizeBufferText(content).split("\n");
|
|
8806
|
-
const def =
|
|
8807
|
-
|
|
8808
|
-
|
|
8809
|
-
|
|
8810
|
-
|
|
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;
|
|
8811
8951
|
const highlighter = def ? new Highlighter(def, syntaxDefinitions) : null;
|
|
8812
8952
|
if (!highlighter) {
|
|
8813
8953
|
process.stdout.write(content);
|
|
@@ -8837,9 +8977,6 @@ async function catFiles(files, colorscheme, syntaxDefinitions, encoding = DEFAUL
|
|
|
8837
8977
|
}
|
|
8838
8978
|
|
|
8839
8979
|
|
|
8840
|
-
mainPromise.then(r=>{
|
|
8841
|
-
|
|
8842
|
-
|
|
8843
8980
|
main().catch((error) => {
|
|
8844
8981
|
try {
|
|
8845
8982
|
(_activeTtyStream ?? process.stdin).setRawMode?.(false);
|
|
@@ -8849,6 +8986,3 @@ main().catch((error) => {
|
|
|
8849
8986
|
process.exit(1);
|
|
8850
8987
|
}
|
|
8851
8988
|
});
|
|
8852
|
-
|
|
8853
|
-
|
|
8854
|
-
})
|
package/src/lua/engine.js
CHANGED
|
@@ -2,7 +2,6 @@ import { existsSync } from "node:fs";
|
|
|
2
2
|
import { readInternalAssetBytes } from "../runtime/assets.js";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
|
-
import { isCompiledBinary, resolveCompiledBaseDir } from "../runtime/compiled.js";
|
|
6
5
|
import { REPO_ROOT } from "../../single-exe/compiled.js";
|
|
7
6
|
|
|
8
7
|
export async function createLuaEngine() {
|
|
@@ -44,9 +43,7 @@ async function resolveLuaWasmLocation() {
|
|
|
44
43
|
}
|
|
45
44
|
}
|
|
46
45
|
|
|
47
|
-
const fallbackPath =
|
|
48
|
-
? join(resolveCompiledBaseDir({ argv: process.argv }), wasmAssetPath)
|
|
49
|
-
: join(REPO_ROOT, wasmAssetPath);
|
|
46
|
+
const fallbackPath = join(REPO_ROOT, wasmAssetPath);
|
|
50
47
|
|
|
51
48
|
return existsSync(fallbackPath) ? fallbackPath : undefined;
|
|
52
49
|
}
|
package/src/plugins/js-bridge.js
CHANGED
|
@@ -4,6 +4,7 @@ import { dirname, join, basename, extname } from "node:path";
|
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
6
|
import { assetPath, hasInternalAssets, listInternalAssetDirs, listInternalAssetPaths, readInternalAssetBytes } from "../runtime/assets.js";
|
|
7
|
+
import { isMdcuiEncoding } from "../runtime/encodings.js";
|
|
7
8
|
import { newMessage, newMessageAtLine, MTError, MTWarning, MTInfo } from "../buffer/message.js";
|
|
8
9
|
import { Loc } from "../buffer/loc.js";
|
|
9
10
|
|
|
@@ -1405,6 +1406,48 @@ export function buildMicroGlobal(jsManager) {
|
|
|
1405
1406
|
return app?.buffer?.lines.join("\n") ?? "";
|
|
1406
1407
|
},
|
|
1407
1408
|
|
|
1409
|
+
// Returns the rendered ANSI document when available, otherwise plain text.
|
|
1410
|
+
getAllAnsiText() {
|
|
1411
|
+
const buffer = getApp()?.buffer;
|
|
1412
|
+
return typeof buffer?._mdcuiAnsiText === "string"
|
|
1413
|
+
? buffer._mdcuiAnsiText
|
|
1414
|
+
: buffer?.lines.join("\n") ?? "";
|
|
1415
|
+
},
|
|
1416
|
+
|
|
1417
|
+
// Activates a 1-based rendered-buffer cell in MDCUI; otherwise falls back to goto.
|
|
1418
|
+
async clickBufferCell(column = 1, line = 1) {
|
|
1419
|
+
const app = getApp();
|
|
1420
|
+
const buffer = app?.buffer;
|
|
1421
|
+
if (!app || !buffer) return false;
|
|
1422
|
+
|
|
1423
|
+
const targetLine = Math.max(1, Math.trunc(Number(line)) || 1);
|
|
1424
|
+
const targetColumn = Math.max(1, Math.trunc(Number(column)) || 1);
|
|
1425
|
+
if (!isMdcuiEncoding(buffer.encoding ?? buffer.Settings?.encoding)) {
|
|
1426
|
+
await app.handleCommand(`goto ${targetLine}:${targetColumn}`);
|
|
1427
|
+
app.render?.();
|
|
1428
|
+
return true;
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
const y = Math.min(targetLine - 1, Math.max(0, buffer.lines.length - 1));
|
|
1432
|
+
const x = Math.min(targetColumn - 1, String(buffer.lines[y] ?? "").length);
|
|
1433
|
+
buffer.cursor = { x, y };
|
|
1434
|
+
const handled = await app.handleMdcuiCellCallback(buffer, y, x, "mouse");
|
|
1435
|
+
app.render?.();
|
|
1436
|
+
return handled;
|
|
1437
|
+
},
|
|
1438
|
+
|
|
1439
|
+
// Sends terminal input through the App's normal parser and event pipeline.
|
|
1440
|
+
async _dispatchRawInput(raw) {
|
|
1441
|
+
const app = getApp();
|
|
1442
|
+
if (!app) return false;
|
|
1443
|
+
const bytes = typeof raw === "string"
|
|
1444
|
+
? new TextEncoder().encode(raw)
|
|
1445
|
+
: raw;
|
|
1446
|
+
await app._dispatchInput(bytes);
|
|
1447
|
+
app.render?.();
|
|
1448
|
+
return true;
|
|
1449
|
+
},
|
|
1450
|
+
|
|
1408
1451
|
// Replaces the entire buffer content with text (may contain newlines).
|
|
1409
1452
|
putAllText(text) {
|
|
1410
1453
|
const app = getApp();
|
|
@@ -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
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
import { isCompiledBinary as isSingleExeCompiled } from "../single-exe/compiled.js";
|
|
3
|
+
|
|
4
|
+
test("single-exe recognizes Bun compiled virtual paths", () => {
|
|
5
|
+
expect(isSingleExeCompiled(["bun", "/$bunfs/root/app.js"])).toBe(true);
|
|
6
|
+
expect(isSingleExeCompiled(["bun", "B:/~BUN/root/app.js"])).toBe(true);
|
|
7
|
+
expect(isSingleExeCompiled(["bun", "/project/src/index.js"])).toBe(false);
|
|
8
|
+
});
|