jsmdcui 0.6.3 → 0.7.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 CHANGED
@@ -2,6 +2,66 @@
2
2
 
3
3
  All notable user-visible changes to jsmdcui are documented here.
4
4
 
5
+ ## [0.7.0] - 2026-07-20
6
+
7
+ This update adds native Kitty image rendering, turns heading task lists into
8
+ Array-style collections in both interfaces, and keeps TUI layout, interaction,
9
+ and checkbox styling consistent at every terminal width.
10
+
11
+ ### Added
12
+
13
+ - Display local Markdown images at their rendered TUI positions with the
14
+ Kitty graphics protocol. Image rows are reserved according to intrinsic
15
+ dimensions, and placements follow scrolling, resizing, and split panes.
16
+ - With `--allow-url` and Kitty mode enabled, download HTTP(S) Markdown images
17
+ through the existing `curl`, `wget`, then Bun `fetch` fallback chain. Relative
18
+ image URLs in downloaded Markdown resolve against the original document URL.
19
+ - Keep Bun's normal linked `📷` fallback for missing, unsupported, and
20
+ unauthorized remote images, and scope Kitty cleanup to image IDs owned by
21
+ jsmdcui.
22
+ - Set Kitty's `C=1` placement flag so displaying an image cannot advance the
23
+ terminal cursor and trigger an unwanted scroll at the bottom of the screen.
24
+ - Add `.push()`, `.pop()`, `.shift()`, `.unshift()`, and `.splice()` to heading
25
+ selectors in both the TUI and WUI. The methods follow their
26
+ `Array.prototype` argument and return-value conventions, operate on direct
27
+ items in the first task list belonging to the heading, and remove nested
28
+ content together with its parent item.
29
+ - Allow inserted task items to be passed as strings for unchecked items or as
30
+ `{ value, checked }` objects, and preserve an emptied TUI list's insertion
31
+ point so later mutations still target the same list.
32
+ - Add read-only heading `.slice(start, end)`, returning fresh
33
+ `{ value, checked }` snapshots that include both checked and unchecked direct
34
+ task items.
35
+ - Add `demos/todo.md` and `demos/todo-zh.md`, runnable Todo examples that use
36
+ text controls and the new list methods to add and remove items and display
37
+ completed or pending tasks.
38
+ - Move secondary examples under `demos/` and add automatic
39
+ `--demo-<filename>` discovery, so newly bundled `demos/<filename>.md` files
40
+ need no parser changes. Keep `--demo` mapped to the root `testapp.md` and
41
+ retain the existing image-processor aliases.
42
+ - Add `--demo-list` to list the root demo and every automatically discovered
43
+ Markdown example from bundled assets or the source tree's `demos/` directory.
44
+ - Add `clean.sh` as a convenience helper for removing generated Markdown
45
+ companion files from the project directory.
46
+
47
+ ### Changed
48
+
49
+ - Document the Array-style heading task-list API, its return values, nested-item
50
+ behavior, existing-list requirement, and non-persistent rendered-state
51
+ semantics in the README and bundled help.
52
+ - Use the complete pane content width consistently for Markdown rendering,
53
+ soft wrapping, cursor and mouse mapping, and Kitty image sizing instead of
54
+ reserving the terminal's final column.
55
+
56
+ ### Fixed
57
+
58
+ - Fix soft wrapping at affected terminal widths shifting the visual cursor by
59
+ one row, which could activate the preceding Markdown action and leave the
60
+ final action unresponsive.
61
+ - Update the Bun Markdown ANSI glyph and style together when toggling a task
62
+ checkbox, so checked items become green and unchecked items no longer retain
63
+ the checked color.
64
+
5
65
  ## [0.6.3] - 2026-07-18
6
66
 
7
67
  This update enforces unique heading IDs at the Markdown source level and
package/README.md CHANGED
@@ -47,9 +47,10 @@ On other platforms, follow the [official Bun installation guide](https://bun.com
47
47
  Choose either of these two ways to run jsmdcui.
48
48
 
49
49
  > **Important:** Opening or rendering a local `.md` file writes or overwrites
50
- > 5 generated files beside it. Starting `--wui` without a file writes the
51
- > 5 files generated from `testapp.md` in the current directory. The source
52
- > Markdown is not changed, but you should run the demo in a directory where
50
+ > 5 generated files beside it.
51
+ > Starting `--wui` without a file or starting all the `--demo`s writes the
52
+ > 5 files generated from `testapp.md` in the current directory.
53
+ > The source Markdown is not changed, but you should run the demo in a directory where
53
54
  > overwriting generated files is safe.
54
55
 
55
56
  ### Route 1: Run with npx
@@ -72,9 +73,10 @@ npx jsmdcui
72
73
  npx jsmdcui --demo
73
74
  ```
74
75
 
76
+ List every bundled demo and its command-line option:
77
+
75
78
  ```sh
76
- # Heading and task-list selector demo
77
- npx jsmdcui --demo-select
79
+ npx jsmdcui --demo-list
78
80
  ```
79
81
 
80
82
  ```sh
@@ -121,22 +123,31 @@ bun src/index.js --wui testapp.md
121
123
 
122
124
  ### Usage table
123
125
 
124
- - The command table below uses the cloned-source form.
126
+ - The command table below assumes you're running from a cloned repository
125
127
  - If you use npx, replace `bun src/index.js` with `npx jsmdcui`
126
128
 
129
+ - I've also provided short aliases
130
+ * bun ./tui = bun src/index.js
131
+ * bun ./wui = bun src/index.js --wui
132
+
127
133
  | Command | Result |
128
134
  | --- | --- |
129
135
  | `bun src/index.js app.md` | Render `app.md` as a read-only terminal UI and write five generated files beside it. |
136
+ | `bun src/index.js --kitty app.md` | Display Markdown images with Kitty graphics and the jsgotty MIME extension. |
137
+ | `bun src/index.js --kitty-compat app.md` | Display Markdown images with Kitty graphics without the non-standard MIME `U` field. |
138
+ | `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. |
139
+ | `JSMDCUI_KITTY_DEBUG=1 bun src/index.js --kitty app.md` | Enable Kitty image placement logging to `kitty-placement.log`. |
130
140
  | `bun src/index.js --check app.md` | Check heading and fenced-block IDs for collisions, print line-by-line details, and exit. |
131
141
  | `bun src/index.js --edit app.md` | Open `app.md` as editable UTF-8 source, overriding automatic Markdown UI detection. |
132
142
  | `bun src/index.js --cat app.md` | Render the terminal version to stdout, write five generated files beside it, and exit. |
133
143
  | `bun src/index.js --testapp.md` | Write the bundled `testapp.md` source to stdout and exit. |
134
144
  | `bun src/index.js --export-readme` | Write or overwrite `./README.md` with the bundled README source and exit. |
145
+ | `bun src/index.js --demo-list` | List `testapp.md` and every bundled `demos/*.md` example with its command-line option, then exit. |
135
146
  | `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. |
136
- | `bun src/index.js --demo-select` | Use local `select.md` when present, otherwise write the bundled selector demo; open it in the terminal UI and write five generated files beside it. |
137
- | `bun src/index.js --demo-imgtool` | Use local `image-processor.md` when present, otherwise write the bundled Bun.Image processor; open it in the terminal UI and write five generated files beside it. |
138
- | `bun src/index.js --demo-imgtool-zh` | Use local `image-processor.zh-TW.md` when present, otherwise write the bundled Traditional Chinese Bun.Image processor; open it in the terminal UI and write five generated files beside it. |
139
- | `bun src/index.js --allow-url URL.md` | Download HTTP(S) Markdown to the current directory, write 5 generated files, and allow its embedded code to run. Only use trusted URLs. |
147
+ | `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. |
148
+ | `bun src/index.js --demo-imgtool` | Compatibility alias for `--demo-image-processor`. |
149
+ | `bun src/index.js --demo-imgtool-zh` | Compatibility alias for `--demo-image-processor.zh-TW`. |
150
+ | `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. |
140
151
  | `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. |
141
152
  | `bun src/index.js --wui app.md` | Write five generated files beside `app.md`, then print and serve a random URL. |
142
153
  | `PORT=8080 bun src/index.js --wui app.md` | Start the browser UI on another port. |
@@ -239,9 +250,10 @@ non-empty content is never removed.
239
250
 
240
251
  Headings can act as form-group selectors. jsmdcui uses the IDs generated by
241
252
  `Bun.markdown.html(..., { headings: { ids: true } })`; for example,
242
- `## Select Color` becomes `#select-color`. A heading selection reads the first
243
- task list directly following that heading and stops at the next heading. Nested
244
- task items are not included in the outer list's value.
253
+ `## Select Color` becomes `#select-color`. A heading selection reads direct
254
+ task items from the first list following that heading and stops at the next
255
+ heading. In the TUI, the first rendered `☐` or `☒` establishes that list and
256
+ its indentation. Nested task items are not included in the outer list's value.
245
257
 
246
258
  Heading IDs share the same selector namespace as all explicitly named fenced
247
259
  blocks, not only `text` and `textarea`, so avoid name collisions between them.
@@ -306,14 +318,65 @@ $('#select-color').val() // "Green"
306
318
  $('#features').val() // ["Search", "Offline mode"]
307
319
  ```
308
320
 
309
- The bundled `select.md` is a multilevel runnable example. Use `--demo-select`
321
+ The first direct task list belonging to a heading can also be changed with
322
+ Array-style methods. String arguments create unchecked items. Pass an object
323
+ to choose the initial checked state:
324
+
325
+ ```js
326
+ $('#features').push('Export')
327
+ $('#features').unshift({ value: 'Import', checked: true })
328
+ $('#features').splice(1, 2, 'Replacement')
329
+ $('#features').slice(0, 2)
330
+ $('#features').pop()
331
+ $('#features').shift()
332
+ ```
333
+
334
+ Like the corresponding `Array.prototype` methods, `.push(...items)` and
335
+ `.unshift(...items)` accept multiple items and return the new number of direct
336
+ items. `.pop()` and `.shift()` return the removed item's visible label, or
337
+ `undefined` when the list is empty. Nested task items are part of their parent
338
+ item: they are not counted separately, and are removed together with that
339
+ parent. These methods change the rendered TUI/WUI state; they do not rewrite
340
+ the source Markdown file. A heading must already have a task list before items
341
+ can be added.
342
+
343
+ `.splice(start, deleteCount, ...items)` follows `Array.prototype.splice()`:
344
+ negative indexes count from the end, omitting `deleteCount` removes through the
345
+ end, and the return value is an array containing the removed visible labels.
346
+
347
+ `.slice(start, end)` is read-only and follows `Array.prototype.slice()`. It
348
+ returns fresh item snapshots, including unchecked items, so changing the
349
+ returned array or its objects does not change the rendered list:
350
+
351
+ ```js
352
+ $('#features').slice()
353
+ // [
354
+ // { value: 'Search', checked: true },
355
+ // { value: 'Notifications', checked: false },
356
+ // ]
357
+ ```
358
+
359
+ `demos/todo.md` and `demos/todo-zh.md` are runnable Todo examples that
360
+ demonstrate `.push()`,
361
+ `.splice()`, `.slice()`, and `{ value, checked }` snapshots using editable text
362
+ controls instead of `prompt()` dialogs. Materialize one in the current
363
+ directory with its demo flag, or open the source-tree copy directly:
364
+
365
+ ```sh
366
+ bun src/index.js --demo-todo
367
+ bun src/index.js --demo-todo-zh
368
+ bun src/index.js demos/todo.md
369
+ bun src/index.js --wui demos/todo-zh.md
370
+ ```
371
+
372
+ The bundled `demos/select.md` is a multilevel runnable example. Use `--demo-select`
310
373
  to write it into the current directory when missing and open it in the TUI, or
311
374
  open it explicitly in either interface:
312
375
 
313
376
  ```sh
314
377
  bun src/index.js --demo-select
315
- bun src/index.js select.md
316
- bun src/index.js --wui select.md
378
+ bun src/index.js demos/select.md
379
+ bun src/index.js --wui demos/select.md
317
380
  ```
318
381
 
319
382
  The available selector methods are:
@@ -324,6 +387,12 @@ The available selector methods are:
324
387
  | `.val(value)` | Replace text-block contents and resize multiline values. | Set textarea/control values and resize textareas. |
325
388
  | `.html()` | Return a selected heading's inner HTML. | Return any successfully selected DOM element's `innerHTML`. |
326
389
  | `.line()` | Return a heading's current 1-based TUI row, or `0` if missing. | Not available. |
390
+ | `.push(...items)` | Append unchecked strings or `{ value, checked }` task items; return the new direct-item count. | Same. |
391
+ | `.pop()` | Remove and return the last direct task item's label, or `undefined`. | Same. |
392
+ | `.shift()` | Remove and return the first direct task item's label, or `undefined`. | Same. |
393
+ | `.unshift(...items)` | Prepend unchecked strings or `{ value, checked }` task items; return the new direct-item count. | Same. |
394
+ | `.splice(start, deleteCount, ...items)` | Remove and insert direct task items; return the removed labels as an array. | Same. |
395
+ | `.slice(start, end)` | Return `{ value, checked }` snapshots without changing the direct task items. | Same. |
327
396
 
328
397
  TUI heading rows are recalculated after text-block rows are added, removed, or
329
398
  replaced with multiline `.val(value)` content.
@@ -398,6 +467,19 @@ The terminal automatically reflows the Markdown when its width changes.
398
467
  Only `javascript:` links execute in the TUI; ordinary web links behave as
399
468
  normal links in the browser.
400
469
 
470
+ Local Markdown images are displayed automatically in terminals that support
471
+ the Kitty graphics protocol. Relative image paths are resolved from the
472
+ Markdown file's directory. jsmdcui reads the image dimensions, reserves the
473
+ corresponding terminal rows, and updates the placement when the document is
474
+ scrolled, resized, or shown in a split pane. Unsupported or missing images, as
475
+ well as remote images not authorized with `--allow-url`, retain Bun's normal
476
+ linked `📷` fallback. To download trusted remote Markdown and display its
477
+ supported HTTP(S) images with Kitty graphics, combine the options:
478
+
479
+ ```sh
480
+ bun src/index.js --kitty --allow-url https://example.com/app.md
481
+ ```
482
+
401
483
  ## Browser interaction
402
484
 
403
485
  The WUI uses normal browser mouse and keyboard behavior. Clicking a
@@ -431,6 +513,13 @@ app.md-server.js
431
513
  They are regenerated from `app.md`, so edit the Markdown source rather than the
432
514
  generated files. The source directory must be writable.
433
515
 
516
+ From the project directory, remove generated `*.md.*` and `*.md-*` companion
517
+ files while keeping the Markdown source files:
518
+
519
+ ```sh
520
+ bun ./clean.sh
521
+ ```
522
+
434
523
  ### Generated heading sections
435
524
 
436
525
  WUI output wraps each h1-h6 and its content in a hierarchical `<section>`.
package/clean.sh ADDED
@@ -0,0 +1,9 @@
1
+ #!/bin/sh
2
+
3
+ sd=$(dirname "$0")
4
+
5
+ rm "$sd"/*.md.* "$sd"/*.md-* 2>/dev/null
6
+ rm "$sd"/demos/*.md.* "$sd"/demos/*.md-* 2>/dev/null
7
+ rm "$sd"/tests/*.md.* "$sd"/tests/*.md-* 2>/dev/null
8
+
9
+ echo "Removed all *.md.* *.md-* from repo root and demos/ folder"
Binary file
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env jsmdcui
2
+
2
3
  # Bun.Image Processor
3
4
 
4
5
  Paste the path to a local image below (for example, `/home/me/photo.jpg`). The output file will be written next to the source as `original.resized.jpg` or `original.resized.png`; the source image will not be overwritten.
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env jsmdcui
2
+
2
3
  # Bun.Image Processor
3
4
 
4
5
  先把本機圖片路徑貼到下方(例如 `/home/me/photo.jpg`)。輸出檔會放在原圖旁邊,檔名為 `original.resized.jpg` 或 `original.resized.png`;原圖不會被覆寫。
@@ -1,3 +1,5 @@
1
+ #!/usr/bin/env jsmdcui
2
+
1
3
  # Select and Task List Demo
2
4
 
3
5
  Use `Enter` or `Space` to toggle a task item in the TUI, then activate these
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env jsmdcui
2
+
3
+ # Todo List
4
+
5
+ ## Todos
6
+
7
+ - [ ] canvas support
8
+ - [x] push/pop/splice/slice list items
9
+ - [x] image by --kitty
10
+ - [ ] clean up codebase
11
+
12
+ ## Actions
13
+
14
+ ```text#todo-input
15
+ Fly to the moon
16
+ ```
17
+
18
+ 在上方輸入 Todo 文字,再選擇新增或移除。移除會刪除第一個文字完全相同的項目。
19
+
20
+ - [新增 Todo](javascript:addTodo())
21
+ - [移除 Todo](javascript:removeTodo())
22
+ - [顯示已完成](javascript:showCompleted())
23
+ - [顯示未完成](javascript:showPending())
24
+
25
+ ## 操作結果:
26
+
27
+ ```text#todo-status
28
+ 尚未操作
29
+ ```
30
+
31
+ ## Filtered Result
32
+
33
+ ```text#todo-result
34
+ 點按「顯示已完成」或「顯示未完成」查看結果
35
+ ```
36
+
37
+ ```js front
38
+ function todoText() {
39
+ return $('#todo-input').val().trim();
40
+ }
41
+
42
+ function showItems(title, checked) {
43
+ const items = $('#todos')
44
+ .slice()
45
+ .filter(item => item.checked === checked);
46
+
47
+ const output = items.length
48
+ ? items.map(item => `${item.checked ? '✓' : '○'} ${item.value}`).join('\n')
49
+ : '(沒有項目)';
50
+ $('#todo-result').val(`${title}\n${output}`);
51
+ $('#todo-status').val(`找到 ${items.length} 個${title}項目`);
52
+ }
53
+
54
+ export function addTodo() {
55
+ const value = todoText();
56
+ if (!value) {
57
+ $('#todo-status').val('新增失敗:請先輸入 Todo 文字');
58
+ return;
59
+ }
60
+
61
+ const length = $('#todos').push(value);
62
+ $('#todo-input').val('');
63
+ $('#todo-status').val(`已新增「${value}」,目前共有 ${length} 個 Todo`);
64
+ }
65
+
66
+ export function removeTodo() {
67
+ const value = todoText();
68
+ if (!value) {
69
+ $('#todo-status').val('移除失敗:請輸入要移除的 Todo 文字');
70
+ return;
71
+ }
72
+
73
+ const items = $('#todos').slice();
74
+ const index = items.findIndex(item => item.value === value);
75
+ if (index < 0) {
76
+ $('#todo-status').val(`移除失敗:找不到「${value}」`);
77
+ return;
78
+ }
79
+
80
+ const removed = $('#todos').splice(index, 1);
81
+ $('#todo-input').val('');
82
+ $('#todo-status').val(`已移除「${removed[0]}」`);
83
+ }
84
+
85
+ export function showCompleted() {
86
+ showItems('已完成', true);
87
+ }
88
+
89
+ export function showPending() {
90
+ showItems('未完成', false);
91
+ }
92
+ ```
package/demos/todo.md ADDED
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env jsmdcui
2
+
3
+ # Todo List
4
+
5
+ ## Todos
6
+
7
+ - [ ] canvas support
8
+ - [x] push/pop/splice/slice list items
9
+ - [x] image by --kitty
10
+ - [ ] clean up codebase
11
+
12
+ ## Actions
13
+
14
+ ```text#todo-input
15
+ Fly to the moon
16
+ ```
17
+
18
+ Enter Todo text above, then choose Add or Remove. Remove deletes the first item whose text matches exactly.
19
+
20
+ - [Add Todo](javascript:addTodo())
21
+ - [Remove Todo](javascript:removeTodo())
22
+ - [Show Completed](javascript:showCompleted())
23
+ - [Show Pending](javascript:showPending())
24
+
25
+ ## Operation Result
26
+
27
+ ```text#todo-status
28
+ No action yet
29
+ ```
30
+
31
+ ## Filtered Result
32
+
33
+ ```text#todo-result
34
+ Select “Show Completed” or “Show Pending” to view matching items
35
+ ```
36
+
37
+ ```js front
38
+ function todoText() {
39
+ return $('#todo-input').val().trim();
40
+ }
41
+
42
+ function showItems(title, checked) {
43
+ const items = $('#todos')
44
+ .slice()
45
+ .filter(item => item.checked === checked);
46
+
47
+ const output = items.length
48
+ ? items.map(item => `${item.checked ? '✓' : '○'} ${item.value}`).join('\n')
49
+ : '(No items)';
50
+ const noun = items.length === 1 ? 'item' : 'items';
51
+ $('#todo-result').val(`${title}\n${output}`);
52
+ $('#todo-status').val(`Found ${items.length} ${title.toLowerCase()} ${noun}`);
53
+ }
54
+
55
+ export function addTodo() {
56
+ const value = todoText();
57
+ if (!value) {
58
+ $('#todo-status').val('Add failed: enter Todo text first');
59
+ return;
60
+ }
61
+
62
+ const length = $('#todos').push(value);
63
+ $('#todo-input').val('');
64
+ $('#todo-status').val(`Added “${value}”; ${length} Todos total`);
65
+ }
66
+
67
+ export function removeTodo() {
68
+ const value = todoText();
69
+ if (!value) {
70
+ $('#todo-status').val('Remove failed: enter the Todo text to remove');
71
+ return;
72
+ }
73
+
74
+ const items = $('#todos').slice();
75
+ const index = items.findIndex(item => item.value === value);
76
+ if (index < 0) {
77
+ $('#todo-status').val(`Remove failed: could not find “${value}”`);
78
+ return;
79
+ }
80
+
81
+ const removed = $('#todos').splice(index, 1);
82
+ $('#todo-input').val('');
83
+ $('#todo-status').val(`Removed “${removed[0]}”`);
84
+ }
85
+
86
+ export function showCompleted() {
87
+ showItems('Completed', true);
88
+ }
89
+
90
+ export function showPending() {
91
+ showItems('Pending', false);
92
+ }
93
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jsmdcui",
3
- "version": "0.6.3",
3
+ "version": "0.7.0",
4
4
  "description": "Markdown as a Common UI for Terminals and Web Browsers",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
package/runmd.mjs CHANGED
@@ -6,6 +6,7 @@ import { readInternalAssetText } from './src/runtime/assets.js'
6
6
  import { REPO_ROOT } from './single-exe/compiled.js'
7
7
 
8
8
  const csl=console.log
9
+ const cse=console.error
9
10
  const mda=Bun.markdown.ansi
10
11
  const mdh=Bun.markdown.html
11
12
  const jss=JSON.stringify
@@ -18,7 +19,7 @@ const TEST_COL=5
18
19
  function logWroteFile(label,path)
19
20
  {
20
21
  if(!process.stdin.isRaw)
21
- csl(mda(`- Wrote to ${label} file: ${path}`))
22
+ cse(mda(`- Wrote to ${label} file: ${path}`))
22
23
  }
23
24
 
24
25
  async function readTemplate(pathname)
@@ -62,32 +63,35 @@ export async function main(tuiWidth=30)
62
63
 
63
64
  // 3. Create Terminal UI
64
65
  let tui = createTui(md,tuiWidth)
65
- csl(mda("\n# TUI"))
66
- csl(tui)
67
- csl(mda('## TUI raw'))
68
- csl(jss(tui))
66
+ cse(mda("\n# TUI"))
67
+ cse(tui)
68
+ cse(mda('## TUI raw'))
69
+ cse(jss(tui))
69
70
 
70
71
 
71
72
  // 4. Create Web UI
72
73
  let wui = await createWui(md,mdpath)
73
- csl(mda('\n# HTML'))
74
- csl(wui)
74
+ cse(mda('\n# HTML'))
75
+ cse(wui)
75
76
 
76
77
 
78
+ /*
77
79
  // 5. Get character from point for TUI
78
80
  let ch = charFromPoint(tui,TEST_ROW,TEST_COL)
79
81
 
80
- csl(mda(
82
+ cse(mda(
81
83
  '# Slicing row,col: '+TEST_ROW+','+TEST_COL
82
84
  ))
83
85
 
84
- csl(jss(ch))
86
+ cse(jss(ch))
87
+
88
+ */
85
89
 
86
90
 
87
91
  const serverPath = mdpath + "-server.js"
88
92
  const svmod = await import(pathToFileURL(path.resolve(serverPath)).href)
89
93
 
90
- csl("\n\n"+mda('# Server'))
94
+ cse("\n\n"+mda('# Server'))
91
95
  svmod.main();
92
96
  }
93
97
 
@@ -336,14 +340,22 @@ export async function createWui(md,mdpath) // HTML
336
340
 
337
341
  const mdb = path.basename(mdpath);
338
342
 
343
+ const responsiveImageStyle = `<style>
344
+ img {
345
+ max-width: 100%;
346
+ height: auto;
347
+ }
348
+ </style>`;
339
349
  const moduleScript = `<scr`+`ipt type="module" src="./${mdb}.front.js"></scr`+`ipt>`;
340
- if (!/^\s*<!doctype html>/i.test(md)) {
350
+ const isFullHtmlDocument = /^\s*<!doctype html>/i.test(md);
351
+ if (!isFullHtmlDocument) {
341
352
  md = `<!doctype html>
342
353
  <html lang="zh-TW">
343
354
  <head>
344
355
  <meta charset="utf-8">
345
356
  <meta name="viewport" content="width=device-width, initial-scale=1">
346
357
  <title>${escapeHtmlAttribute(mdb)}</title>
358
+ ${responsiveImageStyle}
347
359
  </head>
348
360
  <body>
349
361
  ${md}
@@ -351,10 +363,17 @@ ${moduleScript}
351
363
  </body>
352
364
  </html>
353
365
  `;
354
- } else if (/<\/body\s*>/i.test(md)) {
355
- md = md.replace(/<\/body\s*>/i, `${moduleScript}\n</body>`);
356
366
  } else {
357
- md += `\n${moduleScript}\n`;
367
+ if (/<\/head\s*>/i.test(md)) {
368
+ md = md.replace(/<\/head\s*>/i, `${responsiveImageStyle}\n</head>`);
369
+ } else {
370
+ md = `${responsiveImageStyle}\n${md}`;
371
+ }
372
+ if (/<\/body\s*>/i.test(md)) {
373
+ md = md.replace(/<\/body\s*>/i, `${moduleScript}\n</body>`);
374
+ } else {
375
+ md += `\n${moduleScript}\n`;
376
+ }
358
377
  }
359
378
 
360
379