mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-08 22:48:14 -05:00
Added code ability to be dropped in and added ability to prevent drop in block config
This commit is contained in:
@@ -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',
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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 ?? ''}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -45,6 +45,7 @@ export interface SubBlockConfig {
|
||||
columns?: string[]
|
||||
placeholder?: string
|
||||
password?: boolean
|
||||
droppable?: boolean
|
||||
}
|
||||
|
||||
export interface BlockConfig<T extends ToolResponse = ToolResponse> {
|
||||
|
||||
Reference in New Issue
Block a user