@bobfrankston/rmfmail 1.2.120 → 1.2.121
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/client/lib/rmf-tiny.js
CHANGED
|
@@ -285,6 +285,43 @@ export async function createTinyMceEditor(container, opts = {}) {
|
|
|
285
285
|
setup: (ed) => {
|
|
286
286
|
if (opts.initialHtml)
|
|
287
287
|
ed.on("init", () => ed.setContent(opts.initialHtml));
|
|
288
|
+
// Never emit session-scoped blob: URIs from getContent. TinyMCE
|
|
289
|
+
// converts inserted/pasted data: images into blob-cache entries
|
|
290
|
+
// and renders `src="blob:..."` in the editor (and re-blobs the
|
|
291
|
+
// body on every getContent scan, so rewriting the DOM doesn't
|
|
292
|
+
// stick). Plain getContent() restores data: — but the
|
|
293
|
+
// Source-Code dialog uses source_view getContent, which SKIPS
|
|
294
|
+
// the restore, so View→Source showed `src="blob:...:0"` plus
|
|
295
|
+
// the filename alt and read as a dead image (Bob 2026-07-12
|
|
296
|
+
// "insert image does not work ... I see the name for the
|
|
297
|
+
// image"). Restore blob→data on EVERY GetContent, source view
|
|
298
|
+
// included; a source edit saved back re-enters as data: and
|
|
299
|
+
// round-trips cleanly.
|
|
300
|
+
ed.on("GetContent", (e) => {
|
|
301
|
+
if (!e?.content || typeof e.content !== "string" || !e.content.includes("blob:"))
|
|
302
|
+
return;
|
|
303
|
+
e.content = e.content.replace(/src="(blob:[^"]+)"/g, (m, uri) => {
|
|
304
|
+
const info = ed.editorUpload?.blobCache?.getByUri?.(uri);
|
|
305
|
+
return info ? `src="data:${info.blob().type};base64,${info.base64()}"` : m;
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
// Source-Code dialog opens with the caret at the END of the
|
|
309
|
+
// HTML (TinyMCE focuses + selects-to-end), so a long message
|
|
310
|
+
// greets the user scrolled to the BOTTOM (Bob 2026-07-12).
|
|
311
|
+
// Put the caret at the top instead. requestAnimationFrame ×2:
|
|
312
|
+
// the dialog's own focus handler runs after OpenWindow fires.
|
|
313
|
+
ed.on("OpenWindow", () => {
|
|
314
|
+
requestAnimationFrame(() => requestAnimationFrame(() => {
|
|
315
|
+
const ta = document.querySelector(".tox-dialog textarea");
|
|
316
|
+
if (ta && ta.value) {
|
|
317
|
+
try {
|
|
318
|
+
ta.setSelectionRange(0, 0);
|
|
319
|
+
ta.scrollTop = 0;
|
|
320
|
+
}
|
|
321
|
+
catch { /* not the code dialog */ }
|
|
322
|
+
}
|
|
323
|
+
}));
|
|
324
|
+
});
|
|
288
325
|
// Zoom — content font-size scales between ZOOM_MIN and ZOOM_MAX.
|
|
289
326
|
// Reachable via View → Zoom in/out/reset, Ctrl+wheel inside the
|
|
290
327
|
// editor iframe, and Ctrl+= / Ctrl+- / Ctrl+0 shortcuts.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/rmfmail",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.121",
|
|
4
4
|
"description": "Local-first email client with IMAP sync and standalone native app",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "bin/mailx.js",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"@bobfrankston/msger": "^0.1.389",
|
|
43
43
|
"@bobfrankston/node-tcp-transport": "^0.1.10",
|
|
44
44
|
"@bobfrankston/oauthsupport": "^1.0.33",
|
|
45
|
-
"@bobfrankston/rmf-tiny": "^0.1.
|
|
45
|
+
"@bobfrankston/rmf-tiny": "^0.1.38",
|
|
46
46
|
"@bobfrankston/smtp-direct": "^0.1.9",
|
|
47
47
|
"@bobfrankston/tcp-transport": "^0.1.7",
|
|
48
48
|
"@capacitor/android": "^8.3.0",
|
|
@@ -120,7 +120,7 @@
|
|
|
120
120
|
"@bobfrankston/msger": "^0.1.389",
|
|
121
121
|
"@bobfrankston/node-tcp-transport": "^0.1.10",
|
|
122
122
|
"@bobfrankston/oauthsupport": "^1.0.33",
|
|
123
|
-
"@bobfrankston/rmf-tiny": "^0.1.
|
|
123
|
+
"@bobfrankston/rmf-tiny": "^0.1.38",
|
|
124
124
|
"@bobfrankston/smtp-direct": "^0.1.9",
|
|
125
125
|
"@bobfrankston/tcp-transport": "^0.1.7",
|
|
126
126
|
"@capacitor/android": "^8.3.0",
|