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
Expand Up @@ -533,6 +533,8 @@ export const ToolInput = memo(function ToolInput({
for (const [toolIndex, tool] of selectedTools.entries()) {
const blockConfig = allBlocks.find((b: { type: string }) => b.type === tool.type)
if (!blockConfig?.subBlocks) continue
// canonical-index-unscoped: a nested tool resolves against `tool.params`, which only ever
// holds action-surface values — a tool is never invoked in trigger mode.
const toolCanonical = buildCanonicalIndex(blockConfig.subBlocks)
const scopedOverrides = scopeCanonicalModesForTool(
canonicalModeOverrides,
Expand Down Expand Up @@ -1779,7 +1781,8 @@ export const ToolInput = memo(function ToolInput({
: null

const toolCanonicalIndex: CanonicalIndex | null = toolBlock?.subBlocks
? buildCanonicalIndex(toolBlock.subBlocks)
? // canonical-index-unscoped: nested tool params are always the action surface
buildCanonicalIndex(toolBlock.subBlocks)
: null

const toolContextValues = toolCanonicalIndex
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { useCallback, useMemo } from 'react'
import { isEqual } from 'es-toolkit'
import { useStoreWithEqualityFn } from 'zustand/traditional'
import { buildCanonicalIndex, resolveDependencyValue } from '@/lib/workflows/subblocks/visibility'
import {
buildCanonicalIndexForSurface,
resolveDependencyValue,
} from '@/lib/workflows/subblocks/visibility'
import { getBlock } from '@/blocks/registry'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
Expand All @@ -22,9 +25,10 @@ export function useCanonicalSubBlockValue<T = unknown>(
const activeWorkflowId = useWorkflowRegistry((s) => s.activeWorkflowId)
const blockState = useWorkflowStore((state) => state.blocks[blockId])
const blockConfig = blockState?.type ? getBlock(blockState.type) : null
const triggerSurface = blockState?.triggerMode === true
const canonicalIndex = useMemo(
() => buildCanonicalIndex(blockConfig?.subBlocks || []),
[blockConfig?.subBlocks]
() => buildCanonicalIndexForSurface(blockConfig?.subBlocks || [], triggerSurface),
[blockConfig?.subBlocks, triggerSurface]
)
const canonicalModeOverrides = blockState?.data?.canonicalModes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useCallback, useMemo } from 'react'
import { isEqual } from 'es-toolkit'
import { useStoreWithEqualityFn } from 'zustand/traditional'
import {
buildCanonicalIndex,
buildCanonicalIndexForSurface,
isNonEmptyValue,
normalizeDependencyValue,
parseDependsOn,
Expand Down Expand Up @@ -41,9 +41,15 @@ export function useDependsOnGate(
: blockState?.type
? getBlock(blockState.type)
: null
/**
* A nested tool's params are always the ACTION surface — `dependencyBlockType` means
* `blockConfig` describes the tool, not the host block, so the host's trigger mode says
* nothing about which of the tool's fields are live.
*/
const triggerSurface = !dependencyBlockType && blockState?.triggerMode === true
const canonicalIndex = useMemo(
() => buildCanonicalIndex(blockConfig?.subBlocks || []),
[blockConfig?.subBlocks]
() => buildCanonicalIndexForSurface(blockConfig?.subBlocks || [], triggerSurface),
[blockConfig?.subBlocks, triggerSurface]
)
const canonicalModeOverrides = blockState?.data?.canonicalModes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ import { isRetryEligibleBlock } from '@/lib/workflows/blocks/retry-eligibility'
import {
buildCanonicalIndex,
evaluateSubBlockCondition,
getCanonicalSubBlocksForSurface,
hasAdvancedValues,
isCanonicalPair,
isStandaloneAdvancedMode,
resolveCanonicalMode,
shouldUseSubBlockForTriggerModeCanonicalIndex,
} from '@/lib/workflows/subblocks/visibility'
import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider'
import {
Expand Down Expand Up @@ -157,11 +157,10 @@ export function Editor() {
isEqual
)

const subBlocksForCanonical = useMemo(() => {
const subBlocks = blockConfig?.subBlocks || []
if (!triggerMode) return subBlocks
return subBlocks.filter(shouldUseSubBlockForTriggerModeCanonicalIndex)
}, [blockConfig?.subBlocks, triggerMode])
const subBlocksForCanonical = useMemo(
() => getCanonicalSubBlocksForSurface(blockConfig?.subBlocks || [], triggerMode),
[blockConfig?.subBlocks, triggerMode]
)

const canonicalIndex = useMemo(
() => buildCanonicalIndex(subBlocksForCanonical),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { useCallback, useMemo } from 'react'
import {
buildCanonicalIndex,
buildCanonicalIndexForSurface,
evaluateSubBlockCondition,
isSubBlockFeatureEnabled,
isSubBlockHidden,
isSubBlockVisibleForMode,
isSubBlockVisibleForTriggerMode,
isToolInputOnlySubBlock,
shouldUseSubBlockForTriggerModeCanonicalIndex,
} from '@/lib/workflows/subblocks/visibility'
import type { BlockConfig, SubBlockConfig } from '@/blocks/types'
import { usePermissionConfig } from '@/hooks/use-permission-config'
Expand Down Expand Up @@ -48,7 +47,8 @@ export function useEditorSubblockLayout(
config?.subBlocks || [],
blockId,
activeWorkflowId,
blockDataFromStore?.canonicalModes
blockDataFromStore?.canonicalModes,
displayTriggerMode
)

return useMemo(() => {
Expand Down Expand Up @@ -102,10 +102,7 @@ export function useEditorSubblockLayout(
{}
)

const subBlocksForCanonical = displayTriggerMode
? (config.subBlocks || []).filter(shouldUseSubBlockForTriggerModeCanonicalIndex)
: config.subBlocks || []
const canonicalIndex = buildCanonicalIndex(subBlocksForCanonical)
const canonicalIndex = buildCanonicalIndexForSurface(config.subBlocks || [], displayTriggerMode)
const effectiveAdvanced = displayAdvancedMode
const canonicalModeOverrides = blockData?.canonicalModes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import {
} from '@/lib/workflows/subblocks/display'
import {
buildCanonicalIndex,
buildCanonicalIndexForSurface,
hasAdvancedValues,
resolveDependencyValue,
} from '@/lib/workflows/subblocks/visibility'
Expand Down Expand Up @@ -805,14 +806,18 @@ export const WorkflowBlock = memo(function WorkflowBlock({
])
}

const canonicalIndex = useMemo(() => buildCanonicalIndex(config.subBlocks), [config.subBlocks])
const canonicalIndex = useMemo(
() => buildCanonicalIndexForSurface(config.subBlocks, displayTriggerMode),
[config.subBlocks, displayTriggerMode]
)
const canonicalModeOverrides = currentStoreBlock?.data?.canonicalModes

const hiddenByReactiveCondition = useReactiveConditions(
config.subBlocks,
id,
activeWorkflowId,
canonicalModeOverrides
canonicalModeOverrides,
displayTriggerMode
)

const subBlockRowsData = useMemo(() => {
Expand Down Expand Up @@ -859,7 +864,6 @@ export const WorkflowBlock = memo(function WorkflowBlock({
const displayableSubBlocks = getCardSubBlocks(config, {
advanced: effectiveAdvanced,
values: rawValues,
canonicalIndex,
canonicalModeOverrides,
triggerMode: effectiveTrigger,
hiddenIds: hiddenByReactiveCondition,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { useParams } from 'next/navigation'
import { ReactFlowProvider } from 'reactflow'
import { extractReferencePrefixes } from '@/lib/workflows/sanitization/references'
import {
buildCanonicalIndex,
buildCanonicalIndexForSurface,
evaluateSubBlockCondition,
hasAdvancedValues,
isSubBlockFeatureEnabled,
Expand Down Expand Up @@ -1055,9 +1055,10 @@ function PreviewEditorContent({
}, {})
}, [subBlockValues])

const effectiveTrigger = block.triggerMode === true
const canonicalIndex = useMemo(
() => buildCanonicalIndex(blockConfig?.subBlocks || []),
[blockConfig?.subBlocks]
() => buildCanonicalIndexForSurface(blockConfig?.subBlocks || [], effectiveTrigger),
[blockConfig?.subBlocks, effectiveTrigger]
)

const isSubflow = block.type === 'loop' || block.type === 'parallel'
Expand Down Expand Up @@ -1118,7 +1119,6 @@ function PreviewEditorContent({
hasAdvancedValues(blockConfig.subBlocks, rawValues, canonicalIndex)

const isPureTriggerBlock = blockConfig.triggers?.enabled && blockConfig.category === 'triggers'
const effectiveTrigger = block.triggerMode === true

const visibleSubBlocks = blockConfig.subBlocks.filter((subBlock) => {
if (subBlock.hidden || subBlock.hideFromPreview) return false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
resolveWorkflowSelectionLabel,
} from '@/lib/workflows/subblocks/display'
import {
buildCanonicalIndex,
buildCanonicalIndexForSurface,
evaluateSubBlockCondition,
isSubBlockFeatureEnabled,
isSubBlockVisibleForMode,
Expand Down Expand Up @@ -237,10 +237,11 @@ function WorkflowPreviewBlockInner({ data }: NodeProps<WorkflowPreviewBlockData>
} = data

const blockConfig = getBlock(type)
const effectiveTrigger = isTrigger || type === 'starter'

const canonicalIndex = useMemo(
() => buildCanonicalIndex(blockConfig?.subBlocks || []),
[blockConfig?.subBlocks]
() => buildCanonicalIndexForSurface(blockConfig?.subBlocks || [], effectiveTrigger),
[blockConfig?.subBlocks, effectiveTrigger]
)

const rawValues = useMemo(() => {
Expand All @@ -267,7 +268,6 @@ function WorkflowPreviewBlockInner({ data }: NodeProps<WorkflowPreviewBlockData>
if (!blockConfig?.subBlocks) return []

const isPureTriggerBlock = blockConfig.triggers?.enabled && blockConfig.category === 'triggers'
const effectiveTrigger = isTrigger || type === 'starter'

return blockConfig.subBlocks.filter((subBlock) => {
if (subBlock.hidden) return false
Expand Down Expand Up @@ -308,8 +308,7 @@ function WorkflowPreviewBlockInner({ data }: NodeProps<WorkflowPreviewBlockData>
blockConfig?.subBlocks,
blockConfig?.triggers?.enabled,
blockConfig?.category,
type,
isTrigger,
effectiveTrigger,
canonicalIndex,
rawValues,
canvasPresentation,
Expand Down Expand Up @@ -348,7 +347,6 @@ function WorkflowPreviewBlockInner({ data }: NodeProps<WorkflowPreviewBlockData>
* lightweight mode, which has no values to resolve chips from.
*/
const sentenceSegments = useMemo(() => {
const effectiveTrigger = isTrigger || type === 'starter'
if (lightweight || !blockConfig) return null
if (type === 'condition' || type === 'router_v2' || type === 'starter') return null

Expand All @@ -374,7 +372,7 @@ function WorkflowPreviewBlockInner({ data }: NodeProps<WorkflowPreviewBlockData>
(subBlockId) => availableIds.has(subBlockId),
(subBlockId) => onCardById.get(subBlockId) ?? null
)
}, [lightweight, blockConfig, type, isTrigger, visibleSubBlocks, onCardById, rawValues])
}, [lightweight, blockConfig, type, effectiveTrigger, visibleSubBlocks, onCardById, rawValues])

/**
* Compute condition rows for condition blocks.
Expand Down
21 changes: 16 additions & 5 deletions apps/sim/ee/workspace-forking/lib/copy/copy-workflows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -520,11 +520,21 @@ export async function copyWorkflowStateIntoTarget(
let activeCanonicalModes: CanonicalModeOverrides | undefined = (
block.data as { canonicalModes?: Record<string, 'basic' | 'advanced'> } | undefined
)?.canonicalModes
// A mixed action/trigger block shares one `canonicalModes` key across both surfaces, so the
// remap has to know which surface is live: without it a trigger field reads as a dormant
// member of the action pair and the remap clears the value.
const blockTriggerMode = block.triggerMode === true
if (transformSubBlocks) {
subBlocks = transformSubBlocks(subBlocks, block.type, activeCanonicalModes, (next) => {
activeCanonicalModes = next
updatedData = { ...updatedData, canonicalModes: next } as BlockData
})
subBlocks = transformSubBlocks(
subBlocks,
block.type,
activeCanonicalModes,
(next) => {
activeCanonicalModes = next
updatedData = { ...updatedData, canonicalModes: next } as BlockData
},
blockTriggerMode
)
}
if (varIdMapping.size > 0) {
subBlocks = remapVariableIdsInSubBlocks(subBlocks, varIdMapping)
Expand Down Expand Up @@ -565,7 +575,8 @@ export async function copyWorkflowStateIntoTarget(
block.name,
targetCurrent.subBlocks,
subBlocks,
activeCanonicalModes
activeCanonicalModes,
blockTriggerMode
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ function emitAnchoredDependents(params: EmitAnchoredParams): void {
})
const scanSubBlocks = getSelectorContextSubBlocks(config.subBlocks, values, triggerMode)
const canonicalIndex = buildCanonicalIndex(scanSubBlocks)
// canonical-index-unscoped: `scanSubBlocks` is already narrowed to the active surface by
// `getSelectorContextSubBlocks` above, so scoping again here would be a no-op.
const gates = createCanonicalModeGates(scanSubBlocks, values, canonicalModes)
const configById = new Map(scanSubBlocks.filter((cfg) => cfg.id).map((cfg) => [cfg.id, cfg]))
// Shared with `applyDependentOverrides`, so what the modal offers is exactly what the sync
Expand Down
13 changes: 9 additions & 4 deletions apps/sim/ee/workspace-forking/lib/promote/cleared-refs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ function baseSubBlockId(key: string): string {
function collectForkWorkflowReferences(
subBlocks: SubBlockRecord,
config: ReturnType<typeof getBlock>,
canonicalModes: CanonicalModeOverrides | undefined
canonicalModes: CanonicalModeOverrides | undefined,
triggerMode: boolean
): Array<{ workflowId: string; subBlockKey: string }> {
const out: Array<{ workflowId: string; subBlockKey: string }> = []
// Collapse each canonical pair to its ACTIVE member and skip condition-hidden fields: only a
Expand All @@ -103,7 +104,8 @@ function collectForkWorkflowReferences(
const gates = createCanonicalModeGates(
config?.subBlocks,
buildSubBlockValues(subBlocks),
canonicalModes
canonicalModes,
triggerMode
)
const detectionSkipped = (key: string) =>
gates.isDormantMember(key) || gates.isConditionHidden(key)
Expand Down Expand Up @@ -199,6 +201,7 @@ export function collectForkClearedRefCandidates(
blockName: blockLabel,
blockType: block.type,
canonicalModes: block.data?.canonicalModes,
triggerMode: block.triggerMode === true,
})
for (const ref of scan.unmapped) {
if (CLEARED_REF_EXCLUDED_KINDS.has(ref.kind)) continue
Expand Down Expand Up @@ -245,7 +248,8 @@ export function collectForkClearedRefCandidates(
for (const wfRef of collectForkWorkflowReferences(
subBlocks,
config,
block.data?.canonicalModes
block.data?.canonicalModes,
block.triggerMode === true
)) {
if (workflowIdMap.has(wfRef.workflowId)) continue
out.push({
Expand Down Expand Up @@ -397,7 +401,8 @@ function hasForkSyncBlockerCandidates(
const workflowRefs = collectForkWorkflowReferences(
subBlocks,
getBlock(block.type),
block.data?.canonicalModes
block.data?.canonicalModes,
block.triggerMode === true
)
if (workflowRefs.some((ref) => !workflowIdMap.has(ref.workflowId))) return true
}
Expand Down
6 changes: 4 additions & 2 deletions apps/sim/ee/workspace-forking/lib/remap/fork-bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,15 @@ export type ForkCopyResolver = (kind: ForkRemapKind, sourceId: string) => string
* the child defines the key).
*/
export function createForkBootstrapTransform(resolveCopied: ForkCopyResolver): SubBlockTransform {
return (subBlocks, blockType, canonicalModes, onCanonicalModesChanged) => {
return (subBlocks, blockType, canonicalModes, onCanonicalModesChanged, triggerMode) => {
// Every resolution at fork-create IS a copy (the resolver is the copy id map), so all
// remapped keys carry copy provenance - copy-faithful dependents (column picks) survive.
// `blockType`/`canonicalModes` activate the mode policy: active basic remaps, active
// advanced (manual) passes through with its dependents, dormant members clear.
const result = remapForkSubBlocks(subBlocks, resolveCopied, 'create', {
blockType,
canonicalModes,
triggerMode,
isCopiedTarget: (kind, sourceId) => resolveCopied(kind, sourceId) != null,
})
if (result.canonicalModes) onCanonicalModesChanged?.(result.canonicalModes)
Expand All @@ -38,7 +39,8 @@ export function createForkBootstrapTransform(resolveCopied: ForkCopyResolver): S
blockType,
result.remappedKeys,
result.canonicalModes ?? canonicalModes,
result.copyRemappedKeys
result.copyRemappedKeys,
triggerMode
)
}
}
Expand Down
2 changes: 2 additions & 0 deletions apps/sim/ee/workspace-forking/lib/remap/reference-scan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ interface ScannerBlock {
type: string
subBlocks: unknown
canonicalModes?: CanonicalModeOverrides
triggerMode?: boolean
}

/**
Expand Down Expand Up @@ -45,6 +46,7 @@ export function toScannerBlocks(state: WorkflowState): ScannerBlock[] {
type: block.type,
subBlocks: block.subBlocks as unknown,
canonicalModes: block.data?.canonicalModes,
triggerMode: block.triggerMode,
}))
}

Expand Down
Loading
Loading