jsmdcui 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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();
@@ -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
+ });
@@ -21,6 +21,7 @@ test("--help describes the non-overwriting demo behavior", () => {
21
21
  expect(output.match(/Open it in the TUI and write 5 generated files beside it/g)?.length).toBe(2);
22
22
  expect(output).toContain("--demo-imgtool");
23
23
  expect(output).toContain("--demo-imgtool-zh");
24
+ expect(output).toContain("--cdp-maze");
24
25
  });
25
26
 
26
27
  test("--demo-list lists root and automatically discovered demos", () => {
@@ -38,6 +39,27 @@ test("--demo-list lists root and automatically discovered demos", () => {
38
39
  expect(output).toContain("--demo-imgtool --demo-image-processor");
39
40
  });
40
41
 
42
+ test("--export-cdp-maze writes and overwrites the bundled solver", async () => {
43
+ const dir = await mkdtemp(join(tmpdir(), "jsmdcui-export-cdp-maze-"));
44
+ const outputPath = join(dir, "cdp-maze.js");
45
+ try {
46
+ await writeFile(outputPath, "old solver\n");
47
+ const result = Bun.spawnSync([bunBin, tui, "--export-cdp-maze"], {
48
+ cwd: dir,
49
+ stdout: "pipe",
50
+ stderr: "pipe",
51
+ });
52
+
53
+ expect(result.exitCode).toBe(0);
54
+ expect(result.stdout.toString()).toContain(`Wrote ${outputPath}`);
55
+ const exported = await readFile(outputPath, "utf8");
56
+ expect(exported).toContain("export async function runCdpMaze");
57
+ expect(exported).not.toContain("old solver");
58
+ } finally {
59
+ await rm(dir, { recursive: true, force: true });
60
+ }
61
+ });
62
+
41
63
  test("--demo writes bundled testapp.md to cwd before opening it", async () => {
42
64
  const dir = await mkdtemp(join(tmpdir(), "jsmdcui-demo-"));
43
65
  try {
@@ -94,6 +116,24 @@ test("--demo-<filename> automatically loads a matching demos file", async () =>
94
116
  }
95
117
  });
96
118
 
119
+ test("--cdp-maze loads the maze demo", async () => {
120
+ const dir = await mkdtemp(join(tmpdir(), "jsmdcui-cdp-maze-"));
121
+ try {
122
+ const result = Bun.spawnSync([bunBin, tui, "--cdp-maze", "-cat", "-encoding", "utf8"], {
123
+ cwd: dir,
124
+ stdout: "pipe",
125
+ stderr: "pipe",
126
+ });
127
+
128
+ expect(result.exitCode).toBe(0);
129
+ const written = await readFile(join(dir, "maze.md"), "utf8");
130
+ expect(written).toContain("Put the cursor here");
131
+ expect(Bun.stripANSI(result.stdout.toString())).toContain("Put the cursor here");
132
+ } finally {
133
+ await rm(dir, { recursive: true, force: true });
134
+ }
135
+ });
136
+
97
137
  test("--demo-<filename> preserves an existing local copy", async () => {
98
138
  const dir = await mkdtemp(join(tmpdir(), "jsmdcui-demo-generic-existing-"));
99
139
  const existing = "# Keep my selector demo\n";
@@ -83,7 +83,7 @@ Content before the image, line 10.
83
83
 
84
84
  ## Image anchor
85
85
 
86
- ![img](../demo.resized.jpg)
86
+ ![img](../maze.jpg)
87
87
 
88
88
  ## After image 01
89
89
  Content after the image, line 01.
package/demo.resized.jpg DELETED
Binary file
@@ -1,25 +0,0 @@
1
- import { basename, dirname, resolve } from "node:path";
2
- import { readFileSync } from "node:fs";
3
- import { fileURLToPath } from "node:url";
4
-
5
- export function isCompiledBinary(argv = process.argv) {
6
- return Boolean(argv?.[1]?.startsWith?.("/$bunfs/"));
7
- }
8
-
9
- export function resolveCompiledBaseDir({ argv = process.argv, execPath = process.execPath } = {}) {
10
- const bn = basename(execPath);
11
- if (bn.startsWith("ld") ||
12
- bn.startsWith("libld") ||
13
- bn.startsWith("linker") ) {
14
- const realArgv = readFileSync("/proc/self/cmdline", "utf8").match(/[^\0]+/g);
15
- return dirname(realArgv?.[1] ?? execPath);
16
- }
17
- return dirname(execPath) || process.cwd();
18
- }
19
-
20
- export function resolveRepoRoot(importMetaUrl, options = {}) {
21
- if (isCompiledBinary(options.argv)) {
22
- return resolveCompiledBaseDir(options);
23
- }
24
- return resolve(dirname(fileURLToPath(importMetaUrl)), "..");
25
- }