Skip to content

Commit 38ede67

Browse files
authored
Add AI Response Feedback + Copy Buttons (#2457)
1 parent 6a173a6 commit 38ede67

8 files changed

Lines changed: 479 additions & 333 deletions

File tree

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// Copyright 2025, Command Line Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { RpcApi } from "@/app/store/wshclientapi";
5+
import { TabRpcClient } from "@/app/store/wshrpcutil";
6+
import { cn, makeIconClass } from "@/util/util";
7+
import { memo, useState } from "react";
8+
9+
interface AIFeedbackButtonsProps {
10+
messageText: string;
11+
}
12+
13+
export const AIFeedbackButtons = memo(({ messageText }: AIFeedbackButtonsProps) => {
14+
const [thumbsUpClicked, setThumbsUpClicked] = useState(false);
15+
const [thumbsDownClicked, setThumbsDownClicked] = useState(false);
16+
const [copied, setCopied] = useState(false);
17+
18+
const handleThumbsUp = () => {
19+
setThumbsUpClicked(!thumbsUpClicked);
20+
if (thumbsDownClicked) {
21+
setThumbsDownClicked(false);
22+
}
23+
if (!thumbsUpClicked) {
24+
RpcApi.RecordTEventCommand(TabRpcClient, {
25+
event: "waveai:feedback",
26+
props: {
27+
"waveai:feedback": "good",
28+
},
29+
});
30+
}
31+
};
32+
33+
const handleThumbsDown = () => {
34+
setThumbsDownClicked(!thumbsDownClicked);
35+
if (thumbsUpClicked) {
36+
setThumbsUpClicked(false);
37+
}
38+
if (!thumbsDownClicked) {
39+
RpcApi.RecordTEventCommand(TabRpcClient, {
40+
event: "waveai:feedback",
41+
props: {
42+
"waveai:feedback": "bad",
43+
},
44+
});
45+
}
46+
};
47+
48+
const handleCopy = () => {
49+
navigator.clipboard.writeText(messageText);
50+
setCopied(true);
51+
setTimeout(() => setCopied(false), 2000);
52+
};
53+
54+
return (
55+
<div className="flex items-center gap-0.5 mt-2">
56+
<button
57+
onClick={handleThumbsUp}
58+
className={cn(
59+
"p-1.5 rounded cursor-pointer transition-colors",
60+
thumbsUpClicked
61+
? "text-accent"
62+
: "text-secondary hover:bg-gray-700 hover:text-primary"
63+
)}
64+
title="Good Response"
65+
>
66+
<i className={makeIconClass(thumbsUpClicked ? "solid@thumbs-up" : "regular@thumbs-up", false)} />
67+
</button>
68+
<button
69+
onClick={handleThumbsDown}
70+
className={cn(
71+
"p-1.5 rounded cursor-pointer transition-colors",
72+
thumbsDownClicked
73+
? "text-accent"
74+
: "text-secondary hover:bg-gray-700 hover:text-primary"
75+
)}
76+
title="Bad Response"
77+
>
78+
<i className={makeIconClass(thumbsDownClicked ? "solid@thumbs-down" : "regular@thumbs-down", false)} />
79+
</button>
80+
{messageText?.trim() && (
81+
<button
82+
onClick={handleCopy}
83+
className={cn(
84+
"p-1.5 rounded cursor-pointer transition-colors",
85+
copied
86+
? "text-success"
87+
: "text-secondary hover:bg-gray-700 hover:text-primary"
88+
)}
89+
title="Copy Message"
90+
>
91+
<i className={makeIconClass(copied ? "solid@check" : "regular@copy", false)} />
92+
</button>
93+
)}
94+
</div>
95+
);
96+
});
97+
98+
AIFeedbackButtons.displayName = "AIFeedbackButtons";

0 commit comments

Comments
 (0)