@bobfrankston/rmfmail 1.2.326 → 1.2.327
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/.commitmsg +9 -0
- package/.llm/config-cache-crash.md +12 -0
- package/client/compose/compose.bundle.js +19 -4
- package/client/compose/compose.bundle.js.map +2 -2
- package/client/compose/compose.js +45 -4
- package/client/compose/compose.js.map +1 -1
- package/client/compose/compose.ts +45 -4
- package/package.json +1 -1
package/.commitmsg
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
Edit in Word: only the user's own part goes to Word; signature + quoted message are re-attached verbatim
|
|
2
|
+
|
|
3
|
+
html-to-docx flattens <blockquote> to plain paragraphs and mammoth brings back
|
|
4
|
+
only <p>/<a>/<br>, so a reply came home with the quoted message's formatting
|
|
5
|
+
gone and the bordered signature reduced to two lines. splitWordEditable()
|
|
6
|
+
(client/compose/compose.ts) cuts the body at the signature rule
|
|
7
|
+
(hr.mailx-sig-rule, legacy hr[style*=3cm]) or div.reply; the head goes to
|
|
8
|
+
Word, the tail is taken from the editor at apply time and appended unchanged.
|
|
9
|
+
Signature rule now carries class="mailx-sig-rule".
|
|
@@ -32,3 +32,15 @@ Session: Claude Fable 5.1, started 2026-09-13 ~17:00 EDT.
|
|
|
32
32
|
one tick after paste; moves the caret just after (or before) the anchor when only whitespace/U+FEFF
|
|
33
33
|
separates it from the edge. Quill's typed path NOT covered (not the configured editor).
|
|
34
34
|
- Ships as rmfmail 1.2.326 via rebuild.cmd (running). Bob must restart the daemon for the new bundle.
|
|
35
|
+
|
|
36
|
+
## Follow-on (~19:40): Edit in Word lost the quoted message's formatting
|
|
37
|
+
- Cause: whole body → html-to-docx (flattens blockquote) → Word → mammoth (only p/a/br back).
|
|
38
|
+
- Fix (v1.2.327, publishing): `splitWordEditable()` in client/compose/compose.ts — Word gets only the
|
|
39
|
+
part above the signature rule (`hr.mailx-sig-rule`, legacy `hr[style*=3cm]`) or `div.reply`; the
|
|
40
|
+
tail is re-read from the editor at apply time and appended unchanged.
|
|
41
|
+
- OPEN (Bob's direction): replace mammoth on the return path with his `y:\dev\utils\whts` converter.
|
|
42
|
+
whts today = whole-document CLI (`converter(docPath)` writes files, uses globals/logger/drive
|
|
43
|
+
paths, emits CSS classes via styleToClass, pulls sql/itemgen/pdf deps). Needs a lean exported
|
|
44
|
+
`docxToHtml()` core with inline styles before rmfmail can import it. Not touched — separate project.
|
|
45
|
+
- Checked: bundled TinyMCE 8.8.2 has NO Word-HTML cleaner (that is PowerPaste, paid); only the
|
|
46
|
+
generic paste path and `mceInsertClipboardContent`.
|
|
@@ -6259,7 +6259,7 @@ function applyInit(init) {
|
|
|
6259
6259
|
sigHtml = acct.signature;
|
|
6260
6260
|
}
|
|
6261
6261
|
if (sigHtml) {
|
|
6262
|
-
const sigRule = '<hr style="width:3cm;margin:0.8em 0 0.5em 0;border:0;border-top:1px solid #999;">';
|
|
6262
|
+
const sigRule = '<hr class="mailx-sig-rule" style="width:3cm;margin:0.8em 0 0.5em 0;border:0;border-top:1px solid #999;">';
|
|
6263
6263
|
const sigBlock = `<br>${sigRule}${sigHtml}`;
|
|
6264
6264
|
bodyToRender = isReplyForward ? `<br>${sigBlock}<br>${bodyToRender}` : `${bodyToRender}${sigBlock}`;
|
|
6265
6265
|
}
|
|
@@ -7050,6 +7050,18 @@ var extEditHintClose = null;
|
|
|
7050
7050
|
}
|
|
7051
7051
|
}
|
|
7052
7052
|
var wordEditOpening = false;
|
|
7053
|
+
function splitWordEditable(html) {
|
|
7054
|
+
const doc = new DOMParser().parseFromString(html, "text/html");
|
|
7055
|
+
const nodes = Array.from(doc.body.childNodes);
|
|
7056
|
+
const cutAt = nodes.findIndex((n) => n instanceof HTMLElement && n.matches('hr.mailx-sig-rule, hr[style*="3cm"], div.reply'));
|
|
7057
|
+
if (cutAt < 0) return { head: html, tail: "" };
|
|
7058
|
+
const serialize = (part) => {
|
|
7059
|
+
const box = doc.createElement("div");
|
|
7060
|
+
for (const n of part) box.appendChild(n.cloneNode(true));
|
|
7061
|
+
return box.innerHTML;
|
|
7062
|
+
};
|
|
7063
|
+
return { head: serialize(nodes.slice(0, cutAt)), tail: serialize(nodes.slice(cutAt)) };
|
|
7064
|
+
}
|
|
7053
7065
|
document.getElementById("btn-edit-in-word")?.addEventListener("click", async () => {
|
|
7054
7066
|
const btn = document.getElementById("btn-edit-in-word");
|
|
7055
7067
|
if (wordEditOpening) return;
|
|
@@ -7061,7 +7073,9 @@ document.getElementById("btn-edit-in-word")?.addEventListener("click", async ()
|
|
|
7061
7073
|
if (!wordEditId) wordEditId = `compose-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
7062
7074
|
showDraftStatus("Opening in Word\u2026 (this takes a few seconds)", false);
|
|
7063
7075
|
try {
|
|
7064
|
-
const
|
|
7076
|
+
const { head, tail } = splitWordEditable(editor.getHtml());
|
|
7077
|
+
logClientEvent("word-edit-split", { headChars: head.length, tailChars: tail.length, editId: wordEditId });
|
|
7078
|
+
const result = await openInWord(wordEditId, head, popoutInitId || "");
|
|
7065
7079
|
if (!result.ok || result.opener === "none") {
|
|
7066
7080
|
showDraftStatus("Couldn't launch an editor. Install Word, LibreOffice, or set a default for .html.", true);
|
|
7067
7081
|
return;
|
|
@@ -7221,8 +7235,9 @@ onEvent((ev) => {
|
|
|
7221
7235
|
}
|
|
7222
7236
|
try {
|
|
7223
7237
|
extEditHintClose?.();
|
|
7224
|
-
|
|
7225
|
-
|
|
7238
|
+
const { tail } = splitWordEditable(editor.getHtml());
|
|
7239
|
+
logClientEvent("word-edit-applied", { chars: (ev.html || "").length, tailChars: tail.length, editId: ev.editId });
|
|
7240
|
+
editor.setHtml((ev.html || "") + tail);
|
|
7226
7241
|
showDraftStatus("Reloaded edits from external editor.", false);
|
|
7227
7242
|
scheduleDraftSave();
|
|
7228
7243
|
} catch (e) {
|