Preserve HTML email quote history
This commit is contained in:
parent
0cdbdf4186
commit
6127e43113
3 changed files with 42 additions and 4 deletions
|
|
@ -219,8 +219,8 @@
|
||||||
}, { once: true });
|
}, { once: true });
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
<link rel="stylesheet" href="/static/style.css?v=20260630modelfallback">
|
<link rel="stylesheet" href="/static/style.css?v=20260630emailhtmlquote">
|
||||||
<link rel="modulepreload" href="/static/app.js?v=20260630modelfallback">
|
<link rel="modulepreload" href="/static/app.js?v=20260630emailhtmlquote">
|
||||||
<link rel="modulepreload" href="/static/js/chat.js">
|
<link rel="modulepreload" href="/static/js/chat.js">
|
||||||
<link rel="modulepreload" href="/static/js/ui.js">
|
<link rel="modulepreload" href="/static/js/ui.js">
|
||||||
<link rel="modulepreload" href="/static/js/sessions.js">
|
<link rel="modulepreload" href="/static/js/sessions.js">
|
||||||
|
|
@ -2512,7 +2512,7 @@
|
||||||
<script type="module" src="/static/js/settings.js"></script>
|
<script type="module" src="/static/js/settings.js"></script>
|
||||||
<script type="module" src="/static/js/admin.js"></script>
|
<script type="module" src="/static/js/admin.js"></script>
|
||||||
<script type="module" src="/static/js/assistant.js"></script>
|
<script type="module" src="/static/js/assistant.js"></script>
|
||||||
<script type="module" src="/static/app.js?v=20260630modelfallback"></script> <!-- app.js must be LAST -->
|
<script type="module" src="/static/app.js?v=20260630emailhtmlquote"></script> <!-- app.js must be LAST -->
|
||||||
<script type="module" src="/static/js/init.js"></script>
|
<script type="module" src="/static/js/init.js"></script>
|
||||||
<script type="module" src="/static/js/a11y.js"></script>
|
<script type="module" src="/static/js/a11y.js"></script>
|
||||||
<script nonce="{{CSP_NONCE}}">if('serviceWorker' in navigator){navigator.serviceWorker.register('/static/sw.js').catch(()=>{});}</script>
|
<script nonce="{{CSP_NONCE}}">if('serviceWorker' in navigator){navigator.serviceWorker.register('/static/sw.js').catch(()=>{});}</script>
|
||||||
|
|
|
||||||
|
|
@ -2588,9 +2588,47 @@ import { bindMenuDismiss, dismissOrRemove } from './escMenuStack.js';
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _emailQuoteStartOffset(text) {
|
||||||
|
const original = String(text || '');
|
||||||
|
if (!original) return -1;
|
||||||
|
const boundary = String.raw`(?:^|\n|<br\s*\/?>|<\/(?:p|div|blockquote|li|tr|h[1-6])>)`;
|
||||||
|
const patterns = [
|
||||||
|
new RegExp(`${boundary}\\s*(?:[-_=–—\\s]| ){3,}(?:previous|original|forwarded)\\s+(?:message|email|mail)(?:[-_=–—\\s]| ){3,}`, 'i'),
|
||||||
|
new RegExp(`${boundary}\\s*On\\s+.{1,700}?\\s+wrote:\\s*`, 'i'),
|
||||||
|
new RegExp(`${boundary}\\s*-{2,}\\s*Original Message\\s*-{2,}`, 'i'),
|
||||||
|
];
|
||||||
|
let best = -1;
|
||||||
|
for (const re of patterns) {
|
||||||
|
const m = re.exec(original);
|
||||||
|
if (!m) continue;
|
||||||
|
let idx = m.index;
|
||||||
|
const prefix = m[0].match(/^(?:\n|<br\s*\/?>|<\/(?:p|div|blockquote|li|tr|h[1-6])>)/i);
|
||||||
|
if (prefix) idx += prefix[0].length;
|
||||||
|
if (best < 0 || idx < best) best = idx;
|
||||||
|
}
|
||||||
|
const fromRe = new RegExp(`${boundary}\\s*From:\\s*\\S`, 'i');
|
||||||
|
const fromMatch = fromRe.exec(original);
|
||||||
|
if (fromMatch) {
|
||||||
|
let idx = fromMatch.index;
|
||||||
|
const prefix = fromMatch[0].match(/^(?:\n|<br\s*\/?>|<\/(?:p|div|blockquote|li|tr|h[1-6])>)/i);
|
||||||
|
if (prefix) idx += prefix[0].length;
|
||||||
|
const nearby = original.slice(idx, idx + 1200);
|
||||||
|
if (/(?:^|\n|<br\s*\/?>|<\/(?:p|div|blockquote|li|tr|h[1-6])>)\s*(?:To|Subject):\s*/i.test(nearby)) {
|
||||||
|
if (best < 0 || idx < best) best = idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
function _splitEmailReplyQuote(text) {
|
function _splitEmailReplyQuote(text) {
|
||||||
const original = String(text || '');
|
const original = String(text || '');
|
||||||
if (!original) return { body: '', quote: '', stripped: false };
|
if (!original) return { body: '', quote: '', stripped: false };
|
||||||
|
const htmlQuoteOffset = _emailQuoteStartOffset(original);
|
||||||
|
if (htmlQuoteOffset >= 0) {
|
||||||
|
const body = original.slice(0, htmlQuoteOffset).trim();
|
||||||
|
const quote = original.slice(htmlQuoteOffset).trim();
|
||||||
|
return { body, quote, stripped: true };
|
||||||
|
}
|
||||||
const lines = original.split('\n');
|
const lines = original.split('\n');
|
||||||
const quoteIdx = _emailQuoteStartIndex(lines);
|
const quoteIdx = _emailQuoteStartIndex(lines);
|
||||||
if (quoteIdx < 0) return { body: original.trim(), quote: '', stripped: false };
|
if (quoteIdx < 0) return { body: original.trim(), quote: '', stripped: false };
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
// - Other static assets (images/fonts/libs): cache-first with bg refresh.
|
// - Other static assets (images/fonts/libs): cache-first with bg refresh.
|
||||||
// - API / non-GET: never cached.
|
// - API / non-GET: never cached.
|
||||||
// Bump CACHE_NAME whenever the precache list or SW logic changes.
|
// Bump CACHE_NAME whenever the precache list or SW logic changes.
|
||||||
const CACHE_NAME = 'odysseus-v334';
|
const CACHE_NAME = 'odysseus-v335';
|
||||||
|
|
||||||
// Core shell precached on install so repeat opens are instant without any
|
// Core shell precached on install so repeat opens are instant without any
|
||||||
// network wait. Keep this list in sync with the <script type="module"> tags
|
// network wait. Keep this list in sync with the <script type="module"> tags
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue