From 30b5814de55e1a979f288dab5779bfd3f4dabda5 Mon Sep 17 00:00:00 2001 From: CrazyBunQnQ Date: Wed, 25 Feb 2026 22:25:25 +0800 Subject: [PATCH] feat(ui): Add a copy button to the Nexus AI and copy the markdown results after clicking it --- .../src/components/MarkdownRenderer.tsx | 32 +++++++++++++++++-- gitnexus-web/src/components/RightPanel.tsx | 4 ++- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/gitnexus-web/src/components/MarkdownRenderer.tsx b/gitnexus-web/src/components/MarkdownRenderer.tsx index db9081af09..77f6e6f8df 100644 --- a/gitnexus-web/src/components/MarkdownRenderer.tsx +++ b/gitnexus-web/src/components/MarkdownRenderer.tsx @@ -1,10 +1,11 @@ -import React from 'react'; +import React, { useState } from 'react'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism'; import { MermaidDiagram } from './MermaidDiagram'; import { ToolCallCard } from './ToolCallCard'; +import { Copy, Check } from 'lucide-react'; // Custom syntax theme const customTheme = { @@ -28,13 +29,26 @@ interface MarkdownRendererProps { content: string; onLinkClick?: (href: string) => void; toolCalls?: any[]; // Keep flexible for now + showCopyButton?: boolean; } export const MarkdownRenderer: React.FC = ({ content, onLinkClick, - toolCalls + toolCalls, + showCopyButton = false }) => { + const [copied, setCopied] = useState(false); + + const handleCopy = async () => { + try { + await navigator.clipboard.writeText(content); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + } catch (err) { + console.error('Failed to copy:', err); + } + }; // Helper to format text for display (convert [[links]] to markdown links) const formatMarkdownForDisplay = (md: string) => { @@ -166,6 +180,20 @@ export const MarkdownRenderer: React.FC = ({ {formattedContent} + {/* Copy Button */} + {showCopyButton && ( +
+ +
+ )} + {/* Tool Call Cards appended at the bottom if provided */} {toolCalls && toolCalls.length > 0 && (
diff --git a/gitnexus-web/src/components/RightPanel.tsx b/gitnexus-web/src/components/RightPanel.tsx index 1f27fc9174..84be0f7385 100644 --- a/gitnexus-web/src/components/RightPanel.tsx +++ b/gitnexus-web/src/components/RightPanel.tsx @@ -345,7 +345,7 @@ export const RightPanel = () => { {/* Render steps in order (reasoning, tool calls, content interleaved) */} {message.steps && message.steps.length > 0 ? (
- {message.steps.map((step) => ( + {message.steps.map((step, index) => (
{step.type === 'reasoning' && step.content && (
@@ -364,6 +364,7 @@ export const RightPanel = () => { )}
@@ -375,6 +376,7 @@ export const RightPanel = () => { content={message.content} onLinkClick={handleLinkClick} toolCalls={message.toolCalls} + showCopyButton={true} /> )}