{
dependents = getDependentComponents(id, flowStore.val)
const cb = () => {
push(history, flowStore.val)
if (id === 'preprocessor') {
selectionManager.selectId('Input')
flowStore.val.value.preprocessor_module = undefined
} else {
selectNextId(id)
removeAtId(flowStore.val.value.modules, id)
}
refreshStateStore(flowStore)
onDelete?.(id)
delete flowStateStore.val[id]
}
if (Object.keys(dependents).length > 0) {
deleteCallback = cb
} else {
cb()
}
}}
onInsert={async (detail) => {
{
let originalModules
let targetModules
if (
detail.sourceId == 'Input' ||
detail.targetId == 'Result' ||
detail.kind == 'trigger'
) {
targetModules = flowStore.val.value.modules
}
dfs(flowStore.val.value.modules, (mod, modules, branches) => {
if (mod.id == moveManager.movingModuleId) {
originalModules = modules
}
if (detail.branch) {
if (mod.id == detail.branch.rootId) {
targetModules = branches[detail.branch.branch]
}
} else if (mod.id == detail.sourceId || mod.id == detail.targetId) {
targetModules = modules
} else if (mod.id == detail.agentId && mod.value.type === 'aiagent') {
targetModules = mod.value.tools
}
})
if (flowStore.val.value.modules && Array.isArray(flowStore.val.value.modules)) {
await tick()
if (moveManager.movingModuleId) {
push(history, flowStore.val)
if (!originalModules || !targetModules) {
moveManager.clearMoving()
return
}
if (moveManager.movingIds && moveManager.movingIds.length > 1) {
// Multi-move: splice out all moving modules from their parent, insert at target
const firstIndex = originalModules.findIndex(
(m) => m.id === moveManager.movingIds?.[0]
)
const removedModules = originalModules.splice(
firstIndex,
moveManager.movingIds.length
)
let insertIndex = detail.index
if (originalModules === targetModules && firstIndex < detail.index) {
insertIndex -= moveManager.movingIds.length
}
targetModules.splice(insertIndex, 0, ...removedModules)
selectionManager.selectByIds(removedModules.map((m) => m.id))
} else {
let indexToRemove = originalModules.findIndex((m) => moveManager.movingModuleId == m.id)
let [removedModule] = originalModules.splice(indexToRemove, 1)
// When moving within the same array, removal shifts subsequent indices down by 1
let insertIndex = detail.index
if (originalModules === targetModules && indexToRemove < detail.index) {
insertIndex -= 1
}
targetModules.splice(insertIndex, 0, removedModule)
selectionManager.selectId(removedModule.id)
}
moveManager.clearMoving()
} else {
if (detail.isPreprocessor) {
await insertNewPreprocessorModule(
flowStore,
flowStateStore,
detail.inlineScript,
detail.script
)
selectionManager.selectId('preprocessor')
if (detail.inlineScript?.instructions) {
dispatch('generateStep', {
moduleId: 'preprocessor',
lang: detail.inlineScript?.language,
instructions: detail.inlineScript?.instructions
})
}
} else {
const index = (detail.agentId ? targetModules?.length : detail.index) ?? 0
const toolKind: SpecialToolKind | 'flowmoduleTool' | undefined = detail.agentId
? (SPECIAL_TOOL_KINDS as readonly string[]).includes(detail.kind)
? (detail.kind as SpecialToolKind)
: 'flowmoduleTool'
: undefined
await insertNewModuleAtIndex(
targetModules,
index,
detail.kind,
detail.script,
detail.flow,
detail.inlineScript,
toolKind
)
const id = targetModules[index].id
selectionManager.selectId(id)
if (detail.inlineScript?.instructions) {
dispatch('generateStep', {
moduleId: id,
lang: detail.inlineScript?.language,
instructions: detail.inlineScript?.instructions
})
}
if (detail.kind == 'trigger') {
await insertNewModuleAtIndex(
targetModules,
index + 1,
'forloop',
undefined,
undefined,
undefined
)
setExpr(targetModules[index + 1], `results.${id}`)
setScheduledPollSchedule(triggersState, triggersCount)
}
if (detail.flow?.path) {
loadLastJob(detail.flow.path, id)
} else if (detail.script?.path) {
loadLastJob(detail.script?.path, id)
}
}
}
if (['branchone', 'branchall'].includes(detail.kind)) {
await addBranch(targetModules[detail.index ?? 0].id)
}
refreshStateStore(flowStore)
dispatch('change')
}
}
}}
onNewBranch={async (id) => {
if (id) {
await addBranch(id)
refreshStateStore(flowStore)
}
}}
onSelect={(id) => {
flowPropPickerConfig.set(undefined)
}}
onChangeId={(detail) => {
let { id, newId, deps } = detail
dfs(flowStore.val.value.modules, (mod) => {
if (deps[mod.id]) {
deps[mod.id].forEach((dep) => {
if (
mod.value.type == 'rawscript' ||
mod.value.type == 'script' ||
mod.value.type == 'flow'
) {
mod.value.input_transforms = Object.fromEntries(
Object.entries(mod.value.input_transforms).map(([k, v]) => {
if (v.type == 'javascript') {
return [k, { ...v, expr: replaceId(v.expr, id, newId) }]
} else {
return [k, v]
}
})
)
} else if (mod?.value?.type === 'forloopflow') {
if (mod.value.iterator.type === 'javascript') {
mod.value.iterator.expr = replaceId(mod.value.iterator.expr, id, newId)
}
} else if (mod?.value?.type === 'branchone') {
mod.value.branches.forEach((branch) => {
branch.expr = replaceId(branch.expr, id, newId)
})
}
})
}
if (mod.id == id) {
mod.id = newId
}
})
flowStateStore.val[newId] = flowStateStore.val[id]
delete flowStateStore.val[id]
refreshStateStore(flowStore)
selectionManager.selectId(newId)
}}
onDeleteBranch={async ({ id, index }) => {
if (id) {
await removeBranch(id, index)
refreshStateStore(flowStore)
selectionManager.selectId(id)
}
}}
onMove={(id) => {
moveManager.toggleMoving(id)
}}
onDuplicate={(id) => {
let targetModules: FlowModule[] | undefined
let targetIndex: number = -1
dfs(flowStore.val.value.modules, (mod, modules) => {
const idx = modules.findIndex((m) => m.id === id)
if (idx !== -1) {
targetModules = modules
targetIndex = idx
}
})
if (!targetModules || targetIndex === -1) return
push(history, flowStore.val)
const original = targetModules[targetIndex]
const clone: FlowModule = $state.snapshot(original)
// Assign copy id to the clone, and fresh ids to nested modules
clone.id = copyId(original.id, flowStateStore.val, flowStore.val)
flowStateStore.val[clone.id] = emptyFlowModuleState()
dfs([clone], (mod) => {
if (mod.id !== clone.id) {
const newModId = nextId(flowStateStore.val, flowStore.val)
mod.id = newModId
flowStateStore.val[newModId] = emptyFlowModuleState()
}
})
targetModules.splice(targetIndex + 1, 0, clone)
refreshStateStore(flowStore)
selectionManager.selectId(clone.id)
}}
onUpdateMock={(detail) => {
let module = findModuleById(detail.id)
module.mock = $state.snapshot(detail.mock)
refreshStateStore(flowStore)
}}
{onTestFlow}
{isRunning}
{onCancelTestFlow}
{onOpenPreview}
{onHideJobStatus}
exitNoteMode={() => (noteMode = false)}
onNotePositionUpdate={(noteId, position) => {
// Update note position via NoteEditor context in edit mode
if (noteEditorContext?.noteEditor) {
noteEditorContext.noteEditor.updatePosition(noteId, position)
}
}}
multiSelectEnabled
movingIds={moveManager.movingIds}
onDeleteMultiple={deleteMultiple}
onDuplicateMultiple={duplicateMultiple}
onMoveMultiple={moveMultiple}
/>
{#if !disableTutorials}