jsmdcui 0.11.2 → 0.12.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.
- package/CHANGELOG.md +47 -0
- package/README.md +36 -1
- package/package.json +1 -1
- package/runmd.mjs +14 -2
- package/runtime/help/help.md +2 -1
- package/single-exe/README.md +114 -2
- package/single-exe/assetsHelper.js +89 -0
- package/src/cui/kitty-images.mjs +10 -3
- package/src/cui/kitty-mode.mjs +6 -0
- package/src/cui/server.mjs +1 -0
- package/src/index.js +16 -3
- package/src/lua/engine.js +1 -1
- package/src/plugins/js-bridge.js +1 -1
- package/src/plugins/manager.js +1 -1
- package/src/runtime/registry.js +1 -1
- package/tests/assets-helper.test.js +116 -0
- package/tests/kitty-images.test.js +22 -0
- package/tests/kitty-mode.test.js +14 -0
- package/tests/wui-responsive-images.test.js +2 -0
- package/single-exe/README.md-rpc.js +0 -623
- package/single-exe/README.md-server.js +0 -127
- package/single-exe/README.md.back.js +0 -2
- package/single-exe/README.md.front.js +0 -27
- package/single-exe/README.md.html +0 -325
- package/src/runtime/assets.js +0 -90
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,53 @@
|
|
|
2
2
|
|
|
3
3
|
All notable user-visible changes to jsmdcui are documented here.
|
|
4
4
|
|
|
5
|
+
## [0.12.0] - 2026-07-27
|
|
6
|
+
|
|
7
|
+
This release makes Markdown images embedded by Bun's HTML bundler available to
|
|
8
|
+
the compiled terminal UI, adds environment-controlled Kitty rendering, and
|
|
9
|
+
consolidates the reusable single-executable asset helpers.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Add `JSMDCUI_KITTY_MODE=off|compat|extended` as an environment-controlled
|
|
14
|
+
alternative to the Kitty command-line flags. Explicit `--kitty` and
|
|
15
|
+
`--kitty-compat` arguments continue to take precedence.
|
|
16
|
+
- Preserve each generated WUI image source in `data-mdcui-src`. Bun can rewrite
|
|
17
|
+
the normal `src` to its content-hashed public asset path while the original
|
|
18
|
+
Markdown reference remains available for TUI lookup.
|
|
19
|
+
- Add reusable HTML bundle helpers to `single-exe/assetsHelper.js`. They match
|
|
20
|
+
compiled public image paths to `homepage.files`, canonicalize HTML entities
|
|
21
|
+
and percent-encoded image references, and build a Markdown-reference-to-bunfs
|
|
22
|
+
image map from `homepage.index`.
|
|
23
|
+
- Export the bundled `homepage` from generated Markdown server modules so a
|
|
24
|
+
compiled TUI can inspect the same HTML asset manifest without starting the
|
|
25
|
+
WUI server.
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- Reuse images already embedded by Bun's HTML bundler instead of copying a
|
|
30
|
+
second image archive into the executable. The compiled TUI builds the image
|
|
31
|
+
path map once on first Kitty use, caches it, and reads image bytes lazily from
|
|
32
|
+
Bun's compiled virtual filesystem (`/$bunfs/root/...` on POSIX systems or
|
|
33
|
+
`B:/~BUN/...` on Windows).
|
|
34
|
+
- Load the bundled homepage only when running a compiled executable with
|
|
35
|
+
`global.MDCUI_MAIN` configured. Source-tree launches retain the existing
|
|
36
|
+
filesystem and authorized HTTP image fallbacks without importing a generated
|
|
37
|
+
server module.
|
|
38
|
+
- Consolidate runtime asset access on `single-exe/assetsHelper.js` and remove
|
|
39
|
+
the duplicate `src/runtime/assets.js` implementation.
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
|
|
43
|
+
- Match compiled Markdown images independently of Bun's build-entry-relative
|
|
44
|
+
`homepage.files[].input` paths. This fixes images whose Markdown-relative
|
|
45
|
+
path and bundle-entry-relative path have different spellings.
|
|
46
|
+
- Normalize bare `process.env.JSMDCUI_KITTY_MODE=compat` build definitions into
|
|
47
|
+
JavaScript string literals before forwarding them to `bun build`.
|
|
48
|
+
- Use the global `process` binding for Kitty-mode build constants, allowing Bun
|
|
49
|
+
to replace `process.env.JSMDCUI_KITTY_MODE` at build time instead of missing
|
|
50
|
+
an imported and renamed `node:process` binding.
|
|
51
|
+
|
|
5
52
|
## [0.11.2] - 2026-07-26
|
|
6
53
|
|
|
7
54
|
### Added
|
package/README.md
CHANGED
|
@@ -153,6 +153,7 @@ bun src/index.js --wui testapp.md
|
|
|
153
153
|
| `bun src/index.js app.md` | Render `app.md` as a read-only terminal UI and write five generated files beside it. |
|
|
154
154
|
| `bun src/index.js --kitty app.md` | Display Markdown images with Kitty graphics and the jsgotty MIME extension. |
|
|
155
155
|
| `bun src/index.js --kitty-compat app.md` | Convert Markdown images to PNG with `Bun.Image` and display them using the standard Kitty graphics protocol without the non-standard MIME `U` field. |
|
|
156
|
+
| `JSMDCUI_KITTY_MODE=compat bun src/index.js app.md` | Enable `compat` or `extended` Kitty mode without passing a Kitty command-line flag; the default and invalid values are `off`. |
|
|
156
157
|
| `bun src/index.js --kitty --allow-url URL.md` | Download trusted HTTP(S) Markdown and its HTTP(S) images, then display supported images with Kitty graphics. |
|
|
157
158
|
| `JSMDCUI_KITTY_DEBUG=1 bun src/index.js --kitty app.md` | Enable Kitty image placement logging to `kitty-placement.log`. |
|
|
158
159
|
| `bun src/index.js --check app.md` | Check heading and fenced-block IDs for collisions, print line-by-line details, and exit. |
|
|
@@ -576,6 +577,36 @@ supported HTTP(S) images with Kitty graphics, combine the options:
|
|
|
576
577
|
bun src/index.js --kitty --allow-url https://example.com/app.md
|
|
577
578
|
```
|
|
578
579
|
|
|
580
|
+
Images referenced by an app built with `--build-md-exe` are also available to
|
|
581
|
+
the compiled TUI. Bun embeds them through the generated WUI HTML bundle;
|
|
582
|
+
jsmdcui preserves the original Markdown reference, maps it to Bun's
|
|
583
|
+
content-hashed compiled asset (`/$bunfs/root/...` on POSIX systems or
|
|
584
|
+
`B:/~BUN/...` on Windows), and reads the image lazily when Kitty rendering
|
|
585
|
+
needs it. The executable does not need a second copy in the tar asset archive;
|
|
586
|
+
the runtime uses the path reported by Bun rather than assuming either platform
|
|
587
|
+
prefix.
|
|
588
|
+
|
|
589
|
+
To make a no-argument compiled app enable standard Kitty-compatible rendering
|
|
590
|
+
immediately, inline the environment-mode value at build time:
|
|
591
|
+
|
|
592
|
+
```sh
|
|
593
|
+
npx jsmdcui --build-md-exe myapp.md \
|
|
594
|
+
--define process.env.JSMDCUI_KITTY_MODE=compat
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
For the jsgotty MIME extension, use `extended` instead:
|
|
598
|
+
|
|
599
|
+
```sh
|
|
600
|
+
npx jsmdcui --build-md-exe myapp.md \
|
|
601
|
+
--define process.env.JSMDCUI_KITTY_MODE=extended
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
The jsmdcui build wrapper quotes these bare string values before forwarding
|
|
605
|
+
them to Bun. At the call site, `process` must be the Node/Bun global. Do not
|
|
606
|
+
write `import process from "node:process"`: Bun renames that imported binding
|
|
607
|
+
during bundling, so a define targeting `process.env.JSMDCUI_KITTY_MODE` cannot
|
|
608
|
+
match and inline it.
|
|
609
|
+
|
|
579
610
|
### CDP control for TUI automation
|
|
580
611
|
|
|
581
612
|
The terminal UI can expose a Chrome DevTools Protocol (CDP) server, so another
|
|
@@ -783,6 +814,10 @@ npx jsmdcui --build-md-exe myapp.md
|
|
|
783
814
|
|
|
784
815
|
This is a convenience alias for
|
|
785
816
|
`--build-exe --define global.MDCUI_MAIN=myapp.md`.
|
|
817
|
+
The generated HTML images are embedded by Bun and can be displayed by the
|
|
818
|
+
compiled TUI. Add
|
|
819
|
+
`--define process.env.JSMDCUI_KITTY_MODE=compat` after the Markdown path when
|
|
820
|
+
the executable should enable Kitty-compatible images on a no-argument launch.
|
|
786
821
|
|
|
787
822
|
For cross-compilation, specify the Bun target before the Markdown file:
|
|
788
823
|
|
|
@@ -791,7 +826,7 @@ npx jsmdcui --build-md-for bun-linux-x64-v1.3.14 myapp.md
|
|
|
791
826
|
```
|
|
792
827
|
|
|
793
828
|
This similarly expands to
|
|
794
|
-
`--build-for bun-linux-x64 --define global.MDCUI_MAIN=myapp.md`. Both aliases
|
|
829
|
+
`--build-for bun-linux-x64-v1.3.14 --define global.MDCUI_MAIN=myapp.md`. Both aliases
|
|
795
830
|
are expanded before any generated application files are written.
|
|
796
831
|
|
|
797
832
|
Place every build define after `--build-exe`, or after the target argument of
|
package/package.json
CHANGED
package/runmd.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import path from 'node:path'
|
|
4
4
|
import { pathToFileURL } from 'node:url'
|
|
5
|
-
import { readInternalAssetText } from './
|
|
5
|
+
import { readInternalAssetText } from './single-exe/assetsHelper.js'
|
|
6
6
|
import { fenceEventMap } from './src/cui/fence-events.mjs'
|
|
7
7
|
import { REPO_ROOT } from './single-exe/compiled.js'
|
|
8
8
|
|
|
@@ -470,7 +470,8 @@ ${moduleScript}
|
|
|
470
470
|
md += `\n${moduleScript}\n`;
|
|
471
471
|
}
|
|
472
472
|
}
|
|
473
|
-
|
|
473
|
+
|
|
474
|
+
md = annotateWuiImageSources(md)
|
|
474
475
|
|
|
475
476
|
await Bun.write(mdpath+".html",md)
|
|
476
477
|
logWroteFile("html", mdpath+".html")
|
|
@@ -478,6 +479,17 @@ ${moduleScript}
|
|
|
478
479
|
return md;
|
|
479
480
|
}
|
|
480
481
|
|
|
482
|
+
function annotateWuiImageSources(html)
|
|
483
|
+
{
|
|
484
|
+
return String(html).replace(/<img\b[^>]*>/gi, tag => {
|
|
485
|
+
if (/\bdata-mdcui-src\s*=/i.test(tag)) return tag
|
|
486
|
+
const src = tag.match(/\bsrc\s*=\s*(["'])(.*?)\1/i)
|
|
487
|
+
if (!src) return tag
|
|
488
|
+
const attribute = ` data-mdcui-src=${src[1]}${src[2]}${src[1]}`
|
|
489
|
+
return tag.replace(/\s*\/?>$/, ending => `${attribute}${ending}`)
|
|
490
|
+
})
|
|
491
|
+
}
|
|
492
|
+
|
|
481
493
|
export function charFromPoint(tui,row,col)
|
|
482
494
|
{
|
|
483
495
|
tui = tui.split('\n')[row-1] || ""
|
package/runtime/help/help.md
CHANGED
|
@@ -153,6 +153,7 @@ bun src/index.js --wui testapp.md
|
|
|
153
153
|
| `bun src/index.js app.md` | Render `app.md` as a read-only terminal UI and write five generated files beside it. |
|
|
154
154
|
| `bun src/index.js --kitty app.md` | Display Markdown images with Kitty graphics and the jsgotty MIME extension. |
|
|
155
155
|
| `bun src/index.js --kitty-compat app.md` | Convert Markdown images to PNG with `Bun.Image` and display them using the standard Kitty graphics protocol without the non-standard MIME `U` field. |
|
|
156
|
+
| `JSMDCUI_KITTY_MODE=compat bun src/index.js app.md` | Enable `compat` or `extended` Kitty mode without passing a Kitty command-line flag; the default and invalid values are `off`. |
|
|
156
157
|
| `bun src/index.js --kitty --allow-url URL.md` | Download trusted HTTP(S) Markdown and its HTTP(S) images, then display supported images with Kitty graphics. |
|
|
157
158
|
| `JSMDCUI_KITTY_DEBUG=1 bun src/index.js --kitty app.md` | Enable Kitty image placement logging to `kitty-placement.log`. |
|
|
158
159
|
| `bun src/index.js --check app.md` | Check heading and fenced-block IDs for collisions, print line-by-line details, and exit. |
|
|
@@ -791,7 +792,7 @@ npx jsmdcui --build-md-for bun-linux-x64-v1.3.14 myapp.md
|
|
|
791
792
|
```
|
|
792
793
|
|
|
793
794
|
This similarly expands to
|
|
794
|
-
`--build-for bun-linux-x64 --define global.MDCUI_MAIN=myapp.md`. Both aliases
|
|
795
|
+
`--build-for bun-linux-x64-v1.3.14 --define global.MDCUI_MAIN=myapp.md`. Both aliases
|
|
795
796
|
are expanded before any generated application files are written.
|
|
796
797
|
|
|
797
798
|
Place every build define after `--build-exe`, or after the target argument of
|
package/single-exe/README.md
CHANGED
|
@@ -44,8 +44,15 @@ need quotes. `stringifyNonPrimitiveDefineValues()` converts a bare value with
|
|
|
44
44
|
`JSON.stringify()` before it reaches Bun. jsmdcui uses it for
|
|
45
45
|
`global.MDCUI_MAIN`.
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
`global.MDCUI_MAIN` is a jsmdcui application convention, not a name reserved by
|
|
48
|
+
the single-executable helpers. An adopting project can use
|
|
49
|
+
`global.MY_EMBEDDED_APP`, or any other build expression, as long as it updates
|
|
50
|
+
its own call sites and passes the same name to
|
|
51
|
+
`stringifyNonPrimitiveDefineValues()`.
|
|
52
|
+
|
|
53
|
+
The jsmdcui switch defines below are presence-based: `=0` and `=false` still
|
|
54
|
+
enable them. Omit a switch define to disable it; examples consistently use
|
|
55
|
+
`=1`.
|
|
49
56
|
|
|
50
57
|
- `MDCUI_DEFAULT_EDIT=1`: editable-text default.
|
|
51
58
|
- `MDCUI_DEFAULT_DEMO=1`: no-argument `testapp.md` TUI.
|
|
@@ -209,6 +216,111 @@ a resource should be returned as bytes instead of decoded text.
|
|
|
209
216
|
Use `listInternalAssetPaths()` to list embedded asset paths and
|
|
210
217
|
`listInternalAssetDirs()` to list embedded directories.
|
|
211
218
|
|
|
219
|
+
#### Reuse images from a compiled HTML bundle
|
|
220
|
+
|
|
221
|
+
Bun exposes an imported HTML entry as a `homepage` object in a compiled
|
|
222
|
+
executable. Its `index` points to the compiled HTML and its `files` array lists
|
|
223
|
+
the content-hashed assets under Bun's compiled virtual filesystem: paths
|
|
224
|
+
normally begin with `/$bunfs/root/` on POSIX systems and `B:/~BUN/` on
|
|
225
|
+
Windows. The helpers can turn image references preserved in that HTML into a
|
|
226
|
+
lazy path map without copying the image bytes into `internalAssets`. Always use
|
|
227
|
+
the path returned by `homepage.files`; do not hard-code either platform prefix.
|
|
228
|
+
|
|
229
|
+
Before compiling, preserve each original image reference in a custom
|
|
230
|
+
attribute. Its name belongs to the adopting project; it is not fixed by the
|
|
231
|
+
helper. This example chooses `data-original-image`. Bun rewrites `src` but
|
|
232
|
+
leaves the custom attribute intact:
|
|
233
|
+
|
|
234
|
+
```html
|
|
235
|
+
<!-- Input -->
|
|
236
|
+
<img src="./images/photo.jpg" data-original-image="./images/photo.jpg">
|
|
237
|
+
|
|
238
|
+
<!-- Compiled HTML -->
|
|
239
|
+
<img src="/photo-abcd1234.jpg" data-original-image="./images/photo.jpg">
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Export the imported HTML bundle from a module that both the web server and
|
|
243
|
+
terminal runtime can load:
|
|
244
|
+
|
|
245
|
+
```js
|
|
246
|
+
import homepage from "./index.html";
|
|
247
|
+
|
|
248
|
+
export { homepage };
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Build and cache the map only in a compiled executable. Guard the import first:
|
|
252
|
+
a source-tree launch does not necessarily have the generated server or HTML
|
|
253
|
+
module.
|
|
254
|
+
|
|
255
|
+
```js
|
|
256
|
+
import {
|
|
257
|
+
buildHtmlBundleImageMap,
|
|
258
|
+
canonicalHtmlBundleImageHref,
|
|
259
|
+
} from "../single-exe/assetsHelper.js";
|
|
260
|
+
import { IS_COMPILED } from "../single-exe/compiled.js";
|
|
261
|
+
|
|
262
|
+
let imageMapPromise = null;
|
|
263
|
+
const imageSourceAttribute = "data-original-image";
|
|
264
|
+
|
|
265
|
+
function getBundledImageMap() {
|
|
266
|
+
if (!IS_COMPILED || !global.MY_EMBEDDED_APP) return null;
|
|
267
|
+
|
|
268
|
+
imageMapPromise ??= import(global.MY_EMBEDDED_APP + "-server.js")
|
|
269
|
+
.then(module => buildHtmlBundleImageMap(
|
|
270
|
+
module.homepage,
|
|
271
|
+
imageSourceAttribute,
|
|
272
|
+
));
|
|
273
|
+
|
|
274
|
+
return imageMapPromise;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const images = await getBundledImageMap();
|
|
278
|
+
const key = canonicalHtmlBundleImageHref(originalImageHref);
|
|
279
|
+
const bundledPath = images?.get(key);
|
|
280
|
+
const bytes = bundledPath ? await Bun.file(bundledPath).bytes() : null;
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
`global.MY_EMBEDDED_APP` is only an example build-time module selector. Name it
|
|
284
|
+
to match the adopting application, replace it with a direct static import, or
|
|
285
|
+
use another compiled-only condition. The helper does not inspect or require
|
|
286
|
+
`global.MDCUI_MAIN`; that is jsmdcui's own convention. When using a Bun
|
|
287
|
+
`--define`, use the same expression at the call site, for example
|
|
288
|
+
`--define global.MY_EMBEDDED_APP=...`.
|
|
289
|
+
|
|
290
|
+
`buildHtmlBundleImageMap(homepage, sourceAttribute)` reads the compiled HTML
|
|
291
|
+
once and returns a `Map<string, string>` from canonical original image
|
|
292
|
+
references to Bun compiled virtual paths on either platform. The second
|
|
293
|
+
argument is the custom attribute name. It defaults to `"data-mdcui-src"` only
|
|
294
|
+
for jsmdcui's convenience:
|
|
295
|
+
|
|
296
|
+
```js
|
|
297
|
+
const images = await buildHtmlBundleImageMap(homepage);
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Adopting projects can choose any valid HTML attribute name and pass it
|
|
301
|
+
explicitly:
|
|
302
|
+
|
|
303
|
+
```html
|
|
304
|
+
<img src="./photo.jpg" data-original-image="./photo.jpg">
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
```js
|
|
308
|
+
const images = await buildHtmlBundleImageMap(
|
|
309
|
+
homepage,
|
|
310
|
+
"data-original-image",
|
|
311
|
+
);
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
The helper decodes HTML attribute entities and percent encoding so the HTML
|
|
315
|
+
reference can match the equivalent original caller reference. Image bytes
|
|
316
|
+
remain lazy and are read only when the caller uses the returned path.
|
|
317
|
+
|
|
318
|
+
For lower-level lookup, `findHtmlBundleAsset(homepage, publicPath, options)`
|
|
319
|
+
matches a rewritten public path such as `/photo-abcd1234.jpg` to its
|
|
320
|
+
`homepage.files` entry. `findHtmlBundleImageAsset()` restricts the lookup to
|
|
321
|
+
`image/*`, and `htmlBundleImageAssetPath()` returns only the corresponding
|
|
322
|
+
Bun compiled virtual path.
|
|
323
|
+
|
|
212
324
|
### 5. Add the optional build commands to the regular CLI
|
|
213
325
|
|
|
214
326
|
Before normal argument parsing, call `buildEarlyExit` from the regular main
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { posix } from "node:path";
|
|
2
|
+
|
|
1
3
|
const textDecoder = new TextDecoder();
|
|
2
4
|
const textEncoder = new TextEncoder();
|
|
3
5
|
|
|
@@ -12,6 +14,87 @@ export function assetPath(...parts) {
|
|
|
12
14
|
.join("/");
|
|
13
15
|
}
|
|
14
16
|
|
|
17
|
+
export function decodeHtmlAttribute(value) {
|
|
18
|
+
return String(value ?? "").replace(
|
|
19
|
+
/&(?:amp|quot|apos|lt|gt|#39|#x27|#\d+|#x[\da-f]+);/gi,
|
|
20
|
+
(entity) => {
|
|
21
|
+
const named = {
|
|
22
|
+
"&": "&",
|
|
23
|
+
""": "\"",
|
|
24
|
+
"'": "'",
|
|
25
|
+
"<": "<",
|
|
26
|
+
">": ">",
|
|
27
|
+
"'": "'",
|
|
28
|
+
"'": "'",
|
|
29
|
+
}[entity.toLowerCase()];
|
|
30
|
+
if (named != null) return named;
|
|
31
|
+
const hex = /^&#x([\da-f]+);$/i.exec(entity);
|
|
32
|
+
const decimal = /^&#(\d+);$/.exec(entity);
|
|
33
|
+
const codePoint = Number.parseInt(hex?.[1] ?? decimal?.[1] ?? "", hex ? 16 : 10);
|
|
34
|
+
try {
|
|
35
|
+
return Number.isInteger(codePoint) ? String.fromCodePoint(codePoint) : entity;
|
|
36
|
+
} catch {
|
|
37
|
+
return entity;
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function canonicalHtmlBundleImageHref(input, { htmlAttribute = false } = {}) {
|
|
44
|
+
let value = String(input ?? "");
|
|
45
|
+
if (htmlAttribute) value = decodeHtmlAttribute(value);
|
|
46
|
+
try {
|
|
47
|
+
return decodeURIComponent(value);
|
|
48
|
+
} catch {
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function findHtmlBundleAsset(homepage, publicPath, {
|
|
54
|
+
contentTypePrefix = "",
|
|
55
|
+
} = {}) {
|
|
56
|
+
if (!Array.isArray(homepage?.files)) return null;
|
|
57
|
+
const name = posix.basename(String(publicPath ?? "").replace(/[?#].*$/, ""));
|
|
58
|
+
for (const file of homepage.files) {
|
|
59
|
+
if (file?.loader !== "file") continue;
|
|
60
|
+
const contentType = String(file.headers?.["content-type"] ?? "");
|
|
61
|
+
if (contentTypePrefix && !contentType.startsWith(contentTypePrefix)) continue;
|
|
62
|
+
if (posix.basename(String(file.path ?? "")) === name) return file;
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function findHtmlBundleImageAsset(homepage, publicPath) {
|
|
68
|
+
return findHtmlBundleAsset(homepage, publicPath, {
|
|
69
|
+
contentTypePrefix: "image/",
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function htmlBundleImageAssetPath(homepage, publicPath) {
|
|
74
|
+
return findHtmlBundleImageAsset(homepage, publicPath)?.path ?? null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export async function buildHtmlBundleImageMap(
|
|
78
|
+
homepage,
|
|
79
|
+
sourceAttribute = "data-mdcui-src",
|
|
80
|
+
) {
|
|
81
|
+
const images = new Map();
|
|
82
|
+
if (!homepage?.index || !Array.isArray(homepage.files)) return images;
|
|
83
|
+
const html = await Bun.file(homepage.index).text();
|
|
84
|
+
for (const tag of html.match(/<img\b[^>]*>/gi) ?? []) {
|
|
85
|
+
const rawHref = htmlTagAttribute(tag, sourceAttribute);
|
|
86
|
+
const publicPath = htmlTagAttribute(tag, "src");
|
|
87
|
+
if (rawHref == null || publicPath == null) continue;
|
|
88
|
+
const bundledPath = htmlBundleImageAssetPath(homepage, decodeHtmlAttribute(publicPath));
|
|
89
|
+
if (!bundledPath) continue;
|
|
90
|
+
images.set(
|
|
91
|
+
canonicalHtmlBundleImageHref(rawHref, { htmlAttribute: true }),
|
|
92
|
+
bundledPath,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
return images;
|
|
96
|
+
}
|
|
97
|
+
|
|
15
98
|
export function listInternalAssetPaths(prefix = "") {
|
|
16
99
|
const store = getAssetStore();
|
|
17
100
|
if (!store) return [];
|
|
@@ -88,3 +171,9 @@ function iterateAssetKeys(store) {
|
|
|
88
171
|
if (store instanceof Map) return [...store.keys()].map(String);
|
|
89
172
|
return Object.keys(store);
|
|
90
173
|
}
|
|
174
|
+
|
|
175
|
+
function htmlTagAttribute(tag, name) {
|
|
176
|
+
const escapedName = String(name).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
177
|
+
const match = String(tag).match(new RegExp(`\\b${escapedName}\\s*=\\s*([\"'])(.*?)\\1`, "i"));
|
|
178
|
+
return match?.[2] ?? null;
|
|
179
|
+
}
|
package/src/cui/kitty-images.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import { dirname, resolve } from "node:path";
|
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
3
|
import { logKittyPlacement } from "./kitty-debug.mjs";
|
|
4
4
|
import { fetchHttpBytes as defaultFetchHttpBytes } from "../platform/commands.js";
|
|
5
|
+
import { canonicalHtmlBundleImageHref } from "../../single-exe/assetsHelper.js";
|
|
5
6
|
|
|
6
7
|
const PNG = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
|
|
7
8
|
|
|
@@ -63,8 +64,8 @@ function imageSize(buffer) {
|
|
|
63
64
|
}
|
|
64
65
|
const svg = buffer.subarray(0, Math.min(buffer.length, 8192)).toString("utf8");
|
|
65
66
|
if (svg.includes("<svg")) {
|
|
66
|
-
const width = svg.match(/\bwidth=["']([0-9.]+)(?:px)?["']/i);
|
|
67
|
-
const height = svg.match(/\bheight=["']([0-9.]+)(?:px)?["']/i);
|
|
67
|
+
const width = svg.match(/\bwidth=["']([0-9.]+)(?:px)?["']/i); //'
|
|
68
|
+
const height = svg.match(/\bheight=["']([0-9.]+)(?:px)?["']/i); //'
|
|
68
69
|
if (width && height) {
|
|
69
70
|
return { mime: "image/svg+xml", width: Math.round(Number(width[1])), height: Math.round(Number(height[1])) };
|
|
70
71
|
}
|
|
@@ -136,11 +137,17 @@ export async function prepareKittyImages(ansiText, markdownPath, terminalCols =
|
|
|
136
137
|
const images = [];
|
|
137
138
|
const maxCols = Math.max(1, Math.trunc(Number(terminalCols) || 80));
|
|
138
139
|
const oscImage = /\x1b\]8;;([^\x1b]*)\x1b\\(?=[^\n]*📷)/;
|
|
140
|
+
const bundledImages = await options.getBundledImageMap?.() ?? null;
|
|
139
141
|
|
|
140
142
|
for (let sourceLine = 0; sourceLine < inputLines.length; sourceLine++) {
|
|
141
143
|
const line = inputLines[sourceLine];
|
|
142
144
|
const match = line.match(oscImage);
|
|
143
|
-
const
|
|
145
|
+
const bundledPath = match
|
|
146
|
+
? bundledImages?.get(canonicalHtmlBundleImageHref(match[1]))
|
|
147
|
+
: null;
|
|
148
|
+
const source = bundledPath
|
|
149
|
+
? { kind: "bundled", value: bundledPath }
|
|
150
|
+
: match ? imageSource(match[1], markdownPath, options.allowUrl === true) : null;
|
|
144
151
|
if (!source) {
|
|
145
152
|
outputLines.push(line);
|
|
146
153
|
continue;
|
package/src/cui/server.mjs
CHANGED
package/src/index.js
CHANGED
|
@@ -94,17 +94,17 @@ import { accessSync, closeSync, constants, existsSync, openSync, readSync, readd
|
|
|
94
94
|
import { mkdir } from "node:fs/promises";
|
|
95
95
|
import { dirname, basename, join, resolve, sep } from "node:path";
|
|
96
96
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
97
|
-
import process from "node:process";
|
|
98
97
|
import { toggleTaskCheckboxBeforeColumn, updateAnsiTaskCheckbox } from "./cui/task-checkbox.mjs";
|
|
99
98
|
import { fenceEventMap, inlineFenceEventCode } from "./cui/fence-events.mjs";
|
|
100
99
|
import { checkMarkdownIdCollisions, formatMarkdownIdCheckAnsi } from "./cui/id-collision.mjs";
|
|
101
100
|
import { fitKittyImageToWidth, prepareKittyImages } from "./cui/kitty-images.mjs";
|
|
101
|
+
import { kittyModeFromEnvironment } from "./cui/kitty-mode.mjs";
|
|
102
102
|
import { logKittyPlacement } from "./cui/kitty-debug.mjs";
|
|
103
103
|
import { Config } from "./config/config.js";
|
|
104
104
|
import { defaultAllSettings, OPTION_CHOICES, LOCAL_SETTINGS } from "./config/defaults.js";
|
|
105
105
|
import { cleanConfig } from "./config/clean.js";
|
|
106
106
|
import { RuntimeRegistry, RTColorscheme, RTHelp } from "./runtime/registry.js";
|
|
107
|
-
import { assetPath, hasInternalAssets, listInternalAssetDirs, listInternalAssetPaths, readInternalAssetText } from "
|
|
107
|
+
import { assetPath, buildHtmlBundleImageMap, hasInternalAssets, listInternalAssetDirs, listInternalAssetPaths, readInternalAssetText } from "../single-exe/assetsHelper.js";
|
|
108
108
|
//import { PluginManager } from "./plugins/manager.js";
|
|
109
109
|
import { JsPluginManager, buildMicroGlobal, buildTuiBlockIndex, findTuiBlockInIndex, insertTuiTextareaNewline, mergeTuiTextareaBackward, mergeTuiTextareaForward, runAction, listActions } from "./plugins/js-bridge.js";
|
|
110
110
|
import { Colorscheme } from "./config/colorscheme.js";
|
|
@@ -124,6 +124,7 @@ import { writeBackup, removeBackup, applyBackup } from "./buffer/backup.js";
|
|
|
124
124
|
let kittyImageMode = "off";
|
|
125
125
|
let allowRemoteKittyImages = false;
|
|
126
126
|
const remoteMarkdownSources = new Map();
|
|
127
|
+
let bundledImageMapPromise = null;
|
|
127
128
|
import { isHex3Encoding, isMdcuiEncoding } from "./runtime/encodings.js";
|
|
128
129
|
import { createInterface } from "node:readline/promises";
|
|
129
130
|
|
|
@@ -135,6 +136,13 @@ import { expandBuildMdAliases } from "./build-args.js";
|
|
|
135
136
|
const __filename = fileURLToPath(import.meta.url);
|
|
136
137
|
const __dirname = dirname(__filename);
|
|
137
138
|
|
|
139
|
+
function getBundledImageMap() {
|
|
140
|
+
if (!IS_COMPILED || !global.MDCUI_MAIN) return null;
|
|
141
|
+
bundledImageMapPromise ??= import(global.MDCUI_MAIN + "-server.js")
|
|
142
|
+
.then((module) => buildHtmlBundleImageMap(module.homepage));
|
|
143
|
+
return bundledImageMapPromise;
|
|
144
|
+
}
|
|
145
|
+
|
|
138
146
|
|
|
139
147
|
if(!globalThis.Bun)
|
|
140
148
|
{
|
|
@@ -380,6 +388,7 @@ async function renderMdcui(markdown, width = process.stdout.columns || 80, mdpat
|
|
|
380
388
|
: await prepareKittyImages(tui, resolvedImageBasePath, width, {
|
|
381
389
|
allowUrl: allowRemoteKittyImages,
|
|
382
390
|
kittyMode: kittyImageMode,
|
|
391
|
+
getBundledImageMap,
|
|
383
392
|
});
|
|
384
393
|
return {
|
|
385
394
|
rendered: prepared.rendered,
|
|
@@ -1119,7 +1128,7 @@ function parseArgs(argv) {
|
|
|
1119
1128
|
cdpMaze: false,
|
|
1120
1129
|
cdpPort: 0,
|
|
1121
1130
|
cdpAddress: "",
|
|
1122
|
-
kittyMode:
|
|
1131
|
+
kittyMode: kittyModeFromEnvironment(process.env.JSMDCUI_KITTY_MODE),
|
|
1123
1132
|
settings: new Map(),
|
|
1124
1133
|
};
|
|
1125
1134
|
const files = [];
|
|
@@ -1269,6 +1278,9 @@ Modes:
|
|
|
1269
1278
|
Display Markdown images with Kitty graphics and the jsgotty MIME extension
|
|
1270
1279
|
--kitty-compat
|
|
1271
1280
|
Display Markdown images with Kitty graphics without the non-standard MIME U field
|
|
1281
|
+
|
|
1282
|
+
JSMDCUI_KITTY_MODE=off|compat|extended
|
|
1283
|
+
Set the Kitty image mode without command-line flags; explicit Kitty flags override it
|
|
1272
1284
|
|
|
1273
1285
|
--edit
|
|
1274
1286
|
Open files as editable UTF-8 text
|
|
@@ -8265,6 +8277,7 @@ async function main() {
|
|
|
8265
8277
|
addCheckpoint("Argument Parsing");
|
|
8266
8278
|
|
|
8267
8279
|
expandBuildMdAliases(process.argv);
|
|
8280
|
+
stringifyNonPrimitiveDefineValues(process.argv, "process.env.JSMDCUI_KITTY_MODE");
|
|
8268
8281
|
stringifyNonPrimitiveDefineValues(process.argv, "global.MDCUI_MAIN");
|
|
8269
8282
|
const argvMdcuiMain = stringDefineFromArgv(
|
|
8270
8283
|
process.argv,
|
package/src/lua/engine.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
-
import { readInternalAssetBytes } from "
|
|
2
|
+
import { readInternalAssetBytes } from "../../single-exe/assetsHelper.js";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { REPO_ROOT } from "../../single-exe/compiled.js";
|
package/src/plugins/js-bridge.js
CHANGED
|
@@ -3,7 +3,7 @@ import { readdir } from "node:fs/promises";
|
|
|
3
3
|
import { dirname, join, basename, extname } from "node:path";
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
|
-
import { assetPath, hasInternalAssets, listInternalAssetDirs, listInternalAssetPaths, readInternalAssetBytes } from "
|
|
6
|
+
import { assetPath, hasInternalAssets, listInternalAssetDirs, listInternalAssetPaths, readInternalAssetBytes } from "../../single-exe/assetsHelper.js";
|
|
7
7
|
import { isMdcuiEncoding } from "../runtime/encodings.js";
|
|
8
8
|
import { newMessage, newMessageAtLine, MTError, MTWarning, MTInfo } from "../buffer/message.js";
|
|
9
9
|
import { Loc } from "../buffer/loc.js";
|
package/src/plugins/manager.js
CHANGED
|
@@ -4,7 +4,7 @@ import { basename, dirname, extname, join, sep } from "node:path";
|
|
|
4
4
|
import { fetchHttp, downloadFile } from "../platform/commands.js";
|
|
5
5
|
import { extractAndStrip } from "../platform/archive.js";
|
|
6
6
|
import { createLuaEngine } from "../lua/engine.js";
|
|
7
|
-
import { assetPath, hasInternalAssets, internalAssetSource, listInternalAssetDirs, listInternalAssetPaths, readInternalAssetText } from "
|
|
7
|
+
import { assetPath, hasInternalAssets, internalAssetSource, listInternalAssetDirs, listInternalAssetPaths, readInternalAssetText } from "../../single-exe/assetsHelper.js";
|
|
8
8
|
import { execCommand, runBackgroundShell, runCommand } from "../shell/shell.js";
|
|
9
9
|
import { Loc } from "../buffer/loc.js";
|
|
10
10
|
import { BTDefault, BTHelp, BTInfo, BTLog, BTRaw, BTScratch, byteOffset, BufferCore } from "../buffer/buffer.js";
|
package/src/runtime/registry.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { readdir, readFile } from "node:fs/promises";
|
|
3
3
|
import { basename, extname, join } from "node:path";
|
|
4
|
-
import { assetPath, hasInternalAssets, listInternalAssetPaths, readInternalAssetBytes } from "
|
|
4
|
+
import { assetPath, hasInternalAssets, listInternalAssetPaths, readInternalAssetBytes } from "../../single-exe/assetsHelper.js";
|
|
5
5
|
|
|
6
6
|
export const RTColorscheme = 0;
|
|
7
7
|
export const RTSyntax = 1;
|