Email reader: regroup More menu + reshuffle toolbar rows

More menu reorganization:
- Group 1: Open in new tab, Remind to reply
- Group 2 (state): Mark as Unread/Read, Mark as Done/Not Done, Move to
  Archive, Save sender to contacts
- Group 3 (destructive, unchanged): Move to Spam, Move to Trash,
  Delete Permanently
- Renames: Done→'Mark as Done', Archive→'Move to Archive', Mark
  Read/Unread→'Mark as Read'/'Mark as Unread'.
- Mark Unread moves out of group 1 down into the state-change group
  alongside Done; Save sender to contacts moves down into the same
  state group.

Toolbar row reshuffle (applies to both the email-list card reader and
the email document view):
- Row 1 (primary): Reply, Reply all, Forward, Search, More — Forward
  no longer has to fight Search/More for space in the secondary row.
- Row 2 (secondary): AI reply, Summary — gets its own dedicated row.
This commit is contained in:
pewdiepie-archdaemon
2026-06-11 12:50:47 +09:00
parent be126afcf8
commit df908b4c11
+41 -41
View File
@@ -2428,15 +2428,15 @@ async function _toggleCardPreview(card, em) {
<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>
</div>
<div class="email-reader-actions-row email-reader-actions-row-secondary">
<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>
<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>
<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="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>
${attsHtml}
@@ -4278,15 +4278,15 @@ async function _openEmailWindow(em, folder) {
<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>
</div>
<div class="email-reader-actions-row email-reader-actions-row-secondary">
<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>
<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>
<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="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>
${attsHtml}
@@ -4657,7 +4657,13 @@ function _showReaderMoreMenu(em, card, reader, anchor) {
},
},
{
label: em.is_read ? 'Mark Unread' : 'Mark Read',
label: 'Remind to reply',
icon: _bellIcon,
submenu: 'remind',
},
{ separator: true },
{
label: em.is_read ? 'Mark as Unread' : 'Mark as Read',
icon: _unreadIcon,
action: async () => {
const newRead = !em.is_read;
@@ -4673,11 +4679,33 @@ function _showReaderMoreMenu(em, card, reader, anchor) {
},
},
{
label: 'Remind to reply',
icon: _bellIcon,
submenu: 'remind',
label: em.is_answered ? 'Mark as Not Done' : 'Mark as Done',
icon: _checkIcon,
action: async () => {
const newState = !em.is_answered;
em.is_answered = newState;
if (newState) _syncEmailReadState(em.uid, true);
try {
if (newState) {
await fetch(`${API_BASE}/api/email/mark-answered/${em.uid}?folder=${encodeURIComponent(state._libFolder)}${_acct()}`, { method: 'POST' });
await fetch(`${API_BASE}/api/email/mark-read/${em.uid}?folder=${encodeURIComponent(state._libFolder)}${_acct()}`, { method: 'POST' });
} else {
await fetch(`${API_BASE}/api/email/clear-answered/${em.uid}?folder=${encodeURIComponent(state._libFolder)}${_acct()}`, { method: 'POST' });
}
} catch (e) { console.error('Failed to toggle done:', e); }
_renderGrid();
},
},
{
label: 'Move to Archive',
icon: _archIcon,
action: async () => {
try {
await fetch(`${API_BASE}/api/email/archive/${em.uid}?folder=${encodeURIComponent(state._libFolder)}${_acct()}`, { method: 'POST' });
} catch (e) { console.error(e); }
await closeAndRemove();
},
},
{ separator: true },
{
// Save the sender to CardDAV contacts. Pulls name + address off the
// list-item (em); falls back to splitting the local-part for a name.
@@ -4708,34 +4736,6 @@ function _showReaderMoreMenu(em, card, reader, anchor) {
}
},
},
{
label: em.is_answered ? 'Not Done' : 'Done',
icon: _checkIcon,
action: async () => {
const newState = !em.is_answered;
em.is_answered = newState;
if (newState) _syncEmailReadState(em.uid, true);
try {
if (newState) {
await fetch(`${API_BASE}/api/email/mark-answered/${em.uid}?folder=${encodeURIComponent(state._libFolder)}${_acct()}`, { method: 'POST' });
await fetch(`${API_BASE}/api/email/mark-read/${em.uid}?folder=${encodeURIComponent(state._libFolder)}${_acct()}`, { method: 'POST' });
} else {
await fetch(`${API_BASE}/api/email/clear-answered/${em.uid}?folder=${encodeURIComponent(state._libFolder)}${_acct()}`, { method: 'POST' });
}
} catch (e) { console.error('Failed to toggle done:', e); }
_renderGrid();
},
},
{
label: 'Archive',
icon: _archIcon,
action: async () => {
try {
await fetch(`${API_BASE}/api/email/archive/${em.uid}?folder=${encodeURIComponent(state._libFolder)}${_acct()}`, { method: 'POST' });
} catch (e) { console.error(e); }
await closeAndRemove();
},
},
{ separator: true },
{
label: 'Move to Spam',