diff --git a/app/w/[id]/components/workflow-block/components/sub-block/sub-block.tsx b/app/w/[id]/components/workflow-block/components/sub-block/sub-block.tsx
index eea19c7aff..fb003ba9de 100644
--- a/app/w/[id]/components/workflow-block/components/sub-block/sub-block.tsx
+++ b/app/w/[id]/components/workflow-block/components/sub-block/sub-block.tsx
@@ -91,7 +91,7 @@ export function SubBlock({ blockId, config, isConnecting }: SubBlockProps) {
)
case 'tool-input':
@@ -101,7 +101,7 @@ export function SubBlock({ blockId, config, isConnecting }: SubBlockProps) {
diff --git a/app/w/[id]/components/workflow-block/workflow-block.tsx b/app/w/[id]/components/workflow-block/workflow-block.tsx
index 8bdb85d8d5..2566a11647 100644
--- a/app/w/[id]/components/workflow-block/workflow-block.tsx
+++ b/app/w/[id]/components/workflow-block/workflow-block.tsx
@@ -5,9 +5,10 @@ import { Handle, Position } from 'reactflow'
import { cn } from '@/lib/utils'
import { ActionBar } from './components/action-bar/action-bar'
import { ConnectionBlocks } from './components/connection-blocks/connection-blocks'
-import { useState } from 'react'
+import { useState, useEffect, useRef } from 'react'
import { useWorkflowStore } from '@/stores/workflow/store'
import { Badge } from '@/components/ui/badge'
+import { useUpdateNodeInternals } from 'reactflow'
interface WorkflowBlockProps {
id: string
@@ -18,6 +19,11 @@ interface WorkflowBlockProps {
selected?: boolean
}
+interface SubBlockPosition {
+ id: string
+ top: number
+}
+
export function WorkflowBlock({
id,
type,
@@ -37,6 +43,60 @@ export function WorkflowBlock({
const [isEditing, setIsEditing] = useState(false)
const [editedName, setEditedName] = useState('')
const updateBlockName = useWorkflowStore((state) => state.updateBlockName)
+ const blockRef = useRef(null)
+ const [subBlockPositions, setSubBlockPositions] = useState<
+ SubBlockPosition[]
+ >([])
+ const updateNodeInternals = useUpdateNodeInternals()
+
+ // Calculate subblock positions when the component mounts or updates
+ useEffect(() => {
+ const calculatePositions = () => {
+ if (!blockRef.current) return
+
+ const positions: SubBlockPosition[] = []
+ const blockRect = blockRef.current.getBoundingClientRect()
+
+ workflow.subBlocks
+ .filter((block) => block.outputHandle)
+ .forEach((block) => {
+ const subBlockElement = blockRef.current?.querySelector(
+ `[data-subblock-id="${block.id}"]`
+ )
+ if (subBlockElement) {
+ const subBlockRect = subBlockElement.getBoundingClientRect()
+ positions.push({
+ id: block.id,
+ // Move handle 25px up from the bottom
+ top: subBlockRect.bottom - blockRect.top - 25,
+ })
+ }
+ })
+
+ setSubBlockPositions(positions)
+ updateNodeInternals(id)
+ }
+
+ // Calculate initial positions
+ calculatePositions()
+
+ // Use ResizeObserver to detect size changes and recalculate
+ const resizeObserver = new ResizeObserver(() => {
+ calculatePositions()
+ })
+
+ if (blockRef.current) {
+ resizeObserver.observe(blockRef.current)
+ }
+
+ // Recalculate on window resize
+ window.addEventListener('resize', calculatePositions)
+
+ return () => {
+ resizeObserver.disconnect()
+ window.removeEventListener('resize', calculatePositions)
+ }
+ }, [workflow.subBlocks, id, updateNodeInternals])
function groupSubBlocks(subBlocks: SubBlockConfig[]) {
const rows: SubBlockConfig[][] = []
@@ -87,6 +147,7 @@ export function WorkflowBlock({
return (
-
+ {/* Main output handle */}
+ {subBlockPositions.length === 0 && (
+
+ )}
+
+ {/* Subblock output handles */}
+ {subBlockPositions.map((position) => (
+
+ ))}
)
}
diff --git a/blocks/blocks/condition.ts b/blocks/blocks/condition.ts
new file mode 100644
index 0000000000..1a9bccf29a
--- /dev/null
+++ b/blocks/blocks/condition.ts
@@ -0,0 +1,46 @@
+import { ConditionalIcon } from '@/components/icons'
+import { BlockConfig } from '../types'
+import { CodeExecutionOutput } from '@/tools/function/execute'
+
+export const ConditionBlock: BlockConfig = {
+ type: 'condition',
+ toolbar: {
+ title: 'Condition',
+ description: 'Add a condition',
+ bgColor: '#FF972F',
+ icon: ConditionalIcon,
+ category: 'blocks',
+ },
+ tools: {
+ access: ['function_execute']
+ },
+ workflow: {
+ inputs: {
+ code: { type: 'string', required: true }
+ },
+ outputs: {
+ response: {
+ type: {
+ result: 'any',
+ stdout: 'string'
+ }
+ }
+ },
+ subBlocks: [
+ {
+ id: 'if',
+ title: 'if',
+ type: 'code',
+ layout: 'full',
+ outputHandle: true
+ },
+ {
+ id: 'elseIf',
+ title: 'else if',
+ type: 'code',
+ layout: 'full',
+ outputHandle: true
+ }
+ ],
+ },
+}
\ No newline at end of file
diff --git a/blocks/blocks/function.ts b/blocks/blocks/function.ts
index 608e0761bf..ba9f4d6783 100644
--- a/blocks/blocks/function.ts
+++ b/blocks/blocks/function.ts
@@ -7,7 +7,7 @@ export const FunctionBlock: BlockConfig = {
toolbar: {
title: 'Function',
description: 'Add custom logic',
- bgColor: '#FF8D2F',
+ bgColor: '#FF402F',
icon: CodeIcon,
category: 'blocks',
},
@@ -29,10 +29,8 @@ export const FunctionBlock: BlockConfig = {
subBlocks: [
{
id: 'code',
- title: 'Code',
type: 'code',
layout: 'full',
- placeholder: 'Enter your code here...'
}
],
},
diff --git a/blocks/index.ts b/blocks/index.ts
index e94076303b..808d9bc338 100644
--- a/blocks/index.ts
+++ b/blocks/index.ts
@@ -10,6 +10,7 @@ import { JinaBlock } from './blocks/jina'
import { TranslateBlock } from './blocks/translate'
import { SlackMessageBlock } from './blocks/slack'
import { GitHubBlock } from './blocks/github'
+import { ConditionBlock } from './blocks/condition'
// Export blocks for ease of use
export {
@@ -21,7 +22,8 @@ export {
JinaBlock,
TranslateBlock,
SlackMessageBlock,
- GitHubBlock
+ GitHubBlock,
+ ConditionBlock
}
// Registry of all block configurations
@@ -34,7 +36,8 @@ const blocks: Record = {
jina_reader: JinaBlock,
translate: TranslateBlock,
slack_message: SlackMessageBlock,
- github_repo_info: GitHubBlock
+ github_repo_info: GitHubBlock,
+ condition: ConditionBlock
}
// Build a reverse mapping of tools to block types
diff --git a/blocks/types.ts b/blocks/types.ts
index 5f6c755f89..4c94585573 100644
--- a/blocks/types.ts
+++ b/blocks/types.ts
@@ -38,7 +38,7 @@ export interface ParamConfig {
export interface SubBlockConfig {
id: string
- title: string
+ title?: string
type: SubBlockType
layout?: SubBlockLayout
options?: string[] | { label: string; id: string }[]
@@ -48,6 +48,7 @@ export interface SubBlockConfig {
placeholder?: string
password?: boolean
connectionDroppable?: boolean
+ outputHandle?: boolean
hidden?: boolean
value?: (params: Record) => string
}