From de1a71b88e56e4917fd20efc4a4162a41ab880a8 Mon Sep 17 00:00:00 2001 From: Emir Karabeg Date: Fri, 10 Jan 2025 14:16:17 -0800 Subject: [PATCH] Started scaffolding for sub blocks and created first workflow blocks --- app/w/[id]/workflow.tsx | 45 +- app/w/components/block/blocks.ts | 131 +++- app/w/components/block/sub-block.tsx | 108 +++ .../block/{block.tsx => toolbar-block.tsx} | 31 +- app/w/components/block/workflow-block.tsx | 89 +++ app/w/components/toolbar/toolbar.tsx | 10 +- components/ui/card.tsx | 79 +++ components/ui/dialog.tsx | 122 ++++ components/ui/label.tsx | 26 + components/ui/select.tsx | 160 +++++ components/ui/slider.tsx | 28 + components/ui/textarea.tsx | 22 + package-lock.json | 669 ++++++++++++++++++ package.json | 5 + 14 files changed, 1460 insertions(+), 65 deletions(-) create mode 100644 app/w/components/block/sub-block.tsx rename app/w/components/block/{block.tsx => toolbar-block.tsx} (60%) create mode 100644 app/w/components/block/workflow-block.tsx create mode 100644 components/ui/card.tsx create mode 100644 components/ui/dialog.tsx create mode 100644 components/ui/label.tsx create mode 100644 components/ui/select.tsx create mode 100644 components/ui/slider.tsx create mode 100644 components/ui/textarea.tsx diff --git a/app/w/[id]/workflow.tsx b/app/w/[id]/workflow.tsx index 4551089b8..d81ec6bd4 100644 --- a/app/w/[id]/workflow.tsx +++ b/app/w/[id]/workflow.tsx @@ -1,7 +1,8 @@ 'use client' import { useState, useCallback, useEffect } from 'react' -import { BlockProps } from '../components/block/block' +import { BlockConfig, BLOCKS } from '../components/block/blocks' +import { WorkflowBlock } from '../components/block/workflow-block' const ZOOM_SPEED = 0.005 const MIN_ZOOM = 0.5 @@ -10,7 +11,12 @@ const CANVAS_SIZE = 5000 // 5000px x 5000px virtual canvas export default function Workflow() { const [blocks, setBlocks] = useState< - (BlockProps & { id: string; position: { x: number; y: number } })[] + { + id: string + position: { x: number; y: number } + type: string + config: BlockConfig + }[] >([]) const [zoom, setZoom] = useState(1) const [pan, setPan] = useState({ x: 0, y: 0 }) @@ -62,7 +68,7 @@ export default function Workflow() { try { const blockData = JSON.parse( e.dataTransfer.getData('application/json') - ) as BlockProps + ) as BlockConfig // Get the canvas element's bounding rectangle const rect = e.currentTarget.getBoundingClientRect() @@ -77,7 +83,7 @@ export default function Workflow() { const x = mouseX / zoom const y = mouseY / zoom - setBlocks((prev) => [ + setBlocks((prev: any) => [ ...prev, { ...blockData, @@ -194,23 +200,20 @@ export default function Workflow() { onDragOver={handleDragOver} onDrop={handleDrop} > - {blocks.map((block) => ( -
- ))} + {blocks.map((block, index) => { + const blockConfig = + BLOCKS.find((b) => b.type === block.type) || block.config + return ( + + ) + })}
) diff --git a/app/w/components/block/blocks.ts b/app/w/components/block/blocks.ts index fcf85b3f5..1c1f63388 100644 --- a/app/w/components/block/blocks.ts +++ b/app/w/components/block/blocks.ts @@ -1,37 +1,128 @@ import { AgentIcon, ApiIcon, ConditionalIcon } from '@/components/icons' +export interface SubBlockConfig { + title: string + type: 'short-text' | 'long-text' | 'dropdown' | 'slider' | 'group' + options?: string[] // For dropdown + min?: number // For slider + max?: number // For slider + layout?: 'full' | 'half' // Controls if the block takes full width or shares space +} + export interface BlockConfig { type: string - title: string - description: string - bgColor: string - icon: any - category: 'basic' | 'advanced' + toolbar: { + title: string + description: string + bgColor: string + icon: any + category: 'basic' | 'advanced' + } + workflow: { + inputs: { [key: string]: string } + outputs: { [key: string]: string } + subBlocks: SubBlockConfig[] + } } export const BLOCKS: BlockConfig[] = [ { type: 'agent', - title: 'Agent', - description: 'Use any LLM', - bgColor: '#7F2FFF', - icon: AgentIcon, - category: 'basic', + toolbar: { + title: 'Agent', + description: 'Use any LLM', + bgColor: '#7F2FFF', + icon: AgentIcon, + category: 'basic', + }, + workflow: { + inputs: { + prompt: 'string', + }, + outputs: { + response: 'string', + }, + subBlocks: [ + { + title: 'System Prompt', + type: 'long-text', + layout: 'full', + }, + { + title: 'Model', + type: 'dropdown', + layout: 'half', + options: ['GPT-4', 'GPT-3.5', 'Claude'], + }, + { + title: 'Temperature', + type: 'slider', + layout: 'half', + min: 0, + max: 2, + }, + + ], + }, }, { type: 'api', - title: 'API', - description: 'Connect to any API', - bgColor: '#2F55FF', - icon: ApiIcon, - category: 'basic', + toolbar: { + title: 'API', + description: 'Connect to any API', + bgColor: '#2F55FF', + icon: ApiIcon, + category: 'basic', + }, + workflow: { + inputs: { + url: 'string', + method: 'string', + }, + outputs: { + response: 'string', + }, + subBlocks: [ + { + title: 'URL', + type: 'long-text', + }, + { + title: 'Method', + type: 'dropdown', + options: ['GET', 'POST', 'PUT', 'DELETE'], + }, + ], + }, }, { type: 'conditional', - title: 'Conditional', - description: 'Create branching logic', - bgColor: '#FF972F', - icon: ConditionalIcon, - category: 'basic', + toolbar: { + title: 'Conditional', + description: 'Create branching logic', + bgColor: '#FF972F', + icon: ConditionalIcon, + category: 'basic', + }, + workflow: { + inputs: { + // Add conditional-specific inputs + }, + outputs: { + // Add conditional-specific outputs + }, + subBlocks: [ + { + title: 'Condition', + type: 'dropdown', + options: ['True', 'False'], + }, + { + title: 'Action', + type: 'dropdown', + options: ['Do Something', 'Do Nothing'], + }, + ], + }, }, ] \ No newline at end of file diff --git a/app/w/components/block/sub-block.tsx b/app/w/components/block/sub-block.tsx new file mode 100644 index 000000000..949066768 --- /dev/null +++ b/app/w/components/block/sub-block.tsx @@ -0,0 +1,108 @@ +import { Input } from '@/components/ui/input' +import { SubBlockConfig } from './blocks' +import { Textarea } from '@/components/ui/textarea' +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select' +import { Slider } from '@/components/ui/slider' +import { Label } from '@/components/ui/label' +import { useState } from 'react' + +interface SubBlockProps { + config: SubBlockConfig +} + +export function SubBlock({ config }: SubBlockProps) { + const [sliderValue, setSliderValue] = useState( + config.type === 'slider' + ? (config.min || 0) + ((config.max || 100) - (config.min || 0)) / 2 + : 0 + ) + + const handleMouseDown = (e: React.MouseEvent) => { + e.stopPropagation() + } + + const renderInput = () => { + switch (config.type) { + case 'short-text': + return + case 'long-text': + return