@bobfrankston/rmfmail 1.2.221 → 1.2.223
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 +27 -18
- package/bin/mailx.js.map +1 -1
- package/bin/mailx.ts +28 -18
- package/client/app.bundle.js +79 -1
- 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/components/context-menu.js +76 -0
- package/client/components/context-menu.js.map +1 -1
- package/client/components/context-menu.ts +77 -0
- package/client/compose/compose.bundle.js +281 -57
- package/client/compose/compose.bundle.js.map +4 -4
- package/client/compose/compose.js +4 -59
- package/client/compose/compose.js.map +1 -1
- package/client/compose/compose.ts +4 -32
- package/client/compose/edit-commands.js +207 -0
- package/client/compose/edit-commands.js.map +1 -0
- package/client/compose/edit-commands.ts +181 -0
- package/client/compose/editor.js +6 -0
- package/client/compose/editor.js.map +1 -1
- package/client/compose/editor.ts +6 -0
- package/client/compose/spellcheck.js +19 -0
- package/client/compose/spellcheck.js.map +1 -1
- package/client/compose/spellcheck.ts +11 -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-65000}/.package-lock.json +0 -0
|
@@ -1983,6 +1983,188 @@ var init_spellcheck_core = __esm({
|
|
|
1983
1983
|
}
|
|
1984
1984
|
});
|
|
1985
1985
|
|
|
1986
|
+
// client/compose/edit-commands.js
|
|
1987
|
+
var edit_commands_exports = {};
|
|
1988
|
+
__export(edit_commands_exports, {
|
|
1989
|
+
runClipboardCommand: () => runClipboardCommand,
|
|
1990
|
+
runFormatCommand: () => runFormatCommand,
|
|
1991
|
+
standardEditItems: () => standardEditItems
|
|
1992
|
+
});
|
|
1993
|
+
function isTinyEditor(ne) {
|
|
1994
|
+
return !!ne && typeof ne.execCommand === "function" && !!ne.selection;
|
|
1995
|
+
}
|
|
1996
|
+
function expandToWord(ne) {
|
|
1997
|
+
if (isTinyEditor(ne)) {
|
|
1998
|
+
try {
|
|
1999
|
+
if (ne.selection.isCollapsed() && typeof ne.selection.expand === "function") {
|
|
2000
|
+
ne.selection.expand({ type: "word" });
|
|
2001
|
+
}
|
|
2002
|
+
} catch {
|
|
2003
|
+
}
|
|
2004
|
+
return;
|
|
2005
|
+
}
|
|
2006
|
+
if (ne && typeof ne.getSelection === "function" && typeof ne.setSelection === "function") {
|
|
2007
|
+
const sel = ne.getSelection();
|
|
2008
|
+
if (!sel || sel.length > 0)
|
|
2009
|
+
return;
|
|
2010
|
+
const text = ne.getText();
|
|
2011
|
+
let a = sel.index, b = sel.index;
|
|
2012
|
+
while (a > 0 && /\S/.test(text[a - 1]))
|
|
2013
|
+
a--;
|
|
2014
|
+
while (b < text.length && /\S/.test(text[b]))
|
|
2015
|
+
b++;
|
|
2016
|
+
if (b > a)
|
|
2017
|
+
ne.setSelection(a, b - a);
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
function escapeHtml(s) {
|
|
2021
|
+
return s.replace(/[&<>]/g, (c) => ({ "&": "&", "<": "<", ">": ">" })[c]);
|
|
2022
|
+
}
|
|
2023
|
+
async function runClipboardCommand(ne, id) {
|
|
2024
|
+
const isTiny = isTinyEditor(ne);
|
|
2025
|
+
if (id === "paste") {
|
|
2026
|
+
let html2 = "";
|
|
2027
|
+
let text2 = "";
|
|
2028
|
+
if (navigator.clipboard?.read) {
|
|
2029
|
+
for (const item of await navigator.clipboard.read()) {
|
|
2030
|
+
if (item.types.includes("text/html"))
|
|
2031
|
+
html2 = await (await item.getType("text/html")).text();
|
|
2032
|
+
if (item.types.includes("text/plain"))
|
|
2033
|
+
text2 = await (await item.getType("text/plain")).text();
|
|
2034
|
+
}
|
|
2035
|
+
} else {
|
|
2036
|
+
text2 = await navigator.clipboard.readText();
|
|
2037
|
+
}
|
|
2038
|
+
const content = html2 || (text2 ? escapeHtml(text2).replace(/\r?\n/g, "<br>") : "");
|
|
2039
|
+
if (!content)
|
|
2040
|
+
return;
|
|
2041
|
+
if (isTiny) {
|
|
2042
|
+
ne.execCommand("mceInsertContent", false, content);
|
|
2043
|
+
return;
|
|
2044
|
+
}
|
|
2045
|
+
if (ne?.clipboard?.dangerouslyPasteHTML) {
|
|
2046
|
+
const sel = ne.getSelection(true);
|
|
2047
|
+
ne.clipboard.dangerouslyPasteHTML(sel?.index ?? 0, content);
|
|
2048
|
+
return;
|
|
2049
|
+
}
|
|
2050
|
+
throw new Error("editor doesn't support paste");
|
|
2051
|
+
}
|
|
2052
|
+
expandToWord(ne);
|
|
2053
|
+
let html = "";
|
|
2054
|
+
let text = "";
|
|
2055
|
+
if (isTiny) {
|
|
2056
|
+
html = ne.selection.getContent({ format: "html" });
|
|
2057
|
+
text = ne.selection.getContent({ format: "text" });
|
|
2058
|
+
} else {
|
|
2059
|
+
text = window.getSelection()?.toString() || "";
|
|
2060
|
+
}
|
|
2061
|
+
if (!text && !html)
|
|
2062
|
+
return;
|
|
2063
|
+
if (html && typeof ClipboardItem === "function" && navigator.clipboard?.write) {
|
|
2064
|
+
await navigator.clipboard.write([new ClipboardItem({
|
|
2065
|
+
"text/html": new Blob([html], { type: "text/html" }),
|
|
2066
|
+
"text/plain": new Blob([text], { type: "text/plain" })
|
|
2067
|
+
})]);
|
|
2068
|
+
} else {
|
|
2069
|
+
await navigator.clipboard.writeText(text);
|
|
2070
|
+
}
|
|
2071
|
+
if (id !== "cut")
|
|
2072
|
+
return;
|
|
2073
|
+
if (isTiny) {
|
|
2074
|
+
ne.execCommand("Delete");
|
|
2075
|
+
return;
|
|
2076
|
+
}
|
|
2077
|
+
if (typeof ne?.deleteText === "function") {
|
|
2078
|
+
const sel = ne.getSelection();
|
|
2079
|
+
if (sel?.length)
|
|
2080
|
+
ne.deleteText(sel.index, sel.length);
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
2083
|
+
function runFormatCommand(ne, id) {
|
|
2084
|
+
if (!ne)
|
|
2085
|
+
return;
|
|
2086
|
+
expandToWord(ne);
|
|
2087
|
+
if (isTinyEditor(ne)) {
|
|
2088
|
+
const cmd = {
|
|
2089
|
+
bold: "Bold",
|
|
2090
|
+
italic: "Italic",
|
|
2091
|
+
underline: "Underline",
|
|
2092
|
+
strike: "Strikethrough",
|
|
2093
|
+
link: "mceLink",
|
|
2094
|
+
clear: "RemoveFormat"
|
|
2095
|
+
};
|
|
2096
|
+
if (cmd[id])
|
|
2097
|
+
ne.execCommand(cmd[id]);
|
|
2098
|
+
return;
|
|
2099
|
+
}
|
|
2100
|
+
if (typeof ne.format !== "function" || typeof ne.getSelection !== "function")
|
|
2101
|
+
return;
|
|
2102
|
+
const sel = ne.getSelection();
|
|
2103
|
+
if (!sel || sel.length === 0)
|
|
2104
|
+
return;
|
|
2105
|
+
const cur = ne.getFormat(sel);
|
|
2106
|
+
switch (id) {
|
|
2107
|
+
case "bold":
|
|
2108
|
+
ne.format("bold", !cur.bold);
|
|
2109
|
+
break;
|
|
2110
|
+
case "italic":
|
|
2111
|
+
ne.format("italic", !cur.italic);
|
|
2112
|
+
break;
|
|
2113
|
+
case "underline":
|
|
2114
|
+
ne.format("underline", !cur.underline);
|
|
2115
|
+
break;
|
|
2116
|
+
case "strike":
|
|
2117
|
+
ne.format("strike", !cur.strike);
|
|
2118
|
+
break;
|
|
2119
|
+
case "clear":
|
|
2120
|
+
ne.removeFormat(sel.index, sel.length);
|
|
2121
|
+
break;
|
|
2122
|
+
// Quill's link dialog lives in its keyboard binding — Ctrl+K.
|
|
2123
|
+
case "link":
|
|
2124
|
+
try {
|
|
2125
|
+
ne.root.dispatchEvent(new KeyboardEvent("keydown", { key: "K", ctrlKey: true, bubbles: true, cancelable: true }));
|
|
2126
|
+
} catch {
|
|
2127
|
+
}
|
|
2128
|
+
break;
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
function standardEditItems(getEditor, opts = {}) {
|
|
2132
|
+
const report = opts.onError || ((m) => console.warn(`[edit-commands] ${m}`));
|
|
2133
|
+
const clip = (id) => () => {
|
|
2134
|
+
void runClipboardCommand(getEditor(), id).catch((e) => {
|
|
2135
|
+
const key = id === "cut" ? "Ctrl+X" : id === "copy" ? "Ctrl+C" : "Ctrl+V";
|
|
2136
|
+
report(`${id[0].toUpperCase()}${id.slice(1)} failed: ${e?.message || e}. ${key} still works.`);
|
|
2137
|
+
});
|
|
2138
|
+
};
|
|
2139
|
+
const fmt = (id) => () => runFormatCommand(getEditor(), id);
|
|
2140
|
+
const items = [
|
|
2141
|
+
{ label: "", action: () => {
|
|
2142
|
+
}, separator: true },
|
|
2143
|
+
{ label: "Cut", action: clip("cut") },
|
|
2144
|
+
{ label: "Copy", action: clip("copy") },
|
|
2145
|
+
{ label: "Paste", action: clip("paste") },
|
|
2146
|
+
{ label: "", action: () => {
|
|
2147
|
+
}, separator: true },
|
|
2148
|
+
{ label: "Bold", action: fmt("bold") },
|
|
2149
|
+
{ label: "Italic", action: fmt("italic") },
|
|
2150
|
+
{ label: "Underline", action: fmt("underline") },
|
|
2151
|
+
{ label: "Strikethrough", action: fmt("strike") },
|
|
2152
|
+
{ label: "Link\u2026", action: fmt("link") },
|
|
2153
|
+
{ label: "Clear formatting", action: fmt("clear") }
|
|
2154
|
+
];
|
|
2155
|
+
if (opts.showSource) {
|
|
2156
|
+
items.push({ label: "", action: () => {
|
|
2157
|
+
}, separator: true });
|
|
2158
|
+
items.push({ label: "View HTML source\u2026", action: opts.showSource });
|
|
2159
|
+
}
|
|
2160
|
+
return items;
|
|
2161
|
+
}
|
|
2162
|
+
var init_edit_commands = __esm({
|
|
2163
|
+
"client/compose/edit-commands.js"() {
|
|
2164
|
+
"use strict";
|
|
2165
|
+
}
|
|
2166
|
+
});
|
|
2167
|
+
|
|
1986
2168
|
// client/lib/rmf-tiny.js
|
|
1987
2169
|
var rmf_tiny_exports = {};
|
|
1988
2170
|
__export(rmf_tiny_exports, {
|
|
@@ -2080,7 +2262,13 @@ async function createTinyMceEditor(container2, opts = {}) {
|
|
|
2080
2262
|
// 2026-06-01). The paste_* OPTIONS below remain valid (core).
|
|
2081
2263
|
plugins: "lists advlist link table code codesample image searchreplace autolink wordcount emoticons charmap insertdatetime quickbars nonbreaking directionality help",
|
|
2082
2264
|
toolbar: [
|
|
2083
|
-
|
|
2265
|
+
// fontsize on the main bar (Bob 2026-08-07: "there should also
|
|
2266
|
+
// be font sizing on the tool bar"). `fontsize` is the v6+ name
|
|
2267
|
+
// for v5's `fontsizeselect`; it renders the size dropdown and
|
|
2268
|
+
// reflects the size at the caret. The Format ▸ Font sizes
|
|
2269
|
+
// submenu had it all along, which is two clicks deeper than a
|
|
2270
|
+
// size control has any business being.
|
|
2271
|
+
"undo redo | fontsize | bold italic underline strikethrough | forecolor backcolor",
|
|
2084
2272
|
// blockquote on the main bar (was quickbar-only) — Bob
|
|
2085
2273
|
// 2026-07-28: pasted/inserted text needs a one-click quote.
|
|
2086
2274
|
"blockquote bullist numlist outdent indent | link table image code rmfcode | emoticons charmap | help"
|
|
@@ -2931,6 +3119,21 @@ function wireSpellcheck(editor2) {
|
|
|
2931
3119
|
scheduleScan();
|
|
2932
3120
|
}
|
|
2933
3121
|
});
|
|
3122
|
+
items.push(...standardEditItems(() => editor2, {
|
|
3123
|
+
onError: (msg) => {
|
|
3124
|
+
try {
|
|
3125
|
+
editor2.notificationManager?.open({ text: msg, type: "error", timeout: 6e3 });
|
|
3126
|
+
} catch {
|
|
3127
|
+
console.warn(msg);
|
|
3128
|
+
}
|
|
3129
|
+
},
|
|
3130
|
+
showSource: () => {
|
|
3131
|
+
try {
|
|
3132
|
+
editor2.execCommand("mceCodeEditor");
|
|
3133
|
+
} catch {
|
|
3134
|
+
}
|
|
3135
|
+
}
|
|
3136
|
+
}));
|
|
2934
3137
|
const iframeEl = editor2.iframeElement;
|
|
2935
3138
|
const rect = iframeEl ? iframeEl.getBoundingClientRect() : { left: 0, top: 0 };
|
|
2936
3139
|
showSuggestionsMenu(document, rect.left + e.clientX, rect.top + e.clientY, items, [iframeDoc]);
|
|
@@ -2941,6 +3144,7 @@ var init_spellcheck = __esm({
|
|
|
2941
3144
|
"client/compose/spellcheck.js"() {
|
|
2942
3145
|
"use strict";
|
|
2943
3146
|
init_spellcheck_core();
|
|
3147
|
+
init_edit_commands();
|
|
2944
3148
|
SCAN_DEBOUNCE_MS = 600;
|
|
2945
3149
|
WAVE = `url("data:image/svg+xml,${encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="6" height="3"><path d="M0 2 Q1.5 0 3 2 T6 2" stroke="#d33" fill="none" stroke-width="1"/></svg>')}")`;
|
|
2946
3150
|
OVERLAY_ID = "mailx-spell-overlay";
|
|
@@ -3935,6 +4139,8 @@ function createQuillEditor(container2) {
|
|
|
3935
4139
|
label: "Ignore (this session)",
|
|
3936
4140
|
action: () => _spellSp.add(word)
|
|
3937
4141
|
});
|
|
4142
|
+
const { standardEditItems: standardEditItems2 } = await Promise.resolve().then(() => (init_edit_commands(), edit_commands_exports));
|
|
4143
|
+
items.push(...standardEditItems2(() => q));
|
|
3938
4144
|
core.showSuggestionsMenu(document, e.clientX, e.clientY, items);
|
|
3939
4145
|
} catch (err) {
|
|
3940
4146
|
console.warn("[spellcheck] right-click handler error:", err?.message || err);
|
|
@@ -4411,12 +4617,21 @@ async function createTinyMceEditor2(container2, opts = {}) {
|
|
|
4411
4617
|
|
|
4412
4618
|
// client/compose/compose.ts
|
|
4413
4619
|
init_api_client();
|
|
4620
|
+
init_edit_commands();
|
|
4414
4621
|
|
|
4415
4622
|
// client/components/context-menu.js
|
|
4416
4623
|
var activeMenu = null;
|
|
4417
4624
|
var dismissListener = null;
|
|
4418
4625
|
var escapeListener = null;
|
|
4626
|
+
var activeSubmenu = null;
|
|
4627
|
+
function closeSubmenu() {
|
|
4628
|
+
if (activeSubmenu) {
|
|
4629
|
+
activeSubmenu.remove();
|
|
4630
|
+
activeSubmenu = null;
|
|
4631
|
+
}
|
|
4632
|
+
}
|
|
4419
4633
|
function closeContextMenu() {
|
|
4634
|
+
closeSubmenu();
|
|
4420
4635
|
if (activeMenu) {
|
|
4421
4636
|
activeMenu.remove();
|
|
4422
4637
|
activeMenu = null;
|
|
@@ -4430,6 +4645,45 @@ function closeContextMenu() {
|
|
|
4430
4645
|
escapeListener = null;
|
|
4431
4646
|
}
|
|
4432
4647
|
}
|
|
4648
|
+
function openSubmenu(parentRow, items) {
|
|
4649
|
+
closeSubmenu();
|
|
4650
|
+
const sub = document.createElement("div");
|
|
4651
|
+
sub.className = "ctx-menu";
|
|
4652
|
+
for (const item of items) {
|
|
4653
|
+
if (item.separator) {
|
|
4654
|
+
const sep = document.createElement("div");
|
|
4655
|
+
sep.className = "ctx-sep";
|
|
4656
|
+
sub.appendChild(sep);
|
|
4657
|
+
continue;
|
|
4658
|
+
}
|
|
4659
|
+
const el = document.createElement("div");
|
|
4660
|
+
el.className = "ctx-item" + (item.disabled ? " ctx-disabled" : "");
|
|
4661
|
+
el.textContent = item.label;
|
|
4662
|
+
if (item.tooltip)
|
|
4663
|
+
el.title = item.tooltip;
|
|
4664
|
+
if (!item.disabled) {
|
|
4665
|
+
el.addEventListener("click", () => {
|
|
4666
|
+
closeContextMenu();
|
|
4667
|
+
item.action();
|
|
4668
|
+
});
|
|
4669
|
+
}
|
|
4670
|
+
sub.appendChild(el);
|
|
4671
|
+
}
|
|
4672
|
+
document.body.appendChild(sub);
|
|
4673
|
+
const anchor = parentRow.getBoundingClientRect();
|
|
4674
|
+
const vw = window.visualViewport?.width ?? window.innerWidth;
|
|
4675
|
+
const vh = window.visualViewport?.height ?? window.innerHeight;
|
|
4676
|
+
const rect = sub.getBoundingClientRect();
|
|
4677
|
+
let left = anchor.right - 2;
|
|
4678
|
+
if (left + rect.width > vw)
|
|
4679
|
+
left = anchor.left - rect.width + 2;
|
|
4680
|
+
let top = anchor.top;
|
|
4681
|
+
if (top + rect.height > vh)
|
|
4682
|
+
top = vh - rect.height - 4;
|
|
4683
|
+
sub.style.left = `${Math.max(4, left)}px`;
|
|
4684
|
+
sub.style.top = `${Math.max(4, top)}px`;
|
|
4685
|
+
activeSubmenu = sub;
|
|
4686
|
+
}
|
|
4433
4687
|
function showContextMenu(x, y, items) {
|
|
4434
4688
|
closeContextMenu();
|
|
4435
4689
|
const menu = document.createElement("div");
|
|
@@ -4448,6 +4702,24 @@ function showContextMenu(x, y, items) {
|
|
|
4448
4702
|
el.textContent = item.label;
|
|
4449
4703
|
if (item.tooltip)
|
|
4450
4704
|
el.title = item.tooltip;
|
|
4705
|
+
if (item.submenu && item.submenu.length > 0 && !item.disabled) {
|
|
4706
|
+
el.style.display = "flex";
|
|
4707
|
+
el.style.justifyContent = "space-between";
|
|
4708
|
+
el.style.gap = "12px";
|
|
4709
|
+
const caret = document.createElement("span");
|
|
4710
|
+
caret.textContent = "\u25B8";
|
|
4711
|
+
caret.style.opacity = "0.6";
|
|
4712
|
+
el.appendChild(caret);
|
|
4713
|
+
const open = () => openSubmenu(el, item.submenu);
|
|
4714
|
+
el.addEventListener("mouseenter", open);
|
|
4715
|
+
el.addEventListener("click", (e) => {
|
|
4716
|
+
e.stopPropagation();
|
|
4717
|
+
open();
|
|
4718
|
+
});
|
|
4719
|
+
menu.appendChild(el);
|
|
4720
|
+
continue;
|
|
4721
|
+
}
|
|
4722
|
+
el.addEventListener("mouseenter", closeSubmenu);
|
|
4451
4723
|
if (!item.disabled) {
|
|
4452
4724
|
el.addEventListener("click", () => {
|
|
4453
4725
|
closeContextMenu();
|
|
@@ -4479,6 +4751,8 @@ function showContextMenu(x, y, items) {
|
|
|
4479
4751
|
activeMenu = menu;
|
|
4480
4752
|
requestAnimationFrame(() => {
|
|
4481
4753
|
dismissListener = (e) => {
|
|
4754
|
+
if (activeSubmenu?.contains(e.target))
|
|
4755
|
+
return;
|
|
4482
4756
|
if (activeMenu && !activeMenu.contains(e.target)) {
|
|
4483
4757
|
closeContextMenu();
|
|
4484
4758
|
}
|
|
@@ -5407,7 +5681,7 @@ function applyInit(init) {
|
|
|
5407
5681
|
if (init.mode !== "draft" && !init.draftUid) {
|
|
5408
5682
|
let sigHtml = "";
|
|
5409
5683
|
if (acct?.sig?.text) {
|
|
5410
|
-
sigHtml = acct.sig.html ? acct.sig.text :
|
|
5684
|
+
sigHtml = acct.sig.html ? acct.sig.text : escapeHtml2(acct.sig.text).replace(/\n/g, "<br>");
|
|
5411
5685
|
} else if (acct?.signature) {
|
|
5412
5686
|
sigHtml = acct.signature;
|
|
5413
5687
|
}
|
|
@@ -6102,57 +6376,7 @@ window.__msgerContextCommand = (id) => {
|
|
|
6102
6376
|
}
|
|
6103
6377
|
return;
|
|
6104
6378
|
}
|
|
6105
|
-
|
|
6106
|
-
if (!ne) return;
|
|
6107
|
-
const isTiny = typeof ne.execCommand === "function" && ne.selection;
|
|
6108
|
-
if (isTiny) {
|
|
6109
|
-
try {
|
|
6110
|
-
if (ne.selection.isCollapsed() && typeof ne.selection.expand === "function") ne.selection.expand({ type: "word" });
|
|
6111
|
-
} catch {
|
|
6112
|
-
}
|
|
6113
|
-
const cmd = { bold: "Bold", italic: "Italic", underline: "Underline", strike: "Strikethrough", link: "mceLink", clear: "RemoveFormat" };
|
|
6114
|
-
if (cmd[id]) ne.execCommand(cmd[id]);
|
|
6115
|
-
return;
|
|
6116
|
-
}
|
|
6117
|
-
if (typeof ne.format === "function" && typeof ne.getSelection === "function") {
|
|
6118
|
-
let sel = ne.getSelection();
|
|
6119
|
-
if (sel && sel.length === 0) {
|
|
6120
|
-
const text = ne.getText();
|
|
6121
|
-
let a = sel.index, b = sel.index;
|
|
6122
|
-
while (a > 0 && /S/.test(text[a - 1])) a--;
|
|
6123
|
-
while (b < text.length && /S/.test(text[b])) b++;
|
|
6124
|
-
if (b > a) {
|
|
6125
|
-
ne.setSelection(a, b - a);
|
|
6126
|
-
sel = { index: a, length: b - a };
|
|
6127
|
-
}
|
|
6128
|
-
}
|
|
6129
|
-
if (!sel || sel.length === 0) return;
|
|
6130
|
-
const cur = ne.getFormat(sel);
|
|
6131
|
-
switch (id) {
|
|
6132
|
-
case "bold":
|
|
6133
|
-
ne.format("bold", !cur.bold);
|
|
6134
|
-
break;
|
|
6135
|
-
case "italic":
|
|
6136
|
-
ne.format("italic", !cur.italic);
|
|
6137
|
-
break;
|
|
6138
|
-
case "underline":
|
|
6139
|
-
ne.format("underline", !cur.underline);
|
|
6140
|
-
break;
|
|
6141
|
-
case "strike":
|
|
6142
|
-
ne.format("strike", !cur.strike);
|
|
6143
|
-
break;
|
|
6144
|
-
case "clear":
|
|
6145
|
-
ne.removeFormat(sel.index, sel.length);
|
|
6146
|
-
break;
|
|
6147
|
-
// link: Quill's dialog lives in its keyboard binding — Ctrl+K.
|
|
6148
|
-
case "link":
|
|
6149
|
-
try {
|
|
6150
|
-
ne.root.dispatchEvent(new KeyboardEvent("keydown", { key: "K", ctrlKey: true, bubbles: true, cancelable: true }));
|
|
6151
|
-
} catch {
|
|
6152
|
-
}
|
|
6153
|
-
break;
|
|
6154
|
-
}
|
|
6155
|
-
}
|
|
6379
|
+
runFormatCommand(editor?.nativeEditor, id);
|
|
6156
6380
|
};
|
|
6157
6381
|
var ccRow = document.getElementById("compose-cc-row");
|
|
6158
6382
|
var bccRow = document.getElementById("compose-bcc-row");
|
|
@@ -6185,7 +6409,7 @@ function renderAttachmentChips() {
|
|
|
6185
6409
|
const a = attachments[i];
|
|
6186
6410
|
const chip = document.createElement("span");
|
|
6187
6411
|
chip.className = "compose-att-chip";
|
|
6188
|
-
chip.innerHTML = `\u{1F4CE} ${
|
|
6412
|
+
chip.innerHTML = `\u{1F4CE} ${escapeHtml2(a.filename)} (${formatSize(a.size)}) `;
|
|
6189
6413
|
const rm = document.createElement("button");
|
|
6190
6414
|
rm.type = "button";
|
|
6191
6415
|
rm.title = "Remove attachment";
|
|
@@ -6198,7 +6422,7 @@ function renderAttachmentChips() {
|
|
|
6198
6422
|
attEl.appendChild(chip);
|
|
6199
6423
|
}
|
|
6200
6424
|
}
|
|
6201
|
-
function
|
|
6425
|
+
function escapeHtml2(s) {
|
|
6202
6426
|
return s.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
|
6203
6427
|
}
|
|
6204
6428
|
function formatSize(n) {
|
|
@@ -6340,9 +6564,9 @@ function showExternalEditHint(editorLabel) {
|
|
|
6340
6564
|
const panel = document.createElement("div");
|
|
6341
6565
|
panel.style.cssText = "background:var(--color-bg, #fff);color:var(--color-text, #000);border:1px solid var(--color-border, #ccc);border-radius:8px;padding:18px 22px;max-width:480px;box-shadow:0 8px 32px rgba(0,0,0,0.4);font:14px/1.5 system-ui;";
|
|
6342
6566
|
panel.innerHTML = `
|
|
6343
|
-
<div style="font-weight:600;font-size:16px;margin-bottom:10px;">Editing in ${
|
|
6567
|
+
<div style="font-weight:600;font-size:16px;margin-bottom:10px;">Editing in ${escapeHtml2(editorLabel)}</div>
|
|
6344
6568
|
<ol style="margin:0 0 12px 18px;padding:0;">
|
|
6345
|
-
<li>Edit your message in <b>${
|
|
6569
|
+
<li>Edit your message in <b>${escapeHtml2(editorLabel)}</b>.</li>
|
|
6346
6570
|
<li>Save (<b>Ctrl+S</b>). Choose <b>"Web Page, Filtered (.htm)"</b> if asked for a format \u2014 keep the same filename.</li>
|
|
6347
6571
|
<li>Switch back to this window (<b>Alt+Tab</b>). The body will reload here.</li>
|
|
6348
6572
|
<li>Click <b>Send</b> in this window when ready.</li>
|