History

Add Copy Thread ID action to thread context menu

- 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

Julius Marminge committed 8 months ago

Commit eb598531 parent a2682de1 file · +25 −0Browse files

Unified · Split

apps/web/src/components/Sidebar.tsx+25−0
@@ -60,6 +60,13 @@ import { isNonEmpty as isNonEmptyString } from "effect/String"; · 59 unmodified lines
60 60
61const EMPTY_KEYBINDINGS: ResolvedKeybindingsConfig = [];61const EMPTY_KEYBINDINGS: ResolvedKeybindingsConfig = [];
62 62
63async 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
63function formatRelativeTime(iso: string): string {70function 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(