From eb59853596b9ea2547c1bde87d2cd9b59a6fef95 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Thu, 5 Mar 2026 14:41:28 -0800 Subject: [PATCH] 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 --- apps/web/src/components/Sidebar.tsx | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/apps/web/src/components/Sidebar.tsx b/apps/web/src/components/Sidebar.tsx index 827bb75b0fb69acef43ab6b260e13965cd80eaf5..abc33065b0c539d8e2bbaa5f9fbebff8f02f88c5 100644 --- a/apps/web/src/components/Sidebar.tsx +++ b/apps/web/src/components/Sidebar.tsx @@ -60,6 +60,13 @@ import { isNonEmpty as isNonEmptyString } from "effect/String"; const EMPTY_KEYBINDINGS: ResolvedKeybindingsConfig = []; +async function copyTextToClipboard(text: string): Promise { + if (typeof navigator === "undefined" || navigator.clipboard?.writeText === undefined) { + throw new Error("Clipboard API unavailable."); + } + await navigator.clipboard.writeText(text); +} + function formatRelativeTime(iso: string): string { const diff = Date.now() - new Date(iso).getTime(); const minutes = Math.floor(diff / 60_000); @@ -581,6 +588,7 @@ export default function Sidebar() { [ { id: "rename", label: "Rename thread" }, { id: "mark-unread", label: "Mark unread" }, + { id: "copy-thread-id", label: "Copy Thread ID" }, { id: "delete", label: "Delete", destructive: true }, ], position, @@ -599,6 +607,23 @@ export default function Sidebar() { markThreadUnread(threadId); return; } + if (clicked === "copy-thread-id") { + try { + await copyTextToClipboard(threadId); + toastManager.add({ + type: "success", + title: "Thread ID copied", + description: threadId, + }); + } catch (error) { + toastManager.add({ + type: "error", + title: "Failed to copy thread ID", + description: error instanceof Error ? error.message : "An error occurred.", + }); + } + return; + } if (clicked !== "delete") return; if (appSettings.confirmThreadDelete) { const confirmed = await api.dialogs.confirm( -- 2.54.0