Skip to content

Commit 4c4b335

Browse files
committed
Checkpoint
1 parent 0c1ee23 commit 4c4b335

3 files changed

Lines changed: 212 additions & 23 deletions

File tree

apps/sim/app/workspace/[workspaceId]/logs/components/sidebar/sidebar.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { TraceSpansDisplay } from '@/app/workspace/[workspaceId]/logs/components
1616
import { formatDate } from '@/app/workspace/[workspaceId]/logs/utils/format-date'
1717
import { formatCost } from '@/providers/utils'
1818
import type { WorkflowLog } from '@/stores/logs/filters/types'
19-
import { useRouter } from 'next/navigation'
19+
import { useParams, useRouter } from 'next/navigation'
2020

2121
interface LogSidebarProps {
2222
log: WorkflowLog | null
@@ -200,6 +200,8 @@ export function Sidebar({
200200
const [isModelsExpanded, setIsModelsExpanded] = useState(false)
201201
const [isFrozenCanvasOpen, setIsFrozenCanvasOpen] = useState(false)
202202
const scrollAreaRef = useRef<HTMLDivElement>(null)
203+
const router = useRouter()
204+
const params = useParams() as { workspaceId?: string }
203205

204206
// Update currentLogId when log changes
205207
useEffect(() => {
@@ -545,8 +547,7 @@ export function Sidebar({
545547
size='sm'
546548
onClick={() => {
547549
try {
548-
const router = useRouter()
549-
const href = `/workspace/${encodeURIComponent(String(log.workflowId || ''))}/w/${encodeURIComponent(String(log.workflowId || ''))}`
550+
const href = `/workspace/${encodeURIComponent(String(params?.workspaceId || ''))}/w/${encodeURIComponent(String(log.workflowId || ''))}`
550551
router.push(href)
551552
} catch {}
552553
}}

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/debug/debug.tsx

Lines changed: 195 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@ import { cn } from '@/lib/utils'
2626
import { useCurrentWorkflow } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-current-workflow'
2727
import { useWorkflowExecution } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution'
2828
import { getBlock } from '@/blocks'
29+
import { useDebugCanvasStore } from '@/stores/execution/debug-canvas/store'
2930
import { useDebugSnapshotStore } from '@/stores/execution/debug-snapshots/store'
3031
import { useExecutionStore } from '@/stores/execution/store'
3132
import { useVariablesStore } from '@/stores/panel/variables/store'
3233
import { useEnvironmentStore } from '@/stores/settings/environment/store'
34+
import { useWorkflowDiffStore } from '@/stores/workflow-diff/store'
3335
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
3436
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
3537
import { mergeSubblockState } from '@/stores/workflows/utils'
@@ -38,7 +40,6 @@ import { getTool } from '@/tools/utils'
3840
import { getTrigger, getTriggersByProvider } from '@/triggers'
3941
import { useParams } from 'next/navigation'
4042
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
41-
import { useDebugCanvasStore } from '@/stores/execution/debug-canvas/store'
4243
import type { WorkflowState } from '@/stores/workflows/workflow/types'
4344

4445
// Token render cache (LRU-style)
@@ -1737,13 +1738,30 @@ export function DebugPanel() {
17371738
return Object.entries(allEnvVars).filter(([key]) => blockEnvVarRefs.has(key))
17381739
}, [allEnvVars, scopedVariables, focusedBlock, focusedBlockId, activeWorkflowId, blocksList])
17391740

1740-
// Reset hasStartedRef when debug mode is deactivated
1741+
// Reset hasStartedRef and clear debug canvas when debug mode is deactivated
17411742
useEffect(() => {
17421743
if (!isDebugging) {
17431744
hasStartedRef.current = false
17441745
setPanelFocusedBlockId(null)
1746+
setSelectedExecutionKey(undefined)
1747+
// Clear debug canvas when exiting debug mode
1748+
console.log('[Debug] Clearing debug canvas - debug mode deactivated')
1749+
useDebugCanvasStore.getState().clear()
1750+
1751+
// Restore original subblock values
1752+
if (originalSubblockValuesRef.current && activeWorkflowId) {
1753+
const valuesToRestore = originalSubblockValuesRef.current
1754+
console.log('[Debug] Restoring original subblock values on debug exit')
1755+
useSubBlockStore.setState((state) => ({
1756+
workflowValues: {
1757+
...state.workflowValues,
1758+
[activeWorkflowId]: valuesToRestore
1759+
}
1760+
}))
1761+
originalSubblockValuesRef.current = null
1762+
}
17451763
}
1746-
}, [isDebugging, setPanelFocusedBlockId])
1764+
}, [isDebugging, setPanelFocusedBlockId, activeWorkflowId])
17471765

17481766
// Clear init pending when debug context and initial pending arrive
17491767
useEffect(() => {
@@ -1752,9 +1770,29 @@ export function DebugPanel() {
17521770
}
17531771
}, [debugContext, pendingBlocks])
17541772

1773+
// Cleanup debug canvas on unmount
1774+
useEffect(() => {
1775+
return () => {
1776+
console.log('[Debug] Component unmounting - clearing debug canvas')
1777+
useDebugCanvasStore.getState().clear()
1778+
1779+
// Restore original subblock values on unmount
1780+
if (originalSubblockValuesRef.current && activeWorkflowId) {
1781+
const valuesToRestore = originalSubblockValuesRef.current
1782+
console.log('[Debug] Restoring original subblock values on unmount')
1783+
useSubBlockStore.setState((state) => ({
1784+
workflowValues: {
1785+
...state.workflowValues,
1786+
[activeWorkflowId]: valuesToRestore
1787+
}
1788+
}))
1789+
}
1790+
}
1791+
}, [activeWorkflowId])
1792+
17551793
// Load recent executions for this workflow
17561794
useEffect(() => {
1757-
if (!workspaceId || !workflowId) return
1795+
if (!isDebugging || !workspaceId || !workflowId) return
17581796
let canceled = false
17591797
const load = async () => {
17601798
try {
@@ -1777,9 +1815,6 @@ export function DebugPanel() {
17771815
}))
17781816
: []
17791817
setExecutions(items)
1780-
if (!selectedExecutionKey && items.length > 0) {
1781-
setSelectedExecutionKey(items[0].id)
1782-
}
17831818
} catch {
17841819
} finally {
17851820
if (!canceled) setIsLoadingExecutions(false)
@@ -1789,27 +1824,173 @@ export function DebugPanel() {
17891824
return () => {
17901825
canceled = true
17911826
}
1792-
}, [workspaceId, workflowId])
1827+
}, [isDebugging, workspaceId, workflowId])
17931828

1829+
// Track original subblock values to restore when clearing debug canvas
1830+
const originalSubblockValuesRef = useRef<Record<string, Record<string, any>> | null>(null)
1831+
17941832
// Load selected execution's workflow into debug canvas
17951833
useEffect(() => {
1796-
if (!selectedExecutionKey) return
1834+
if (!selectedExecutionKey) {
1835+
// Clear debug canvas and restore original subblock values when no execution is selected
1836+
console.log('[Debug] Clearing debug canvas - no selection')
1837+
useDebugCanvasStore.getState().clear()
1838+
1839+
// Restore original subblock values if we saved them
1840+
if (originalSubblockValuesRef.current && activeWorkflowId) {
1841+
const valuesToRestore = originalSubblockValuesRef.current
1842+
console.log('[Debug] Restoring original subblock values')
1843+
useSubBlockStore.setState((state) => ({
1844+
workflowValues: {
1845+
...state.workflowValues,
1846+
[activeWorkflowId]: valuesToRestore
1847+
}
1848+
}))
1849+
originalSubblockValuesRef.current = null
1850+
}
1851+
return
1852+
}
17971853
const selected = executions.find((e) => e.id === selectedExecutionKey)
17981854
const execId = selected?.executionId
1799-
if (!execId) return
1855+
if (!execId) {
1856+
console.log('[Debug] No executionId for selected item')
1857+
return
1858+
}
18001859

18011860
let canceled = false
18021861
const load = async () => {
18031862
try {
1863+
console.log('[Debug] Loading frozen canvas for execution:', execId)
18041864
const response = await fetch(`/api/logs/${encodeURIComponent(execId)}/frozen-canvas`)
1865+
if (!response.ok) {
1866+
console.error('[Debug] Failed to fetch frozen canvas:', response.status)
1867+
return
1868+
}
18051869
const json = await response.json()
18061870
if (canceled) return
1807-
const state = json?.data?.workflowState as WorkflowState | undefined
1808-
if (state) {
1871+
1872+
// Try multiple possible response formats
1873+
const state = (json?.workflowState || json?.data?.workflowState || json) as WorkflowState | undefined
1874+
console.log('[Debug] Parsed workflow state:', {
1875+
hasState: !!state,
1876+
hasBlocks: !!(state?.blocks),
1877+
blockCount: state?.blocks ? Object.keys(state.blocks).length : 0,
1878+
hasEdges: !!(state?.edges),
1879+
edgeCount: state?.edges ? state.edges.length : 0
1880+
})
1881+
console.log('[Debug] Blocks:', state?.blocks)
1882+
console.log('[Debug] Edges:', state?.edges)
1883+
console.log('[Debug] Full state:', state)
1884+
1885+
if (state && state.blocks && Object.keys(state.blocks).length > 0) {
1886+
// Ensure edges array exists (it might be missing from old snapshots)
1887+
if (!state.edges) {
1888+
console.log('[Debug] No edges in state, creating empty array')
1889+
state.edges = []
1890+
}
1891+
1892+
// Ensure loops and parallels exist
1893+
if (!state.loops) state.loops = {}
1894+
if (!state.parallels) state.parallels = {}
1895+
1896+
// Clear any active diff mode first
1897+
try {
1898+
const diffStore = useWorkflowDiffStore.getState()
1899+
if (diffStore.isShowingDiff) {
1900+
diffStore.clearDiff()
1901+
}
1902+
} catch (e) {
1903+
console.error('[Debug] Error clearing diff:', e)
1904+
}
1905+
1906+
// Activate debug canvas with the fetched workflow state
1907+
console.log('[Debug] Activating debug canvas with state')
1908+
console.log('[Debug] State to activate:', {
1909+
blocks: Object.keys(state.blocks),
1910+
edges: state.edges,
1911+
loops: state.loops,
1912+
parallels: state.parallels
1913+
})
1914+
1915+
// Extract and load subblock values from the frozen state
1916+
const subblockValues: Record<string, Record<string, any>> = {}
1917+
Object.entries(state.blocks).forEach(([blockId, block]) => {
1918+
const blockState = block as any
1919+
if (blockState.subBlocks) {
1920+
subblockValues[blockId] = {}
1921+
Object.entries(blockState.subBlocks).forEach(([subblockId, subblock]) => {
1922+
const subblockData = subblock as any
1923+
if (subblockData && 'value' in subblockData) {
1924+
subblockValues[blockId][subblockId] = subblockData.value
1925+
}
1926+
})
1927+
}
1928+
})
1929+
1930+
console.log('[Debug] Extracted subblock values:', subblockValues)
1931+
1932+
// Load subblock values into the store for the current workflow
1933+
if (activeWorkflowId && Object.keys(subblockValues).length > 0) {
1934+
// Save original values if not already saved
1935+
if (!originalSubblockValuesRef.current) {
1936+
const currentValues = useSubBlockStore.getState().workflowValues[activeWorkflowId]
1937+
if (currentValues) {
1938+
originalSubblockValuesRef.current = { ...currentValues }
1939+
console.log('[Debug] Saved original subblock values')
1940+
}
1941+
}
1942+
1943+
console.log('[Debug] Loading subblock values for workflow:', activeWorkflowId)
1944+
useSubBlockStore.setState((state) => ({
1945+
workflowValues: {
1946+
...state.workflowValues,
1947+
[activeWorkflowId]: subblockValues
1948+
}
1949+
}))
1950+
}
1951+
18091952
useDebugCanvasStore.getState().activate(state)
1953+
1954+
// Reset debug execution state to align with the new canvas
1955+
useDebugSnapshotStore.getState().clear()
1956+
1957+
const execStore = useExecutionStore.getState()
1958+
execStore.setIsExecuting(false)
1959+
execStore.setIsDebugging(true)
1960+
execStore.setExecutor(null)
1961+
execStore.setDebugContext(null)
1962+
execStore.setExecutingBlockIds(new Set())
1963+
execStore.setActiveBlocks(new Set())
1964+
1965+
// Determine starter block from the loaded state and set as pending/focused
1966+
const blocks = Object.values(state.blocks || {}) as any[]
1967+
const starterBlock = blocks.find((b: any) => b?.type === 'starter' || b?.metadata?.id === 'starter') as any
1968+
const starterIdFromState = starterBlock?.id as string | undefined
1969+
console.log('[Debug] Setting starter as pending:', starterIdFromState)
1970+
execStore.setPendingBlocks(starterIdFromState ? [starterIdFromState] : [])
1971+
execStore.setPanelFocusedBlockId(starterIdFromState || null)
1972+
1973+
// Verify the canvas was activated and trigger a re-render
1974+
setTimeout(() => {
1975+
const debugState = useDebugCanvasStore.getState()
1976+
console.log('[Debug] Debug canvas state after activation:', {
1977+
isActive: debugState.isActive,
1978+
hasWorkflowState: !!debugState.workflowState,
1979+
workflowStateBlocks: debugState.workflowState?.blocks ? Object.keys(debugState.workflowState.blocks) : null,
1980+
workflowStateEdges: debugState.workflowState?.edges
1981+
})
1982+
1983+
// Try to trigger a re-render by updating the debug canvas again
1984+
if (debugState.isActive && debugState.workflowState) {
1985+
console.log('[Debug] Re-activating to force re-render')
1986+
useDebugCanvasStore.getState().setWorkflowState(debugState.workflowState)
1987+
}
1988+
}, 100)
1989+
} else {
1990+
console.warn('[Debug] Invalid or empty workflow state received')
18101991
}
18111992
} catch (err) {
1812-
// Silently ignore load errors
1993+
console.error('[Debug] Error loading frozen canvas:', err)
18131994
}
18141995
}
18151996
load()
@@ -2124,13 +2305,7 @@ export function DebugPanel() {
21242305
<Select value={selectedExecutionKey} onValueChange={(v) => setSelectedExecutionKey(v)}>
21252306
<SelectTrigger className='h-8 w-[220px] text-[11px] leading-4'>
21262307
<SelectValue
2127-
placeholder={
2128-
isLoadingExecutions
2129-
? 'Loading executions...'
2130-
: executions.length > 0
2131-
? 'Select execution'
2132-
: 'No executions'
2133-
}
2308+
placeholder={isLoadingExecutions ? 'Loading executions...' : 'Select execution'}
21342309
/>
21352310
</SelectTrigger>
21362311
<SelectContent>

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-current-workflow.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ export function useCurrentWorkflow(): CurrentWorkflow {
5757
// Prefer debug canvas if active
5858
const hasDebugCanvas = !!debugCanvas.isActive && !!debugCanvas.workflowState
5959
if (hasDebugCanvas) {
60+
console.log('[useCurrentWorkflow] Using debug canvas state', {
61+
isActive: debugCanvas.isActive,
62+
hasWorkflowState: !!debugCanvas.workflowState,
63+
blockCount: debugCanvas.workflowState ? Object.keys(debugCanvas.workflowState.blocks || {}).length : 0,
64+
edgeCount: debugCanvas.workflowState ? (debugCanvas.workflowState.edges || []).length : 0
65+
})
6066
const activeWorkflow = debugCanvas.workflowState as WorkflowState
6167
return {
6268
blocks: activeWorkflow.blocks,
@@ -85,6 +91,13 @@ export function useCurrentWorkflow(): CurrentWorkflow {
8591
const hasDiffBlocks = !!diffWorkflow && Object.keys((diffWorkflow as any).blocks || {}).length > 0
8692
const shouldUseDiff = isShowingDiff && isDiffReady && hasDiffBlocks
8793
const activeWorkflow = shouldUseDiff ? diffWorkflow : normalWorkflow
94+
95+
console.log('[useCurrentWorkflow] Not using debug canvas', {
96+
debugCanvasIsActive: debugCanvas.isActive,
97+
debugCanvasHasState: !!debugCanvas.workflowState,
98+
usingDiff: shouldUseDiff,
99+
normalBlockCount: Object.keys(normalWorkflow.blocks || {}).length
100+
})
88101

89102
return {
90103
// Current workflow state

0 commit comments

Comments
 (0)