Copy request id (for Dyad Pro) (#1523)

Based on #1488 by [vedantbhatotia](https://github.com/vedantbhatotia)

<!-- CURSOR_SUMMARY -->
> [!NOTE]
> <sup>[Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) is
generating a summary for commit
cc504f6d56ff72407dd5d0135befbf5d9cc695b5. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: vedantbhatotia <vedantbhatotia@gmail.com>
This commit is contained in:
Will Chen
2025-10-13 16:41:46 -07:00
committed by GitHub
parent e99e19e86b
commit ffa4c3ad01
7 changed files with 832 additions and 13 deletions

View File

@@ -17,7 +17,7 @@ import { formatDistanceToNow, format } from "date-fns";
import { useVersions } from "@/hooks/useVersions";
import { useAtomValue } from "jotai";
import { selectedAppIdAtom } from "@/atoms/appAtoms";
import { useMemo } from "react";
import { useEffect, useMemo, useRef, useState } from "react";
import { useCopyToClipboard } from "@/hooks/useCopyToClipboard";
import {
Tooltip,
@@ -58,6 +58,17 @@ const ChatMessage = ({ message, isLastMessage }: ChatMessageProps) => {
return null;
}, [message.commitHash, message.role, liveVersions]);
// handle copy request id
const [copiedRequestId, setCopiedRequestId] = useState(false);
const copiedRequestIdTimeoutRef = useRef<NodeJS.Timeout | null>(null);
useEffect(() => {
return () => {
if (copiedRequestIdTimeoutRef.current) {
clearTimeout(copiedRequestIdTimeoutRef.current);
}
};
}, []);
// Format the message timestamp
const formatTimestamp = (timestamp: string | Date) => {
const now = new Date();
@@ -199,7 +210,7 @@ const ChatMessage = ({ message, isLastMessage }: ChatMessageProps) => {
</div>
{/* Timestamp and commit info for assistant messages - only visible on hover */}
{message.role === "assistant" && message.createdAt && (
<div className="mt-1 flex items-center justify-start space-x-2 text-xs text-gray-500 dark:text-gray-400 ">
<div className="mt-1 flex flex-wrap items-center justify-start space-x-2 text-xs text-gray-500 dark:text-gray-400 ">
<div className="flex items-center space-x-1">
<Clock className="h-3 w-3" />
<span>{formatTimestamp(message.createdAt)}</span>
@@ -208,16 +219,64 @@ const ChatMessage = ({ message, isLastMessage }: ChatMessageProps) => {
<div className="flex items-center space-x-1">
<GitCommit className="h-3 w-3" />
{messageVersion && messageVersion.message && (
<span className="max-w-70 truncate font-medium">
{
messageVersion.message
.replace(/^\[dyad\]\s*/i, "")
.split("\n")[0]
}
</span>
<Tooltip>
<TooltipTrigger asChild>
<span className="max-w-50 truncate font-medium">
{
messageVersion.message
.replace(/^\[dyad\]\s*/i, "")
.split("\n")[0]
}
</span>
</TooltipTrigger>
<TooltipContent>{messageVersion.message}</TooltipContent>
</Tooltip>
)}
</div>
)}
{message.requestId && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<button
onClick={() => {
if (!message.requestId) return;
navigator.clipboard
.writeText(message.requestId)
.then(() => {
setCopiedRequestId(true);
if (copiedRequestIdTimeoutRef.current) {
clearTimeout(copiedRequestIdTimeoutRef.current);
}
copiedRequestIdTimeoutRef.current = setTimeout(
() => setCopiedRequestId(false),
2000,
);
})
.catch(() => {
// noop
});
}}
className="flex items-center space-x-1 px-1 py-0.5 hover:bg-gray-100 dark:hover:bg-gray-800 rounded transition-colors duration-200 cursor-pointer"
>
{copiedRequestId ? (
<Check className="h-3 w-3 text-green-500" />
) : (
<Copy className="h-3 w-3" />
)}
<span className="text-xs">
{copiedRequestId ? "Copied" : "Request ID"}
</span>
</button>
</TooltipTrigger>
<TooltipContent>
{copiedRequestId
? "Copied!"
: `Copy Request ID: ${message.requestId.slice(0, 8)}...`}
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
</div>
)}
</div>