diff --git a/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/execution-snapshot/execution-snapshot.tsx b/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/execution-snapshot/execution-snapshot.tsx index 64169b66793..955955e8c4d 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/execution-snapshot/execution-snapshot.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/execution-snapshot/execution-snapshot.tsx @@ -1,7 +1,7 @@ 'use client' import type React from 'react' -import { useRef, useState } from 'react' +import { useState } from 'react' import { AlertCircle } from 'lucide-react' import { createPortal } from 'react-dom' import { @@ -65,7 +65,6 @@ export function ExecutionSnapshot({ const [isMenuOpen, setIsMenuOpen] = useState(false) const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 }) - const menuRef = useRef(null) function closeMenu() { setIsMenuOpen(false) diff --git a/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/file-download/file-download.tsx b/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/file-download/file-download.tsx index f25a63842b5..ca40674b3ef 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/file-download/file-download.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/file-download/file-download.tsx @@ -1,9 +1,8 @@ 'use client' -import { useState } from 'react' import { createLogger } from '@sim/logger' import { useRouter } from 'next/navigation' -import { Button, Loader } from '@/components/emcn' +import { Button } from '@/components/emcn' import { Download } from '@/components/emcn/icons' import { extractWorkspaceIdFromExecutionKey, getViewerUrl } from '@/lib/uploads/utils/file-utils' @@ -41,14 +40,9 @@ function formatFileSize(bytes: number): string { } function FileCard({ file, isExecutionFile = false, workspaceId }: FileCardProps) { - const [isDownloading, setIsDownloading] = useState(false) const router = useRouter() const handleDownload = () => { - if (isDownloading) return - - setIsDownloading(true) - try { logger.info(`Initiating download for file: ${file.name}`) @@ -91,8 +85,6 @@ function FileCard({ file, isExecutionFile = false, workspaceId }: FileCardProps) } } catch (error) { logger.error(`Failed to download file ${file.name}:`, error) - } finally { - setIsDownloading(false) } } @@ -113,14 +105,9 @@ function FileCard({ file, isExecutionFile = false, workspaceId }: FileCardProps) variant='ghost' className='!h-[20px] !px-1.5 !py-0 text-xs' onClick={handleDownload} - disabled={isDownloading} > - {isDownloading ? ( - - ) : ( - - )} - {isDownloading ? 'Opening...' : 'Download'} + + Download @@ -148,84 +135,3 @@ export function FileCards({ files, isExecutionFile = false, workspaceId }: FileC ) } - -export function FileDownload({ - file, - isExecutionFile = false, - className, - workspaceId, -}: { - file: FileData - isExecutionFile?: boolean - className?: string - workspaceId?: string -}) { - const [isDownloading, setIsDownloading] = useState(false) - const router = useRouter() - - const handleDownload = () => { - if (isDownloading) return - - setIsDownloading(true) - - try { - logger.info(`Initiating download for file: ${file.name}`) - - if (file.key.startsWith('url/')) { - if (file.url) { - window.open(file.url, '_blank') - logger.info(`Opened URL-type file directly: ${file.url}`) - return - } - throw new Error('URL is required for URL-type files') - } - - let resolvedWorkspaceId = workspaceId - if (!resolvedWorkspaceId && isExecutionFile) { - resolvedWorkspaceId = extractWorkspaceIdFromExecutionKey(file.key) || undefined - } else if (!resolvedWorkspaceId) { - const segments = file.key.split('/') - if (segments.length >= 2 && /^[a-f0-9-]{36}$/.test(segments[0])) { - resolvedWorkspaceId = segments[0] - } - } - - if (isExecutionFile) { - const serveUrl = `/api/files/serve/${encodeURIComponent(file.key)}?context=execution` - window.open(serveUrl, '_blank') - logger.info(`Opened execution file serve URL: ${serveUrl}`) - } else { - const viewerUrl = resolvedWorkspaceId ? getViewerUrl(file.key, resolvedWorkspaceId) : null - - if (viewerUrl) { - router.push(viewerUrl) - logger.info(`Navigated to viewer URL: ${viewerUrl}`) - } else { - logger.warn( - `Could not construct viewer URL for file: ${file.name}, falling back to serve URL` - ) - const serveUrl = `/api/files/serve/${encodeURIComponent(file.key)}?context=workspace` - window.open(serveUrl, '_blank') - } - } - } catch (error) { - logger.error(`Failed to download file ${file.name}:`, error) - } finally { - setIsDownloading(false) - } - } - - return ( - - ) -} - -export default FileCards diff --git a/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/file-download/index.ts b/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/file-download/index.ts index fae73a3ef9e..d384b447553 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/file-download/index.ts +++ b/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/file-download/index.ts @@ -1 +1 @@ -export { FileCards, FileDownload } from './file-download' +export { FileCards } from './file-download' diff --git a/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/trace-view/trace-view.tsx b/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/trace-view/trace-view.tsx index 0a90434e568..fc4030998e8 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/trace-view/trace-view.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/trace-view/trace-view.tsx @@ -45,6 +45,7 @@ import { parseTime, } from '@/app/workspace/[workspaceId]/logs/components/log-details/utils' import { useCodeViewerFeatures } from '@/hooks/use-code-viewer' +import { useCopyToClipboard } from '@/hooks/use-copy-to-clipboard' const DEFAULT_TREE_PANE_WIDTH = 240 const MIN_TREE_PANE_WIDTH = 200 @@ -428,7 +429,7 @@ function DetailCodeSection({ const [isOpen, setIsOpen] = useState(defaultOpen) const [isContextMenuOpen, setIsContextMenuOpen] = useState(false) const [contextMenuPosition, setContextMenuPosition] = useState({ x: 0, y: 0 }) - const [copied, setCopied] = useState(false) + const { copied, copy } = useCopyToClipboard({ resetMs: 1500 }) const contentRef = useRef(null) const { @@ -459,9 +460,7 @@ function DetailCodeSection({ } function handleCopy() { - navigator.clipboard.writeText(jsonString) - setCopied(true) - setTimeout(() => setCopied(false), 1500) + copy(jsonString) setIsContextMenuOpen(false) } @@ -819,6 +818,7 @@ const TraceDetailPane = memo(function TraceDetailPane({ span }: { span: TraceSpa */ export const TraceView = memo(function TraceView({ traceSpans, runCostDollars }: TraceViewProps) { const treeRef = useRef(null) + const { copied: traceCopied, copy: copyTrace } = useCopyToClipboard() const [searchQuery, setSearchQuery] = useState('') const [treePaneWidth, setTreePaneWidth] = useState(DEFAULT_TREE_PANE_WIDTH) const treePaneWidthRef = useRef(DEFAULT_TREE_PANE_WIDTH) @@ -1042,6 +1042,26 @@ export const TraceView = memo(function TraceView({ traceSpans, runCostDollars }: placeholder='Filter spans' className='w-[140px]' /> + + + + + + {traceCopied ? 'Copied' : 'Copy raw trace'} + +