diff --git a/static/index.html b/static/index.html index 35388819f..c0fd60197 100644 --- a/static/index.html +++ b/static/index.html @@ -219,8 +219,8 @@ }, { once: true }); })(); - - + + @@ -2512,7 +2512,7 @@ - + diff --git a/static/js/document.js b/static/js/document.js index 6413cf94b..c4c97e841 100644 --- a/static/js/document.js +++ b/static/js/document.js @@ -2668,6 +2668,15 @@ import { bindMenuDismiss, dismissOrRemove } from './escMenuStack.js'; summary.title = summary.textContent; } + function _setEmailHeaderInputValue(id, value, { preserveFocused = true, preserveNonEmpty = false } = {}) { + const el = document.getElementById(id); + if (!el) return; + const next = value || ''; + if (preserveFocused && document.activeElement === el) return; + if (preserveNonEmpty && !next && el.value) return; + if (el.value !== next) el.value = next; + } + function _setEmailHeaderCollapsed(collapsed, { manual = true } = {}) { const header = document.getElementById('doc-email-header'); const btn = document.getElementById('doc-email-collapse-btn'); @@ -2730,13 +2739,10 @@ import { bindMenuDismiss, dismissOrRemove } from './escMenuStack.js'; document.getElementById('doc-editor-highlight')?.classList.add('email-mode'); let fields = _parseEmailHeader(doc.content || ''); fields = _emailFieldsWithLocalDraft(fields); - const toInput = document.getElementById('doc-email-to'); const subjectInput = document.getElementById('doc-email-subject'); - const inReplyTo = document.getElementById('doc-email-in-reply-to'); - const refs = document.getElementById('doc-email-references'); const textarea = document.getElementById('doc-editor-textarea'); - if (toInput) toInput.value = fields.to; - if (subjectInput) subjectInput.value = fields.subject; + _setEmailHeaderInputValue('doc-email-to', fields.to, { preserveNonEmpty: true }); + _setEmailHeaderInputValue('doc-email-subject', fields.subject, { preserveNonEmpty: true }); _setEmailHeaderCollapsed(!!(doc && doc._emailHeaderCollapsed), { manual: false }); if (subjectInput && !subjectInput._emailTabBodyBound) { subjectInput._emailTabBodyBound = true; @@ -2747,12 +2753,10 @@ import { bindMenuDismiss, dismissOrRemove } from './escMenuStack.js'; } }); } - if (inReplyTo) inReplyTo.value = fields.inReplyTo; - if (refs) refs.value = fields.references; - const sourceUid = document.getElementById('doc-email-source-uid'); - const sourceFolder = document.getElementById('doc-email-source-folder'); - if (sourceUid) sourceUid.value = fields.sourceUid || ''; - if (sourceFolder) sourceFolder.value = fields.sourceFolder || ''; + _setEmailHeaderInputValue('doc-email-in-reply-to', fields.inReplyTo, { preserveNonEmpty: true }); + _setEmailHeaderInputValue('doc-email-references', fields.references, { preserveNonEmpty: true }); + _setEmailHeaderInputValue('doc-email-source-uid', fields.sourceUid || '', { preserveNonEmpty: true }); + _setEmailHeaderInputValue('doc-email-source-folder', fields.sourceFolder || '', { preserveNonEmpty: true }); // Show/hide unread button only if we have a source UID (came from inbox) const unreadBtn = document.getElementById('doc-email-unread-btn'); if (unreadBtn) unreadBtn.style.display = fields.sourceUid ? '' : 'none'; @@ -2875,11 +2879,14 @@ import { bindMenuDismiss, dismissOrRemove } from './escMenuStack.js'; const ccRow = document.getElementById('doc-email-cc-row'); const bccRow = document.getElementById('doc-email-bcc-row'); const ccToggle = document.getElementById('doc-email-show-cc'); - const ccInput = document.getElementById('doc-email-cc'); - const bccInput = document.getElementById('doc-email-bcc'); - if (ccInput) ccInput.value = fields.cc || ''; - if (bccInput) bccInput.value = fields.bcc || ''; - const hasCcBcc = !!(fields.cc || fields.bcc); + _setEmailHeaderInputValue('doc-email-cc', fields.cc || '', { preserveNonEmpty: true }); + _setEmailHeaderInputValue('doc-email-bcc', fields.bcc || '', { preserveNonEmpty: true }); + const hasCcBcc = !!( + fields.cc || + fields.bcc || + document.getElementById('doc-email-cc')?.value || + document.getElementById('doc-email-bcc')?.value + ); if (ccRow) ccRow.style.display = hasCcBcc ? '' : 'none'; if (bccRow) bccRow.style.display = hasCcBcc ? '' : 'none'; if (ccToggle) ccToggle.style.display = hasCcBcc ? 'none' : ''; @@ -2898,19 +2905,14 @@ import { bindMenuDismiss, dismissOrRemove } from './escMenuStack.js'; return; } - const setValue = (id, value) => { - const el = document.getElementById(id); - const next = value || ''; - if (el && el.value !== next) el.value = next; - }; - setValue('doc-email-to', fields.to); - setValue('doc-email-subject', fields.subject); - setValue('doc-email-in-reply-to', fields.inReplyTo); - setValue('doc-email-references', fields.references); - setValue('doc-email-source-uid', fields.sourceUid || ''); - setValue('doc-email-source-folder', fields.sourceFolder || ''); - setValue('doc-email-cc', fields.cc || ''); - setValue('doc-email-bcc', fields.bcc || ''); + _setEmailHeaderInputValue('doc-email-to', fields.to, { preserveNonEmpty: true }); + _setEmailHeaderInputValue('doc-email-subject', fields.subject, { preserveNonEmpty: true }); + _setEmailHeaderInputValue('doc-email-in-reply-to', fields.inReplyTo, { preserveNonEmpty: true }); + _setEmailHeaderInputValue('doc-email-references', fields.references, { preserveNonEmpty: true }); + _setEmailHeaderInputValue('doc-email-source-uid', fields.sourceUid || '', { preserveNonEmpty: true }); + _setEmailHeaderInputValue('doc-email-source-folder', fields.sourceFolder || '', { preserveNonEmpty: true }); + _setEmailHeaderInputValue('doc-email-cc', fields.cc || '', { preserveNonEmpty: true }); + _setEmailHeaderInputValue('doc-email-bcc', fields.bcc || '', { preserveNonEmpty: true }); const unreadBtn = document.getElementById('doc-email-unread-btn'); if (unreadBtn) unreadBtn.style.display = fields.sourceUid ? '' : 'none'; @@ -4029,7 +4031,7 @@ import { bindMenuDismiss, dismissOrRemove } from './escMenuStack.js'; const prevId = activeDocId; if (prevId && prevId !== docId && docs.has(prevId)) { const prev = docs.get(prevId); - if (!(prev.content || '').trim() && !(prev.title || '').trim()) { + if (prev.language !== 'email' && !(prev.content || '').trim() && !(prev.title || '').trim()) { fetch(`${API_BASE}/api/document/${prevId}`, { method: 'DELETE' }).catch(() => {}); docs.delete(prevId); _syncDocIndicator(); @@ -8828,6 +8830,9 @@ import { bindMenuDismiss, dismissOrRemove } from './escMenuStack.js'; }), }); if (res.status === 404) { + if (silent && localDoc?.language === 'email') { + return; + } // Streaming/empty email drafts can leave a local tab pointing at a temp // or already-deleted document. Do not keep surfacing autosave errors for // a document the backend no longer knows about. diff --git a/static/sw.js b/static/sw.js index 3590ad362..8e8930e9d 100644 --- a/static/sw.js +++ b/static/sw.js @@ -7,7 +7,7 @@ // - Other static assets (images/fonts/libs): cache-first with bg refresh. // - API / non-GET: never cached. // Bump CACHE_NAME whenever the precache list or SW logic changes. -const CACHE_NAME = 'odysseus-v332'; +const CACHE_NAME = 'odysseus-v333'; // Core shell precached on install so repeat opens are instant without any // network wait. Keep this list in sync with the