jsmdcui 0.4.0 → 0.6.3

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,231 @@
2
2
 
3
3
  All notable user-visible changes to jsmdcui are documented here.
4
4
 
5
+ ## [0.6.3] - 2026-07-18
6
+
7
+ This update enforces unique heading IDs at the Markdown source level and
8
+ clarifies the portable fenced-block identity contract shared by the TUI and
9
+ WUI.
10
+
11
+ ### Changed
12
+
13
+ - Treat duplicate Markdown headings that generate the same base ID as a
14
+ source-level collision. Later headings no longer pass `--check` merely
15
+ because Bun would silently assign generated suffixes such as `-1` or `-2`
16
+ that the Markdown author cannot see.
17
+ - Document that a selectable fenced-block declaration requires a tag and uses
18
+ the `tag#id.class` identity form. The officially supported portable control
19
+ tags are currently limited to `text` and `textarea` so the same Markdown
20
+ behaves consistently in both the TUI and WUI.
21
+ - Clarify that selector queries may omit the tag or classes after a valid
22
+ declaration.
23
+ - Expand checker and TUI selector regression coverage across tag/no-tag and
24
+ class/no-class combinations, heading-to-heading, heading-to-block, and
25
+ block-to-block ID collisions.
26
+
27
+ ### Fixed
28
+
29
+ - Fix duplicate source headings incorrectly passing the checker after Bun
30
+ automatically changed the generated ID of later headings.
31
+
32
+ ## [0.6.2] - 2026-07-18
33
+
34
+ This update makes Markdown ID-collision checks match the full TUI `$()` block
35
+ selector and gives check results clearer final status banners.
36
+
37
+ ### Changed
38
+
39
+ - Extend `--check FILE.md` from `text` and `textarea` controls to every
40
+ explicitly named fenced block accepted by the TUI `$()` selector, including
41
+ identities such as `hello#myid` and `json#config`.
42
+ - Report separate heading and fenced-block declaration counts, and describe
43
+ arbitrary block tags and their source lines in collision details.
44
+ - Replace the previous success label with a large green `PASSED` banner and
45
+ add a matching large red `FAILED` banner at the bottom of failed reports.
46
+ Both true-color foregrounds are generated with
47
+ `Bun.color(..., "ansi-16m")`, with standard ANSI fallbacks.
48
+ - Update the README and bundled help to clarify that generated heading IDs
49
+ share the selector namespace with all explicitly named fenced blocks, not
50
+ only editable text controls.
51
+
52
+ ### Fixed
53
+
54
+ - Fix `--check` overlooking collisions between a heading and a non-text
55
+ fenced block even though TUI `$().val()` could select both declarations.
56
+ - Add regression coverage for arbitrary fenced-block IDs and the colored
57
+ `PASSED` and `FAILED` terminal statuses.
58
+
59
+ ## [0.6.1] - 2026-07-18
60
+
61
+ This update adds Markdown UI ID-collision diagnostics and bundles a practical
62
+ Bun.Image processor demo in English and Traditional Chinese.
63
+
64
+ ### Added
65
+
66
+ - Add `--check FILE.md` to inspect Markdown heading and text-control IDs
67
+ without opening a UI or writing generated companion files. The report uses
68
+ `Bun.markdown.ansi()` headings and nested lists, identifies every collision
69
+ by ID, source type, line number, and original declaration, and exits with
70
+ status `0` for unique IDs, `1` for collisions, or `2` for usage and read
71
+ errors.
72
+ - Add `image-processor.md` and `image-processor.zh-TW.md`, runnable Bun.Image
73
+ tools for the TUI and WUI. They support image metadata inspection, resizing,
74
+ fit and resampling filters, enlargement prevention, EXIF auto-orientation,
75
+ rotation, vertical and horizontal mirroring, brightness and saturation
76
+ adjustment, and JPEG or PNG output options.
77
+ - Add detailed image-processing status fields. Successful writes show the
78
+ output path, dimensions, byte count, and all selected option values; failed
79
+ metadata reads and writes show the available error details and stack.
80
+ - Add `--demo-imgtool` and `--demo-imgtool-zh`. Each command preserves an
81
+ existing local processor Markdown file or writes its bundled language
82
+ version when missing, then opens it in the TUI.
83
+ - Bundle both image processor Markdown files in the single-executable asset
84
+ archive and add regression coverage for ID checks and both demo languages.
85
+
86
+ ### Changed
87
+
88
+ - Consolidate bundled demo Markdown loading behind one
89
+ `bundledMarkdownSource(filename)` implementation shared by `testapp.md`,
90
+ `select.md`, and both image processor variants.
91
+ - Document that heading selector values include the complete visible label.
92
+ Selection parsers should use deliberate substring, prefix, or token checks
93
+ when labels contain explanatory text instead of assuming exact identifiers.
94
+ - Document that generated heading IDs and explicitly named controls share the
95
+ same selector namespace and that every selectable ID should be unique.
96
+
97
+ ### Fixed
98
+
99
+ - Prevent explanatory text in image processor task-list labels from breaking
100
+ yes/no, fit, filter, and output-format selection parsing.
101
+ - Fix the English image processor status update silently targeting a heading
102
+ after `## Write Status` generated the same `#write-status` ID as its text
103
+ control.
104
+ - Catch image metadata and processing failures at both frontend and backend
105
+ boundaries so errors are displayed in the appropriate status field.
106
+
107
+ ## [0.6.0] - 2026-07-18
108
+
109
+ This update turns Markdown headings and task lists into cross-environment form
110
+ controls, improves the safety and consistency of the bundled demo workflow,
111
+ and adds a way to export the bundled README as a runnable Markdown app.
112
+
113
+ ### Added
114
+
115
+ - Add `--export-readme` to write or overwrite `./README.md` with the bundled
116
+ README source and exit.
117
+ - Add terminal task-checkbox activation. Activating a protected mdcui row at
118
+ or after its `☐` or `☒` toggles that checkbox and still allows the remaining
119
+ default cell callback to run. JavaScript links continue to take priority.
120
+ - Add hierarchical `<section>` wrappers to generated WUI HTML. Every h1-h6
121
+ opens a section; an equal or higher-level heading closes the applicable open
122
+ sections, while lower-level headings create nested sections. Content before
123
+ the first heading remains outside the generated sections.
124
+ - Add heading lookup to the TUI `$` API using the IDs generated by
125
+ `Bun.markdown.html(..., { headings: { ids: true } })`.
126
+ - Add `$('#heading-id').html()` in TUI to return a heading's inner HTML, and
127
+ `$('#heading-id').line()` to return its current 1-based terminal row or `0`
128
+ when it cannot be found. Line lookup follows text-block row additions,
129
+ removals, and multiline `.val(value)` replacements.
130
+ - Add `.html()` getters to WUI `$` selections. Any successfully selected DOM
131
+ element returns its `innerHTML`; missing or invalid selections return an
132
+ empty string.
133
+ - Add heading-based task-list values in both interfaces. `.val()` reads the
134
+ first task-list group directly following the selected heading, stops at the
135
+ next heading, and ignores nested task items. IDs beginning with `select`
136
+ return the first checked value or `null`; other IDs return all checked values
137
+ as an array or `[]`.
138
+ - Add `select.md`, a runnable multilevel heading and task-list example with
139
+ links that display single-select and multiple-select values through
140
+ `alert()`.
141
+ - Bundle `select.md` in packaged assets and add `--demo-select`. The command
142
+ preserves an existing `./select.md`, writes the bundled copy when missing,
143
+ opens it in the TUI, and generates its five companion files.
144
+
145
+ ### Changed
146
+
147
+ - Make `--demo` preserve an existing `./testapp.md`. The bundled source is now
148
+ written only when the file is missing, then opened in the terminal UI.
149
+ - Make `--wui` without a file follow the same source-file rule: use an existing
150
+ `./testapp.md`, or write the bundled demo there when missing, before creating
151
+ the generated companion files.
152
+ - Wrap generated WUI task-checkbox text in `<label>` so clicking the associated
153
+ text toggles the checkbox. Direct text and immediately following paragraph
154
+ elements are included; unrelated tags and nested lists remain outside.
155
+ - Extend the 0.5 `$().val()` contract: text and textarea controls retain their
156
+ string getter/setter behavior, while heading selections now return scalar,
157
+ array, or `null` task-list values according to the heading ID.
158
+ - Add a demo screenshot and expand the README and built-in help with clearer
159
+ TUI/WUI startup, demo preservation, and README export instructions.
160
+
161
+ ### Fixed
162
+
163
+ - Ensure WUI fallback to the bundled `testapp.md` materializes the Markdown
164
+ source in the current working directory instead of generating only its
165
+ companion files.
166
+ - Protect comments and `script`, `style`, `pre`, `code`, `textarea`, and
167
+ `template` regions while wrapping WUI headings, preventing heading-like HTML
168
+ strings inside those regions from changing the section hierarchy.
169
+ - Keep TUI heading lookup independent of ANSI heading text and duplicate
170
+ labels by mapping Bun-generated heading order and level to Bun's current
171
+ h1-h6 ANSI signatures.
172
+
173
+ ## [0.5.0] - 2026-07-16
174
+
175
+ This update adds cross-environment form-like text blocks and a small
176
+ jQuery-style selector API, while keeping the same Markdown source usable in
177
+ both the terminal and browser interfaces.
178
+
179
+ ### Added
180
+
181
+ - Add a minimal global `$` API to terminal frontend evaluation, JavaScript
182
+ plugins, generated browser frontend modules, and `javascript:` links.
183
+ - Support tag, ID, class, and combined selectors such as `$('text')`,
184
+ `$('#answer')`, `$('.field')`, and `$('text#answer.field')`.
185
+ - Add `.val()` and `.val(value)` for reading and replacing block contents.
186
+ Getters always return a string and use an empty string for missing elements,
187
+ invalid selectors, or lookup errors. Setters support chaining and resize
188
+ terminal blocks to fit multiline values.
189
+ - Recognize both raw Markdown fenced blocks and rendered Bun ANSI blocks,
190
+ including ASCII and Unicode frame characters.
191
+ - Add editable terminal `text` blocks. Content after the protected `│ ` or
192
+ `| ` prefix can be edited, while frame characters, line joins, inserted
193
+ newlines, and multiline pastes remain protected.
194
+ - Add interactive terminal text-block resizing. Activating the lower-left
195
+ frame corner adds an empty content row; activating the upper-left corner
196
+ removes only a trailing empty row and never deletes non-empty content.
197
+ - Add the optional frontend lifecycle callback
198
+ `onMdcuiExit({ reason, path, $ })`. The terminal awaits it before closing an
199
+ mdcui buffer and invokes it at most once per buffer.
200
+ - Add Markdown syntax completions for fenced block language identifiers.
201
+
202
+ ### Changed
203
+
204
+ - Convert `text` and `textarea` fenced blocks into native browser
205
+ `<textarea>` elements while preserving their ID, classes, original language
206
+ metadata, and selector compatibility.
207
+ - Prefer native `document.querySelector()` in the browser `$` implementation,
208
+ then fall back to mdcui metadata and the original `pre > code` structure.
209
+ - Automatically wrap and resize generated browser textareas on page load,
210
+ input, `.val(value)`, and window resize. Initial `rows` and `cols` are
211
+ derived from the Markdown content.
212
+ - Generate complete HTML5 documents for WUI output, including `<!doctype
213
+ html>`, UTF-8 metadata, a responsive viewport, a document title, and
214
+ `lang="zh-TW"`.
215
+ - Enable `softwrap` by default.
216
+ - Close modified mdcui buffers without displaying a save prompt. Applications
217
+ can use `onMdcuiExit` to collect edited field values or call backend RPC
218
+ functions before closing.
219
+ - Update the bundled example with editable text fields, selector API examples,
220
+ answer validation, and manual text-box resizing instructions.
221
+
222
+ ### Fixed
223
+
224
+ - Keep terminal text-block frame prefixes intact during editing and prevent
225
+ Delete at the end of a content row from merging it with the next row.
226
+ - Preserve browser textarea IDs and classes during Markdown-to-HTML
227
+ conversion, including compatibility metadata for original `text` selectors.
228
+ - Keep README and built-in help lifecycle documentation synchronized.
229
+
5
230
  ## [0.4.0] - 2026-07-16
6
231
 
7
232
  This release separates executable Markdown UI views from ordinary editable
package/README.md CHANGED
@@ -1,3 +1,20 @@
1
+ ![Demo](https://raw.githubusercontent.com/jjtseng93/jsmdcui/main/demo.jpg)
2
+
3
+ # Demo App
4
+ ## My app
5
+
6
+ - [Say hello](javascript:sayHello())
7
+ - [Get server time](javascript:showServerTime())
8
+ - [Update text box](javascript:updateText())
9
+ - [Show selected](javascript:alert(JSON.stringify($('#my-app').val())))
10
+ - [x] task1
11
+ - [x] task2
12
+ - [ ] unselected task
13
+
14
+ ```text#myid.myclass
15
+ Editable in both TUI and WUI
16
+ ```
17
+
1
18
  # Introduction
2
19
  - jsmdcui stands for:
3
20
  - JavaScript Markdown Cross-environment User Interface
@@ -10,6 +27,12 @@
10
27
  - Original projects:
11
28
  - https://github.com/jjtseng93/bunmicro
12
29
  - https://github.com/micro-editor/micro
30
+ - .
31
+ - This README is itself a runnable app.
32
+
33
+ ```sh
34
+ npx jsmdcui@latest README.md
35
+ ```
13
36
 
14
37
  ## Quick start
15
38
 
@@ -24,28 +47,52 @@ On other platforms, follow the [official Bun installation guide](https://bun.com
24
47
  Choose either of these two ways to run jsmdcui.
25
48
 
26
49
  > **Important:** Opening or rendering a local `.md` file writes or overwrites
27
- > five generated files beside it. Starting `--wui` without a file writes the
28
- > five files generated from `testapp.md` in the current directory. The source
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
29
52
  > Markdown is not changed, but you should run the demo in a directory where
30
53
  > overwriting generated files is safe.
31
54
 
32
55
  ### Route 1: Run with npx
33
56
 
34
- This route requires both `npx` and `bun` on your `PATH`; no source checkout is
35
- required. Open the normal terminal editor:
57
+ - This route requires both `npx` and `bun` on your `PATH`; no source checkout is
58
+ required.
59
+
60
+ #### Open the normal terminal editor
36
61
 
37
62
  ```sh
38
63
  npx jsmdcui
39
64
  ```
40
65
 
41
- Or start the default browser demo:
66
+ #### Start `testapp.md` as the TUI/WUI demo
67
+ - If `testapp.md` is missing, the bundled copy is written to the current working directory
68
+ - If `testapp.md` already exists, it will be used and won't be overwritten.
69
+
70
+ ```sh
71
+ # TUI(Terminal User Interface) Demo
72
+ npx jsmdcui --demo
73
+ ```
74
+
75
+ ```sh
76
+ # Heading and task-list selector demo
77
+ npx jsmdcui --demo-select
78
+ ```
42
79
 
43
80
  ```sh
81
+ # Bun.Image processor demo
82
+ npx jsmdcui --demo-imgtool
83
+ ```
84
+
85
+ ```sh
86
+ # Traditional Chinese Bun.Image processor demo
87
+ npx jsmdcui --demo-imgtool-zh
88
+ ```
89
+
90
+ ```sh
91
+ # WUI(Web User Interface) Demo
44
92
  npx jsmdcui --wui
45
93
  ```
46
94
 
47
- If the current directory already contains `testapp.md`, that local file is used
48
- instead of the bundled demo. To open your own Markdown UI, pass its path:
95
+ #### Open your own Markdown CUI App
49
96
 
50
97
  ```sh
51
98
  npx jsmdcui app.md
@@ -60,30 +107,37 @@ cd jsmdcui
60
107
  bun src/index.js testapp.md
61
108
  ```
62
109
 
63
- The last command opens the included demo in the terminal. Use the arrow keys to
64
- move, `Enter` or `Space` to activate an item, and `Ctrl-Q` to quit. To open the
65
- same demo in a browser instead:
110
+ - The last command opens the included demo in the terminal
111
+ - Use arrow keys to move around
112
+ - `Enter`, `Space`, or mouse click to activate an item, and `Ctrl-Q` to quit.
113
+ - To open the same demo in a browser instead:
66
114
 
67
115
  ```sh
68
116
  bun src/index.js --wui testapp.md
69
117
  ```
70
118
 
71
- After starting `--wui`, open the last printed `http://...` URL in a browser.
72
- Keep the command running while using the page, and press `Ctrl-C` in that
73
- terminal to stop the server.
119
+ - After starting `--wui`, open the last printed `http://...` URL in a browser.
120
+ - Keep the command running while using the CUI App, and press `Ctrl-C` in that terminal to stop the server.
74
121
 
75
- The command table below uses the cloned-source form. If you use npx, replace
76
- `bun src/index.js` with `npx jsmdcui`.
122
+ ### Usage table
123
+
124
+ - The command table below uses the cloned-source form.
125
+ - If you use npx, replace `bun src/index.js` with `npx jsmdcui`
77
126
 
78
127
  | Command | Result |
79
128
  | --- | --- |
80
129
  | `bun src/index.js app.md` | Render `app.md` as a read-only terminal UI and write five generated files beside it. |
130
+ | `bun src/index.js --check app.md` | Check heading and fenced-block IDs for collisions, print line-by-line details, and exit. |
81
131
  | `bun src/index.js --edit app.md` | Open `app.md` as editable UTF-8 source, overriding automatic Markdown UI detection. |
82
132
  | `bun src/index.js --cat app.md` | Render the terminal version to stdout, write five generated files beside it, and exit. |
83
133
  | `bun src/index.js --testapp.md` | Write the bundled `testapp.md` source to stdout and exit. |
84
- | `bun src/index.js --demo` | Outputs & overwrites `./testapp.md`, opens it in the terminal UI, and writes 5 generated files beside it. |
134
+ | `bun src/index.js --export-readme` | Write or overwrite `./README.md` with the bundled README source and exit. |
135
+ | `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. |
85
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. |
86
- | `bun src/index.js --wui` | Use local `testapp.md` when present, otherwise use the bundled demo; write five generated files in the current directory, then print and serve a random URL. |
140
+ | `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. |
87
141
  | `bun src/index.js --wui app.md` | Write five generated files beside `app.md`, then print and serve a random URL. |
88
142
  | `PORT=8080 bun src/index.js --wui app.md` | Start the browser UI on another port. |
89
143
  | `bun src/index.js` | Open the normal terminal editor with an empty buffer. |
@@ -96,10 +150,19 @@ options.
96
150
  Create `app.md`:
97
151
 
98
152
  ````md
99
- # My app
153
+ ## My app
100
154
 
101
155
  - [Say hello](javascript:sayHello())
102
156
  - [Get server time](javascript:showServerTime())
157
+ - [Update text box](javascript:updateText())
158
+ - [Show selected](javascript:alert(JSON.stringify($('#my-app').val())))
159
+ - [x] task1
160
+ - [x] task2
161
+ - [ ] unselected task
162
+
163
+ ```text#myid.myclass
164
+ Editable in both TUI and WUI
165
+ ```
103
166
 
104
167
  ```js front
105
168
  export function sayHello() {
@@ -111,6 +174,10 @@ export async function showServerTime() {
111
174
  const time = await rpc.getServerTime();
112
175
  alert(time);
113
176
  }
177
+
178
+ export function updateText() {
179
+ $('#myid').val($('.myclass').val() + ' ✓');
180
+ }
114
181
  ```
115
182
 
116
183
  ```js back
@@ -127,12 +194,149 @@ bun src/index.js app.md
127
194
  bun src/index.js --wui app.md
128
195
  ```
129
196
 
197
+ - The resulting App UI is shown at the beginning of this README.md
198
+ - Run this demo app directly by:
199
+ * bun src/index.js README.md
200
+ * If you didn't clone the repo, use --export-readme to write README.md to the current folder
201
+
202
+ ### Text blocks
203
+
204
+ Both `text` and `textarea` fenced blocks define editable text fields:
205
+
206
+ ````md
207
+ ```text#message.note
208
+ Initial value
209
+ ```
210
+ ````
211
+
212
+ A fenced-block declaration must include a tag. Its supported identity syntax is
213
+ `tag`, optionally followed by `#id` and one or more `.class` names, for
214
+ example `text#message.note` or `textarea#notes.readonly`. Tags, IDs, and class
215
+ names must begin with an ASCII letter or underscore; their remaining
216
+ characters may also include digits, `_`, `-`, and `:`. Declarations such as
217
+ `#message` and `#message.note` have no tag and are therefore not recognized as
218
+ selectable fenced blocks.
219
+
220
+ The currently supported fenced-block tags are only `text` and `textarea` so
221
+ the same control works consistently in both the TUI and WUI. Selector queries
222
+ may omit the tag or classes after a valid declaration, so all of
223
+ `$('text#message.note')`, `$('text#message')`, `$('#message.note')`, and
224
+ `$('#message')` can select the example above.
225
+
226
+ The same Markdown works in both interfaces. In the browser WUI it becomes a
227
+ native `<textarea>` with the declared ID and classes. Long text wraps
228
+ automatically, and the field height is recalculated when the user types, the
229
+ window is resized, or frontend code calls `.val(value)`.
230
+
231
+ In the terminal TUI, only content after the protected `│ ` or `| ` prefix can
232
+ be edited. The frame prefix cannot be deleted, Enter cannot insert a newline,
233
+ Delete at the end of a row cannot join the next row, and multiline paste is
234
+ blocked. Activate the lower-left frame corner to add a row. Activate the
235
+ upper-left frame corner to remove the trailing row only when it is empty;
236
+ non-empty content is never removed.
237
+
238
+ ### Heading task lists and selector API
239
+
240
+ Headings can act as form-group selectors. jsmdcui uses the IDs generated by
241
+ `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.
245
+
246
+ Heading IDs share the same selector namespace as all explicitly named fenced
247
+ blocks, not only `text` and `textarea`, so avoid name collisions between them.
248
+ For example, `## Write Status` generates `#write-status` and must not be used
249
+ together with a block such as `text#write-status` or
250
+ `textarea#write-status`.
251
+ Otherwise, `$('#write-status')` may select the heading instead of the block and
252
+ updates can appear to do nothing. Rename either declaration so every
253
+ selectable ID is unique.
254
+
255
+ Duplicate source headings are also treated as collisions when they generate
256
+ the same base ID. Although Bun would automatically rename later headings with
257
+ suffixes such as `-1` and `-2`, those implicit selector names are not visible
258
+ in the Markdown source and should not be relied on. Give each heading a name
259
+ that produces a unique ID before rendering.
260
+
261
+ Run `bun src/index.js --check app.md` (or `jsmdcui --check app.md`) to
262
+ check these IDs without opening either UI or writing generated files. The
263
+ command prints each collision with its source type, line number, and original
264
+ declaration, then exits immediately. Its exit status is `0` when all IDs are
265
+ unique, `1` when collisions are found, and `2` for invalid arguments or read
266
+ errors.
267
+
268
+ If the heading ID begins with `select`, `.val()` behaves like a single select:
269
+ it returns the first checked item or `null`. Other heading IDs behave like a
270
+ multiple select and return all checked items as an array, or `[]` when none are
271
+ checked.
272
+
273
+ Heading values contain the complete visible label, including any explanatory
274
+ text. When interpreting a selection, prefer `value.includes(...)` (or another
275
+ deliberate prefix/token parser) unless labels are guaranteed to be exact,
276
+ stable identifiers. For example, `yes(flip vertically)` should not be tested
277
+ with `value === "yes"`.
278
+
279
+ ````md
280
+ ## Select Color
281
+
282
+ - [ ] Red
283
+ - [x] Green
284
+ - [ ] Blue
285
+
286
+ ## Features
287
+
288
+ - [x] Search
289
+ - [ ] Notifications
290
+ - [x] Offline mode
291
+
292
+ ```js front
293
+ export function showValues() {
294
+ alert(JSON.stringify({
295
+ color: $('#select-color').val(),
296
+ features: $('#features').val(),
297
+ }));
298
+ }
299
+ ```
300
+ ````
301
+
302
+ The same getter works in TUI and WUI:
303
+
304
+ ```js
305
+ $('#select-color').val() // "Green"
306
+ $('#features').val() // ["Search", "Offline mode"]
307
+ ```
308
+
309
+ The bundled `select.md` is a multilevel runnable example. Use `--demo-select`
310
+ to write it into the current directory when missing and open it in the TUI, or
311
+ open it explicitly in either interface:
312
+
313
+ ```sh
314
+ bun src/index.js --demo-select
315
+ bun src/index.js select.md
316
+ bun src/index.js --wui select.md
317
+ ```
318
+
319
+ The available selector methods are:
320
+
321
+ | Method | TUI | WUI |
322
+ | --- | --- | --- |
323
+ | `.val()` | Read text blocks or heading task-list values. | Read textareas/controls or heading task-list values. |
324
+ | `.val(value)` | Replace text-block contents and resize multiline values. | Set textarea/control values and resize textareas. |
325
+ | `.html()` | Return a selected heading's inner HTML. | Return any successfully selected DOM element's `innerHTML`. |
326
+ | `.line()` | Return a heading's current 1-based TUI row, or `0` if missing. | Not available. |
327
+
328
+ TUI heading rows are recalculated after text-block rows are added, removed, or
329
+ replaced with multiline `.val(value)` content.
330
+
130
331
  The three UI building blocks are:
131
332
 
132
333
  - Regular Markdown provides headings, text, lists, task checkboxes, code, and
133
334
  links.
134
335
  - A `js front` block contains UI code. Exported functions can use
135
336
  `alert`, `confirm`, `prompt`, and the generated `rpc` client.
337
+ - A front module may export `async function onMdcuiExit({ reason, path, $ })`.
338
+ The terminal UI awaits it before closing an `mdcui` buffer. Modified
339
+ `mdcui` buffers close without a save prompt.
136
340
  - A `js back` block exports trusted backend functions. In the browser WUI,
137
341
  `rpc` publishes only exported functions whose exported names do not start
138
342
  with `_`. Call a published function from the front end with
@@ -149,6 +353,22 @@ Use a `javascript:` Markdown link to run front-end code:
149
353
  [Button label](javascript:exportedFunction())
150
354
  ```
151
355
 
356
+ Use `onMdcuiExit` when a terminal Markdown app needs to submit or otherwise
357
+ process edited fields before it closes:
358
+
359
+ ```js
360
+ export async function onMdcuiExit({ reason, path, $ }) {
361
+ await rpc.saveDraft({
362
+ reason,
363
+ path,
364
+ message: $('#message').val(),
365
+ });
366
+ }
367
+ ```
368
+
369
+ The callback is optional, may be asynchronous, and is called at most once for
370
+ each mdcui buffer.
371
+
152
372
  The front and back code blocks are extracted and are not shown in the rendered
153
373
  UI. In the terminal, `rpc` calls the generated backend module directly. In the
154
374
  browser, it sends the call to the relative `rpc` endpoint under the printed
@@ -156,14 +376,15 @@ UUID URL (`/<uuid>/rpc`).
156
376
 
157
377
  ## Terminal interaction
158
378
 
159
- Markdown files automatically use `mdcui` mode. The rendered buffer is
160
- read-only, but navigation, selection, search, and copy remain available.
379
+ Markdown files automatically use `mdcui` mode. Most rendered content remains
380
+ protected, while `text` block content rows can be edited. Navigation,
381
+ selection, search, and copy remain available.
161
382
 
162
383
  | Input | Result |
163
384
  | --- | --- |
164
385
  | Arrow keys, `Home`, `End`, `PageUp`, `PageDown` | Move through the rendered UI. |
165
- | `Enter` or `Space` | Activate the cell under the cursor. Put the cursor on a `javascript:` link to run it. |
166
- | Left click | Move to and activate the clicked cell. A `javascript:` link runs immediately. |
386
+ | `Enter` or `Space` | Activate the cell under the cursor. Put the cursor on a `javascript:` link to run it, or on or to the right of `☐` or `☒` to toggle that task. |
387
+ | Left click | Move to and activate the clicked cell. A `javascript:` link runs immediately; clicking on or to the right of `☐` or `☒` toggles that task. |
167
388
  | Mouse wheel | Scroll three rows at a time. |
168
389
  | `Shift` + arrow keys | Select rendered text. |
169
390
  | `Ctrl-C` | Copy the selection, or the current line when nothing is selected. |
@@ -181,9 +402,10 @@ normal links in the browser.
181
402
 
182
403
  The WUI uses normal browser mouse and keyboard behavior. Clicking a
183
404
  `javascript:` link calls its front-end function, regular links navigate
184
- normally, and task checkboxes can be toggled. `alert`, `confirm`, and `prompt`
185
- use the browser's built-in dialogs. Checkbox changes exist only in the current
186
- page: refreshing does not preserve them and does not update the Markdown file.
405
+ normally, and task checkboxes can be toggled by clicking either the checkbox or
406
+ its associated text. `alert`, `confirm`, and `prompt` use the browser's built-in
407
+ dialogs. Checkbox changes exist only in the current page: refreshing does not
408
+ preserve them and does not update the Markdown file.
187
409
 
188
410
  The WUI uses port `3000` by default and accepts connections through the
189
411
  machine's available network interfaces. The printed `localhost` URL is for the
@@ -209,6 +431,26 @@ app.md-server.js
209
431
  They are regenerated from `app.md`, so edit the Markdown source rather than the
210
432
  generated files. The source directory must be writable.
211
433
 
434
+ ### Generated heading sections
435
+
436
+ WUI output wraps each h1-h6 and its content in a hierarchical `<section>`.
437
+ Lower-level headings create nested sections; the next equal or higher-level
438
+ heading closes the applicable sections. Content before the first heading stays
439
+ outside all generated sections.
440
+
441
+ ```html
442
+ <section>
443
+ <h1 id="chapter">Chapter</h1>
444
+ <section>
445
+ <h2 id="topic">Topic</h2>
446
+ </section>
447
+ </section>
448
+ ```
449
+
450
+ This structure provides stable boundaries for CSS, DOM queries, and heading
451
+ task-list values. Heading-like text inside comments, scripts, styles, code,
452
+ textareas, and templates is protected and does not create sections.
453
+
212
454
  ## Security
213
455
 
214
456
  A Markdown UI is an executable application, not a passive document. Starting a