@bobfrankston/rmfmail 1.2.221 → 1.2.222
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/bin/mailx.js +30 -18
- package/bin/mailx.js.map +1 -1
- package/bin/mailx.ts +31 -18
- package/client/app.bundle.js +11 -0
- package/client/app.bundle.js.map +2 -2
- package/client/app.js +21 -0
- package/client/app.js.map +1 -1
- package/client/app.ts +21 -0
- package/client/compose/compose.bundle.js +65 -1
- package/client/compose/compose.bundle.js.map +3 -3
- package/client/compose/compose.js +90 -0
- package/client/compose/compose.js.map +1 -1
- package/client/compose/compose.ts +72 -0
- package/client/lib/rmf-tiny.js +7 -1
- package/package.json +3 -3
- /package/packages/mailx-imap/{node_modules.npmglobalize-stash-39780 → node_modules.npmglobalize-stash-104584}/.package-lock.json +0 -0
package/client/app.ts
CHANGED
|
@@ -1449,6 +1449,27 @@ function showComposeOverlay(title = "Compose"): HTMLIFrameElement {
|
|
|
1449
1449
|
return frame;
|
|
1450
1450
|
}
|
|
1451
1451
|
|
|
1452
|
+
// Native context-menu commands land in the TOP frame: msger dispatches them
|
|
1453
|
+
// with CoreWebView2.ExecuteScript, which always runs against the top-level
|
|
1454
|
+
// document. But the in-window compose is an IFRAME, and the handler that owns
|
|
1455
|
+
// the semantics (__msgerContextCommand) is defined inside it — so every
|
|
1456
|
+
// right-click item in the in-window compose hit msger's `window.__msgerContext
|
|
1457
|
+
// Command && …` guard, found nothing, and silently did nothing. The items were
|
|
1458
|
+
// registered for this window in v1.2.155; the dispatcher never was. Forward to
|
|
1459
|
+
// the frontmost compose overlay (same origin, so reaching into it is legal).
|
|
1460
|
+
(window as any).__msgerContextCommand = (id: string): void => {
|
|
1461
|
+
const frames = [...document.querySelectorAll<HTMLIFrameElement>(".compose-overlay iframe")];
|
|
1462
|
+
// Frontmost = highest z-index on the wrapper; mousedown maintains it, so
|
|
1463
|
+
// the one the user just right-clicked is the one on top.
|
|
1464
|
+
const target = frames.sort((a, b) => {
|
|
1465
|
+
const z = (el: HTMLIFrameElement) => Number((el.closest(".compose-overlay") as HTMLElement)?.style.zIndex || 0);
|
|
1466
|
+
return z(b) - z(a);
|
|
1467
|
+
})[0];
|
|
1468
|
+
try {
|
|
1469
|
+
(target?.contentWindow as any)?.__msgerContextCommand?.(id);
|
|
1470
|
+
} catch { /* frame torn down mid-click */ }
|
|
1471
|
+
};
|
|
1472
|
+
|
|
1452
1473
|
/** Drop a transparent full-viewport shield in front of every other element
|
|
1453
1474
|
* so mousemove events stay in the document during a drag. Setting
|
|
1454
1475
|
* pointer-events:none on the compose iframe alone wasn't enough — the
|
|
@@ -2080,7 +2080,13 @@ async function createTinyMceEditor(container2, opts = {}) {
|
|
|
2080
2080
|
// 2026-06-01). The paste_* OPTIONS below remain valid (core).
|
|
2081
2081
|
plugins: "lists advlist link table code codesample image searchreplace autolink wordcount emoticons charmap insertdatetime quickbars nonbreaking directionality help",
|
|
2082
2082
|
toolbar: [
|
|
2083
|
-
|
|
2083
|
+
// fontsize on the main bar (Bob 2026-08-07: "there should also
|
|
2084
|
+
// be font sizing on the tool bar"). `fontsize` is the v6+ name
|
|
2085
|
+
// for v5's `fontsizeselect`; it renders the size dropdown and
|
|
2086
|
+
// reflects the size at the caret. The Format ▸ Font sizes
|
|
2087
|
+
// submenu had it all along, which is two clicks deeper than a
|
|
2088
|
+
// size control has any business being.
|
|
2089
|
+
"undo redo | fontsize | bold italic underline strikethrough | forecolor backcolor",
|
|
2084
2090
|
// blockquote on the main bar (was quickbar-only) — Bob
|
|
2085
2091
|
// 2026-07-28: pasted/inserted text needs a one-click quote.
|
|
2086
2092
|
"blockquote bullist numlist outdent indent | link table image code rmfcode | emoticons charmap | help"
|
|
@@ -6094,6 +6100,60 @@ async function popoutHandoff() {
|
|
|
6094
6100
|
window.addEventListener("compose-popout", () => {
|
|
6095
6101
|
void popoutHandoff();
|
|
6096
6102
|
});
|
|
6103
|
+
async function runClipboardCommand(id) {
|
|
6104
|
+
const ne = editor?.nativeEditor;
|
|
6105
|
+
const isTiny = !!ne && typeof ne.execCommand === "function" && !!ne.selection;
|
|
6106
|
+
const label = id === "cut" ? "Cut" : id === "copy" ? "Copy" : "Paste";
|
|
6107
|
+
const shortcut = id === "cut" ? "Ctrl+X" : id === "copy" ? "Ctrl+C" : "Ctrl+V";
|
|
6108
|
+
try {
|
|
6109
|
+
if (id === "paste") {
|
|
6110
|
+
let html2 = "";
|
|
6111
|
+
let text2 = "";
|
|
6112
|
+
if (navigator.clipboard?.read) {
|
|
6113
|
+
for (const item of await navigator.clipboard.read()) {
|
|
6114
|
+
if (item.types.includes("text/html")) html2 = await (await item.getType("text/html")).text();
|
|
6115
|
+
if (item.types.includes("text/plain")) text2 = await (await item.getType("text/plain")).text();
|
|
6116
|
+
}
|
|
6117
|
+
} else {
|
|
6118
|
+
text2 = await navigator.clipboard.readText();
|
|
6119
|
+
}
|
|
6120
|
+
const content = html2 || (text2 ? text2.replace(/[&<>]/g, (c) => ({ "&": "&", "<": "<", ">": ">" })[c]).replace(/\r?\n/g, "<br>") : "");
|
|
6121
|
+
if (!content) return;
|
|
6122
|
+
if (isTiny) ne.execCommand("mceInsertContent", false, content);
|
|
6123
|
+
else if (ne && typeof ne.getSelection === "function") {
|
|
6124
|
+
const sel = ne.getSelection(true);
|
|
6125
|
+
ne.clipboard?.dangerouslyPasteHTML?.(sel?.index ?? 0, content);
|
|
6126
|
+
} else throw new Error("no editor");
|
|
6127
|
+
return;
|
|
6128
|
+
}
|
|
6129
|
+
if (isTiny) {
|
|
6130
|
+
try {
|
|
6131
|
+
if (ne.selection.isCollapsed() && typeof ne.selection.expand === "function") ne.selection.expand({ type: "word" });
|
|
6132
|
+
} catch {
|
|
6133
|
+
}
|
|
6134
|
+
}
|
|
6135
|
+
const html = isTiny ? ne.selection.getContent({ format: "html" }) : "";
|
|
6136
|
+
const text = isTiny ? ne.selection.getContent({ format: "text" }) : window.getSelection()?.toString() || "";
|
|
6137
|
+
if (!text && !html) return;
|
|
6138
|
+
if (html && typeof ClipboardItem === "function" && navigator.clipboard?.write) {
|
|
6139
|
+
await navigator.clipboard.write([new ClipboardItem({
|
|
6140
|
+
"text/html": new Blob([html], { type: "text/html" }),
|
|
6141
|
+
"text/plain": new Blob([text], { type: "text/plain" })
|
|
6142
|
+
})]);
|
|
6143
|
+
} else {
|
|
6144
|
+
await navigator.clipboard.writeText(text);
|
|
6145
|
+
}
|
|
6146
|
+
if (id === "cut") {
|
|
6147
|
+
if (isTiny) ne.execCommand("Delete");
|
|
6148
|
+
else if (ne && typeof ne.deleteText === "function") {
|
|
6149
|
+
const sel = ne.getSelection();
|
|
6150
|
+
if (sel?.length) ne.deleteText(sel.index, sel.length);
|
|
6151
|
+
}
|
|
6152
|
+
}
|
|
6153
|
+
} catch (e) {
|
|
6154
|
+
showDraftStatus(`${label} failed: ${e?.message || e}. ${shortcut} still works.`, true);
|
|
6155
|
+
}
|
|
6156
|
+
}
|
|
6097
6157
|
window.__msgerContextCommand = (id) => {
|
|
6098
6158
|
if (id === "source") {
|
|
6099
6159
|
try {
|
|
@@ -6102,6 +6162,10 @@ window.__msgerContextCommand = (id) => {
|
|
|
6102
6162
|
}
|
|
6103
6163
|
return;
|
|
6104
6164
|
}
|
|
6165
|
+
if (id === "cut" || id === "copy" || id === "paste") {
|
|
6166
|
+
void runClipboardCommand(id);
|
|
6167
|
+
return;
|
|
6168
|
+
}
|
|
6105
6169
|
const ne = editor?.nativeEditor;
|
|
6106
6170
|
if (!ne) return;
|
|
6107
6171
|
const isTiny = typeof ne.execCommand === "function" && ne.selection;
|