@humain/ui 2.4.0 → 2.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index150.js +1 -1
- package/dist/index150.js.map +1 -1
- package/dist/wc/AIInput.js +31 -31
- package/dist/wc/AIInput.js.map +1 -1
- package/dist/wc-react/AIInput.js +1 -1
- package/dist/wc-react/AIInput.js.map +1 -1
- package/package.json +1 -1
- package/skills/humain-ui/SKILL.md +1 -1
- package/skills/humain-ui-wc/SKILL.md +1 -1
- package/skills/humain-ui-wc.skill +0 -0
- package/skills/humain-ui.skill +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AIInput.js","names":[],"sources":["../../src/components/organisms/ai/AIInput/paste-detection.ts","../../src/components/organisms/ai/AIInput/AIInput.tsx"],"sourcesContent":["/**\n * Paste-detection heuristics for the AIInput pasteAsFile feature.\n * Pure module — no React, no DOM, no I/O.\n */\n\nexport type PasteKind = 'json' | 'xml' | 'csv' | 'log' | 'code' | 'text';\n\nexport interface DetectionOpts {\n /** Char count above which plain text always converts. Default: 500 */\n charThreshold?: number;\n /** Line count above which plain text always converts. Default: 10 */\n lineThreshold?: number;\n /** Run structured-content heuristics. Default: true */\n detectStructured?: boolean;\n}\n\nexport interface PasteAnalysis {\n shouldConvert: boolean;\n kind: PasteKind;\n}\n\nconst LARGE_TEXT_CHAR_THRESHOLD = 500;\nconst LARGE_TEXT_LINE_THRESHOLD = 10;\nconst STRUCTURED_DETECTION_MAX = 1_000_000;\n\nconst CODE_PATTERN =\n /^(import |from |export |function |class |const |let |var |def |package )/m;\nconst TIMESTAMP_PATTERN = /^\\d{4}-\\d{2}-\\d{2}[\\sT]\\d{2}:\\d{2}:\\d{2}/m;\nconst STACK_TRACE_PATTERN = /^\\s+at\\s+/m;\n\nexport const KIND_TO_FILE: Readonly<\n Record<PasteKind, { ext: string; mime: string }>\n> = Object.freeze({\n json: { ext: 'json', mime: 'application/json' },\n xml: { ext: 'xml', mime: 'application/xml' },\n csv: { ext: 'csv', mime: 'text/csv' },\n log: { ext: 'log', mime: 'text/plain' },\n code: { ext: 'txt', mime: 'text/plain' },\n text: { ext: 'txt', mime: 'text/plain' },\n});\n\nexport function mapKindToFile(kind: PasteKind): { ext: string; mime: string } {\n return KIND_TO_FILE[kind];\n}\n\n/**\n * Single-pass paste analysis: splits lines once, runs each detector at most once.\n * Used by the hook to avoid the duplicate work of calling shouldConvertPaste\n * and detectPasteKind separately.\n */\nexport function analyzePaste(\n text: string,\n opts: DetectionOpts = {},\n): PasteAnalysis {\n if (!text) return { shouldConvert: false, kind: 'text' };\n\n const charThreshold = opts.charThreshold ?? LARGE_TEXT_CHAR_THRESHOLD;\n const lineThreshold = opts.lineThreshold ?? LARGE_TEXT_LINE_THRESHOLD;\n const detectStructured = opts.detectStructured ?? true;\n\n const sizeTriggered = text.length > charThreshold;\n const lines = text.split('\\n');\n const lineTriggered = lines.length > lineThreshold;\n const canDetect = detectStructured && text.length <= STRUCTURED_DETECTION_MAX;\n\n let kind: PasteKind = 'text';\n if (canDetect) {\n if (isJson(text)) kind = 'json';\n else if (isXmlOrHtml(text)) kind = 'xml';\n else if (isCode(text, lines)) kind = 'code';\n else if (isCsv(lines)) kind = 'csv';\n else if (isLogOutput(text, lines)) kind = 'log';\n }\n\n const shouldConvert = sizeTriggered || lineTriggered || kind !== 'text';\n return { shouldConvert, kind };\n}\n\n/**\n * Decide whether the pasted text should convert to a file attachment.\n * Thin wrapper over analyzePaste — kept for the public API and consumers\n * who only need the boolean.\n */\nexport function shouldConvertPaste(\n text: string,\n opts: DetectionOpts = {},\n): boolean {\n return analyzePaste(text, opts).shouldConvert;\n}\n\n/**\n * Classify the paste kind. Returns 'text' when detection is disabled or the\n * text exceeds STRUCTURED_DETECTION_MAX (parse cost guard).\n */\nexport function detectPasteKind(\n text: string,\n opts: DetectionOpts = {},\n): PasteKind {\n return analyzePaste(text, opts).kind;\n}\n\nfunction isJson(text: string): boolean {\n const trimmed = text.trimStart();\n if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) return false;\n try {\n JSON.parse(trimmed);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction isXmlOrHtml(text: string): boolean {\n const trimmed = text.trimStart();\n if (!trimmed.startsWith('<')) return false;\n return /<\\/\\w+>/.test(trimmed);\n}\n\nfunction isCode(text: string, lines: string[]): boolean {\n if (lines.length < 2) return false;\n return CODE_PATTERN.test(text);\n}\n\nfunction isCsv(lines: string[]): boolean {\n const nonEmpty = lines.filter((line) => line.trim().length > 0);\n if (nonEmpty.length < 3) return false;\n\n for (const delimiter of [',', '\\t']) {\n const columnCounts = nonEmpty.map((line) => line.split(delimiter).length);\n const firstCount = columnCounts[0];\n if (firstCount < 2) continue;\n if (!columnCounts.every((c) => c === firstCount)) continue;\n // For comma delimiter, require min 2 commas per line. With consistent\n // column counts, comma count = firstCount - 1, so firstCount >= 3.\n if (delimiter === ',' && firstCount < 3) continue;\n return true;\n }\n return false;\n}\n\nfunction isLogOutput(text: string, lines: string[]): boolean {\n if (lines.length < 2) return false;\n const timestampedLines = lines.filter((l) => TIMESTAMP_PATTERN.test(l));\n if (timestampedLines.length >= 2) return true;\n return STACK_TRACE_PATTERN.test(text);\n}\n","import { cva, type VariantProps } from 'class-variance-authority';\nimport { ArrowDown, ArrowUp, X } from 'lucide-react';\nimport * as React from 'react';\nimport { Button } from '@/components/atoms/buttons/Button';\nimport { FileUploadItem } from '@/components/molecules/files/FileUpload';\nimport {\n matchesAcceptToken,\n toFileList,\n useDropZone,\n} from '@/hooks/use-drop-zone';\nimport { createSafeContext } from '@/lib/create-safe-context';\nimport { cn } from '@/lib/utils';\nimport { analyzePaste, mapKindToFile, type PasteKind } from './paste-detection';\n\n// =============================================================================\n// Shared Styles\n// =============================================================================\n\nconst textareaClasses =\n 'flex-1 h-[40px] resize-none overflow-y-auto bg-transparent text-base text-foreground placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:text-muted-foreground';\n\nconst compactTextareaClasses =\n 'flex-1 h-[32px] resize-none overflow-y-auto bg-transparent text-base leading-6 text-foreground placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:text-muted-foreground';\n\n// =============================================================================\n// Variants\n// =============================================================================\n\nconst inputVariants = cva(\n 'flex items-center w-full transition-[color,background-color,border-color,box-shadow,opacity]',\n {\n variants: {\n state: {\n default: '',\n focused: '',\n withText: '',\n disabled: 'cursor-not-allowed',\n attachments: '',\n dragover: '',\n },\n variant: {\n default: 'flex-col gap-2 py-4 px-4 rounded-[inherit]',\n compact: 'flex-row gap-2 py-4 px-4 rounded-[inherit]',\n inline: 'flex-row gap-2 px-4 py-4 rounded-[inherit]',\n },\n },\n defaultVariants: {\n state: 'default',\n variant: 'default',\n },\n },\n);\n\nfunction getAIInputShellClassName({\n state,\n variant,\n className,\n}: {\n state: VariantProps<typeof inputVariants>['state'];\n variant: 'default' | 'compact' | 'inline';\n className?: string;\n}) {\n const isDefault = variant === 'default';\n const isDisabled = state === 'disabled';\n const isPrimaryState =\n state === 'focused' ||\n state === 'withText' ||\n state === 'attachments' ||\n state === 'dragover';\n const isStrongState =\n state === 'focused' || state === 'withText' || state === 'dragover';\n\n return cn(\n 'relative w-full overflow-hidden border bg-background shadow-[var(--shadow-ai-input)] transition-[color,background-color,border-color,box-shadow,opacity] duration-150 ease-out',\n isDefault ? 'rounded-3xl max-w-[772px]' : 'rounded-2xl',\n isDisabled\n ? 'border-border bg-muted'\n : isPrimaryState\n ? 'border-primary'\n : 'border-ring/50 hover:border-primary',\n isStrongState ? 'border-2' : 'border-[1.25px]',\n className,\n );\n}\n\n// =============================================================================\n// Drop Overlay (shared across all variants)\n// =============================================================================\n\nfunction DropOverlay({\n isDefault,\n dropText,\n}: {\n isDefault: boolean;\n dropText: string;\n}) {\n return (\n <div\n data-slot=\"ai-input-drop-overlay\"\n role=\"status\"\n aria-live=\"polite\"\n className={cn(\n 'delight-drop-enter absolute inset-1.5 border-2 border-dashed border-primary/50 dark:border-primary/60 pointer-events-none flex flex-col items-center justify-center gap-1.5 z-10',\n isDefault ? 'rounded-[calc(1.5rem-8px)]' : 'rounded-[calc(1rem-8px)]',\n )}\n >\n <ArrowDown className=\"size-4 text-primary/70 delight-drop-icon\" />\n <span className=\"text-sm text-muted-foreground font-medium\">\n {dropText}\n </span>\n </div>\n );\n}\n\nDropOverlay.displayName = 'AIInput.DropOverlay';\n\n// =============================================================================\n// File Attachment Types\n// =============================================================================\n\nexport interface UploadingFile {\n id: string;\n name: string;\n size: string;\n progress: number;\n status: 'uploading' | 'complete' | 'error';\n /** File extension for icon display (e.g., 'pdf', 'docx') */\n fileExtension?: string;\n}\n\n// =============================================================================\n// Paste-as-File public types\n// =============================================================================\n\nexport type { PasteKind } from './paste-detection';\n\nexport interface PasteAsFileConfig {\n /** Char count above which plain text always converts. Default: 500 */\n threshold?: number;\n /** Line count above which plain text always converts. Default: 10 */\n lineThreshold?: number;\n /** Run structured-content heuristics (JSON/XML/CSV/code/log). Default: true */\n detectStructured?: boolean;\n /**\n * Auto-fill textarea after conversion. Receives filename + detected kind.\n * Return string to set as textarea value (appended with leading space if textarea non-empty).\n * Empty/whitespace-only return is treated as no-op.\n * Default: undefined → textarea stays empty.\n */\n prefillTemplate?: (filename: string, kind: PasteKind) => string;\n /** Fired after onFilesSelected and prefill. */\n onConverted?: (filename: string, kind: PasteKind) => void;\n}\n\n/**\n * Accepts boolean for runtime toggling. `false` and `undefined` both disable.\n * `true` enables with defaults. Object enables with overrides.\n */\nexport type PasteAsFileProp = boolean | PasteAsFileConfig;\n\n// =============================================================================\n// usePasteAsFile — internal hook\n// =============================================================================\n\ninterface UsePasteAsFileOpts {\n pasteAsFile: PasteAsFileProp | undefined;\n onFilesSelected: ((files: FileList) => void) | undefined;\n accept: string | undefined;\n disabled: boolean;\n variant: 'default' | 'compact' | 'inline';\n value: string;\n setValue: (next: string) => void;\n}\n\nfunction usePasteAsFile(opts: UsePasteAsFileOpts): {\n onPaste?: React.ClipboardEventHandler<HTMLTextAreaElement>;\n} {\n const counterRef = React.useRef(0);\n const warnedRef = React.useRef(false);\n const optsRef = React.useRef(opts);\n\n // Keep the ref pointing at the latest opts each render. This pattern lets the\n // returned `onPaste` be referentially stable (empty-deps useCallback) while\n // still seeing the freshest pasteAsFile config and callbacks.\n React.useEffect(() => {\n optsRef.current = opts;\n });\n\n const onPaste = React.useCallback<\n React.ClipboardEventHandler<HTMLTextAreaElement>\n >((e) => {\n const { pasteAsFile, onFilesSelected, accept, disabled, value, setValue } =\n optsRef.current;\n\n if (!pasteAsFile || disabled) return;\n\n const text = e.clipboardData.getData('text/plain');\n if (!text) return;\n\n const config: PasteAsFileConfig = pasteAsFile === true ? {} : pasteAsFile;\n\n const detectionOpts = {\n charThreshold: config.threshold,\n lineThreshold: config.lineThreshold,\n detectStructured: config.detectStructured,\n };\n\n const { shouldConvert, kind } = analyzePaste(text, detectionOpts);\n if (!shouldConvert) return;\n\n if (!onFilesSelected) {\n if (process.env.NODE_ENV !== 'production' && !warnedRef.current) {\n console.warn(\n '[AIInput] pasteAsFile is set but onFilesSelected is missing. Paste-to-file is a no-op.',\n );\n warnedRef.current = true;\n }\n return;\n }\n\n const { ext, mime } = mapKindToFile(kind);\n const nProspective = counterRef.current + 1;\n const filename = `Pasted-${nProspective}.${ext}`;\n\n if (\n accept &&\n !accept\n .split(',')\n .some((token) =>\n matchesAcceptToken({ name: filename, type: mime }, token),\n )\n ) {\n return;\n }\n\n let fileList: FileList;\n try {\n const file = new File([text], filename, { type: mime });\n fileList = toFileList([file]);\n } catch {\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n '[AIInput] DataTransfer unavailable; falling back to native paste.',\n );\n }\n return;\n }\n\n e.preventDefault();\n counterRef.current = nProspective;\n onFilesSelected(fileList);\n\n if (config.prefillTemplate) {\n const tpl = config.prefillTemplate(filename, kind);\n if (tpl?.trim()) {\n setValue(value ? `${value} ${tpl}` : tpl);\n }\n }\n\n config.onConverted?.(filename, kind);\n }, []);\n\n if (opts.variant === 'inline' || !opts.pasteAsFile) {\n return { onPaste: undefined };\n }\n\n return { onPaste };\n}\n\n// =============================================================================\n// Context\n// =============================================================================\n\ninterface AIInputContextValue {\n /** Current input value */\n value: string;\n /** Whether the input has text */\n hasText: boolean;\n /** Whether the input is focused */\n isFocused: boolean;\n /** Whether the input is disabled */\n disabled: boolean;\n /** Visual variant */\n variant: 'default' | 'compact' | 'inline';\n /** Files being uploaded */\n uploadingFiles: UploadingFile[];\n /** Has attachments */\n hasAttachments: boolean;\n /** Update input value */\n setValue: (value: string) => void;\n /** Set focus state */\n setIsFocused: (focused: boolean) => void;\n /** Handle submit */\n handleSubmit: () => void;\n /** Handle key down */\n handleKeyDown: (\n e: React.KeyboardEvent<HTMLTextAreaElement | HTMLInputElement>,\n ) => void;\n /** Handle clear */\n handleClear: () => void;\n /** Paste handler from usePasteAsFile (only set when paste-as-file is active). */\n onPaste?: React.ClipboardEventHandler<HTMLTextAreaElement>;\n /** Remove file */\n onRemoveFile?: (fileId: string) => void;\n /** Cancel upload */\n onCancelUpload?: (fileId: string) => void;\n /** Retry failed upload */\n onRetryUpload?: (fileId: string) => void;\n /** Remove all files */\n onRemoveAllFiles?: () => void;\n /** Whether files are being dragged over the input */\n isDragging: boolean;\n /** Open the native file picker */\n openFilePicker: () => void;\n /** Callback when files are selected (via drop or picker) */\n onFilesSelected?: (files: FileList) => void;\n /** Textarea ref (for default variant) */\n textareaRef: React.RefObject<HTMLTextAreaElement | null>;\n /** Input ref (for compact variant) */\n inputRef: React.RefObject<HTMLInputElement | null>;\n}\n\nconst [AIInputContext, useAIInputContext] =\n createSafeContext<AIInputContextValue>('AIInput');\n\n// =============================================================================\n// Compound Components - Building Blocks\n// =============================================================================\n\nexport interface ActionButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /** Icon component to render */\n icon: React.ComponentType<{ className?: string }>;\n /** Tooltip text */\n tooltip?: string;\n}\n\nfunction ActionButton({\n icon: Icon,\n onClick,\n disabled,\n tooltip,\n className,\n ...props\n}: ActionButtonProps) {\n return (\n <button\n type=\"button\"\n data-slot=\"action-button\"\n onClick={onClick}\n disabled={disabled}\n title={tooltip}\n aria-label={tooltip}\n className={cn(\n 'p-2 rounded-full text-secondary-foreground hover:bg-accent transition-colors disabled:opacity-50 disabled:cursor-not-allowed',\n className,\n )}\n {...props}\n >\n <Icon className=\"size-4\" />\n </button>\n );\n}\n\nActionButton.displayName = 'AIInput.ActionButton';\n\nexport interface SubmitButtonProps\n extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {\n /** Icon component to render (defaults to ArrowUp) */\n icon?: React.ComponentType<{ className?: string }>;\n}\n\nfunction SubmitButton({\n icon: Icon = ArrowUp,\n className,\n onClick,\n disabled,\n ...props\n}: SubmitButtonProps) {\n return (\n <Button\n type=\"button\"\n data-slot=\"submit-button\"\n size=\"icon-sm\"\n onClick={onClick}\n disabled={disabled}\n aria-label=\"Send message\"\n className={cn(\n 'rounded-full bg-primary hover:bg-primary/90 text-primary-foreground disabled:bg-accent disabled:text-muted-foreground transition-colors duration-150 ease-out',\n className,\n )}\n {...props}\n >\n <Icon className=\"size-4\" />\n </Button>\n );\n}\n\nSubmitButton.displayName = 'AIInput.SubmitButton';\n\nexport interface CompactActionButtonProps\n extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {\n /** Icon component to render */\n icon: React.ComponentType<{ className?: string }>;\n /** Accessible label for the icon-only button (required for a11y) */\n 'aria-label': string;\n}\n\n/**\n * @deprecated Use `ActionButton` instead. CompactActionButton will be removed in a future major version.\n */\nfunction CompactActionButton({\n icon: Icon,\n className,\n onClick,\n disabled,\n ...props\n}: CompactActionButtonProps) {\n return (\n <Button\n type=\"button\"\n data-slot=\"compact-action-button\"\n onClick={onClick}\n disabled={disabled}\n appearance=\"outline\"\n variant=\"secondary\"\n size=\"icon\"\n className={cn(\n 'rounded-full border-border text-foreground hover:bg-muted',\n className,\n )}\n {...props}\n >\n <Icon className=\"size-5\" />\n </Button>\n );\n}\n\nCompactActionButton.displayName = 'AIInput.CompactActionButton';\n\nfunction RemoveAllButton({ onClick }: { onClick?: () => void }) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n aria-label=\"Remove all attached files\"\n className=\"flex items-center gap-1.5 h-5 px-2 py-1 rounded-full bg-accent border border-border text-xs font-medium text-foreground hover:bg-accent/80 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring transition-colors\"\n >\n <X className=\"size-4\" />\n Remove all\n </button>\n );\n}\n\nRemoveAllButton.displayName = 'AIInput.RemoveAllButton';\n\n// =============================================================================\n// Render Function Types (for monolithic API)\n// =============================================================================\n\n/** Props passed to custom submit button render function */\nexport interface SubmitButtonRenderProps {\n onClick: () => void;\n disabled: boolean;\n}\n\n/** Render function type for custom submit button */\nexport type SubmitButtonRenderFn = (\n props: SubmitButtonRenderProps,\n) => React.ReactElement;\n\n/** Props passed to custom default button render function */\nexport interface DefaultButtonRenderProps {\n disabled: boolean;\n}\n\n/** Render function type for custom default button */\nexport type DefaultButtonRenderFn = (\n props: DefaultButtonRenderProps,\n) => React.ReactElement;\n\nfunction AIInputSubmitButton({\n customButton,\n onClick,\n disabled,\n}: {\n customButton?: SubmitButtonRenderFn;\n onClick: () => void;\n disabled: boolean;\n}) {\n const buttonProps: SubmitButtonRenderProps = {\n onClick,\n disabled,\n };\n\n if (customButton) {\n return customButton(buttonProps);\n }\n\n return <SubmitButton {...buttonProps} />;\n}\n\nfunction AIInputDefaultButton({\n customButton,\n disabled,\n}: {\n customButton?: DefaultButtonRenderFn;\n disabled: boolean;\n}) {\n const buttonProps: DefaultButtonRenderProps = {\n disabled,\n };\n\n if (customButton) {\n return customButton(buttonProps);\n }\n\n return <SubmitButton icon={ArrowUp} {...buttonProps} />;\n}\n\n// =============================================================================\n// Composable Subcomponents (for dual-API)\n// =============================================================================\n\nexport interface AIInputRootProps {\n /** Children (composable subcomponents) */\n children: React.ReactNode;\n /** Visual variant of the input */\n variant?: 'default' | 'compact' | 'inline';\n /** Current input value (controlled) */\n value?: string;\n /** Default input value (uncontrolled) */\n defaultValue?: string;\n /** Callback when value changes */\n onChange?: (value: string) => void;\n /** Callback when message is submitted */\n onSubmit?: (message: string) => void;\n /** Files being uploaded */\n uploadingFiles?: UploadingFile[];\n /** Callback when file is removed */\n onRemoveFile?: (fileId: string) => void;\n /** Callback when file upload is cancelled */\n onCancelUpload?: (fileId: string) => void;\n /** Callback to retry a failed upload */\n onRetryUpload?: (fileId: string) => void;\n /** Callback to remove all files */\n onRemoveAllFiles?: () => void;\n /** Whether the input is disabled */\n disabled?: boolean;\n /** Additional class name */\n className?: string;\n /** Callback when files are selected (via drag-drop or file picker) */\n onFilesSelected?: (files: FileList) => void;\n /** Accepted file types (e.g. 'image/*,.pdf') */\n accept?: string;\n /** Allow multiple file selection */\n multiple?: boolean;\n /** Text shown in the drop overlay */\n dropText?: string;\n /** Convert large/structured paste into a file attachment. Requires onFilesSelected. */\n pasteAsFile?: PasteAsFileProp;\n}\n\nfunction AIInputRoot({\n children,\n variant = 'default',\n value: controlledValue,\n defaultValue = '',\n onChange,\n onSubmit,\n uploadingFiles = [],\n onRemoveFile,\n onCancelUpload,\n onRetryUpload,\n onRemoveAllFiles,\n disabled = false,\n className,\n onFilesSelected,\n accept,\n multiple = true,\n dropText = 'Drop files to attach',\n pasteAsFile,\n}: AIInputRootProps) {\n const [internalValue, setInternalValue] = React.useState(defaultValue);\n const [isFocused, setIsFocused] = React.useState(false);\n const textareaRef = React.useRef<HTMLTextAreaElement>(null);\n const inputRef = React.useRef<HTMLInputElement>(null);\n const rootRef = React.useRef<HTMLDivElement>(null);\n\n const { isDragging, dropZoneProps, openFilePicker } = useDropZone({\n onFilesSelected,\n accept,\n multiple,\n disabled,\n containerRef: rootRef,\n });\n\n const isControlled = controlledValue !== undefined;\n const value = isControlled ? controlledValue : internalValue;\n const hasText = value.trim().length > 0;\n const hasAttachments = uploadingFiles.length > 0;\n const setValue = React.useCallback(\n (newValue: string) => {\n if (onChange) {\n onChange(newValue);\n }\n if (!isControlled) {\n setInternalValue(newValue);\n }\n },\n [onChange, isControlled],\n );\n\n const { onPaste } = usePasteAsFile({\n pasteAsFile,\n onFilesSelected,\n accept,\n disabled,\n variant,\n value,\n setValue,\n });\n\n const handleSubmit = React.useCallback(() => {\n if (hasText && onSubmit) {\n onSubmit(value);\n if (!isControlled) {\n setInternalValue('');\n }\n }\n }, [hasText, onSubmit, value, isControlled]);\n\n const handleKeyDown = React.useCallback(\n (e: React.KeyboardEvent<HTMLTextAreaElement | HTMLInputElement>) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n handleSubmit();\n }\n },\n [handleSubmit],\n );\n\n const handleClear = React.useCallback(() => {\n setValue('');\n if (variant === 'inline') {\n inputRef.current?.focus();\n } else {\n textareaRef.current?.focus();\n }\n }, [setValue, variant]);\n\n // Determine visual state\n const getState = (): VariantProps<typeof inputVariants>['state'] => {\n if (disabled) return 'disabled';\n if (isDragging) return 'dragover';\n if (hasAttachments) return 'attachments';\n if (hasText) return 'withText';\n if (isFocused) return 'focused';\n return 'default';\n };\n\n const contextValue = React.useMemo<AIInputContextValue>(\n () => ({\n value,\n hasText,\n isFocused,\n disabled,\n variant,\n uploadingFiles,\n hasAttachments,\n setValue,\n setIsFocused,\n handleSubmit,\n handleKeyDown,\n handleClear,\n onPaste,\n onRemoveFile,\n onCancelUpload,\n onRetryUpload,\n onRemoveAllFiles,\n isDragging,\n openFilePicker,\n onFilesSelected,\n textareaRef,\n inputRef,\n }),\n [\n value,\n hasText,\n isFocused,\n disabled,\n variant,\n uploadingFiles,\n hasAttachments,\n setValue,\n handleSubmit,\n handleKeyDown,\n handleClear,\n onPaste,\n onRemoveFile,\n onCancelUpload,\n onRetryUpload,\n onRemoveAllFiles,\n isDragging,\n openFilePicker,\n onFilesSelected,\n ],\n );\n\n const isDefault = variant === 'default';\n const state = getState();\n\n return (\n <AIInputContext.Provider value={contextValue}>\n <div\n ref={rootRef}\n data-slot=\"ai-input\"\n data-dragging={isDragging}\n {...dropZoneProps}\n {...(onFilesSelected\n ? { 'aria-label': 'Chat input with file drop zone' }\n : {})}\n className={getAIInputShellClassName({ state, variant, className })}\n >\n <div\n data-slot=\"ai-input-inner\"\n className={cn(\n inputVariants({ state, variant }),\n 'relative',\n !isDefault && hasAttachments && 'flex-col items-stretch py-3',\n disabled && !isDefault ? 'bg-muted' : 'bg-background',\n 'rounded-[inherit]',\n disabled && isDefault && 'bg-muted',\n )}\n >\n {isDragging && (\n <DropOverlay isDefault={isDefault} dropText={dropText} />\n )}\n {children}\n </div>\n </div>\n </AIInputContext.Provider>\n );\n}\n\nAIInputRoot.displayName = 'AIInput.Root';\n\n// =============================================================================\n// AIInput.TextArea - The text input area\n// =============================================================================\n\nexport interface AIInputTextAreaProps {\n /** Placeholder text */\n placeholder?: string;\n /** Additional class name */\n className?: string;\n /** Show clear button when text is present */\n showClearButton?: boolean;\n}\n\nfunction AIInputTextArea({\n placeholder = 'How can HUMAIN ONE help you today?',\n className,\n showClearButton = true,\n}: AIInputTextAreaProps) {\n const ctx = useAIInputContext();\n const isInline = ctx.variant === 'inline';\n\n const handleChange = (\n e: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>,\n ) => {\n ctx.setValue(e.target.value);\n };\n\n if (isInline) {\n return (\n <input\n ref={ctx.inputRef}\n data-slot=\"ai-input-text\"\n type=\"text\"\n value={ctx.value}\n onChange={handleChange}\n onFocus={() => ctx.setIsFocused(true)}\n onBlur={() => ctx.setIsFocused(false)}\n onKeyDown={ctx.handleKeyDown}\n placeholder={placeholder}\n aria-label={placeholder}\n disabled={ctx.disabled}\n className={cn(\n 'flex-1 min-w-0 bg-transparent text-sm leading-5 text-foreground placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:text-muted-foreground',\n className,\n )}\n />\n );\n }\n\n return (\n <div\n data-slot=\"ai-input-text-area\"\n className={cn(\n 'flex gap-2',\n ctx.variant === 'compact' && !ctx.hasAttachments\n ? 'min-w-0 flex-1 items-center'\n : 'w-full items-start',\n )}\n >\n <textarea\n ref={ctx.textareaRef}\n data-slot=\"ai-input-textarea\"\n value={ctx.value}\n onChange={handleChange}\n onPaste={ctx.onPaste}\n onFocus={() => ctx.setIsFocused(true)}\n onBlur={() => ctx.setIsFocused(false)}\n onKeyDown={ctx.handleKeyDown}\n placeholder={placeholder}\n aria-label={placeholder}\n disabled={ctx.disabled}\n rows={1}\n className={cn(\n ctx.variant === 'compact' ? compactTextareaClasses : textareaClasses,\n className,\n )}\n />\n {showClearButton &&\n ctx.variant !== 'compact' &&\n ctx.hasText &&\n !ctx.disabled && (\n <button\n type=\"button\"\n onClick={ctx.handleClear}\n aria-label=\"Clear input\"\n className=\"p-2 rounded-full hover:bg-accent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring transition-colors duration-150 ease-out shrink-0\"\n >\n <X className=\"size-5 text-muted-foreground\" />\n </button>\n )}\n </div>\n );\n}\n\nAIInputTextArea.displayName = 'AIInput.TextArea';\n\n// =============================================================================\n// AIInput.LeadingIcon - Leading icon for inline variant\n// =============================================================================\n\nexport interface AIInputLeadingIconProps {\n /** Icon content (brand icon, spinner, avatar) */\n children: React.ReactNode;\n /** Additional class name */\n className?: string;\n}\n\nfunction AIInputLeadingIcon({ children, className }: AIInputLeadingIconProps) {\n return (\n <div\n data-slot=\"ai-input-leading-icon\"\n aria-hidden=\"true\"\n className={cn(\n 'flex items-center justify-center shrink-0 size-9 rounded-xl bg-foreground/4 border border-border',\n className,\n )}\n >\n {children}\n </div>\n );\n}\n\nAIInputLeadingIcon.displayName = 'AIInput.LeadingIcon';\n\n// =============================================================================\n// AIInput.SendButton - The send/submit button\n// =============================================================================\n\nexport interface AIInputSendButtonProps\n extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {\n /** Icon component to render */\n icon?: React.ComponentType<{ className?: string }>;\n}\n\nfunction AIInputSendButton({\n icon,\n className,\n ...props\n}: AIInputSendButtonProps) {\n const ctx = useAIInputContext();\n const Icon = icon ?? ArrowUp;\n\n return (\n <Button\n type=\"button\"\n data-slot=\"ai-input-send-button\"\n size=\"icon-sm\"\n onClick={ctx.handleSubmit}\n disabled={ctx.disabled || !ctx.hasText}\n aria-label=\"Send message\"\n className={cn(\n 'rounded-full bg-primary hover:bg-primary/90 text-primary-foreground disabled:bg-accent disabled:text-muted-foreground',\n 'transition-all duration-150 ease-out active:scale-90 motion-reduce:active:scale-100',\n className,\n )}\n {...props}\n >\n <Icon className=\"size-4\" />\n </Button>\n );\n}\n\nAIInputSendButton.displayName = 'AIInput.SendButton';\n\n// =============================================================================\n// AIInput.AttachButton - Attach file button\n// =============================================================================\n\nexport interface AIInputAttachButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /** Icon component to render */\n icon: React.ComponentType<{ className?: string }>;\n /** Tooltip text */\n tooltip?: string;\n}\n\nfunction AIInputAttachButton({\n icon: Icon,\n tooltip,\n className,\n onClick,\n ...props\n}: AIInputAttachButtonProps) {\n const ctx = useAIInputContext();\n\n const handleClick = React.useCallback(\n (e: React.MouseEvent<HTMLButtonElement>) => {\n if (onClick) {\n onClick(e);\n } else if (ctx.onFilesSelected) {\n ctx.openFilePicker();\n }\n },\n [onClick, ctx.onFilesSelected, ctx.openFilePicker],\n );\n\n return (\n <button\n type=\"button\"\n data-slot=\"ai-input-attach-button\"\n onClick={handleClick}\n disabled={ctx.disabled}\n title={tooltip}\n aria-label={tooltip ?? 'Attach file'}\n className={cn(\n 'p-2.5 min-h-11 min-w-11 flex items-center justify-center rounded-full text-secondary-foreground hover:bg-accent transition-colors disabled:opacity-50 disabled:cursor-not-allowed',\n className,\n )}\n {...props}\n >\n <Icon className=\"size-4\" />\n </button>\n );\n}\n\nAIInputAttachButton.displayName = 'AIInput.AttachButton';\n\n// =============================================================================\n// AIInput.Actions - Container for action buttons\n// =============================================================================\n\nexport interface AIInputActionsProps {\n /** Children (action buttons) */\n children?: React.ReactNode;\n /** Position of actions */\n position?: 'leading' | 'trailing';\n /** Additional class name */\n className?: string;\n}\n\nfunction AIInputActions({\n children,\n position = 'leading',\n className,\n}: AIInputActionsProps) {\n return (\n <div\n data-slot=\"ai-input-actions\"\n data-position={position}\n className={cn('flex items-center gap-2', className)}\n >\n {children}\n </div>\n );\n}\n\nAIInputActions.displayName = 'AIInput.Actions';\n\n// =============================================================================\n// AIInput.ActionBar - The bottom action bar (for default variant)\n// =============================================================================\n\nexport interface AIInputActionBarProps {\n /** Children (leading actions, trailing actions, send button) */\n children?: React.ReactNode;\n /** Additional class name */\n className?: string;\n}\n\nfunction AIInputActionBar({ children, className }: AIInputActionBarProps) {\n const ctx = useAIInputContext();\n const isCompactRow = ctx.variant === 'compact' && !ctx.hasAttachments;\n\n return (\n <div\n data-slot=\"ai-input-action-bar\"\n className={cn(\n 'flex items-center',\n isCompactRow ? 'shrink-0 gap-3' : 'justify-between w-full',\n className,\n )}\n >\n {children}\n </div>\n );\n}\n\nAIInputActionBar.displayName = 'AIInput.ActionBar';\n\n// =============================================================================\n// AIInput.FilePreview - File attachments section\n// =============================================================================\n\nexport interface AIInputFilePreviewProps {\n /** Additional class name */\n className?: string;\n}\n\nfunction AIInputFilePreview({ className }: AIInputFilePreviewProps) {\n const ctx = useAIInputContext();\n\n if (!ctx.hasAttachments) return null;\n\n const completedFiles = ctx.uploadingFiles.filter(\n (f) => f.status === 'complete' || f.progress >= 100,\n );\n\n // Compact/inline variants use the mobile file mini-card.\n if (ctx.variant !== 'default') {\n return (\n <div\n data-slot=\"ai-input-file-preview\"\n className={cn('w-full flex flex-col gap-2', className)}\n >\n <div className=\"flex gap-2 w-full overflow-x-auto\">\n {ctx.uploadingFiles.map((file, index) => (\n <FileUploadItem\n key={file.id}\n mobile\n fileName={file.name}\n fileExtension={file.fileExtension ?? file.name.split('.').pop()}\n progress={file.progress}\n status={file.status}\n className=\"shrink-0 delight-stagger\"\n style={\n {\n '--delight-delay': `${index * 60}ms`,\n } as React.CSSProperties\n }\n onRemove={() => ctx.onRemoveFile?.(file.id)}\n onCancel={() => ctx.onCancelUpload?.(file.id)}\n onRetry={() => ctx.onRetryUpload?.(file.id)}\n />\n ))}\n </div>\n <div className=\"flex items-center justify-between w-full\">\n <span className=\"text-xs text-card-foreground\">\n {completedFiles.length} file\n {completedFiles.length !== 1 ? 's' : ''} added\n </span>\n <RemoveAllButton onClick={ctx.onRemoveAllFiles} />\n </div>\n <div className=\"w-full h-px bg-border\" />\n </div>\n );\n }\n\n // Default variant uses the horizontal file mini-card.\n return (\n <div\n data-slot=\"ai-input-file-preview\"\n className={cn('w-full flex flex-col gap-2', className)}\n >\n <div className=\"flex items-start gap-2 w-full overflow-x-auto\">\n {ctx.uploadingFiles.map((file, index) => (\n <FileUploadItem\n key={file.id}\n fileName={file.name}\n fileSize={file.size}\n fileExtension={file.fileExtension ?? file.name.split('.').pop()}\n progress={file.progress}\n status={file.status}\n className=\"flex-1 min-w-0 delight-stagger\"\n style={\n { '--delight-delay': `${index * 60}ms` } as React.CSSProperties\n }\n onRemove={() => ctx.onRemoveFile?.(file.id)}\n onCancel={() => ctx.onCancelUpload?.(file.id)}\n onRetry={() => ctx.onRetryUpload?.(file.id)}\n />\n ))}\n </div>\n <div className=\"flex items-end justify-between w-full\">\n <span className=\"text-xs text-card-foreground\">\n {completedFiles.length} file\n {completedFiles.length !== 1 ? 's' : ''} added\n </span>\n <RemoveAllButton onClick={ctx.onRemoveAllFiles} />\n </div>\n <div className=\"w-full h-px bg-border\" />\n </div>\n );\n}\n\nAIInputFilePreview.displayName = 'AIInput.FilePreview';\n\n// =============================================================================\n// Monolithic AIInput Component (Batteries-Included)\n// =============================================================================\n\nexport interface AIInputProps extends VariantProps<typeof inputVariants> {\n /** Visual variant of the input */\n variant?: 'default' | 'compact' | 'inline';\n /** Placeholder text */\n placeholder?: string;\n /** Current input value */\n value?: string;\n /** Initial input value for uncontrolled usage */\n defaultValue?: string;\n /** Callback when value changes */\n onChange?: (value: string) => void;\n /** Callback when message is submitted */\n onSubmit?: (message: string) => void;\n /** Files being uploaded */\n uploadingFiles?: UploadingFile[];\n /** Callback when file is removed */\n onRemoveFile?: (fileId: string) => void;\n /** Callback when file upload is cancelled */\n onCancelUpload?: (fileId: string) => void;\n /** Callback to retry a failed upload */\n onRetryUpload?: (fileId: string) => void;\n /** Callback to remove all files */\n onRemoveAllFiles?: () => void;\n /** Callback when files are selected (via drag-drop or file picker) */\n onFilesSelected?: (files: FileList) => void;\n /** Accepted file types (e.g. 'image/*,.pdf') */\n accept?: string;\n /** Allow multiple file selection */\n multiple?: boolean;\n /** Text shown in the drop overlay */\n dropText?: string;\n /** Whether the input is disabled */\n disabled?: boolean;\n /** Additional class name */\n className?: string;\n\n // ==========================================================================\n // Slot Props (for customizing buttons)\n // ==========================================================================\n\n /** Actions rendered on the left side of the action bar, or before submit in inline mode */\n leadingActions?: React.ReactNode;\n /** Actions rendered on the right side, before submit button */\n trailingActions?: React.ReactNode;\n /** Custom submit button render function - receives onClick and disabled */\n submitButton?: SubmitButtonRenderFn;\n /** Custom default button render function (default variant only) - receives disabled */\n defaultButton?: DefaultButtonRenderFn;\n /** Leading icon for inline variant (brand icon, spinner, avatar). Required for inline, ignored for other variants. */\n leadingIcon?: React.ReactNode;\n /** Convert large/structured paste into a file attachment. */\n pasteAsFile?: PasteAsFileProp;\n}\n\nfunction AIInputDefault({\n variant = 'default',\n placeholder = 'How can HUMAIN ONE help you today?',\n value,\n defaultValue = '',\n onChange,\n onSubmit,\n uploadingFiles = [],\n onRemoveFile,\n onCancelUpload,\n onRetryUpload,\n onRemoveAllFiles,\n onFilesSelected,\n accept,\n multiple = true,\n dropText = 'Drop files to attach',\n disabled = false,\n className,\n // Slot props\n leadingActions,\n trailingActions,\n submitButton,\n defaultButton,\n leadingIcon,\n pasteAsFile,\n}: AIInputProps) {\n const [internalValue, setInternalValue] = React.useState(defaultValue);\n const [isFocused, setIsFocused] = React.useState(false);\n const textareaRef = React.useRef<HTMLTextAreaElement>(null);\n const inputRef = React.useRef<HTMLInputElement>(null);\n const rootRef = React.useRef<HTMLDivElement>(null);\n\n const { isDragging, dropZoneProps } = useDropZone({\n onFilesSelected,\n accept,\n multiple,\n disabled,\n containerRef: rootRef,\n });\n\n const inputValue = value !== undefined ? value : internalValue;\n const hasText = inputValue.trim().length > 0;\n const hasAttachments = uploadingFiles.length > 0;\n const isControlled = value !== undefined;\n\n const isInline = variant === 'inline';\n const isDefault = variant === 'default';\n\n // Determine visual state\n const getState = (): VariantProps<typeof inputVariants>['state'] => {\n if (disabled) return 'disabled';\n if (isDragging) return 'dragover';\n if (hasAttachments) return 'attachments';\n if (hasText) return 'withText';\n if (isFocused) return 'focused';\n return 'default';\n };\n\n const setValue = React.useCallback(\n (next: string) => {\n if (onChange) {\n onChange(next);\n }\n if (!isControlled) {\n setInternalValue(next);\n }\n },\n [onChange, isControlled],\n );\n\n const handleChange = (\n e: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>,\n ) => {\n setValue(e.target.value);\n };\n\n const { onPaste } = usePasteAsFile({\n pasteAsFile,\n onFilesSelected,\n accept,\n disabled,\n variant,\n value: inputValue,\n setValue,\n });\n\n const handleSubmit = () => {\n if (hasText && onSubmit) {\n onSubmit(inputValue);\n if (!isControlled) {\n setInternalValue('');\n }\n }\n };\n\n const handleKeyDown = (\n e: React.KeyboardEvent<HTMLTextAreaElement | HTMLInputElement>,\n ) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n handleSubmit();\n }\n };\n\n const handleClear = () => {\n if (onChange) {\n onChange('');\n }\n if (!isControlled) {\n setInternalValue('');\n }\n if (variant === 'inline') {\n inputRef.current?.focus();\n } else {\n textareaRef.current?.focus();\n }\n };\n\n // Shared: completed file count for file preview sections\n const completedCount = uploadingFiles.filter(\n (f) => f.status === 'complete' || f.progress >= 100,\n ).length;\n\n // Compact file preview (shared by compact + inline variants, skipped for default)\n const compactFilePreview = !isDefault && hasAttachments && (\n <div\n data-slot=\"ai-input-file-preview\"\n className=\"w-full flex flex-col gap-2\"\n >\n <div className=\"flex gap-2 w-full overflow-x-auto\">\n {uploadingFiles.map((file, index) => (\n <FileUploadItem\n key={file.id}\n mobile\n fileName={file.name}\n fileExtension={file.fileExtension ?? file.name.split('.').pop()}\n progress={file.progress}\n status={file.status}\n className=\"shrink-0 delight-stagger\"\n style={\n { '--delight-delay': `${index * 60}ms` } as React.CSSProperties\n }\n onRemove={() => onRemoveFile?.(file.id)}\n onCancel={() => onCancelUpload?.(file.id)}\n onRetry={() => onRetryUpload?.(file.id)}\n />\n ))}\n </div>\n <div className=\"flex items-center justify-between w-full\">\n <span className=\"text-xs text-card-foreground\">\n {completedCount} file\n {completedCount !== 1 ? 's' : ''} added\n </span>\n <RemoveAllButton onClick={onRemoveAllFiles} />\n </div>\n <div className=\"w-full h-px bg-border\" />\n </div>\n );\n\n // Inline variant\n if (isInline) {\n return (\n <div\n ref={rootRef}\n data-slot=\"ai-input\"\n data-dragging={isDragging}\n {...dropZoneProps}\n {...(onFilesSelected\n ? { 'aria-label': 'Chat input with file drop zone' }\n : {})}\n className={getAIInputShellClassName({\n state: getState(),\n variant: 'inline',\n className,\n })}\n >\n <div\n data-slot=\"ai-input-inner\"\n className={cn(\n inputVariants({ state: getState(), variant: 'inline' }),\n 'relative bg-transparent',\n hasAttachments && 'flex-col items-stretch py-3',\n )}\n >\n {isDragging && (\n <DropOverlay isDefault={isDefault} dropText={dropText} />\n )}\n {compactFilePreview}\n\n <div className=\"flex items-center gap-2 w-full\">\n {leadingIcon ? (\n <AIInputLeadingIcon>{leadingIcon}</AIInputLeadingIcon>\n ) : null}\n\n <input\n ref={inputRef}\n data-slot=\"ai-input-text\"\n type=\"text\"\n value={inputValue}\n onChange={handleChange}\n onFocus={() => setIsFocused(true)}\n onBlur={() => setIsFocused(false)}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n aria-label={placeholder}\n disabled={disabled}\n className=\"flex-1 min-w-0 bg-transparent text-sm leading-5 text-foreground placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:text-muted-foreground\"\n />\n\n {leadingActions}\n {trailingActions}\n\n <AIInputSubmitButton\n customButton={submitButton}\n onClick={handleSubmit}\n disabled={disabled || !hasText}\n />\n </div>\n </div>\n </div>\n );\n }\n\n // Compact variant\n if (!isDefault) {\n return (\n <div\n ref={rootRef}\n data-slot=\"ai-input\"\n data-dragging={isDragging}\n {...dropZoneProps}\n {...(onFilesSelected\n ? { 'aria-label': 'Chat input with file drop zone' }\n : {})}\n className={getAIInputShellClassName({\n state: getState(),\n variant: 'compact',\n className,\n })}\n >\n <div\n data-slot=\"ai-input-inner\"\n className={cn(\n inputVariants({ state: getState(), variant: 'compact' }),\n 'relative bg-transparent',\n hasAttachments && 'flex-col items-stretch',\n )}\n >\n {isDragging && (\n <DropOverlay isDefault={isDefault} dropText={dropText} />\n )}\n {compactFilePreview}\n\n <div\n data-slot=\"ai-input-text-area\"\n className={cn(\n 'flex gap-2',\n hasAttachments\n ? 'w-full items-start'\n : 'min-w-0 flex-1 items-center',\n )}\n >\n <textarea\n ref={textareaRef}\n data-slot=\"ai-input-textarea\"\n value={inputValue}\n onChange={handleChange}\n onFocus={() => setIsFocused(true)}\n onBlur={() => setIsFocused(false)}\n onKeyDown={handleKeyDown}\n onPaste={onPaste}\n placeholder={placeholder}\n aria-label={placeholder}\n disabled={disabled}\n rows={1}\n className={compactTextareaClasses}\n />\n </div>\n\n <div\n data-slot=\"ai-input-action-bar\"\n className={cn(\n 'flex items-center',\n hasAttachments ? 'justify-between w-full' : 'shrink-0 gap-3',\n )}\n >\n <div\n data-slot=\"ai-input-actions\"\n className=\"flex items-center gap-2\"\n >\n {leadingActions}\n </div>\n <div\n data-slot=\"ai-input-actions\"\n className=\"flex items-center gap-2\"\n >\n {trailingActions}\n {hasText ? (\n <AIInputSubmitButton\n customButton={submitButton}\n onClick={handleSubmit}\n disabled={disabled || !hasText}\n />\n ) : (\n <AIInputDefaultButton\n customButton={defaultButton}\n disabled={disabled}\n />\n )}\n </div>\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <div\n ref={rootRef}\n data-slot=\"ai-input\"\n data-dragging={isDragging}\n {...dropZoneProps}\n {...(onFilesSelected\n ? { 'aria-label': 'Chat input with file drop zone' }\n : {})}\n className={getAIInputShellClassName({\n state: getState(),\n variant: 'default',\n className,\n })}\n >\n <div\n data-slot=\"ai-input-inner\"\n className={cn(\n inputVariants({ state: getState(), variant: 'default' }),\n 'relative bg-transparent',\n )}\n >\n {isDragging && (\n <DropOverlay isDefault={isDefault} dropText={dropText} />\n )}\n {/* Attachments Section (above input, matching Figma layout) */}\n {hasAttachments && (\n <div\n data-slot=\"ai-input-file-preview\"\n className=\"w-full flex flex-col gap-2\"\n >\n <div className=\"flex items-start gap-2 w-full overflow-x-auto\">\n {uploadingFiles.map((file, index) => (\n <FileUploadItem\n key={file.id}\n fileName={file.name}\n fileSize={file.size}\n fileExtension={\n file.fileExtension ?? file.name.split('.').pop()\n }\n progress={file.progress}\n status={file.status}\n className=\"flex-1 min-w-0 delight-stagger\"\n style={\n {\n '--delight-delay': `${index * 60}ms`,\n } as React.CSSProperties\n }\n onRemove={() => onRemoveFile?.(file.id)}\n onCancel={() => onCancelUpload?.(file.id)}\n onRetry={() => onRetryUpload?.(file.id)}\n />\n ))}\n </div>\n\n <div className=\"flex items-end justify-between w-full\">\n <span className=\"text-xs text-card-foreground\">\n {completedCount} file\n {completedCount !== 1 ? 's' : ''} added\n </span>\n <RemoveAllButton onClick={onRemoveAllFiles} />\n </div>\n\n <div className=\"w-full h-px bg-border\" />\n </div>\n )}\n\n <div\n data-slot=\"ai-input-text-area\"\n className=\"flex items-start gap-2 w-full\"\n >\n <textarea\n ref={textareaRef}\n data-slot=\"ai-input-textarea\"\n value={inputValue}\n onChange={handleChange}\n onFocus={() => setIsFocused(true)}\n onBlur={() => setIsFocused(false)}\n onKeyDown={handleKeyDown}\n onPaste={onPaste}\n placeholder={placeholder}\n aria-label={placeholder}\n disabled={disabled}\n rows={1}\n className={textareaClasses}\n />\n {hasText && !disabled && (\n <button\n type=\"button\"\n onClick={handleClear}\n aria-label=\"Clear input\"\n className=\"p-2 rounded-full hover:bg-accent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring transition-colors duration-150 ease-out shrink-0\"\n >\n <X className=\"size-5 text-muted-foreground\" />\n </button>\n )}\n </div>\n\n <div\n data-slot=\"ai-input-action-bar\"\n className=\"flex items-center justify-between w-full\"\n >\n <div data-slot=\"ai-input-actions\" className=\"flex items-center gap-2\">\n {leadingActions}\n </div>\n\n <div data-slot=\"ai-input-actions\" className=\"flex items-center gap-2\">\n {trailingActions}\n {hasText ? (\n <AIInputSubmitButton\n customButton={submitButton}\n onClick={handleSubmit}\n disabled={disabled || !hasText}\n />\n ) : (\n <AIInputDefaultButton\n customButton={defaultButton}\n disabled={disabled}\n />\n )}\n </div>\n </div>\n </div>\n </div>\n );\n}\n\nAIInputDefault.displayName = 'AIInput';\n\n// =============================================================================\n// Export: Object.assign for dual-API\n// =============================================================================\n\n/**\n * AIInput - Dual-API component for AI chat input.\n *\n * Supports two usage patterns:\n *\n * 1. **Monolithic API (batteries-included)**:\n * ```tsx\n * <AIInput\n * placeholder=\"Ask anything...\"\n * onSubmit={handleSubmit}\n * leadingActions={<AIInput.ActionButton icon={Plus} />}\n * />\n * ```\n *\n * 2. **Composable API (full customization)**:\n * ```tsx\n * <AIInput.Root onSubmit={handleSubmit}>\n * <AIInput.TextArea placeholder=\"Ask anything...\" />\n * <AIInput.ActionBar>\n * <AIInput.Actions position=\"leading\">\n * <AIInput.AttachButton icon={Plus} tooltip=\"Add file\" />\n * </AIInput.Actions>\n * <AIInput.Actions position=\"trailing\">\n * <AIInput.SendButton />\n * </AIInput.Actions>\n * </AIInput.ActionBar>\n * <AIInput.FilePreview />\n * </AIInput.Root>\n * ```\n */\nconst AIInput = Object.assign(AIInputDefault, {\n Root: AIInputRoot,\n TextArea: AIInputTextArea,\n LeadingIcon: AIInputLeadingIcon,\n SendButton: AIInputSendButton,\n AttachButton: AIInputAttachButton,\n Actions: AIInputActions,\n ActionBar: AIInputActionBar,\n FilePreview: AIInputFilePreview,\n // Legacy compound components (also available on main export)\n ActionButton,\n SubmitButton,\n CompactActionButton,\n});\n\nexport {\n // Compound components (also available as AIInput.X)\n ActionButton,\n AIInput,\n AIInputActionBar,\n AIInputActions,\n AIInputAttachButton,\n AIInputFilePreview,\n AIInputLeadingIcon,\n // New composable components\n AIInputRoot,\n AIInputSendButton,\n AIInputTextArea,\n CompactActionButton,\n inputVariants,\n SubmitButton,\n // Context hook for custom subcomponents\n useAIInputContext,\n};\n\n// Note: SubmitButtonRenderProps, SubmitButtonRenderFn, DefaultButtonRenderProps,\n// and DefaultButtonRenderFn are exported at their definition sites above\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AIInput.js","names":[],"sources":["../../src/components/organisms/ai/AIInput/paste-detection.ts","../../src/components/organisms/ai/AIInput/AIInput.tsx"],"sourcesContent":["/**\n * Paste-detection heuristics for the AIInput pasteAsFile feature.\n * Pure module — no React, no DOM, no I/O.\n */\n\nexport type PasteKind = 'json' | 'xml' | 'csv' | 'log' | 'code' | 'text';\n\nexport interface DetectionOpts {\n /** Char count above which plain text always converts. Default: 500 */\n charThreshold?: number;\n /** Line count above which plain text always converts. Default: 10 */\n lineThreshold?: number;\n /** Run structured-content heuristics. Default: true */\n detectStructured?: boolean;\n}\n\nexport interface PasteAnalysis {\n shouldConvert: boolean;\n kind: PasteKind;\n}\n\nconst LARGE_TEXT_CHAR_THRESHOLD = 500;\nconst LARGE_TEXT_LINE_THRESHOLD = 10;\nconst STRUCTURED_DETECTION_MAX = 1_000_000;\n\nconst CODE_PATTERN =\n /^(import |from |export |function |class |const |let |var |def |package )/m;\nconst TIMESTAMP_PATTERN = /^\\d{4}-\\d{2}-\\d{2}[\\sT]\\d{2}:\\d{2}:\\d{2}/m;\nconst STACK_TRACE_PATTERN = /^\\s+at\\s+/m;\n\nexport const KIND_TO_FILE: Readonly<\n Record<PasteKind, { ext: string; mime: string }>\n> = Object.freeze({\n json: { ext: 'json', mime: 'application/json' },\n xml: { ext: 'xml', mime: 'application/xml' },\n csv: { ext: 'csv', mime: 'text/csv' },\n log: { ext: 'log', mime: 'text/plain' },\n code: { ext: 'txt', mime: 'text/plain' },\n text: { ext: 'txt', mime: 'text/plain' },\n});\n\nexport function mapKindToFile(kind: PasteKind): { ext: string; mime: string } {\n return KIND_TO_FILE[kind];\n}\n\n/**\n * Single-pass paste analysis: splits lines once, runs each detector at most once.\n * Used by the hook to avoid the duplicate work of calling shouldConvertPaste\n * and detectPasteKind separately.\n */\nexport function analyzePaste(\n text: string,\n opts: DetectionOpts = {},\n): PasteAnalysis {\n if (!text) return { shouldConvert: false, kind: 'text' };\n\n const charThreshold = opts.charThreshold ?? LARGE_TEXT_CHAR_THRESHOLD;\n const lineThreshold = opts.lineThreshold ?? LARGE_TEXT_LINE_THRESHOLD;\n const detectStructured = opts.detectStructured ?? true;\n\n const sizeTriggered = text.length > charThreshold;\n const lines = text.split('\\n');\n const lineTriggered = lines.length > lineThreshold;\n const canDetect = detectStructured && text.length <= STRUCTURED_DETECTION_MAX;\n\n let kind: PasteKind = 'text';\n if (canDetect) {\n if (isJson(text)) kind = 'json';\n else if (isXmlOrHtml(text)) kind = 'xml';\n else if (isCode(text, lines)) kind = 'code';\n else if (isCsv(lines)) kind = 'csv';\n else if (isLogOutput(text, lines)) kind = 'log';\n }\n\n const shouldConvert = sizeTriggered || lineTriggered || kind !== 'text';\n return { shouldConvert, kind };\n}\n\n/**\n * Decide whether the pasted text should convert to a file attachment.\n * Thin wrapper over analyzePaste — kept for the public API and consumers\n * who only need the boolean.\n */\nexport function shouldConvertPaste(\n text: string,\n opts: DetectionOpts = {},\n): boolean {\n return analyzePaste(text, opts).shouldConvert;\n}\n\n/**\n * Classify the paste kind. Returns 'text' when detection is disabled or the\n * text exceeds STRUCTURED_DETECTION_MAX (parse cost guard).\n */\nexport function detectPasteKind(\n text: string,\n opts: DetectionOpts = {},\n): PasteKind {\n return analyzePaste(text, opts).kind;\n}\n\nfunction isJson(text: string): boolean {\n const trimmed = text.trimStart();\n if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) return false;\n try {\n JSON.parse(trimmed);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction isXmlOrHtml(text: string): boolean {\n const trimmed = text.trimStart();\n if (!trimmed.startsWith('<')) return false;\n return /<\\/\\w+>/.test(trimmed);\n}\n\nfunction isCode(text: string, lines: string[]): boolean {\n if (lines.length < 2) return false;\n return CODE_PATTERN.test(text);\n}\n\nfunction isCsv(lines: string[]): boolean {\n const nonEmpty = lines.filter((line) => line.trim().length > 0);\n if (nonEmpty.length < 3) return false;\n\n for (const delimiter of [',', '\\t']) {\n const columnCounts = nonEmpty.map((line) => line.split(delimiter).length);\n const firstCount = columnCounts[0];\n if (firstCount < 2) continue;\n if (!columnCounts.every((c) => c === firstCount)) continue;\n // For comma delimiter, require min 2 commas per line. With consistent\n // column counts, comma count = firstCount - 1, so firstCount >= 3.\n if (delimiter === ',' && firstCount < 3) continue;\n return true;\n }\n return false;\n}\n\nfunction isLogOutput(text: string, lines: string[]): boolean {\n if (lines.length < 2) return false;\n const timestampedLines = lines.filter((l) => TIMESTAMP_PATTERN.test(l));\n if (timestampedLines.length >= 2) return true;\n return STACK_TRACE_PATTERN.test(text);\n}\n","import { cva, type VariantProps } from 'class-variance-authority';\nimport { ArrowDown, ArrowUp, X } from 'lucide-react';\nimport * as React from 'react';\nimport { Button } from '@/components/atoms/buttons/Button';\nimport { FileUploadItem } from '@/components/molecules/files/FileUpload';\nimport {\n matchesAcceptToken,\n toFileList,\n useDropZone,\n} from '@/hooks/use-drop-zone';\nimport { createSafeContext } from '@/lib/create-safe-context';\nimport { cn } from '@/lib/utils';\nimport { analyzePaste, mapKindToFile, type PasteKind } from './paste-detection';\n\n// =============================================================================\n// Shared Styles\n// =============================================================================\n\nconst textareaClasses =\n 'flex-1 h-[40px] resize-none overflow-y-auto bg-transparent text-base text-foreground placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:text-muted-foreground';\n\nconst compactTextareaClasses =\n 'flex-1 h-[32px] resize-none overflow-y-auto bg-transparent text-base leading-6 text-foreground placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:text-muted-foreground';\n\n// =============================================================================\n// Variants\n// =============================================================================\n\nconst inputVariants = cva(\n 'flex items-center w-full transition-[color,background-color,border-color,box-shadow,opacity]',\n {\n variants: {\n state: {\n default: '',\n focused: '',\n withText: '',\n disabled: 'cursor-not-allowed',\n attachments: '',\n dragover: '',\n },\n variant: {\n default: 'flex-col gap-2 py-4 px-4 rounded-[inherit]',\n compact: 'flex-row gap-2 py-4 px-4 rounded-[inherit]',\n inline: 'flex-row gap-2 px-4 py-4 rounded-[inherit]',\n },\n },\n defaultVariants: {\n state: 'default',\n variant: 'default',\n },\n },\n);\n\nfunction getAIInputShellClassName({\n state,\n variant,\n className,\n}: {\n state: VariantProps<typeof inputVariants>['state'];\n variant: 'default' | 'compact' | 'inline';\n className?: string;\n}) {\n const isDefault = variant === 'default';\n const isDisabled = state === 'disabled';\n const isPrimaryState =\n state === 'focused' ||\n state === 'withText' ||\n state === 'attachments' ||\n state === 'dragover';\n const isStrongState =\n state === 'focused' || state === 'withText' || state === 'dragover';\n\n return cn(\n 'relative w-full overflow-hidden border bg-background shadow-[var(--shadow-ai-input)] transition-[color,background-color,border-color,box-shadow,opacity] duration-150 ease-out',\n isDefault ? 'rounded-3xl max-w-[772px]' : 'rounded-2xl',\n isDisabled\n ? 'border-border bg-muted'\n : isPrimaryState\n ? 'border-primary'\n : 'border-ring/50 hover:border-primary',\n isStrongState ? 'border-2' : 'border-[1.25px]',\n className,\n );\n}\n\n// =============================================================================\n// Drop Overlay (shared across all variants)\n// =============================================================================\n\nfunction DropOverlay({\n isDefault,\n dropText,\n}: {\n isDefault: boolean;\n dropText: string;\n}) {\n return (\n <div\n data-slot=\"ai-input-drop-overlay\"\n role=\"status\"\n aria-live=\"polite\"\n className={cn(\n 'delight-drop-enter absolute inset-1.5 border-2 border-dashed border-primary/50 dark:border-primary/60 pointer-events-none flex flex-col items-center justify-center gap-1.5 z-10',\n isDefault ? 'rounded-[calc(1.5rem-8px)]' : 'rounded-[calc(1rem-8px)]',\n )}\n >\n <ArrowDown className=\"size-4 text-primary/70 delight-drop-icon\" />\n <span className=\"text-sm text-muted-foreground font-medium\">\n {dropText}\n </span>\n </div>\n );\n}\n\nDropOverlay.displayName = 'AIInput.DropOverlay';\n\n// =============================================================================\n// File Attachment Types\n// =============================================================================\n\nexport interface UploadingFile {\n id: string;\n name: string;\n size: string;\n progress: number;\n status: 'uploading' | 'complete' | 'error';\n /** File extension for icon display (e.g., 'pdf', 'docx') */\n fileExtension?: string;\n}\n\n// =============================================================================\n// Paste-as-File public types\n// =============================================================================\n\nexport type { PasteKind } from './paste-detection';\n\nexport interface PasteAsFileConfig {\n /** Char count above which plain text always converts. Default: 500 */\n threshold?: number;\n /** Line count above which plain text always converts. Default: 10 */\n lineThreshold?: number;\n /** Run structured-content heuristics (JSON/XML/CSV/code/log). Default: true */\n detectStructured?: boolean;\n /**\n * Auto-fill textarea after conversion. Receives filename + detected kind.\n * Return string to set as textarea value (appended with leading space if textarea non-empty).\n * Empty/whitespace-only return is treated as no-op.\n * Default: undefined → textarea stays empty.\n */\n prefillTemplate?: (filename: string, kind: PasteKind) => string;\n /** Fired after onFilesSelected and prefill. */\n onConverted?: (filename: string, kind: PasteKind) => void;\n}\n\n/**\n * Accepts boolean for runtime toggling. `false` and `undefined` both disable.\n * `true` enables with defaults. Object enables with overrides.\n */\nexport type PasteAsFileProp = boolean | PasteAsFileConfig;\n\n// =============================================================================\n// usePasteAsFile — internal hook\n// =============================================================================\n\ninterface UsePasteAsFileOpts {\n pasteAsFile: PasteAsFileProp | undefined;\n onFilesSelected: ((files: FileList) => void) | undefined;\n accept: string | undefined;\n disabled: boolean;\n variant: 'default' | 'compact' | 'inline';\n value: string;\n setValue: (next: string) => void;\n}\n\nfunction usePasteAsFile(opts: UsePasteAsFileOpts): {\n onPaste?: React.ClipboardEventHandler<HTMLTextAreaElement>;\n} {\n const counterRef = React.useRef(0);\n const warnedRef = React.useRef(false);\n const optsRef = React.useRef(opts);\n\n // Keep the ref pointing at the latest opts each render. This pattern lets the\n // returned `onPaste` be referentially stable (empty-deps useCallback) while\n // still seeing the freshest pasteAsFile config and callbacks.\n React.useEffect(() => {\n optsRef.current = opts;\n });\n\n const onPaste = React.useCallback<\n React.ClipboardEventHandler<HTMLTextAreaElement>\n >((e) => {\n const { pasteAsFile, onFilesSelected, accept, disabled, value, setValue } =\n optsRef.current;\n\n if (!pasteAsFile || disabled) return;\n\n const text = e.clipboardData.getData('text/plain');\n if (!text) return;\n\n const config: PasteAsFileConfig = pasteAsFile === true ? {} : pasteAsFile;\n\n const detectionOpts = {\n charThreshold: config.threshold,\n lineThreshold: config.lineThreshold,\n detectStructured: config.detectStructured,\n };\n\n const { shouldConvert, kind } = analyzePaste(text, detectionOpts);\n if (!shouldConvert) return;\n\n if (!onFilesSelected) {\n if (process.env.NODE_ENV !== 'production' && !warnedRef.current) {\n console.warn(\n '[AIInput] pasteAsFile is set but onFilesSelected is missing. Paste-to-file is a no-op.',\n );\n warnedRef.current = true;\n }\n return;\n }\n\n const { ext, mime } = mapKindToFile(kind);\n const nProspective = counterRef.current + 1;\n const filename = `Pasted-${nProspective}.${ext}`;\n\n if (\n accept &&\n !accept\n .split(',')\n .some((token) =>\n matchesAcceptToken({ name: filename, type: mime }, token),\n )\n ) {\n return;\n }\n\n let fileList: FileList;\n try {\n const file = new File([text], filename, { type: mime });\n fileList = toFileList([file]);\n } catch {\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n '[AIInput] DataTransfer unavailable; falling back to native paste.',\n );\n }\n return;\n }\n\n e.preventDefault();\n counterRef.current = nProspective;\n onFilesSelected(fileList);\n\n if (config.prefillTemplate) {\n const tpl = config.prefillTemplate(filename, kind);\n if (tpl?.trim()) {\n setValue(value ? `${value} ${tpl}` : tpl);\n }\n }\n\n config.onConverted?.(filename, kind);\n }, []);\n\n if (opts.variant === 'inline' || !opts.pasteAsFile) {\n return { onPaste: undefined };\n }\n\n return { onPaste };\n}\n\n// =============================================================================\n// Context\n// =============================================================================\n\ninterface AIInputContextValue {\n /** Current input value */\n value: string;\n /** Whether the input has text */\n hasText: boolean;\n /** Whether the input is focused */\n isFocused: boolean;\n /** Whether the input is disabled */\n disabled: boolean;\n /** Visual variant */\n variant: 'default' | 'compact' | 'inline';\n /** Files being uploaded */\n uploadingFiles: UploadingFile[];\n /** Has attachments */\n hasAttachments: boolean;\n /** Update input value */\n setValue: (value: string) => void;\n /** Set focus state */\n setIsFocused: (focused: boolean) => void;\n /** Handle submit */\n handleSubmit: () => void;\n /** Handle key down */\n handleKeyDown: (\n e: React.KeyboardEvent<HTMLTextAreaElement | HTMLInputElement>,\n ) => void;\n /** Handle clear */\n handleClear: () => void;\n /** Paste handler from usePasteAsFile (only set when paste-as-file is active). */\n onPaste?: React.ClipboardEventHandler<HTMLTextAreaElement>;\n /** Remove file */\n onRemoveFile?: (fileId: string) => void;\n /** Cancel upload */\n onCancelUpload?: (fileId: string) => void;\n /** Retry failed upload */\n onRetryUpload?: (fileId: string) => void;\n /** Remove all files */\n onRemoveAllFiles?: () => void;\n /** Whether files are being dragged over the input */\n isDragging: boolean;\n /** Open the native file picker */\n openFilePicker: () => void;\n /** Callback when files are selected (via drop or picker) */\n onFilesSelected?: (files: FileList) => void;\n /** Textarea ref (for default variant) */\n textareaRef: React.RefObject<HTMLTextAreaElement | null>;\n /** Input ref (for compact variant) */\n inputRef: React.RefObject<HTMLInputElement | null>;\n}\n\nconst [AIInputContext, useAIInputContext] =\n createSafeContext<AIInputContextValue>('AIInput');\n\n// =============================================================================\n// Compound Components - Building Blocks\n// =============================================================================\n\nexport interface ActionButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /** Icon component to render */\n icon: React.ComponentType<{ className?: string }>;\n /** Tooltip text */\n tooltip?: string;\n}\n\nfunction ActionButton({\n icon: Icon,\n onClick,\n disabled,\n tooltip,\n className,\n ...props\n}: ActionButtonProps) {\n return (\n <button\n type=\"button\"\n data-slot=\"action-button\"\n onClick={onClick}\n disabled={disabled}\n title={tooltip}\n aria-label={tooltip}\n className={cn(\n 'p-2 rounded-full text-secondary-foreground hover:bg-accent transition-colors disabled:opacity-50 disabled:cursor-not-allowed',\n className,\n )}\n {...props}\n >\n <Icon className=\"size-4\" />\n </button>\n );\n}\n\nActionButton.displayName = 'AIInput.ActionButton';\n\nexport interface SubmitButtonProps\n extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {\n /** Icon component to render (defaults to ArrowUp) */\n icon?: React.ComponentType<{ className?: string }>;\n}\n\nfunction SubmitButton({\n icon: Icon = ArrowUp,\n className,\n onClick,\n disabled,\n ...props\n}: SubmitButtonProps) {\n return (\n <Button\n type=\"button\"\n data-slot=\"submit-button\"\n size=\"icon-sm\"\n onClick={onClick}\n disabled={disabled}\n aria-label=\"Send message\"\n className={cn(\n 'rounded-full bg-primary hover:bg-primary/90 text-primary-foreground disabled:bg-accent disabled:text-muted-foreground transition-colors duration-150 ease-out',\n className,\n )}\n {...props}\n >\n <Icon className=\"size-4\" />\n </Button>\n );\n}\n\nSubmitButton.displayName = 'AIInput.SubmitButton';\n\nexport interface CompactActionButtonProps\n extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {\n /** Icon component to render */\n icon: React.ComponentType<{ className?: string }>;\n /** Accessible label for the icon-only button (required for a11y) */\n 'aria-label': string;\n}\n\n/**\n * @deprecated Use `ActionButton` instead. CompactActionButton will be removed in a future major version.\n */\nfunction CompactActionButton({\n icon: Icon,\n className,\n onClick,\n disabled,\n ...props\n}: CompactActionButtonProps) {\n return (\n <Button\n type=\"button\"\n data-slot=\"compact-action-button\"\n onClick={onClick}\n disabled={disabled}\n appearance=\"outline\"\n variant=\"secondary\"\n size=\"icon\"\n className={cn(\n 'rounded-full border-border text-foreground hover:bg-muted',\n className,\n )}\n {...props}\n >\n <Icon className=\"size-5\" />\n </Button>\n );\n}\n\nCompactActionButton.displayName = 'AIInput.CompactActionButton';\n\nfunction RemoveAllButton({ onClick }: { onClick?: () => void }) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n aria-label=\"Remove all attached files\"\n className=\"flex items-center gap-1.5 h-5 px-2 py-1 rounded-full bg-accent border border-border text-xs font-medium text-foreground hover:bg-accent/80 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring transition-colors\"\n >\n <X className=\"size-4\" />\n Remove all\n </button>\n );\n}\n\nRemoveAllButton.displayName = 'AIInput.RemoveAllButton';\n\n// =============================================================================\n// Render Function Types (for monolithic API)\n// =============================================================================\n\n/** Props passed to custom submit button render function */\nexport interface SubmitButtonRenderProps {\n onClick: () => void;\n disabled: boolean;\n}\n\n/** Render function type for custom submit button */\nexport type SubmitButtonRenderFn = (\n props: SubmitButtonRenderProps,\n) => React.ReactElement;\n\n/** Props passed to custom default button render function */\nexport interface DefaultButtonRenderProps {\n disabled: boolean;\n}\n\n/** Render function type for custom default button */\nexport type DefaultButtonRenderFn = (\n props: DefaultButtonRenderProps,\n) => React.ReactElement;\n\nfunction AIInputSubmitButton({\n customButton,\n onClick,\n disabled,\n}: {\n customButton?: SubmitButtonRenderFn;\n onClick: () => void;\n disabled: boolean;\n}) {\n const buttonProps: SubmitButtonRenderProps = {\n onClick,\n disabled,\n };\n\n if (customButton) {\n return customButton(buttonProps);\n }\n\n return <SubmitButton {...buttonProps} />;\n}\n\nfunction AIInputDefaultButton({\n customButton,\n disabled,\n}: {\n customButton?: DefaultButtonRenderFn;\n disabled: boolean;\n}) {\n const buttonProps: DefaultButtonRenderProps = {\n disabled,\n };\n\n if (customButton) {\n return customButton(buttonProps);\n }\n\n return <SubmitButton icon={ArrowUp} disabled />;\n}\n\n// =============================================================================\n// Composable Subcomponents (for dual-API)\n// =============================================================================\n\nexport interface AIInputRootProps {\n /** Children (composable subcomponents) */\n children: React.ReactNode;\n /** Visual variant of the input */\n variant?: 'default' | 'compact' | 'inline';\n /** Current input value (controlled) */\n value?: string;\n /** Default input value (uncontrolled) */\n defaultValue?: string;\n /** Callback when value changes */\n onChange?: (value: string) => void;\n /** Callback when message is submitted */\n onSubmit?: (message: string) => void;\n /** Files being uploaded */\n uploadingFiles?: UploadingFile[];\n /** Callback when file is removed */\n onRemoveFile?: (fileId: string) => void;\n /** Callback when file upload is cancelled */\n onCancelUpload?: (fileId: string) => void;\n /** Callback to retry a failed upload */\n onRetryUpload?: (fileId: string) => void;\n /** Callback to remove all files */\n onRemoveAllFiles?: () => void;\n /** Whether the input is disabled */\n disabled?: boolean;\n /** Additional class name */\n className?: string;\n /** Callback when files are selected (via drag-drop or file picker) */\n onFilesSelected?: (files: FileList) => void;\n /** Accepted file types (e.g. 'image/*,.pdf') */\n accept?: string;\n /** Allow multiple file selection */\n multiple?: boolean;\n /** Text shown in the drop overlay */\n dropText?: string;\n /** Convert large/structured paste into a file attachment. Requires onFilesSelected. */\n pasteAsFile?: PasteAsFileProp;\n}\n\nfunction AIInputRoot({\n children,\n variant = 'default',\n value: controlledValue,\n defaultValue = '',\n onChange,\n onSubmit,\n uploadingFiles = [],\n onRemoveFile,\n onCancelUpload,\n onRetryUpload,\n onRemoveAllFiles,\n disabled = false,\n className,\n onFilesSelected,\n accept,\n multiple = true,\n dropText = 'Drop files to attach',\n pasteAsFile,\n}: AIInputRootProps) {\n const [internalValue, setInternalValue] = React.useState(defaultValue);\n const [isFocused, setIsFocused] = React.useState(false);\n const textareaRef = React.useRef<HTMLTextAreaElement>(null);\n const inputRef = React.useRef<HTMLInputElement>(null);\n const rootRef = React.useRef<HTMLDivElement>(null);\n\n const { isDragging, dropZoneProps, openFilePicker } = useDropZone({\n onFilesSelected,\n accept,\n multiple,\n disabled,\n containerRef: rootRef,\n });\n\n const isControlled = controlledValue !== undefined;\n const value = isControlled ? controlledValue : internalValue;\n const hasText = value.trim().length > 0;\n const hasAttachments = uploadingFiles.length > 0;\n const setValue = React.useCallback(\n (newValue: string) => {\n if (onChange) {\n onChange(newValue);\n }\n if (!isControlled) {\n setInternalValue(newValue);\n }\n },\n [onChange, isControlled],\n );\n\n const { onPaste } = usePasteAsFile({\n pasteAsFile,\n onFilesSelected,\n accept,\n disabled,\n variant,\n value,\n setValue,\n });\n\n const handleSubmit = React.useCallback(() => {\n if (hasText && onSubmit) {\n onSubmit(value);\n if (!isControlled) {\n setInternalValue('');\n }\n }\n }, [hasText, onSubmit, value, isControlled]);\n\n const handleKeyDown = React.useCallback(\n (e: React.KeyboardEvent<HTMLTextAreaElement | HTMLInputElement>) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n handleSubmit();\n }\n },\n [handleSubmit],\n );\n\n const handleClear = React.useCallback(() => {\n setValue('');\n if (variant === 'inline') {\n inputRef.current?.focus();\n } else {\n textareaRef.current?.focus();\n }\n }, [setValue, variant]);\n\n // Determine visual state\n const getState = (): VariantProps<typeof inputVariants>['state'] => {\n if (disabled) return 'disabled';\n if (isDragging) return 'dragover';\n if (hasAttachments) return 'attachments';\n if (hasText) return 'withText';\n if (isFocused) return 'focused';\n return 'default';\n };\n\n const contextValue = React.useMemo<AIInputContextValue>(\n () => ({\n value,\n hasText,\n isFocused,\n disabled,\n variant,\n uploadingFiles,\n hasAttachments,\n setValue,\n setIsFocused,\n handleSubmit,\n handleKeyDown,\n handleClear,\n onPaste,\n onRemoveFile,\n onCancelUpload,\n onRetryUpload,\n onRemoveAllFiles,\n isDragging,\n openFilePicker,\n onFilesSelected,\n textareaRef,\n inputRef,\n }),\n [\n value,\n hasText,\n isFocused,\n disabled,\n variant,\n uploadingFiles,\n hasAttachments,\n setValue,\n handleSubmit,\n handleKeyDown,\n handleClear,\n onPaste,\n onRemoveFile,\n onCancelUpload,\n onRetryUpload,\n onRemoveAllFiles,\n isDragging,\n openFilePicker,\n onFilesSelected,\n ],\n );\n\n const isDefault = variant === 'default';\n const state = getState();\n\n return (\n <AIInputContext.Provider value={contextValue}>\n <div\n ref={rootRef}\n data-slot=\"ai-input\"\n data-dragging={isDragging}\n {...dropZoneProps}\n {...(onFilesSelected\n ? { 'aria-label': 'Chat input with file drop zone' }\n : {})}\n className={getAIInputShellClassName({ state, variant, className })}\n >\n <div\n data-slot=\"ai-input-inner\"\n className={cn(\n inputVariants({ state, variant }),\n 'relative',\n !isDefault && hasAttachments && 'flex-col items-stretch py-3',\n disabled && !isDefault ? 'bg-muted' : 'bg-background',\n 'rounded-[inherit]',\n disabled && isDefault && 'bg-muted',\n )}\n >\n {isDragging && (\n <DropOverlay isDefault={isDefault} dropText={dropText} />\n )}\n {children}\n </div>\n </div>\n </AIInputContext.Provider>\n );\n}\n\nAIInputRoot.displayName = 'AIInput.Root';\n\n// =============================================================================\n// AIInput.TextArea - The text input area\n// =============================================================================\n\nexport interface AIInputTextAreaProps {\n /** Placeholder text */\n placeholder?: string;\n /** Additional class name */\n className?: string;\n /** Show clear button when text is present */\n showClearButton?: boolean;\n}\n\nfunction AIInputTextArea({\n placeholder = 'How can HUMAIN ONE help you today?',\n className,\n showClearButton = true,\n}: AIInputTextAreaProps) {\n const ctx = useAIInputContext();\n const isInline = ctx.variant === 'inline';\n\n const handleChange = (\n e: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>,\n ) => {\n ctx.setValue(e.target.value);\n };\n\n if (isInline) {\n return (\n <input\n ref={ctx.inputRef}\n data-slot=\"ai-input-text\"\n type=\"text\"\n value={ctx.value}\n onChange={handleChange}\n onFocus={() => ctx.setIsFocused(true)}\n onBlur={() => ctx.setIsFocused(false)}\n onKeyDown={ctx.handleKeyDown}\n placeholder={placeholder}\n aria-label={placeholder}\n disabled={ctx.disabled}\n className={cn(\n 'flex-1 min-w-0 bg-transparent text-sm leading-5 text-foreground placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:text-muted-foreground',\n className,\n )}\n />\n );\n }\n\n return (\n <div\n data-slot=\"ai-input-text-area\"\n className={cn(\n 'flex gap-2',\n ctx.variant === 'compact' && !ctx.hasAttachments\n ? 'min-w-0 flex-1 items-center'\n : 'w-full items-start',\n )}\n >\n <textarea\n ref={ctx.textareaRef}\n data-slot=\"ai-input-textarea\"\n value={ctx.value}\n onChange={handleChange}\n onPaste={ctx.onPaste}\n onFocus={() => ctx.setIsFocused(true)}\n onBlur={() => ctx.setIsFocused(false)}\n onKeyDown={ctx.handleKeyDown}\n placeholder={placeholder}\n aria-label={placeholder}\n disabled={ctx.disabled}\n rows={1}\n className={cn(\n ctx.variant === 'compact' ? compactTextareaClasses : textareaClasses,\n className,\n )}\n />\n {showClearButton &&\n ctx.variant !== 'compact' &&\n ctx.hasText &&\n !ctx.disabled && (\n <button\n type=\"button\"\n onClick={ctx.handleClear}\n aria-label=\"Clear input\"\n className=\"p-2 rounded-full hover:bg-accent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring transition-colors duration-150 ease-out shrink-0\"\n >\n <X className=\"size-5 text-muted-foreground\" />\n </button>\n )}\n </div>\n );\n}\n\nAIInputTextArea.displayName = 'AIInput.TextArea';\n\n// =============================================================================\n// AIInput.LeadingIcon - Leading icon for inline variant\n// =============================================================================\n\nexport interface AIInputLeadingIconProps {\n /** Icon content (brand icon, spinner, avatar) */\n children: React.ReactNode;\n /** Additional class name */\n className?: string;\n}\n\nfunction AIInputLeadingIcon({ children, className }: AIInputLeadingIconProps) {\n return (\n <div\n data-slot=\"ai-input-leading-icon\"\n aria-hidden=\"true\"\n className={cn(\n 'flex items-center justify-center shrink-0 size-9 rounded-xl bg-foreground/4 border border-border',\n className,\n )}\n >\n {children}\n </div>\n );\n}\n\nAIInputLeadingIcon.displayName = 'AIInput.LeadingIcon';\n\n// =============================================================================\n// AIInput.SendButton - The send/submit button\n// =============================================================================\n\nexport interface AIInputSendButtonProps\n extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {\n /** Icon component to render */\n icon?: React.ComponentType<{ className?: string }>;\n}\n\nfunction AIInputSendButton({\n icon,\n className,\n ...props\n}: AIInputSendButtonProps) {\n const ctx = useAIInputContext();\n const Icon = icon ?? ArrowUp;\n\n return (\n <Button\n type=\"button\"\n data-slot=\"ai-input-send-button\"\n size=\"icon-sm\"\n onClick={ctx.handleSubmit}\n disabled={ctx.disabled || !ctx.hasText}\n aria-label=\"Send message\"\n className={cn(\n 'rounded-full bg-primary hover:bg-primary/90 text-primary-foreground disabled:bg-accent disabled:text-muted-foreground',\n 'transition-all duration-150 ease-out active:scale-90 motion-reduce:active:scale-100',\n className,\n )}\n {...props}\n >\n <Icon className=\"size-4\" />\n </Button>\n );\n}\n\nAIInputSendButton.displayName = 'AIInput.SendButton';\n\n// =============================================================================\n// AIInput.AttachButton - Attach file button\n// =============================================================================\n\nexport interface AIInputAttachButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /** Icon component to render */\n icon: React.ComponentType<{ className?: string }>;\n /** Tooltip text */\n tooltip?: string;\n}\n\nfunction AIInputAttachButton({\n icon: Icon,\n tooltip,\n className,\n onClick,\n ...props\n}: AIInputAttachButtonProps) {\n const ctx = useAIInputContext();\n\n const handleClick = React.useCallback(\n (e: React.MouseEvent<HTMLButtonElement>) => {\n if (onClick) {\n onClick(e);\n } else if (ctx.onFilesSelected) {\n ctx.openFilePicker();\n }\n },\n [onClick, ctx.onFilesSelected, ctx.openFilePicker],\n );\n\n return (\n <button\n type=\"button\"\n data-slot=\"ai-input-attach-button\"\n onClick={handleClick}\n disabled={ctx.disabled}\n title={tooltip}\n aria-label={tooltip ?? 'Attach file'}\n className={cn(\n 'p-2.5 min-h-11 min-w-11 flex items-center justify-center rounded-full text-secondary-foreground hover:bg-accent transition-colors disabled:opacity-50 disabled:cursor-not-allowed',\n className,\n )}\n {...props}\n >\n <Icon className=\"size-4\" />\n </button>\n );\n}\n\nAIInputAttachButton.displayName = 'AIInput.AttachButton';\n\n// =============================================================================\n// AIInput.Actions - Container for action buttons\n// =============================================================================\n\nexport interface AIInputActionsProps {\n /** Children (action buttons) */\n children?: React.ReactNode;\n /** Position of actions */\n position?: 'leading' | 'trailing';\n /** Additional class name */\n className?: string;\n}\n\nfunction AIInputActions({\n children,\n position = 'leading',\n className,\n}: AIInputActionsProps) {\n return (\n <div\n data-slot=\"ai-input-actions\"\n data-position={position}\n className={cn('flex items-center gap-2', className)}\n >\n {children}\n </div>\n );\n}\n\nAIInputActions.displayName = 'AIInput.Actions';\n\n// =============================================================================\n// AIInput.ActionBar - The bottom action bar (for default variant)\n// =============================================================================\n\nexport interface AIInputActionBarProps {\n /** Children (leading actions, trailing actions, send button) */\n children?: React.ReactNode;\n /** Additional class name */\n className?: string;\n}\n\nfunction AIInputActionBar({ children, className }: AIInputActionBarProps) {\n const ctx = useAIInputContext();\n const isCompactRow = ctx.variant === 'compact' && !ctx.hasAttachments;\n\n return (\n <div\n data-slot=\"ai-input-action-bar\"\n className={cn(\n 'flex items-center',\n isCompactRow ? 'shrink-0 gap-3' : 'justify-between w-full',\n className,\n )}\n >\n {children}\n </div>\n );\n}\n\nAIInputActionBar.displayName = 'AIInput.ActionBar';\n\n// =============================================================================\n// AIInput.FilePreview - File attachments section\n// =============================================================================\n\nexport interface AIInputFilePreviewProps {\n /** Additional class name */\n className?: string;\n}\n\nfunction AIInputFilePreview({ className }: AIInputFilePreviewProps) {\n const ctx = useAIInputContext();\n\n if (!ctx.hasAttachments) return null;\n\n const completedFiles = ctx.uploadingFiles.filter(\n (f) => f.status === 'complete' || f.progress >= 100,\n );\n\n // Compact/inline variants use the mobile file mini-card.\n if (ctx.variant !== 'default') {\n return (\n <div\n data-slot=\"ai-input-file-preview\"\n className={cn('w-full flex flex-col gap-2', className)}\n >\n <div className=\"flex gap-2 w-full overflow-x-auto\">\n {ctx.uploadingFiles.map((file, index) => (\n <FileUploadItem\n key={file.id}\n mobile\n fileName={file.name}\n fileExtension={file.fileExtension ?? file.name.split('.').pop()}\n progress={file.progress}\n status={file.status}\n className=\"shrink-0 delight-stagger\"\n style={\n {\n '--delight-delay': `${index * 60}ms`,\n } as React.CSSProperties\n }\n onRemove={() => ctx.onRemoveFile?.(file.id)}\n onCancel={() => ctx.onCancelUpload?.(file.id)}\n onRetry={() => ctx.onRetryUpload?.(file.id)}\n />\n ))}\n </div>\n <div className=\"flex items-center justify-between w-full\">\n <span className=\"text-xs text-card-foreground\">\n {completedFiles.length} file\n {completedFiles.length !== 1 ? 's' : ''} added\n </span>\n <RemoveAllButton onClick={ctx.onRemoveAllFiles} />\n </div>\n <div className=\"w-full h-px bg-border\" />\n </div>\n );\n }\n\n // Default variant uses the horizontal file mini-card.\n return (\n <div\n data-slot=\"ai-input-file-preview\"\n className={cn('w-full flex flex-col gap-2', className)}\n >\n <div className=\"flex items-start gap-2 w-full overflow-x-auto\">\n {ctx.uploadingFiles.map((file, index) => (\n <FileUploadItem\n key={file.id}\n fileName={file.name}\n fileSize={file.size}\n fileExtension={file.fileExtension ?? file.name.split('.').pop()}\n progress={file.progress}\n status={file.status}\n className=\"flex-1 min-w-0 delight-stagger\"\n style={\n { '--delight-delay': `${index * 60}ms` } as React.CSSProperties\n }\n onRemove={() => ctx.onRemoveFile?.(file.id)}\n onCancel={() => ctx.onCancelUpload?.(file.id)}\n onRetry={() => ctx.onRetryUpload?.(file.id)}\n />\n ))}\n </div>\n <div className=\"flex items-end justify-between w-full\">\n <span className=\"text-xs text-card-foreground\">\n {completedFiles.length} file\n {completedFiles.length !== 1 ? 's' : ''} added\n </span>\n <RemoveAllButton onClick={ctx.onRemoveAllFiles} />\n </div>\n <div className=\"w-full h-px bg-border\" />\n </div>\n );\n}\n\nAIInputFilePreview.displayName = 'AIInput.FilePreview';\n\n// =============================================================================\n// Monolithic AIInput Component (Batteries-Included)\n// =============================================================================\n\nexport interface AIInputProps extends VariantProps<typeof inputVariants> {\n /** Visual variant of the input */\n variant?: 'default' | 'compact' | 'inline';\n /** Placeholder text */\n placeholder?: string;\n /** Current input value */\n value?: string;\n /** Initial input value for uncontrolled usage */\n defaultValue?: string;\n /** Callback when value changes */\n onChange?: (value: string) => void;\n /** Callback when message is submitted */\n onSubmit?: (message: string) => void;\n /** Files being uploaded */\n uploadingFiles?: UploadingFile[];\n /** Callback when file is removed */\n onRemoveFile?: (fileId: string) => void;\n /** Callback when file upload is cancelled */\n onCancelUpload?: (fileId: string) => void;\n /** Callback to retry a failed upload */\n onRetryUpload?: (fileId: string) => void;\n /** Callback to remove all files */\n onRemoveAllFiles?: () => void;\n /** Callback when files are selected (via drag-drop or file picker) */\n onFilesSelected?: (files: FileList) => void;\n /** Accepted file types (e.g. 'image/*,.pdf') */\n accept?: string;\n /** Allow multiple file selection */\n multiple?: boolean;\n /** Text shown in the drop overlay */\n dropText?: string;\n /** Whether the input is disabled */\n disabled?: boolean;\n /** Additional class name */\n className?: string;\n\n // ==========================================================================\n // Slot Props (for customizing buttons)\n // ==========================================================================\n\n /** Actions rendered on the left side of the action bar, or before submit in inline mode */\n leadingActions?: React.ReactNode;\n /** Actions rendered on the right side, before submit button */\n trailingActions?: React.ReactNode;\n /** Custom submit button render function - receives onClick and disabled */\n submitButton?: SubmitButtonRenderFn;\n /** Custom default button render function (default variant only) - receives disabled */\n defaultButton?: DefaultButtonRenderFn;\n /** Leading icon for inline variant (brand icon, spinner, avatar). Required for inline, ignored for other variants. */\n leadingIcon?: React.ReactNode;\n /** Convert large/structured paste into a file attachment. */\n pasteAsFile?: PasteAsFileProp;\n}\n\nfunction AIInputDefault({\n variant = 'default',\n placeholder = 'How can HUMAIN ONE help you today?',\n value,\n defaultValue = '',\n onChange,\n onSubmit,\n uploadingFiles = [],\n onRemoveFile,\n onCancelUpload,\n onRetryUpload,\n onRemoveAllFiles,\n onFilesSelected,\n accept,\n multiple = true,\n dropText = 'Drop files to attach',\n disabled = false,\n className,\n // Slot props\n leadingActions,\n trailingActions,\n submitButton,\n defaultButton,\n leadingIcon,\n pasteAsFile,\n}: AIInputProps) {\n const [internalValue, setInternalValue] = React.useState(defaultValue);\n const [isFocused, setIsFocused] = React.useState(false);\n const textareaRef = React.useRef<HTMLTextAreaElement>(null);\n const inputRef = React.useRef<HTMLInputElement>(null);\n const rootRef = React.useRef<HTMLDivElement>(null);\n\n const { isDragging, dropZoneProps } = useDropZone({\n onFilesSelected,\n accept,\n multiple,\n disabled,\n containerRef: rootRef,\n });\n\n const inputValue = value !== undefined ? value : internalValue;\n const hasText = inputValue.trim().length > 0;\n const hasAttachments = uploadingFiles.length > 0;\n const isControlled = value !== undefined;\n\n const isInline = variant === 'inline';\n const isDefault = variant === 'default';\n\n // Determine visual state\n const getState = (): VariantProps<typeof inputVariants>['state'] => {\n if (disabled) return 'disabled';\n if (isDragging) return 'dragover';\n if (hasAttachments) return 'attachments';\n if (hasText) return 'withText';\n if (isFocused) return 'focused';\n return 'default';\n };\n\n const setValue = React.useCallback(\n (next: string) => {\n if (onChange) {\n onChange(next);\n }\n if (!isControlled) {\n setInternalValue(next);\n }\n },\n [onChange, isControlled],\n );\n\n const handleChange = (\n e: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>,\n ) => {\n setValue(e.target.value);\n };\n\n const { onPaste } = usePasteAsFile({\n pasteAsFile,\n onFilesSelected,\n accept,\n disabled,\n variant,\n value: inputValue,\n setValue,\n });\n\n const handleSubmit = () => {\n if (hasText && onSubmit) {\n onSubmit(inputValue);\n if (!isControlled) {\n setInternalValue('');\n }\n }\n };\n\n const handleKeyDown = (\n e: React.KeyboardEvent<HTMLTextAreaElement | HTMLInputElement>,\n ) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n handleSubmit();\n }\n };\n\n const handleClear = () => {\n if (onChange) {\n onChange('');\n }\n if (!isControlled) {\n setInternalValue('');\n }\n if (variant === 'inline') {\n inputRef.current?.focus();\n } else {\n textareaRef.current?.focus();\n }\n };\n\n // Shared: completed file count for file preview sections\n const completedCount = uploadingFiles.filter(\n (f) => f.status === 'complete' || f.progress >= 100,\n ).length;\n\n // Compact file preview (shared by compact + inline variants, skipped for default)\n const compactFilePreview = !isDefault && hasAttachments && (\n <div\n data-slot=\"ai-input-file-preview\"\n className=\"w-full flex flex-col gap-2\"\n >\n <div className=\"flex gap-2 w-full overflow-x-auto\">\n {uploadingFiles.map((file, index) => (\n <FileUploadItem\n key={file.id}\n mobile\n fileName={file.name}\n fileExtension={file.fileExtension ?? file.name.split('.').pop()}\n progress={file.progress}\n status={file.status}\n className=\"shrink-0 delight-stagger\"\n style={\n { '--delight-delay': `${index * 60}ms` } as React.CSSProperties\n }\n onRemove={() => onRemoveFile?.(file.id)}\n onCancel={() => onCancelUpload?.(file.id)}\n onRetry={() => onRetryUpload?.(file.id)}\n />\n ))}\n </div>\n <div className=\"flex items-center justify-between w-full\">\n <span className=\"text-xs text-card-foreground\">\n {completedCount} file\n {completedCount !== 1 ? 's' : ''} added\n </span>\n <RemoveAllButton onClick={onRemoveAllFiles} />\n </div>\n <div className=\"w-full h-px bg-border\" />\n </div>\n );\n\n // Inline variant\n if (isInline) {\n return (\n <div\n ref={rootRef}\n data-slot=\"ai-input\"\n data-dragging={isDragging}\n {...dropZoneProps}\n {...(onFilesSelected\n ? { 'aria-label': 'Chat input with file drop zone' }\n : {})}\n className={getAIInputShellClassName({\n state: getState(),\n variant: 'inline',\n className,\n })}\n >\n <div\n data-slot=\"ai-input-inner\"\n className={cn(\n inputVariants({ state: getState(), variant: 'inline' }),\n 'relative bg-transparent',\n hasAttachments && 'flex-col items-stretch py-3',\n )}\n >\n {isDragging && (\n <DropOverlay isDefault={isDefault} dropText={dropText} />\n )}\n {compactFilePreview}\n\n <div className=\"flex items-center gap-2 w-full\">\n {leadingIcon ? (\n <AIInputLeadingIcon>{leadingIcon}</AIInputLeadingIcon>\n ) : null}\n\n <input\n ref={inputRef}\n data-slot=\"ai-input-text\"\n type=\"text\"\n value={inputValue}\n onChange={handleChange}\n onFocus={() => setIsFocused(true)}\n onBlur={() => setIsFocused(false)}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n aria-label={placeholder}\n disabled={disabled}\n className=\"flex-1 min-w-0 bg-transparent text-sm leading-5 text-foreground placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:text-muted-foreground\"\n />\n\n {leadingActions}\n {trailingActions}\n\n <AIInputSubmitButton\n customButton={submitButton}\n onClick={handleSubmit}\n disabled={disabled || !hasText}\n />\n </div>\n </div>\n </div>\n );\n }\n\n // Compact variant\n if (!isDefault) {\n return (\n <div\n ref={rootRef}\n data-slot=\"ai-input\"\n data-dragging={isDragging}\n {...dropZoneProps}\n {...(onFilesSelected\n ? { 'aria-label': 'Chat input with file drop zone' }\n : {})}\n className={getAIInputShellClassName({\n state: getState(),\n variant: 'compact',\n className,\n })}\n >\n <div\n data-slot=\"ai-input-inner\"\n className={cn(\n inputVariants({ state: getState(), variant: 'compact' }),\n 'relative bg-transparent',\n hasAttachments && 'flex-col items-stretch',\n )}\n >\n {isDragging && (\n <DropOverlay isDefault={isDefault} dropText={dropText} />\n )}\n {compactFilePreview}\n\n <div\n data-slot=\"ai-input-text-area\"\n className={cn(\n 'flex gap-2',\n hasAttachments\n ? 'w-full items-start'\n : 'min-w-0 flex-1 items-center',\n )}\n >\n <textarea\n ref={textareaRef}\n data-slot=\"ai-input-textarea\"\n value={inputValue}\n onChange={handleChange}\n onFocus={() => setIsFocused(true)}\n onBlur={() => setIsFocused(false)}\n onKeyDown={handleKeyDown}\n onPaste={onPaste}\n placeholder={placeholder}\n aria-label={placeholder}\n disabled={disabled}\n rows={1}\n className={compactTextareaClasses}\n />\n </div>\n\n <div\n data-slot=\"ai-input-action-bar\"\n className={cn(\n 'flex items-center',\n hasAttachments ? 'justify-between w-full' : 'shrink-0 gap-3',\n )}\n >\n <div\n data-slot=\"ai-input-actions\"\n className=\"flex items-center gap-2\"\n >\n {leadingActions}\n </div>\n <div\n data-slot=\"ai-input-actions\"\n className=\"flex items-center gap-2\"\n >\n {trailingActions}\n {hasText ? (\n <AIInputSubmitButton\n customButton={submitButton}\n onClick={handleSubmit}\n disabled={disabled || !hasText}\n />\n ) : (\n <AIInputDefaultButton\n customButton={defaultButton}\n disabled={disabled}\n />\n )}\n </div>\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <div\n ref={rootRef}\n data-slot=\"ai-input\"\n data-dragging={isDragging}\n {...dropZoneProps}\n {...(onFilesSelected\n ? { 'aria-label': 'Chat input with file drop zone' }\n : {})}\n className={getAIInputShellClassName({\n state: getState(),\n variant: 'default',\n className,\n })}\n >\n <div\n data-slot=\"ai-input-inner\"\n className={cn(\n inputVariants({ state: getState(), variant: 'default' }),\n 'relative bg-transparent',\n )}\n >\n {isDragging && (\n <DropOverlay isDefault={isDefault} dropText={dropText} />\n )}\n {/* Attachments Section (above input, matching Figma layout) */}\n {hasAttachments && (\n <div\n data-slot=\"ai-input-file-preview\"\n className=\"w-full flex flex-col gap-2\"\n >\n <div className=\"flex items-start gap-2 w-full overflow-x-auto\">\n {uploadingFiles.map((file, index) => (\n <FileUploadItem\n key={file.id}\n fileName={file.name}\n fileSize={file.size}\n fileExtension={\n file.fileExtension ?? file.name.split('.').pop()\n }\n progress={file.progress}\n status={file.status}\n className=\"flex-1 min-w-0 delight-stagger\"\n style={\n {\n '--delight-delay': `${index * 60}ms`,\n } as React.CSSProperties\n }\n onRemove={() => onRemoveFile?.(file.id)}\n onCancel={() => onCancelUpload?.(file.id)}\n onRetry={() => onRetryUpload?.(file.id)}\n />\n ))}\n </div>\n\n <div className=\"flex items-end justify-between w-full\">\n <span className=\"text-xs text-card-foreground\">\n {completedCount} file\n {completedCount !== 1 ? 's' : ''} added\n </span>\n <RemoveAllButton onClick={onRemoveAllFiles} />\n </div>\n\n <div className=\"w-full h-px bg-border\" />\n </div>\n )}\n\n <div\n data-slot=\"ai-input-text-area\"\n className=\"flex items-start gap-2 w-full\"\n >\n <textarea\n ref={textareaRef}\n data-slot=\"ai-input-textarea\"\n value={inputValue}\n onChange={handleChange}\n onFocus={() => setIsFocused(true)}\n onBlur={() => setIsFocused(false)}\n onKeyDown={handleKeyDown}\n onPaste={onPaste}\n placeholder={placeholder}\n aria-label={placeholder}\n disabled={disabled}\n rows={1}\n className={textareaClasses}\n />\n {hasText && !disabled && (\n <button\n type=\"button\"\n onClick={handleClear}\n aria-label=\"Clear input\"\n className=\"p-2 rounded-full hover:bg-accent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring transition-colors duration-150 ease-out shrink-0\"\n >\n <X className=\"size-5 text-muted-foreground\" />\n </button>\n )}\n </div>\n\n <div\n data-slot=\"ai-input-action-bar\"\n className=\"flex items-center justify-between w-full\"\n >\n <div data-slot=\"ai-input-actions\" className=\"flex items-center gap-2\">\n {leadingActions}\n </div>\n\n <div data-slot=\"ai-input-actions\" className=\"flex items-center gap-2\">\n {trailingActions}\n {hasText ? (\n <AIInputSubmitButton\n customButton={submitButton}\n onClick={handleSubmit}\n disabled={disabled || !hasText}\n />\n ) : (\n <AIInputDefaultButton\n customButton={defaultButton}\n disabled={disabled}\n />\n )}\n </div>\n </div>\n </div>\n </div>\n );\n}\n\nAIInputDefault.displayName = 'AIInput';\n\n// =============================================================================\n// Export: Object.assign for dual-API\n// =============================================================================\n\n/**\n * AIInput - Dual-API component for AI chat input.\n *\n * Supports two usage patterns:\n *\n * 1. **Monolithic API (batteries-included)**:\n * ```tsx\n * <AIInput\n * placeholder=\"Ask anything...\"\n * onSubmit={handleSubmit}\n * leadingActions={<AIInput.ActionButton icon={Plus} />}\n * />\n * ```\n *\n * 2. **Composable API (full customization)**:\n * ```tsx\n * <AIInput.Root onSubmit={handleSubmit}>\n * <AIInput.TextArea placeholder=\"Ask anything...\" />\n * <AIInput.ActionBar>\n * <AIInput.Actions position=\"leading\">\n * <AIInput.AttachButton icon={Plus} tooltip=\"Add file\" />\n * </AIInput.Actions>\n * <AIInput.Actions position=\"trailing\">\n * <AIInput.SendButton />\n * </AIInput.Actions>\n * </AIInput.ActionBar>\n * <AIInput.FilePreview />\n * </AIInput.Root>\n * ```\n */\nconst AIInput = Object.assign(AIInputDefault, {\n Root: AIInputRoot,\n TextArea: AIInputTextArea,\n LeadingIcon: AIInputLeadingIcon,\n SendButton: AIInputSendButton,\n AttachButton: AIInputAttachButton,\n Actions: AIInputActions,\n ActionBar: AIInputActionBar,\n FilePreview: AIInputFilePreview,\n // Legacy compound components (also available on main export)\n ActionButton,\n SubmitButton,\n CompactActionButton,\n});\n\nexport {\n // Compound components (also available as AIInput.X)\n ActionButton,\n AIInput,\n AIInputActionBar,\n AIInputActions,\n AIInputAttachButton,\n AIInputFilePreview,\n AIInputLeadingIcon,\n // New composable components\n AIInputRoot,\n AIInputSendButton,\n AIInputTextArea,\n CompactActionButton,\n inputVariants,\n SubmitButton,\n // Context hook for custom subcomponents\n useAIInputContext,\n};\n\n// Note: SubmitButtonRenderProps, SubmitButtonRenderFn, DefaultButtonRenderProps,\n// and DefaultButtonRenderFn are exported at their definition sites above\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@ name: humain-ui
|
|
|
3
3
|
description: Use when building, migrating, auditing, or bootstrapping React UI with @humain/ui, including pages, dashboards, app shells, forms, dialogs, data tables, charts, AI chat workspaces, or Figma-to-Humain mapping.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Humain Foundation UI (v2.4.
|
|
6
|
+
# Humain Foundation UI (v2.4.2)
|
|
7
7
|
|
|
8
8
|
Build product UI with `@humain/ui`, a React 18/19 component library with Tailwind v4, dark mode, design tokens, Web Component projections, and AI-facing references.
|
|
9
9
|
|
|
@@ -3,7 +3,7 @@ name: humain-ui-wc
|
|
|
3
3
|
description: Use when building browser-native prototypes, iframe artifacts, static HTML, custom-element demos, framework-neutral integrations, or any HUMAIN UI that must use hf-* Web Components instead of React JSX.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Humain Foundation UI Web Components (v2.4.
|
|
6
|
+
# Humain Foundation UI Web Components (v2.4.2)
|
|
7
7
|
|
|
8
8
|
**Identity** - You build HUMAIN interfaces with `hf-*` custom elements. You keep the same product taste, spacing discipline, and component intent as the React `humain-ui` skill, but you only use Web Component APIs that actually exist in the manifest.
|
|
9
9
|
|
|
Binary file
|
package/skills/humain-ui.skill
CHANGED
|
Binary file
|