From d0fee5c8f76d4f53a25c8b9b1ec67a55ed8a2460 Mon Sep 17 00:00:00 2001
From: debu-2 ]*>.*?
", re.IGNORECASE | re.DOTALL),
- re.compile(r'
", "\n", cleaned, flags=re.IGNORECASE)
- cleaned = re.sub(r"
++responder: D++sender: C
${quote.replace(/\n/g, "` - : "
")}
${(target?.new_reply_content || target?.body_text || target?.snippet || "").replace(/\n/g, "`, + bodyHtml: buildReplyBodyHtml(target), }); }} onExtractActions={() => detail && extractActions(detail)} diff --git a/frontend/src/styles.css b/frontend/src/styles.css index b541321..cdaefb4 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -945,6 +945,9 @@ h1 em { color:var(--coral); font-style:normal; font-weight:500; } .compose-editor:focus { outline:none; box-shadow:none; } .compose-editor:empty:before { content:attr(data-placeholder); color:#aaa4ac; pointer-events:none; } .compose-editor blockquote { margin:8px 0; padding-left:12px; border-left:3px solid #e5dfd9; color:#67677a; } +.compose-editor.quotes-collapsed .gmail_quote { display:none; } +.compose-quoted-toggle { align-self:flex-start; margin:0 0 10px 16px; padding:1px 8px 3px; border:1px solid #dedbe2; border-radius:5px; background:#f3f1f5; color:#77717d; font:700 12px/1 "DM Sans"; letter-spacing:1px; cursor:pointer; } +.compose-quoted-toggle:hover { background:#eae7ed; color:#45404a; } .compose-bottom-stack { flex-shrink:0; border-top:1px solid #EEECEF; background:#fff; } .compose-agent-bar { height:42px; padding:0 var(--compose-pad); display:grid; grid-template-columns:auto 1fr auto; align-items:center; gap:10px; border-top:1px solid #EEEAF8; border-bottom:1px solid #EEEAF8; background:#FBF9FF; color:#77728C; font-size:11px; } .compose-agent-bar .agent-spark { color:#7357F6; } diff --git a/frontend/src/utils/emailReply.js b/frontend/src/utils/emailReply.js new file mode 100644 index 0000000..54ce2aa --- /dev/null +++ b/frontend/src/utils/emailReply.js @@ -0,0 +1,144 @@ +const BLOCK_TAGS = new Set(["DIV", "P", "PRE", "LI", "UL", "OL"]); +const SAFE_TAGS = new Set([ + "A", + "B", + "BLOCKQUOTE", + "BR", + "CODE", + "DIV", + "EM", + "I", + "LI", + "OL", + "P", + "PRE", + "SPAN", + "STRONG", + "U", + "UL", +]); + +export function escapeHtml(value = "") { + return value + .replaceAll("&", "&") + .replaceAll("<", "<") + .replaceAll(">", ">") + .replaceAll('"', """) + .replaceAll("'", "'"); +} + +function cleanMessageHtml(value) { + if (!value) return ""; + const doc = new DOMParser().parseFromString(value, "text/html"); + + function clean(node) { + [...node.childNodes].forEach((child) => { + if (child.nodeType !== Node.ELEMENT_NODE) return; + if (!SAFE_TAGS.has(child.tagName)) { + if (["SCRIPT", "STYLE", "IFRAME", "OBJECT", "EMBED"].includes(child.tagName)) { + child.remove(); + return; + } + clean(child); + child.replaceWith(...child.childNodes); + return; + } + + [...child.attributes].forEach((attribute) => child.removeAttribute(attribute.name)); + if (child.tagName === "BLOCKQUOTE") { + child.setAttribute( + "style", + "margin:0 0 0 .8ex;border-left:1px solid #ccc;padding-left:1ex", + ); + } + clean(child); + }); + } + + clean(doc.body); + return doc.body.innerHTML; +} + +function textBodyAsHtml(value) { + return `
")}
', + original, + "", + "