1- import React from 'react' ;
2- import { Database , Search , Code , MessageSquare , AlertTriangle } from 'lucide-react' ;
1+ import React , { useState } from 'react' ;
2+ import { Database , Search , Code , MessageSquare , AlertTriangle , Copy , Check } from 'lucide-react' ;
33import { Avatar , AvatarFallback , AvatarImage } from '@/components/ui/avatar' ;
44import { Card , CardContent } from '@/components/ui/card' ;
55import { Badge } from '@/components/ui/badge' ;
@@ -36,6 +36,18 @@ interface ChatMessageProps {
3636}
3737
3838const ChatMessage = ( { type, content, steps, queryData, analysisInfo, confirmationData, progress, user, onConfirm, onCancel } : ChatMessageProps ) => {
39+ const [ copied , setCopied ] = useState ( false ) ;
40+
41+ const handleCopyQuery = async ( ) => {
42+ try {
43+ await navigator . clipboard . writeText ( content ) ;
44+ setCopied ( true ) ;
45+ setTimeout ( ( ) => setCopied ( false ) , 2000 ) ;
46+ } catch ( err ) {
47+ console . error ( 'Failed to copy text:' , err ) ;
48+ }
49+ } ;
50+
3951 if ( type === 'confirmation' ) {
4052 const operationType = ( confirmationData ?. operationType ?? 'UNKNOWN' ) . toUpperCase ( ) ;
4153 const isHighRisk = [ 'DELETE' , 'DROP' , 'TRUNCATE' ] . includes ( operationType ) ;
@@ -64,8 +76,8 @@ const ChatMessage = ({ type, content, steps, queryData, analysisInfo, confirmati
6476 This operation will perform a < span className = { `font-semibold ${ isHighRisk ? 'text-red-400' : 'text-yellow-400' } ` } > { operationType } </ span > query:
6577 </ p >
6678 { confirmationData ?. sqlQuery && (
67- < div className = "bg-gray-900 border border-gray-700 rounded p-3 overflow-x-auto " >
68- < pre className = "text-sm font-mono text-gray-200" >
79+ < div className = "bg-gray-900 border border-gray-700 rounded p-3" >
80+ < pre className = "text-sm font-mono text-gray-200 whitespace-pre-wrap break-words overflow-wrap-anywhere " >
6981 < code className = "language-sql" > { confirmationData . sqlQuery } </ code >
7082 </ pre >
7183 </ div >
@@ -156,10 +168,25 @@ const ChatMessage = ({ type, content, steps, queryData, analysisInfo, confirmati
156168 </ div >
157169
158170 { hasSQL && (
159- < div className = "overflow-x-auto -mx-2 px-2" >
160- < pre className = "bg-gray-900 text-gray-200 p-3 rounded text-sm mb-3 w-fit min-w-full font-mono" >
161- < code className = "language-sql" > { content } </ code >
162- </ pre >
171+ < div className = "-mx-2 px-2" >
172+ < div className = "relative" >
173+ < Button
174+ variant = "ghost"
175+ size = "sm"
176+ onClick = { handleCopyQuery }
177+ className = "absolute top-2 right-2 z-10 h-8 w-8 p-0 hover:bg-gray-800"
178+ title = { copied ? "Copied!" : "Copy query" }
179+ >
180+ { copied ? (
181+ < Check className = "w-4 h-4 text-green-400" />
182+ ) : (
183+ < Copy className = "w-4 h-4 text-gray-400" />
184+ ) }
185+ </ Button >
186+ < pre className = "bg-gray-900 text-gray-200 p-3 rounded text-sm mb-3 font-mono whitespace-pre-wrap break-words overflow-wrap-anywhere" >
187+ < code className = "language-sql" > { content } </ code >
188+ </ pre >
189+ </ div >
163190 </ div >
164191 ) }
165192
@@ -219,7 +246,7 @@ const ChatMessage = ({ type, content, steps, queryData, analysisInfo, confirmati
219246 < thead className = "sticky top-0 bg-gray-800 z-10" >
220247 < tr className = "border-b border-gray-700" >
221248 { Object . keys ( queryData [ 0 ] ) . map ( ( column ) => (
222- < th key = { column } className = "text-left px-3 py-2 text-gray-300 font-semibold bg-gray-800 whitespace-nowrap" >
249+ < th key = { column } className = "text-left px-3 py-2 text-gray-300 font-semibold bg-gray-800 break-words" style = { { maxWidth : '300px' , minWidth : '100px' } } >
223250 { column }
224251 </ th >
225252 ) ) }
@@ -229,7 +256,7 @@ const ChatMessage = ({ type, content, steps, queryData, analysisInfo, confirmati
229256 { queryData . map ( ( row , index ) => (
230257 < tr key = { index } className = "border-b border-gray-700/50 hover:bg-gray-700/30" >
231258 { Object . values ( row ) . map ( ( value : any , cellIndex ) => (
232- < td key = { cellIndex } className = "px-3 py-2 text-gray-200 whitespace-nowrap" >
259+ < td key = { cellIndex } className = "px-3 py-2 text-gray-200 break-words" style = { { maxWidth : '300px' , minWidth : '100px' } } >
233260 { String ( value ) }
234261 </ td >
235262 ) ) }
0 commit comments