@bobfrankston/rmfmail 1.2.147 → 1.2.149
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/client/compose/compose.bundle.js +70 -7
- package/client/compose/compose.bundle.js.map +2 -2
- package/client/lib/rmf-tiny.js +87 -7
- package/package.json +3 -3
- package/packages/mailx-service/index.d.ts.map +1 -1
- package/packages/mailx-service/index.js +71 -26
- package/packages/mailx-service/index.js.map +1 -1
- package/packages/mailx-service/index.ts +69 -26
- /package/packages/mailx-imap/{node_modules.npmglobalize-stash-69320 → node_modules.npmglobalize-stash-41964}/.package-lock.json +0 -0
|
@@ -2064,7 +2064,12 @@ async function createTinyMceEditor(container2, opts = {}) {
|
|
|
2064
2064
|
// clutters more than it helps in an email composer.
|
|
2065
2065
|
quickbars_selection_toolbar: "bold italic underline | forecolor | quicklink blockquote",
|
|
2066
2066
|
quickbars_insert_toolbar: false,
|
|
2067
|
-
|
|
2067
|
+
// Word-style layout buttons (registered in setup below): in-line
|
|
2068
|
+
// with text, image-left/text-wraps-right, image-right/text-wraps-
|
|
2069
|
+
// left. Replaced the generic alignleft/center/right icons, which
|
|
2070
|
+
// don't communicate wrapping (Bob 2026-07-18: "the align is
|
|
2071
|
+
// confusing, better to use the Word icons for the three cases").
|
|
2072
|
+
quickbars_image_toolbar: "rmfwrapinline rmfwrapleft rmfwrapright",
|
|
2068
2073
|
// Code-sample dropdown languages. TinyMCE's default list omits
|
|
2069
2074
|
// "Text" / plain — every option triggers syntax highlighting
|
|
2070
2075
|
// which mangles unrelated paste content (Bob 2026-05-24).
|
|
@@ -2137,12 +2142,11 @@ async function createTinyMceEditor(container2, opts = {}) {
|
|
|
2137
2142
|
relative_urls: false,
|
|
2138
2143
|
remove_script_host: false,
|
|
2139
2144
|
paste_data_images: true,
|
|
2140
|
-
//
|
|
2141
|
-
//
|
|
2142
|
-
//
|
|
2143
|
-
//
|
|
2144
|
-
|
|
2145
|
-
paste_retain_style_properties: "color background background-color font-family font-size font-weight font-style text-decoration text-align padding padding-top padding-bottom padding-left padding-right margin margin-top margin-bottom margin-left margin-right border border-top border-bottom border-left border-right",
|
|
2145
|
+
// NOTE: paste_word_valid_elements and paste_retain_style_properties
|
|
2146
|
+
// were REMOVED in TinyMCE 8 (core paste keeps formatting by
|
|
2147
|
+
// default now) — passing them only triggered the deprecation
|
|
2148
|
+
// console warning on every compose load. Removed 2026-07-18; if
|
|
2149
|
+
// TinyMCE is ever pinned back to ≤7, restore them from git.
|
|
2146
2150
|
// Auto-link bare URLs in pasted content. TinyMCE's `autolink`
|
|
2147
2151
|
// plugin only fires on TYPED space/enter; URLs that arrive via
|
|
2148
2152
|
// clipboard (browser address bar, terminal copy) come in as
|
|
@@ -2203,6 +2207,65 @@ async function createTinyMceEditor(container2, opts = {}) {
|
|
|
2203
2207
|
setup: (ed) => {
|
|
2204
2208
|
if (opts.initialHtml)
|
|
2205
2209
|
ed.on("init", () => ed.setContent(opts.initialHtml));
|
|
2210
|
+
const ln = (x1, x2, y) => `<rect x="${x1}" y="${y}" width="${x2 - x1}" height="1.5" rx="0.75" fill="currentColor"/>`;
|
|
2211
|
+
const arch = (cx, base) => `<path d="M ${cx - 4.5} ${base} a 4.5 4.5 0 0 1 9 0 z" fill="currentColor"/>`;
|
|
2212
|
+
const svg = (body) => `<svg width="24" height="24" viewBox="0 0 24 24">${body}</svg>`;
|
|
2213
|
+
ed.ui.registry.addIcon("rmf-wrap-inline", svg(ln(3, 21, 4.5) + ln(3, 6.5, 14.5) + arch(12, 16) + ln(17.5, 21, 14.5) + ln(3, 21, 19.5)));
|
|
2214
|
+
ed.ui.registry.addIcon("rmf-wrap-left", svg(ln(3, 21, 4.5) + arch(7.5, 16) + ln(14, 21, 9.5) + ln(14, 21, 14.5) + ln(3, 21, 19.5)));
|
|
2215
|
+
ed.ui.registry.addIcon("rmf-wrap-right", svg(ln(3, 21, 4.5) + ln(3, 10, 9.5) + ln(3, 10, 14.5) + arch(16.5, 16) + ln(3, 21, 19.5)));
|
|
2216
|
+
const applyWrap = (mode) => {
|
|
2217
|
+
const node = ed.selection.getNode();
|
|
2218
|
+
if (!node || node.nodeName !== "IMG")
|
|
2219
|
+
return;
|
|
2220
|
+
ed.undoManager.transact(() => {
|
|
2221
|
+
ed.dom.setStyles(node, {
|
|
2222
|
+
float: mode === "inline" ? "" : mode,
|
|
2223
|
+
margin: mode === "inline" ? "" : mode === "left" ? "0 12px 8px 0" : "0 0 8px 12px"
|
|
2224
|
+
});
|
|
2225
|
+
});
|
|
2226
|
+
ed.nodeChanged();
|
|
2227
|
+
};
|
|
2228
|
+
const wrapButton = (name, icon, tip, mode) => {
|
|
2229
|
+
ed.ui.registry.addToggleButton(name, {
|
|
2230
|
+
icon,
|
|
2231
|
+
tooltip: tip,
|
|
2232
|
+
onAction: () => applyWrap(mode),
|
|
2233
|
+
onSetup: (api) => {
|
|
2234
|
+
const update = () => {
|
|
2235
|
+
const node = ed.selection.getNode();
|
|
2236
|
+
const f = node?.nodeName === "IMG" ? node.style.float || "inline" : "";
|
|
2237
|
+
api.setActive(f === mode || mode === "inline" && f === "none");
|
|
2238
|
+
};
|
|
2239
|
+
ed.on("NodeChange", update);
|
|
2240
|
+
return () => ed.off("NodeChange", update);
|
|
2241
|
+
}
|
|
2242
|
+
});
|
|
2243
|
+
};
|
|
2244
|
+
wrapButton("rmfwrapinline", "rmf-wrap-inline", "In line with text", "inline");
|
|
2245
|
+
wrapButton("rmfwrapleft", "rmf-wrap-left", "Image left \u2014 text wraps around the right", "left");
|
|
2246
|
+
wrapButton("rmfwrapright", "rmf-wrap-right", "Image right \u2014 text wraps around the left", "right");
|
|
2247
|
+
ed.on("paste", (e) => {
|
|
2248
|
+
const cb = e.clipboardData;
|
|
2249
|
+
if (!cb)
|
|
2250
|
+
return;
|
|
2251
|
+
const imgItem = Array.from(cb.items || []).find((i) => i.kind === "file" && i.type.startsWith("image/"));
|
|
2252
|
+
if (!imgItem)
|
|
2253
|
+
return;
|
|
2254
|
+
if ((cb.getData("text/plain") || "").trim())
|
|
2255
|
+
return;
|
|
2256
|
+
const file = imgItem.getAsFile();
|
|
2257
|
+
if (!file)
|
|
2258
|
+
return;
|
|
2259
|
+
e.preventDefault();
|
|
2260
|
+
const reader = new FileReader();
|
|
2261
|
+
reader.onload = () => {
|
|
2262
|
+
try {
|
|
2263
|
+
ed.insertContent(`<img src="${String(reader.result || "")}" alt="${file.name || "pasted image"}">`);
|
|
2264
|
+
} catch {
|
|
2265
|
+
}
|
|
2266
|
+
};
|
|
2267
|
+
reader.readAsDataURL(file);
|
|
2268
|
+
});
|
|
2206
2269
|
ed.on("GetContent", (e) => {
|
|
2207
2270
|
if (!e?.content || typeof e.content !== "string" || !e.content.includes("blob:"))
|
|
2208
2271
|
return;
|