- Add a `Copy Thread ID` item to the thread context menu in the sidebar - Copy the selected thread ID via Clipboard API - Show success/error toasts based on copy result
Unified · Split
| @@ -60,6 +60,13 @@ import { isNonEmpty as isNonEmptyString } from "effect/String"; · 59 unmodified lines | |||
| 60 | 60 | ||
| 61 | const EMPTY_KEYBINDINGS: ResolvedKeybindingsConfig = []; | 61 | const EMPTY_KEYBINDINGS: ResolvedKeybindingsConfig = []; |
| 62 | 62 | ||
| 63 | async function copyTextToClipboard(text: string): Promise<void> { | ||
| 64 | if (typeof navigator === "undefined" || navigator.clipboard?.writeText === undefined) { | ||
| 65 | throw new Error("Clipboard API unavailable."); | ||
| 66 | } | ||
| 67 | await navigator.clipboard.writeText(text); | ||
| 68 | } | ||
| 69 | |||
| 63 | function formatRelativeTime(iso: string): string { | 70 | function formatRelativeTime(iso: string): string { |
| 64 | const diff = Date.now() - new Date(iso).getTime(); | 71 | const diff = Date.now() - new Date(iso).getTime(); |
| 65 | const minutes = Math.floor(diff / 60_000); | 72 | const minutes = Math.floor(diff / 60_000); |
| @@ -581,6 +588,7 @@ export default function Sidebar() { · 515 unmodified lines | |||
| 581 | [ | 588 | [ |
| 582 | { id: "rename", label: "Rename thread" }, | 589 | { id: "rename", label: "Rename thread" }, |
| 583 | { id: "mark-unread", label: "Mark unread" }, | 590 | { id: "mark-unread", label: "Mark unread" }, |
| 591 | { id: "copy-thread-id", label: "Copy Thread ID" }, | ||
| 584 | { id: "delete", label: "Delete", destructive: true }, | 592 | { id: "delete", label: "Delete", destructive: true }, |
| 585 | ], | 593 | ], |
| 586 | position, | 594 | position, |
| @@ -599,6 +607,23 @@ export default function Sidebar() { · 12 unmodified lines | |||
| 599 | markThreadUnread(threadId); | 607 | markThreadUnread(threadId); |
| 600 | return; | 608 | return; |
| 601 | } | 609 | } |
| 610 | if (clicked === "copy-thread-id") { | ||
| 611 | try { | ||
| 612 | await copyTextToClipboard(threadId); | ||
| 613 | toastManager.add({ | ||
| 614 | type: "success", | ||
| 615 | title: "Thread ID copied", | ||
| 616 | description: threadId, | ||
| 617 | }); | ||
| 618 | } catch (error) { | ||
| 619 | toastManager.add({ | ||
| 620 | type: "error", | ||
| 621 | title: "Failed to copy thread ID", | ||
| 622 | description: error instanceof Error ? error.message : "An error occurred.", | ||
| 623 | }); | ||
| 624 | } | ||
| 625 | return; | ||
| 626 | } | ||
| 602 | if (clicked !== "delete") return; | 627 | if (clicked !== "delete") return; |
| 603 | if (appSettings.confirmThreadDelete) { | 628 | if (appSettings.confirmThreadDelete) { |
| 604 | const confirmed = await api.dialogs.confirm( | 629 | const confirmed = await api.dialogs.confirm( |