Compare commits

..

6 Commits

Author SHA1 Message Date
Vikhyath Mondreti
37f4f7d09d deps 2026-02-06 11:47:42 -08:00
Vikhyath Mondreti
fc7194b170 fix fallback case 2026-02-06 11:25:35 -08:00
Vikhyath Mondreti
e13bbc1cdd add try catch 2026-02-06 11:12:21 -08:00
Vikhyath Mondreti
12f36871d9 few more things 2026-02-06 11:10:52 -08:00
Vikhyath Mondreti
c89ae23edb improvements to resolve nested subblock values 2026-02-06 11:04:50 -08:00
Vikhyath Mondreti
8a10e9fc93 improvement(preview): nested workflow snapshots/preview when not executed 2026-02-06 10:14:41 -08:00
17 changed files with 171 additions and 43 deletions

View File

@@ -35,6 +35,7 @@ interface CredentialSelectorProps {
disabled?: boolean
isPreview?: boolean
previewValue?: any | null
previewContextValues?: Record<string, unknown>
}
export function CredentialSelector({
@@ -43,6 +44,7 @@ export function CredentialSelector({
disabled = false,
isPreview = false,
previewValue,
previewContextValues,
}: CredentialSelectorProps) {
const [showOAuthModal, setShowOAuthModal] = useState(false)
const [editingValue, setEditingValue] = useState('')
@@ -67,7 +69,11 @@ export function CredentialSelector({
canUseCredentialSets
)
const { depsSatisfied, dependsOn } = useDependsOnGate(blockId, subBlock, { disabled, isPreview })
const { depsSatisfied, dependsOn } = useDependsOnGate(blockId, subBlock, {
disabled,
isPreview,
previewContextValues,
})
const hasDependencies = dependsOn.length > 0
const effectiveDisabled = disabled || (hasDependencies && !depsSatisfied)

View File

@@ -5,6 +5,7 @@ import { Tooltip } from '@/components/emcn'
import { SelectorCombobox } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/selector-combobox/selector-combobox'
import { useDependsOnGate } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-depends-on-gate'
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value'
import { resolvePreviewContextValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/utils'
import type { SubBlockConfig } from '@/blocks/types'
import type { SelectorContext } from '@/hooks/selectors/types'
@@ -33,7 +34,9 @@ export function DocumentSelector({
previewContextValues,
})
const [knowledgeBaseIdFromStore] = useSubBlockValue(blockId, 'knowledgeBaseId')
const knowledgeBaseIdValue = previewContextValues?.knowledgeBaseId ?? knowledgeBaseIdFromStore
const knowledgeBaseIdValue = previewContextValues
? resolvePreviewContextValue(previewContextValues.knowledgeBaseId)
: knowledgeBaseIdFromStore
const normalizedKnowledgeBaseId =
typeof knowledgeBaseIdValue === 'string' && knowledgeBaseIdValue.trim().length > 0
? knowledgeBaseIdValue

View File

@@ -17,6 +17,7 @@ import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/
import { TagDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown'
import { useSubBlockInput } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-input'
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value'
import { resolvePreviewContextValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/utils'
import { useAccessibleReferencePrefixes } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-accessible-reference-prefixes'
import type { SubBlockConfig } from '@/blocks/types'
import { useKnowledgeBaseTagDefinitions } from '@/hooks/kb/use-knowledge-base-tag-definitions'
@@ -77,7 +78,9 @@ export function DocumentTagEntry({
})
const [knowledgeBaseIdFromStore] = useSubBlockValue(blockId, 'knowledgeBaseId')
const knowledgeBaseIdValue = previewContextValues?.knowledgeBaseId ?? knowledgeBaseIdFromStore
const knowledgeBaseIdValue = previewContextValues
? resolvePreviewContextValue(previewContextValues.knowledgeBaseId)
: knowledgeBaseIdFromStore
const knowledgeBaseId =
typeof knowledgeBaseIdValue === 'string' && knowledgeBaseIdValue.trim().length > 0
? knowledgeBaseIdValue

View File

@@ -9,6 +9,7 @@ import { SelectorCombobox } from '@/app/workspace/[workspaceId]/w/[workflowId]/c
import { useDependsOnGate } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-depends-on-gate'
import { useForeignCredential } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-foreign-credential'
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value'
import { resolvePreviewContextValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/utils'
import { getBlock } from '@/blocks/registry'
import type { SubBlockConfig } from '@/blocks/types'
import { isDependency } from '@/blocks/utils'
@@ -62,42 +63,56 @@ export function FileSelectorInput({
const [domainValueFromStore] = useSubBlockValue(blockId, 'domain')
const connectedCredential = previewContextValues?.credential ?? blockValues.credential
const domainValue = previewContextValues?.domain ?? domainValueFromStore
const connectedCredential = previewContextValues
? resolvePreviewContextValue(previewContextValues.credential)
: blockValues.credential
const domainValue = previewContextValues
? resolvePreviewContextValue(previewContextValues.domain)
: domainValueFromStore
const teamIdValue = useMemo(
() =>
previewContextValues?.teamId ??
resolveDependencyValue('teamId', blockValues, canonicalIndex, canonicalModeOverrides),
[previewContextValues?.teamId, blockValues, canonicalIndex, canonicalModeOverrides]
previewContextValues
? resolvePreviewContextValue(previewContextValues.teamId)
: resolveDependencyValue('teamId', blockValues, canonicalIndex, canonicalModeOverrides),
[previewContextValues, blockValues, canonicalIndex, canonicalModeOverrides]
)
const siteIdValue = useMemo(
() =>
previewContextValues?.siteId ??
resolveDependencyValue('siteId', blockValues, canonicalIndex, canonicalModeOverrides),
[previewContextValues?.siteId, blockValues, canonicalIndex, canonicalModeOverrides]
previewContextValues
? resolvePreviewContextValue(previewContextValues.siteId)
: resolveDependencyValue('siteId', blockValues, canonicalIndex, canonicalModeOverrides),
[previewContextValues, blockValues, canonicalIndex, canonicalModeOverrides]
)
const collectionIdValue = useMemo(
() =>
previewContextValues?.collectionId ??
resolveDependencyValue('collectionId', blockValues, canonicalIndex, canonicalModeOverrides),
[previewContextValues?.collectionId, blockValues, canonicalIndex, canonicalModeOverrides]
previewContextValues
? resolvePreviewContextValue(previewContextValues.collectionId)
: resolveDependencyValue(
'collectionId',
blockValues,
canonicalIndex,
canonicalModeOverrides
),
[previewContextValues, blockValues, canonicalIndex, canonicalModeOverrides]
)
const projectIdValue = useMemo(
() =>
previewContextValues?.projectId ??
resolveDependencyValue('projectId', blockValues, canonicalIndex, canonicalModeOverrides),
[previewContextValues?.projectId, blockValues, canonicalIndex, canonicalModeOverrides]
previewContextValues
? resolvePreviewContextValue(previewContextValues.projectId)
: resolveDependencyValue('projectId', blockValues, canonicalIndex, canonicalModeOverrides),
[previewContextValues, blockValues, canonicalIndex, canonicalModeOverrides]
)
const planIdValue = useMemo(
() =>
previewContextValues?.planId ??
resolveDependencyValue('planId', blockValues, canonicalIndex, canonicalModeOverrides),
[previewContextValues?.planId, blockValues, canonicalIndex, canonicalModeOverrides]
previewContextValues
? resolvePreviewContextValue(previewContextValues.planId)
: resolveDependencyValue('planId', blockValues, canonicalIndex, canonicalModeOverrides),
[previewContextValues, blockValues, canonicalIndex, canonicalModeOverrides]
)
const normalizedCredentialId =

View File

@@ -6,6 +6,7 @@ import { SelectorCombobox } from '@/app/workspace/[workspaceId]/w/[workflowId]/c
import { useDependsOnGate } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-depends-on-gate'
import { useForeignCredential } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-foreign-credential'
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value'
import { resolvePreviewContextValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/utils'
import type { SubBlockConfig } from '@/blocks/types'
import { resolveSelectorForSubBlock } from '@/hooks/selectors/resolution'
import { useCollaborativeWorkflow } from '@/hooks/use-collaborative-workflow'
@@ -17,6 +18,7 @@ interface FolderSelectorInputProps {
disabled?: boolean
isPreview?: boolean
previewValue?: any | null
previewContextValues?: Record<string, unknown>
}
export function FolderSelectorInput({
@@ -25,9 +27,13 @@ export function FolderSelectorInput({
disabled = false,
isPreview = false,
previewValue,
previewContextValues,
}: FolderSelectorInputProps) {
const [storeValue] = useSubBlockValue(blockId, subBlock.id)
const [connectedCredential] = useSubBlockValue(blockId, 'credential')
const [credentialFromStore] = useSubBlockValue(blockId, 'credential')
const connectedCredential = previewContextValues
? resolvePreviewContextValue(previewContextValues.credential)
: credentialFromStore
const { collaborativeSetSubblockValue } = useCollaborativeWorkflow()
const { activeWorkflowId } = useWorkflowRegistry()
const [selectedFolderId, setSelectedFolderId] = useState<string>('')
@@ -47,7 +53,11 @@ export function FolderSelectorInput({
)
// Central dependsOn gating
const { finalDisabled } = useDependsOnGate(blockId, subBlock, { disabled, isPreview })
const { finalDisabled } = useDependsOnGate(blockId, subBlock, {
disabled,
isPreview,
previewContextValues,
})
// Get the current value from the store or prop value if in preview mode
useEffect(() => {

View File

@@ -7,6 +7,7 @@ import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/
import { TagDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown'
import { useSubBlockInput } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-input'
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value'
import { resolvePreviewContextValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/utils'
import { useAccessibleReferencePrefixes } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-accessible-reference-prefixes'
import { useWorkflowState } from '@/hooks/queries/workflows'
@@ -37,6 +38,8 @@ interface InputMappingProps {
isPreview?: boolean
previewValue?: Record<string, unknown>
disabled?: boolean
/** Sub-block values from the preview context for resolving sibling sub-block values */
previewContextValues?: Record<string, unknown>
}
/**
@@ -50,9 +53,13 @@ export function InputMapping({
isPreview = false,
previewValue,
disabled = false,
previewContextValues,
}: InputMappingProps) {
const [mapping, setMapping] = useSubBlockValue(blockId, subBlockId)
const [selectedWorkflowId] = useSubBlockValue(blockId, 'workflowId')
const [storeWorkflowId] = useSubBlockValue(blockId, 'workflowId')
const selectedWorkflowId = previewContextValues
? resolvePreviewContextValue(previewContextValues.workflowId)
: storeWorkflowId
const inputController = useSubBlockInput({
blockId,

View File

@@ -17,6 +17,7 @@ import { type FilterFieldType, getOperatorsForFieldType } from '@/lib/knowledge/
import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/formatted-text'
import { TagDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown'
import { useSubBlockInput } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-input'
import { resolvePreviewContextValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/utils'
import { useAccessibleReferencePrefixes } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-accessible-reference-prefixes'
import type { SubBlockConfig } from '@/blocks/types'
import { useKnowledgeBaseTagDefinitions } from '@/hooks/kb/use-knowledge-base-tag-definitions'
@@ -69,7 +70,9 @@ export function KnowledgeTagFilters({
const overlayRefs = useRef<Record<string, HTMLDivElement>>({})
const [knowledgeBaseIdFromStore] = useSubBlockValue(blockId, 'knowledgeBaseId')
const knowledgeBaseIdValue = previewContextValues?.knowledgeBaseId ?? knowledgeBaseIdFromStore
const knowledgeBaseIdValue = previewContextValues
? resolvePreviewContextValue(previewContextValues.knowledgeBaseId)
: knowledgeBaseIdFromStore
const knowledgeBaseId =
typeof knowledgeBaseIdValue === 'string' && knowledgeBaseIdValue.trim().length > 0
? knowledgeBaseIdValue

View File

@@ -6,6 +6,7 @@ import { cn } from '@/lib/core/utils/cn'
import { LongInput } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/long-input/long-input'
import { ShortInput } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/short-input/short-input'
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value'
import { resolvePreviewContextValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/utils'
import type { SubBlockConfig } from '@/blocks/types'
import { useMcpTools } from '@/hooks/mcp/use-mcp-tools'
import { formatParameterLabel } from '@/tools/params'
@@ -18,6 +19,7 @@ interface McpDynamicArgsProps {
disabled?: boolean
isPreview?: boolean
previewValue?: any
previewContextValues?: Record<string, unknown>
}
/**
@@ -47,12 +49,19 @@ export function McpDynamicArgs({
disabled = false,
isPreview = false,
previewValue,
previewContextValues,
}: McpDynamicArgsProps) {
const params = useParams()
const workspaceId = params.workspaceId as string
const { mcpTools, isLoading } = useMcpTools(workspaceId)
const [selectedTool] = useSubBlockValue(blockId, 'tool')
const [cachedSchema] = useSubBlockValue(blockId, '_toolSchema')
const [toolFromStore] = useSubBlockValue(blockId, 'tool')
const selectedTool = previewContextValues
? resolvePreviewContextValue(previewContextValues.tool)
: toolFromStore
const [schemaFromStore] = useSubBlockValue(blockId, '_toolSchema')
const cachedSchema = previewContextValues
? resolvePreviewContextValue(previewContextValues._toolSchema)
: schemaFromStore
const [toolArgs, setToolArgs] = useSubBlockValue(blockId, subBlockId)
const selectedToolConfig = mcpTools.find((tool) => tool.id === selectedTool)

View File

@@ -4,6 +4,7 @@ import { useEffect, useMemo, useState } from 'react'
import { useParams } from 'next/navigation'
import { Combobox } from '@/components/emcn/components'
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value'
import { resolvePreviewContextValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/utils'
import type { SubBlockConfig } from '@/blocks/types'
import { useMcpTools } from '@/hooks/mcp/use-mcp-tools'
@@ -13,6 +14,7 @@ interface McpToolSelectorProps {
disabled?: boolean
isPreview?: boolean
previewValue?: string | null
previewContextValues?: Record<string, unknown>
}
export function McpToolSelector({
@@ -21,6 +23,7 @@ export function McpToolSelector({
disabled = false,
isPreview = false,
previewValue,
previewContextValues,
}: McpToolSelectorProps) {
const params = useParams()
const workspaceId = params.workspaceId as string
@@ -31,7 +34,10 @@ export function McpToolSelector({
const [storeValue, setStoreValue] = useSubBlockValue(blockId, subBlock.id)
const [, setSchemaCache] = useSubBlockValue(blockId, '_toolSchema')
const [serverValue] = useSubBlockValue(blockId, 'server')
const [serverFromStore] = useSubBlockValue(blockId, 'server')
const serverValue = previewContextValues
? resolvePreviewContextValue(previewContextValues.server)
: serverFromStore
const label = subBlock.placeholder || 'Select tool'

View File

@@ -9,6 +9,7 @@ import { SelectorCombobox } from '@/app/workspace/[workspaceId]/w/[workflowId]/c
import { useDependsOnGate } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-depends-on-gate'
import { useForeignCredential } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-foreign-credential'
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value'
import { resolvePreviewContextValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/utils'
import { getBlock } from '@/blocks/registry'
import type { SubBlockConfig } from '@/blocks/types'
import { resolveSelectorForSubBlock } from '@/hooks/selectors/resolution'
@@ -55,14 +56,19 @@ export function ProjectSelectorInput({
return (workflowValues as Record<string, Record<string, unknown>>)[blockId] || {}
})
const connectedCredential = previewContextValues?.credential ?? blockValues.credential
const jiraDomain = previewContextValues?.domain ?? jiraDomainFromStore
const connectedCredential = previewContextValues
? resolvePreviewContextValue(previewContextValues.credential)
: blockValues.credential
const jiraDomain = previewContextValues
? resolvePreviewContextValue(previewContextValues.domain)
: jiraDomainFromStore
const linearTeamId = useMemo(
() =>
previewContextValues?.teamId ??
resolveDependencyValue('teamId', blockValues, canonicalIndex, canonicalModeOverrides),
[previewContextValues?.teamId, blockValues, canonicalIndex, canonicalModeOverrides]
previewContextValues
? resolvePreviewContextValue(previewContextValues.teamId)
: resolveDependencyValue('teamId', blockValues, canonicalIndex, canonicalModeOverrides),
[previewContextValues, blockValues, canonicalIndex, canonicalModeOverrides]
)
const serviceId = subBlock.serviceId || ''

View File

@@ -8,6 +8,7 @@ import { buildCanonicalIndex, resolveDependencyValue } from '@/lib/workflows/sub
import { SelectorCombobox } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/selector-combobox/selector-combobox'
import { useDependsOnGate } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-depends-on-gate'
import { useForeignCredential } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-foreign-credential'
import { resolvePreviewContextValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/utils'
import { getBlock } from '@/blocks/registry'
import type { SubBlockConfig } from '@/blocks/types'
import { resolveSelectorForSubBlock, type SelectorResolution } from '@/hooks/selectors/resolution'
@@ -66,9 +67,12 @@ export function SheetSelectorInput({
[blockValues, canonicalIndex, canonicalModeOverrides]
)
const connectedCredential = previewContextValues?.credential ?? connectedCredentialFromStore
const connectedCredential = previewContextValues
? resolvePreviewContextValue(previewContextValues.credential)
: connectedCredentialFromStore
const spreadsheetId = previewContextValues
? (previewContextValues.spreadsheetId ?? previewContextValues.manualSpreadsheetId)
? (resolvePreviewContextValue(previewContextValues.spreadsheetId) ??
resolvePreviewContextValue(previewContextValues.manualSpreadsheetId))
: spreadsheetIdFromStore
const normalizedCredentialId =

View File

@@ -8,6 +8,7 @@ import { SelectorCombobox } from '@/app/workspace/[workspaceId]/w/[workflowId]/c
import { useDependsOnGate } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-depends-on-gate'
import { useForeignCredential } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-foreign-credential'
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value'
import { resolvePreviewContextValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/utils'
import type { SubBlockConfig } from '@/blocks/types'
import type { SelectorContext, SelectorKey } from '@/hooks/selectors/types'
@@ -58,9 +59,15 @@ export function SlackSelectorInput({
const [botToken] = useSubBlockValue(blockId, 'botToken')
const [connectedCredential] = useSubBlockValue(blockId, 'credential')
const effectiveAuthMethod = previewContextValues?.authMethod ?? authMethod
const effectiveBotToken = previewContextValues?.botToken ?? botToken
const effectiveCredential = previewContextValues?.credential ?? connectedCredential
const effectiveAuthMethod = previewContextValues
? resolvePreviewContextValue(previewContextValues.authMethod)
: authMethod
const effectiveBotToken = previewContextValues
? resolvePreviewContextValue(previewContextValues.botToken)
: botToken
const effectiveCredential = previewContextValues
? resolvePreviewContextValue(previewContextValues.credential)
: connectedCredential
const [_selectedValue, setSelectedValue] = useState<string | null>(null)
const serviceId = subBlock.serviceId || ''

View File

@@ -332,6 +332,7 @@ function FolderSelectorSyncWrapper({
dependsOn: uiComponent.dependsOn,
}}
disabled={disabled}
previewContextValues={previewContextValues}
/>
</GenericSyncWrapper>
)

View File

@@ -785,6 +785,7 @@ function SubBlockComponent({
disabled={isDisabled}
isPreview={isPreview}
previewValue={previewValue}
previewContextValues={isPreview ? subBlockValues : undefined}
/>
)
@@ -820,6 +821,7 @@ function SubBlockComponent({
disabled={isDisabled}
isPreview={isPreview}
previewValue={previewValue}
previewContextValues={isPreview ? subBlockValues : undefined}
/>
)
@@ -831,6 +833,7 @@ function SubBlockComponent({
disabled={isDisabled}
isPreview={isPreview}
previewValue={previewValue}
previewContextValues={isPreview ? subBlockValues : undefined}
/>
)
@@ -853,6 +856,7 @@ function SubBlockComponent({
disabled={isDisabled}
isPreview={isPreview}
previewValue={previewValue as any}
previewContextValues={isPreview ? subBlockValues : undefined}
/>
)
@@ -864,6 +868,7 @@ function SubBlockComponent({
disabled={isDisabled}
isPreview={isPreview}
previewValue={previewValue as any}
previewContextValues={isPreview ? subBlockValues : undefined}
/>
)
@@ -875,6 +880,7 @@ function SubBlockComponent({
disabled={isDisabled}
isPreview={isPreview}
previewValue={previewValue as any}
previewContextValues={isPreview ? subBlockValues : undefined}
/>
)
@@ -899,6 +905,7 @@ function SubBlockComponent({
isPreview={isPreview}
previewValue={previewValue as any}
disabled={isDisabled}
previewContextValues={isPreview ? subBlockValues : undefined}
/>
)
@@ -934,6 +941,7 @@ function SubBlockComponent({
disabled={isDisabled}
isPreview={isPreview}
previewValue={previewValue}
previewContextValues={isPreview ? subBlockValues : undefined}
/>
)
@@ -967,6 +975,7 @@ function SubBlockComponent({
disabled={isDisabled}
isPreview={isPreview}
previewValue={previewValue as any}
previewContextValues={isPreview ? subBlockValues : undefined}
/>
)
@@ -978,6 +987,7 @@ function SubBlockComponent({
disabled={isDisabled}
isPreview={isPreview}
previewValue={previewValue}
previewContextValues={isPreview ? subBlockValues : undefined}
/>
)

View File

@@ -0,0 +1,18 @@
/**
* Extracts the raw value from a preview context entry.
*
* @remarks
* In the sub-block preview context, values are wrapped as `{ value: T }` objects
* (the full sub-block state). In the tool-input preview context, values are already
* raw. This function normalizes both cases to return the underlying value.
*
* @param raw - The preview context entry, which may be a raw value or a `{ value: T }` wrapper
* @returns The unwrapped value, or `null` if the input is nullish
*/
export function resolvePreviewContextValue(raw: unknown): unknown {
if (raw === null || raw === undefined) return null
if (typeof raw === 'object' && !Array.isArray(raw) && 'value' in raw) {
return (raw as Record<string, unknown>).value ?? null
}
return raw
}

View File

@@ -784,8 +784,12 @@ function PreviewEditorContent({
? childWorkflowSnapshotState
: childWorkflowState
const resolvedIsLoadingChildWorkflow = isExecutionMode ? false : isLoadingChildWorkflow
const isBlockNotExecuted = isExecutionMode && !executionData
const isMissingChildWorkflow =
Boolean(childWorkflowId) && !resolvedIsLoadingChildWorkflow && !resolvedChildWorkflowState
Boolean(childWorkflowId) &&
!isBlockNotExecuted &&
!resolvedIsLoadingChildWorkflow &&
!resolvedChildWorkflowState
/** Drills down into the child workflow or opens it in a new tab */
const handleExpandChildWorkflow = useCallback(() => {
@@ -1192,7 +1196,7 @@ function PreviewEditorContent({
<div ref={subBlocksRef} className='subblocks-section flex flex-1 flex-col overflow-hidden'>
<div className='flex-1 overflow-y-auto overflow-x-hidden'>
{/* Not Executed Banner - shown when in execution mode but block wasn't executed */}
{isExecutionMode && !executionData && (
{isBlockNotExecuted && (
<div className='flex min-w-0 flex-col gap-[8px] overflow-hidden border-[var(--border)] border-b px-[12px] py-[10px]'>
<div className='flex items-center justify-between'>
<Badge variant='gray-secondary' size='sm' dot>
@@ -1419,9 +1423,11 @@ function PreviewEditorContent({
) : (
<div className='flex h-full items-center justify-center bg-[var(--surface-3)]'>
<span className='text-[13px] text-[var(--text-tertiary)]'>
{isMissingChildWorkflow
? DELETED_WORKFLOW_LABEL
: 'Unable to load preview'}
{isBlockNotExecuted
? 'Not Executed'
: isMissingChildWorkflow
? DELETED_WORKFLOW_LABEL
: 'Unable to load preview'}
</span>
</div>
)}

View File

@@ -33,11 +33,25 @@ export class SnapshotService implements ISnapshotService {
const existingSnapshot = await this.getSnapshotByHash(workflowId, stateHash)
if (existingSnapshot) {
let refreshedState: WorkflowState = existingSnapshot.stateData
try {
await db
.update(workflowExecutionSnapshots)
.set({ stateData: state })
.where(eq(workflowExecutionSnapshots.id, existingSnapshot.id))
refreshedState = state
} catch (error) {
logger.warn(
`Failed to refresh snapshot stateData for ${existingSnapshot.id}, continuing with existing data`,
error
)
}
logger.info(
`Reusing existing snapshot for workflow ${workflowId} (hash: ${stateHash.slice(0, 12)}...)`
)
return {
snapshot: existingSnapshot,
snapshot: { ...existingSnapshot, stateData: refreshedState },
isNew: false,
}
}