mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-10 23:48:09 -05:00
Lint
This commit is contained in:
@@ -1,14 +1,10 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useRef } from 'react'
|
||||
import { Upload, FileText, Plus, AlertCircle, CheckCircle } from 'lucide-react'
|
||||
import { useRef, useState } from 'react'
|
||||
import { AlertCircle, CheckCircle, FileText, Plus, Upload } from 'lucide-react'
|
||||
import { useParams, useRouter } from 'next/navigation'
|
||||
import { Alert, AlertDescription } from '@/components/ui/alert'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -17,16 +13,19 @@ import {
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
|
||||
import { Alert, AlertDescription } from '@/components/ui/alert'
|
||||
import { createLogger } from '@/lib/logs/console-logger'
|
||||
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
|
||||
import { useCollaborativeWorkflow } from '@/hooks/use-collaborative-workflow'
|
||||
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
|
||||
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
|
||||
import { importWorkflowFromYaml, parseWorkflowYaml } from '@/stores/workflows/yaml/importer'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useParams } from 'next/navigation'
|
||||
|
||||
const logger = createLogger('ImportControls')
|
||||
|
||||
@@ -44,12 +43,12 @@ export function ImportControls({ disabled = false }: ImportControlsProps) {
|
||||
warnings: string[]
|
||||
summary?: string
|
||||
} | null>(null)
|
||||
|
||||
|
||||
const fileInputRef = useRef<HTMLInputElement>(null)
|
||||
const router = useRouter()
|
||||
const params = useParams()
|
||||
const workspaceId = params.workspaceId as string
|
||||
|
||||
|
||||
// Stores and hooks
|
||||
const { createWorkflow } = useWorkflowRegistry()
|
||||
const { collaborativeAddBlock, collaborativeAddEdge } = useCollaborativeWorkflow()
|
||||
@@ -67,8 +66,10 @@ export function ImportControls({ disabled = false }: ImportControlsProps) {
|
||||
logger.error('Failed to read file:', error)
|
||||
setImportResult({
|
||||
success: false,
|
||||
errors: [`Failed to read file: ${error instanceof Error ? error.message : 'Unknown error'}`],
|
||||
warnings: []
|
||||
errors: [
|
||||
`Failed to read file: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
||||
],
|
||||
warnings: [],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -83,7 +84,7 @@ export function ImportControls({ disabled = false }: ImportControlsProps) {
|
||||
setImportResult({
|
||||
success: false,
|
||||
errors: ['YAML content is required'],
|
||||
warnings: []
|
||||
warnings: [],
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -94,12 +95,12 @@ export function ImportControls({ disabled = false }: ImportControlsProps) {
|
||||
try {
|
||||
// First validate the YAML without importing
|
||||
const { data: yamlWorkflow, errors: parseErrors } = parseWorkflowYaml(yamlContent)
|
||||
|
||||
|
||||
if (!yamlWorkflow || parseErrors.length > 0) {
|
||||
setImportResult({
|
||||
success: false,
|
||||
errors: parseErrors,
|
||||
warnings: []
|
||||
warnings: [],
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -116,9 +117,9 @@ export function ImportControls({ disabled = false }: ImportControlsProps) {
|
||||
router.push(`/workspace/${workspaceId}/w/${newWorkflowId}`)
|
||||
|
||||
// Small delay to ensure navigation and workflow initialization
|
||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000))
|
||||
|
||||
// Import the YAML into the new workflow
|
||||
// Import the YAML into the new workflow
|
||||
const result = await importWorkflowFromYaml(yamlContent, {
|
||||
addBlock: collaborativeAddBlock,
|
||||
addEdge: collaborativeAddEdge,
|
||||
@@ -133,7 +134,7 @@ export function ImportControls({ disabled = false }: ImportControlsProps) {
|
||||
// This will be called after navigation, so we need to get blocks from the store
|
||||
const { useWorkflowStore } = require('@/stores/workflows/workflow/store')
|
||||
return useWorkflowStore.getState().blocks
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
setImportResult(result)
|
||||
@@ -146,13 +147,12 @@ export function ImportControls({ disabled = false }: ImportControlsProps) {
|
||||
setImportResult(null)
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Failed to import YAML workflow:', error)
|
||||
setImportResult({
|
||||
success: false,
|
||||
errors: [`Import failed: ${error instanceof Error ? error.message : 'Unknown error'}`],
|
||||
warnings: []
|
||||
warnings: [],
|
||||
})
|
||||
} finally {
|
||||
setIsImporting(false)
|
||||
@@ -198,10 +198,12 @@ export function ImportControls({ disabled = false }: ImportControlsProps) {
|
||||
<Upload className='h-4 w-4' />
|
||||
<div className='flex flex-col'>
|
||||
<span>Upload YAML File</span>
|
||||
<span className='text-muted-foreground text-xs'>Import from .yaml or .yml file</span>
|
||||
<span className='text-muted-foreground text-xs'>
|
||||
Import from .yaml or .yml file
|
||||
</span>
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
|
||||
|
||||
<DropdownMenuItem
|
||||
onClick={handleOpenYamlDialog}
|
||||
disabled={isDisabled}
|
||||
@@ -236,11 +238,12 @@ export function ImportControls({ disabled = false }: ImportControlsProps) {
|
||||
|
||||
{/* YAML Import Dialog */}
|
||||
<Dialog open={showYamlDialog} onOpenChange={setShowYamlDialog}>
|
||||
<DialogContent className='max-w-4xl max-h-[80vh] flex flex-col'>
|
||||
<DialogContent className='flex max-h-[80vh] max-w-4xl flex-col'>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Import Workflow from YAML</DialogTitle>
|
||||
<DialogDescription>
|
||||
Paste your workflow YAML content below. This will create a new workflow with the blocks and connections defined in the YAML.
|
||||
Paste your workflow YAML content below. This will create a new workflow with the
|
||||
blocks and connections defined in the YAML.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
@@ -284,10 +287,12 @@ blocks:
|
||||
)}
|
||||
{importResult.warnings.length > 0 && (
|
||||
<div className='mt-2'>
|
||||
<div className='text-sm font-medium'>Warnings:</div>
|
||||
<div className='font-medium text-sm'>Warnings:</div>
|
||||
<ul className='mt-1 space-y-1 text-sm'>
|
||||
{importResult.warnings.map((warning, index) => (
|
||||
<li key={index} className='text-yellow-700'>• {warning}</li>
|
||||
<li key={index} className='text-yellow-700'>
|
||||
• {warning}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
@@ -321,10 +326,7 @@ blocks:
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleYamlImport}
|
||||
disabled={isImporting || !yamlContent.trim()}
|
||||
>
|
||||
<Button onClick={handleYamlImport} disabled={isImporting || !yamlContent.trim()}>
|
||||
{isImporting ? 'Importing...' : 'Import Workflow'}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
@@ -332,4 +334,4 @@ blocks:
|
||||
</Dialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,8 +57,8 @@ import {
|
||||
import { useWorkflowExecution } from '../../hooks/use-workflow-execution'
|
||||
import { DeploymentControls } from './components/deployment-controls/deployment-controls'
|
||||
import { ExportControls } from './components/export-controls/export-controls'
|
||||
import { ImportControls } from './components/import-controls/import-controls'
|
||||
import { HistoryDropdownItem } from './components/history-dropdown-item/history-dropdown-item'
|
||||
import { ImportControls } from './components/import-controls/import-controls'
|
||||
import { MarketplaceModal } from './components/marketplace-modal/marketplace-modal'
|
||||
import { NotificationDropdownItem } from './components/notification-dropdown-item/notification-dropdown-item'
|
||||
import { UserAvatarStack } from './components/user-avatar-stack/user-avatar-stack'
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { load as yamlParse } from 'js-yaml'
|
||||
import { createLogger } from '@/lib/logs/console-logger'
|
||||
import { getBlock } from '@/blocks'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
|
||||
const logger = createLogger('WorkflowYamlImporter')
|
||||
|
||||
@@ -48,59 +47,62 @@ interface ImportResult {
|
||||
/**
|
||||
* Parse YAML content and validate its structure
|
||||
*/
|
||||
export function parseWorkflowYaml(yamlContent: string): { data: YamlWorkflow | null; errors: string[] } {
|
||||
export function parseWorkflowYaml(yamlContent: string): {
|
||||
data: YamlWorkflow | null
|
||||
errors: string[]
|
||||
} {
|
||||
const errors: string[] = []
|
||||
|
||||
|
||||
try {
|
||||
const data = yamlParse(yamlContent) as any
|
||||
|
||||
|
||||
// Validate top-level structure
|
||||
if (!data || typeof data !== 'object') {
|
||||
errors.push('Invalid YAML: Root must be an object')
|
||||
return { data: null, errors }
|
||||
}
|
||||
|
||||
|
||||
if (!data.version) {
|
||||
errors.push('Missing required field: version')
|
||||
}
|
||||
|
||||
|
||||
if (!data.blocks || typeof data.blocks !== 'object') {
|
||||
errors.push('Missing or invalid field: blocks')
|
||||
return { data: null, errors }
|
||||
}
|
||||
|
||||
|
||||
// Validate blocks structure
|
||||
Object.entries(data.blocks).forEach(([blockId, block]: [string, any]) => {
|
||||
if (!block || typeof block !== 'object') {
|
||||
errors.push(`Invalid block definition for '${blockId}': must be an object`)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (!block.type || typeof block.type !== 'string') {
|
||||
errors.push(`Invalid block '${blockId}': missing or invalid 'type' field`)
|
||||
}
|
||||
|
||||
|
||||
if (!block.name || typeof block.name !== 'string') {
|
||||
errors.push(`Invalid block '${blockId}': missing or invalid 'name' field`)
|
||||
}
|
||||
|
||||
|
||||
if (block.inputs && typeof block.inputs !== 'object') {
|
||||
errors.push(`Invalid block '${blockId}': 'inputs' must be an object`)
|
||||
}
|
||||
|
||||
|
||||
if (block.preceding && !Array.isArray(block.preceding)) {
|
||||
errors.push(`Invalid block '${blockId}': 'preceding' must be an array`)
|
||||
}
|
||||
|
||||
|
||||
if (block.following && !Array.isArray(block.following)) {
|
||||
errors.push(`Invalid block '${blockId}': 'following' must be an array`)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
if (errors.length > 0) {
|
||||
return { data: null, errors }
|
||||
}
|
||||
|
||||
|
||||
return { data: data as YamlWorkflow, errors: [] }
|
||||
} catch (error) {
|
||||
errors.push(`YAML parsing error: ${error instanceof Error ? error.message : 'Unknown error'}`)
|
||||
@@ -114,63 +116,65 @@ export function parseWorkflowYaml(yamlContent: string): { data: YamlWorkflow | n
|
||||
function validateBlockReferences(yamlWorkflow: YamlWorkflow): string[] {
|
||||
const errors: string[] = []
|
||||
const blockIds = new Set(Object.keys(yamlWorkflow.blocks))
|
||||
|
||||
|
||||
Object.entries(yamlWorkflow.blocks).forEach(([blockId, block]) => {
|
||||
// Check preceding references
|
||||
if (block.preceding) {
|
||||
block.preceding.forEach(precedingId => {
|
||||
block.preceding.forEach((precedingId) => {
|
||||
if (!blockIds.has(precedingId)) {
|
||||
errors.push(`Block '${blockId}' references non-existent preceding block '${precedingId}'`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// Check following references
|
||||
if (block.following) {
|
||||
block.following.forEach(followingId => {
|
||||
block.following.forEach((followingId) => {
|
||||
if (!blockIds.has(followingId)) {
|
||||
errors.push(`Block '${blockId}' references non-existent following block '${followingId}'`)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that block types exist and are valid
|
||||
*/
|
||||
function validateBlockTypes(yamlWorkflow: YamlWorkflow): { errors: string[], warnings: string[] } {
|
||||
function validateBlockTypes(yamlWorkflow: YamlWorkflow): { errors: string[]; warnings: string[] } {
|
||||
const errors: string[] = []
|
||||
const warnings: string[] = []
|
||||
|
||||
|
||||
Object.entries(yamlWorkflow.blocks).forEach(([blockId, block]) => {
|
||||
// Check if block type exists
|
||||
const blockConfig = getBlock(block.type)
|
||||
|
||||
|
||||
// Special handling for container blocks
|
||||
if (block.type === 'loop' || block.type === 'parallel') {
|
||||
// These are valid container types
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (!blockConfig) {
|
||||
errors.push(`Unknown block type '${block.type}' for block '${blockId}'`)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// Validate inputs against block configuration
|
||||
if (block.inputs && blockConfig.subBlocks) {
|
||||
Object.keys(block.inputs).forEach(inputKey => {
|
||||
const subBlockConfig = blockConfig.subBlocks.find(sb => sb.id === inputKey)
|
||||
Object.keys(block.inputs).forEach((inputKey) => {
|
||||
const subBlockConfig = blockConfig.subBlocks.find((sb) => sb.id === inputKey)
|
||||
if (!subBlockConfig) {
|
||||
warnings.push(`Block '${blockId}' has unknown input '${inputKey}' for type '${block.type}'`)
|
||||
warnings.push(
|
||||
`Block '${blockId}' has unknown input '${inputKey}' for type '${block.type}'`
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
return { errors, warnings }
|
||||
}
|
||||
|
||||
@@ -178,75 +182,77 @@ function validateBlockTypes(yamlWorkflow: YamlWorkflow): { errors: string[], war
|
||||
* Calculate positions for blocks based on their connections
|
||||
* Uses a simple layered approach similar to the auto-layout algorithm
|
||||
*/
|
||||
function calculateBlockPositions(yamlWorkflow: YamlWorkflow): Record<string, { x: number; y: number }> {
|
||||
function calculateBlockPositions(
|
||||
yamlWorkflow: YamlWorkflow
|
||||
): Record<string, { x: number; y: number }> {
|
||||
const positions: Record<string, { x: number; y: number }> = {}
|
||||
const blockIds = Object.keys(yamlWorkflow.blocks)
|
||||
|
||||
|
||||
// Find starter blocks (no preceding connections)
|
||||
const starterBlocks = blockIds.filter(id => {
|
||||
const starterBlocks = blockIds.filter((id) => {
|
||||
const block = yamlWorkflow.blocks[id]
|
||||
return !block.preceding || block.preceding.length === 0
|
||||
})
|
||||
|
||||
|
||||
// If no starter blocks found, use first block as starter
|
||||
if (starterBlocks.length === 0 && blockIds.length > 0) {
|
||||
starterBlocks.push(blockIds[0])
|
||||
}
|
||||
|
||||
|
||||
// Build layers
|
||||
const layers: string[][] = []
|
||||
const visited = new Set<string>()
|
||||
const queue = [...starterBlocks]
|
||||
|
||||
|
||||
// BFS to organize blocks into layers
|
||||
while (queue.length > 0) {
|
||||
const currentLayer: string[] = []
|
||||
const currentLayerSize = queue.length
|
||||
|
||||
|
||||
for (let i = 0; i < currentLayerSize; i++) {
|
||||
const blockId = queue.shift()!
|
||||
if (visited.has(blockId)) continue
|
||||
|
||||
|
||||
visited.add(blockId)
|
||||
currentLayer.push(blockId)
|
||||
|
||||
|
||||
// Add following blocks to queue
|
||||
const block = yamlWorkflow.blocks[blockId]
|
||||
if (block.following) {
|
||||
block.following.forEach(followingId => {
|
||||
block.following.forEach((followingId) => {
|
||||
if (!visited.has(followingId)) {
|
||||
queue.push(followingId)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (currentLayer.length > 0) {
|
||||
layers.push(currentLayer)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Add any remaining blocks as isolated layer
|
||||
const remainingBlocks = blockIds.filter(id => !visited.has(id))
|
||||
const remainingBlocks = blockIds.filter((id) => !visited.has(id))
|
||||
if (remainingBlocks.length > 0) {
|
||||
layers.push(remainingBlocks)
|
||||
}
|
||||
|
||||
|
||||
// Calculate positions
|
||||
const horizontalSpacing = 600
|
||||
const verticalSpacing = 200
|
||||
const startX = 150
|
||||
const startY = 300
|
||||
|
||||
|
||||
layers.forEach((layer, layerIndex) => {
|
||||
const layerX = startX + layerIndex * horizontalSpacing
|
||||
|
||||
|
||||
layer.forEach((blockId, blockIndex) => {
|
||||
const blockY = startY + (blockIndex - layer.length / 2) * verticalSpacing
|
||||
positions[blockId] = { x: layerX, y: blockY }
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
return positions
|
||||
}
|
||||
|
||||
@@ -258,27 +264,27 @@ export function convertYamlToWorkflow(yamlWorkflow: YamlWorkflow): ImportResult
|
||||
const warnings: string[] = []
|
||||
const blocks: ImportedBlock[] = []
|
||||
const edges: ImportedEdge[] = []
|
||||
|
||||
|
||||
// Validate block references
|
||||
const referenceErrors = validateBlockReferences(yamlWorkflow)
|
||||
errors.push(...referenceErrors)
|
||||
|
||||
|
||||
// Validate block types
|
||||
const { errors: typeErrors, warnings: typeWarnings } = validateBlockTypes(yamlWorkflow)
|
||||
errors.push(...typeErrors)
|
||||
warnings.push(...typeWarnings)
|
||||
|
||||
|
||||
if (errors.length > 0) {
|
||||
return { blocks: [], edges: [], errors, warnings }
|
||||
}
|
||||
|
||||
|
||||
// Calculate positions
|
||||
const positions = calculateBlockPositions(yamlWorkflow)
|
||||
|
||||
|
||||
// Convert blocks
|
||||
Object.entries(yamlWorkflow.blocks).forEach(([blockId, yamlBlock]) => {
|
||||
const position = positions[blockId] || { x: 100, y: 100 }
|
||||
|
||||
|
||||
const importedBlock: ImportedBlock = {
|
||||
id: blockId,
|
||||
type: yamlBlock.type,
|
||||
@@ -286,7 +292,7 @@ export function convertYamlToWorkflow(yamlWorkflow: YamlWorkflow): ImportResult
|
||||
inputs: yamlBlock.inputs || {},
|
||||
position,
|
||||
}
|
||||
|
||||
|
||||
// Add container-specific data
|
||||
if (yamlBlock.type === 'loop' || yamlBlock.type === 'parallel') {
|
||||
importedBlock.data = {
|
||||
@@ -295,16 +301,16 @@ export function convertYamlToWorkflow(yamlWorkflow: YamlWorkflow): ImportResult
|
||||
type: yamlBlock.type === 'loop' ? 'loopNode' : 'parallelNode',
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
blocks.push(importedBlock)
|
||||
})
|
||||
|
||||
|
||||
// Convert edges from connections
|
||||
Object.entries(yamlWorkflow.blocks).forEach(([blockId, yamlBlock]) => {
|
||||
if (yamlBlock.following) {
|
||||
yamlBlock.following.forEach(targetId => {
|
||||
yamlBlock.following.forEach((targetId) => {
|
||||
const edgeId = `${blockId}-${targetId}-${Date.now()}`
|
||||
|
||||
|
||||
const edge: ImportedEdge = {
|
||||
id: edgeId,
|
||||
source: blockId,
|
||||
@@ -313,12 +319,12 @@ export function convertYamlToWorkflow(yamlWorkflow: YamlWorkflow): ImportResult
|
||||
targetHandle: 'target',
|
||||
type: 'workflowEdge',
|
||||
}
|
||||
|
||||
|
||||
edges.push(edge)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
return { blocks, edges, errors, warnings }
|
||||
}
|
||||
|
||||
@@ -344,31 +350,33 @@ export async function importWorkflowFromYaml(
|
||||
}
|
||||
): Promise<{ success: boolean; errors: string[]; warnings: string[]; summary?: string }> {
|
||||
logger.info('Starting YAML workflow import')
|
||||
|
||||
|
||||
try {
|
||||
// Parse YAML
|
||||
const { data: yamlWorkflow, errors: parseErrors } = parseWorkflowYaml(yamlContent)
|
||||
|
||||
|
||||
if (!yamlWorkflow || parseErrors.length > 0) {
|
||||
return { success: false, errors: parseErrors, warnings: [] }
|
||||
}
|
||||
|
||||
|
||||
// Convert to importable format
|
||||
const { blocks, edges, errors, warnings } = convertYamlToWorkflow(yamlWorkflow)
|
||||
|
||||
|
||||
if (errors.length > 0) {
|
||||
return { success: false, errors, warnings }
|
||||
}
|
||||
|
||||
|
||||
logger.info(`Importing ${blocks.length} blocks and ${edges.length} edges`)
|
||||
|
||||
|
||||
// Check for existing blocks (new workflows already have a starter block)
|
||||
const existingBlocks = workflowActions.getExistingBlocks()
|
||||
const existingStarterBlocks = Object.values(existingBlocks).filter((block: any) => block.type === 'starter')
|
||||
|
||||
const existingStarterBlocks = Object.values(existingBlocks).filter(
|
||||
(block: any) => block.type === 'starter'
|
||||
)
|
||||
|
||||
let actualBlocksCreated = 0
|
||||
let starterBlockId: string | null = null
|
||||
|
||||
|
||||
// Create blocks, but handle starter blocks specially
|
||||
for (const block of blocks) {
|
||||
if (block.type === 'starter') {
|
||||
@@ -377,7 +385,7 @@ export async function importWorkflowFromYaml(
|
||||
const existingStarter = existingStarterBlocks[0] as any
|
||||
starterBlockId = existingStarter.id
|
||||
logger.debug(`Using existing starter block: ${starterBlockId}`)
|
||||
|
||||
|
||||
// Update the starter block's inputs if needed
|
||||
Object.entries(block.inputs).forEach(([inputKey, inputValue]) => {
|
||||
if (inputValue !== undefined && inputValue !== null && starterBlockId !== null) {
|
||||
@@ -415,9 +423,9 @@ export async function importWorkflowFromYaml(
|
||||
block.extent
|
||||
)
|
||||
actualBlocksCreated++
|
||||
|
||||
|
||||
// Update edges to use the new generated ID
|
||||
edges.forEach(edge => {
|
||||
edges.forEach((edge) => {
|
||||
if (edge.source === block.id) {
|
||||
edge.source = generatedId
|
||||
}
|
||||
@@ -425,15 +433,15 @@ export async function importWorkflowFromYaml(
|
||||
edge.target = generatedId
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
// Store mapping for setting inputs later
|
||||
block.id = generatedId
|
||||
}
|
||||
|
||||
|
||||
// Update edges to use the starter block ID
|
||||
if (block.type === 'starter' && starterBlockId !== null) {
|
||||
const starterId = starterBlockId // TypeScript now knows this is string
|
||||
edges.forEach(edge => {
|
||||
edges.forEach((edge) => {
|
||||
if (edge.source === block.id) {
|
||||
edge.source = starterId
|
||||
}
|
||||
@@ -443,10 +451,10 @@ export async function importWorkflowFromYaml(
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Small delay to ensure blocks are created before adding edges
|
||||
await new Promise(resolve => setTimeout(resolve, 200))
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200))
|
||||
|
||||
// Create edges
|
||||
let edgesCreated = 0
|
||||
for (const edge of edges) {
|
||||
@@ -454,7 +462,7 @@ export async function importWorkflowFromYaml(
|
||||
logger.debug(`Creating edge: ${edge.source} -> ${edge.target}`)
|
||||
workflowActions.addEdge({
|
||||
...edge,
|
||||
id: crypto.randomUUID() // Generate unique edge ID
|
||||
id: crypto.randomUUID(), // Generate unique edge ID
|
||||
})
|
||||
edgesCreated++
|
||||
} catch (error) {
|
||||
@@ -462,10 +470,10 @@ export async function importWorkflowFromYaml(
|
||||
warnings.push(`Failed to create connection from ${edge.source} to ${edge.target}`)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Small delay before setting input values
|
||||
await new Promise(resolve => setTimeout(resolve, 200))
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200))
|
||||
|
||||
// Set input values for non-starter blocks
|
||||
for (const block of blocks) {
|
||||
if (block.type !== 'starter') {
|
||||
@@ -482,30 +490,29 @@ export async function importWorkflowFromYaml(
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Apply auto layout after a delay
|
||||
setTimeout(() => {
|
||||
logger.debug('Applying auto layout')
|
||||
workflowActions.applyAutoLayout()
|
||||
}, 800)
|
||||
|
||||
|
||||
const summary = `Successfully imported ${actualBlocksCreated} new blocks and ${edgesCreated} connections`
|
||||
logger.info(summary)
|
||||
|
||||
|
||||
return {
|
||||
success: true,
|
||||
errors: [],
|
||||
warnings,
|
||||
summary
|
||||
summary,
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
const errorMessage = `Import failed: ${error instanceof Error ? error.message : 'Unknown error'}`
|
||||
logger.error(errorMessage, error)
|
||||
return {
|
||||
success: false,
|
||||
errors: [errorMessage],
|
||||
warnings: []
|
||||
warnings: [],
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user