Skip to content

feat: copy button on user messages, hover on message for buttons#110

Merged
t3dotgg merged 1 commit intomainfrom
theo/copy-on-hover
Feb 27, 2026
Merged

feat: copy button on user messages, hover on message for buttons#110
t3dotgg merged 1 commit intomainfrom
theo/copy-on-hover

Conversation

@t3dotgg
Copy link
Copy Markdown
Member

@t3dotgg t3dotgg commented Feb 27, 2026

image

Note

Add a copy button to user message bubbles and show message controls on hover in apps/web/src/components/ChatView.tsx

Introduce a memoized MessageCopyButton that writes text to the clipboard and toggles CopyIcon/CheckIcon for 2000ms, and update user message bubbles to reveal copy and revert controls on hover/focus with opacity transitions in ChatView.tsx.

📍Where to Start

Start with the MessageCopyButton component and its usage within MessagesTimeline in ChatView.tsx.

Macroscope summarized e09f859.

Summary by CodeRabbit

  • New Features
    • Added copy-to-clipboard functionality for user messages in the chat timeline. A copy button appears when hovering over messages with visual confirmation when text is successfully copied.
    • Enhanced message action button interactions and layout for improved usability.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 27, 2026

Walkthrough

A MessageCopyButton component was added to ChatView.tsx that copies message text to the clipboard and displays brief visual feedback. The button is integrated into the MessagesTimeline component, appearing alongside existing message actions with hover activation and layout adjustments for consistent spacing.

Changes

Cohort / File(s) Summary
Message Copy Button Feature
apps/web/src/components/ChatView.tsx
Added MessageCopyButton component with copy-to-clipboard functionality and visual feedback. Integrated into MessagesTimeline for user messages with hover/focus group container. Added CopyIcon and CheckIcon imports from lucide-react. Minor layout adjustments for spacing consistency.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main changes: adding a copy button to user messages and implementing hover-triggered visibility for message action buttons.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch theo/copy-on-hover

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/components/ChatView.tsx`:
- Around line 2341-2344: handleCopy currently calls
navigator.clipboard.writeText without handling rejections and always sets
setCopied(true), and the timeout is never cleared on unmount; update handleCopy
to await or use .then/.catch on navigator.clipboard.writeText(text) and only
call setCopied(true) when the promise resolves, log or surface the error on
rejection and avoid setting copied on failure, store the timeout id in a ref
(e.g., copyTimeoutRef) and use clearTimeout(copyTimeoutRef.current) before
setting a new timeout, and add a cleanup (in a useEffect return) to clear the
timeout on unmount; reference handleCopy, navigator.clipboard.writeText,
setCopied and the timeout logic when making changes.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3a43047 and e09f859.

📒 Files selected for processing (1)
  • apps/web/src/components/ChatView.tsx

Comment on lines +2341 to +2344
const handleCopy = useCallback(() => {
void navigator.clipboard.writeText(text);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Handle clipboard API errors and clean up timeout on unmount.

navigator.clipboard.writeText can reject (permission denied, insecure context). Currently, failures are silently ignored and the user sees "copied" feedback regardless. Additionally, the timeout should be cleaned up if the component unmounts.

🛡️ Proposed fix
 const MessageCopyButton = memo(function MessageCopyButton({ text }: { text: string }) {
   const [copied, setCopied] = useState(false);
 
   const handleCopy = useCallback(() => {
-    void navigator.clipboard.writeText(text);
-    setCopied(true);
-    setTimeout(() => setCopied(false), 2000);
+    navigator.clipboard.writeText(text).then(
+      () => setCopied(true),
+      () => {
+        // Optionally show error feedback or silently fail
+      }
+    );
   }, [text]);
+
+  useEffect(() => {
+    if (!copied) return;
+    const timer = setTimeout(() => setCopied(false), 2000);
+    return () => clearTimeout(timer);
+  }, [copied]);
 
   return (
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/ChatView.tsx` around lines 2341 - 2344, handleCopy
currently calls navigator.clipboard.writeText without handling rejections and
always sets setCopied(true), and the timeout is never cleared on unmount; update
handleCopy to await or use .then/.catch on navigator.clipboard.writeText(text)
and only call setCopied(true) when the promise resolves, log or surface the
error on rejection and avoid setting copied on failure, store the timeout id in
a ref (e.g., copyTimeoutRef) and use clearTimeout(copyTimeoutRef.current) before
setting a new timeout, and add a cleanup (in a useEffect return) to clear the
timeout on unmount; reference handleCopy, navigator.clipboard.writeText,
setCopied and the timeout logic when making changes.

@t3dotgg t3dotgg merged commit 0b8e098 into main Feb 27, 2026
3 checks passed
jjalangtry pushed a commit to jjalangtry/t3code that referenced this pull request Mar 16, 2026
smraikai pushed a commit to smraikai/t3code that referenced this pull request Apr 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant