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) {
- - ${_hasMultipleRecipients(data) ? `` : ''} - - +
+ + ${_hasMultipleRecipients(data) ? `` : ''} + -
@@ -4409,17 +4408,16 @@ async function _openEmailWindow(em, folder) {
- - ${_hasMultipleRecipients(data) ? `` : ''} - - +
+ + ${_hasMultipleRecipients(data) ? `` : ''} + -
@@ -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;