Added code ability to be dropped in and added ability to prevent drop in block config

This commit is contained in:
Emir Karabeg
2025-01-30 19:37:13 -08:00
parent cb4d5f8da6
commit 3d147dc8bf
6 changed files with 90 additions and 4 deletions

View File

@@ -29,6 +29,7 @@ const MATCHING_PAIRS: Record<string, string> = {
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<HTMLTextAreaElement>) => {
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<HTMLTextAreaElement>) => {
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<HTMLDivElement>(null)
@@ -591,7 +654,10 @@ export function Code({ blockId, subBlockId }: CodeProps) {
return (
<div
ref={containerRef}
className="font-mono text-sm border rounded-md overflow-hidden relative"
className={cn(
'font-mono text-sm border rounded-md overflow-hidden relative',
isConnecting && 'ring-2 ring-blue-500 ring-offset-2'
)}
onWheel={handleScroll}
>
<div className="absolute top-0 left-0 z-50 h-full bg-background">
@@ -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',

View File

@@ -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<HTMLTextAreaElement>(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}

View File

@@ -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 ?? ''}

View File

@@ -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 <Code blockId={blockId} subBlockId={config.id} />
return (
<Code
blockId={blockId}
subBlockId={config.id}
isConnecting={isConnecting}
/>
)
case 'switch':
return (
<Switch

View File

@@ -97,7 +97,8 @@ export const AgentBlock: BlockConfig<ChatResponse> = {
type: "short-input",
layout: "full",
placeholder: "Enter your API key",
password: true
password: true,
droppable: false
},
{
id: 'responseFormat',

View File

@@ -45,6 +45,7 @@ export interface SubBlockConfig {
columns?: string[]
placeholder?: string
password?: boolean
droppable?: boolean
}
export interface BlockConfig<T extends ToolResponse = ToolResponse> {