@bobfrankston/rmfmail 1.2.331 → 1.2.333

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.
@@ -501,14 +501,44 @@ export async function createTinyMceEditor(container, opts = {}) {
501
501
  // image"). Restore blob→data on EVERY GetContent, source view
502
502
  // included; a source edit saved back re-enters as data: and
503
503
  // round-trips cleanly.
504
+ const restoreBlobSrcs = (html) => html.replace(/src="(blob:[^"]+)"/g, (m, uri) => {
505
+ const info = ed.editorUpload?.blobCache?.getByUri?.(uri);
506
+ return info ? `src="data:${info.blob().type};base64,${info.base64()}"` : m;
507
+ });
504
508
  ed.on("GetContent", (e) => {
505
509
  if (!e?.content || typeof e.content !== "string" || !e.content.includes("blob:"))
506
510
  return;
507
- e.content = e.content.replace(/src="(blob:[^"]+)"/g, (m, uri) => {
508
- const info = ed.editorUpload?.blobCache?.getByUri?.(uri);
509
- return info ? `src="data:${info.blob().type};base64,${info.base64()}"` : m;
510
- });
511
+ e.content = restoreBlobSrcs(e.content);
511
512
  });
513
+ // 2026-09-16 — Claude Code (Fable 5.1), at Bob's direction.
514
+ // Native Ctrl+C / Ctrl+X serialize the live DOM, where those
515
+ // same images are `src="blob:..."` — a URL scoped to this
516
+ // editor session. Pasted anywhere else (another compose, Word,
517
+ // a browser) the image is dead (Bob 2026-09-15: "^a^c loses
518
+ // the <img"). Only when the selection carries a blob: src do
519
+ // we take over: same blob→data restore on the copied HTML,
520
+ // plain-text flavour alongside. Selections without blob:
521
+ // images keep the browser's native copy untouched.
522
+ const copyWithImageData = (e) => {
523
+ if (!e?.clipboardData)
524
+ return;
525
+ let html = "";
526
+ try {
527
+ html = ed.selection.getContent({ format: "html" }) || "";
528
+ }
529
+ catch {
530
+ return;
531
+ } // selection API unavailable → native copy
532
+ if (!html.includes("blob:"))
533
+ return;
534
+ e.preventDefault();
535
+ e.clipboardData.setData("text/html", restoreBlobSrcs(html));
536
+ e.clipboardData.setData("text/plain", ed.selection.getContent({ format: "text" }) || "");
537
+ if (e.type === "cut")
538
+ ed.execCommand("Delete");
539
+ };
540
+ ed.on("copy", copyWithImageData);
541
+ ed.on("cut", copyWithImageData);
512
542
  // Source-Code dialog opens with the caret at the END of the
513
543
  // HTML (TinyMCE focuses + selects-to-end), so a long message
514
544
  // greets the user scrolled to the BOTTOM (Bob 2026-07-12).
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/mailx-client",
3
- "version": "1.0.47",
3
+ "version": "1.0.48",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "scripts": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/rmfmail",
3
- "version": "1.2.331",
3
+ "version": "1.2.333",
4
4
  "description": "Local-first email client with IMAP sync and standalone native app",
5
5
  "type": "module",
6
6
  "main": "bin/mailx.js",
@@ -41,7 +41,7 @@
41
41
  "@bobfrankston/msger": "^0.1.431",
42
42
  "@bobfrankston/node-tcp-transport": "^0.1.10",
43
43
  "@bobfrankston/oauthsupport": "^1.0.35",
44
- "@bobfrankston/rmf-tiny": "^0.1.54",
44
+ "@bobfrankston/rmf-tiny": "^0.1.55",
45
45
  "@bobfrankston/smtp-direct": "^0.1.9",
46
46
  "@bobfrankston/tcp-transport": "^0.1.8",
47
47
  "@capacitor/android": "^8.3.0",
@@ -126,7 +126,7 @@
126
126
  "@bobfrankston/msger": "^0.1.431",
127
127
  "@bobfrankston/node-tcp-transport": "^0.1.10",
128
128
  "@bobfrankston/oauthsupport": "^1.0.35",
129
- "@bobfrankston/rmf-tiny": "^0.1.54",
129
+ "@bobfrankston/rmf-tiny": "^0.1.55",
130
130
  "@bobfrankston/smtp-direct": "^0.1.9",
131
131
  "@bobfrankston/tcp-transport": "^0.1.8",
132
132
  "@capacitor/android": "^8.3.0",