From e6269e58170650beeea5678230bd85528ade6497 Mon Sep 17 00:00:00 2001 From: Dotta Date: Tue, 17 Mar 2026 08:08:14 -0500 Subject: [PATCH] Add wrapping paths with copy-to-clipboard in workspace properties - Replace truncated paths with wrapping text (break-all) so full paths are visible - Add CopyablePath component with a copy icon that appears on hover and shows a green checkmark after copying - Apply to all workspace paths: cwd, branch name, repo URL, and the project primary workspace fallback Co-Authored-By: Paperclip --- ui/src/components/IssueProperties.tsx | 51 +++++++++++++++++++-------- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/ui/src/components/IssueProperties.tsx b/ui/src/components/IssueProperties.tsx index a381ff4b..587d2eef 100644 --- a/ui/src/components/IssueProperties.tsx +++ b/ui/src/components/IssueProperties.tsx @@ -1,4 +1,4 @@ -import { useMemo, useState } from "react"; +import { useCallback, useMemo, useRef, useState } from "react"; import { Link } from "@/lib/router"; import type { Issue } from "@paperclipai/shared"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; @@ -19,7 +19,7 @@ import { formatDate, cn, projectUrl } from "../lib/utils"; import { timeAgo } from "../lib/timeAgo"; import { Separator } from "@/components/ui/separator"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; -import { User, Hexagon, ArrowUpRight, Tag, Plus, Trash2 } from "lucide-react"; +import { User, Hexagon, ArrowUpRight, Tag, Plus, Trash2, Copy, Check } from "lucide-react"; import { AgentIcon } from "./AgentIconPicker"; const EXECUTION_WORKSPACE_OPTIONS = [ @@ -129,6 +129,37 @@ function PropertyPicker({ ); } +/** Displays a path/value with a copy-to-clipboard icon and "Copied!" feedback. */ +function CopyablePath({ value, label, className }: { value: string; label?: string; className?: string }) { + const [copied, setCopied] = useState(false); + const timerRef = useRef>(undefined); + const handleCopy = useCallback(async () => { + try { + await navigator.clipboard.writeText(value); + setCopied(true); + clearTimeout(timerRef.current); + timerRef.current = setTimeout(() => setCopied(false), 1500); + } catch { /* noop */ } + }, [value]); + + return ( +
+ + {label && {label} } + {value} + + +
+ ); +} + export function IssueProperties({ issue, onUpdate, inline }: IssuePropertiesProps) { const { selectedCompanyId } = useCompany(); const queryClient = useQueryClient(); @@ -675,26 +706,18 @@ export function IssueProperties({ issue, onUpdate, inline }: IssuePropertiesProp {issue.currentExecutionWorkspace.status} {issue.currentExecutionWorkspace.cwd && ( -
- {issue.currentExecutionWorkspace.cwd} -
+ )} {issue.currentExecutionWorkspace.branchName && ( -
- Branch: {issue.currentExecutionWorkspace.branchName} -
+ )} {issue.currentExecutionWorkspace.repoUrl && ( -
- Repo: {issue.currentExecutionWorkspace.repoUrl} -
+ )} )} {!issue.currentExecutionWorkspace && currentProject?.primaryWorkspace?.cwd && ( -
- {currentProject.primaryWorkspace.cwd} -
+ )}