mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-07-15 21:11:08 +02:00
Merge pull request #650 from rowboatlabs/mail-enhancements
feat(email): drafts, search, read-state controls & Superhuman-style shortcuts
This commit is contained in:
commit
3ba94402d3
8 changed files with 2064 additions and 259 deletions
|
|
@ -74,7 +74,7 @@ import { summarizeMeeting } from '@x/core/dist/knowledge/summarize_meeting.js';
|
|||
import { getAccessToken } from '@x/core/dist/auth/tokens.js';
|
||||
import { getRowboatConfig } from '@x/core/dist/config/rowboat.js';
|
||||
import { runLiveNoteAgent } from '@x/core/dist/knowledge/live-note/runner.js';
|
||||
import { listImportantThreads, listEverythingElseThreads, saveMessageBodyHeight, triggerSync as triggerGmailSync, sendThreadReply, archiveThread, trashThread, markThreadRead, getAccountEmail, getAccountName, getConnectionStatus as getGmailConnectionStatus } from '@x/core/dist/knowledge/sync_gmail.js';
|
||||
import { listImportantThreads, listEverythingElseThreads, saveMessageBodyHeight, triggerSync as triggerGmailSync, sendThreadReply, saveThreadDraft, deleteThreadDraft, listDraftThreads, searchThreads, archiveThread, trashThread, markThreadRead, downloadAttachment, getAccountEmail, getAccountName, getConnectionStatus as getGmailConnectionStatus } from '@x/core/dist/knowledge/sync_gmail.js';
|
||||
import { searchContacts as searchGmailContacts, warmContactIndex } from '@x/core/dist/knowledge/gmail_contacts.js';
|
||||
import { searchSentContacts, warmSentContacts } from '@x/core/dist/knowledge/gmail_sent_contacts.js';
|
||||
import { getGoogleDocsConnectionStatus, importGoogleDoc, syncGoogleDocDown, syncGoogleDocUp, getGoogleDocLink } from '@x/core/dist/knowledge/google_docs.js';
|
||||
|
|
@ -782,6 +782,18 @@ export function setupIpcHandlers() {
|
|||
'gmail:sendReply': async (_event, args) => {
|
||||
return sendThreadReply(args);
|
||||
},
|
||||
'gmail:saveDraft': async (_event, args) => {
|
||||
return saveThreadDraft(args);
|
||||
},
|
||||
'gmail:deleteDraft': async (_event, args) => {
|
||||
return deleteThreadDraft(args.draftId);
|
||||
},
|
||||
'gmail:getDrafts': async () => {
|
||||
return listDraftThreads();
|
||||
},
|
||||
'gmail:search': async (_event, args) => {
|
||||
return searchThreads(args.query, { limit: args.limit });
|
||||
},
|
||||
'gmail:getConnectionStatus': async () => {
|
||||
return getGmailConnectionStatus();
|
||||
},
|
||||
|
|
@ -798,7 +810,10 @@ export function setupIpcHandlers() {
|
|||
return trashThread(args.threadId);
|
||||
},
|
||||
'gmail:markThreadRead': async (_event, args) => {
|
||||
return markThreadRead(args.threadId);
|
||||
return markThreadRead(args.threadId, args.read);
|
||||
},
|
||||
'gmail:downloadAttachment': async (_event, args) => {
|
||||
return downloadAttachment(args);
|
||||
},
|
||||
'gmail:saveMessageHeight': async (_event, args) => {
|
||||
saveMessageBodyHeight(args.threadId, args.messageId, args.height);
|
||||
|
|
|
|||
|
|
@ -169,6 +169,25 @@
|
|||
color: var(--gm-placeholder);
|
||||
}
|
||||
|
||||
.gmail-search-clear {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
background: transparent;
|
||||
color: var(--gm-text-muted);
|
||||
cursor: pointer;
|
||||
transition: background 120ms ease, color 120ms ease;
|
||||
}
|
||||
|
||||
.gmail-search-clear:hover {
|
||||
background: var(--gm-icon-hover-bg);
|
||||
color: var(--gm-text);
|
||||
}
|
||||
|
||||
.gmail-icon-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
|
@ -205,6 +224,46 @@
|
|||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Native list virtualization: offscreen rows skip layout and paint entirely.
|
||||
Applied only to rows without a mounted ThreadDetail (those hold iframes and
|
||||
composers, which must keep rendering while offscreen). */
|
||||
.gmail-row-group-cv {
|
||||
content-visibility: auto;
|
||||
contain-intrinsic-size: auto 40px;
|
||||
}
|
||||
|
||||
/* While the list is scrolling, rows ignore the pointer so hover restyles and
|
||||
prefetch timers don't compete with frame rendering. */
|
||||
.gmail-shell[data-scrolling] .gmail-row-shell {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Archived/trashed rows slide out and collapse before removal. Removing the
|
||||
class (failed action) snaps the row back. */
|
||||
.gmail-row-group-leaving {
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
animation: gmail-row-leave 160ms ease-in forwards;
|
||||
}
|
||||
|
||||
@keyframes gmail-row-leave {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
max-height: 48px;
|
||||
}
|
||||
60% {
|
||||
opacity: 0;
|
||||
transform: translateX(32px);
|
||||
max-height: 48px;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: translateX(32px);
|
||||
max-height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.gmail-list-header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
|
|
@ -319,6 +378,20 @@
|
|||
background: var(--gm-bg-row-selected-hover);
|
||||
}
|
||||
|
||||
/* The j/k keyboard cursor. Declared after the hover rules (same specificity)
|
||||
so the focus ring survives hovering the focused row. */
|
||||
.gmail-row-focused,
|
||||
.gmail-row-focused:hover {
|
||||
background: var(--gm-bg-row-hover);
|
||||
box-shadow: inset 0 0 0 1px var(--gm-accent);
|
||||
}
|
||||
|
||||
.gmail-row-selected.gmail-row-focused,
|
||||
.gmail-row-selected.gmail-row-focused:hover {
|
||||
background: var(--gm-bg-row-selected);
|
||||
box-shadow: inset 2px 0 0 var(--gm-accent), inset 0 0 0 1px var(--gm-accent);
|
||||
}
|
||||
|
||||
.gmail-row-unread {
|
||||
color: var(--gm-text);
|
||||
}
|
||||
|
|
@ -394,12 +467,50 @@
|
|||
border-top: 1px solid var(--gm-border);
|
||||
border-bottom: 1px solid var(--gm-border);
|
||||
box-shadow: inset 2px 0 0 var(--gm-accent);
|
||||
/* Replays whenever the detail is shown — hidden details are display: none,
|
||||
so un-hiding restarts the animation. */
|
||||
animation: gmail-detail-open 140ms ease-out;
|
||||
}
|
||||
|
||||
.gmail-detail-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@keyframes gmail-detail-open {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-6px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* The inline reply/forward composer pops in from below the thread. */
|
||||
.gmail-compose-inline {
|
||||
animation: gmail-compose-open 140ms ease-out;
|
||||
}
|
||||
|
||||
@keyframes gmail-compose-open {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(8px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.gmail-detail-inline,
|
||||
.gmail-compose-inline,
|
||||
.gmail-row-group-leaving {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
.gmail-detail-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue