Skip to content

Commit 5ee8129

Browse files
anchored toast
1 parent 40aa530 commit 5ee8129

2 files changed

Lines changed: 61 additions & 38 deletions

File tree

Lines changed: 60 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,83 @@
1-
import { memo } from "react";
1+
import { memo, useRef } from "react";
22
import { CopyIcon, CheckIcon } from "lucide-react";
33
import { Button } from "../ui/button";
44
import { useCopyToClipboard } from "~/hooks/useCopyToClipboard";
55
import { cn } from "~/lib/utils";
6+
import { anchoredToastManager } from "../ui/toast";
7+
import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip";
8+
9+
const ANCHORED_TOAST_TIMEOUT_MS = 1000;
10+
const onCopy = (ref: React.RefObject<HTMLButtonElement | null>) => {
11+
if (ref.current) {
12+
anchoredToastManager.add({
13+
data: {
14+
tooltipStyle: true,
15+
},
16+
positionerProps: {
17+
anchor: ref.current,
18+
},
19+
timeout: ANCHORED_TOAST_TIMEOUT_MS,
20+
title: "Copied!",
21+
});
22+
}
23+
};
24+
25+
const onCopyError = (ref: React.RefObject<HTMLButtonElement | null>, error: Error) => {
26+
if (ref.current) {
27+
anchoredToastManager.add({
28+
data: {
29+
tooltipStyle: true,
30+
},
31+
positionerProps: {
32+
anchor: ref.current,
33+
},
34+
timeout: ANCHORED_TOAST_TIMEOUT_MS,
35+
title: "Failed to copy",
36+
description: error.message,
37+
});
38+
}
39+
};
640

741
export const MessageCopyButton = memo(function MessageCopyButton({
842
text,
9-
title = "Copy message",
1043
size = "xs",
1144
variant = "outline",
1245
className,
13-
onCopy,
14-
onError,
1546
}: {
1647
text: string;
1748
title?: string;
1849
size?: "xs" | "icon-xs";
1950
variant?: "outline" | "ghost";
2051
className?: string;
21-
onCopy?: () => void;
22-
onError?: (error: Error) => void;
2352
}) {
53+
const ref = useRef<HTMLButtonElement>(null);
2454
const { copyToClipboard, isCopied } = useCopyToClipboard<void>({
25-
...(onCopy ? { onCopy: () => onCopy() } : {}),
26-
...(onError ? { onError: (error: Error) => onError(error) } : {}),
55+
onCopy: () => onCopy(ref),
56+
onError: (error: Error) => onCopyError(ref, error),
57+
timeout: ANCHORED_TOAST_TIMEOUT_MS,
2758
});
28-
const buttonTitle = isCopied ? "Copied" : title;
2959

3060
return (
31-
<Button
32-
type="button"
33-
size={size}
34-
variant={variant}
35-
className={cn(className)}
36-
onClick={() => copyToClipboard(text, undefined)}
37-
title={buttonTitle}
38-
aria-label={buttonTitle}
39-
>
40-
{isCopied ? <CheckIcon className="size-3 text-success" /> : <CopyIcon className="size-3" />}
41-
</Button>
61+
<Tooltip>
62+
<TooltipTrigger
63+
render={
64+
<Button
65+
aria-label="Copy link"
66+
disabled={isCopied}
67+
onClick={() => copyToClipboard(text)}
68+
ref={ref}
69+
type="button"
70+
size={size}
71+
variant={variant}
72+
className={cn(className)}
73+
/>
74+
}
75+
>
76+
{isCopied ? <CheckIcon className="size-3 text-success" /> : <CopyIcon className="size-3" />}
77+
</TooltipTrigger>
78+
<TooltipPopup>
79+
<p>Copy to clipboard</p>
80+
</TooltipPopup>
81+
</Tooltip>
4282
);
4383
});

apps/web/src/components/chat/MessagesTimeline.tsx

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ import {
5757
import { cn } from "~/lib/utils";
5858
import { type TimestampFormat } from "@t3tools/contracts/settings";
5959
import { formatTimestamp } from "../../timestampFormat";
60-
import { toastManager } from "../ui/toast";
60+
6161
import {
6262
buildInlineTerminalContextText,
6363
formatInlineTerminalContextLabel,
@@ -66,21 +66,6 @@ import {
6666

6767
const ALWAYS_UNVIRTUALIZED_TAIL_ROWS = 8;
6868

69-
const handleAssistantCopySuccess = () => {
70-
toastManager.add({
71-
type: "success",
72-
title: "Assistant response copied",
73-
});
74-
};
75-
76-
const handleAssistantCopyError = (error: Error) => {
77-
toastManager.add({
78-
type: "error",
79-
title: "Failed to copy assistant response",
80-
description: error.message,
81-
});
82-
};
83-
8469
interface MessagesTimelineProps {
8570
hasMessages: boolean;
8671
isWorking: boolean;
@@ -562,8 +547,6 @@ export const MessagesTimeline = memo(function MessagesTimeline({
562547
size="icon-xs"
563548
variant="outline"
564549
className="border-border/50 bg-background/35 text-muted-foreground/45 shadow-none hover:border-border/70 hover:bg-background/55 hover:text-muted-foreground/70"
565-
onCopy={handleAssistantCopySuccess}
566-
onError={handleAssistantCopyError}
567550
/>
568551
</div>
569552
) : null}

0 commit comments

Comments
 (0)