@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.
@@ -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.120",
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.37",
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.37",
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",