diff --git a/static/js/emailLibrary.js b/static/js/emailLibrary.js index 44c6011..9fa052b 100644 --- a/static/js/emailLibrary.js +++ b/static/js/emailLibrary.js @@ -1820,11 +1820,18 @@ function _prefetchAdjacentEmails(card, count = 3) { async function _toggleCardPreview(card, em) { const grid = card.closest('.doclib-grid'); + const gridRect = grid?.getBoundingClientRect?.(); + const currentRect = card.getBoundingClientRect(); + const stableOpenHeight = Math.max( + currentRect.height || 0, + Math.min(Math.max(260, window.innerHeight * 0.56), gridRect?.height || window.innerHeight) + ); // Already expanded — collapse if (card.classList.contains('email-card-expanded')) { card.classList.remove('email-card-expanded'); card.classList.remove('doclib-card-expanded'); + card.style.minHeight = ''; document.getElementById('email-lib-modal')?.classList.remove('email-reading'); const reader = card.querySelector('.email-card-reader'); if (reader) reader.remove(); @@ -1836,6 +1843,7 @@ async function _toggleCardPreview(card, em) { grid.querySelectorAll('.email-card-expanded').forEach(c => { c.classList.remove('email-card-expanded'); c.classList.remove('doclib-card-expanded'); + c.style.minHeight = ''; const r = c.querySelector('.email-card-reader'); if (r) r.remove(); }); @@ -1843,6 +1851,7 @@ async function _toggleCardPreview(card, em) { card.classList.add('email-card-expanded'); card.classList.add('doclib-card-expanded'); + card.style.minHeight = `${Math.round(stableOpenHeight)}px`; if (!em.is_read) { _syncEmailReadState(em.uid, true); fetch(`${API_BASE}/api/email/mark-read/${em.uid}?folder=${encodeURIComponent(state._libFolder)}${_acct()}`, { method: 'POST' }) @@ -1855,7 +1864,8 @@ async function _toggleCardPreview(card, em) { // Show loading reader with whirlpool spinner const reader = document.createElement('div'); - reader.className = 'email-card-reader'; + reader.className = 'email-card-reader email-card-reader-loading'; + reader.style.minHeight = `${Math.max(180, Math.round(stableOpenHeight - 70))}px`; const loadingWrap = document.createElement('div'); loadingWrap.style.cssText = 'padding:20px;display:flex;justify-content:center;align-items:center;flex:1;'; const sp = spinnerModule.createWhirlpool(28); @@ -1935,6 +1945,8 @@ async function _toggleCardPreview(card, em) { ${attsHtml}
${_renderEmailBody(data)}
`; + reader.classList.remove('email-card-reader-loading'); + reader.style.minHeight = ''; // Attachment header click toggles fold/unfold (same UX as the summary). const attsWrap = reader.querySelector('.email-reader-atts-wrap'); diff --git a/static/style.css b/static/style.css index b0fd1a7..18164e7 100644 --- a/static/style.css +++ b/static/style.css @@ -14403,6 +14403,7 @@ body.left-dock-active { overbearing on desktop; the size jump alone is enough signal. */ border: 1px solid var(--border) !important; box-shadow: 0 6px 18px rgba(0,0,0,0.12) !important; + animation: none !important; } /* Desktop-only, ONLY on the currently-expanded email card. Nudges the title row down 6px / right 2px, bolds the subject, hides the timestamp from the @@ -26072,6 +26073,13 @@ button .spinner-whirlpool { min-height: 0; font-size: 12px; } +.email-card-reader-loading { + background: color-mix(in srgb, var(--panel) 82%, transparent); +} +.email-card-reader-loading .spinner-whirlpool, +.email-card-reader-loading .ai-spinner-whirlpool { + opacity: 0.85; +} /* Per-email unread dot in the expanded reader's title row. Background color stays inline (per-sender hue from _senderColor), but the dot gets a soft breathing glow + a 4px vertical nudge so it reads as centered against