@@ -45,6 +45,7 @@ import {
4545 parseTime ,
4646} from '@/app/workspace/[workspaceId]/logs/components/log-details/utils'
4747import { useCodeViewerFeatures } from '@/hooks/use-code-viewer'
48+ import { useCopyToClipboard } from '@/hooks/use-copy-to-clipboard'
4849
4950const DEFAULT_TREE_PANE_WIDTH = 240
5051const MIN_TREE_PANE_WIDTH = 200
@@ -819,6 +820,7 @@ const TraceDetailPane = memo(function TraceDetailPane({ span }: { span: TraceSpa
819820 */
820821export const TraceView = memo ( function TraceView ( { traceSpans, runCostDollars } : TraceViewProps ) {
821822 const treeRef = useRef < HTMLDivElement > ( null )
823+ const { copied : traceCopied , copy : copyTrace } = useCopyToClipboard ( )
822824 const [ searchQuery , setSearchQuery ] = useState ( '' )
823825 const [ treePaneWidth , setTreePaneWidth ] = useState ( DEFAULT_TREE_PANE_WIDTH )
824826 const treePaneWidthRef = useRef ( DEFAULT_TREE_PANE_WIDTH )
@@ -1042,6 +1044,26 @@ export const TraceView = memo(function TraceView({ traceSpans, runCostDollars }:
10421044 placeholder = 'Filter spans'
10431045 className = 'w-[140px]'
10441046 />
1047+ < Tooltip . Root >
1048+ < Tooltip . Trigger asChild >
1049+ < Button
1050+ type = 'button'
1051+ variant = 'ghost'
1052+ className = '!p-1'
1053+ onClick = { ( ) => copyTrace ( JSON . stringify ( traceSpans , null , 2 ) ) }
1054+ aria-label = 'Copy raw trace'
1055+ >
1056+ { traceCopied ? (
1057+ < Check className = 'size-[12px] text-[var(--text-success)]' />
1058+ ) : (
1059+ < Clipboard className = 'size-[12px]' />
1060+ ) }
1061+ </ Button >
1062+ </ Tooltip . Trigger >
1063+ < Tooltip . Content side = 'top' >
1064+ { traceCopied ? 'Copied' : 'Copy raw trace' }
1065+ </ Tooltip . Content >
1066+ </ Tooltip . Root >
10451067 < Tooltip . Root >
10461068 < Tooltip . Trigger asChild >
10471069 < Button
0 commit comments