mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-06-15 17:25:26 -04:00
Email reader: two-row action layout — Summary+More above, Reply/Forward/AI reply below
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.
This commit is contained in:
+52
-20
@@ -2559,17 +2559,16 @@ async function _toggleCardPreview(card, em) {
|
||||
</div>
|
||||
<div class="email-reader-actions">
|
||||
<div class="email-reader-actions-row email-reader-actions-row-primary">
|
||||
<button class="memory-toolbar-btn reader-icon-btn" data-act="reply" title="Reply"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 17 4 12 9 7"/><path d="M20 18v-2a4 4 0 0 0-4-4H4"/></svg><span class="reader-btn-label">Reply</span></button>
|
||||
${_hasMultipleRecipients(data) ? `<button class="memory-toolbar-btn reader-icon-btn" data-act="reply-all" title="Reply All"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="7 17 2 12 7 7"/><polyline points="12 17 7 12 12 7"/><path d="M22 18v-2a4 4 0 0 0-4-4H7"/></svg><span class="reader-btn-label">Reply all</span></button>` : ''}
|
||||
<button class="memory-toolbar-btn reader-icon-btn" data-act="forward" title="Forward"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 17 20 12 15 7"/><path d="M4 18v-2a4 4 0 0 1 4-4h12"/></svg><span class="reader-btn-label">Forward</span></button>
|
||||
<button class="memory-toolbar-btn reader-icon-btn" data-act="from-sender" title="Search text in this thread"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg><span class="reader-btn-label">Search</span></button>
|
||||
<button class="memory-toolbar-btn reader-icon-btn" data-act="summarize" title="Summarize">${_summaryIcon(data)}<span class="reader-btn-label">Summary</span></button>
|
||||
<div class="email-reader-more-wrap" style="position:relative">
|
||||
<button class="memory-toolbar-btn reader-icon-btn" data-act="more" title="More actions"><svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><circle cx="12" cy="5" r="2"/><circle cx="12" cy="12" r="2"/><circle cx="12" cy="19" r="2"/></svg><span class="reader-btn-label">More</span></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="email-reader-actions-row email-reader-actions-row-secondary">
|
||||
<button class="memory-toolbar-btn reader-icon-btn" data-act="reply" title="Reply"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 17 4 12 9 7"/><path d="M20 18v-2a4 4 0 0 0-4-4H4"/></svg><span class="reader-btn-label">Reply</span></button>
|
||||
${_hasMultipleRecipients(data) ? `<button class="memory-toolbar-btn reader-icon-btn" data-act="reply-all" title="Reply All"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="7 17 2 12 7 7"/><polyline points="12 17 7 12 12 7"/><path d="M22 18v-2a4 4 0 0 0-4-4H7"/></svg><span class="reader-btn-label">Reply all</span></button>` : ''}
|
||||
<button class="memory-toolbar-btn reader-icon-btn" data-act="forward" title="Forward"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 17 20 12 15 7"/><path d="M4 18v-2a4 4 0 0 1 4-4h12"/></svg><span class="reader-btn-label">Forward</span></button>
|
||||
<button class="memory-toolbar-btn reader-icon-btn" data-act="ai-reply" title="${data.cached_ai_reply ? 'AI Reply (cached draft ready)' : 'AI Reply (suggest a draft)'}">${_aiReplyIcon(data)}<span class="reader-btn-label">AI reply</span></button>
|
||||
<button class="memory-toolbar-btn reader-icon-btn" data-act="summarize" title="Summarize">${_summaryIcon(data)}<span class="reader-btn-label">Summary</span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -4409,17 +4408,16 @@ async function _openEmailWindow(em, folder) {
|
||||
</div>
|
||||
<div class="email-reader-actions">
|
||||
<div class="email-reader-actions-row email-reader-actions-row-primary">
|
||||
<button class="memory-toolbar-btn reader-icon-btn" data-act="reply" title="Reply"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 17 4 12 9 7"/><path d="M20 18v-2a4 4 0 0 0-4-4H4"/></svg><span class="reader-btn-label">Reply</span></button>
|
||||
${_hasMultipleRecipients(data) ? `<button class="memory-toolbar-btn reader-icon-btn" data-act="reply-all" title="Reply All"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="7 17 2 12 7 7"/><polyline points="12 17 7 12 12 7"/><path d="M22 18v-2a4 4 0 0 0-4-4H7"/></svg><span class="reader-btn-label">Reply all</span></button>` : ''}
|
||||
<button class="memory-toolbar-btn reader-icon-btn" data-act="forward" title="Forward"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 17 20 12 15 7"/><path d="M4 18v-2a4 4 0 0 1 4-4h12"/></svg><span class="reader-btn-label">Forward</span></button>
|
||||
<button class="memory-toolbar-btn reader-icon-btn" data-act="from-sender" title="Search text in this thread"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg><span class="reader-btn-label">Search</span></button>
|
||||
<button class="memory-toolbar-btn reader-icon-btn" data-act="summarize" title="Summarize">${_summaryIcon(data)}<span class="reader-btn-label">Summary</span></button>
|
||||
<div class="email-reader-more-wrap" style="position:relative">
|
||||
<button class="memory-toolbar-btn reader-icon-btn" data-act="more" title="More actions"><svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><circle cx="12" cy="5" r="2"/><circle cx="12" cy="12" r="2"/><circle cx="12" cy="19" r="2"/></svg><span class="reader-btn-label">More</span></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="email-reader-actions-row email-reader-actions-row-secondary">
|
||||
<button class="memory-toolbar-btn reader-icon-btn" data-act="reply" title="Reply"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 17 4 12 9 7"/><path d="M20 18v-2a4 4 0 0 0-4-4H4"/></svg><span class="reader-btn-label">Reply</span></button>
|
||||
${_hasMultipleRecipients(data) ? `<button class="memory-toolbar-btn reader-icon-btn" data-act="reply-all" title="Reply All"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="7 17 2 12 7 7"/><polyline points="12 17 7 12 12 7"/><path d="M22 18v-2a4 4 0 0 0-4-4H7"/></svg><span class="reader-btn-label">Reply all</span></button>` : ''}
|
||||
<button class="memory-toolbar-btn reader-icon-btn" data-act="forward" title="Forward"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 17 20 12 15 7"/><path d="M4 18v-2a4 4 0 0 1 4-4h12"/></svg><span class="reader-btn-label">Forward</span></button>
|
||||
<button class="memory-toolbar-btn reader-icon-btn" data-act="ai-reply" title="${data.cached_ai_reply ? 'AI Reply (cached draft ready)' : 'AI Reply (suggest a draft)'}">${_aiReplyIcon(data)}<span class="reader-btn-label">AI reply</span></button>
|
||||
<button class="memory-toolbar-btn reader-icon-btn" data-act="summarize" title="Summarize">${_summaryIcon(data)}<span class="reader-btn-label">Summary</span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -5359,7 +5357,7 @@ function _summaryIcon(data) {
|
||||
return `<svg width="14" height="14" viewBox="0 0 24 24" fill="${fill}"><path d="M12 0L14.59 8.41L23 12L14.59 15.59L12 24L9.41 15.59L1 12L9.41 8.41Z"/></svg>`;
|
||||
}
|
||||
|
||||
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 = `
|
||||
<button class="memory-toolbar-btn" data-mode="ai-reply-fast" title="Shorter, faster draft" style="display:inline-flex;align-items:center;gap:5px;">
|
||||
<svg width="11" height="11" viewBox="0 0 24 24" fill="var(--accent, var(--red))" aria-hidden="true"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>
|
||||
Fast
|
||||
</button>
|
||||
<button class="memory-toolbar-btn" data-mode="ai-reply-full" title="Uses the fuller reply context" style="display:inline-flex;align-items:center;gap:5px;">
|
||||
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="var(--accent, var(--red))" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><circle cx="12" cy="12" r="5"/><circle cx="12" cy="12" r="1.5" fill="var(--accent, var(--red))"/></svg>
|
||||
Full
|
||||
</button>
|
||||
<div class="email-ai-reply-row" style="display:flex;align-items:center;gap:4px;">
|
||||
<button class="memory-toolbar-btn" data-mode="ai-reply-fast" title="Shorter, faster draft" style="display:inline-flex;align-items:center;gap:5px;">
|
||||
<svg width="11" height="11" viewBox="0 0 24 24" fill="var(--accent, var(--red))" aria-hidden="true"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>
|
||||
Fast
|
||||
</button>
|
||||
<button class="memory-toolbar-btn" data-mode="ai-reply-full" title="Uses the fuller reply context" style="display:inline-flex;align-items:center;gap:5px;">
|
||||
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="var(--accent, var(--red))" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><circle cx="12" cy="12" r="5"/><circle cx="12" cy="12" r="1.5" fill="var(--accent, var(--red))"/></svg>
|
||||
Full
|
||||
</button>
|
||||
<button class="memory-toolbar-btn" data-act="note-toggle" title="Add a note about how to reply" style="display:inline-flex;align-items:center;justify-content:center;padding:4px 6px;line-height:1;">
|
||||
<svg width="14" height="4" viewBox="0 0 14 4" fill="var(--accent, var(--red))" aria-hidden="true"><circle cx="2" cy="2" r="1.4"/><circle cx="7" cy="2" r="1.4"/><circle cx="12" cy="2" r="1.4"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="email-ai-reply-note" hidden style="display:flex;flex-direction:column;gap:5px;padding-top:6px;border-top:1px solid var(--border);margin-top:6px;">
|
||||
<textarea class="email-ai-reply-note-text" placeholder="Tell the AI how to reply (e.g. 'thank them and confirm Tuesday at 2', 'decline politely')" rows="3" style="resize:vertical;width:100%;min-width:240px;font-family:inherit;font-size:12px;padding:6px 8px;border:1px solid var(--border);border-radius:5px;background:var(--bg);color:var(--fg);box-sizing:border-box;"></textarea>
|
||||
<div style="display:flex;justify-content:flex-end;">
|
||||
<button class="memory-toolbar-btn" data-act="note-send" title="Draft using this note" style="display:inline-flex;align-items:center;gap:5px;color:var(--accent, var(--red));border-color:color-mix(in srgb, var(--accent, var(--red)) 45%, var(--border));">
|
||||
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>
|
||||
Draft with note
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
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);
|
||||
}
|
||||
|
||||
+9
-13
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user