@bobfrankston/rmfmail 1.2.105 → 1.2.108
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/TODO.md +1 -1
- package/bin/mailx.js +14 -3
- package/bin/mailx.js.map +1 -1
- package/bin/mailx.ts +14 -3
- package/client/compose/compose.bundle.js +30 -3
- package/client/compose/compose.bundle.js.map +2 -2
- package/client/lib/rmf-tiny.js +41 -3
- package/debug.md +33 -0
- package/package.json +1 -1
package/client/lib/rmf-tiny.js
CHANGED
|
@@ -172,15 +172,28 @@ export async function createTinyMceEditor(container, opts = {}) {
|
|
|
172
172
|
const input = document.createElement("input");
|
|
173
173
|
input.type = "file";
|
|
174
174
|
input.accept = "image/*";
|
|
175
|
+
// MUST be connected to the document before .click() — WebView2
|
|
176
|
+
// (Chromium) silently refuses to open the OS file dialog for a
|
|
177
|
+
// DETACHED file input, so the Browse button in the Insert Image
|
|
178
|
+
// dialog did nothing while drag-drop (no input) and URL-source
|
|
179
|
+
// insert both worked (Bob 2026-07-06 "insert image is failing …
|
|
180
|
+
// but drag drop does work"). The compose Attach button never hit
|
|
181
|
+
// this because it uses a permanent #compose-file input. Hidden +
|
|
182
|
+
// removed after change so it leaves no DOM residue.
|
|
183
|
+
input.style.display = "none";
|
|
184
|
+
const cleanup = () => { input.remove(); };
|
|
175
185
|
input.onchange = () => {
|
|
176
186
|
const file = input.files && input.files[0];
|
|
177
|
-
if (!file)
|
|
187
|
+
if (!file) {
|
|
188
|
+
cleanup();
|
|
178
189
|
return;
|
|
190
|
+
}
|
|
179
191
|
const reader = new FileReader();
|
|
180
|
-
reader.onload = () => cb(reader.result, { alt: file.name, title: file.name });
|
|
181
|
-
reader.onerror = () => cb("");
|
|
192
|
+
reader.onload = () => { cb(reader.result, { alt: file.name, title: file.name }); cleanup(); };
|
|
193
|
+
reader.onerror = () => { cb(""); cleanup(); };
|
|
182
194
|
reader.readAsDataURL(file);
|
|
183
195
|
};
|
|
196
|
+
document.body.appendChild(input);
|
|
184
197
|
input.click();
|
|
185
198
|
},
|
|
186
199
|
// Right-click context menu DISABLED so WebView2's native menu
|
|
@@ -327,6 +340,31 @@ export async function createTinyMceEditor(container, opts = {}) {
|
|
|
327
340
|
applyZoom();
|
|
328
341
|
saveZoom();
|
|
329
342
|
};
|
|
343
|
+
// Don't spell-check URLs or email addresses (Bob 2026-07-06).
|
|
344
|
+
// WebView2's native spell-check underlines every token, so
|
|
345
|
+
// "gmail", "iecc", "https", path segments etc. all draw red
|
|
346
|
+
// squiggles inside links. The browser skips any subtree whose
|
|
347
|
+
// ancestor carries spellcheck="false", and autolink already
|
|
348
|
+
// wraps bare URLs + emails in <a> (as does mailto: / http
|
|
349
|
+
// linking), so tagging every link suppresses exactly the
|
|
350
|
+
// URL/email case while leaving prose checked. Idempotent —
|
|
351
|
+
// only touches links missing the attribute — so running it on
|
|
352
|
+
// every NodeChange is cheap. Also covers late-linked tokens the
|
|
353
|
+
// moment autolink converts them.
|
|
354
|
+
const suppressLinkSpellcheck = () => {
|
|
355
|
+
try {
|
|
356
|
+
const body = ed.getBody();
|
|
357
|
+
if (!body)
|
|
358
|
+
return;
|
|
359
|
+
const links = body.querySelectorAll("a:not([spellcheck])");
|
|
360
|
+
for (const a of links)
|
|
361
|
+
a.setAttribute("spellcheck", "false");
|
|
362
|
+
}
|
|
363
|
+
catch { /* editor torn down mid-call — non-fatal */ }
|
|
364
|
+
};
|
|
365
|
+
ed.on("init", suppressLinkSpellcheck);
|
|
366
|
+
ed.on("SetContent", suppressLinkSpellcheck);
|
|
367
|
+
ed.on("NodeChange", suppressLinkSpellcheck);
|
|
330
368
|
// Custom "Insert code" — wraps the stock codesample plugin
|
|
331
369
|
// (keeps its Prism highlighting + edit-in-place) but adds a
|
|
332
370
|
// "Box it in" border checkbox so a snippet is visually
|
package/debug.md
CHANGED
|
@@ -79,3 +79,36 @@ log (`~/.rmfmail/logs/rmfmail-<date>.log`) for `[parse]`, `[parse-worker]`,
|
|
|
79
79
|
Note: while a large first-sync is running the event loop is saturated and any
|
|
80
80
|
endpoint can be slow — that saturation is itself a bug under investigation, not
|
|
81
81
|
a debug-server limitation.
|
|
82
|
+
|
|
83
|
+
## Popout window (🗗) headless testing
|
|
84
|
+
|
|
85
|
+
`--debug-server` runs also log the popout server's per-launch token:
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
[popout] window server on http://127.0.0.1:<port> (loopback, token-gated)
|
|
89
|
+
[popout] DEBUG token=<token> (logged because --debug-server)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
With those you can exercise the whole native-popout path without clicking:
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
# The page the msger window loads (subject + toolbar + body iframe)
|
|
96
|
+
curl "http://127.0.0.1:<port>/v?a=<acct>&u=<uid>&f=<folderId>&t=<token>"
|
|
97
|
+
|
|
98
|
+
# Toolbar action → daemon → popoutAction event → main window opens editable
|
|
99
|
+
# compose (reply/replyAll/forward/editAsNew) or flags/deletes
|
|
100
|
+
curl -X POST -H "content-type: application/json" -d '{"action":"reply"}' \
|
|
101
|
+
"http://127.0.0.1:<port>/action?a=<acct>&u=<uid>&f=<folderId>&t=<token>"
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Visual check without disturbing the desktop: msger render mode loads the same
|
|
105
|
+
URL in a hidden window and writes a PNG —
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
node --input-type=module -e "import {showMessageBox} from '@bobfrankston/msger';
|
|
109
|
+
await showMessageBox({url:'http://127.0.0.1:<port>/v?...&t=<token>',
|
|
110
|
+
size:{width:820,height:900}, render:'c:/temp/popout.png', renderDelay:800});"
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Success signals in the log: `[popout] action <name> a=... u=...` on the daemon
|
|
114
|
+
side, then `[client] compose-tick ... mode=reply ... compose visible`.
|