"use client"; import { useEffect, useRef, useState, memo, useCallback } from "react"; import { Textarea } from "@kandev/ui/textarea"; import { Combobox } from "./combobox"; const CURSOR_POINTER_CLASS = "cursor-pointer"; type RepositoryOption = { value: string; label: string; renderLabel: () => React.ReactNode; }; type RepositorySelectorProps = { options: RepositoryOption[]; value: string; onValueChange: (value: string) => void; disabled: boolean; placeholder: string; searchPlaceholder: string; emptyMessage: string; triggerClassName?: string; }; export const RepositorySelector = memo(function RepositorySelector({ options, value, onValueChange, disabled, placeholder, searchPlaceholder, emptyMessage, triggerClassName, }: RepositorySelectorProps) { return ( ); }); type BranchOption = { value: string; label: string; renderLabel: () => React.ReactNode; }; type BranchSelectorProps = { options: BranchOption[]; value: string; onValueChange: (value: string) => void; disabled: boolean; placeholder: string; searchPlaceholder: string; emptyMessage: string; }; export const BranchSelector = memo(function BranchSelector({ options, value, onValueChange, disabled, placeholder, searchPlaceholder, emptyMessage, }: BranchSelectorProps) { return ( ); }); type AgentSelectorProps = { options: Array<{ value: string; label: string; renderLabel: () => React.ReactNode }>; value: string; onValueChange: (value: string) => void; disabled: boolean; placeholder: string; triggerClassName?: string; }; export const AgentSelector = memo(function AgentSelector({ options, value, onValueChange, disabled, placeholder, triggerClassName, }: AgentSelectorProps) { return ( ); }); type ExecutorSelectorProps = { options: Array<{ value: string; label: string; renderLabel?: () => React.ReactNode }>; value: string; onValueChange: (value: string) => void; disabled: boolean; placeholder: string; triggerClassName?: string; }; export const ExecutorSelector = memo(function ExecutorSelector({ options, value, onValueChange, disabled, placeholder, triggerClassName, }: ExecutorSelectorProps) { return ( ); }); type ExecutorProfileSelectorProps = { options: Array<{ value: string; label: string; renderLabel?: () => React.ReactNode }>; value: string; onValueChange: (value: string) => void; disabled: boolean; placeholder: string; triggerClassName?: string; }; export const ExecutorProfileSelector = memo(function ExecutorProfileSelector({ options, value, onValueChange, disabled, placeholder, triggerClassName, }: ExecutorProfileSelectorProps) { return ( ); }); type InlineTaskNameProps = { value: string; onChange: (value: string) => void; autoFocus?: boolean; }; export const InlineTaskName = memo(function InlineTaskName({ value, onChange, autoFocus, }: InlineTaskNameProps) { const inputRef = useRef(null); useEffect(() => { if (autoFocus && inputRef.current) { inputRef.current.focus(); inputRef.current.select(); } }, [autoFocus]); return ( onChange(e.target.value)} placeholder="task-name" data-testid="task-title-input" size={Math.max(value.length, 9)} className="bg-transparent border-none outline-none focus:ring-0 text-sm font-medium min-w-0 rounded-md px-1.5 py-0.5 -mx-1.5 hover:bg-muted focus:bg-muted transition-colors" /> ); }); // Memoized description input to prevent re-rendering the entire dialog on every keystroke type TaskFormInputsProps = { isSessionMode: boolean; autoFocus?: boolean; initialDescription: string; onDescriptionChange: (hasContent: boolean) => void; onKeyDown: (e: React.KeyboardEvent) => void; descriptionValueRef: React.RefObject<{ getValue: () => string } | null>; disabled?: boolean; placeholder?: string; }; export const TaskFormInputs = memo(function TaskFormInputs({ isSessionMode, autoFocus, initialDescription, onDescriptionChange, onKeyDown, descriptionValueRef, disabled, placeholder, }: TaskFormInputsProps) { const [description, setDescription] = useState(initialDescription); const textareaRef = useRef(null); useEffect(() => { const ref = descriptionValueRef as React.MutableRefObject<{ getValue: () => string } | null>; if (ref) { ref.current = { getValue: () => description }; } }, [description, descriptionValueRef]); // Auto-resize textarea + optional auto-focus with cursor at end useEffect(() => { const textarea = textareaRef.current; if (!textarea) return; textarea.style.height = "auto"; textarea.style.height = `${textarea.scrollHeight}px`; }, [description]); useEffect(() => { if (!autoFocus) return; const textarea = textareaRef.current; if (!textarea) return; textarea.focus(); textarea.setSelectionRange(textarea.value.length, textarea.value.length); }, [autoFocus]); const handleDescriptionChange = useCallback( (e: React.ChangeEvent) => { const newValue = e.target.value; const hadContent = description.trim().length > 0; const hasContent = newValue.trim().length > 0; setDescription(newValue); if (hadContent !== hasContent) { onDescriptionChange(hasContent); } }, [description, onDescriptionChange], ); return (