@bobfrankston/rmfmail 1.2.148 → 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/lib/rmf-tiny.js
CHANGED
|
@@ -146,7 +146,12 @@ export async function createTinyMceEditor(container, opts = {}) {
|
|
|
146
146
|
// clutters more than it helps in an email composer.
|
|
147
147
|
quickbars_selection_toolbar: "bold italic underline | forecolor | quicklink blockquote",
|
|
148
148
|
quickbars_insert_toolbar: false,
|
|
149
|
-
|
|
149
|
+
// Word-style layout buttons (registered in setup below): in-line
|
|
150
|
+
// with text, image-left/text-wraps-right, image-right/text-wraps-
|
|
151
|
+
// left. Replaced the generic alignleft/center/right icons, which
|
|
152
|
+
// don't communicate wrapping (Bob 2026-07-18: "the align is
|
|
153
|
+
// confusing, better to use the Word icons for the three cases").
|
|
154
|
+
quickbars_image_toolbar: "rmfwrapinline rmfwrapleft rmfwrapright",
|
|
150
155
|
// Code-sample dropdown languages. TinyMCE's default list omits
|
|
151
156
|
// "Text" / plain — every option triggers syntax highlighting
|
|
152
157
|
// which mangles unrelated paste content (Bob 2026-05-24).
|
|
@@ -284,6 +289,48 @@ export async function createTinyMceEditor(container, opts = {}) {
|
|
|
284
289
|
setup: (ed) => {
|
|
285
290
|
if (opts.initialHtml)
|
|
286
291
|
ed.on("init", () => ed.setContent(opts.initialHtml));
|
|
292
|
+
// ── Image layout buttons (Word "Layout Options" style) ──
|
|
293
|
+
// Icons mimic Word's glyphs: an arch (the image) among text
|
|
294
|
+
// lines, so each button SHOWS its result instead of a generic
|
|
295
|
+
// align arrow. Three cases: in line with text, image left
|
|
296
|
+
// (text wraps right), image right (text wraps left).
|
|
297
|
+
const ln = (x1, x2, y) => `<rect x="${x1}" y="${y}" width="${x2 - x1}" height="1.5" rx="0.75" fill="currentColor"/>`;
|
|
298
|
+
const arch = (cx, base) => `<path d="M ${cx - 4.5} ${base} a 4.5 4.5 0 0 1 9 0 z" fill="currentColor"/>`;
|
|
299
|
+
const svg = (body) => `<svg width="24" height="24" viewBox="0 0 24 24">${body}</svg>`;
|
|
300
|
+
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)));
|
|
301
|
+
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)));
|
|
302
|
+
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)));
|
|
303
|
+
const applyWrap = (mode) => {
|
|
304
|
+
const node = ed.selection.getNode();
|
|
305
|
+
if (!node || node.nodeName !== "IMG")
|
|
306
|
+
return;
|
|
307
|
+
ed.undoManager.transact(() => {
|
|
308
|
+
ed.dom.setStyles(node, {
|
|
309
|
+
float: mode === "inline" ? "" : mode,
|
|
310
|
+
margin: mode === "inline" ? "" :
|
|
311
|
+
mode === "left" ? "0 12px 8px 0" : "0 0 8px 12px",
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
ed.nodeChanged();
|
|
315
|
+
};
|
|
316
|
+
const wrapButton = (name, icon, tip, mode) => {
|
|
317
|
+
ed.ui.registry.addToggleButton(name, {
|
|
318
|
+
icon, tooltip: tip,
|
|
319
|
+
onAction: () => applyWrap(mode),
|
|
320
|
+
onSetup: (api) => {
|
|
321
|
+
const update = () => {
|
|
322
|
+
const node = ed.selection.getNode();
|
|
323
|
+
const f = node?.nodeName === "IMG" ? (node.style.float || "inline") : "";
|
|
324
|
+
api.setActive(f === mode || (mode === "inline" && f === "none"));
|
|
325
|
+
};
|
|
326
|
+
ed.on("NodeChange", update);
|
|
327
|
+
return () => ed.off("NodeChange", update);
|
|
328
|
+
},
|
|
329
|
+
});
|
|
330
|
+
};
|
|
331
|
+
wrapButton("rmfwrapinline", "rmf-wrap-inline", "In line with text", "inline");
|
|
332
|
+
wrapButton("rmfwrapleft", "rmf-wrap-left", "Image left — text wraps around the right", "left");
|
|
333
|
+
wrapButton("rmfwrapright", "rmf-wrap-right", "Image right — text wraps around the left", "right");
|
|
287
334
|
// Bitmap-first paste. An image copied from a web app (Gemini,
|
|
288
335
|
// browser "Copy image") puts BOTH a bitmap file item and an
|
|
289
336
|
// html flavor on the clipboard — and the html flavor is a
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/rmfmail",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.149",
|
|
4
4
|
"description": "Local-first email client with IMAP sync and standalone native app",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "bin/mailx.js",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"@bobfrankston/msger": "^0.1.398",
|
|
44
44
|
"@bobfrankston/node-tcp-transport": "^0.1.10",
|
|
45
45
|
"@bobfrankston/oauthsupport": "^1.0.34",
|
|
46
|
-
"@bobfrankston/rmf-tiny": "^0.1.
|
|
46
|
+
"@bobfrankston/rmf-tiny": "^0.1.40",
|
|
47
47
|
"@bobfrankston/smtp-direct": "^0.1.9",
|
|
48
48
|
"@bobfrankston/tcp-transport": "^0.1.8",
|
|
49
49
|
"@capacitor/android": "^8.3.0",
|
|
@@ -121,7 +121,7 @@
|
|
|
121
121
|
"@bobfrankston/msger": "^0.1.398",
|
|
122
122
|
"@bobfrankston/node-tcp-transport": "^0.1.10",
|
|
123
123
|
"@bobfrankston/oauthsupport": "^1.0.34",
|
|
124
|
-
"@bobfrankston/rmf-tiny": "^0.1.
|
|
124
|
+
"@bobfrankston/rmf-tiny": "^0.1.40",
|
|
125
125
|
"@bobfrankston/smtp-direct": "^0.1.9",
|
|
126
126
|
"@bobfrankston/tcp-transport": "^0.1.8",
|
|
127
127
|
"@capacitor/android": "^8.3.0",
|