@bobfrankston/rmfmail 1.2.253 → 1.2.256
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/android-bootstrap.bundle.js +2 -1
- package/client/android-bootstrap.bundle.js.map +2 -2
- package/client/app.bundle.js.map +1 -1
- package/client/compose/compose.bundle.js +77 -1
- package/client/compose/compose.bundle.js.map +2 -2
- package/client/lib/rmf-tiny.js +105 -1
- package/client/package.json +1 -1
- package/package.json +9 -9
- package/packages/mailx-imap/package-lock.json +2 -2
- package/packages/mailx-imap/package.json +1 -1
- package/packages/mailx-settings/package.json +1 -1
- package/packages/mailx-store/package.json +1 -1
- package/packages/mailx-store-web/package.json +1 -1
- package/packages/mailx-types/mime-build.js +8 -1
- package/packages/mailx-types/mime-build.js.map +1 -1
- package/packages/mailx-types/mime-build.ts +8 -1
- package/packages/mailx-types/package.json +1 -1
- /package/packages/mailx-imap/{node_modules.npmglobalize-stash-54924 → node_modules.npmglobalize-stash-47236}/.package-lock.json +0 -0
package/client/lib/rmf-tiny.js
CHANGED
|
@@ -173,7 +173,11 @@ export async function createTinyMceEditor(container, opts = {}) {
|
|
|
173
173
|
// left. Replaced the generic alignleft/center/right icons, which
|
|
174
174
|
// don't communicate wrapping (Bob 2026-07-18: "the align is
|
|
175
175
|
// confusing, better to use the Word icons for the three cases").
|
|
176
|
-
|
|
176
|
+
// …plus a border control (Bob 2026-08-13: "image editing should have
|
|
177
|
+
// an option to put a border around the image. It could be a check box
|
|
178
|
+
// or a width size"). Split button: click = 1px border on/off, ▾ = pick
|
|
179
|
+
// the width. Registered in setup below.
|
|
180
|
+
quickbars_image_toolbar: "rmfwrapinline rmfwrapleft rmfwrapright | rmfborder",
|
|
177
181
|
// Code-sample dropdown languages. TinyMCE's default list omits
|
|
178
182
|
// "Text" / plain — every option triggers syntax highlighting
|
|
179
183
|
// which mangles unrelated paste content (Bob 2026-05-24).
|
|
@@ -192,6 +196,11 @@ export async function createTinyMceEditor(container, opts = {}) {
|
|
|
192
196
|
// OS file-picker the compose Attach button uses (<input type=file>,
|
|
193
197
|
// which works in the msger WebView), then inlines the chosen image as
|
|
194
198
|
// a base64 data URL — matching the drag-drop-an-image behavior.
|
|
199
|
+
// Insert/Edit Image → Advanced tab: border width + style, and the
|
|
200
|
+
// vertical/horizontal spacing that goes with a framed image. This is
|
|
201
|
+
// the precise-control half of the border feature; the quickbar split
|
|
202
|
+
// button below is the one-click half.
|
|
203
|
+
image_advtab: true,
|
|
195
204
|
file_picker_types: "image",
|
|
196
205
|
file_picker_callback: (cb, _value, meta) => {
|
|
197
206
|
if (meta && meta.filetype && meta.filetype !== "image")
|
|
@@ -353,6 +362,74 @@ export async function createTinyMceEditor(container, opts = {}) {
|
|
|
353
362
|
wrapButton("rmfwrapinline", "rmf-wrap-inline", "In line with text", "inline");
|
|
354
363
|
wrapButton("rmfwrapleft", "rmf-wrap-left", "Image left — text wraps around the right", "left");
|
|
355
364
|
wrapButton("rmfwrapright", "rmf-wrap-right", "Image right — text wraps around the left", "right");
|
|
365
|
+
// ── Image border ──
|
|
366
|
+
// Gray rather than black: a border's job here is to separate the
|
|
367
|
+
// image from the text around it, and #888 stays visible whether
|
|
368
|
+
// the recipient reads on a white or a dark background — a black
|
|
369
|
+
// frame disappears into a dark-mode message body.
|
|
370
|
+
const BORDER_COLOR = "#888888";
|
|
371
|
+
const BORDER_WIDTHS = [1, 2, 3, 5];
|
|
372
|
+
ed.ui.registry.addIcon("rmf-border", `<svg width="24" height="24" viewBox="0 0 24 24">`
|
|
373
|
+
+ `<rect x="3.5" y="4.5" width="17" height="15" rx="1" fill="none" stroke="currentColor" stroke-width="2"/>`
|
|
374
|
+
+ arch(12, 16.5)
|
|
375
|
+
+ `</svg>`);
|
|
376
|
+
/** Current visible border width in px — 0 when there is none.
|
|
377
|
+
* A width with `border-style: none` draws nothing, so it counts
|
|
378
|
+
* as no border. */
|
|
379
|
+
const borderWidthOf = (node) => {
|
|
380
|
+
if (!node || node.nodeName !== "IMG")
|
|
381
|
+
return 0;
|
|
382
|
+
const style = node.style?.borderStyle || "";
|
|
383
|
+
if (!style || style === "none")
|
|
384
|
+
return 0;
|
|
385
|
+
const w = parseFloat(node.style?.borderWidth || "");
|
|
386
|
+
return Number.isFinite(w) ? w : 0;
|
|
387
|
+
};
|
|
388
|
+
const applyBorder = (px) => {
|
|
389
|
+
const node = ed.selection.getNode();
|
|
390
|
+
if (!node || node.nodeName !== "IMG")
|
|
391
|
+
return;
|
|
392
|
+
ed.undoManager.transact(() => {
|
|
393
|
+
ed.dom.setStyles(node, px > 0
|
|
394
|
+
? { border: `${px}px solid ${BORDER_COLOR}` }
|
|
395
|
+
: { border: "" });
|
|
396
|
+
});
|
|
397
|
+
ed.nodeChanged();
|
|
398
|
+
};
|
|
399
|
+
ed.ui.registry.addSplitButton("rmfborder", {
|
|
400
|
+
icon: "rmf-border",
|
|
401
|
+
tooltip: "Border around the image — click to toggle, ▾ to pick the width",
|
|
402
|
+
// Click toggles: bordered → none, unbordered → 1px hairline.
|
|
403
|
+
onAction: () => applyBorder(borderWidthOf(ed.selection.getNode()) > 0 ? 0 : 1),
|
|
404
|
+
onItemAction: (_api, value) => applyBorder(Number(value)),
|
|
405
|
+
select: (value) => Number(value) === borderWidthOf(ed.selection.getNode()),
|
|
406
|
+
fetch: (cb) => cb([
|
|
407
|
+
{ type: "choiceitem", text: "No border", value: "0" },
|
|
408
|
+
...BORDER_WIDTHS.map((w) => ({ type: "choiceitem", text: `${w} px`, value: String(w) })),
|
|
409
|
+
]),
|
|
410
|
+
onSetup: (api) => {
|
|
411
|
+
const update = () => api.setActive(borderWidthOf(ed.selection.getNode()) > 0);
|
|
412
|
+
ed.on("NodeChange", update);
|
|
413
|
+
return () => ed.off("NodeChange", update);
|
|
414
|
+
},
|
|
415
|
+
});
|
|
416
|
+
// The image dialog's Advanced tab takes a border WIDTH and a border
|
|
417
|
+
// STYLE as separate fields, and CSS defaults the style to `none` —
|
|
418
|
+
// so typing "2" and leaving the style dropdown alone produced an
|
|
419
|
+
// image with an invisible border and no hint why. A width the user
|
|
420
|
+
// asked for means a border they can see: fill in `solid`.
|
|
421
|
+
ed.on("NodeChange", () => {
|
|
422
|
+
const n = ed.selection.getNode();
|
|
423
|
+
if (!n || n.nodeName !== "IMG")
|
|
424
|
+
return;
|
|
425
|
+
const w = parseFloat(n.style?.borderWidth || "");
|
|
426
|
+
const s = n.style?.borderStyle || "";
|
|
427
|
+
if (Number.isFinite(w) && w > 0 && (!s || s === "none")) {
|
|
428
|
+
ed.dom.setStyle(n, "border-style", "solid");
|
|
429
|
+
if (!n.style.borderColor)
|
|
430
|
+
ed.dom.setStyle(n, "border-color", BORDER_COLOR);
|
|
431
|
+
}
|
|
432
|
+
});
|
|
356
433
|
// Bitmap-first paste. An image copied from a web app (Gemini,
|
|
357
434
|
// browser "Copy image") puts BOTH a bitmap file item and an
|
|
358
435
|
// html flavor on the clipboard — and the html flavor is a
|
|
@@ -920,7 +997,26 @@ export async function createTinyMceEditor(container, opts = {}) {
|
|
|
920
997
|
function openSourceDialog(editor) {
|
|
921
998
|
const SENTINEL = ""; // private-use area — never in real mail text
|
|
922
999
|
let offset = -1;
|
|
1000
|
+
let savedRng = null;
|
|
923
1001
|
try {
|
|
1002
|
+
// The sentinel must be INSERTED, never SUBSTITUTED. selection.setContent()
|
|
1003
|
+
// replaces whatever is selected — and right-clicking an image selects the
|
|
1004
|
+
// <img>, so "view source" swallowed the image (Bob 2026-08-13: "if I right
|
|
1005
|
+
// mouse view source on an image the image disappears"). Same loss for any
|
|
1006
|
+
// non-collapsed text selection, and undoManager.ignore kept it out of the
|
|
1007
|
+
// undo stack, so Ctrl+Z couldn't bring it back either. Collapse to the
|
|
1008
|
+
// range START first (that's the caret the source view should land on), then
|
|
1009
|
+
// restore the user's original selection once the sentinel is out again.
|
|
1010
|
+
const sel = editor.selection;
|
|
1011
|
+
const rng = sel.getRng();
|
|
1012
|
+
if (rng) {
|
|
1013
|
+
savedRng = rng.cloneRange(); // live range — tracks the insert/strip
|
|
1014
|
+
if (!rng.collapsed) {
|
|
1015
|
+
const collapsed = rng.cloneRange();
|
|
1016
|
+
collapsed.collapse(true);
|
|
1017
|
+
sel.setRng(collapsed);
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
924
1020
|
// Insert the sentinel at the caret without polluting undo history.
|
|
925
1021
|
const insert = () => editor.selection.setContent(SENTINEL);
|
|
926
1022
|
if (typeof editor.undoManager?.ignore === "function")
|
|
@@ -945,6 +1041,14 @@ function openSourceDialog(editor) {
|
|
|
945
1041
|
}
|
|
946
1042
|
}
|
|
947
1043
|
catch { /* sentinel cleanup is best-effort */ }
|
|
1044
|
+
// Put the selection back where the user left it (the selected image
|
|
1045
|
+
// stays selected, so closing the dialog leaves the quickbar up).
|
|
1046
|
+
if (savedRng) {
|
|
1047
|
+
try {
|
|
1048
|
+
editor.selection.setRng(savedRng);
|
|
1049
|
+
}
|
|
1050
|
+
catch { /* range invalidated by the strip — caret stays put */ }
|
|
1051
|
+
}
|
|
948
1052
|
}
|
|
949
1053
|
catch { /* fall through to a plain source view at offset 0 */ }
|
|
950
1054
|
// Display the pristine source (sentinel already stripped from the DOM);
|
package/client/package.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/rmfmail",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.256",
|
|
4
4
|
"description": "Local-first email client with IMAP sync and standalone native app",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "bin/mailx.js",
|
|
@@ -34,14 +34,14 @@
|
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@bobfrankston/iflow-direct": "^0.1.65",
|
|
36
36
|
"@bobfrankston/mailx-host": "^0.1.15",
|
|
37
|
-
"@bobfrankston/mailx-imap": "^0.1.
|
|
38
|
-
"@bobfrankston/mailx-store-web": "^0.1.
|
|
37
|
+
"@bobfrankston/mailx-imap": "^0.1.153",
|
|
38
|
+
"@bobfrankston/mailx-store-web": "^0.1.80",
|
|
39
39
|
"@bobfrankston/mailx-sync": "^0.1.29",
|
|
40
40
|
"@bobfrankston/miscinfo": "^1.0.17",
|
|
41
|
-
"@bobfrankston/msger": "^0.1.
|
|
41
|
+
"@bobfrankston/msger": "^0.1.425",
|
|
42
42
|
"@bobfrankston/node-tcp-transport": "^0.1.10",
|
|
43
43
|
"@bobfrankston/oauthsupport": "^1.0.34",
|
|
44
|
-
"@bobfrankston/rmf-tiny": "^0.1.
|
|
44
|
+
"@bobfrankston/rmf-tiny": "^0.1.47",
|
|
45
45
|
"@bobfrankston/smtp-direct": "^0.1.9",
|
|
46
46
|
"@bobfrankston/tcp-transport": "^0.1.8",
|
|
47
47
|
"@capacitor/android": "^8.3.0",
|
|
@@ -114,14 +114,14 @@
|
|
|
114
114
|
"dependencies": {
|
|
115
115
|
"@bobfrankston/iflow-direct": "^0.1.65",
|
|
116
116
|
"@bobfrankston/mailx-host": "^0.1.15",
|
|
117
|
-
"@bobfrankston/mailx-imap": "^0.1.
|
|
118
|
-
"@bobfrankston/mailx-store-web": "^0.1.
|
|
117
|
+
"@bobfrankston/mailx-imap": "^0.1.153",
|
|
118
|
+
"@bobfrankston/mailx-store-web": "^0.1.80",
|
|
119
119
|
"@bobfrankston/mailx-sync": "^0.1.29",
|
|
120
120
|
"@bobfrankston/miscinfo": "^1.0.17",
|
|
121
|
-
"@bobfrankston/msger": "^0.1.
|
|
121
|
+
"@bobfrankston/msger": "^0.1.425",
|
|
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.47",
|
|
125
125
|
"@bobfrankston/smtp-direct": "^0.1.9",
|
|
126
126
|
"@bobfrankston/tcp-transport": "^0.1.8",
|
|
127
127
|
"@capacitor/android": "^8.3.0",
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/mailx-imap",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.153",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bobfrankston/mailx-imap",
|
|
9
|
-
"version": "0.1.
|
|
9
|
+
"version": "0.1.153",
|
|
10
10
|
"license": "ISC",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@bobfrankston/iflow-direct": "^0.1.27",
|
|
@@ -146,8 +146,15 @@ function wrapWithAttachments(commonHeaders, inner, attachments, newBoundary) {
|
|
|
146
146
|
return [...commonHeaders, ...inner.headers].join("\r\n") + `\r\n\r\n` + inner.body;
|
|
147
147
|
}
|
|
148
148
|
const mixedBoundary = newBoundary();
|
|
149
|
+
// RFC 2046: a boundary delimiter is "CRLF--boundary" — it MUST start its
|
|
150
|
+
// own line. The multipart/alternative inner body already ends with CRLF,
|
|
151
|
+
// but a bare quoted-printable body (draft, or a text-only send) does not,
|
|
152
|
+
// which glued `</p>--mailx_abc` together and made mailparser report ZERO
|
|
153
|
+
// attachments even though the bytes were all there. Normalize instead of
|
|
154
|
+
// assuming, so no future inner shape can reintroduce it.
|
|
155
|
+
const endCRLF = (s) => (s.endsWith("\r\n") ? s : s + "\r\n");
|
|
149
156
|
const parts = [
|
|
150
|
-
`--${mixedBoundary}\r\n` + inner.headers.join("\r\n") + `\r\n\r\n` + inner.body,
|
|
157
|
+
`--${mixedBoundary}\r\n` + inner.headers.join("\r\n") + `\r\n\r\n` + endCRLF(inner.body),
|
|
151
158
|
];
|
|
152
159
|
for (const att of attachments) {
|
|
153
160
|
// A quote or newline in a filename would break out of the header.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mime-build.js","sourceRoot":"","sources":["mime-build.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"mime-build.js","sourceRoot":"","sources":["mime-build.ts"],"names":[],"mappings":"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"}
|
|
@@ -209,8 +209,15 @@ function wrapWithAttachments(
|
|
|
209
209
|
return [...commonHeaders, ...inner.headers].join("\r\n") + `\r\n\r\n` + inner.body;
|
|
210
210
|
}
|
|
211
211
|
const mixedBoundary = newBoundary();
|
|
212
|
+
// RFC 2046: a boundary delimiter is "CRLF--boundary" — it MUST start its
|
|
213
|
+
// own line. The multipart/alternative inner body already ends with CRLF,
|
|
214
|
+
// but a bare quoted-printable body (draft, or a text-only send) does not,
|
|
215
|
+
// which glued `</p>--mailx_abc` together and made mailparser report ZERO
|
|
216
|
+
// attachments even though the bytes were all there. Normalize instead of
|
|
217
|
+
// assuming, so no future inner shape can reintroduce it.
|
|
218
|
+
const endCRLF = (s: string): string => (s.endsWith("\r\n") ? s : s + "\r\n");
|
|
212
219
|
const parts: string[] = [
|
|
213
|
-
`--${mixedBoundary}\r\n` + inner.headers.join("\r\n") + `\r\n\r\n` + inner.body,
|
|
220
|
+
`--${mixedBoundary}\r\n` + inner.headers.join("\r\n") + `\r\n\r\n` + endCRLF(inner.body),
|
|
214
221
|
];
|
|
215
222
|
for (const att of attachments) {
|
|
216
223
|
// A quote or newline in a filename would break out of the header.
|