zele 0.3.12 → 0.3.13

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/mail-tui.js CHANGED
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  // Termcast email extension — browse and read emails in a Raycast-like TUI.
3
3
  // Uses List with detail view, sections for date grouping, infinite scroll via
4
4
  // useCachedPromise pagination, and a dropdown for account selection.
@@ -30,6 +30,9 @@ import { renderEmailBody, replyParser, formatDate, formatSender, } from './outpu
30
30
  const DEFAULT_PAGE_SIZE = 25;
31
31
  const MIN_PAGE_SIZE = 10;
32
32
  const VISIBLE_ROWS_OFFSET = 6;
33
+ /** Spacing mode for the mail list. 'relaxed' renders each item as 3 lines. */
34
+ const LIST_SPACING_MODE = 'relaxed';
35
+ const LINES_PER_ITEM = LIST_SPACING_MODE === 'relaxed' ? 3 : 1;
33
36
  const ACCOUNT_COLORS = [
34
37
  Color.Blue,
35
38
  Color.Green,
@@ -97,7 +100,9 @@ function threadStatusIcon(thread) {
97
100
  function getPageSizeFromTerminalHeight(rows) {
98
101
  if (typeof rows !== 'number' || rows <= 0)
99
102
  return DEFAULT_PAGE_SIZE;
100
- return Math.max(MIN_PAGE_SIZE, rows - VISIBLE_ROWS_OFFSET);
103
+ const visibleRows = rows - VISIBLE_ROWS_OFFSET;
104
+ const itemCount = Math.floor(visibleRows / LINES_PER_ITEM);
105
+ return Math.max(MIN_PAGE_SIZE, itemCount);
101
106
  }
102
107
  // ---------------------------------------------------------------------------
103
108
  // Data fetching
@@ -126,7 +131,7 @@ function AccountDropdown({ accounts, value, onChange, onAdded, onRemoved, }) {
126
131
  }, 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
132
  source: Icon.Person,
128
133
  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 })] })] }));
134
+ } }, a.email))) }), _jsxs(List.Dropdown.Section, { title: 'Manage Accounts', 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 })] })] }));
130
135
  }
131
136
  // ---------------------------------------------------------------------------
132
137
  // Add Account (interactive login)
@@ -201,83 +206,77 @@ function ManageAccounts({ onAdded, onRemoved, }) {
201
206
  color: Color.SecondaryText,
202
207
  },
203
208
  },
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')] }));
209
+ ], actions: _jsxs(ActionPanel, { children: [_jsx(Action.CopyToClipboard, { title: 'Copy Email', content: a.email }), _jsx(Action, { title: 'Logout Account', icon: Icon.Trash, 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')] }));
205
210
  }
206
- // ---------------------------------------------------------------------------
207
- // Reply Form
208
- // ---------------------------------------------------------------------------
209
- function ReplyForm({ threadId, account, replyAll, revalidate, }) {
211
+ function ComposeForm({ mode, initialAccount, accounts, onSent }) {
210
212
  const { pop } = useNavigation();
211
213
  const [isLoading, setIsLoading] = useState(false);
214
+ const [selectedAccount, setSelectedAccount] = useState(initialAccount);
215
+ const navigationTitle = mode.type === 'forward'
216
+ ? 'Forward'
217
+ : mode.replyAll
218
+ ? 'Reply All'
219
+ : 'Reply';
220
+ const bodyPlaceholder = mode.type === 'forward'
221
+ ? 'Add a message (optional)...'
222
+ : 'Type your reply...';
212
223
  const handleSubmit = async (values) => {
213
- if (!values.body?.trim()) {
224
+ // Validate based on mode
225
+ if (mode.type === 'forward' && !values.to?.trim()) {
214
226
  await showToast({
215
227
  style: Toast.Style.Failure,
216
- title: 'Body is required',
228
+ title: 'Recipient is required',
217
229
  });
218
230
  return;
219
231
  }
220
- setIsLoading(true);
221
- const { client } = await getClient([account]);
222
- const result = await client.replyToThread({
223
- threadId,
224
- body: values.body,
225
- replyAll,
226
- });
227
- setIsLoading(false);
228
- if (result instanceof Error) {
229
- await showFailureToast(result, { title: 'Failed to send reply' });
230
- return;
231
- }
232
- await showToast({ style: Toast.Style.Success, title: 'Reply sent' });
233
- revalidate();
234
- pop();
235
- };
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...' }) }));
237
- }
238
- // ---------------------------------------------------------------------------
239
- // Forward Form
240
- // ---------------------------------------------------------------------------
241
- function ForwardForm({ threadId, account, revalidate, }) {
242
- const { pop } = useNavigation();
243
- const [isLoading, setIsLoading] = useState(false);
244
- const handleSubmit = async (values) => {
245
- if (!values.to?.trim()) {
232
+ if (mode.type !== 'forward' && !values.body?.trim()) {
246
233
  await showToast({
247
234
  style: Toast.Style.Failure,
248
- title: 'Recipient is required',
235
+ title: 'Message is required',
249
236
  });
250
237
  return;
251
238
  }
252
239
  setIsLoading(true);
253
- const recipients = values.to
254
- .split(',')
255
- .map((e) => ({ email: e.trim() }))
256
- .filter((e) => e.email);
257
- const { client } = await getClient([account]);
258
- const result = await client.forwardThread({
259
- threadId,
260
- to: recipients,
261
- body: values.body || undefined,
262
- });
240
+ const { client } = await getClient([selectedAccount]);
241
+ let result;
242
+ if (mode.type === 'forward') {
243
+ const recipients = (values.to ?? '')
244
+ .split(',')
245
+ .map((e) => ({ email: e.trim() }))
246
+ .filter((e) => e.email);
247
+ result = await client.forwardThread({
248
+ threadId: mode.threadId,
249
+ to: recipients,
250
+ body: values.body || undefined,
251
+ });
252
+ }
253
+ else {
254
+ result = await client.replyToThread({
255
+ threadId: mode.threadId,
256
+ body: values.body ?? '',
257
+ replyAll: mode.replyAll,
258
+ });
259
+ }
263
260
  setIsLoading(false);
264
261
  if (result instanceof Error) {
265
- await showFailureToast(result, { title: 'Failed to forward' });
262
+ await showFailureToast(result, {
263
+ title: mode.type === 'forward' ? 'Failed to forward' : 'Failed to send reply',
264
+ });
266
265
  return;
267
266
  }
268
- await showToast({
269
- style: Toast.Style.Success,
270
- title: `Forwarded to ${values.to}`,
271
- });
272
- revalidate();
267
+ const successTitle = mode.type === 'forward'
268
+ ? `Forwarded to ${values.to}`
269
+ : 'Reply sent';
270
+ await showToast({ style: Toast.Style.Success, title: successTitle });
271
+ onSent?.();
273
272
  pop();
274
273
  };
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...' })] }));
274
+ return (_jsxs(Form, { isLoading: isLoading, navigationTitle: navigationTitle, actions: _jsx(ActionPanel, { children: _jsx(Action.SubmitForm, { title: mode.type === 'forward' ? 'Forward' : 'Send Reply', onSubmit: handleSubmit }) }), children: [accounts.length > 1 && (_jsx(Form.Dropdown, { id: 'account', title: 'From', value: selectedAccount, onChange: (v) => setSelectedAccount(Array.isArray(v) ? v[0] ?? initialAccount : v), children: accounts.map((a) => (_jsx(Form.Dropdown.Item, { value: a.email, title: a.email }, a.email))) })), mode.type === 'forward' && (_jsx(Form.TextField, { id: 'to', title: 'To', placeholder: 'recipient@example.com' })), _jsx(Form.TextArea, { id: 'body', title: 'Message', placeholder: bodyPlaceholder })] }));
276
275
  }
277
276
  // ---------------------------------------------------------------------------
278
277
  // Thread Detail (full thread view, pushed via Enter)
279
278
  // ---------------------------------------------------------------------------
280
- function ThreadDetail({ threadId, account, revalidate, }) {
279
+ function ThreadDetail({ threadId, account, accounts, revalidate, }) {
281
280
  const thread = useCachedPromise(async (tid, acct) => {
282
281
  const { client } = await getClient([acct]);
283
282
  const result = await client.getThread({ threadId: tid });
@@ -322,10 +321,10 @@ function ThreadDetail({ threadId, account, revalidate, }) {
322
321
  .filter((l) => typeof l === 'string' && !l.startsWith('Label_')) // skip internal IDs
323
322
  .slice(0, 10);
324
323
  const latestMsg = messages[messages.length - 1];
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: {
324
+ 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(ComposeForm, { mode: { type: 'reply', threadId }, initialAccount: account, accounts: accounts, onSent: revalidate }) }), _jsx(Action.Push, { title: 'Reply All', icon: Icon.Reply, shortcut: {
326
325
  modifiers: ['ctrl', 'shift'],
327
326
  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) }))] })] }) }));
327
+ }, target: _jsx(ComposeForm, { mode: { type: 'reply', threadId, replyAll: true }, initialAccount: account, accounts: accounts, onSent: revalidate }) }), _jsx(Action.Push, { title: 'Forward', icon: Icon.Forward, shortcut: { modifiers: ['ctrl'], key: 'f' }, target: _jsx(ComposeForm, { mode: { type: 'forward', threadId }, initialAccount: account, accounts: accounts, onSent: 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) }))] })] }) }));
329
328
  }
330
329
  // ---------------------------------------------------------------------------
331
330
  // Main Command
@@ -335,7 +334,19 @@ export default function Command() {
335
334
  const [searchText, setSearchText] = useState('');
336
335
  const [isShowingDetail, setIsShowingDetail] = useState(true);
337
336
  const [selectedThreads, setSelectedThreads] = useState([]);
337
+ const [activeMutations, setActiveMutations] = useState(0);
338
+ const isMutating = activeMutations > 0;
338
339
  const { height: terminalRows } = useTerminalDimensions();
340
+ /** Wrap async mutation calls to track global loading state. */
341
+ const withMutation = useCallback(async (fn) => {
342
+ setActiveMutations((n) => n + 1);
343
+ try {
344
+ return await fn();
345
+ }
346
+ finally {
347
+ setActiveMutations((n) => n - 1);
348
+ }
349
+ }, []);
339
350
  const pageSize = getPageSizeFromTerminalHeight(terminalRows);
340
351
  const accounts = useAccounts();
341
352
  const accountList = accounts.data ?? [];
@@ -462,34 +473,36 @@ export default function Command() {
462
473
  const handleBulkAction = useCallback(async (actionName, fn) => {
463
474
  if (selectedThreads.length === 0)
464
475
  return;
465
- // Group selected threads by account
466
- const byAccount = new Map();
467
- for (const tid of selectedThreads) {
468
- const thread = allThreads.find((t) => t.id === tid);
469
- if (!thread)
470
- continue;
471
- const list = byAccount.get(thread.account) ?? [];
472
- list.push(tid);
473
- byAccount.set(thread.account, list);
474
- }
475
- for (const [acct, ids] of byAccount) {
476
- const { client } = await getClient([acct]);
477
- const result = await fn(client, ids);
478
- if (result instanceof Error) {
479
- await showFailureToast(result, {
480
- title: `Failed to ${actionName}`,
481
- });
482
- return;
476
+ await withMutation(async () => {
477
+ // Group selected threads by account
478
+ const byAccount = new Map();
479
+ for (const tid of selectedThreads) {
480
+ const thread = allThreads.find((t) => t.id === tid);
481
+ if (!thread)
482
+ continue;
483
+ const list = byAccount.get(thread.account) ?? [];
484
+ list.push(tid);
485
+ byAccount.set(thread.account, list);
483
486
  }
484
- }
485
- await showToast({
486
- style: Toast.Style.Success,
487
- title: `${actionName}: ${selectedThreads.length} thread(s)`,
487
+ for (const [acct, ids] of byAccount) {
488
+ const { client } = await getClient([acct]);
489
+ const result = await fn(client, ids);
490
+ if (result instanceof Error) {
491
+ await showFailureToast(result, {
492
+ title: `Failed to ${actionName}`,
493
+ });
494
+ return;
495
+ }
496
+ }
497
+ await showToast({
498
+ style: Toast.Style.Success,
499
+ title: `${actionName}: ${selectedThreads.length} thread(s)`,
500
+ });
501
+ setSelectedThreads([]);
502
+ revalidate();
488
503
  });
489
- setSelectedThreads([]);
490
- revalidate();
491
- }, [selectedThreads, allThreads, revalidate]);
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) => {
504
+ }, [selectedThreads, allThreads, revalidate, withMutation]);
505
+ return (_jsx(List, { isLoading: isLoading || accounts.isLoading || isMutating, isShowingDetail: isShowingDetail, spacingMode: LIST_SPACING_MODE, 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) => {
493
506
  const isSelected = selectedThreads.includes(thread.id);
494
507
  const hasSelection = selectedThreads.length > 0;
495
508
  // Icon: selection mode or status
@@ -527,114 +540,68 @@ export default function Command() {
527
540
  thread.from.email,
528
541
  thread.from.name ?? '',
529
542
  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) => {
537
- for (const id of ids) {
538
- await c.trash({
539
- threadId: id,
543
+ ], detail: detail, actions: _jsx(ActionPanel, { children: hasSelection ? (
544
+ // ─────────────────────────────────────────────
545
+ // SELECTION MODE
546
+ // ─────────────────────────────────────────────
547
+ _jsxs(_Fragment, { children: [_jsxs(ActionPanel.Section, { title: 'Selection', children: [_jsx(Action, { title: isSelected ? 'Deselect Thread' : 'Select Thread', icon: isSelected ? Icon.Circle : Icon.CheckCircle, shortcut: { modifiers: ['ctrl'], key: 'x' }, onAction: () => toggleSelection(thread.id) }), _jsx(Action, { title: `Archive ${selectedThreads.length} Selected`, icon: Icon.Tray, onAction: () => handleBulkAction('Archived', (c, ids) => c.archive({ threadIds: ids })) }), _jsx(Action, { title: `Mark ${selectedThreads.length} as Read`, icon: Icon.Eye, onAction: () => handleBulkAction('Marked as read', (c, ids) => c.markAsRead({ threadIds: ids })) }), _jsx(Action, { title: `Star ${selectedThreads.length} Selected`, icon: Icon.Star, onAction: () => handleBulkAction('Starred', (c, ids) => c.star({ threadIds: ids })) }), _jsx(Action, { title: `Trash ${selectedThreads.length} Selected`, icon: Icon.Trash, onAction: () => handleBulkAction('Trashed', async (c, ids) => {
548
+ for (const id of ids) {
549
+ await c.trash({ threadId: id });
550
+ }
551
+ }) }), _jsx(Action, { title: 'Deselect All', icon: Icon.XMarkCircle, onAction: () => setSelectedThreads([]) })] }), _jsxs(ActionPanel.Section, { children: [_jsx(Action, { title: 'Refresh', icon: Icon.ArrowClockwise, shortcut: { modifiers: ['ctrl', 'shift'], key: 'r' }, onAction: () => revalidate() }), _jsx(Action, { title: 'Toggle Detail', icon: Icon.Sidebar, shortcut: { modifiers: ['ctrl'], key: 'd' }, onAction: () => setIsShowingDetail((v) => !v) })] })] })) : (
552
+ // ─────────────────────────────────────────────
553
+ // NORMAL MODE
554
+ // ─────────────────────────────────────────────
555
+ _jsxs(_Fragment, { children: [_jsxs(ActionPanel.Section, { children: [_jsx(Action.Push, { title: 'Open Thread', icon: Icon.Eye, target: _jsx(ThreadDetail, { threadId: thread.id, account: thread.account, accounts: accountList, revalidate: revalidate }) }), _jsx(Action, { title: 'Select Thread', icon: Icon.CheckCircle, shortcut: { modifiers: ['ctrl'], key: 'x' }, onAction: () => toggleSelection(thread.id) }), _jsx(Action, { title: thread.unread ? 'Mark as Read' : 'Mark as Unread', icon: thread.unread ? Icon.Eye : Icon.EyeDisabled, shortcut: { modifiers: ['ctrl'], key: 'u' }, onAction: () => withMutation(async () => {
556
+ const { client } = await getClient([thread.account]);
557
+ const result = thread.unread
558
+ ? await client.markAsRead({ threadIds: [thread.id] })
559
+ : await client.markAsUnread({ threadIds: [thread.id] });
560
+ if (result instanceof Error) {
561
+ await showFailureToast(result);
562
+ return;
563
+ }
564
+ await showToast({
565
+ style: Toast.Style.Success,
566
+ title: thread.unread ? 'Marked as read' : 'Marked as unread',
540
567
  });
541
- }
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 () => {
549
- const { client } = await getClient([thread.account]);
550
- const result = thread.unread
551
- ? await client.markAsRead({
552
- threadIds: [thread.id],
553
- })
554
- : await client.markAsUnread({
555
- threadIds: [thread.id],
568
+ revalidate();
569
+ }) }), _jsx(Action, { title: 'Archive', icon: Icon.Tray, shortcut: { modifiers: ['ctrl'], key: 'e' }, onAction: () => withMutation(async () => {
570
+ const { client } = await getClient([thread.account]);
571
+ const result = await client.archive({ threadIds: [thread.id] });
572
+ if (result instanceof Error) {
573
+ await showFailureToast(result);
574
+ return;
575
+ }
576
+ await showToast({
577
+ style: Toast.Style.Success,
578
+ title: 'Archived',
556
579
  });
557
- if (result instanceof Error) {
558
- await showFailureToast(result);
559
- return;
560
- }
561
- await showToast({
562
- style: Toast.Style.Success,
563
- title: thread.unread
564
- ? 'Marked as read'
565
- : 'Marked as unread',
566
- });
567
- revalidate();
568
- } }), _jsx(Action, { title: 'Archive', icon: Icon.Tray, shortcut: {
569
- modifiers: ['ctrl'],
570
- key: 'e',
571
- }, onAction: async () => {
572
- const { client } = await getClient([thread.account]);
573
- const result = await client.archive({
574
- threadIds: [thread.id],
575
- });
576
- if (result instanceof Error) {
577
- await showFailureToast(result);
578
- return;
579
- }
580
- await showToast({
581
- style: Toast.Style.Success,
582
- title: 'Archived',
583
- });
584
- revalidate();
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 () => {
591
- const { client } = await getClient([thread.account]);
592
- const isStarred = thread.labelIds.includes('STARRED');
593
- const result = isStarred
594
- ? await client.unstar({
595
- threadIds: [thread.id],
596
- })
597
- : await client.star({
598
- threadIds: [thread.id],
580
+ revalidate();
581
+ }) }), _jsx(Action, { title: thread.labelIds.includes('STARRED') ? 'Unstar' : 'Star', icon: Icon.Star, shortcut: { modifiers: ['ctrl'], key: 's' }, onAction: () => withMutation(async () => {
582
+ const { client } = await getClient([thread.account]);
583
+ const isStarred = thread.labelIds.includes('STARRED');
584
+ const result = isStarred
585
+ ? await client.unstar({ threadIds: [thread.id] })
586
+ : await client.star({ threadIds: [thread.id] });
587
+ if (result instanceof Error) {
588
+ await showFailureToast(result);
589
+ return;
590
+ }
591
+ await showToast({
592
+ style: Toast.Style.Success,
593
+ title: isStarred ? 'Unstarred' : 'Starred',
599
594
  });
600
- if (result instanceof Error) {
601
- await showFailureToast(result);
602
- return;
603
- }
595
+ revalidate();
596
+ }) })] }), _jsxs(ActionPanel.Section, { title: 'Reply & Forward', children: [_jsx(Action.Push, { title: 'Reply', icon: Icon.Reply, shortcut: { modifiers: ['ctrl'], key: 'r' }, target: _jsx(ComposeForm, { mode: { type: 'reply', threadId: thread.id }, initialAccount: thread.account, accounts: accountList, onSent: revalidate }) }), _jsx(Action.Push, { title: 'Reply All', icon: Icon.Reply, shortcut: { modifiers: ['ctrl', 'shift'], key: 'r' }, target: _jsx(ComposeForm, { mode: { type: 'reply', threadId: thread.id, replyAll: true }, initialAccount: thread.account, accounts: accountList, onSent: revalidate }) }), _jsx(Action.Push, { title: 'Forward', icon: Icon.Forward, shortcut: { modifiers: ['ctrl'], key: 'f' }, target: _jsx(ComposeForm, { mode: { type: 'forward', threadId: thread.id }, initialAccount: thread.account, accounts: accountList, onSent: 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, shortcut: { modifiers: ['ctrl'], key: 'backspace' }, onAction: () => withMutation(async () => {
597
+ const { client } = await getClient([thread.account]);
598
+ await client.trash({ threadId: thread.id });
604
599
  await showToast({
605
600
  style: Toast.Style.Success,
606
- title: isStarred ? 'Unstarred' : 'Starred',
601
+ title: 'Trashed',
607
602
  });
608
603
  revalidate();
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 () => {
622
- const { client } = await getClient([thread.account]);
623
- await client.trash({
624
- threadId: thread.id,
625
- });
626
- await showToast({
627
- style: Toast.Style.Success,
628
- title: 'Trashed',
629
- });
630
- revalidate();
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}`));
604
+ }) }) }), _jsxs(ActionPanel.Section, { children: [_jsx(Action, { title: 'Refresh', icon: Icon.ArrowClockwise, shortcut: { modifiers: ['ctrl', 'shift'], key: 'r' }, onAction: () => revalidate() }), _jsx(Action, { title: 'Toggle Detail', icon: Icon.Sidebar, shortcut: { modifiers: ['ctrl'], key: 'd' }, onAction: () => setIsShowingDetail((v) => !v) })] })] })) }) }, `${thread.account}-${thread.id}`));
638
605
  }) }, section.name))) }));
639
606
  }
640
607
  // ---------------------------------------------------------------------------