From 06b6d6357aa45141ac35ec326a8ea6115f78326f Mon Sep 17 00:00:00 2001 From: Adam Bowker Date: Wed, 8 Apr 2026 08:13:39 -0700 Subject: [PATCH] feat(sig): add copy task ID to context menu Generated-By: PostHog Code Task-Id: f9d7c635-6d95-4255-8a4d-d2281b1e0389 --- .../src/main/services/context-menu/schemas.ts | 1 + .../src/main/services/context-menu/service.ts | 1 + .../task-detail/components/TaskDetail.tsx | 28 +++++-------------- .../src/renderer/hooks/useTaskContextMenu.ts | 5 ++++ 4 files changed, 14 insertions(+), 21 deletions(-) diff --git a/apps/code/src/main/services/context-menu/schemas.ts b/apps/code/src/main/services/context-menu/schemas.ts index 1a9749ee3..62238fa06 100644 --- a/apps/code/src/main/services/context-menu/schemas.ts +++ b/apps/code/src/main/services/context-menu/schemas.ts @@ -33,6 +33,7 @@ const externalAppAction = z.discriminatedUnion("type", [ const taskAction = z.discriminatedUnion("type", [ z.object({ type: z.literal("rename") }), z.object({ type: z.literal("pin") }), + z.object({ type: z.literal("copy-task-id") }), z.object({ type: z.literal("suspend") }), z.object({ type: z.literal("archive") }), z.object({ type: z.literal("delete") }), diff --git a/apps/code/src/main/services/context-menu/service.ts b/apps/code/src/main/services/context-menu/service.ts index 4881a6401..d30e81022 100644 --- a/apps/code/src/main/services/context-menu/service.ts +++ b/apps/code/src/main/services/context-menu/service.ts @@ -110,6 +110,7 @@ export class ContextMenuService { return this.showMenu([ this.item("Rename", { type: "rename" }), this.item(isPinned ? "Unpin" : "Pin", { type: "pin" }), + this.item("Copy Task ID", { type: "copy-task-id" }), this.separator(), ...(worktreePath ? [this.item("Suspend", { type: "suspend" as const })] diff --git a/apps/code/src/renderer/features/task-detail/components/TaskDetail.tsx b/apps/code/src/renderer/features/task-detail/components/TaskDetail.tsx index a001af20d..5b233b4b4 100644 --- a/apps/code/src/renderer/features/task-detail/components/TaskDetail.tsx +++ b/apps/code/src/renderer/features/task-detail/components/TaskDetail.tsx @@ -16,11 +16,10 @@ import { useWorkspace } from "@features/workspace/hooks/useWorkspace"; import { useBlurOnEscape } from "@hooks/useBlurOnEscape"; import { useFileWatcher } from "@hooks/useFileWatcher"; import { useSetHeaderContent } from "@hooks/useSetHeaderContent"; -import { Box, Flex, Text, Tooltip } from "@radix-ui/themes"; +import { Box, Flex, Text } from "@radix-ui/themes"; import type { Task } from "@shared/types"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useHotkeys, useHotkeysContext } from "react-hotkeys-hook"; -import { toast } from "sonner"; import { ExternalAppsOpener } from "./ExternalAppsOpener"; const MIN_REVIEW_WIDTH = 300; @@ -86,33 +85,20 @@ export function TaskDetail({ task: initialTask }: TaskDetailProps) { useBlurOnEscape(); useWorkspaceEvents(taskId); - const copyTaskId = useCallback(() => { - navigator.clipboard.writeText(taskId); - toast.success("Task ID copied"); - }, [taskId]); - const headerContent = useMemo( () => ( {task.title} - - - - - {openTargetPath && } - + {openTargetPath && ( + + + + )} ), - [task.title, taskId, openTargetPath, copyTaskId], + [task.title, openTargetPath], ); useSetHeaderContent(headerContent); diff --git a/apps/code/src/renderer/hooks/useTaskContextMenu.ts b/apps/code/src/renderer/hooks/useTaskContextMenu.ts index 2c125abf3..1a8db1b5a 100644 --- a/apps/code/src/renderer/hooks/useTaskContextMenu.ts +++ b/apps/code/src/renderer/hooks/useTaskContextMenu.ts @@ -7,6 +7,7 @@ import type { Task } from "@shared/types"; import { handleExternalAppAction } from "@utils/handleExternalAppAction"; import { logger } from "@utils/logger"; import { useCallback, useState } from "react"; +import { toast } from "sonner"; const log = logger.scope("context-menu"); @@ -47,6 +48,10 @@ export function useTaskContextMenu() { case "pin": onTogglePin?.(); break; + case "copy-task-id": + navigator.clipboard.writeText(task.id); + toast.success("Task ID copied"); + break; case "suspend": await suspendTask({ taskId: task.id, reason: "manual" }); break;