jsmdcui 0.3.0 → 0.5.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,143 @@
2
2
 
3
3
  All notable user-visible changes to jsmdcui are documented here.
4
4
 
5
+ ## [0.5.0] - 2026-07-16
6
+
7
+ This update adds cross-environment form-like text blocks and a small
8
+ jQuery-style selector API, while keeping the same Markdown source usable in
9
+ both the terminal and browser interfaces.
10
+
11
+ ### Added
12
+
13
+ - Add a minimal global `$` API to terminal frontend evaluation, JavaScript
14
+ plugins, generated browser frontend modules, and `javascript:` links.
15
+ - Support tag, ID, class, and combined selectors such as `$('text')`,
16
+ `$('#answer')`, `$('.field')`, and `$('text#answer.field')`.
17
+ - Add `.val()` and `.val(value)` for reading and replacing block contents.
18
+ Getters always return a string and use an empty string for missing elements,
19
+ invalid selectors, or lookup errors. Setters support chaining and resize
20
+ terminal blocks to fit multiline values.
21
+ - Recognize both raw Markdown fenced blocks and rendered Bun ANSI blocks,
22
+ including ASCII and Unicode frame characters.
23
+ - Add editable terminal `text` blocks. Content after the protected `│ ` or
24
+ `| ` prefix can be edited, while frame characters, line joins, inserted
25
+ newlines, and multiline pastes remain protected.
26
+ - Add interactive terminal text-block resizing. Activating the lower-left
27
+ frame corner adds an empty content row; activating the upper-left corner
28
+ removes only a trailing empty row and never deletes non-empty content.
29
+ - Add the optional frontend lifecycle callback
30
+ `onMdcuiExit({ reason, path, $ })`. The terminal awaits it before closing an
31
+ mdcui buffer and invokes it at most once per buffer.
32
+ - Add Markdown syntax completions for fenced block language identifiers.
33
+
34
+ ### Changed
35
+
36
+ - Convert `text` and `textarea` fenced blocks into native browser
37
+ `<textarea>` elements while preserving their ID, classes, original language
38
+ metadata, and selector compatibility.
39
+ - Prefer native `document.querySelector()` in the browser `$` implementation,
40
+ then fall back to mdcui metadata and the original `pre > code` structure.
41
+ - Automatically wrap and resize generated browser textareas on page load,
42
+ input, `.val(value)`, and window resize. Initial `rows` and `cols` are
43
+ derived from the Markdown content.
44
+ - Generate complete HTML5 documents for WUI output, including `<!doctype
45
+ html>`, UTF-8 metadata, a responsive viewport, a document title, and
46
+ `lang="zh-TW"`.
47
+ - Enable `softwrap` by default.
48
+ - Close modified mdcui buffers without displaying a save prompt. Applications
49
+ can use `onMdcuiExit` to collect edited field values or call backend RPC
50
+ functions before closing.
51
+ - Update the bundled example with editable text fields, selector API examples,
52
+ answer validation, and manual text-box resizing instructions.
53
+
54
+ ### Fixed
55
+
56
+ - Keep terminal text-block frame prefixes intact during editing and prevent
57
+ Delete at the end of a content row from merging it with the next row.
58
+ - Preserve browser textarea IDs and classes during Markdown-to-HTML
59
+ conversion, including compatibility metadata for original `text` selectors.
60
+ - Keep README and built-in help lifecycle documentation synchronized.
61
+
62
+ ## [0.4.0] - 2026-07-16
63
+
64
+ This release separates executable Markdown UI views from ordinary editable
65
+ buffers, makes encoding behavior predictable across opens, tabs, splits, and
66
+ reopens, and adds explicit demo and trusted-remote execution modes.
67
+
68
+ ### Added
69
+
70
+ - Add `--demo`. It outputs and overwrites `./testapp.md`, opens it as a terminal
71
+ Markdown UI, and writes the five generated companion files beside it.
72
+ - Add `--allow-url` for trusted remote Markdown apps. It downloads an HTTP(S)
73
+ resource into the current directory, opens the downloaded copy through the
74
+ normal local Markdown UI pipeline, writes the five generated companion files,
75
+ and permits its embedded frontend and backend code to run.
76
+ - Add regression coverage for global encoding handling, mdcui backup isolation,
77
+ the bundled demo workflow, implicit Markdown rendering, and explicit UTF-8
78
+ editing.
79
+
80
+ ### Changed
81
+
82
+ - Treat `mdcui` buffers as independent, derived, read-only views. Every mdcui
83
+ open creates a new buffer instead of entering the same-path editable-buffer
84
+ cache.
85
+ - Continue sharing ordinary buffers opened from the same absolute path.
86
+ UTF-8 and other non-mdcui views share content, modification state, saves, and
87
+ reopens.
88
+ - Re-evaluate buffer sharing after `reopen`:
89
+ - reopening an mdcui view as a normal encoding joins an existing same-path
90
+ editable buffer, or registers itself when no such buffer exists;
91
+ - reopening a shared editable view as mdcui detaches only the current pane
92
+ into a new mdcui buffer and leaves the other shared panes unchanged.
93
+ - Make interactive `open`, `tab`, `vsplit`, and `hsplit` operations perform
94
+ fresh `.md` automatic detection for files that are not already represented
95
+ by a normal cached buffer.
96
+ - Ignore a top-level global `encoding` value read from `settings.json`.
97
+ Encoding is selected per invocation or buffer; command-line encoding options
98
+ remain effective for the current run and are not persisted.
99
+ - Organize `--testapp.md` and `--demo` under a dedicated `Demo` section in
100
+ `--help`, and document commands that overwrite source or generated files.
101
+
102
+ ### Fixed
103
+
104
+ - Fix `.md` files opened from inside the editor failing to enter mdcui mode.
105
+ - Fix encoding changes leaking between independent mdcui tabs.
106
+ - Fix `reopen mdcui` mutating every pane that shared the original editable
107
+ buffer.
108
+ - Fix mdcui-to-UTF-8 reopens remaining outside the normal same-path buffer
109
+ cache.
110
+ - Delay applying an explicit reopen encoding until the reopen is confirmed, so
111
+ canceling the save prompt does not alter the shared buffer.
112
+
113
+ ### Security
114
+
115
+ - Exclude mdcui buffers completely from crash-recovery backups: they do not
116
+ create, apply, prompt for, or remove backups. This prevents a derived
117
+ read-only view from consuming or deleting a backup belonging to an editable
118
+ view of the same Markdown source.
119
+ - Remote HTTP(S) Markdown remains non-executable and does not write companion
120
+ files by default.
121
+ - `--allow-url` is an explicit trust boundary. It writes downloaded content and
122
+ generated JavaScript/HTML files into the current directory, may overwrite
123
+ files with the same names, and runs code from the remote document with the
124
+ permissions of the jsmdcui process. Use it only with trusted URLs and in a
125
+ directory where those writes are safe.
126
+
127
+ ## [0.3.1] - 2026-07-16
128
+
129
+ ### Added
130
+
131
+ - Add `--edit` to open Markdown files as editable UTF-8 source instead of
132
+ automatically entering mdcui mode.
133
+ - Add `--cat` regression coverage for implicit mdcui rendering and explicit
134
+ UTF-8 overrides.
135
+
136
+ ### Fixed
137
+
138
+ - Prevent an explicit encoding choice from being replaced by automatic mdcui
139
+ detection.
140
+ - Correct terminal raw-mode handling around log and prompt output.
141
+
5
142
  ## [0.3.0] - 2026-07-16
6
143
 
7
144
  Initial usable release, based on the bunmicro terminal editor.
package/README.md CHANGED
@@ -78,8 +78,11 @@ The command table below uses the cloned-source form. If you use npx, replace
78
78
  | Command | Result |
79
79
  | --- | --- |
80
80
  | `bun src/index.js app.md` | Render `app.md` as a read-only terminal UI and write five generated files beside it. |
81
+ | `bun src/index.js --edit app.md` | Open `app.md` as editable UTF-8 source, overriding automatic Markdown UI detection. |
81
82
  | `bun src/index.js --cat app.md` | Render the terminal version to stdout, write five generated files beside it, and exit. |
82
83
  | `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. |
85
+ | `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. |
83
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. |
84
87
  | `bun src/index.js --wui app.md` | Write five generated files beside `app.md`, then print and serve a random URL. |
85
88
  | `PORT=8080 bun src/index.js --wui app.md` | Start the browser UI on another port. |
@@ -97,6 +100,11 @@ Create `app.md`:
97
100
 
98
101
  - [Say hello](javascript:sayHello())
99
102
  - [Get server time](javascript:showServerTime())
103
+ - [Update text box](javascript:updateText())
104
+
105
+ ```text#myid.myclass
106
+ Editable in both TUI and WUI
107
+ ```
100
108
 
101
109
  ```js front
102
110
  export function sayHello() {
@@ -108,6 +116,10 @@ export async function showServerTime() {
108
116
  const time = await rpc.getServerTime();
109
117
  alert(time);
110
118
  }
119
+
120
+ export function updateText() {
121
+ $('#myid').val($('.myclass').val() + ' ✓');
122
+ }
111
123
  ```
112
124
 
113
125
  ```js back
@@ -124,12 +136,37 @@ bun src/index.js app.md
124
136
  bun src/index.js --wui app.md
125
137
  ```
126
138
 
139
+ ### Text blocks
140
+
141
+ Both `text` and `textarea` fenced blocks define editable text fields:
142
+
143
+ ````md
144
+ ```text#message.note
145
+ Initial value
146
+ ```
147
+ ````
148
+
149
+ The same Markdown works in both interfaces. In the browser WUI it becomes a
150
+ native `<textarea>` with the declared ID and classes. Long text wraps
151
+ automatically, and the field height is recalculated when the user types, the
152
+ window is resized, or frontend code calls `.val(value)`.
153
+
154
+ In the terminal TUI, only content after the protected `│ ` or `| ` prefix can
155
+ be edited. The frame prefix cannot be deleted, Enter cannot insert a newline,
156
+ Delete at the end of a row cannot join the next row, and multiline paste is
157
+ blocked. Activate the lower-left frame corner to add a row. Activate the
158
+ upper-left frame corner to remove the trailing row only when it is empty;
159
+ non-empty content is never removed.
160
+
127
161
  The three UI building blocks are:
128
162
 
129
163
  - Regular Markdown provides headings, text, lists, task checkboxes, code, and
130
164
  links.
131
165
  - A `js front` block contains UI code. Exported functions can use
132
166
  `alert`, `confirm`, `prompt`, and the generated `rpc` client.
167
+ - A front module may export `async function onMdcuiExit({ reason, path, $ })`.
168
+ The terminal UI awaits it before closing an `mdcui` buffer. Modified
169
+ `mdcui` buffers close without a save prompt.
133
170
  - A `js back` block exports trusted backend functions. In the browser WUI,
134
171
  `rpc` publishes only exported functions whose exported names do not start
135
172
  with `_`. Call a published function from the front end with
@@ -146,6 +183,22 @@ Use a `javascript:` Markdown link to run front-end code:
146
183
  [Button label](javascript:exportedFunction())
147
184
  ```
148
185
 
186
+ Use `onMdcuiExit` when a terminal Markdown app needs to submit or otherwise
187
+ process edited fields before it closes:
188
+
189
+ ```js
190
+ export async function onMdcuiExit({ reason, path, $ }) {
191
+ await rpc.saveDraft({
192
+ reason,
193
+ path,
194
+ message: $('#message').val(),
195
+ });
196
+ }
197
+ ```
198
+
199
+ The callback is optional, may be asynchronous, and is called at most once for
200
+ each mdcui buffer.
201
+
149
202
  The front and back code blocks are extracted and are not shown in the rendered
150
203
  UI. In the terminal, `rpc` calls the generated backend module directly. In the
151
204
  browser, it sends the call to the relative `rpc` endpoint under the printed
@@ -153,8 +206,9 @@ UUID URL (`/<uuid>/rpc`).
153
206
 
154
207
  ## Terminal interaction
155
208
 
156
- Markdown files automatically use `mdcui` mode. The rendered buffer is
157
- read-only, but navigation, selection, search, and copy remain available.
209
+ Markdown files automatically use `mdcui` mode. Most rendered content remains
210
+ protected, while `text` block content rows can be edited. Navigation,
211
+ selection, search, and copy remain available.
158
212
 
159
213
  | Input | Result |
160
214
  | --- | --- |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jsmdcui",
3
- "version": "0.3.0",
3
+ "version": "0.5.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
@@ -17,6 +17,7 @@ const TEST_COL=5
17
17
 
18
18
  function logWroteFile(label,path)
19
19
  {
20
+ if(!process.stdin.isRaw)
20
21
  csl(mda(`- Wrote to ${label} file: ${path}`))
21
22
  }
22
23
 
@@ -188,6 +189,54 @@ export function createTui(md,TERMINAL_WIDTH=30) // ANSI Colors
188
189
  )
189
190
  }
190
191
 
192
+ function parseWuiControlIdentity(info)
193
+ {
194
+ const match = String(info ?? "").match(/^([A-Za-z_][\w:-]*)(?:#([A-Za-z_][\w:-]*))?((?:\.[A-Za-z_][\w:-]*)*)$/);
195
+ if (!match || !["text", "textarea"].includes(match[1])) return null;
196
+ return {
197
+ tag: match[1],
198
+ id: match[2] || "",
199
+ classes: match[3] ? match[3].slice(1).split(".") : [],
200
+ };
201
+ }
202
+
203
+ function escapeHtmlAttribute(value)
204
+ {
205
+ return String(value)
206
+ .replaceAll("&", "&amp;")
207
+ .replaceAll('"', "&quot;")
208
+ .replaceAll("<", "&lt;")
209
+ .replaceAll(">", "&gt;");
210
+ }
211
+
212
+ export function convertWuiTextareas(html)
213
+ {
214
+ return String(html).replace(
215
+ /<pre><code class="language-([^"]+)">([^]*?)<\/code><\/pre>/g,
216
+ (whole, info, content) => {
217
+ const identity = parseWuiControlIdentity(info);
218
+ if (!identity) return whole;
219
+ const value = content.replace(/\n$/, "");
220
+ const contentLines = value.split("\n");
221
+ const cols = Math.max(1, ...contentLines.map(line => [...line].length));
222
+ const attrs = [
223
+ `data-mdcui-tag="${identity.tag}"`,
224
+ `data-mdcui-language="${escapeHtmlAttribute(info)}"`,
225
+ identity.id ? `id="${escapeHtmlAttribute(identity.id)}"` : "",
226
+ `class="${escapeHtmlAttribute([
227
+ `language-${info}`,
228
+ ...identity.classes,
229
+ ].join(" "))}"`,
230
+ `rows="${Math.max(1, contentLines.length)}"`,
231
+ `cols="${cols}"`,
232
+ 'wrap="soft"',
233
+ 'style="box-sizing:border-box;max-width:100%;width:100%;resize:vertical;overflow-y:hidden"',
234
+ ].filter(Boolean).join(" ");
235
+ return `<textarea ${attrs}>${value}</textarea>`;
236
+ },
237
+ );
238
+ }
239
+
191
240
  export async function createWui(md,mdpath) // HTML
192
241
  {
193
242
 
@@ -208,16 +257,31 @@ export async function createWui(md,mdpath) // HTML
208
257
  'class="task-list-item-checkbox" disabled',
209
258
  'class="task-list-item-checkbox"'
210
259
  )
260
+
261
+ md = convertWuiTextareas(md)
211
262
 
212
263
  const mdb = path.basename(mdpath);
213
264
 
214
-
215
- md += `
216
-
217
- <scr`+`ipt type="module" src="./${mdb}.front.js">
218
- </scr`+`ipt>
219
-
220
- `;
265
+ const moduleScript = `<scr`+`ipt type="module" src="./${mdb}.front.js"></scr`+`ipt>`;
266
+ if (!/^\s*<!doctype html>/i.test(md)) {
267
+ md = `<!doctype html>
268
+ <html lang="zh-TW">
269
+ <head>
270
+ <meta charset="utf-8">
271
+ <meta name="viewport" content="width=device-width, initial-scale=1">
272
+ <title>${escapeHtmlAttribute(mdb)}</title>
273
+ </head>
274
+ <body>
275
+ ${md}
276
+ ${moduleScript}
277
+ </body>
278
+ </html>
279
+ `;
280
+ } else if (/<\/body\s*>/i.test(md)) {
281
+ md = md.replace(/<\/body\s*>/i, `${moduleScript}\n</body>`);
282
+ } else {
283
+ md += `\n${moduleScript}\n`;
284
+ }
221
285
 
222
286
 
223
287
  await Bun.write(mdpath+".html",md)
@@ -78,8 +78,11 @@ The command table below uses the cloned-source form. If you use npx, replace
78
78
  | Command | Result |
79
79
  | --- | --- |
80
80
  | `bun src/index.js app.md` | Render `app.md` as a read-only terminal UI and write five generated files beside it. |
81
+ | `bun src/index.js --edit app.md` | Open `app.md` as editable UTF-8 source, overriding automatic Markdown UI detection. |
81
82
  | `bun src/index.js --cat app.md` | Render the terminal version to stdout, write five generated files beside it, and exit. |
82
83
  | `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. |
85
+ | `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. |
83
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. |
84
87
  | `bun src/index.js --wui app.md` | Write five generated files beside `app.md`, then print and serve a random URL. |
85
88
  | `PORT=8080 bun src/index.js --wui app.md` | Start the browser UI on another port. |
@@ -97,6 +100,11 @@ Create `app.md`:
97
100
 
98
101
  - [Say hello](javascript:sayHello())
99
102
  - [Get server time](javascript:showServerTime())
103
+ - [Update text box](javascript:updateText())
104
+
105
+ ```text#myid.myclass
106
+ Editable in both TUI and WUI
107
+ ```
100
108
 
101
109
  ```js front
102
110
  export function sayHello() {
@@ -108,6 +116,10 @@ export async function showServerTime() {
108
116
  const time = await rpc.getServerTime();
109
117
  alert(time);
110
118
  }
119
+
120
+ export function updateText() {
121
+ $('#myid').val($('.myclass').val() + ' ✓');
122
+ }
111
123
  ```
112
124
 
113
125
  ```js back
@@ -124,12 +136,37 @@ bun src/index.js app.md
124
136
  bun src/index.js --wui app.md
125
137
  ```
126
138
 
139
+ ### Text blocks
140
+
141
+ Both `text` and `textarea` fenced blocks define editable text fields:
142
+
143
+ ````md
144
+ ```text#message.note
145
+ Initial value
146
+ ```
147
+ ````
148
+
149
+ The same Markdown works in both interfaces. In the browser WUI it becomes a
150
+ native `<textarea>` with the declared ID and classes. Long text wraps
151
+ automatically, and the field height is recalculated when the user types, the
152
+ window is resized, or frontend code calls `.val(value)`.
153
+
154
+ In the terminal TUI, only content after the protected `│ ` or `| ` prefix can
155
+ be edited. The frame prefix cannot be deleted, Enter cannot insert a newline,
156
+ Delete at the end of a row cannot join the next row, and multiline paste is
157
+ blocked. Activate the lower-left frame corner to add a row. Activate the
158
+ upper-left frame corner to remove the trailing row only when it is empty;
159
+ non-empty content is never removed.
160
+
127
161
  The three UI building blocks are:
128
162
 
129
163
  - Regular Markdown provides headings, text, lists, task checkboxes, code, and
130
164
  links.
131
165
  - A `js front` block contains UI code. Exported functions can use
132
166
  `alert`, `confirm`, `prompt`, and the generated `rpc` client.
167
+ - A front module may export `async function onMdcuiExit({ reason, path, $ })`.
168
+ The terminal UI awaits it before closing an `mdcui` buffer. Modified
169
+ `mdcui` buffers close without a save prompt.
133
170
  - A `js back` block exports trusted backend functions. In the browser WUI,
134
171
  `rpc` publishes only exported functions whose exported names do not start
135
172
  with `_`. Call a published function from the front end with
@@ -146,6 +183,22 @@ Use a `javascript:` Markdown link to run front-end code:
146
183
  [Button label](javascript:exportedFunction())
147
184
  ```
148
185
 
186
+ Use `onMdcuiExit` when a terminal Markdown app needs to submit or otherwise
187
+ process edited fields before it closes:
188
+
189
+ ```js
190
+ export async function onMdcuiExit({ reason, path, $ }) {
191
+ await rpc.saveDraft({
192
+ reason,
193
+ path,
194
+ message: $('#message').val(),
195
+ });
196
+ }
197
+ ```
198
+
199
+ The callback is optional, may be asynchronous, and is called at most once for
200
+ each mdcui buffer.
201
+
149
202
  The front and back code blocks are extracted and are not shown in the rendered
150
203
  UI. In the terminal, `rpc` calls the generated backend module directly. In the
151
204
  browser, it sends the call to the relative `rpc` endpoint under the printed
@@ -153,8 +206,9 @@ UUID URL (`/<uuid>/rpc`).
153
206
 
154
207
  ## Terminal interaction
155
208
 
156
- Markdown files automatically use `mdcui` mode. The rendered buffer is
157
- read-only, but navigation, selection, search, and copy remain available.
209
+ Markdown files automatically use `mdcui` mode. Most rendered content remains
210
+ protected, while `text` block content rows can be edited. Navigation,
211
+ selection, search, and copy remain available.
158
212
 
159
213
  | Input | Result |
160
214
  | --- | --- |
@@ -47,3 +47,15 @@ rules:
47
47
  start: "`"
48
48
  end: "`"
49
49
  rules: []
50
+
51
+
52
+ # keywords
53
+ # javascript alert confirm prompt rpc
54
+ # front back
55
+ # console log error JSON stringify parse
56
+ # async await Promise
57
+ # textarea text password search
58
+ # email url tel number range color
59
+ # date time datetime-local month week
60
+ # checkbox radio file hidden image button submit reset
61
+
@@ -5,9 +5,14 @@ import { existsSync, statSync, unlinkSync } from "node:fs";
5
5
  import { mkdir, writeFile, readFile, rename } from "node:fs/promises";
6
6
  import { homedir } from "node:os";
7
7
  import { createHash } from "node:crypto";
8
+ import { isMdcuiEncoding } from "../runtime/encodings.js";
8
9
 
9
10
  export const BACKUP_SUFFIX = ".micro-backup";
10
11
 
12
+ function isMdcuiBuffer(buf) {
13
+ return isMdcuiEncoding(buf?.encoding ?? buf?.Settings?.encoding);
14
+ }
15
+
11
16
  export function getBackupDir(buf, configDir) {
12
17
  const raw = String(buf?.Settings?.backupdir ?? "");
13
18
  if (!raw) return join(configDir, "backups");
@@ -49,7 +54,7 @@ export function determineBackupPath(backupDirPath, absPath) {
49
54
  }
50
55
 
51
56
  export async function writeBackup(buf, configDir, path = buf?.AbsPath ?? buf?.path, { force = false } = {}) {
52
- if ((!force && !buf?.Settings?.backup) || !path || buf.type !== "default") return false;
57
+ if (isMdcuiBuffer(buf) || (!force && !buf?.Settings?.backup) || !path || buf.type !== "default") return false;
53
58
  const dir = getBackupDir(buf, configDir);
54
59
  await mkdir(dir, { recursive: true });
55
60
  const { name, resolveName } = determineBackupPath(dir, path);
@@ -66,7 +71,7 @@ export async function writeBackup(buf, configDir, path = buf?.AbsPath ?? buf?.pa
66
71
  }
67
72
 
68
73
  export function removeBackup(buf, configDir, path = buf?.AbsPath ?? buf?.path) {
69
- if (buf?.Settings?.permbackup || buf?._forceKeepBackup) return;
74
+ if (isMdcuiBuffer(buf) || buf?.Settings?.permbackup || buf?._forceKeepBackup) return;
70
75
  if (!path || buf.type !== "default") return;
71
76
  const dir = getBackupDir(buf, configDir);
72
77
  const { name, resolveName } = determineBackupPath(dir, path);
@@ -77,7 +82,7 @@ export function removeBackup(buf, configDir, path = buf?.AbsPath ?? buf?.path) {
77
82
  // promptFn(msg) -> Promise<string>
78
83
  // Returns { recovered: bool, abort: bool }
79
84
  export async function applyBackup(buf, configDir, promptFn) {
80
- if (!buf?.Settings?.backup || buf?.Settings?.permbackup) return { recovered: false, abort: false };
85
+ if (isMdcuiBuffer(buf) || !buf?.Settings?.backup || buf?.Settings?.permbackup) return { recovered: false, abort: false };
81
86
  if (!buf.path || buf.type !== "default") return { recovered: false, abort: false };
82
87
 
83
88
  const dir = getBackupDir(buf, configDir);
@@ -27,6 +27,9 @@ export class Config {
27
27
  const text = await readFile(path, "utf8");
28
28
  if (text.trimStart().startsWith("null") || text.trim() === "") return;
29
29
  this.parsedSettings = Bun.JSON5.parse(text);
30
+ // jsmdcui chooses the input encoding per invocation/buffer. A persisted
31
+ // global encoding must neither affect startup nor survive the next save.
32
+ delete this.parsedSettings.encoding;
30
33
  this.applyParsedSettings(this.parsedSettings);
31
34
  }
32
35
 
@@ -38,7 +38,7 @@ export const DEFAULT_COMMON_SETTINGS = {
38
38
  scrollspeed: 2,
39
39
  showchars: "",
40
40
  smartpaste: true,
41
- softwrap: false,
41
+ softwrap: true,
42
42
  splitbottom: true,
43
43
  splitright: true,
44
44
  statusformatl: "$(filename) $(modified)$(overwrite)($(line),$(col)) $(status.paste)| ft:$(opt:filetype) | $(opt:fileformat) | $(opt:encoding)",
@@ -98,7 +98,7 @@ export const OPTION_CHOICES = {
98
98
 
99
99
  // Settings that are buffer-local only and must never be written to the global config file.
100
100
  // Mirrors Go micro's config.LocalSettings.
101
- export const LOCAL_SETTINGS = new Set(["readonly", "filetype", "fileformat"]);
101
+ export const LOCAL_SETTINGS = new Set(["readonly", "filetype", "fileformat", "encoding"]);
102
102
 
103
103
  export function defaultAllSettings() {
104
104
  return { ...DEFAULT_COMMON_SETTINGS, ...DEFAULT_GLOBAL_ONLY_SETTINGS };