From 3d147dc8bf708506349621a181bf53f17bc198e8 Mon Sep 17 00:00:00 2001 From: Emir Karabeg Date: Thu, 30 Jan 2025 19:37:13 -0800 Subject: [PATCH] Added code ability to be dropped in and added ability to prevent drop in block config --- .../components/sub-block/components/code.tsx | 72 ++++++++++++++++++- .../sub-block/components/long-input.tsx | 4 ++ .../sub-block/components/short-input.tsx | 4 ++ .../components/sub-block/sub-block.tsx | 10 ++- blocks/blocks/agent.ts | 3 +- blocks/types.ts | 1 + 6 files changed, 90 insertions(+), 4 deletions(-) diff --git a/app/w/components/workflow-block/components/sub-block/components/code.tsx b/app/w/components/workflow-block/components/sub-block/components/code.tsx index 3ef2f4b945..94e5aadc7e 100644 --- a/app/w/components/workflow-block/components/sub-block/components/code.tsx +++ b/app/w/components/workflow-block/components/sub-block/components/code.tsx @@ -29,6 +29,7 @@ const MATCHING_PAIRS: Record = { interface CodeProps { blockId: string subBlockId: string + isConnecting: boolean } function useCodeLines(blockId: string, subBlockId: string) { @@ -72,6 +73,64 @@ function useCodeLines(blockId: string, subBlockId: string) { }) }, []) + const handleDrop = useCallback( + (lineIndex: number, e: React.DragEvent) => { + e.preventDefault() + try { + const data = JSON.parse(e.dataTransfer.getData('application/json')) + + const isValidConnectionBlock = + data.type === 'connectionBlock' && + data.connectionData.sourceBlockId === blockId + + if (!isValidConnectionBlock) return + + const textarea = e.currentTarget + const start = textarea.selectionStart + const end = textarea.selectionEnd + const currentContent = lines[lineIndex].content + + const connectionName = data.connectionData.name + .replace(/\s+/g, '') + .toLowerCase() + const outputSuffix = + data.connectionData.outputType === 'any' + ? 'res' + : data.connectionData.outputType + + const tag = `<${connectionName}.${outputSuffix}>` + const newContent = + currentContent.substring(0, start) + + tag + + currentContent.substring(end) + + setLines((prevLines) => { + const newLines = [...prevLines] + newLines[lineIndex] = { + ...newLines[lineIndex], + content: newContent, + } + return newLines + }) + + // Set cursor position after the inserted tag + setTimeout(() => { + textarea.selectionStart = textarea.selectionEnd = start + tag.length + }, 0) + } catch (error) { + console.error('Failed to parse drop data:', error) + } + }, + [blockId, lines, setLines] + ) + + const handleDragOver = useCallback( + (e: React.DragEvent) => { + e.preventDefault() // Required to allow drops + }, + [] + ) + return { lines, setLines, @@ -80,10 +139,12 @@ function useCodeLines(blockId: string, subBlockId: string) { handleChange, selection, setSelection, + handleDrop, + handleDragOver, } } -export function Code({ blockId, subBlockId }: CodeProps) { +export function Code({ blockId, subBlockId, isConnecting }: CodeProps) { const { lines, setLines, @@ -92,6 +153,8 @@ export function Code({ blockId, subBlockId }: CodeProps) { handleChange, selection, setSelection, + handleDrop, + handleDragOver, } = useCodeLines(blockId, subBlockId) const textareaRefs = useRef<(HTMLTextAreaElement | null)[]>([]) const scrollContainerRef = useRef(null) @@ -591,7 +654,10 @@ export function Code({ blockId, subBlockId }: CodeProps) { return (
@@ -634,6 +700,8 @@ export function Code({ blockId, subBlockId }: CodeProps) { onChange={(e) => handleChange(i, e.target.value)} onKeyDown={handleKeyDown} onFocus={() => setCurrentLine(i)} + onDrop={(e) => handleDrop(i, e)} + onDragOver={handleDragOver} className={cn( textareaClassName, 'overflow-hidden w-full', diff --git a/app/w/components/workflow-block/components/sub-block/components/long-input.tsx b/app/w/components/workflow-block/components/sub-block/components/long-input.tsx index 87e4c69df8..49c40c417f 100644 --- a/app/w/components/workflow-block/components/sub-block/components/long-input.tsx +++ b/app/w/components/workflow-block/components/sub-block/components/long-input.tsx @@ -2,12 +2,14 @@ import { Textarea } from '@/components/ui/textarea' import { useSubBlockValue } from '../hooks/use-sub-block-value' import { cn } from '@/lib/utils' import { useState, useRef, useEffect } from 'react' +import { SubBlockConfig } from '@/blocks/types' interface LongInputProps { placeholder?: string blockId: string subBlockId: string isConnecting: boolean + config: SubBlockConfig } export function LongInput({ @@ -15,6 +17,7 @@ export function LongInput({ blockId, subBlockId, isConnecting, + config, }: LongInputProps) { const [value, setValue] = useSubBlockValue(blockId, subBlockId) const textareaRef = useRef(null) @@ -86,6 +89,7 @@ export function LongInput({ className={cn( 'w-full resize-none placeholder:text-muted-foreground/50 allow-scroll text-transparent caret-foreground break-words whitespace-pre-wrap', isConnecting && + config?.droppable !== false && 'focus-visible:ring-blue-500 ring-2 ring-blue-500 ring-offset-2' )} rows={4} diff --git a/app/w/components/workflow-block/components/sub-block/components/short-input.tsx b/app/w/components/workflow-block/components/sub-block/components/short-input.tsx index e3a6031f34..b1f9184b89 100644 --- a/app/w/components/workflow-block/components/sub-block/components/short-input.tsx +++ b/app/w/components/workflow-block/components/sub-block/components/short-input.tsx @@ -2,6 +2,7 @@ import { Input } from '@/components/ui/input' import { useState, useRef, useEffect } from 'react' import { useSubBlockValue } from '../hooks/use-sub-block-value' import { cn } from '@/lib/utils' +import { SubBlockConfig } from '@/blocks/types' interface ShortInputProps { placeholder?: string @@ -9,6 +10,7 @@ interface ShortInputProps { blockId: string subBlockId: string isConnecting: boolean + config: SubBlockConfig } export function ShortInput({ @@ -17,6 +19,7 @@ export function ShortInput({ placeholder, password, isConnecting, + config, }: ShortInputProps) { const [isFocused, setIsFocused] = useState(false) const [value, setValue] = useSubBlockValue(blockId, subBlockId) @@ -107,6 +110,7 @@ export function ShortInput({ className={cn( 'w-full placeholder:text-muted-foreground/50 allow-scroll text-transparent caret-foreground', isConnecting && + config?.droppable !== false && 'focus-visible:ring-blue-500 ring-2 ring-blue-500 ring-offset-2' )} placeholder={placeholder ?? ''} diff --git a/app/w/components/workflow-block/components/sub-block/sub-block.tsx b/app/w/components/workflow-block/components/sub-block/sub-block.tsx index c72329c9bb..176cdff96b 100644 --- a/app/w/components/workflow-block/components/sub-block/sub-block.tsx +++ b/app/w/components/workflow-block/components/sub-block/sub-block.tsx @@ -29,6 +29,7 @@ export function SubBlock({ blockId, config, isConnecting }: SubBlockProps) { placeholder={config.placeholder} password={config.password} isConnecting={isConnecting} + config={config} /> ) case 'long-input': @@ -38,6 +39,7 @@ export function SubBlock({ blockId, config, isConnecting }: SubBlockProps) { subBlockId={config.id} placeholder={config.placeholder} isConnecting={isConnecting} + config={config} /> ) case 'dropdown': @@ -71,7 +73,13 @@ export function SubBlock({ blockId, config, isConnecting }: SubBlockProps) { /> ) case 'code': - return + return ( + + ) case 'switch': return ( = { type: "short-input", layout: "full", placeholder: "Enter your API key", - password: true + password: true, + droppable: false }, { id: 'responseFormat', diff --git a/blocks/types.ts b/blocks/types.ts index 65c7c7c0da..4fb43f01b7 100644 --- a/blocks/types.ts +++ b/blocks/types.ts @@ -45,6 +45,7 @@ export interface SubBlockConfig { columns?: string[] placeholder?: string password?: boolean + droppable?: boolean } export interface BlockConfig {