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
6060
6161 const EMPTY_KEYBINDINGS: ResolvedKeybindingsConfig = [];
6262
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+
6370 function formatRelativeTime(iso: string): string {
6471 const diff = Date.now() - new Date(iso).getTime();
6572 const minutes = Math.floor(diff / 60_000);
@@ -581,6 +588,7 @@ export default function Sidebar() { · 515 unmodified lines
581588 [
582589 { id: "rename", label: "Rename thread" },
583590 { id: "mark-unread", label: "Mark unread" },
591+ { id: "copy-thread-id", label: "Copy Thread ID" },
584592 { id: "delete", label: "Delete", destructive: true },
585593 ],
586594 position,
@@ -599,6 +607,23 @@ export default function Sidebar() { · 12 unmodified lines
599607 markThreadUnread(threadId);
600608 return;
601609 }
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+ }
602627 if (clicked !== "delete") return;
603628 if (appSettings.confirmThreadDelete) {
604629 const confirmed = await api.dialogs.confirm(