Revert "feat: copy messages on hoverable icon (#1492)" (#1508)

This commit is contained in:
Alex Hancock
2025-03-04 14:08:01 -05:00
committed by GitHub
parent 48838c91c4
commit 7b767abfe6
5 changed files with 25 additions and 59 deletions
+1 -1
View File
@@ -235,7 +235,7 @@ export default function ChatView({
{messages.length === 0 ? ( {messages.length === 0 ? (
<Splash append={(text) => append(createUserMessage(text))} /> <Splash append={(text) => append(createUserMessage(text))} />
) : ( ) : (
<ScrollArea ref={scrollRef} className="flex-1 pl-4" autoScroll> <ScrollArea ref={scrollRef} className="flex-1 px-4" autoScroll>
{filteredMessages.map((message, index) => ( {filteredMessages.map((message, index) => (
<div key={message.id || index} className="mt-[16px]"> <div key={message.id || index} className="mt-[16px]">
{isUserMessage(message) ? ( {isUserMessage(message) ? (
+1 -10
View File
@@ -12,7 +12,6 @@ import {
getToolConfirmationContent, getToolConfirmationContent,
} from '../types/message'; } from '../types/message';
import ToolCallConfirmation from './ToolCallConfirmation'; import ToolCallConfirmation from './ToolCallConfirmation';
import CopyButton from './ui/CopyButton';
interface GooseMessageProps { interface GooseMessageProps {
message: Message; message: Message;
@@ -68,17 +67,9 @@ export default function GooseMessage({ message, metadata, messages, append }: Go
{/* Always show the top content area if there are tool calls, even if textContent is empty */} {/* Always show the top content area if there are tool calls, even if textContent is empty */}
{(textContent || toolRequests.length > 0) && ( {(textContent || toolRequests.length > 0) && (
<div <div
className={`goose-message-content bg-bgSubtle rounded-2xl px-4 py-2 ${toolRequests.length > 0 ? 'rounded-b-none' : ''} relative group`} className={`goose-message-content bg-bgSubtle rounded-2xl px-4 py-2 ${toolRequests.length > 0 ? 'rounded-b-none' : ''}`}
> >
{textContent ? <MarkdownContent content={textContent} /> : null} {textContent ? <MarkdownContent content={textContent} /> : null}
{/* Only show CopyButton if there's text content and no tool requests/responses */}
{textContent && message.content.every((content) => content.type === 'text') && (
<CopyButton
text={textContent}
className="absolute -bottom-2 -right-2 p-1.5 rounded-full bg-white dark:bg-gray-800 shadow-md z-[1000] hover:bg-gray-100 dark:hover:bg-gray-700 opacity-0 group-hover:opacity-100 transition-opacity"
iconClassName="h-4 w-4 text-gray-800 dark:text-white"
/>
)}
</div> </div>
)} )}
+22 -5
View File
@@ -1,11 +1,12 @@
import React from 'react'; import React, { useState } from 'react';
import ReactMarkdown from 'react-markdown'; import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm'; import remarkGfm from 'remark-gfm';
import rehypeRaw from 'rehype-raw'; import rehypeRaw from 'rehype-raw';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism'; import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism';
import { oneLight } from 'react-syntax-highlighter/dist/cjs/styles/prism';
import { Check, Copy } from './icons';
import { visit } from 'unist-util-visit'; import { visit } from 'unist-util-visit';
import CopyButton from './ui/CopyButton';
const UrlTransform = { const UrlTransform = {
a: ({ node, ...props }) => <a {...props} target="_blank" rel="noopener noreferrer" />, a: ({ node, ...props }) => <a {...props} target="_blank" rel="noopener noreferrer" />,
@@ -30,14 +31,28 @@ interface MarkdownContentProps {
} }
const CodeBlock = ({ language, children }: { language: string; children: string }) => { const CodeBlock = ({ language, children }: { language: string; children: string }) => {
const [copied, setCopied] = useState(false);
const handleCopy = async () => {
try {
await navigator.clipboard.writeText(children);
setCopied(true);
setTimeout(() => setCopied(false), 2000); // Reset after 2 seconds
} catch (err) {
console.error('Failed to copy text: ', err);
}
};
return ( return (
<div className="relative group w-full"> <div className="relative group w-full">
<CopyButton <button
text={children} onClick={handleCopy}
className="absolute right-2 bottom-2 p-1.5 rounded-lg bg-gray-700/50 text-gray-300 className="absolute right-2 bottom-2 p-1.5 rounded-lg bg-gray-700/50 text-gray-300
opacity-0 group-hover:opacity-100 transition-opacity duration-200 opacity-0 group-hover:opacity-100 transition-opacity duration-200
hover:bg-gray-600/50 hover:text-gray-100 z-10" hover:bg-gray-600/50 hover:text-gray-100 z-10"
/> title="Copy code"
>
{copied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />}
</button>
<div className="w-full overflow-x-auto"> <div className="w-full overflow-x-auto">
<SyntaxHighlighter <SyntaxHighlighter
style={oneDark} style={oneDark}
@@ -64,6 +79,8 @@ const CodeBlock = ({ language, children }: { language: string; children: string
}; };
export default function MarkdownContent({ content, className = '' }: MarkdownContentProps) { export default function MarkdownContent({ content, className = '' }: MarkdownContentProps) {
// Determine whether dark mode is enabled
const isDarkMode = document.documentElement.classList.contains('dark');
return ( return (
<div className="w-full overflow-x-hidden"> <div className="w-full overflow-x-hidden">
<ReactMarkdown <ReactMarkdown
+1 -7
View File
@@ -3,7 +3,6 @@ import LinkPreview from './LinkPreview';
import { extractUrls } from '../utils/urlUtils'; import { extractUrls } from '../utils/urlUtils';
import MarkdownContent from './MarkdownContent'; import MarkdownContent from './MarkdownContent';
import { Message, getTextContent } from '../types/message'; import { Message, getTextContent } from '../types/message';
import CopyButton from './ui/CopyButton';
interface UserMessageProps { interface UserMessageProps {
message: Message; message: Message;
@@ -19,13 +18,8 @@ export default function UserMessage({ message }: UserMessageProps) {
return ( return (
<div className="flex justify-end mt-[16px] w-full opacity-0 animate-[appear_150ms_ease-in_forwards]"> <div className="flex justify-end mt-[16px] w-full opacity-0 animate-[appear_150ms_ease-in_forwards]">
<div className="flex-col max-w-[85%]"> <div className="flex-col max-w-[85%]">
<div className="flex bg-slate text-white rounded-xl rounded-br-none py-2 px-3 mr-4 relative group"> <div className="flex bg-slate text-white rounded-xl rounded-br-none py-2 px-3">
<MarkdownContent content={textContent} className="text-white" /> <MarkdownContent content={textContent} className="text-white" />
<CopyButton
text={textContent}
className="absolute -bottom-2 -right-2 p-1.5 rounded-full bg-white dark:bg-gray-800 shadow-md z-[1000] hover:bg-gray-100 dark:hover:bg-gray-700 opacity-0 group-hover:opacity-100 transition-opacity"
iconClassName="h-4 w-4 text-gray-800 dark:text-white"
/>
</div> </div>
{/* TODO(alexhancock): Re-enable link previews once styled well again */} {/* TODO(alexhancock): Re-enable link previews once styled well again */}
@@ -1,36 +0,0 @@
import React, { useState } from 'react';
import { Check, Copy } from '../icons';
interface CopyButtonProps {
text: string;
className?: string;
iconClassName?: string;
lightIcon?: boolean;
}
export default function CopyButton({
text,
className = 'absolute bottom-2 right-2 p-1.5 rounded-lg bg-gray-700/50 text-gray-300 opacity-0 group-hover:opacity-100 transition-opacity duration-200 hover:bg-gray-600/50 hover:text-gray-100',
iconClassName = 'h-4 w-4',
lightIcon = false,
}: CopyButtonProps) {
const [copied, setCopied] = useState(false);
const handleCopy = async () => {
try {
await navigator.clipboard.writeText(text);
setCopied(true);
setTimeout(() => setCopied(false), 2000); // Reset after 2 seconds
} catch (err) {
console.error('Failed to copy text: ', err);
}
};
const Icon = copied ? Check : Copy;
return (
<button onClick={handleCopy} className={className} title="Copy text">
<Icon className={`${iconClassName} ${lightIcon ? 'text-white' : ''}`} />
</button>
);
}