zele 0.3.10 → 0.3.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1 -1
- package/dist/generated/internal/class.js +2 -10
- package/dist/generated/internal/class.js.map +1 -1
- package/dist/generated/internal/prismaNamespace.d.ts +2 -2
- package/dist/generated/internal/prismaNamespace.js +4 -4
- package/dist/mail-tui.js +200 -52
- package/dist/mail-tui.js.map +1 -1
- package/package.json +6 -5
- package/src/cli.ts +1 -1
- package/src/generated/internal/class.ts +2 -10
- package/src/generated/internal/prismaNamespace.ts +4 -4
- package/src/mail-tui.tsx +408 -130
package/dist/mail-tui.js
CHANGED
|
@@ -10,23 +10,33 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
10
10
|
// window.fetch → gets undefined → "fetchImpl is not a function". Fix: ensure
|
|
11
11
|
// window.fetch is set to the native Bun fetch.
|
|
12
12
|
const globalWithWindow = globalThis;
|
|
13
|
-
if (typeof globalThis.fetch === 'function' &&
|
|
14
|
-
|
|
13
|
+
if (typeof globalThis.fetch === 'function' &&
|
|
14
|
+
typeof globalWithWindow.window?.fetch !== 'function') {
|
|
15
|
+
globalWithWindow.window = {
|
|
16
|
+
...globalWithWindow.window,
|
|
17
|
+
fetch: globalThis.fetch,
|
|
18
|
+
};
|
|
15
19
|
}
|
|
16
20
|
import { Action, ActionPanel, Color, Detail, Form, Icon, List, showToast, Toast, useNavigation, showFailureToast, } from 'termcast';
|
|
17
21
|
import { useTerminalDimensions } from '@opentui/react';
|
|
18
22
|
import { useCachedPromise } from '@termcast/utils';
|
|
19
23
|
import { useState, useMemo, useCallback, useEffect } from 'react';
|
|
20
|
-
import { getClients, getClient, listAccounts, login, logout } from './auth.js';
|
|
24
|
+
import { getClients, getClient, listAccounts, login, logout, } from './auth.js';
|
|
21
25
|
import { isTruthy } from './api-utils.js';
|
|
22
|
-
import { renderEmailBody, replyParser, formatDate, formatSender } from './output.js';
|
|
26
|
+
import { renderEmailBody, replyParser, formatDate, formatSender, } from './output.js';
|
|
23
27
|
// ---------------------------------------------------------------------------
|
|
24
28
|
// Constants
|
|
25
29
|
// ---------------------------------------------------------------------------
|
|
26
30
|
const DEFAULT_PAGE_SIZE = 25;
|
|
27
31
|
const MIN_PAGE_SIZE = 10;
|
|
28
32
|
const VISIBLE_ROWS_OFFSET = 6;
|
|
29
|
-
const ACCOUNT_COLORS = [
|
|
33
|
+
const ACCOUNT_COLORS = [
|
|
34
|
+
Color.Blue,
|
|
35
|
+
Color.Green,
|
|
36
|
+
Color.Purple,
|
|
37
|
+
Color.Orange,
|
|
38
|
+
Color.Magenta,
|
|
39
|
+
];
|
|
30
40
|
const ADD_ACCOUNT = '__add_account__';
|
|
31
41
|
const MANAGE_ACCOUNTS = '__manage_accounts__';
|
|
32
42
|
// ---------------------------------------------------------------------------
|
|
@@ -64,16 +74,24 @@ function dateSection(dateStr) {
|
|
|
64
74
|
return 'This Month';
|
|
65
75
|
return 'Older';
|
|
66
76
|
}
|
|
67
|
-
const SECTION_ORDER = [
|
|
77
|
+
const SECTION_ORDER = [
|
|
78
|
+
'Last 10 Minutes',
|
|
79
|
+
'Last Hour',
|
|
80
|
+
'Today',
|
|
81
|
+
'Yesterday',
|
|
82
|
+
'This Week',
|
|
83
|
+
'This Month',
|
|
84
|
+
'Older',
|
|
85
|
+
];
|
|
68
86
|
function threadStatusIcon(thread) {
|
|
69
87
|
const unread = thread.unread;
|
|
70
88
|
const starred = thread.labelIds?.includes('STARRED') ?? false;
|
|
71
89
|
if (unread && starred)
|
|
72
90
|
return { source: Icon.Star, tintColor: Color.Red };
|
|
73
91
|
if (unread)
|
|
74
|
-
return { source: Icon.CircleFilled, tintColor: Color.
|
|
92
|
+
return { source: Icon.CircleFilled, tintColor: Color.Yellow };
|
|
75
93
|
if (starred)
|
|
76
|
-
return { source: Icon.Star, tintColor: Color.
|
|
94
|
+
return { source: Icon.Star, tintColor: Color.Orange };
|
|
77
95
|
return { source: Icon.Circle, tintColor: Color.SecondaryText };
|
|
78
96
|
}
|
|
79
97
|
function getPageSizeFromTerminalHeight(rows) {
|
|
@@ -95,7 +113,7 @@ function useAccounts() {
|
|
|
95
113
|
// ---------------------------------------------------------------------------
|
|
96
114
|
function AccountDropdown({ accounts, value, onChange, onAdded, onRemoved, }) {
|
|
97
115
|
const { push } = useNavigation();
|
|
98
|
-
return (_jsxs(List.Dropdown, { tooltip:
|
|
116
|
+
return (_jsxs(List.Dropdown, { tooltip: 'Account', value: value, onChange: (newValue) => {
|
|
99
117
|
if (newValue === ADD_ACCOUNT) {
|
|
100
118
|
push(_jsx(AddAccount, { onAdded: onAdded }));
|
|
101
119
|
return;
|
|
@@ -105,7 +123,10 @@ function AccountDropdown({ accounts, value, onChange, onAdded, onRemoved, }) {
|
|
|
105
123
|
return;
|
|
106
124
|
}
|
|
107
125
|
onChange(newValue);
|
|
108
|
-
}, children: [_jsx(List.Dropdown.Item, { title:
|
|
126
|
+
}, children: [_jsx(List.Dropdown.Item, { title: 'All Accounts', value: 'all', icon: Icon.Globe }), _jsx(List.Dropdown.Section, { title: 'Accounts', children: accounts.map((a) => (_jsx(List.Dropdown.Item, { title: a.email, value: a.email, icon: {
|
|
127
|
+
source: Icon.Person,
|
|
128
|
+
tintColor: accountColor(a.email),
|
|
129
|
+
} }, a.email))) }), _jsxs(List.Dropdown.Section, { children: [_jsx(List.Dropdown.Item, { title: 'Add Account', value: ADD_ACCOUNT, icon: Icon.Plus }), _jsx(List.Dropdown.Item, { title: 'Manage Accounts', value: MANAGE_ACCOUNTS, icon: Icon.Gear })] })] }));
|
|
109
130
|
}
|
|
110
131
|
// ---------------------------------------------------------------------------
|
|
111
132
|
// Add Account (interactive login)
|
|
@@ -129,7 +150,10 @@ function AddAccount({ onAdded, }) {
|
|
|
129
150
|
return;
|
|
130
151
|
}
|
|
131
152
|
await onAdded?.(result.email);
|
|
132
|
-
await showToast({
|
|
153
|
+
await showToast({
|
|
154
|
+
style: Toast.Style.Success,
|
|
155
|
+
title: `Added ${result.email}`,
|
|
156
|
+
});
|
|
133
157
|
pop();
|
|
134
158
|
};
|
|
135
159
|
useEffect(() => {
|
|
@@ -138,7 +162,7 @@ function AddAccount({ onAdded, }) {
|
|
|
138
162
|
setDidAutoStart(true);
|
|
139
163
|
void handleLogin();
|
|
140
164
|
}, [didAutoStart]);
|
|
141
|
-
return (_jsx(Detail, { navigationTitle:
|
|
165
|
+
return (_jsx(Detail, { navigationTitle: 'Add Account', markdown: `# Add Account\n\n` +
|
|
142
166
|
`The browser opens automatically for Google sign-in.\n\n` +
|
|
143
167
|
`Complete login in the browser, then come back here. ` +
|
|
144
168
|
`This screen waits for the localhost callback and will finish automatically.`, actions: _jsx(ActionPanel, { children: _jsx(Action, { title: isLoggingIn ? 'Waiting for Login...' : 'Open Browser Again', icon: Icon.Globe, onAction: handleLogin }) }) }));
|
|
@@ -155,14 +179,29 @@ function ManageAccounts({ onAdded, onRemoved, }) {
|
|
|
155
179
|
const handleRemoved = async (email) => {
|
|
156
180
|
const result = await logout(email);
|
|
157
181
|
if (result instanceof Error) {
|
|
158
|
-
await showFailureToast(result, {
|
|
182
|
+
await showFailureToast(result, {
|
|
183
|
+
title: `Failed to remove ${email}`,
|
|
184
|
+
});
|
|
159
185
|
return;
|
|
160
186
|
}
|
|
161
187
|
await accounts.revalidate();
|
|
162
188
|
await onRemoved?.(email);
|
|
163
|
-
await showToast({
|
|
189
|
+
await showToast({
|
|
190
|
+
style: Toast.Style.Success,
|
|
191
|
+
title: `Removed ${email}`,
|
|
192
|
+
});
|
|
164
193
|
};
|
|
165
|
-
return (_jsxs(List, { navigationTitle:
|
|
194
|
+
return (_jsxs(List, { navigationTitle: 'Manage Accounts', isLoading: accounts.isLoading, children: [accounts.data?.map((a) => (_jsx(List.Item, { title: a.email, icon: {
|
|
195
|
+
source: Icon.Person,
|
|
196
|
+
tintColor: accountColor(a.email),
|
|
197
|
+
}, accessories: [
|
|
198
|
+
{
|
|
199
|
+
tag: {
|
|
200
|
+
value: a.appId.slice(0, 12) + '...',
|
|
201
|
+
color: Color.SecondaryText,
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
], actions: _jsxs(ActionPanel, { children: [_jsx(Action.CopyToClipboard, { title: 'Copy Email', content: a.email }), _jsx(Action, { title: 'Logout Account', icon: Icon.Trash, style: Action.Style.Destructive, onAction: () => handleRemoved(a.email) })] }) }, `${a.email}-${a.appId}`))), _jsx(List.Item, { title: 'Add Account', icon: Icon.Plus, actions: _jsx(ActionPanel, { children: _jsx(Action.Push, { title: 'Add Account', target: _jsx(AddAccount, { onAdded: handleAdded }) }) }) }, 'add-account')] }));
|
|
166
205
|
}
|
|
167
206
|
// ---------------------------------------------------------------------------
|
|
168
207
|
// Reply Form
|
|
@@ -172,7 +211,10 @@ function ReplyForm({ threadId, account, replyAll, revalidate, }) {
|
|
|
172
211
|
const [isLoading, setIsLoading] = useState(false);
|
|
173
212
|
const handleSubmit = async (values) => {
|
|
174
213
|
if (!values.body?.trim()) {
|
|
175
|
-
await showToast({
|
|
214
|
+
await showToast({
|
|
215
|
+
style: Toast.Style.Failure,
|
|
216
|
+
title: 'Body is required',
|
|
217
|
+
});
|
|
176
218
|
return;
|
|
177
219
|
}
|
|
178
220
|
setIsLoading(true);
|
|
@@ -191,7 +233,7 @@ function ReplyForm({ threadId, account, replyAll, revalidate, }) {
|
|
|
191
233
|
revalidate();
|
|
192
234
|
pop();
|
|
193
235
|
};
|
|
194
|
-
return (_jsx(Form, { isLoading: isLoading, navigationTitle: replyAll ? 'Reply All' : 'Reply', actions: _jsx(ActionPanel, { children: _jsx(Action.SubmitForm, { title:
|
|
236
|
+
return (_jsx(Form, { isLoading: isLoading, navigationTitle: replyAll ? 'Reply All' : 'Reply', actions: _jsx(ActionPanel, { children: _jsx(Action.SubmitForm, { title: 'Send Reply', onSubmit: handleSubmit }) }), children: _jsx(Form.TextArea, { id: 'body', title: 'Message', placeholder: 'Type your reply...' }) }));
|
|
195
237
|
}
|
|
196
238
|
// ---------------------------------------------------------------------------
|
|
197
239
|
// Forward Form
|
|
@@ -201,11 +243,17 @@ function ForwardForm({ threadId, account, revalidate, }) {
|
|
|
201
243
|
const [isLoading, setIsLoading] = useState(false);
|
|
202
244
|
const handleSubmit = async (values) => {
|
|
203
245
|
if (!values.to?.trim()) {
|
|
204
|
-
await showToast({
|
|
246
|
+
await showToast({
|
|
247
|
+
style: Toast.Style.Failure,
|
|
248
|
+
title: 'Recipient is required',
|
|
249
|
+
});
|
|
205
250
|
return;
|
|
206
251
|
}
|
|
207
252
|
setIsLoading(true);
|
|
208
|
-
const recipients = values.to
|
|
253
|
+
const recipients = values.to
|
|
254
|
+
.split(',')
|
|
255
|
+
.map((e) => ({ email: e.trim() }))
|
|
256
|
+
.filter((e) => e.email);
|
|
209
257
|
const { client } = await getClient([account]);
|
|
210
258
|
const result = await client.forwardThread({
|
|
211
259
|
threadId,
|
|
@@ -217,11 +265,14 @@ function ForwardForm({ threadId, account, revalidate, }) {
|
|
|
217
265
|
await showFailureToast(result, { title: 'Failed to forward' });
|
|
218
266
|
return;
|
|
219
267
|
}
|
|
220
|
-
await showToast({
|
|
268
|
+
await showToast({
|
|
269
|
+
style: Toast.Style.Success,
|
|
270
|
+
title: `Forwarded to ${values.to}`,
|
|
271
|
+
});
|
|
221
272
|
revalidate();
|
|
222
273
|
pop();
|
|
223
274
|
};
|
|
224
|
-
return (_jsxs(Form, { isLoading: isLoading, navigationTitle:
|
|
275
|
+
return (_jsxs(Form, { isLoading: isLoading, navigationTitle: 'Forward', actions: _jsx(ActionPanel, { children: _jsx(Action.SubmitForm, { title: 'Forward', onSubmit: handleSubmit }) }), children: [_jsx(Form.TextField, { id: 'to', title: 'To', placeholder: 'recipient@example.com' }), _jsx(Form.TextArea, { id: 'body', title: 'Message', placeholder: 'Optional message to prepend...' })] }));
|
|
225
276
|
}
|
|
226
277
|
// ---------------------------------------------------------------------------
|
|
227
278
|
// Thread Detail (full thread view, pushed via Enter)
|
|
@@ -235,7 +286,7 @@ function ThreadDetail({ threadId, account, revalidate, }) {
|
|
|
235
286
|
return result.parsed;
|
|
236
287
|
}, [threadId, account]);
|
|
237
288
|
if (thread.isLoading || !thread.data) {
|
|
238
|
-
return _jsx(Detail, { markdown:
|
|
289
|
+
return _jsx(Detail, { markdown: '', navigationTitle: 'Loading...' });
|
|
239
290
|
}
|
|
240
291
|
const t = thread.data;
|
|
241
292
|
const messages = t.messages;
|
|
@@ -255,7 +306,9 @@ function ThreadDetail({ threadId, account, revalidate, }) {
|
|
|
255
306
|
else {
|
|
256
307
|
body = renderEmailBody(msg.body, msg.mimeType);
|
|
257
308
|
}
|
|
258
|
-
return [heading, attachmentLine, '', body]
|
|
309
|
+
return [heading, attachmentLine, '', body]
|
|
310
|
+
.filter((l) => l !== null)
|
|
311
|
+
.join('\n');
|
|
259
312
|
});
|
|
260
313
|
const markdown = `# ${t.subject}\n\n---\n\n` + parts.join('\n\n---\n\n');
|
|
261
314
|
// Collect unique participants
|
|
@@ -269,7 +322,10 @@ function ThreadDetail({ threadId, account, revalidate, }) {
|
|
|
269
322
|
.filter((l) => typeof l === 'string' && !l.startsWith('Label_')) // skip internal IDs
|
|
270
323
|
.slice(0, 10);
|
|
271
324
|
const latestMsg = messages[messages.length - 1];
|
|
272
|
-
return (_jsx(Detail, { navigationTitle: t.subject, markdown: markdown, metadata: _jsxs(Detail.Metadata, { children: [_jsx(Detail.Metadata.Label, { title:
|
|
325
|
+
return (_jsx(Detail, { navigationTitle: t.subject, markdown: markdown, metadata: _jsxs(Detail.Metadata, { children: [_jsx(Detail.Metadata.Label, { title: 'From', text: formatSender(latestMsg.from) }), _jsx(Detail.Metadata.Label, { title: 'To', text: latestMsg.to.map((r) => r.name || r.email).join(', ') }), latestMsg.cc && latestMsg.cc.length > 0 && (_jsx(Detail.Metadata.Label, { title: 'Cc', text: latestMsg.cc.map((r) => r.name || r.email).join(', ') })), _jsx(Detail.Metadata.Label, { title: 'Date', text: latestMsg.date }), _jsx(Detail.Metadata.Separator, {}), _jsx(Detail.Metadata.Label, { title: 'Messages', text: String(t.messageCount) }), _jsx(Detail.Metadata.Label, { title: 'Participants', text: [...participants.values()].join(', ') }), labels.length > 0 && (_jsx(Detail.Metadata.TagList, { title: 'Labels', children: labels.map((l) => (_jsx(Detail.Metadata.TagList.Item, { text: l, color: labelColor(l) }, l))) })), _jsx(Detail.Metadata.Separator, {}), _jsx(Detail.Metadata.Label, { title: 'Thread ID', text: t.id }), _jsx(Detail.Metadata.Label, { title: 'Account', text: account })] }), actions: _jsxs(ActionPanel, { children: [_jsxs(ActionPanel.Section, { title: 'Reply & Forward', children: [_jsx(Action.Push, { title: 'Reply', icon: Icon.Reply, shortcut: { modifiers: ['ctrl'], key: 'r' }, target: _jsx(ReplyForm, { threadId: threadId, account: account, revalidate: revalidate }) }), _jsx(Action.Push, { title: 'Reply All', icon: Icon.Reply, shortcut: {
|
|
326
|
+
modifiers: ['ctrl', 'shift'],
|
|
327
|
+
key: 'r',
|
|
328
|
+
}, target: _jsx(ReplyForm, { threadId: threadId, account: account, replyAll: true, revalidate: revalidate }) }), _jsx(Action.Push, { title: 'Forward', icon: Icon.Forward, shortcut: { modifiers: ['ctrl'], key: 'f' }, target: _jsx(ForwardForm, { threadId: threadId, account: account, revalidate: revalidate }) })] }), _jsxs(ActionPanel.Section, { title: 'Copy', children: [_jsx(Action.CopyToClipboard, { title: 'Copy Thread ID', content: t.id }), _jsx(Action.CopyToClipboard, { title: 'Copy Subject', content: t.subject }), latestMsg && (_jsx(Action.CopyToClipboard, { title: 'Copy Email Body', content: renderEmailBody(latestMsg.body, latestMsg.mimeType) }))] })] }) }));
|
|
273
329
|
}
|
|
274
330
|
// ---------------------------------------------------------------------------
|
|
275
331
|
// Main Command
|
|
@@ -298,10 +354,15 @@ export default function Command() {
|
|
|
298
354
|
pageToken: pageToken || undefined,
|
|
299
355
|
});
|
|
300
356
|
if (result instanceof Error) {
|
|
301
|
-
await showFailureToast(result, {
|
|
357
|
+
await showFailureToast(result, {
|
|
358
|
+
title: 'Failed to fetch emails',
|
|
359
|
+
});
|
|
302
360
|
return { data: [], hasMore: false };
|
|
303
361
|
}
|
|
304
|
-
const data = result.threads.map((t) => ({
|
|
362
|
+
const data = result.threads.map((t) => ({
|
|
363
|
+
...t,
|
|
364
|
+
account: email,
|
|
365
|
+
}));
|
|
305
366
|
return {
|
|
306
367
|
data,
|
|
307
368
|
hasMore: !!result.nextPageToken,
|
|
@@ -354,7 +415,10 @@ export default function Command() {
|
|
|
354
415
|
return {
|
|
355
416
|
data: merged,
|
|
356
417
|
hasMore,
|
|
357
|
-
cursor: {
|
|
418
|
+
cursor: {
|
|
419
|
+
mode: 'multi',
|
|
420
|
+
nextByAccount,
|
|
421
|
+
},
|
|
358
422
|
};
|
|
359
423
|
};
|
|
360
424
|
}, [searchText, selectedAccount, pageSize], { keepPreviousData: true });
|
|
@@ -390,7 +454,9 @@ export default function Command() {
|
|
|
390
454
|
const multiAccount = accountList.length > 1;
|
|
391
455
|
// Selection helpers
|
|
392
456
|
const toggleSelection = useCallback((threadId) => {
|
|
393
|
-
setSelectedThreads((prev) => prev.includes(threadId)
|
|
457
|
+
setSelectedThreads((prev) => prev.includes(threadId)
|
|
458
|
+
? prev.filter((id) => id !== threadId)
|
|
459
|
+
: [...prev, threadId]);
|
|
394
460
|
}, []);
|
|
395
461
|
// Bulk actions
|
|
396
462
|
const handleBulkAction = useCallback(async (actionName, fn) => {
|
|
@@ -410,68 +476,127 @@ export default function Command() {
|
|
|
410
476
|
const { client } = await getClient([acct]);
|
|
411
477
|
const result = await fn(client, ids);
|
|
412
478
|
if (result instanceof Error) {
|
|
413
|
-
await showFailureToast(result, {
|
|
479
|
+
await showFailureToast(result, {
|
|
480
|
+
title: `Failed to ${actionName}`,
|
|
481
|
+
});
|
|
414
482
|
return;
|
|
415
483
|
}
|
|
416
484
|
}
|
|
417
|
-
await showToast({
|
|
485
|
+
await showToast({
|
|
486
|
+
style: Toast.Style.Success,
|
|
487
|
+
title: `${actionName}: ${selectedThreads.length} thread(s)`,
|
|
488
|
+
});
|
|
418
489
|
setSelectedThreads([]);
|
|
419
490
|
revalidate();
|
|
420
491
|
}, [selectedThreads, allThreads, revalidate]);
|
|
421
|
-
return (_jsx(List, { isLoading: isLoading || accounts.isLoading, isShowingDetail: isShowingDetail, searchBarPlaceholder:
|
|
492
|
+
return (_jsx(List, { isLoading: isLoading || accounts.isLoading, isShowingDetail: isShowingDetail, searchBarPlaceholder: 'Search emails...', onSearchTextChange: setSearchText, throttle: true, pagination: pagination ? { ...pagination, pageSize } : undefined, searchBarAccessory: accountList.length > 0 ? (_jsx(AccountDropdown, { accounts: accountList, value: selectedAccount, onChange: setSelectedAccount, onAdded: handleAccountAdded, onRemoved: handleAccountRemoved })) : undefined, children: sections.map((section) => (_jsx(List.Section, { title: section.name, children: section.threads.map((thread) => {
|
|
422
493
|
const isSelected = selectedThreads.includes(thread.id);
|
|
423
494
|
const hasSelection = selectedThreads.length > 0;
|
|
424
495
|
// Icon: selection mode or status
|
|
425
496
|
const icon = hasSelection
|
|
426
|
-
? {
|
|
497
|
+
? {
|
|
498
|
+
source: isSelected ? Icon.CheckCircle : Icon.Circle,
|
|
499
|
+
tintColor: isSelected ? Color.Blue : Color.SecondaryText,
|
|
500
|
+
}
|
|
427
501
|
: threadStatusIcon(thread);
|
|
428
502
|
// Accessories
|
|
429
503
|
const accessories = [];
|
|
430
504
|
if (thread.messageCount > 1) {
|
|
431
|
-
accessories.push({
|
|
505
|
+
accessories.push({
|
|
506
|
+
tag: {
|
|
507
|
+
value: String(thread.messageCount),
|
|
508
|
+
color: Color.SecondaryText,
|
|
509
|
+
},
|
|
510
|
+
});
|
|
432
511
|
}
|
|
433
512
|
if (multiAccount || selectedAccount === 'all') {
|
|
434
|
-
accessories.push({
|
|
513
|
+
accessories.push({
|
|
514
|
+
tag: {
|
|
515
|
+
value: thread.account.split('@')[0] ?? thread.account,
|
|
516
|
+
color: accountColor(thread.account),
|
|
517
|
+
},
|
|
518
|
+
});
|
|
435
519
|
}
|
|
436
520
|
accessories.push({ text: formatDate(thread.date) });
|
|
437
521
|
// Detail panel: latest message body as markdown
|
|
438
|
-
const detail = isShowingDetail ? (_jsx(List.Item.Detail, { markdown: `# ${thread.subject}\n\n${thread.snippet}`, metadata: _jsxs(List.Item.Detail.Metadata, { children: [_jsx(List.Item.Detail.Metadata.Label, { title:
|
|
522
|
+
const detail = isShowingDetail ? (_jsx(List.Item.Detail, { markdown: `# ${thread.subject}\n\n${thread.snippet}`, metadata: _jsxs(List.Item.Detail.Metadata, { children: [_jsx(List.Item.Detail.Metadata.Label, { title: 'From', text: formatSender(thread.from) }), _jsx(List.Item.Detail.Metadata.Label, { title: 'Date', text: thread.date }), _jsx(List.Item.Detail.Metadata.Separator, {}), thread.labelIds.length > 0 && (_jsx(List.Item.Detail.Metadata.TagList, { title: 'Labels', children: thread.labelIds
|
|
439
523
|
.filter((l) => !l.startsWith('Label_'))
|
|
440
524
|
.slice(0, 8)
|
|
441
|
-
.map((l) => (_jsx(List.Item.Detail.Metadata.TagList.Item, { text: l, color: labelColor(l) }, l))) })), _jsx(List.Item.Detail.Metadata.Separator, {}), _jsx(List.Item.Detail.Metadata.Label, { title:
|
|
442
|
-
return (_jsx(List.Item, { title: thread.subject || '(no subject)', subtitle: formatSender(thread.from), icon: icon, accessories: accessories, keywords: [
|
|
525
|
+
.map((l) => (_jsx(List.Item.Detail.Metadata.TagList.Item, { text: l, color: labelColor(l) }, l))) })), _jsx(List.Item.Detail.Metadata.Separator, {}), _jsx(List.Item.Detail.Metadata.Label, { title: 'Messages', text: String(thread.messageCount) }), _jsx(List.Item.Detail.Metadata.Label, { title: 'Thread ID', text: thread.id }), multiAccount && (_jsx(List.Item.Detail.Metadata.Label, { title: 'Account', text: thread.account }))] }) })) : undefined;
|
|
526
|
+
return (_jsx(List.Item, { title: thread.subject || '(no subject)', subtitle: formatSender(thread.from), icon: icon, accessories: accessories, keywords: [
|
|
527
|
+
thread.from.email,
|
|
528
|
+
thread.from.name ?? '',
|
|
529
|
+
thread.account,
|
|
530
|
+
], detail: detail, actions: _jsxs(ActionPanel, { children: [hasSelection && (_jsxs(ActionPanel.Section, { title: 'Selection', children: [_jsx(Action, { title: isSelected ? 'Deselect Thread' : 'Select Thread', icon: isSelected ? Icon.CheckCircle : Icon.Circle, onAction: () => toggleSelection(thread.id) }), _jsx(Action, { title: `Archive ${selectedThreads.length} Selected`, icon: Icon.Tray, onAction: () => handleBulkAction('Archived', (c, ids) => c.archive({
|
|
531
|
+
threadIds: ids,
|
|
532
|
+
})) }), _jsx(Action, { title: `Mark ${selectedThreads.length} as Read`, icon: Icon.Eye, onAction: () => handleBulkAction('Marked as read', (c, ids) => c.markAsRead({
|
|
533
|
+
threadIds: ids,
|
|
534
|
+
})) }), _jsx(Action, { title: `Star ${selectedThreads.length} Selected`, icon: Icon.Star, onAction: () => handleBulkAction('Starred', (c, ids) => c.star({
|
|
535
|
+
threadIds: ids,
|
|
536
|
+
})) }), _jsx(Action, { title: `Trash ${selectedThreads.length} Selected`, icon: Icon.Trash, style: Action.Style.Destructive, onAction: () => handleBulkAction('Trashed', async (c, ids) => {
|
|
443
537
|
for (const id of ids) {
|
|
444
|
-
await c.trash({
|
|
538
|
+
await c.trash({
|
|
539
|
+
threadId: id,
|
|
540
|
+
});
|
|
445
541
|
}
|
|
446
|
-
}) }), _jsx(Action, { title:
|
|
542
|
+
}) }), _jsx(Action, { title: 'Deselect All', icon: Icon.XMarkCircle, onAction: () => setSelectedThreads([]) })] })), _jsxs(ActionPanel.Section, { children: [_jsx(Action.Push, { title: 'Open Thread', icon: Icon.Eye, target: _jsx(ThreadDetail, { threadId: thread.id, account: thread.account, revalidate: revalidate }) }), !hasSelection && (_jsx(Action, { title: 'Select Thread', icon: Icon.CheckCircle, shortcut: {
|
|
543
|
+
modifiers: ['ctrl'],
|
|
544
|
+
key: 'x',
|
|
545
|
+
}, onAction: () => toggleSelection(thread.id) })), _jsx(Action, { title: thread.unread ? 'Mark as Read' : 'Mark as Unread', icon: thread.unread ? Icon.Eye : Icon.EyeDisabled, shortcut: {
|
|
546
|
+
modifiers: ['ctrl'],
|
|
547
|
+
key: 'u',
|
|
548
|
+
}, onAction: async () => {
|
|
447
549
|
const { client } = await getClient([thread.account]);
|
|
448
550
|
const result = thread.unread
|
|
449
|
-
? await client.markAsRead({
|
|
450
|
-
|
|
551
|
+
? await client.markAsRead({
|
|
552
|
+
threadIds: [thread.id],
|
|
553
|
+
})
|
|
554
|
+
: await client.markAsUnread({
|
|
555
|
+
threadIds: [thread.id],
|
|
556
|
+
});
|
|
451
557
|
if (result instanceof Error) {
|
|
452
558
|
await showFailureToast(result);
|
|
453
559
|
return;
|
|
454
560
|
}
|
|
455
561
|
await showToast({
|
|
456
562
|
style: Toast.Style.Success,
|
|
457
|
-
title: thread.unread
|
|
563
|
+
title: thread.unread
|
|
564
|
+
? 'Marked as read'
|
|
565
|
+
: 'Marked as unread',
|
|
458
566
|
});
|
|
459
567
|
revalidate();
|
|
460
|
-
} }), _jsx(Action, { title:
|
|
568
|
+
} }), _jsx(Action, { title: 'Archive', icon: Icon.Tray, shortcut: {
|
|
569
|
+
modifiers: ['ctrl'],
|
|
570
|
+
key: 'e',
|
|
571
|
+
}, onAction: async () => {
|
|
461
572
|
const { client } = await getClient([thread.account]);
|
|
462
|
-
const result = await client.archive({
|
|
573
|
+
const result = await client.archive({
|
|
574
|
+
threadIds: [thread.id],
|
|
575
|
+
});
|
|
463
576
|
if (result instanceof Error) {
|
|
464
577
|
await showFailureToast(result);
|
|
465
578
|
return;
|
|
466
579
|
}
|
|
467
|
-
await showToast({
|
|
580
|
+
await showToast({
|
|
581
|
+
style: Toast.Style.Success,
|
|
582
|
+
title: 'Archived',
|
|
583
|
+
});
|
|
468
584
|
revalidate();
|
|
469
|
-
} }), _jsx(Action, { title: thread.labelIds.includes('STARRED')
|
|
585
|
+
} }), _jsx(Action, { title: thread.labelIds.includes('STARRED')
|
|
586
|
+
? 'Unstar'
|
|
587
|
+
: 'Star', icon: Icon.Star, shortcut: {
|
|
588
|
+
modifiers: ['ctrl'],
|
|
589
|
+
key: 's',
|
|
590
|
+
}, onAction: async () => {
|
|
470
591
|
const { client } = await getClient([thread.account]);
|
|
471
592
|
const isStarred = thread.labelIds.includes('STARRED');
|
|
472
593
|
const result = isStarred
|
|
473
|
-
? await client.unstar({
|
|
474
|
-
|
|
594
|
+
? await client.unstar({
|
|
595
|
+
threadIds: [thread.id],
|
|
596
|
+
})
|
|
597
|
+
: await client.star({
|
|
598
|
+
threadIds: [thread.id],
|
|
599
|
+
});
|
|
475
600
|
if (result instanceof Error) {
|
|
476
601
|
await showFailureToast(result);
|
|
477
602
|
return;
|
|
@@ -481,12 +606,35 @@ export default function Command() {
|
|
|
481
606
|
title: isStarred ? 'Unstarred' : 'Starred',
|
|
482
607
|
});
|
|
483
608
|
revalidate();
|
|
484
|
-
} })] }), _jsxs(ActionPanel.Section, { title:
|
|
609
|
+
} })] }), _jsxs(ActionPanel.Section, { title: 'Reply & Forward', children: [_jsx(Action.Push, { title: 'Reply', icon: Icon.Reply, shortcut: {
|
|
610
|
+
modifiers: ['ctrl'],
|
|
611
|
+
key: 'r',
|
|
612
|
+
}, target: _jsx(ReplyForm, { threadId: thread.id, account: thread.account, revalidate: revalidate }) }), _jsx(Action.Push, { title: 'Reply All', icon: Icon.Reply, shortcut: {
|
|
613
|
+
modifiers: ['ctrl', 'shift'],
|
|
614
|
+
key: 'r',
|
|
615
|
+
}, target: _jsx(ReplyForm, { threadId: thread.id, account: thread.account, replyAll: true, revalidate: revalidate }) }), _jsx(Action.Push, { title: 'Forward', icon: Icon.Forward, shortcut: {
|
|
616
|
+
modifiers: ['ctrl'],
|
|
617
|
+
key: 'f',
|
|
618
|
+
}, target: _jsx(ForwardForm, { threadId: thread.id, account: thread.account, revalidate: revalidate }) })] }), _jsxs(ActionPanel.Section, { title: 'Copy', children: [_jsx(Action.CopyToClipboard, { title: 'Copy Thread ID', content: thread.id }), _jsx(Action.CopyToClipboard, { title: 'Copy Subject', content: thread.subject }), _jsx(Action.CopyToClipboard, { title: 'Copy Sender Email', content: thread.from.email })] }), _jsx(ActionPanel.Section, { children: _jsx(Action, { title: 'Trash', icon: Icon.Trash, style: Action.Style.Destructive, shortcut: {
|
|
619
|
+
modifiers: ['ctrl'],
|
|
620
|
+
key: 'backspace',
|
|
621
|
+
}, onAction: async () => {
|
|
485
622
|
const { client } = await getClient([thread.account]);
|
|
486
|
-
await client.trash({
|
|
487
|
-
|
|
623
|
+
await client.trash({
|
|
624
|
+
threadId: thread.id,
|
|
625
|
+
});
|
|
626
|
+
await showToast({
|
|
627
|
+
style: Toast.Style.Success,
|
|
628
|
+
title: 'Trashed',
|
|
629
|
+
});
|
|
488
630
|
revalidate();
|
|
489
|
-
} }) }), _jsxs(ActionPanel.Section, { children: [_jsx(Action, { title:
|
|
631
|
+
} }) }), _jsxs(ActionPanel.Section, { children: [_jsx(Action, { title: 'Refresh', icon: Icon.ArrowClockwise, shortcut: {
|
|
632
|
+
modifiers: ['ctrl', 'shift'],
|
|
633
|
+
key: 'r',
|
|
634
|
+
}, onAction: () => revalidate() }), _jsx(Action, { title: 'Toggle Detail', icon: Icon.Sidebar, shortcut: {
|
|
635
|
+
modifiers: ['ctrl'],
|
|
636
|
+
key: 'd',
|
|
637
|
+
}, onAction: () => setIsShowingDetail((v) => !v) })] })] }) }, `${thread.account}-${thread.id}`));
|
|
490
638
|
}) }, section.name))) }));
|
|
491
639
|
}
|
|
492
640
|
// ---------------------------------------------------------------------------
|