@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.
@@ -3022,14 +3022,34 @@ async function createTinyMceEditor(container2, opts = {}) {
3022
3022
  };
3023
3023
  reader.readAsDataURL(file);
3024
3024
  });
3025
+ const restoreBlobSrcs = (html) => html.replace(/src="(blob:[^"]+)"/g, (m, uri) => {
3026
+ const info = ed.editorUpload?.blobCache?.getByUri?.(uri);
3027
+ return info ? `src="data:${info.blob().type};base64,${info.base64()}"` : m;
3028
+ });
3025
3029
  ed.on("GetContent", (e) => {
3026
3030
  if (!e?.content || typeof e.content !== "string" || !e.content.includes("blob:"))
3027
3031
  return;
3028
- e.content = e.content.replace(/src="(blob:[^"]+)"/g, (m, uri) => {
3029
- const info = ed.editorUpload?.blobCache?.getByUri?.(uri);
3030
- return info ? `src="data:${info.blob().type};base64,${info.base64()}"` : m;
3031
- });
3032
+ e.content = restoreBlobSrcs(e.content);
3032
3033
  });
3034
+ const copyWithImageData = (e) => {
3035
+ if (!e?.clipboardData)
3036
+ return;
3037
+ let html = "";
3038
+ try {
3039
+ html = ed.selection.getContent({ format: "html" }) || "";
3040
+ } catch {
3041
+ return;
3042
+ }
3043
+ if (!html.includes("blob:"))
3044
+ return;
3045
+ e.preventDefault();
3046
+ e.clipboardData.setData("text/html", restoreBlobSrcs(html));
3047
+ e.clipboardData.setData("text/plain", ed.selection.getContent({ format: "text" }) || "");
3048
+ if (e.type === "cut")
3049
+ ed.execCommand("Delete");
3050
+ };
3051
+ ed.on("copy", copyWithImageData);
3052
+ ed.on("cut", copyWithImageData);
3033
3053
  ed.on("OpenWindow", () => {
3034
3054
  requestAnimationFrame(() => requestAnimationFrame(() => {
3035
3055
  const ta = document.querySelector(".tox-dialog textarea");
@@ -5560,6 +5580,7 @@ async function tryEditor(type) {
5560
5580
  }
5561
5581
  } catch {
5562
5582
  }
5583
+ if (ed) wirePasteImageInlining(ed, type);
5563
5584
  return ed;
5564
5585
  } catch (e) {
5565
5586
  logClientEvent("compose-editor-create-failed", { type, error: String(e?.message || e) });
@@ -6495,6 +6516,29 @@ function editorBodyEl() {
6495
6516
  return document.querySelector(".ql-editor") || document.querySelector(".tt-content .tiptap");
6496
6517
  }
6497
6518
  var INLINE_CHECKED_ATTR = "data-mailx-inline-checked";
6519
+ function wirePasteImageInlining(ed, type) {
6520
+ const onPaste = () => {
6521
+ const body2 = editorBodyEl();
6522
+ if (!body2) return;
6523
+ for (const img of Array.from(body2.querySelectorAll(`img:not([${INLINE_CHECKED_ATTR}])`))) {
6524
+ img.setAttribute(INLINE_CHECKED_ATTR, "1");
6525
+ }
6526
+ setTimeout(() => {
6527
+ void inlinePastedRemoteImages();
6528
+ }, 50);
6529
+ setTimeout(() => {
6530
+ void inlinePastedRemoteImages();
6531
+ }, 800);
6532
+ };
6533
+ const native = ed?.nativeEditor;
6534
+ if (type === "tinymce" && native?.on) {
6535
+ native.on("paste", onPaste);
6536
+ return;
6537
+ }
6538
+ const body = editorBodyEl();
6539
+ if (body) body.addEventListener("paste", onPaste);
6540
+ else console.error("[compose] paste-image inlining not wired: no editor body");
6541
+ }
6498
6542
  async function inlinePastedRemoteImages() {
6499
6543
  const body = editorBodyEl();
6500
6544
  if (!body) return;
@@ -6516,7 +6560,6 @@ async function inlinePastedRemoteImages() {
6516
6560
  }
6517
6561
  function scheduleDraftSave() {
6518
6562
  markComposeDirty();
6519
- void inlinePastedRemoteImages();
6520
6563
  if (draftDebounceTimer) clearTimeout(draftDebounceTimer);
6521
6564
  draftDebounceTimer = setTimeout(() => {
6522
6565
  draftDebounceTimer = null;