From 39331fafb578f39b08335ae0627e2d772ecfc9a5 Mon Sep 17 00:00:00 2001 From: pewdiepie-archdaemon Date: Thu, 11 Jun 2026 19:46:06 +0900 Subject: [PATCH] Email reader: primary action row literally inside the From row Restructured the DOM so the Reply / Reply-all / Forward row lives INSIDE the email-reader-meta-from div (after the chips span), and the Summary / AI / More row sits directly below as a sibling of From inside the meta. Killed the outer email-reader-actions wrapper that kept letting the buttons drift out of position. CSS now pushes the primary row right via margin-left:auto on the From row and right-aligns the secondary row below it. --- static/js/emailLibrary.js | 89 +++++++++++++++++++++++---------------- static/style.css | 12 ++++++ 2 files changed, 65 insertions(+), 36 deletions(-) diff --git a/static/js/emailLibrary.js b/static/js/emailLibrary.js index bf8ca0e02..887459252 100644 --- a/static/js/emailLibrary.js +++ b/static/js/emailLibrary.js @@ -1768,7 +1768,7 @@ function _scoreFilterOption(opt, needle) { return 0; } -function _filterSuggestions(needle, limit = 8) { +function _filterSuggestions(needle, limit = 10) { const n = String(needle || '').trim().toLowerCase(); if (!n) return []; // Filter / attachment matches first — typing 'unread' should surface @@ -1781,7 +1781,21 @@ function _filterSuggestions(needle, limit = 8) { const contactMatches = src .map(s => ({ s: { kind: 'contact', ...s }, score: _scoreSuggestion(s, n) })) .filter(x => x.score > 0); - return filterMatches.concat(contactMatches) + // Email subject / sender-name matches — use the snapshot (unfiltered + // list) when available so suggestions don't shrink as pills narrow the + // visible grid. Cap to 4 so contacts + filters stay visible. + const emails = _libPreSearchEmails || state._libEmails || []; + const emailMatches = []; + for (const em of emails) { + const subj = String(em.subject || '').toLowerCase(); + const fromN = String(em.from_name || '').toLowerCase(); + let score = 0; + if (subj.startsWith(n) || fromN.startsWith(n)) score = 3; + else if (subj.includes(n) || fromN.includes(n)) score = 1; + if (score > 0) emailMatches.push({ s: { kind: 'email', uid: em.uid, subject: em.subject || '(no subject)', from_name: em.from_name || em.from_address || '' }, score }); + if (emailMatches.length >= 4) break; + } + return filterMatches.concat(contactMatches).concat(emailMatches) .sort((a, b) => b.score - a.score) .slice(0, limit) .map(x => x.s); @@ -2734,7 +2748,7 @@ function _createCard(em) { const star = document.createElement('span'); star.title = 'Favorited'; star.style.cssText = 'color:var(--accent, var(--red));opacity:0.85;flex-shrink:0;display:inline-flex;'; - star.innerHTML = ''; + star.innerHTML = ''; titleRow.appendChild(star); } @@ -3018,17 +3032,14 @@ async function _toggleCardPreview(card, em) { reader.innerHTML = `
-
${attsHtml} @@ -4716,17 +4731,14 @@ async function _openEmailAsTab(em, folder) { reader.innerHTML = `
-
${attsHtml} @@ -4873,17 +4889,14 @@ async function _openEmailWindow(em, folder) { bodyEl.innerHTML = `
-
${attsHtml} diff --git a/static/style.css b/static/style.css index c3d195b2b..cb2e6259b 100644 --- a/static/style.css +++ b/static/style.css @@ -28044,6 +28044,18 @@ button .spinner-whirlpool { gap: 6px; min-width: 0; } +/* Primary action row (Reply / Reply-all / Forward) lives INSIDE + the From row and gets pushed to the right edge. */ +.email-reader-meta-row.email-reader-meta-from > .email-reader-actions-row-primary { + margin-left: auto; + flex-shrink: 0; +} +/* Secondary action row (Summary / AI / More) sits below the From + row as a sibling, right-aligned. */ +.email-reader-meta > .email-reader-actions-row-secondary { + justify-content: flex-end; + margin-top: 2px; +} /* Gmail-style chevron — collapsed view shows only From; the chevron reveals the To/Cc details inline below. */ .email-reader-meta-toggle {