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 c3efc7acc0..5a04e35f5e 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 @@ -21,6 +21,11 @@ export function ShortInput({ const inputRef = useRef(null) const [isFocused, setIsFocused] = useState(false) const [value, setValue] = useSubBlockValue(blockId, subBlockId) + const [connections, setConnections] = useState([]) + + useEffect(() => { + console.log(connections) + }, [connections]) useEffect(() => { if (inputRef.current && isFocused) { @@ -30,6 +35,22 @@ export function ShortInput({ } }, [value, isFocused]) + // Add regex pattern for connection syntax + const connectionPattern = /<([a-z0-9]+)\.(string|number|boolean|res|any)>/g + + const updateConnections = (inputValue: string) => { + const newConnections = Array.from( + inputValue.matchAll(connectionPattern) + ).map((match) => match[0].slice(1, -1)) // Remove < and > + setConnections(Array.from(new Set(newConnections))) // Use Set to ensure uniqueness + } + + const handleChange = (e: React.ChangeEvent) => { + const newValue = e.target.value + setValue(newValue) + updateConnections(newValue) + } + const handleDrop = (e: React.DragEvent) => { e.preventDefault() try { @@ -39,13 +60,17 @@ export function ShortInput({ data.connectionData.sourceBlockId === blockId ) { const currentValue = value?.toString() ?? '' - const newValue = - currentValue + - data.connectionData.name.replace(' ', '').toLowerCase() + - (data.connectionData.outputType === 'any' - ? '.res' - : `.${data.connectionData.outputType}`) + const formattedName = data.connectionData.name + .replace(' ', '') + .toLowerCase() + const connectionType = + data.connectionData.outputType === 'any' + ? 'res' + : data.connectionData.outputType + const newConnection = formattedName + '.' + connectionType + const newValue = currentValue + `<${newConnection}>` setValue(newValue) + updateConnections(newValue) } } catch (error) { console.error('Failed to parse drop data:', error) @@ -56,6 +81,28 @@ export function ShortInput({ e.preventDefault() // This is needed to allow drops } + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Backspace' && inputRef.current) { + const cursorPosition: any = inputRef.current.selectionStart + const currentValue = value?.toString() ?? '' + + // Check if cursor is right after a connection closing bracket + for (const connection of connections) { + const pattern = `<${connection}>` + const index = currentValue.lastIndexOf(pattern, cursorPosition) + + if (index !== -1 && index + pattern.length === cursorPosition) { + e.preventDefault() + const newValue = + currentValue.slice(0, index) + + currentValue.slice(index + pattern.length) + setValue(newValue) + return + } + } + } + } + const displayValue = password && !isFocused ? '•'.repeat(value?.toString().length ?? 0) @@ -71,7 +118,9 @@ export function ShortInput({ placeholder={placeholder ?? ''} type="text" value={displayValue} - onChange={(e) => setValue(e.target.value)} + connections={connections} + onChange={handleChange} + onKeyDown={handleKeyDown} onFocus={() => setIsFocused(true)} onBlur={() => setIsFocused(false)} onDrop={handleDrop} diff --git a/components/ui/input.tsx b/components/ui/input.tsx index 68551b9276..76ec7f300b 100644 --- a/components/ui/input.tsx +++ b/components/ui/input.tsx @@ -1,22 +1,110 @@ -import * as React from "react" +import * as React from 'react' -import { cn } from "@/lib/utils" +import { cn } from '@/lib/utils' -const Input = React.forwardRef>( - ({ className, type, ...props }, ref) => { - return ( - - ) +interface InputProps + extends Omit, 'value' | 'onChange'> { + className?: string + value?: string + connections?: string[] + onChange?: (e: React.ChangeEvent) => void +} + +const Input = React.forwardRef( + ( + { className, type, value = '', connections = [], onChange, ...props }, + ref + ) => { + // Create a hidden input for maintaining focus and handling keyboard events + const hiddenInputRef = React.useRef(null) + + const renderContent = () => { + if (!connections?.length) { + return ( + + ) + } + + // Split text by connection patterns + const parts = [] + let lastIndex = 0 + + connections.forEach((connection) => { + const pattern = `<${connection}>` + const index = value.indexOf(pattern, lastIndex) + + if (index !== -1) { + // Add text before connection + if (index > lastIndex) { + parts.push({ + type: 'text', + content: value.slice(lastIndex, index), + }) + } + // Add connection + parts.push({ + type: 'connection', + content: connection, + }) + lastIndex = index + pattern.length + } + }) + + // Add remaining text + if (lastIndex < value.length) { + parts.push({ + type: 'text', + content: value.slice(lastIndex), + }) + } + + return ( +
hiddenInputRef.current?.focus()} + > +
+ {parts.map((part, index) => + part.type === 'connection' ? ( + + {part.content} + + ) : ( + {part.content} + ) + )} + +
+
+ ) + } + + return renderContent() } ) -Input.displayName = "Input" +Input.displayName = 'Input' export { Input } diff --git a/stores/workflow/types.ts b/stores/workflow/types.ts index c1b9dee369..2586419193 100644 --- a/stores/workflow/types.ts +++ b/stores/workflow/types.ts @@ -14,20 +14,10 @@ export interface BlockState { subBlocks: Record outputType: OutputType } - -export interface Connection { - id: string - blockId: string - outputType: string - startIndex: number - endIndex: number -} - export interface SubBlockState { id: string type: SubBlockType value: string | number | string[][] | null - connections?: Connection[] } export interface WorkflowState {