From 6a392542f37d372902d2ab46e8dd7a571f838d2b Mon Sep 17 00:00:00 2001 From: pewdiepie-archdaemon Date: Thu, 11 Jun 2026 18:40:16 +0900 Subject: [PATCH] =?UTF-8?q?Email=20reader:=20two-row=20action=20layout=20?= =?UTF-8?q?=E2=80=94=20Summary+More=20above,=20Reply/Forward/AI=20reply=20?= =?UTF-8?q?below?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restructure the action cluster so it stays as two visible rows inside .email-reader-actions instead of flattening via display:contents: - Top row: Summary, More - Bottom row: Reply, Reply all (conditional), Forward, AI reply Dropped the Search button — wasn't part of the requested layout. CSS: .email-reader-actions becomes flex column with both rows right-aligned; .email-reader-actions-row becomes a real flex row (no more display:contents flattening) so each row stays on its own line. Whole block continues to sit beside the From/To meta inside .email-reader-header. --- static/js/emailLibrary.js | 72 ++++++++++++++++++++++++++++----------- static/style.css | 22 +++++------- 2 files changed, 61 insertions(+), 33 deletions(-) diff --git a/static/js/emailLibrary.js b/static/js/emailLibrary.js index 458a7471e..a8380bba6 100644 --- a/static/js/emailLibrary.js +++ b/static/js/emailLibrary.js @@ -2559,17 +2559,16 @@ async function _toggleCardPreview(card, em) {
@@ -4409,17 +4408,16 @@ async function _openEmailWindow(em, folder) {
@@ -5359,7 +5357,7 @@ function _summaryIcon(data) { return ``; } -async function _runAiReplyFromButton(btn, em, data, mode) { +async function _runAiReplyFromButton(btn, em, data, mode, noteHint = '') { _snapEmailModalToLeftSidebar(btn.closest('.modal')); btn.disabled = true; const orig = btn.innerHTML; @@ -5371,7 +5369,7 @@ async function _runAiReplyFromButton(btn, em, data, mode) { btn.appendChild(wp.element); } catch (_) {} try { - if (state._onEmailClick) await state._onEmailClick({ email: em, emailData: data, mode }); + if (state._onEmailClick) await state._onEmailClick({ email: em, emailData: data, mode, noteHint }); } finally { try { wp && wp.stop(); } catch (_) {} btn.disabled = false; @@ -5406,16 +5404,47 @@ function _showAiReplyChoice(btn, em, data) { // Full = layered concentric circles to suggest "more, deeper" — not a fully // filled circle so it reads as a complement to the lightning, not as a "stop". menu.innerHTML = ` - - +
+ + + +
+ `; menu.addEventListener('click', async (ev) => { + const noteToggle = ev.target.closest('[data-act="note-toggle"]'); + if (noteToggle) { + ev.preventDefault(); ev.stopPropagation(); + const panel = menu.querySelector('.email-ai-reply-note'); + const wasHidden = panel.hidden; + panel.hidden = !wasHidden; + if (wasHidden) panel.querySelector('textarea')?.focus(); + return; + } + const noteSend = ev.target.closest('[data-act="note-send"]'); + if (noteSend) { + ev.preventDefault(); ev.stopPropagation(); + const note = (menu.querySelector('.email-ai-reply-note-text')?.value || '').trim(); + _closeAiReplyChoice(); + await _runAiReplyFromButton(btn, em, data, 'ai-reply-full', note); + return; + } const choice = ev.target.closest('[data-mode]'); if (!choice) return; ev.preventDefault(); @@ -5424,6 +5453,9 @@ function _showAiReplyChoice(btn, em, data) { _closeAiReplyChoice(); await _runAiReplyFromButton(btn, em, data, mode); }); + // Esc closes the popover; ignore plain clicks inside the menu so the + // textarea stays focused. + menu.addEventListener('mousedown', (ev) => ev.stopPropagation()); document.body.appendChild(menu); setTimeout(() => document.addEventListener('click', _closeAiReplyChoice, true), 0); } diff --git a/static/style.css b/static/style.css index 94b79b599..ce23aa6d7 100644 --- a/static/style.css +++ b/static/style.css @@ -28078,23 +28078,19 @@ button .spinner-whirlpool { .recipient-chip { flex-shrink: 0; } } .email-reader-actions { - display: flex; gap: 4px; flex-wrap: wrap; align-items: center; + display: flex; flex-direction: column; gap: 4px; align-items: flex-end; flex-shrink: 0; - justify-content: flex-end; margin-top: -4px; } -/* The HTML wraps the buttons in two .email-reader-actions-row divs (primary - + secondary). On mobile those flatten via `display: contents` inside the - max-width:768px block; apply the same here so the whole row stays on one - line on desktop too. */ +/* Two stacked rows inside .email-reader-actions: + primary — Summary + More (top) + secondary — Reply / Reply all / Forward / AI reply (bottom) */ .email-reader-actions-row { - display: contents; -} -/* Pin the More button to the far right of the flattened flex row — it - sits at position 5 in the primary row's source order, so without an - explicit order the secondary AI/Summary buttons land after it. */ -.email-reader-actions .email-reader-more-wrap { - order: 99; + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-end; + gap: 4px; } .email-reader-atts { display: flex; flex-wrap: wrap; gap: 6px;