Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client'

import { useEffect, useMemo, useRef, useState } from 'react'
import { ChevronDown, ChevronUp, Eye, X } from 'lucide-react'
import { ChevronDown, ChevronUp, Eye, Loader2, X } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { CopyButton } from '@/components/ui/copy-button'
import { ScrollArea } from '@/components/ui/scroll-area'
Expand Down Expand Up @@ -209,6 +209,15 @@ export function Sidebar({
}
}, [log?.id])

const isLoadingDetails = useMemo(() => {
if (!log) return false
// Only show while we expect details to arrive (has executionId)
if (!log.executionId) return false
const hasEnhanced = !!log.executionData?.enhanced
const hasAnyDetails = hasEnhanced || !!log.cost || Array.isArray(log.executionData?.traceSpans)
return !hasAnyDetails
}, [log])

const formattedContent = useMemo(() => {
if (!log) return null

Expand Down Expand Up @@ -476,6 +485,14 @@ export function Sidebar({
</div>
)}

{/* Suspense while details load (positioned after summary fields) */}
{isLoadingDetails && (
<div className='flex w-full items-center justify-start gap-2 py-2 text-muted-foreground'>
<Loader2 className='h-4 w-4 animate-spin' />
<span className='text-sm'>Loading details…</span>
</div>
)}

{/* Files */}
{log.files && log.files.length > 0 && (
<div>
Expand Down Expand Up @@ -527,11 +544,7 @@ export function Sidebar({
</div>
)}

{/* Message Content */}
<div className='w-full pb-2'>
<h3 className='mb-1 font-medium text-muted-foreground text-xs'>Message</h3>
<div className='w-full'>{formattedContent}</div>
</div>
{/* end suspense */}

{/* Trace Spans (if available and this is a workflow execution log) */}
{isWorkflowExecutionLog && log.executionData?.traceSpans && (
Expand Down
26 changes: 16 additions & 10 deletions apps/sim/app/workspace/[workspaceId]/logs/logs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,10 @@ export default function Logs() {
const [selectedLog, setSelectedLog] = useState<WorkflowLog | null>(null)
const [selectedLogIndex, setSelectedLogIndex] = useState<number>(-1)
const [isSidebarOpen, setIsSidebarOpen] = useState(false)
const [isDetailsLoading, setIsDetailsLoading] = useState(false)
const detailsCacheRef = useRef<Map<string, any>>(new Map())
const detailsAbortRef = useRef<AbortController | null>(null)
const currentDetailsIdRef = useRef<string | null>(null)
const selectedRowRef = useRef<HTMLTableRowElement | null>(null)
const loaderRef = useRef<HTMLDivElement>(null)
const scrollContainerRef = useRef<HTMLDivElement>(null)
Expand Down Expand Up @@ -118,6 +120,7 @@ export default function Logs() {
const index = logs.findIndex((l) => l.id === log.id)
setSelectedLogIndex(index)
setIsSidebarOpen(true)
setIsDetailsLoading(true)

// Fetch details for current, previous, and next concurrently with cache
const currentId = log.id
Expand All @@ -134,24 +137,26 @@ export default function Logs() {
}
const controller = new AbortController()
detailsAbortRef.current = controller
currentDetailsIdRef.current = currentId

const idsToFetch: Array<{ id: string; merge: boolean }> = []
if (currentId && !detailsCacheRef.current.has(currentId))
idsToFetch.push({ id: currentId, merge: true })
const cachedCurrent = currentId ? detailsCacheRef.current.get(currentId) : undefined
if (currentId && !cachedCurrent) idsToFetch.push({ id: currentId, merge: true })
if (prevId && !detailsCacheRef.current.has(prevId))
idsToFetch.push({ id: prevId, merge: false })
if (nextId && !detailsCacheRef.current.has(nextId))
idsToFetch.push({ id: nextId, merge: false })

if (idsToFetch.length === 0) {
const cached = detailsCacheRef.current.get(currentId)
if (cached) {
setSelectedLog((prev) =>
prev && prev.id === currentId ? ({ ...(prev as any), ...(cached as any) } as any) : prev
)
}
return
// Merge cached current immediately
if (cachedCurrent) {
setSelectedLog((prev) =>
prev && prev.id === currentId
? ({ ...(prev as any), ...(cachedCurrent as any) } as any)
: prev
)
setIsDetailsLoading(false)
}
if (idsToFetch.length === 0) return

Promise.all(
idsToFetch.map(async ({ id, merge }) => {
Expand All @@ -166,6 +171,7 @@ export default function Logs() {
setSelectedLog((prev) =>
prev && prev.id === id ? ({ ...(prev as any), ...(detailed as any) } as any) : prev
)
if (currentDetailsIdRef.current === id) setIsDetailsLoading(false)
}
}
} catch (e: any) {
Expand Down