@bobfrankston/rmfmail 1.2.222 → 1.2.224
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 +6 -9
- package/bin/mailx.js.map +1 -1
- package/bin/mailx.ts +6 -9
- package/client/android-bootstrap.bundle.js +38 -4
- package/client/android-bootstrap.bundle.js.map +2 -2
- package/client/app.bundle.js +149 -32
- package/client/app.bundle.js.map +2 -2
- package/client/components/address-book.js +93 -29
- package/client/components/address-book.js.map +1 -1
- package/client/components/address-book.ts +103 -24
- 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 +276 -116
- package/client/compose/compose.bundle.js.map +4 -4
- package/client/compose/compose.js +4 -149
- package/client/compose/compose.js.map +1 -1
- package/client/compose/compose.ts +4 -104
- 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/api-client.js +2 -2
- package/client/lib/api-client.js.map +1 -1
- package/client/lib/api-client.ts +2 -2
- package/client/lib/mailxapi.js +2 -2
- package/client/styles/components.css +40 -0
- package/package.json +7 -7
- package/packages/mailx-imap/index.d.ts.map +1 -1
- package/packages/mailx-imap/index.js +32 -4
- package/packages/mailx-imap/index.js.map +1 -1
- package/packages/mailx-imap/index.ts +34 -5
- package/packages/mailx-imap/package-lock.json +2 -2
- package/packages/mailx-imap/package.json +1 -1
- package/packages/mailx-service/index.d.ts +1 -1
- package/packages/mailx-service/index.d.ts.map +1 -1
- package/packages/mailx-service/index.js +33 -4
- package/packages/mailx-service/index.js.map +1 -1
- package/packages/mailx-service/index.ts +28 -4
- package/packages/mailx-service/jsonrpc.js +1 -1
- package/packages/mailx-service/jsonrpc.js.map +1 -1
- package/packages/mailx-service/jsonrpc.ts +1 -1
- package/packages/mailx-service/package.json +1 -1
- package/packages/mailx-store/db.d.ts +30 -10
- package/packages/mailx-store/db.d.ts.map +1 -1
- package/packages/mailx-store/db.js +108 -10
- package/packages/mailx-store/db.js.map +1 -1
- package/packages/mailx-store/db.ts +126 -13
- package/packages/mailx-store/package.json +1 -1
- package/packages/mailx-store-web/android-bootstrap.d.ts.map +1 -1
- package/packages/mailx-store-web/android-bootstrap.js +17 -2
- package/packages/mailx-store-web/android-bootstrap.js.map +1 -1
- package/packages/mailx-store-web/android-bootstrap.ts +17 -2
- package/packages/mailx-store-web/db.d.ts +1 -1
- package/packages/mailx-store-web/db.d.ts.map +1 -1
- package/packages/mailx-store-web/db.js +17 -2
- package/packages/mailx-store-web/db.js.map +1 -1
- package/packages/mailx-store-web/db.ts +18 -3
- package/packages/mailx-store-web/package.json +1 -1
- /package/packages/mailx-imap/{node_modules.npmglobalize-stash-104584 → node_modules.npmglobalize-stash-142920}/.package-lock.json +0 -0
|
@@ -1303,8 +1303,8 @@ function hasBccHistoryTo(email) {
|
|
|
1303
1303
|
function listContacts(query, page = 1, pageSize = 100) {
|
|
1304
1304
|
return ipc().listContacts(query, page, pageSize);
|
|
1305
1305
|
}
|
|
1306
|
-
function upsertContact(name, email) {
|
|
1307
|
-
return ipc().upsertContact(name, email);
|
|
1306
|
+
function upsertContact(name, email, fields) {
|
|
1307
|
+
return ipc().upsertContact(name, email, fields);
|
|
1308
1308
|
}
|
|
1309
1309
|
function deleteContact(email) {
|
|
1310
1310
|
return ipc().deleteContact(email);
|
|
@@ -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, {
|
|
@@ -2937,6 +3119,21 @@ function wireSpellcheck(editor2) {
|
|
|
2937
3119
|
scheduleScan();
|
|
2938
3120
|
}
|
|
2939
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
|
+
}));
|
|
2940
3137
|
const iframeEl = editor2.iframeElement;
|
|
2941
3138
|
const rect = iframeEl ? iframeEl.getBoundingClientRect() : { left: 0, top: 0 };
|
|
2942
3139
|
showSuggestionsMenu(document, rect.left + e.clientX, rect.top + e.clientY, items, [iframeDoc]);
|
|
@@ -2947,6 +3144,7 @@ var init_spellcheck = __esm({
|
|
|
2947
3144
|
"client/compose/spellcheck.js"() {
|
|
2948
3145
|
"use strict";
|
|
2949
3146
|
init_spellcheck_core();
|
|
3147
|
+
init_edit_commands();
|
|
2950
3148
|
SCAN_DEBOUNCE_MS = 600;
|
|
2951
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>')}")`;
|
|
2952
3150
|
OVERLAY_ID = "mailx-spell-overlay";
|
|
@@ -3941,6 +4139,8 @@ function createQuillEditor(container2) {
|
|
|
3941
4139
|
label: "Ignore (this session)",
|
|
3942
4140
|
action: () => _spellSp.add(word)
|
|
3943
4141
|
});
|
|
4142
|
+
const { standardEditItems: standardEditItems2 } = await Promise.resolve().then(() => (init_edit_commands(), edit_commands_exports));
|
|
4143
|
+
items.push(...standardEditItems2(() => q));
|
|
3944
4144
|
core.showSuggestionsMenu(document, e.clientX, e.clientY, items);
|
|
3945
4145
|
} catch (err) {
|
|
3946
4146
|
console.warn("[spellcheck] right-click handler error:", err?.message || err);
|
|
@@ -4417,12 +4617,21 @@ async function createTinyMceEditor2(container2, opts = {}) {
|
|
|
4417
4617
|
|
|
4418
4618
|
// client/compose/compose.ts
|
|
4419
4619
|
init_api_client();
|
|
4620
|
+
init_edit_commands();
|
|
4420
4621
|
|
|
4421
4622
|
// client/components/context-menu.js
|
|
4422
4623
|
var activeMenu = null;
|
|
4423
4624
|
var dismissListener = null;
|
|
4424
4625
|
var escapeListener = null;
|
|
4626
|
+
var activeSubmenu = null;
|
|
4627
|
+
function closeSubmenu() {
|
|
4628
|
+
if (activeSubmenu) {
|
|
4629
|
+
activeSubmenu.remove();
|
|
4630
|
+
activeSubmenu = null;
|
|
4631
|
+
}
|
|
4632
|
+
}
|
|
4425
4633
|
function closeContextMenu() {
|
|
4634
|
+
closeSubmenu();
|
|
4426
4635
|
if (activeMenu) {
|
|
4427
4636
|
activeMenu.remove();
|
|
4428
4637
|
activeMenu = null;
|
|
@@ -4436,6 +4645,45 @@ function closeContextMenu() {
|
|
|
4436
4645
|
escapeListener = null;
|
|
4437
4646
|
}
|
|
4438
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
|
+
}
|
|
4439
4687
|
function showContextMenu(x, y, items) {
|
|
4440
4688
|
closeContextMenu();
|
|
4441
4689
|
const menu = document.createElement("div");
|
|
@@ -4454,6 +4702,24 @@ function showContextMenu(x, y, items) {
|
|
|
4454
4702
|
el.textContent = item.label;
|
|
4455
4703
|
if (item.tooltip)
|
|
4456
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);
|
|
4457
4723
|
if (!item.disabled) {
|
|
4458
4724
|
el.addEventListener("click", () => {
|
|
4459
4725
|
closeContextMenu();
|
|
@@ -4485,6 +4751,8 @@ function showContextMenu(x, y, items) {
|
|
|
4485
4751
|
activeMenu = menu;
|
|
4486
4752
|
requestAnimationFrame(() => {
|
|
4487
4753
|
dismissListener = (e) => {
|
|
4754
|
+
if (activeSubmenu?.contains(e.target))
|
|
4755
|
+
return;
|
|
4488
4756
|
if (activeMenu && !activeMenu.contains(e.target)) {
|
|
4489
4757
|
closeContextMenu();
|
|
4490
4758
|
}
|
|
@@ -5413,7 +5681,7 @@ function applyInit(init) {
|
|
|
5413
5681
|
if (init.mode !== "draft" && !init.draftUid) {
|
|
5414
5682
|
let sigHtml = "";
|
|
5415
5683
|
if (acct?.sig?.text) {
|
|
5416
|
-
sigHtml = acct.sig.html ? acct.sig.text :
|
|
5684
|
+
sigHtml = acct.sig.html ? acct.sig.text : escapeHtml2(acct.sig.text).replace(/\n/g, "<br>");
|
|
5417
5685
|
} else if (acct?.signature) {
|
|
5418
5686
|
sigHtml = acct.signature;
|
|
5419
5687
|
}
|
|
@@ -6100,60 +6368,6 @@ async function popoutHandoff() {
|
|
|
6100
6368
|
window.addEventListener("compose-popout", () => {
|
|
6101
6369
|
void popoutHandoff();
|
|
6102
6370
|
});
|
|
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
|
-
}
|
|
6157
6371
|
window.__msgerContextCommand = (id) => {
|
|
6158
6372
|
if (id === "source") {
|
|
6159
6373
|
try {
|
|
@@ -6162,61 +6376,7 @@ window.__msgerContextCommand = (id) => {
|
|
|
6162
6376
|
}
|
|
6163
6377
|
return;
|
|
6164
6378
|
}
|
|
6165
|
-
|
|
6166
|
-
void runClipboardCommand(id);
|
|
6167
|
-
return;
|
|
6168
|
-
}
|
|
6169
|
-
const ne = editor?.nativeEditor;
|
|
6170
|
-
if (!ne) return;
|
|
6171
|
-
const isTiny = typeof ne.execCommand === "function" && ne.selection;
|
|
6172
|
-
if (isTiny) {
|
|
6173
|
-
try {
|
|
6174
|
-
if (ne.selection.isCollapsed() && typeof ne.selection.expand === "function") ne.selection.expand({ type: "word" });
|
|
6175
|
-
} catch {
|
|
6176
|
-
}
|
|
6177
|
-
const cmd = { bold: "Bold", italic: "Italic", underline: "Underline", strike: "Strikethrough", link: "mceLink", clear: "RemoveFormat" };
|
|
6178
|
-
if (cmd[id]) ne.execCommand(cmd[id]);
|
|
6179
|
-
return;
|
|
6180
|
-
}
|
|
6181
|
-
if (typeof ne.format === "function" && typeof ne.getSelection === "function") {
|
|
6182
|
-
let sel = ne.getSelection();
|
|
6183
|
-
if (sel && sel.length === 0) {
|
|
6184
|
-
const text = ne.getText();
|
|
6185
|
-
let a = sel.index, b = sel.index;
|
|
6186
|
-
while (a > 0 && /S/.test(text[a - 1])) a--;
|
|
6187
|
-
while (b < text.length && /S/.test(text[b])) b++;
|
|
6188
|
-
if (b > a) {
|
|
6189
|
-
ne.setSelection(a, b - a);
|
|
6190
|
-
sel = { index: a, length: b - a };
|
|
6191
|
-
}
|
|
6192
|
-
}
|
|
6193
|
-
if (!sel || sel.length === 0) return;
|
|
6194
|
-
const cur = ne.getFormat(sel);
|
|
6195
|
-
switch (id) {
|
|
6196
|
-
case "bold":
|
|
6197
|
-
ne.format("bold", !cur.bold);
|
|
6198
|
-
break;
|
|
6199
|
-
case "italic":
|
|
6200
|
-
ne.format("italic", !cur.italic);
|
|
6201
|
-
break;
|
|
6202
|
-
case "underline":
|
|
6203
|
-
ne.format("underline", !cur.underline);
|
|
6204
|
-
break;
|
|
6205
|
-
case "strike":
|
|
6206
|
-
ne.format("strike", !cur.strike);
|
|
6207
|
-
break;
|
|
6208
|
-
case "clear":
|
|
6209
|
-
ne.removeFormat(sel.index, sel.length);
|
|
6210
|
-
break;
|
|
6211
|
-
// link: Quill's dialog lives in its keyboard binding — Ctrl+K.
|
|
6212
|
-
case "link":
|
|
6213
|
-
try {
|
|
6214
|
-
ne.root.dispatchEvent(new KeyboardEvent("keydown", { key: "K", ctrlKey: true, bubbles: true, cancelable: true }));
|
|
6215
|
-
} catch {
|
|
6216
|
-
}
|
|
6217
|
-
break;
|
|
6218
|
-
}
|
|
6219
|
-
}
|
|
6379
|
+
runFormatCommand(editor?.nativeEditor, id);
|
|
6220
6380
|
};
|
|
6221
6381
|
var ccRow = document.getElementById("compose-cc-row");
|
|
6222
6382
|
var bccRow = document.getElementById("compose-bcc-row");
|
|
@@ -6249,7 +6409,7 @@ function renderAttachmentChips() {
|
|
|
6249
6409
|
const a = attachments[i];
|
|
6250
6410
|
const chip = document.createElement("span");
|
|
6251
6411
|
chip.className = "compose-att-chip";
|
|
6252
|
-
chip.innerHTML = `\u{1F4CE} ${
|
|
6412
|
+
chip.innerHTML = `\u{1F4CE} ${escapeHtml2(a.filename)} (${formatSize(a.size)}) `;
|
|
6253
6413
|
const rm = document.createElement("button");
|
|
6254
6414
|
rm.type = "button";
|
|
6255
6415
|
rm.title = "Remove attachment";
|
|
@@ -6262,7 +6422,7 @@ function renderAttachmentChips() {
|
|
|
6262
6422
|
attEl.appendChild(chip);
|
|
6263
6423
|
}
|
|
6264
6424
|
}
|
|
6265
|
-
function
|
|
6425
|
+
function escapeHtml2(s) {
|
|
6266
6426
|
return s.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
|
6267
6427
|
}
|
|
6268
6428
|
function formatSize(n) {
|
|
@@ -6404,9 +6564,9 @@ function showExternalEditHint(editorLabel) {
|
|
|
6404
6564
|
const panel = document.createElement("div");
|
|
6405
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;";
|
|
6406
6566
|
panel.innerHTML = `
|
|
6407
|
-
<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>
|
|
6408
6568
|
<ol style="margin:0 0 12px 18px;padding:0;">
|
|
6409
|
-
<li>Edit your message in <b>${
|
|
6569
|
+
<li>Edit your message in <b>${escapeHtml2(editorLabel)}</b>.</li>
|
|
6410
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>
|
|
6411
6571
|
<li>Switch back to this window (<b>Alt+Tab</b>). The body will reload here.</li>
|
|
6412
6572
|
<li>Click <b>Send</b> in this window when ready.</li>
|