@@ -4,11 +4,14 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
44import {
55 ArrowDown ,
66 ArrowUp ,
7+ Check ,
78 ChevronDown as ChevronDownIcon ,
89 ChevronUp ,
10+ Clipboard ,
911 ExternalLink ,
1012 Maximize2 ,
1113 RepeatIcon ,
14+ Search ,
1215 SplitIcon ,
1316 X ,
1417} from 'lucide-react'
@@ -813,6 +816,13 @@ function PreviewEditorContent({
813816 } = useContextMenu ( )
814817
815818 const [ contextMenuData , setContextMenuData ] = useState ( { content : '' , copyOnly : false } )
819+ const [ copiedSection , setCopiedSection ] = useState < 'input' | 'output' | null > ( null )
820+
821+ const handleCopySection = useCallback ( ( content : string , section : 'input' | 'output' ) => {
822+ navigator . clipboard . writeText ( content )
823+ setCopiedSection ( section )
824+ setTimeout ( ( ) => setCopiedSection ( null ) , 1500 )
825+ } , [ ] )
816826
817827 const openContextMenu = useCallback (
818828 ( e : React . MouseEvent , content : string , copyOnly : boolean ) => {
@@ -862,9 +872,6 @@ function PreviewEditorContent({
862872 }
863873 } , [ contextMenuData . content ] )
864874
865- /**
866- * Handles mouse down event on the resize handle to initiate resizing
867- */
868875 const handleConnectionsResizeMouseDown = useCallback (
869876 ( e : React . MouseEvent ) => {
870877 setIsResizing ( true )
@@ -874,18 +881,12 @@ function PreviewEditorContent({
874881 [ connectionsHeight ]
875882 )
876883
877- /**
878- * Toggle connections collapsed state
879- */
880884 const toggleConnectionsCollapsed = useCallback ( ( ) => {
881885 setConnectionsHeight ( ( prev ) =>
882886 prev <= MIN_CONNECTIONS_HEIGHT ? DEFAULT_CONNECTIONS_HEIGHT : MIN_CONNECTIONS_HEIGHT
883887 )
884888 } , [ ] )
885889
886- /**
887- * Sets up resize event listeners during resize operations
888- */
889890 useEffect ( ( ) => {
890891 if ( ! isResizing ) return
891892
@@ -1205,7 +1206,11 @@ function PreviewEditorContent({
12051206 }
12061207 emptyMessage = 'No input data'
12071208 >
1208- < div onContextMenu = { handleExecutionContextMenu } ref = { contentRef } >
1209+ < div
1210+ onContextMenu = { handleExecutionContextMenu }
1211+ ref = { contentRef }
1212+ className = 'relative'
1213+ >
12091214 < Code . Viewer
12101215 code = { formatValueAsJson ( executionData . input ) }
12111216 language = 'json'
@@ -1215,6 +1220,49 @@ function PreviewEditorContent({
12151220 currentMatchIndex = { currentMatchIndex }
12161221 onMatchCountChange = { handleMatchCountChange }
12171222 />
1223+ { /* Action buttons overlay */ }
1224+ { ! isSearchActive && (
1225+ < div className = 'absolute top-[7px] right-[6px] z-10 flex gap-[4px]' >
1226+ < Tooltip . Root >
1227+ < Tooltip . Trigger asChild >
1228+ < Button
1229+ type = 'button'
1230+ variant = 'ghost'
1231+ onClick = { ( e ) => {
1232+ e . stopPropagation ( )
1233+ handleCopySection ( formatValueAsJson ( executionData . input ) , 'input' )
1234+ } }
1235+ className = 'h-[20px] w-[20px] cursor-pointer border border-[var(--border-1)] bg-transparent p-0 backdrop-blur-sm hover:bg-[var(--surface-4)]'
1236+ >
1237+ { copiedSection === 'input' ? (
1238+ < Check className = 'h-[10px] w-[10px] text-[var(--text-success)]' />
1239+ ) : (
1240+ < Clipboard className = 'h-[10px] w-[10px]' />
1241+ ) }
1242+ </ Button >
1243+ </ Tooltip . Trigger >
1244+ < Tooltip . Content side = 'top' >
1245+ { copiedSection === 'input' ? 'Copied' : 'Copy' }
1246+ </ Tooltip . Content >
1247+ </ Tooltip . Root >
1248+ < Tooltip . Root >
1249+ < Tooltip . Trigger asChild >
1250+ < Button
1251+ type = 'button'
1252+ variant = 'ghost'
1253+ onClick = { ( e ) => {
1254+ e . stopPropagation ( )
1255+ activateSearch ( )
1256+ } }
1257+ className = 'h-[20px] w-[20px] cursor-pointer border border-[var(--border-1)] bg-transparent p-0 backdrop-blur-sm hover:bg-[var(--surface-4)]'
1258+ >
1259+ < Search className = 'h-[10px] w-[10px]' />
1260+ </ Button >
1261+ </ Tooltip . Trigger >
1262+ < Tooltip . Content side = 'top' > Search</ Tooltip . Content >
1263+ </ Tooltip . Root >
1264+ </ div >
1265+ ) }
12181266 </ div >
12191267 </ CollapsibleSection >
12201268 ) }
@@ -1231,7 +1279,7 @@ function PreviewEditorContent({
12311279 emptyMessage = 'No output data'
12321280 isError = { executionData . status === 'error' }
12331281 >
1234- < div onContextMenu = { handleExecutionContextMenu } >
1282+ < div onContextMenu = { handleExecutionContextMenu } className = 'relative' >
12351283 < Code . Viewer
12361284 code = { formatValueAsJson ( executionData . output ) }
12371285 language = 'json'
@@ -1244,6 +1292,49 @@ function PreviewEditorContent({
12441292 currentMatchIndex = { currentMatchIndex }
12451293 onMatchCountChange = { handleMatchCountChange }
12461294 />
1295+ { /* Action buttons overlay */ }
1296+ { ! isSearchActive && (
1297+ < div className = 'absolute top-[7px] right-[6px] z-10 flex gap-[4px]' >
1298+ < Tooltip . Root >
1299+ < Tooltip . Trigger asChild >
1300+ < Button
1301+ type = 'button'
1302+ variant = 'ghost'
1303+ onClick = { ( e ) => {
1304+ e . stopPropagation ( )
1305+ handleCopySection ( formatValueAsJson ( executionData . output ) , 'output' )
1306+ } }
1307+ className = 'h-[20px] w-[20px] cursor-pointer border border-[var(--border-1)] bg-transparent p-0 backdrop-blur-sm hover:bg-[var(--surface-4)]'
1308+ >
1309+ { copiedSection === 'output' ? (
1310+ < Check className = 'h-[10px] w-[10px] text-[var(--text-success)]' />
1311+ ) : (
1312+ < Clipboard className = 'h-[10px] w-[10px]' />
1313+ ) }
1314+ </ Button >
1315+ </ Tooltip . Trigger >
1316+ < Tooltip . Content side = 'top' >
1317+ { copiedSection === 'output' ? 'Copied' : 'Copy' }
1318+ </ Tooltip . Content >
1319+ </ Tooltip . Root >
1320+ < Tooltip . Root >
1321+ < Tooltip . Trigger asChild >
1322+ < Button
1323+ type = 'button'
1324+ variant = 'ghost'
1325+ onClick = { ( e ) => {
1326+ e . stopPropagation ( )
1327+ activateSearch ( )
1328+ } }
1329+ className = 'h-[20px] w-[20px] cursor-pointer border border-[var(--border-1)] bg-transparent p-0 backdrop-blur-sm hover:bg-[var(--surface-4)]'
1330+ >
1331+ < Search className = 'h-[10px] w-[10px]' />
1332+ </ Button >
1333+ </ Tooltip . Trigger >
1334+ < Tooltip . Content side = 'top' > Search</ Tooltip . Content >
1335+ </ Tooltip . Root >
1336+ </ div >
1337+ ) }
12471338 </ div >
12481339 </ CollapsibleSection >
12491340 ) }
0 commit comments