improvement(tag-dropdown): removed custom styling on tag dropdown popover, fixed execution ordering in terminal and loops entries (#3126)

* improvement(tag-dropdown): removeed custom styling on tag dropdown popover, fixed execution ordering in terminal and loops entries

* ack pr comments

* handle old records
This commit is contained in:
Waleed
2026-02-03 18:32:40 -08:00
committed by GitHub
parent c51f266ad7
commit 4ba22527b6
15 changed files with 167 additions and 68 deletions

View File

@@ -567,6 +567,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
blockId: string,
blockName: string,
blockType: string,
executionOrder: number,
iterationContext?: IterationContext
) => {
logger.info(`[${requestId}] 🔷 onBlockStart called:`, { blockId, blockName, blockType })
@@ -579,6 +580,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
blockId,
blockName,
blockType,
executionOrder,
...(iterationContext && {
iterationCurrent: iterationContext.iterationCurrent,
iterationTotal: iterationContext.iterationTotal,
@@ -617,6 +619,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
error: callbackData.output.error,
durationMs: callbackData.executionTime || 0,
startedAt: callbackData.startedAt,
executionOrder: callbackData.executionOrder,
endedAt: callbackData.endedAt,
...(iterationContext && {
iterationCurrent: iterationContext.iterationCurrent,
@@ -644,6 +647,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
output: callbackData.output,
durationMs: callbackData.executionTime || 0,
startedAt: callbackData.startedAt,
executionOrder: callbackData.executionOrder,
endedAt: callbackData.endedAt,
...(iterationContext && {
iterationCurrent: iterationContext.iterationCurrent,

View File

@@ -908,8 +908,10 @@ const PopoverContextCapture: React.FC<{
* When in nested folders, goes back one level at a time.
* At the root folder level, closes the folder.
*/
const TagDropdownBackButton: React.FC = () => {
const { isInFolder, closeFolder, colorScheme, size } = usePopoverContext()
const TagDropdownBackButton: React.FC<{ setSelectedIndex: (index: number) => void }> = ({
setSelectedIndex,
}) => {
const { isInFolder, closeFolder, size, isKeyboardNav, setKeyboardNav } = usePopoverContext()
const nestedNav = useNestedNavigation()
if (!isInFolder) return null
@@ -922,28 +924,31 @@ const TagDropdownBackButton: React.FC = () => {
closeFolder()
}
const handleMouseEnter = () => {
if (isKeyboardNav) return
setKeyboardNav(false)
setSelectedIndex(-1)
}
return (
<div
className={cn(
'flex min-w-0 cursor-pointer items-center gap-[8px] rounded-[6px] px-[6px] font-base',
size === 'sm' ? 'h-[22px] text-[11px]' : 'h-[26px] text-[13px]',
colorScheme === 'inverted'
? 'text-white hover:bg-[#363636] hover:text-white dark:text-[var(--text-primary)] dark:hover:bg-[var(--surface-5)]'
: 'text-[var(--text-primary)] hover:bg-[var(--border-1)]'
)}
role='button'
onClick={handleBackClick}
<PopoverItem
onMouseDown={(e) => {
e.preventDefault()
e.stopPropagation()
handleBackClick(e)
}}
onMouseEnter={handleMouseEnter}
>
<svg
className={size === 'sm' ? 'h-3 w-3' : 'h-3.5 w-3.5'}
className={cn('shrink-0', size === 'sm' ? 'h-3 w-3' : 'h-3.5 w-3.5')}
fill='none'
viewBox='0 0 24 24'
stroke='currentColor'
>
<path strokeLinecap='round' strokeLinejoin='round' strokeWidth={2} d='M15 19l-7-7 7-7' />
</svg>
<span>Back</span>
</div>
<span className='shrink-0'>Back</span>
</PopoverItem>
)
}
@@ -1961,8 +1966,8 @@ export const TagDropdown: React.FC<TagDropdownProps> = ({
onOpenAutoFocus={(e) => e.preventDefault()}
onCloseAutoFocus={(e) => e.preventDefault()}
>
<TagDropdownBackButton />
<PopoverScrollArea ref={scrollAreaRef}>
<TagDropdownBackButton setSelectedIndex={setSelectedIndex} />
{flatTagList.length === 0 ? (
<div className='px-[6px] py-[8px] text-[12px] text-[var(--white)]/60'>
No matching tags found

View File

@@ -105,11 +105,9 @@ export function useTerminalFilters() {
})
}
// Apply sorting by timestamp
// Sort by executionOrder (monotonically increasing integer from server)
result = [...result].sort((a, b) => {
const timeA = new Date(a.timestamp).getTime()
const timeB = new Date(b.timestamp).getTime()
const comparison = timeA - timeB
const comparison = a.executionOrder - b.executionOrder
return sortConfig.direction === 'asc' ? comparison : -comparison
})

View File

@@ -184,13 +184,9 @@ function buildEntryTree(entries: ConsoleEntry[]): EntryNode[] {
group.blocks.push(entry)
}
// Sort blocks within each iteration by start time ascending (oldest first, top-down)
// Sort blocks within each iteration by executionOrder ascending (oldest first, top-down)
for (const group of iterationGroupsMap.values()) {
group.blocks.sort((a, b) => {
const aStart = new Date(a.startedAt || a.timestamp).getTime()
const bStart = new Date(b.startedAt || b.timestamp).getTime()
return aStart - bStart
})
group.blocks.sort((a, b) => a.executionOrder - b.executionOrder)
}
// Group iterations by iterationType to create subflow parents
@@ -225,6 +221,8 @@ function buildEntryTree(entries: ConsoleEntry[]): EntryNode[] {
const totalDuration = allBlocks.reduce((sum, b) => sum + (b.durationMs || 0), 0)
// Create synthetic subflow parent entry
// Use the minimum executionOrder from all child blocks for proper ordering
const subflowExecutionOrder = Math.min(...allBlocks.map((b) => b.executionOrder))
const syntheticSubflow: ConsoleEntry = {
id: `subflow-${iterationType}-${firstIteration.blocks[0]?.executionId || 'unknown'}`,
timestamp: new Date(subflowStartMs).toISOString(),
@@ -234,6 +232,7 @@ function buildEntryTree(entries: ConsoleEntry[]): EntryNode[] {
blockType: iterationType,
executionId: firstIteration.blocks[0]?.executionId,
startedAt: new Date(subflowStartMs).toISOString(),
executionOrder: subflowExecutionOrder,
endedAt: new Date(subflowEndMs).toISOString(),
durationMs: totalDuration,
success: !allBlocks.some((b) => b.error),
@@ -251,6 +250,8 @@ function buildEntryTree(entries: ConsoleEntry[]): EntryNode[] {
)
const iterDuration = iterBlocks.reduce((sum, b) => sum + (b.durationMs || 0), 0)
// Use the minimum executionOrder from blocks in this iteration
const iterExecutionOrder = Math.min(...iterBlocks.map((b) => b.executionOrder))
const syntheticIteration: ConsoleEntry = {
id: `iteration-${iterationType}-${iterGroup.iterationCurrent}-${iterBlocks[0]?.executionId || 'unknown'}`,
timestamp: new Date(iterStartMs).toISOString(),
@@ -260,6 +261,7 @@ function buildEntryTree(entries: ConsoleEntry[]): EntryNode[] {
blockType: iterationType,
executionId: iterBlocks[0]?.executionId,
startedAt: new Date(iterStartMs).toISOString(),
executionOrder: iterExecutionOrder,
endedAt: new Date(iterEndMs).toISOString(),
durationMs: iterDuration,
success: !iterBlocks.some((b) => b.error),
@@ -300,14 +302,9 @@ function buildEntryTree(entries: ConsoleEntry[]): EntryNode[] {
nodeType: 'block' as const,
}))
// Combine all nodes and sort by start time ascending (oldest first, top-down)
// Combine all nodes and sort by executionOrder ascending (oldest first, top-down)
const allNodes = [...subflowNodes, ...regularNodes]
allNodes.sort((a, b) => {
const aStart = new Date(a.entry.startedAt || a.entry.timestamp).getTime()
const bStart = new Date(b.entry.startedAt || b.entry.timestamp).getTime()
return aStart - bStart
})
allNodes.sort((a, b) => a.entry.executionOrder - b.entry.executionOrder)
return allNodes
}

View File

@@ -926,6 +926,7 @@ export function useWorkflowExecution() {
})
// Add entry to terminal immediately with isRunning=true
// Use server-provided executionOrder to ensure correct sort order
const startedAt = new Date().toISOString()
addConsole({
input: {},
@@ -933,6 +934,7 @@ export function useWorkflowExecution() {
success: undefined,
durationMs: undefined,
startedAt,
executionOrder: data.executionOrder,
endedAt: undefined,
workflowId: activeWorkflowId,
blockId: data.blockId,
@@ -948,8 +950,6 @@ export function useWorkflowExecution() {
},
onBlockCompleted: (data) => {
logger.info('onBlockCompleted received:', { data })
activeBlocksSet.delete(data.blockId)
setActiveBlocks(new Set(activeBlocksSet))
setBlockRunStatus(data.blockId, 'success')
@@ -976,6 +976,7 @@ export function useWorkflowExecution() {
success: true,
durationMs: data.durationMs,
startedAt,
executionOrder: data.executionOrder,
endedAt,
})
@@ -987,6 +988,7 @@ export function useWorkflowExecution() {
replaceOutput: data.output,
success: true,
durationMs: data.durationMs,
startedAt,
endedAt,
isRunning: false,
// Pass through iteration context for subflow grouping
@@ -1027,6 +1029,7 @@ export function useWorkflowExecution() {
error: data.error,
durationMs: data.durationMs,
startedAt,
executionOrder: data.executionOrder,
endedAt,
})
@@ -1039,6 +1042,7 @@ export function useWorkflowExecution() {
success: false,
error: data.error,
durationMs: data.durationMs,
startedAt,
endedAt,
isRunning: false,
// Pass through iteration context for subflow grouping
@@ -1163,6 +1167,7 @@ export function useWorkflowExecution() {
if (existingLogs.length === 0) {
// No blocks executed yet - this is a pre-execution error
// Use 0 for executionOrder so validation errors appear first
addConsole({
input: {},
output: {},
@@ -1170,6 +1175,7 @@ export function useWorkflowExecution() {
error: data.error,
durationMs: data.duration || 0,
startedAt: new Date(Date.now() - (data.duration || 0)).toISOString(),
executionOrder: 0,
endedAt: new Date().toISOString(),
workflowId: activeWorkflowId,
blockId: 'validation',
@@ -1237,6 +1243,7 @@ export function useWorkflowExecution() {
blockType = error.blockType || blockType
}
// Use MAX_SAFE_INTEGER so execution errors appear at the end of the log
useTerminalConsoleStore.getState().addConsole({
input: {},
output: {},
@@ -1244,6 +1251,7 @@ export function useWorkflowExecution() {
error: normalizedMessage,
durationMs: 0,
startedAt: new Date().toISOString(),
executionOrder: Number.MAX_SAFE_INTEGER,
endedAt: new Date().toISOString(),
workflowId: activeWorkflowId || '',
blockId,
@@ -1615,6 +1623,7 @@ export function useWorkflowExecution() {
success: true,
durationMs: data.durationMs,
startedAt,
executionOrder: data.executionOrder,
endedAt,
})
@@ -1624,6 +1633,7 @@ export function useWorkflowExecution() {
success: true,
durationMs: data.durationMs,
startedAt,
executionOrder: data.executionOrder,
endedAt,
workflowId,
blockId: data.blockId,
@@ -1653,6 +1663,7 @@ export function useWorkflowExecution() {
output: {},
success: false,
error: data.error,
executionOrder: data.executionOrder,
durationMs: data.durationMs,
startedAt,
endedAt,
@@ -1665,6 +1676,7 @@ export function useWorkflowExecution() {
error: data.error,
durationMs: data.durationMs,
startedAt,
executionOrder: data.executionOrder,
endedAt,
workflowId,
blockId: data.blockId,

View File

@@ -111,6 +111,7 @@ export async function executeWorkflowWithFullLogging(
success: true,
durationMs: event.data.durationMs,
startedAt: new Date(Date.now() - event.data.durationMs).toISOString(),
executionOrder: event.data.executionOrder,
endedAt: new Date().toISOString(),
workflowId: activeWorkflowId,
blockId: event.data.blockId,
@@ -140,6 +141,7 @@ export async function executeWorkflowWithFullLogging(
error: event.data.error,
durationMs: event.data.durationMs,
startedAt: new Date(Date.now() - event.data.durationMs).toISOString(),
executionOrder: event.data.executionOrder,
endedAt: new Date().toISOString(),
workflowId: activeWorkflowId,
blockId: event.data.blockId,

View File

@@ -21,12 +21,13 @@ import {
generatePauseContextId,
mapNodeMetadataToPauseScopes,
} from '@/executor/human-in-the-loop/utils.ts'
import type {
BlockHandler,
BlockLog,
BlockState,
ExecutionContext,
NormalizedBlockOutput,
import {
type BlockHandler,
type BlockLog,
type BlockState,
type ExecutionContext,
getNextExecutionOrder,
type NormalizedBlockOutput,
} from '@/executor/types'
import { streamingResponseFormatProcessor } from '@/executor/utils'
import { buildBlockExecutionError, normalizeError } from '@/executor/utils/errors'
@@ -68,7 +69,7 @@ export class BlockExecutor {
if (!isSentinel) {
blockLog = this.createBlockLog(ctx, node.id, block, node)
ctx.blockLogs.push(blockLog)
this.callOnBlockStart(ctx, node, block)
this.callOnBlockStart(ctx, node, block, blockLog.executionOrder)
}
const startTime = performance.now()
@@ -159,7 +160,7 @@ export class BlockExecutor {
this.state.setBlockOutput(node.id, normalizedOutput, duration)
if (!isSentinel) {
if (!isSentinel && blockLog) {
const displayOutput = filterOutputForLog(block.metadata?.id || '', normalizedOutput, {
block,
})
@@ -170,8 +171,9 @@ export class BlockExecutor {
this.sanitizeInputsForLog(resolvedInputs),
displayOutput,
duration,
blockLog!.startedAt,
blockLog!.endedAt
blockLog.startedAt,
blockLog.executionOrder,
blockLog.endedAt
)
}
@@ -268,7 +270,7 @@ export class BlockExecutor {
}
)
if (!isSentinel) {
if (!isSentinel && blockLog) {
const displayOutput = filterOutputForLog(block.metadata?.id || '', errorOutput, { block })
this.callOnBlockComplete(
ctx,
@@ -277,8 +279,9 @@ export class BlockExecutor {
this.sanitizeInputsForLog(input),
displayOutput,
duration,
blockLog!.startedAt,
blockLog!.endedAt
blockLog.startedAt,
blockLog.executionOrder,
blockLog.endedAt
)
}
@@ -346,6 +349,7 @@ export class BlockExecutor {
blockName,
blockType: block.metadata?.id ?? DEFAULTS.BLOCK_TYPE,
startedAt: new Date().toISOString(),
executionOrder: getNextExecutionOrder(ctx),
endedAt: '',
durationMs: 0,
success: false,
@@ -409,7 +413,12 @@ export class BlockExecutor {
return result
}
private callOnBlockStart(ctx: ExecutionContext, node: DAGNode, block: SerializedBlock): void {
private callOnBlockStart(
ctx: ExecutionContext,
node: DAGNode,
block: SerializedBlock,
executionOrder: number
): void {
const blockId = node.id
const blockName = block.metadata?.name ?? blockId
const blockType = block.metadata?.id ?? DEFAULTS.BLOCK_TYPE
@@ -417,7 +426,13 @@ export class BlockExecutor {
const iterationContext = this.getIterationContext(ctx, node)
if (this.contextExtensions.onBlockStart) {
this.contextExtensions.onBlockStart(blockId, blockName, blockType, iterationContext)
this.contextExtensions.onBlockStart(
blockId,
blockName,
blockType,
executionOrder,
iterationContext
)
}
}
@@ -429,6 +444,7 @@ export class BlockExecutor {
output: NormalizedBlockOutput,
duration: number,
startedAt: string,
executionOrder: number,
endedAt: string
): void {
const blockId = node.id
@@ -447,6 +463,7 @@ export class BlockExecutor {
output,
executionTime: duration,
startedAt,
executionOrder,
endedAt,
},
iterationContext

View File

@@ -55,7 +55,13 @@ export interface IterationContext {
export interface ExecutionCallbacks {
onStream?: (streamingExec: any) => Promise<void>
onBlockStart?: (blockId: string, blockName: string, blockType: string) => Promise<void>
onBlockStart?: (
blockId: string,
blockName: string,
blockType: string,
executionOrder: number,
iterationContext?: IterationContext
) => Promise<void>
onBlockComplete?: (
blockId: string,
blockName: string,
@@ -97,6 +103,7 @@ export interface ContextExtensions {
blockId: string,
blockName: string,
blockType: string,
executionOrder: number,
iterationContext?: IterationContext
) => Promise<void>
onBlockComplete?: (
@@ -108,6 +115,7 @@ export interface ContextExtensions {
output: NormalizedBlockOutput
executionTime: number
startedAt: string
executionOrder: number
endedAt: string
},
iterationContext?: IterationContext

View File

@@ -7,7 +7,11 @@ import type { DAG } from '@/executor/dag/builder'
import type { EdgeManager } from '@/executor/execution/edge-manager'
import type { LoopScope } from '@/executor/execution/state'
import type { BlockStateController, ContextExtensions } from '@/executor/execution/types'
import type { ExecutionContext, NormalizedBlockOutput } from '@/executor/types'
import {
type ExecutionContext,
getNextExecutionOrder,
type NormalizedBlockOutput,
} from '@/executor/types'
import type { LoopConfigWithNodes } from '@/executor/types/loop'
import { replaceValidReferences } from '@/executor/utils/reference-validation'
import {
@@ -286,6 +290,7 @@ export class LoopOrchestrator {
output,
executionTime: DEFAULTS.EXECUTION_TIME,
startedAt: now,
executionOrder: getNextExecutionOrder(ctx),
endedAt: now,
})
}

View File

@@ -3,7 +3,11 @@ import { DEFAULTS } from '@/executor/constants'
import type { DAG } from '@/executor/dag/builder'
import type { ParallelScope } from '@/executor/execution/state'
import type { BlockStateWriter, ContextExtensions } from '@/executor/execution/types'
import type { ExecutionContext, NormalizedBlockOutput } from '@/executor/types'
import {
type ExecutionContext,
getNextExecutionOrder,
type NormalizedBlockOutput,
} from '@/executor/types'
import type { ParallelConfigWithNodes } from '@/executor/types/parallel'
import { ParallelExpander } from '@/executor/utils/parallel-expansion'
import {
@@ -270,6 +274,7 @@ export class ParallelOrchestrator {
output,
executionTime: 0,
startedAt: now,
executionOrder: getNextExecutionOrder(ctx),
endedAt: now,
})
}

View File

@@ -114,6 +114,11 @@ export interface BlockLog {
loopId?: string
parallelId?: string
iterationIndex?: number
/**
* Monotonically increasing integer (1, 2, 3, ...) for accurate block ordering.
* Generated via getNextExecutionOrder() to ensure deterministic sorting.
*/
executionOrder: number
/**
* Child workflow trace spans for nested workflow execution.
* Stored separately from output to keep output clean for display
@@ -227,7 +232,12 @@ export interface ExecutionContext {
edges?: Array<{ source: string; target: string }>
onStream?: (streamingExecution: StreamingExecution) => Promise<void>
onBlockStart?: (blockId: string, blockName: string, blockType: string) => Promise<void>
onBlockStart?: (
blockId: string,
blockName: string,
blockType: string,
executionOrder: number
) => Promise<void>
onBlockComplete?: (
blockId: string,
blockName: string,
@@ -268,6 +278,23 @@ export interface ExecutionContext {
* Stop execution after this block completes. Used for "run until block" feature.
*/
stopAfterBlockId?: string
/**
* Counter for generating monotonically increasing execution order values.
* Starts at 0 and increments for each block. Use getNextExecutionOrder() to access.
*/
executionOrderCounter?: { value: number }
}
/**
* Gets the next execution order value for a block.
* Returns a simple incrementing integer (1, 2, 3, ...) for clear ordering.
*/
export function getNextExecutionOrder(ctx: ExecutionContext): number {
if (!ctx.executionOrderCounter) {
ctx.executionOrderCounter = { value: 0 }
}
return ++ctx.executionOrderCounter.value
}
export interface ExecutionResult {

View File

@@ -1,7 +1,7 @@
import { createLogger } from '@sim/logger'
import { LOOP, PARALLEL, PARSING, REFERENCE } from '@/executor/constants'
import type { ContextExtensions } from '@/executor/execution/types'
import type { BlockLog, ExecutionContext } from '@/executor/types'
import { type BlockLog, type ExecutionContext, getNextExecutionOrder } from '@/executor/types'
import type { VariableResolver } from '@/executor/variables/resolver'
const logger = createLogger('SubflowUtils')
@@ -208,6 +208,7 @@ export function addSubflowErrorLog(
contextExtensions: ContextExtensions | null
): void {
const now = new Date().toISOString()
const execOrder = getNextExecutionOrder(ctx)
const block = ctx.workflow?.blocks?.find((b) => b.id === blockId)
const blockName = block?.metadata?.name || (blockType === 'loop' ? 'Loop' : 'Parallel')
@@ -217,6 +218,7 @@ export function addSubflowErrorLog(
blockName,
blockType,
startedAt: now,
executionOrder: execOrder,
endedAt: now,
durationMs: 0,
success: false,
@@ -233,6 +235,7 @@ export function addSubflowErrorLog(
output: { error: errorMessage },
executionTime: 0,
startedAt: now,
executionOrder: execOrder,
endedAt: now,
})
}

View File

@@ -1,7 +1,3 @@
/**
* SSE Event types for workflow execution
*/
import type { SubflowType } from '@/stores/workflows/workflow/types'
export type ExecutionEventType =
@@ -83,7 +79,7 @@ export interface BlockStartedEvent extends BaseExecutionEvent {
blockId: string
blockName: string
blockType: string
// Iteration context for loops and parallels
executionOrder: number
iterationCurrent?: number
iterationTotal?: number
iterationType?: SubflowType
@@ -104,8 +100,8 @@ export interface BlockCompletedEvent extends BaseExecutionEvent {
output: any
durationMs: number
startedAt: string
executionOrder: number
endedAt: string
// Iteration context for loops and parallels
iterationCurrent?: number
iterationTotal?: number
iterationType?: SubflowType
@@ -126,8 +122,8 @@ export interface BlockErrorEvent extends BaseExecutionEvent {
error: string
durationMs: number
startedAt: string
executionOrder: number
endedAt: string
// Iteration context for loops and parallels
iterationCurrent?: number
iterationTotal?: number
iterationType?: SubflowType
@@ -228,6 +224,7 @@ export function createSSECallbacks(options: SSECallbackOptions) {
blockId: string,
blockName: string,
blockType: string,
executionOrder: number,
iterationContext?: { iterationCurrent: number; iterationTotal: number; iterationType: string }
) => {
sendEvent({
@@ -239,6 +236,7 @@ export function createSSECallbacks(options: SSECallbackOptions) {
blockId,
blockName,
blockType,
executionOrder,
...(iterationContext && {
iterationCurrent: iterationContext.iterationCurrent,
iterationTotal: iterationContext.iterationTotal,
@@ -257,6 +255,7 @@ export function createSSECallbacks(options: SSECallbackOptions) {
output: any
executionTime: number
startedAt: string
executionOrder: number
endedAt: string
},
iterationContext?: { iterationCurrent: number; iterationTotal: number; iterationType: string }
@@ -284,6 +283,7 @@ export function createSSECallbacks(options: SSECallbackOptions) {
error: callbackData.output.error,
durationMs: callbackData.executionTime || 0,
startedAt: callbackData.startedAt,
executionOrder: callbackData.executionOrder,
endedAt: callbackData.endedAt,
...iterationData,
},
@@ -302,6 +302,7 @@ export function createSSECallbacks(options: SSECallbackOptions) {
output: callbackData.output,
durationMs: callbackData.executionTime || 0,
startedAt: callbackData.startedAt,
executionOrder: callbackData.executionOrder,
endedAt: callbackData.endedAt,
...iterationData,
},

View File

@@ -287,6 +287,14 @@ export const useTerminalConsoleStore = create<ConsoleStore>()(
return entry
}
if (
typeof update === 'object' &&
update.iterationCurrent !== undefined &&
entry.iterationCurrent !== update.iterationCurrent
) {
return entry
}
if (typeof update === 'string') {
const newOutput = updateBlockOutput(entry.output, update)
return { ...entry, output: newOutput }
@@ -324,6 +332,10 @@ export const useTerminalConsoleStore = create<ConsoleStore>()(
updatedEntry.success = update.success
}
if (update.startedAt !== undefined) {
updatedEntry.startedAt = update.startedAt
}
if (update.endedAt !== undefined) {
updatedEntry.endedAt = update.endedAt
}
@@ -397,9 +409,15 @@ export const useTerminalConsoleStore = create<ConsoleStore>()(
},
merge: (persistedState, currentState) => {
const persisted = persistedState as Partial<ConsoleStore> | undefined
const entries = (persisted?.entries ?? currentState.entries).map((entry, index) => {
if (entry.executionOrder === undefined) {
return { ...entry, executionOrder: index + 1 }
}
return entry
})
return {
...currentState,
entries: persisted?.entries ?? currentState.entries,
entries,
isOpen: persisted?.isOpen ?? currentState.isOpen,
}
},

View File

@@ -10,6 +10,7 @@ export interface ConsoleEntry {
blockType: string
executionId?: string
startedAt?: string
executionOrder: number
endedAt?: string
durationMs?: number
success?: boolean
@@ -20,9 +21,7 @@ export interface ConsoleEntry {
iterationCurrent?: number
iterationTotal?: number
iterationType?: SubflowType
/** Whether this block is currently running */
isRunning?: boolean
/** Whether this block execution was canceled */
isCanceled?: boolean
}
@@ -33,14 +32,12 @@ export interface ConsoleUpdate {
error?: string | Error | null
warning?: string
success?: boolean
startedAt?: string
endedAt?: string
durationMs?: number
input?: any
/** Whether this block is currently running */
isRunning?: boolean
/** Whether this block execution was canceled */
isCanceled?: boolean
/** Iteration context for subflow blocks */
iterationCurrent?: number
iterationTotal?: number
iterationType?: SubflowType