@magicx-eng/ai-autocomplete-react 0.1.5 → 0.1.7

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/AIAutocomplete.tsx","../src/AIAutocomplete.module.css","../src/AIAutocompleteDropdown.module.css","../src/components/SuggestionGrid.module.css","../src/components/SuggestionItem.module.css","../src/components/SuggestionItem.tsx","../src/components/SuggestionGrid.tsx","../src/AIAutocompleteDropdown.tsx","../src/components/PillList.module.css","../src/components/PillList.tsx","../src/hooks/useAIAutocomplete.ts","../src/utils/filtering.ts","../src/utils/segments.ts","../src/hooks/useAutocompleteFetch.ts","../src/utils/api.ts","../src/utils/buildQuery.ts","../src/utils/overrides.ts","../src/hooks/useAutocompleteKeyboard.ts","../src/hooks/useAutocompletePills.ts"],"sourcesContent":["import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from \"react\";\nimport styles from \"./AIAutocomplete.module.css\";\nimport { AIAutocompleteDropdown } from \"./AIAutocompleteDropdown\";\nimport { PillList } from \"./components/PillList\";\nimport { useAIAutocomplete } from \"./hooks/useAIAutocomplete\";\nimport type { AIAutocompleteHandle, AIAutocompleteProps, AutocompleteResult } from \"./types\";\nimport { buildQuery } from \"./utils/buildQuery\";\n\nexport const AIAutocomplete = forwardRef<AIAutocompleteHandle, AIAutocompleteProps>(\n function AIAutocomplete(\n {\n onSubmit,\n onError,\n optionOverrides,\n maskCompletedText,\n placeholder,\n className,\n apiConfig,\n columns,\n value,\n completedParams: controlledParams,\n onChange: onChangeProp,\n onParamsChange,\n },\n ref,\n ) {\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n const [showCheckmark, setShowCheckmark] = useState(false);\n // Render-phase ref assignment — matches the pattern in useAIAutocomplete.ts\n const handleSubmitRef = useRef<(result: AutocompleteResult) => void>(() => {});\n const checkmarkTimerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n\n useEffect(() => {\n textareaRef.current?.focus();\n return () => clearTimeout(checkmarkTimerRef.current);\n }, []);\n\n const {\n completedParams,\n suggestionPills,\n setActivePill,\n segments,\n inputProps,\n dropdownProps,\n reset,\n } = useAIAutocomplete({\n onSubmit: (result) => handleSubmitRef.current(result),\n onError,\n optionOverrides,\n maskCompletedText,\n placeholder,\n apiConfig,\n columns,\n value,\n completedParams: controlledParams,\n onChange: onChangeProp,\n onParamsChange,\n });\n\n useImperativeHandle(\n ref,\n () => ({\n focus: () => textareaRef.current?.focus(),\n reset,\n }),\n [reset],\n );\n\n const handleContainerClick = () => {\n textareaRef.current?.focus();\n };\n\n const canSubmit = !!inputProps.value || completedParams.length > 0;\n\n const handleSubmit = useCallback(() => {\n if (!canSubmit) return;\n const { rawQuery, completedParams: finalParams } = buildQuery(\n inputProps.value,\n completedParams,\n );\n onSubmit({\n query: inputProps.value.trim(),\n raw_query: rawQuery,\n completed_params: finalParams,\n });\n reset();\n setShowCheckmark(true);\n clearTimeout(checkmarkTimerRef.current);\n checkmarkTimerRef.current = setTimeout(() => setShowCheckmark(false), 3000);\n }, [canSubmit, inputProps.value, completedParams, onSubmit, reset]);\n\n handleSubmitRef.current = handleSubmit;\n\n const { onChange, placeholder: inputPlaceholder, ...restProps } = inputProps;\n const isEmpty = !inputProps.value;\n\n return (\n <div className={`${styles.container} ${className ?? \"\"}`}>\n <div className={`${styles.checkmark} ${showCheckmark ? styles.checkmarkVisible : \"\"}`}>\n <svg\n width=\"72\"\n height=\"72\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n role=\"img\"\n aria-label=\"Success\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"12\" fill=\"#34C759\" />\n <path\n d=\"M7 12.5l3.5 3.5L17 9\"\n stroke=\"#000\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={styles.checkmarkPath}\n />\n </svg>\n </div>\n <AIAutocompleteDropdown {...dropdownProps} />\n {/* biome-ignore lint/a11y/useKeyWithClickEvents: container click delegates to textarea focus */}\n {/* biome-ignore lint/a11y/noStaticElementInteractions: wrapper delegates focus to textarea */}\n <div className={styles.inputWrapper} onClick={handleContainerClick}>\n <div className={styles.editorArea}>\n <div className={styles.sizerContent} aria-hidden=\"true\">\n {isEmpty && inputPlaceholder ? (\n <span className={styles.placeholderText}>{inputPlaceholder} </span>\n ) : (\n <span className={styles.sizerText}>\n {segments.map((seg, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: segments are positional and don't reorder\n <span key={`${i}-${seg.type}`}>{seg.value}</span>\n ))}\n {segments.length === 0 && \"\\u00A0\"}\n </span>\n )}\n {\" \"}\n <PillList pills={suggestionPills} activePillIndex={0} onSelectPill={setActivePill} />\n </div>\n <textarea\n ref={textareaRef}\n className={styles.textarea}\n rows={1}\n onChange={onChange}\n {...restProps}\n />\n </div>\n <button\n type=\"button\"\n className={styles.submitButton}\n disabled={!canSubmit}\n onClick={(e) => {\n e.stopPropagation();\n handleSubmit();\n }}\n aria-label=\"Submit\"\n >\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 18 18\"\n fill=\"none\"\n role=\"img\"\n aria-label=\"Submit\"\n >\n <path\n d=\"M9 14V4M9 4L4 9M9 4L14 9\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </button>\n </div>\n </div>\n );\n },\n);\n",".container {\n position: relative;\n font-family: \"IBM Plex Sans\", sans-serif;\n}\n\n.checkmark {\n position: absolute;\n bottom: -130px;\n left: 50%;\n transform: translateX(-50%) translateY(8px) scale(0.8);\n opacity: 0;\n pointer-events: none;\n z-index: 10;\n animation: none;\n}\n\n.checkmarkVisible {\n animation: checkmarkFadeInOut 3s ease forwards;\n}\n\n@keyframes checkmarkFadeInOut {\n 0% {\n opacity: 0;\n transform: translateX(-50%) translateY(8px) scale(0.8);\n }\n 10% {\n opacity: 1;\n transform: translateX(-50%) translateY(0) scale(1);\n }\n 80% {\n opacity: 1;\n transform: translateX(-50%) translateY(0) scale(1);\n }\n 100% {\n opacity: 0;\n transform: translateX(-50%) translateY(-8px) scale(0.8);\n }\n}\n\n.checkmarkPath {\n stroke-dasharray: 30;\n stroke-dashoffset: 30;\n}\n\n.checkmarkVisible .checkmarkPath {\n animation: drawCheck 0.4s ease forwards 0.1s;\n}\n\n@keyframes drawCheck {\n to {\n stroke-dashoffset: 0;\n }\n}\n\n.inputWrapper {\n min-height: 60px;\n padding: 24px;\n border: 1px solid var(--ac-color-border-default, #9ea5b2);\n border-radius: 23px;\n background: transparent;\n overflow: hidden;\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.editorArea {\n position: relative;\n flex: 1;\n min-width: 0;\n}\n\n.sizerContent {\n position: relative;\n z-index: 1;\n pointer-events: none;\n min-height: 60px;\n white-space: pre-wrap;\n word-break: break-word;\n font-family: inherit;\n font-size: 21px;\n line-height: 38px;\n}\n\n.sizerText {\n color: transparent;\n}\n\n.placeholderText {\n color: var(--ac-color-text-muted, #c1c4cb);\n opacity: 0.7;\n}\n\n.textarea {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n padding: 0;\n border: none;\n background: transparent;\n color: var(--ac-color-text-default, #fff);\n caret-color: var(--ac-color-text-default, #fff);\n font-family: inherit;\n font-size: 21px;\n line-height: 38px;\n white-space: pre-wrap;\n word-break: break-word;\n outline: none;\n resize: none;\n overflow: hidden;\n}\n\n.textarea::placeholder {\n color: var(--ac-color-text-muted, #c1c4cb);\n}\n\n.submitButton {\n flex-shrink: 0;\n width: 36px;\n height: 36px;\n border-radius: 50%;\n border: none;\n background: var(--ac-color-text-default, #fff);\n color: var(--ac-color-bg-default, #000);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0;\n transition: opacity 0.2s ease;\n}\n\n.submitButton:hover {\n opacity: 0.85;\n}\n",".dropdown {\n position: absolute;\n left: 0;\n right: 0;\n top: 100%;\n margin-top: 6px;\n background: var(--ac-color-background-default, #00002d);\n border-radius: 23px;\n overflow: hidden;\n z-index: 10;\n opacity: 0;\n pointer-events: none;\n transition: opacity 400ms cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.visible {\n opacity: 1;\n pointer-events: auto;\n}\n",".grid {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px 18px;\n padding: 18px 24px;\n max-height: 192px;\n overflow-y: auto;\n scrollbar-width: thin;\n scrollbar-color: rgba(255, 255, 255, 0.3) transparent;\n}\n\n.grid::-webkit-scrollbar {\n width: 6px;\n}\n\n.grid::-webkit-scrollbar-track {\n background: transparent;\n}\n\n.grid::-webkit-scrollbar-thumb {\n background: rgba(255, 255, 255, 0.3);\n border-radius: 3px;\n}\n",".item {\n display: flex;\n align-items: center;\n font-family: \"IBM Plex Sans\", sans-serif;\n font-size: 21px;\n line-height: 30px;\n color: var(--ac-color-text-muted, #c1c4cb);\n white-space: nowrap;\n opacity: 0.35;\n animation: fadeIn 400ms cubic-bezier(0.4, 0, 0.2, 1) forwards;\n}\n\n@keyframes fadeIn {\n from {\n opacity: 0;\n }\n}\n\n.tappable {\n cursor: pointer;\n}\n\n.tappable:hover {\n color: var(--ac-color-text-default, #fff);\n}\n\n.nonTappable {\n cursor: default;\n opacity: 0.3;\n}\n\n.highlighted {\n color: var(--ac-color-text-default, #fff);\n opacity: 0.5;\n}\n\n.tag {\n font-size: 13px;\n margin-left: 6px;\n opacity: 0.5;\n}\n","import type { SuggestionOption } from \"../types\";\nimport styles from \"./SuggestionItem.module.css\";\n\ninterface SuggestionItemProps {\n option: SuggestionOption;\n isHighlighted: boolean;\n onSelect: (option: SuggestionOption) => void;\n onHighlight: () => void;\n id: string;\n}\n\nexport function SuggestionItem({\n option,\n isHighlighted,\n onSelect,\n onHighlight,\n id,\n}: SuggestionItemProps) {\n const className = [\n styles.item,\n isHighlighted ? styles.highlighted : \"\",\n option.is_tappable ? styles.tappable : styles.nonTappable,\n ]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <div\n id={id}\n role=\"option\"\n aria-selected={isHighlighted}\n className={className}\n tabIndex={option.is_tappable ? 0 : -1}\n onClick={() => option.is_tappable && onSelect(option)}\n onKeyDown={(e) => {\n if (option.is_tappable && (e.key === \"Enter\" || e.key === \" \")) {\n e.preventDefault();\n onSelect(option);\n }\n }}\n onMouseEnter={onHighlight}\n >\n {option.icon ? `${option.icon} ${option.text}` : option.text}\n {option.tag && <span className={styles.tag}>{option.tag}</span>}\n </div>\n );\n}\n","import type { SuggestionOption } from \"../types\";\nimport styles from \"./SuggestionGrid.module.css\";\nimport { SuggestionItem } from \"./SuggestionItem\";\n\ninterface SuggestionGridProps {\n options: SuggestionOption[];\n activeIndex: number;\n onSelect: (option: SuggestionOption) => void;\n onHighlight: (index: number) => void;\n listboxId: string;\n}\n\nexport function SuggestionGrid({\n options,\n activeIndex,\n onSelect,\n onHighlight,\n listboxId,\n}: SuggestionGridProps) {\n return (\n <div className={styles.grid}>\n {options.map((option, i) => (\n <SuggestionItem\n key={option.text}\n option={option}\n isHighlighted={i === activeIndex}\n onSelect={onSelect}\n onHighlight={() => onHighlight(i)}\n id={`${listboxId}-option-${i}`}\n />\n ))}\n </div>\n );\n}\n","import styles from \"./AIAutocompleteDropdown.module.css\";\nimport { SuggestionGrid } from \"./components/SuggestionGrid\";\nimport type { AIAutocompleteDropdownProps } from \"./types\";\n\nexport function AIAutocompleteDropdown({\n suggestions,\n activeIndex,\n onSelect,\n onHighlight,\n isOpen,\n id,\n className,\n}: AIAutocompleteDropdownProps) {\n const activeSuggestion = suggestions[0];\n const hasOptions = isOpen && activeSuggestion && activeSuggestion.options.length > 0;\n\n return (\n <div\n id={id}\n role=\"listbox\"\n className={`${styles.dropdown} ${hasOptions ? styles.visible : \"\"} ${className ?? \"\"}`}\n onMouseDown={(e) => e.preventDefault()}\n >\n {activeSuggestion && activeSuggestion.options.length > 0 && (\n <SuggestionGrid\n options={activeSuggestion.options}\n activeIndex={activeIndex}\n onSelect={onSelect}\n onHighlight={onHighlight}\n listboxId={id}\n />\n )}\n </div>\n );\n}\n",".list {\n position: relative;\n z-index: 1;\n pointer-events: auto;\n display: inline-flex;\n gap: 5px;\n align-items: center;\n vertical-align: middle;\n transform: translateY(-3px);\n}\n\n.pill {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n height: 36px;\n padding: 7px 9px;\n border: none;\n border-radius: 6px;\n background: var(--ac-color-background-supportive, #313255);\n color: var(--ac-color-text-muted, #c1c4cb);\n font-family: \"IBM Plex Sans\", sans-serif;\n font-size: 21px;\n line-height: 30px;\n cursor: pointer;\n white-space: nowrap;\n animation: fadeIn 400ms cubic-bezier(0.4, 0, 0.2, 1) forwards;\n}\n\n.pill:hover {\n filter: brightness(1.2);\n}\n\n.active {\n outline: 1px solid #5a5b8a;\n}\n\n@keyframes fadeIn {\n from {\n opacity: 0;\n }\n}\n","import type { Suggestion } from \"../types\";\nimport styles from \"./PillList.module.css\";\n\ninterface PillListProps {\n pills: Suggestion[];\n activePillIndex: number;\n onSelectPill: (index: number) => void;\n}\n\nfunction getPillOpacity(index: number): number {\n if (index === 0) return 0.4;\n if (index === 1) return 0.3;\n return 0.15;\n}\n\nexport function PillList({ pills, activePillIndex, onSelectPill }: PillListProps) {\n return (\n <span className={styles.list}>\n {pills.map((pill, i) => (\n <button\n key={`${pill.type}-${pill.text}`}\n type=\"button\"\n className={`${styles.pill} ${i === activePillIndex ? styles.active : \"\"}`}\n style={{ opacity: getPillOpacity(i) }}\n onClick={() => onSelectPill(i)}\n >\n {pill.text}\n </button>\n ))}\n </span>\n );\n}\n","import { type ChangeEvent, useCallback, useId, useMemo, useRef, useState } from \"react\";\nimport type {\n CompletedParamState,\n Suggestion,\n SuggestionOption,\n UseAIAutocompleteOptions,\n UseAIAutocompleteReturn,\n} from \"../types\";\nimport { filterOptions, findExactMatch } from \"../utils/filtering\";\nimport { deriveSegments, reconcileParams } from \"../utils/segments\";\nimport { useAutocompleteFetch, useDebouncedFetch } from \"./useAutocompleteFetch\";\nimport { useAutocompleteKeyboard } from \"./useAutocompleteKeyboard\";\nimport { useAutocompletePills } from \"./useAutocompletePills\";\n\nexport function useAIAutocomplete({\n onSubmit,\n onError,\n optionOverrides,\n maskCompletedText,\n placeholder: customPlaceholder,\n apiConfig,\n columns = 2,\n value: controlledValue,\n completedParams: controlledParams,\n onChange: onChangeProp,\n onParamsChange,\n}: UseAIAutocompleteOptions): UseAIAutocompleteReturn {\n // === Controlled / uncontrolled state ===\n const isTextControlled = controlledValue !== undefined;\n const isParamsControlled = controlledParams !== undefined;\n\n const [internalText, setInternalText] = useState(\"\");\n const [internalCompletedParams, setInternalCompletedParams] = useState<CompletedParamState[]>([]);\n const [suggestions, setSuggestions] = useState<Suggestion[]>([]);\n const [activeDropdownIndex, setActiveDropdownIndex] = useState(-1);\n\n const text = isTextControlled ? controlledValue : internalText;\n const completedParams = isParamsControlled ? controlledParams : internalCompletedParams;\n\n // === Refs — must be before setText/setCompletedParams which read them ===\n const onSubmitRef = useRef(onSubmit);\n onSubmitRef.current = onSubmit;\n const onChangeRef = useRef(onChangeProp);\n onChangeRef.current = onChangeProp;\n const onParamsChangeRef = useRef(onParamsChange);\n onParamsChangeRef.current = onParamsChange;\n const controlledValueRef = useRef(controlledValue);\n controlledValueRef.current = controlledValue;\n const controlledParamsRef = useRef(controlledParams);\n controlledParamsRef.current = controlledParams;\n\n // Stable setText that reads controlled value + callback via refs (no stale closures)\n const setText = useCallback(\n (value: string | ((prev: string) => string)) => {\n if (typeof value === \"function\") {\n if (isTextControlled) {\n const newVal = value(controlledValueRef.current ?? \"\");\n onChangeRef.current?.(newVal);\n } else {\n setInternalText((prev) => {\n const newVal = value(prev);\n onChangeRef.current?.(newVal);\n return newVal;\n });\n }\n } else {\n if (!isTextControlled) setInternalText(value);\n onChangeRef.current?.(value);\n }\n },\n [isTextControlled],\n );\n\n // Stable setCompletedParams — same ref pattern\n const setCompletedParams = useCallback(\n (value: CompletedParamState[] | ((prev: CompletedParamState[]) => CompletedParamState[])) => {\n if (typeof value === \"function\") {\n if (isParamsControlled) {\n const newVal = value(controlledParamsRef.current ?? []);\n onParamsChangeRef.current?.(newVal);\n } else {\n setInternalCompletedParams((prev) => {\n const newVal = value(prev);\n onParamsChangeRef.current?.(newVal);\n return newVal;\n });\n }\n } else {\n if (!isParamsControlled) setInternalCompletedParams(value);\n onParamsChangeRef.current?.(value);\n }\n },\n [isParamsControlled],\n );\n const onErrorRef = useRef(onError);\n onErrorRef.current = onError;\n const optionOverridesRef = useRef(optionOverrides);\n optionOverridesRef.current = optionOverrides;\n const maskCompletedTextRef = useRef(maskCompletedText);\n maskCompletedTextRef.current = maskCompletedText;\n const apiConfigRef = useRef(apiConfig);\n apiConfigRef.current = apiConfig;\n const textRef = useRef(text);\n textRef.current = text;\n const suggestionsRef = useRef(suggestions);\n suggestionsRef.current = suggestions;\n const filterBaseRef = useRef(0);\n const pillTappedRef = useRef(false);\n const skipNextFetchRef = useRef(false);\n const listboxId = useId();\n\n // === Fetch ===\n const { doFetch, isLoading, error, isReady, lastRawQueryRef } = useAutocompleteFetch({\n textRef,\n suggestionsRef,\n filterBaseRef,\n maskCompletedTextRef,\n apiConfigRef,\n optionOverridesRef,\n onErrorRef,\n setCompletedParams,\n setSuggestions,\n setActiveDropdownIndex,\n });\n\n useDebouncedFetch({\n text,\n completedParams,\n doFetch,\n filterBaseRef,\n skipNextFetchRef,\n suggestionsRef,\n lastRawQueryRef,\n });\n\n // === Derived ===\n const segments = useMemo(() => deriveSegments(text, completedParams), [text, completedParams]);\n\n filterBaseRef.current = Math.min(filterBaseRef.current, text.length);\n const filterQuery = text.slice(filterBaseRef.current);\n\n const placeholderText = useMemo(() => {\n const serverPlaceholder = suggestions\n .filter((s) => s.type === \"placeholder\")\n .map((s) => s.text)\n .join(\" \");\n return serverPlaceholder || customPlaceholder || \"\";\n }, [suggestions, customPlaceholder]);\n const actionableSuggestions = useMemo(\n () => suggestions.filter((s) => s.type !== \"placeholder\"),\n [suggestions],\n );\n const activeSuggestion: Suggestion | undefined = actionableSuggestions[0];\n const overrideFn = activeSuggestion ? optionOverrides?.[activeSuggestion.type] : undefined;\n const baseOptions = activeSuggestion\n ? overrideFn\n ? overrideFn(filterQuery.trim())\n : (activeSuggestion.options ?? [])\n : [];\n const filteredOptions = useMemo(\n () => filterOptions(baseOptions, filterQuery),\n [baseOptions, filterQuery],\n );\n const hasPlaceholder = placeholderText.length > 0;\n const isDropdownOpen =\n !isLoading &&\n filteredOptions.length > 0 &&\n (!!text || pillTappedRef.current || !hasPlaceholder);\n\n // === Option selection ===\n const selectOption = useCallback(\n (option: SuggestionOption) => {\n if (!activeSuggestion) return;\n\n const completed: CompletedParamState = {\n id: crypto.randomUUID(),\n placeholder: \"\",\n type: activeSuggestion.type,\n text: option.text,\n kind: option.kind,\n suggestionType: activeSuggestion.type,\n suggestionPlaceholder: activeSuggestion.text,\n options: activeSuggestion.options,\n metadata: option.metadata,\n };\n\n const base = filterBaseRef.current;\n let prefix = textRef.current.slice(0, base);\n if (prefix.length > 0 && !prefix.endsWith(\" \")) {\n const lastWord = prefix.split(/\\s+/).pop() ?? \"\";\n if (lastWord && option.text.toLowerCase().startsWith(lastWord.toLowerCase())) {\n prefix = prefix.slice(0, prefix.length - lastWord.length);\n }\n }\n\n const needsSpace = prefix.length > 0 && prefix[prefix.length - 1] !== \" \";\n const newText = prefix + (needsSpace ? \" \" : \"\") + option.text + \" \";\n setText(newText);\n filterBaseRef.current = newText.length;\n setCompletedParams((prev) => [...prev, completed]);\n setSuggestions((prev) => prev.filter((s) => s !== activeSuggestion));\n pillTappedRef.current = false;\n setActiveDropdownIndex(-1);\n const remainingActionable = actionableSuggestions.length - 1;\n if (remainingActionable > 0) {\n skipNextFetchRef.current = true;\n }\n },\n [activeSuggestion, actionableSuggestions, setText, setCompletedParams],\n );\n\n // === Text change handler ===\n const handleChange = useCallback(\n (e: ChangeEvent<HTMLTextAreaElement>) => {\n const raw = e.target.value;\n const shouldCapitalize =\n raw.length > 0 &&\n !(e.nativeEvent as InputEvent)?.isComposing &&\n raw[0] !== raw[0].toUpperCase();\n const newValue = shouldCapitalize ? raw[0].toUpperCase() + raw.slice(1) : raw;\n setText(newValue);\n pillTappedRef.current = false;\n setActiveDropdownIndex(-1);\n\n const { valid, invalid } = reconcileParams(newValue, completedParams);\n if (invalid.length > 0) {\n setCompletedParams(() => valid);\n for (const param of invalid) {\n setSuggestions((prev) => [\n {\n type: param.suggestionType,\n text: param.suggestionPlaceholder,\n required: true,\n options: param.options,\n },\n ...prev,\n ]);\n }\n }\n\n if (activeSuggestion && invalid.length === 0) {\n const newFilterQuery = newValue.slice(filterBaseRef.current);\n const match = findExactMatch(activeSuggestion.options, newFilterQuery);\n if (match) {\n setCompletedParams((prev) => [\n ...prev,\n {\n id: crypto.randomUUID(),\n placeholder: \"\",\n type: activeSuggestion.type,\n text: match.text,\n kind: match.kind,\n suggestionType: activeSuggestion.type,\n suggestionPlaceholder: activeSuggestion.text,\n options: activeSuggestion.options,\n metadata: match.metadata,\n },\n ]);\n setSuggestions((prev) => prev.filter((s) => s !== activeSuggestion));\n }\n }\n },\n [completedParams, activeSuggestion, setText, setCompletedParams],\n );\n\n // === Keyboard ===\n const { handleKeyDown } = useAutocompleteKeyboard({\n activeDropdownIndex,\n setActiveDropdownIndex,\n filteredOptions,\n selectOption,\n onSubmitRef,\n text,\n completedParams,\n isDropdownOpen,\n hasPlaceholder,\n placeholderText,\n suggestions,\n filterBaseRef,\n columns,\n setText,\n setCompletedParams,\n setSuggestions,\n });\n\n // === Pills ===\n const pills = useAutocompletePills({\n completedParams,\n suggestions,\n setCompletedParams,\n setSuggestions,\n setActiveDropdownIndex,\n filterBaseRef,\n pillTappedRef,\n });\n\n const reEditParam = useCallback(\n (param: CompletedParamState) => {\n pills.reEditParam(param).apply(setText);\n },\n [pills, setText],\n );\n\n // === Reset ===\n const reset = useCallback(() => {\n setText(\"\");\n setCompletedParams(() => []);\n setSuggestions([]);\n setActiveDropdownIndex(-1);\n filterBaseRef.current = 0;\n lastRawQueryRef.current = \"\";\n doFetch(\"\", []);\n }, [doFetch, setText, setCompletedParams, lastRawQueryRef]);\n\n const activeDescendantId =\n activeDropdownIndex >= 0 ? `${listboxId}-option-${activeDropdownIndex}` : undefined;\n\n return {\n completedParams,\n suggestionPills: actionableSuggestions,\n setActivePill: pills.setActivePill,\n removeLastParam: pills.removeLastParam,\n reEditParam,\n segments,\n suggestions,\n activeIndex: activeDropdownIndex,\n isReady,\n isLoading,\n error,\n inputProps: {\n value: text,\n placeholder: placeholderText || undefined,\n onChange: handleChange,\n onKeyDown: handleKeyDown,\n role: \"combobox\" as const,\n \"aria-expanded\": isDropdownOpen,\n \"aria-activedescendant\": activeDescendantId,\n \"aria-autocomplete\": \"list\" as const,\n \"aria-controls\": listboxId,\n },\n reset,\n dropdownProps: {\n suggestions: activeSuggestion ? [{ ...activeSuggestion, options: filteredOptions }] : [],\n activeIndex: activeDropdownIndex,\n onSelect: selectOption,\n onHighlight: setActiveDropdownIndex,\n isOpen: isDropdownOpen,\n id: listboxId,\n },\n };\n}\n","import type { SuggestionOption } from \"../types\";\n\n/**\n * Filters options using partial substring match on the text after the last completed param.\n */\nexport function filterOptions(options: SuggestionOption[], query: string): SuggestionOption[] {\n const trimmed = query.trimStart();\n if (!trimmed) return options;\n const lower = trimmed.toLowerCase();\n return options.filter((o) => !o.is_tappable || o.text.toLowerCase().includes(lower));\n}\n\n/**\n * Finds an exact match for the trimmed filter query against options.\n */\nexport function findExactMatch(\n options: SuggestionOption[],\n query: string,\n): SuggestionOption | null {\n const trimmed = query.trim();\n if (!trimmed) return null;\n const lower = trimmed.toLowerCase();\n return options.find((o) => o.is_tappable && o.text.toLowerCase() === lower) ?? null;\n}\n","import type { CompletedParamState, Segment } from \"../types\";\n\n/**\n * Derives segments for overlay rendering by matching completed params in text.\n */\nexport function deriveSegments(text: string, completedParams: CompletedParamState[]): Segment[] {\n const result: Segment[] = [];\n let pos = 0;\n\n for (const param of completedParams) {\n const idx = text.indexOf(param.text, pos);\n if (idx === -1) continue;\n if (idx > pos) {\n result.push({ type: \"text\", value: text.slice(pos, idx) });\n }\n result.push({ type: \"completed\", value: param.text, param });\n pos = idx + param.text.length;\n }\n\n const remaining = text.slice(pos);\n if (remaining) {\n result.push({ type: \"text\", value: remaining });\n }\n\n return result;\n}\n\n/**\n * Checks which completed params still exist in the new text.\n */\nexport function reconcileParams(\n text: string,\n completedParams: CompletedParamState[],\n): { valid: CompletedParamState[]; invalid: CompletedParamState[] } {\n const valid: CompletedParamState[] = [];\n const invalid: CompletedParamState[] = [];\n let pos = 0;\n\n for (const param of completedParams) {\n const idx = text.indexOf(param.text, pos);\n if (idx === -1) {\n invalid.push(param);\n } else {\n valid.push(param);\n pos = idx + param.text.length;\n }\n }\n\n return { valid, invalid };\n}\n","import { type MutableRefObject, useCallback, useEffect, useRef, useState } from \"react\";\nimport type {\n APIConfig,\n CompletedParamState,\n OptionOverrides,\n Suggestion,\n SuggestionOption,\n} from \"../types\";\nimport { fetchSuggestions } from \"../utils/api\";\nimport { buildQuery } from \"../utils/buildQuery\";\nimport { filterOptions, findExactMatch } from \"../utils/filtering\";\nimport { applyOptionOverrides } from \"../utils/overrides\";\n\nconst DEBOUNCE_MS = 100;\nconst SLOW_DEBOUNCE_MS = 300;\nconst MIN_CHARS_DIFF = 2;\n\ninterface UseAutocompleteFetchOptions {\n textRef: MutableRefObject<string>;\n suggestionsRef: MutableRefObject<Suggestion[]>;\n filterBaseRef: MutableRefObject<number>;\n maskCompletedTextRef: MutableRefObject<boolean | undefined>;\n apiConfigRef: MutableRefObject<APIConfig | undefined>;\n optionOverridesRef: MutableRefObject<OptionOverrides | undefined>;\n onErrorRef: MutableRefObject<((error: Error) => void) | undefined>;\n setCompletedParams: (fn: (prev: CompletedParamState[]) => CompletedParamState[]) => void;\n setSuggestions: (fn: Suggestion[] | ((prev: Suggestion[]) => Suggestion[])) => void;\n setActiveDropdownIndex: (index: number) => void;\n}\n\ninterface UseAutocompleteFetchReturn {\n doFetch: (rawQuery: string, completed: CompletedParamState[]) => Promise<void>;\n isLoading: boolean;\n error: Error | null;\n isReady: boolean;\n lastRawQueryRef: MutableRefObject<string>;\n}\n\nexport function useAutocompleteFetch({\n textRef,\n suggestionsRef,\n filterBaseRef,\n maskCompletedTextRef,\n apiConfigRef,\n optionOverridesRef,\n onErrorRef,\n setCompletedParams,\n setSuggestions,\n setActiveDropdownIndex,\n}: UseAutocompleteFetchOptions): UseAutocompleteFetchReturn {\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [isReady, setIsReady] = useState(false);\n const fetchVersionRef = useRef(0);\n const abortRef = useRef<AbortController | null>(null);\n const lastRawQueryRef = useRef(\"\");\n\n const doFetch = useCallback(\n async (rawQuery: string, completed: CompletedParamState[]) => {\n abortRef.current?.abort();\n const controller = new AbortController();\n abortRef.current = controller;\n const version = ++fetchVersionRef.current;\n const textAtRequest = textRef.current.length;\n\n const hasSuggestions = suggestionsRef.current.some((s) => s.type !== \"placeholder\");\n if (!hasSuggestions) setIsLoading(true);\n setError(null);\n try {\n const rawCount = completed.find(\n (p) => p.type === \"contact\" && p.metadata?.contact_account_count,\n )?.metadata?.contact_account_count;\n\n const res = await fetchSuggestions(rawQuery, completed, {\n maskCompletedText: maskCompletedTextRef.current,\n signal: controller.signal,\n contactAccountCount: typeof rawCount === \"number\" ? rawCount : undefined,\n apiConfig: apiConfigRef.current,\n });\n\n if (version !== fetchVersionRef.current) return;\n\n let newSuggestions = applyOptionOverrides(\n res.data.suggestions ?? [],\n optionOverridesRef.current,\n );\n setIsReady(res.data.is_ready ?? false);\n lastRawQueryRef.current = rawQuery;\n\n const input = res.data.input ?? [];\n const lastInput = input[input.length - 1];\n const currentText = textRef.current;\n if (lastInput?.state === \"in_progress\") {\n const inProgressIdx = currentText.toLowerCase().lastIndexOf(lastInput.text.toLowerCase());\n if (inProgressIdx !== -1) {\n filterBaseRef.current = inProgressIdx;\n } else {\n filterBaseRef.current = textAtRequest;\n }\n } else {\n filterBaseRef.current = textAtRequest;\n }\n\n // Check if the user already typed an exact match while waiting\n const actionable = newSuggestions.filter((s) => s.type !== \"placeholder\");\n const active = actionable[0];\n if (active) {\n const query = currentText.slice(filterBaseRef.current);\n const match = findExactMatch(active.options, query);\n if (match) {\n setCompletedParams((prev) => [\n ...prev,\n {\n id: crypto.randomUUID(),\n placeholder: \"\",\n type: active.type,\n text: match.text,\n kind: match.kind,\n suggestionType: active.type,\n suggestionPlaceholder: active.text,\n options: active.options,\n metadata: match.metadata,\n },\n ]);\n newSuggestions = newSuggestions.filter((s) => s !== active);\n }\n }\n\n setSuggestions(newSuggestions);\n setIsLoading(false);\n setActiveDropdownIndex(-1);\n } catch (err) {\n if (version === fetchVersionRef.current) {\n const caughtError = err instanceof Error ? err : new Error(String(err));\n setError(caughtError);\n setIsLoading(false);\n onErrorRef.current?.(caughtError);\n }\n }\n },\n [\n textRef,\n suggestionsRef,\n filterBaseRef,\n maskCompletedTextRef,\n apiConfigRef,\n optionOverridesRef,\n onErrorRef,\n setCompletedParams,\n setSuggestions,\n setActiveDropdownIndex,\n ],\n );\n\n // Mount fetch\n useEffect(() => {\n doFetch(\"\", []);\n return () => {\n abortRef.current?.abort();\n };\n }, [doFetch]);\n\n return { doFetch, isLoading, error, isReady, lastRawQueryRef };\n}\n\ninterface UseDebouncedFetchOptions {\n text: string;\n completedParams: CompletedParamState[];\n doFetch: (rawQuery: string, completed: CompletedParamState[]) => Promise<void>;\n filterBaseRef: MutableRefObject<number>;\n skipNextFetchRef: MutableRefObject<boolean>;\n suggestionsRef: MutableRefObject<Suggestion[]>;\n lastRawQueryRef: MutableRefObject<string>;\n}\n\nexport function useDebouncedFetch({\n text,\n completedParams,\n doFetch,\n filterBaseRef,\n skipNextFetchRef,\n suggestionsRef,\n lastRawQueryRef,\n}: UseDebouncedFetchOptions): void {\n const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const slowDebounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const hasFetchedRef = useRef(true);\n\n useEffect(() => {\n if (debounceRef.current) clearTimeout(debounceRef.current);\n if (slowDebounceRef.current) clearTimeout(slowDebounceRef.current);\n\n const attemptFetch = (minDiff: number): boolean => {\n if (skipNextFetchRef.current) {\n skipNextFetchRef.current = false;\n return false;\n }\n\n if (!text && completedParams.length === 0) {\n if (!hasFetchedRef.current) {\n hasFetchedRef.current = true;\n return false;\n }\n doFetch(\"\", []);\n return true;\n }\n\n const currentQuery = text.slice(filterBaseRef.current);\n const currentSuggestions = suggestionsRef.current;\n const actionable = currentSuggestions.filter((s: Suggestion) => s.type !== \"placeholder\");\n const active = actionable[0];\n const currentFiltered = active ? filterOptions(active.options, currentQuery) : [];\n const tappableFiltered = currentFiltered.filter((o: SuggestionOption) => o.is_tappable);\n const hasExactMatch = active ? findExactMatch(active.options, currentQuery) !== null : false;\n\n const isInFilterZone = currentQuery.trim().length > 0;\n if (tappableFiltered.length > 0 && !hasExactMatch && isInFilterZone) return false;\n\n const { rawQuery, completedParams: updatedParams } = buildQuery(text, completedParams);\n const isDeleting = rawQuery.length < lastRawQueryRef.current.length;\n const charDiff = Math.abs(rawQuery.length - lastRawQueryRef.current.length);\n if (isDeleting || charDiff >= minDiff) {\n doFetch(rawQuery, updatedParams);\n return true;\n }\n return false;\n };\n\n debounceRef.current = setTimeout(() => {\n if (attemptFetch(MIN_CHARS_DIFF)) {\n if (slowDebounceRef.current) clearTimeout(slowDebounceRef.current);\n }\n }, DEBOUNCE_MS);\n\n slowDebounceRef.current = setTimeout(() => attemptFetch(1), SLOW_DEBOUNCE_MS);\n\n return () => {\n if (debounceRef.current) clearTimeout(debounceRef.current);\n if (slowDebounceRef.current) clearTimeout(slowDebounceRef.current);\n };\n }, [\n text,\n completedParams,\n doFetch,\n filterBaseRef,\n skipNextFetchRef,\n suggestionsRef,\n lastRawQueryRef,\n ]);\n}\n","import type {\n APIConfig,\n AutocompleteRequest,\n AutocompleteResponse,\n CompletedParam,\n CompletedParamState,\n} from \"../types\";\n\ndeclare const __SDK_VERSION__: string;\nconst SDK_VERSION = typeof __SDK_VERSION__ !== \"undefined\" ? __SDK_VERSION__ : \"0.0.0\";\n\n// process.env.* values are replaced at build time by the bundler's `define` config.\n// Use optional chaining (process?.env), NOT `typeof process !== \"undefined\"` guards —\n// the typeof guard prevents the replacement from taking effect in browsers.\ndeclare const process: { env: Record<string, string | undefined> } | undefined;\nconst API_ENDPOINT = process?.env.MAGICX_API_ENDPOINT || \"/api/suggest\";\n\nlet hasWarnedMissingKey = false;\n\nfunction getApiKey(config?: APIConfig): string {\n const key = config?.apiKey || process?.env.MAGICX_AI_AUTOCOMPLETE_API_KEY || \"\";\n if (!key && !hasWarnedMissingKey) {\n hasWarnedMissingKey = true;\n // biome-ignore lint/suspicious/noConsole: intentional dev warning\n console.warn(\n \"[AIAutocomplete] No API key set (MAGICX_AI_AUTOCOMPLETE_API_KEY). \" +\n \"Requests will be sent without an Authorization header.\",\n );\n }\n return key;\n}\n\nfunction getAuthScheme(config?: APIConfig): \"Bearer\" | \"Basic\" {\n if (config?.authScheme) return config.authScheme;\n const scheme = process?.env.MAGICX_AUTH_SCHEME;\n return scheme === \"Basic\" ? \"Basic\" : \"Bearer\";\n}\n\nfunction generateRequestId(): string {\n return crypto.randomUUID();\n}\n\nfunction toWireParam(param: CompletedParamState, includeText: boolean): CompletedParam {\n return {\n placeholder: param.placeholder,\n type: param.type,\n ...(includeText && { text: param.text }),\n kind: param.kind,\n };\n}\n\nexport async function fetchSuggestions(\n rawQuery: string,\n completedParams: CompletedParamState[],\n options?: {\n maskCompletedText?: boolean;\n signal?: AbortSignal;\n contactAccountCount?: number;\n apiConfig?: APIConfig;\n },\n): Promise<AutocompleteResponse> {\n const apiConfig = options?.apiConfig;\n const apiKey = getApiKey(apiConfig);\n const authScheme = getAuthScheme(apiConfig);\n const includeText = !options?.maskCompletedText;\n const contactAccountCount = options?.contactAccountCount;\n\n const body: AutocompleteRequest = {\n data: {\n raw_query: rawQuery,\n completed_params: completedParams.map((p) => toWireParam(p, includeText)),\n ...(contactAccountCount != null && { contact_account_count: contactAccountCount }),\n },\n meta: {\n request_id: generateRequestId(),\n request_at: new Date().toISOString(),\n language: typeof navigator !== \"undefined\" ? navigator.language : \"en-US\",\n client_version: SDK_VERSION,\n },\n };\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"X-App-Identifier\": process?.env.MAGICX_APP_IDENTIFIER || \"active-campaign-demo\",\n ...apiConfig?.headers,\n };\n if (apiKey) {\n headers.Authorization = authScheme === \"Basic\" ? `Basic ${btoa(apiKey)}` : `Bearer ${apiKey}`;\n }\n\n const response = await fetch(API_ENDPOINT, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n signal: options?.signal,\n });\n\n if (!response.ok) {\n throw new Error(`API error: ${response.status} ${response.statusText}`);\n }\n\n return response.json() as Promise<AutocompleteResponse>;\n}\n","import type { CompletedParamState } from \"../types\";\n\ninterface BuildQueryResult {\n rawQuery: string;\n completedParams: CompletedParamState[];\n}\n\n/**\n * Takes the raw input text and completed params (without placeholders),\n * replaces each completed param's text in the string with a {{TYPE_N}} token,\n * and returns the transformed query + params with placeholders filled in.\n *\n * Replacements happen left-to-right, first occurrence only per param.\n * Counter is per-type (e.g. {{TASK_1}}, {{GOAL_1}}, {{GOAL_2}}).\n */\nexport function buildQuery(text: string, completedParams: CompletedParamState[]): BuildQueryResult {\n let result = text;\n const typeCounts: Record<string, number> = {};\n const updatedParams: CompletedParamState[] = [];\n\n for (const param of completedParams) {\n const count = (typeCounts[param.type] ?? 0) + 1;\n typeCounts[param.type] = count;\n\n const typeKey = param.type.toUpperCase().replace(/\\s+/g, \"_\");\n const placeholder = `{{${typeKey}_${count}}}`;\n\n // Replace first occurrence of the param text in the string\n const index = result.indexOf(param.text);\n if (index !== -1) {\n result = result.slice(0, index) + placeholder + result.slice(index + param.text.length);\n }\n\n updatedParams.push({ ...param, placeholder });\n }\n\n return { rawQuery: result, completedParams: updatedParams };\n}\n","import type { Suggestion, SuggestionOption } from \"../types\";\n\n/**\n * Applies option overrides by calling the override function with an empty query,\n * then prepending results to suggestions that match by type, deduplicating by text.\n */\nexport function applyOptionOverrides(\n suggestions: Suggestion[],\n overrides?: Record<string, (query: string) => SuggestionOption[]>,\n): Suggestion[] {\n if (!overrides) return suggestions;\n return suggestions.map((s) => {\n const fn = overrides[s.type];\n if (!fn) return s;\n const extra = fn(\"\");\n if (extra.length === 0) return s;\n const existingTexts = new Set(extra.map((o) => o.text));\n const deduped = (s.options ?? []).filter((o) => !existingTexts.has(o.text));\n return { ...s, options: [...extra, ...deduped] };\n });\n}\n","import { type KeyboardEvent, type MutableRefObject, useCallback } from \"react\";\nimport type {\n AutocompleteResult,\n CompletedParamState,\n Suggestion,\n SuggestionOption,\n} from \"../types\";\nimport { buildQuery } from \"../utils/buildQuery\";\n\ninterface UseAutocompleteKeyboardOptions {\n activeDropdownIndex: number;\n setActiveDropdownIndex: (index: number) => void;\n filteredOptions: SuggestionOption[];\n selectOption: (option: SuggestionOption) => void;\n onSubmitRef: MutableRefObject<((result: AutocompleteResult) => void) | undefined>;\n text: string;\n completedParams: CompletedParamState[];\n isDropdownOpen: boolean;\n hasPlaceholder: boolean;\n placeholderText: string;\n suggestions: Suggestion[];\n filterBaseRef: MutableRefObject<number>;\n columns: number;\n setText: (value: string) => void;\n setCompletedParams: (fn: (prev: CompletedParamState[]) => CompletedParamState[]) => void;\n setSuggestions: (fn: (prev: Suggestion[]) => Suggestion[]) => void;\n}\n\nexport function useAutocompleteKeyboard({\n activeDropdownIndex,\n setActiveDropdownIndex,\n filteredOptions,\n selectOption,\n onSubmitRef,\n text,\n completedParams,\n isDropdownOpen,\n hasPlaceholder,\n placeholderText,\n suggestions,\n filterBaseRef,\n columns,\n setText,\n setCompletedParams,\n setSuggestions,\n}: UseAutocompleteKeyboardOptions) {\n const getTappableIndices = useCallback(() => {\n const tappable = filteredOptions\n .map((o, i) => (o.is_tappable ? i : -1))\n .filter((i) => i !== -1);\n const left = tappable.filter((i) => i % columns === 0);\n const right = tappable.filter((i) => i % columns === 1);\n return [...left, ...right];\n }, [filteredOptions, columns]);\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent<HTMLTextAreaElement>) => {\n const tappableIndices = getTappableIndices();\n\n switch (e.key) {\n case \"ArrowDown\": {\n e.preventDefault();\n if (tappableIndices.length === 0) return;\n const currentPos = tappableIndices.indexOf(activeDropdownIndex);\n const nextPos = currentPos < tappableIndices.length - 1 ? currentPos + 1 : 0;\n setActiveDropdownIndex(tappableIndices[nextPos]);\n break;\n }\n case \"ArrowUp\": {\n e.preventDefault();\n if (tappableIndices.length === 0) return;\n const currentPos = tappableIndices.indexOf(activeDropdownIndex);\n const prevPos = currentPos > 0 ? currentPos - 1 : tappableIndices.length - 1;\n setActiveDropdownIndex(tappableIndices[prevPos]);\n break;\n }\n case \"ArrowRight\": {\n if (activeDropdownIndex < 0) break;\n if (activeDropdownIndex % columns === 0) {\n const rightNeighbor = activeDropdownIndex + 1;\n if (\n rightNeighbor < filteredOptions.length &&\n filteredOptions[rightNeighbor]?.is_tappable\n ) {\n e.preventDefault();\n setActiveDropdownIndex(rightNeighbor);\n }\n }\n break;\n }\n case \"ArrowLeft\": {\n if (activeDropdownIndex < 0) break;\n if (activeDropdownIndex % columns === 1) {\n const leftNeighbor = activeDropdownIndex - 1;\n if (leftNeighbor >= 0 && filteredOptions[leftNeighbor]?.is_tappable) {\n e.preventDefault();\n setActiveDropdownIndex(leftNeighbor);\n }\n }\n break;\n }\n case \"Enter\": {\n e.preventDefault();\n if (activeDropdownIndex >= 0 && filteredOptions[activeDropdownIndex]?.is_tappable) {\n selectOption(filteredOptions[activeDropdownIndex]);\n } else if (onSubmitRef.current) {\n const { rawQuery, completedParams: finalParams } = buildQuery(text, completedParams);\n const result: AutocompleteResult = {\n query: text.trim(),\n raw_query: rawQuery,\n completed_params: finalParams,\n };\n onSubmitRef.current(result);\n }\n break;\n }\n case \"Tab\": {\n if (activeDropdownIndex >= 0 && filteredOptions[activeDropdownIndex]?.is_tappable) {\n e.preventDefault();\n selectOption(filteredOptions[activeDropdownIndex]);\n } else if (isDropdownOpen) {\n const firstTappable = filteredOptions.find((o) => o.is_tappable);\n if (firstTappable) {\n e.preventDefault();\n selectOption(firstTappable);\n }\n } else if (!text && hasPlaceholder) {\n e.preventDefault();\n const placeholderSuggestion = suggestions.find((s) => s.type === \"placeholder\");\n if (placeholderSuggestion) {\n setText(placeholderText);\n filterBaseRef.current = placeholderText.length;\n setCompletedParams((prev) => [\n ...prev,\n {\n id: crypto.randomUUID(),\n placeholder: \"\",\n type: placeholderSuggestion.type,\n text: placeholderText,\n kind: null,\n suggestionType: placeholderSuggestion.type,\n suggestionPlaceholder: placeholderSuggestion.text,\n options: placeholderSuggestion.options,\n },\n ]);\n setSuggestions((prev) => prev.filter((s) => s !== placeholderSuggestion));\n } else {\n setText(placeholderText);\n filterBaseRef.current = placeholderText.length;\n }\n }\n break;\n }\n case \"Escape\":\n setActiveDropdownIndex(-1);\n break;\n }\n },\n [\n activeDropdownIndex,\n columns,\n completedParams,\n filteredOptions,\n filterBaseRef,\n getTappableIndices,\n hasPlaceholder,\n isDropdownOpen,\n onSubmitRef,\n placeholderText,\n selectOption,\n setActiveDropdownIndex,\n setCompletedParams,\n setSuggestions,\n setText,\n suggestions,\n text,\n ],\n );\n\n return { handleKeyDown, getTappableIndices };\n}\n","import { type MutableRefObject, useCallback } from \"react\";\nimport type { CompletedParamState, Suggestion } from \"../types\";\n\ninterface UseAutocompletePillsOptions {\n completedParams: CompletedParamState[];\n suggestions: Suggestion[];\n setCompletedParams: (fn: (prev: CompletedParamState[]) => CompletedParamState[]) => void;\n setSuggestions: (fn: Suggestion[] | ((prev: Suggestion[]) => Suggestion[])) => void;\n setActiveDropdownIndex: (index: number) => void;\n filterBaseRef: MutableRefObject<number>;\n pillTappedRef: MutableRefObject<boolean>;\n}\n\nexport function useAutocompletePills({\n completedParams,\n suggestions,\n setCompletedParams,\n setSuggestions,\n setActiveDropdownIndex,\n filterBaseRef,\n pillTappedRef,\n}: UseAutocompletePillsOptions) {\n const setActivePill = useCallback(\n (index: number) => {\n const actionable = suggestions.filter((s) => s.type !== \"placeholder\");\n if (index < 0 || index >= actionable.length) return;\n const moved = actionable[index];\n const rest = actionable.filter((_, i) => i !== index);\n const placeholders = suggestions.filter((s) => s.type === \"placeholder\");\n setSuggestions([...placeholders, moved, ...rest]);\n pillTappedRef.current = true;\n setActiveDropdownIndex(-1);\n },\n [suggestions, setSuggestions, setActiveDropdownIndex, pillTappedRef],\n );\n\n const removeLastParam = useCallback(() => {\n if (completedParams.length === 0) return;\n const lastParam = completedParams[completedParams.length - 1];\n const restoredSuggestion: Suggestion = {\n type: lastParam.suggestionType,\n text: lastParam.suggestionPlaceholder,\n required: true,\n options: lastParam.options,\n };\n setCompletedParams((prev) => prev.slice(0, -1));\n setSuggestions((prev: Suggestion[]) => [restoredSuggestion, ...prev]);\n setActiveDropdownIndex(-1);\n }, [completedParams, setCompletedParams, setSuggestions, setActiveDropdownIndex]);\n\n const reEditParam = useCallback(\n (param: CompletedParamState) => {\n const restoredSuggestion: Suggestion = {\n type: param.suggestionType,\n text: param.suggestionPlaceholder,\n required: true,\n options: param.options,\n };\n return {\n apply: (setText: (fn: (prev: string) => string) => void) => {\n setText((prev) => {\n let pos = 0;\n for (const p of completedParams) {\n const idx = prev.indexOf(p.text, pos);\n if (idx === -1) continue;\n if (p.id === param.id) {\n const before = prev.slice(0, idx);\n const after = prev.slice(idx + param.text.length);\n const cleaned = (before + after).replace(/ {2,}/g, \" \");\n filterBaseRef.current = Math.min(filterBaseRef.current, cleaned.length);\n return cleaned;\n }\n pos = idx + p.text.length;\n }\n return prev;\n });\n setCompletedParams((prev) => prev.filter((p) => p.id !== param.id));\n setSuggestions((prev: Suggestion[]) => [restoredSuggestion, ...prev]);\n setActiveDropdownIndex(-1);\n pillTappedRef.current = true;\n },\n };\n },\n [\n completedParams,\n setCompletedParams,\n setSuggestions,\n setActiveDropdownIndex,\n filterBaseRef,\n pillTappedRef,\n ],\n );\n\n return { setActivePill, removeLastParam, reEditParam };\n}\n"],"mappings":"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","names":["forwardRef","useCallback","useEffect","useImperativeHandle","useRef","useState","AIAutocomplete_default","AIAutocompleteDropdown_default","SuggestionGrid_default","SuggestionItem_default","jsx","jsxs","SuggestionItem","option","isHighlighted","onSelect","onHighlight","id","className","SuggestionItem_default","e","jsx","SuggestionGrid","options","activeIndex","onSelect","onHighlight","listboxId","SuggestionGrid_default","option","SuggestionItem","jsx","AIAutocompleteDropdown","suggestions","activeIndex","onSelect","onHighlight","isOpen","id","className","activeSuggestion","hasOptions","AIAutocompleteDropdown_default","e","SuggestionGrid","PillList_default","jsx","getPillOpacity","index","PillList","pills","activePillIndex","onSelectPill","PillList_default","pill","i","useCallback","useId","useMemo","useRef","useState","filterOptions","options","query","trimmed","lower","o","findExactMatch","deriveSegments","text","completedParams","result","pos","param","idx","remaining","reconcileParams","valid","invalid","useCallback","useEffect","useRef","useState","SDK_VERSION","API_ENDPOINT","hasWarnedMissingKey","getApiKey","config","key","getAuthScheme","generateRequestId","toWireParam","param","includeText","fetchSuggestions","rawQuery","completedParams","options","apiConfig","apiKey","authScheme","contactAccountCount","body","p","headers","response","buildQuery","text","completedParams","result","typeCounts","updatedParams","param","count","placeholder","index","applyOptionOverrides","suggestions","overrides","s","fn","extra","existingTexts","o","deduped","DEBOUNCE_MS","SLOW_DEBOUNCE_MS","MIN_CHARS_DIFF","useAutocompleteFetch","textRef","suggestionsRef","filterBaseRef","maskCompletedTextRef","apiConfigRef","optionOverridesRef","onErrorRef","setCompletedParams","setSuggestions","setActiveDropdownIndex","isLoading","setIsLoading","useState","error","setError","isReady","setIsReady","fetchVersionRef","useRef","abortRef","lastRawQueryRef","doFetch","useCallback","rawQuery","completed","controller","version","textAtRequest","s","rawCount","p","res","fetchSuggestions","newSuggestions","applyOptionOverrides","input","lastInput","currentText","inProgressIdx","active","query","match","findExactMatch","prev","err","caughtError","useEffect","useDebouncedFetch","text","completedParams","skipNextFetchRef","debounceRef","slowDebounceRef","hasFetchedRef","attemptFetch","minDiff","currentQuery","tappableFiltered","filterOptions","o","hasExactMatch","isInFilterZone","updatedParams","buildQuery","isDeleting","charDiff","useCallback","useAutocompleteKeyboard","activeDropdownIndex","setActiveDropdownIndex","filteredOptions","selectOption","onSubmitRef","text","completedParams","isDropdownOpen","hasPlaceholder","placeholderText","suggestions","filterBaseRef","columns","setText","setCompletedParams","setSuggestions","getTappableIndices","useCallback","tappable","o","i","left","right","e","tappableIndices","currentPos","nextPos","prevPos","rightNeighbor","leftNeighbor","rawQuery","finalParams","buildQuery","result","firstTappable","placeholderSuggestion","s","prev","useCallback","useAutocompletePills","completedParams","suggestions","setCompletedParams","setSuggestions","setActiveDropdownIndex","filterBaseRef","pillTappedRef","setActivePill","index","actionable","s","moved","rest","_","i","placeholders","removeLastParam","lastParam","restoredSuggestion","prev","reEditParam","param","setText","pos","p","idx","before","after","cleaned","useAIAutocomplete","onSubmit","onError","optionOverrides","maskCompletedText","customPlaceholder","apiConfig","columns","controlledValue","controlledParams","onChangeProp","onParamsChange","isTextControlled","isParamsControlled","internalText","setInternalText","useState","internalCompletedParams","setInternalCompletedParams","suggestions","setSuggestions","activeDropdownIndex","setActiveDropdownIndex","text","completedParams","onSubmitRef","useRef","onChangeRef","onParamsChangeRef","controlledValueRef","controlledParamsRef","setText","useCallback","value","newVal","prev","setCompletedParams","onErrorRef","optionOverridesRef","maskCompletedTextRef","apiConfigRef","textRef","suggestionsRef","filterBaseRef","pillTappedRef","skipNextFetchRef","listboxId","useId","doFetch","isLoading","error","isReady","lastRawQueryRef","useAutocompleteFetch","useDebouncedFetch","segments","useMemo","deriveSegments","filterQuery","placeholderText","s","actionableSuggestions","activeSuggestion","overrideFn","baseOptions","filteredOptions","filterOptions","hasPlaceholder","isDropdownOpen","selectOption","option","completed","base","prefix","lastWord","needsSpace","newText","handleChange","e","raw","newValue","valid","invalid","reconcileParams","param","newFilterQuery","match","findExactMatch","handleKeyDown","useAutocompleteKeyboard","pills","useAutocompletePills","reEditParam","reset","activeDescendantId","jsx","jsxs","AIAutocomplete","forwardRef","onSubmit","onError","optionOverrides","maskCompletedText","placeholder","className","apiConfig","columns","value","controlledParams","onChangeProp","onParamsChange","ref","textareaRef","useRef","showCheckmark","setShowCheckmark","useState","handleSubmitRef","checkmarkTimerRef","useEffect","completedParams","suggestionPills","setActivePill","segments","inputProps","dropdownProps","reset","useAIAutocomplete","result","useImperativeHandle","handleContainerClick","canSubmit","handleSubmit","useCallback","rawQuery","finalParams","buildQuery","onChange","inputPlaceholder","restProps","isEmpty","AIAutocomplete_default","AIAutocompleteDropdown","seg","i","PillList","e"]}
1
+ {"version":3,"sources":["../src/AIAutocomplete.tsx","../src/AIAutocomplete.module.css","../src/AIAutocompleteDropdown.module.css","../src/components/SuggestionGrid.module.css","../src/components/SuggestionItem.module.css","../src/components/SuggestionItem.tsx","../src/components/SuggestionGrid.tsx","../src/AIAutocompleteDropdown.tsx","../src/components/PillList.module.css","../src/components/PillList.tsx","../src/hooks/useAIAutocomplete.ts","../src/utils/filtering.ts","../src/utils/segments.ts","../src/hooks/useAutocompleteFetch.ts","../src/utils/api.ts","../src/utils/buildQuery.ts","../src/utils/overrides.ts","../src/hooks/useAutocompleteKeyboard.ts","../src/hooks/useAutocompletePills.ts"],"sourcesContent":["import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from \"react\";\nimport styles from \"./AIAutocomplete.module.css\";\nimport { AIAutocompleteDropdown } from \"./AIAutocompleteDropdown\";\nimport { PillList } from \"./components/PillList\";\nimport { useAIAutocomplete } from \"./hooks/useAIAutocomplete\";\nimport type { AIAutocompleteHandle, AIAutocompleteProps, AutocompleteResult } from \"./types\";\nimport { buildQuery } from \"./utils/buildQuery\";\n\nexport const AIAutocomplete = forwardRef<AIAutocompleteHandle, AIAutocompleteProps>(\n function AIAutocomplete(\n {\n onSubmit,\n onError,\n optionOverrides,\n maskCompletedText,\n placeholder,\n className,\n apiConfig,\n columns,\n eagerSuggestions,\n value,\n completedParams: controlledParams,\n onChange: onChangeProp,\n onParamsChange,\n },\n ref,\n ) {\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n const [showCheckmark, setShowCheckmark] = useState(false);\n // Render-phase ref assignment — matches the pattern in useAIAutocomplete.ts\n const handleSubmitRef = useRef<(result: AutocompleteResult) => void>(() => {});\n const checkmarkTimerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n\n useEffect(() => {\n textareaRef.current?.focus();\n return () => clearTimeout(checkmarkTimerRef.current);\n }, []);\n\n const {\n completedParams,\n suggestionPills,\n setActivePill,\n segments,\n inputProps,\n dropdownProps,\n reset,\n } = useAIAutocomplete({\n onSubmit: (result) => handleSubmitRef.current(result),\n onError,\n optionOverrides,\n maskCompletedText,\n placeholder,\n apiConfig,\n columns,\n eagerSuggestions,\n value,\n completedParams: controlledParams,\n onChange: onChangeProp,\n onParamsChange,\n });\n\n useImperativeHandle(\n ref,\n () => ({\n focus: () => textareaRef.current?.focus(),\n reset,\n }),\n [reset],\n );\n\n const handleContainerClick = () => {\n textareaRef.current?.focus();\n };\n\n const canSubmit = !!inputProps.value || completedParams.length > 0;\n\n const handleSubmit = useCallback(() => {\n if (!canSubmit) return;\n const { rawQuery, completedParams: finalParams } = buildQuery(\n inputProps.value,\n completedParams,\n );\n onSubmit({\n query: inputProps.value.trim(),\n raw_query: rawQuery,\n completed_params: finalParams,\n });\n reset();\n setShowCheckmark(true);\n clearTimeout(checkmarkTimerRef.current);\n checkmarkTimerRef.current = setTimeout(() => setShowCheckmark(false), 3000);\n }, [canSubmit, inputProps.value, completedParams, onSubmit, reset]);\n\n handleSubmitRef.current = handleSubmit;\n\n const { onChange, placeholder: inputPlaceholder, ...restProps } = inputProps;\n const isEmpty = !inputProps.value;\n\n return (\n <div className={`${styles.container} ${className ?? \"\"}`}>\n <div className={`${styles.checkmark} ${showCheckmark ? styles.checkmarkVisible : \"\"}`}>\n <svg\n width=\"72\"\n height=\"72\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n role=\"img\"\n aria-label=\"Success\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"12\" fill=\"#34C759\" />\n <path\n d=\"M7 12.5l3.5 3.5L17 9\"\n stroke=\"#000\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={styles.checkmarkPath}\n />\n </svg>\n </div>\n <AIAutocompleteDropdown {...dropdownProps} />\n {/* biome-ignore lint/a11y/useKeyWithClickEvents: container click delegates to textarea focus */}\n {/* biome-ignore lint/a11y/noStaticElementInteractions: wrapper delegates focus to textarea */}\n <div className={styles.inputWrapper} onClick={handleContainerClick}>\n <div className={styles.editorArea}>\n <div className={styles.sizerContent} aria-hidden=\"true\">\n {isEmpty && inputPlaceholder ? (\n <span className={styles.placeholderText}>{inputPlaceholder} </span>\n ) : (\n <span className={styles.sizerText}>\n {segments.map((seg, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: segments are positional and don't reorder\n <span key={`${i}-${seg.type}`}>{seg.value}</span>\n ))}\n {segments.length === 0 && \"\\u00A0\"}\n </span>\n )}\n {\" \"}\n <PillList pills={suggestionPills} activePillIndex={0} onSelectPill={setActivePill} />\n </div>\n <textarea\n ref={textareaRef}\n className={styles.textarea}\n rows={1}\n onChange={onChange}\n {...restProps}\n />\n </div>\n <button\n type=\"button\"\n className={styles.submitButton}\n disabled={!canSubmit}\n onClick={(e) => {\n e.stopPropagation();\n handleSubmit();\n }}\n aria-label=\"Submit\"\n >\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 18 18\"\n fill=\"none\"\n role=\"img\"\n aria-label=\"Submit\"\n >\n <path\n d=\"M9 14V4M9 4L4 9M9 4L14 9\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </button>\n </div>\n </div>\n );\n },\n);\n",".container {\n position: relative;\n font-family: \"IBM Plex Sans\", sans-serif;\n}\n\n.checkmark {\n position: absolute;\n bottom: -130px;\n left: 50%;\n transform: translateX(-50%) translateY(8px) scale(0.8);\n opacity: 0;\n pointer-events: none;\n z-index: 10;\n animation: none;\n}\n\n.checkmarkVisible {\n animation: checkmarkFadeInOut 3s ease forwards;\n}\n\n@keyframes checkmarkFadeInOut {\n 0% {\n opacity: 0;\n transform: translateX(-50%) translateY(8px) scale(0.8);\n }\n 10% {\n opacity: 1;\n transform: translateX(-50%) translateY(0) scale(1);\n }\n 80% {\n opacity: 1;\n transform: translateX(-50%) translateY(0) scale(1);\n }\n 100% {\n opacity: 0;\n transform: translateX(-50%) translateY(-8px) scale(0.8);\n }\n}\n\n.checkmarkPath {\n stroke-dasharray: 30;\n stroke-dashoffset: 30;\n}\n\n.checkmarkVisible .checkmarkPath {\n animation: drawCheck 0.4s ease forwards 0.1s;\n}\n\n@keyframes drawCheck {\n to {\n stroke-dashoffset: 0;\n }\n}\n\n.inputWrapper {\n min-height: 60px;\n padding: 24px;\n border: 1px solid var(--ac-color-border-default, #9ea5b2);\n border-radius: 23px;\n background: transparent;\n overflow: hidden;\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.editorArea {\n position: relative;\n flex: 1;\n min-width: 0;\n}\n\n.sizerContent {\n position: relative;\n z-index: 1;\n pointer-events: none;\n min-height: 60px;\n white-space: pre-wrap;\n word-break: break-word;\n font-family: inherit;\n font-size: 21px;\n line-height: 38px;\n}\n\n.sizerText {\n color: transparent;\n}\n\n.placeholderText {\n color: var(--ac-color-text-muted, #c1c4cb);\n opacity: 0.7;\n}\n\n.textarea {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n padding: 0;\n border: none;\n background: transparent;\n color: var(--ac-color-text-default, #fff);\n caret-color: var(--ac-color-text-default, #fff);\n font-family: inherit;\n font-size: 21px;\n line-height: 38px;\n white-space: pre-wrap;\n word-break: break-word;\n outline: none;\n resize: none;\n overflow: hidden;\n}\n\n.textarea::placeholder {\n color: var(--ac-color-text-muted, #c1c4cb);\n}\n\n.submitButton {\n flex-shrink: 0;\n width: 36px;\n height: 36px;\n border-radius: 50%;\n border: none;\n background: var(--ac-color-text-default, #fff);\n color: var(--ac-color-bg-default, #000);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0;\n transition: opacity 0.2s ease;\n}\n\n.submitButton:hover {\n opacity: 0.85;\n}\n",".dropdown {\n position: absolute;\n left: 0;\n right: 0;\n top: 100%;\n margin-top: 6px;\n background: var(--ac-color-background-default, #00002d);\n border-radius: 23px;\n overflow: hidden;\n z-index: 10;\n opacity: 0;\n pointer-events: none;\n transition: opacity 400ms cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.visible {\n opacity: 1;\n pointer-events: auto;\n}\n",".grid {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px 18px;\n padding: 18px 24px;\n max-height: 192px;\n overflow-y: auto;\n scrollbar-width: thin;\n scrollbar-color: rgba(255, 255, 255, 0.3) transparent;\n}\n\n.grid::-webkit-scrollbar {\n width: 6px;\n}\n\n.grid::-webkit-scrollbar-track {\n background: transparent;\n}\n\n.grid::-webkit-scrollbar-thumb {\n background: rgba(255, 255, 255, 0.3);\n border-radius: 3px;\n}\n",".item {\n display: flex;\n align-items: center;\n font-family: \"IBM Plex Sans\", sans-serif;\n font-size: 21px;\n line-height: 30px;\n color: var(--ac-color-text-muted, #c1c4cb);\n white-space: nowrap;\n opacity: 0.35;\n animation: fadeIn 400ms cubic-bezier(0.4, 0, 0.2, 1) forwards;\n}\n\n@keyframes fadeIn {\n from {\n opacity: 0;\n }\n}\n\n.tappable {\n cursor: pointer;\n}\n\n.tappable:hover {\n color: var(--ac-color-text-default, #fff);\n}\n\n.nonTappable {\n cursor: default;\n opacity: 0.3;\n}\n\n.highlighted {\n color: var(--ac-color-text-default, #fff);\n opacity: 0.5;\n}\n\n.tag {\n font-size: 13px;\n margin-left: 6px;\n opacity: 0.5;\n}\n","import type { SuggestionOption } from \"../types\";\nimport styles from \"./SuggestionItem.module.css\";\n\ninterface SuggestionItemProps {\n option: SuggestionOption;\n isHighlighted: boolean;\n onSelect: (option: SuggestionOption) => void;\n onHighlight: () => void;\n id: string;\n}\n\nexport function SuggestionItem({\n option,\n isHighlighted,\n onSelect,\n onHighlight,\n id,\n}: SuggestionItemProps) {\n const className = [\n styles.item,\n isHighlighted ? styles.highlighted : \"\",\n option.is_tappable ? styles.tappable : styles.nonTappable,\n ]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <div\n id={id}\n role=\"option\"\n aria-selected={isHighlighted}\n className={className}\n tabIndex={option.is_tappable ? 0 : -1}\n onClick={() => option.is_tappable && onSelect(option)}\n onKeyDown={(e) => {\n if (option.is_tappable && (e.key === \"Enter\" || e.key === \" \")) {\n e.preventDefault();\n onSelect(option);\n }\n }}\n onMouseEnter={onHighlight}\n >\n {option.icon ? `${option.icon} ${option.text}` : option.text}\n {option.tag && <span className={styles.tag}>{option.tag}</span>}\n </div>\n );\n}\n","import type { SuggestionOption } from \"../types\";\nimport styles from \"./SuggestionGrid.module.css\";\nimport { SuggestionItem } from \"./SuggestionItem\";\n\ninterface SuggestionGridProps {\n options: SuggestionOption[];\n activeIndex: number;\n onSelect: (option: SuggestionOption) => void;\n onHighlight: (index: number) => void;\n listboxId: string;\n}\n\nexport function SuggestionGrid({\n options,\n activeIndex,\n onSelect,\n onHighlight,\n listboxId,\n}: SuggestionGridProps) {\n return (\n <div className={styles.grid}>\n {options.map((option, i) => (\n <SuggestionItem\n key={option.text}\n option={option}\n isHighlighted={i === activeIndex}\n onSelect={onSelect}\n onHighlight={() => onHighlight(i)}\n id={`${listboxId}-option-${i}`}\n />\n ))}\n </div>\n );\n}\n","import styles from \"./AIAutocompleteDropdown.module.css\";\nimport { SuggestionGrid } from \"./components/SuggestionGrid\";\nimport type { AIAutocompleteDropdownProps } from \"./types\";\n\nexport function AIAutocompleteDropdown({\n suggestions,\n activeIndex,\n onSelect,\n onHighlight,\n isOpen,\n id,\n className,\n}: AIAutocompleteDropdownProps) {\n const activeSuggestion = suggestions[0];\n const hasOptions = isOpen && activeSuggestion && activeSuggestion.options.length > 0;\n\n return (\n <div\n id={id}\n role=\"listbox\"\n className={`${styles.dropdown} ${hasOptions ? styles.visible : \"\"} ${className ?? \"\"}`}\n onMouseDown={(e) => e.preventDefault()}\n >\n {activeSuggestion && activeSuggestion.options.length > 0 && (\n <SuggestionGrid\n options={activeSuggestion.options}\n activeIndex={activeIndex}\n onSelect={onSelect}\n onHighlight={onHighlight}\n listboxId={id}\n />\n )}\n </div>\n );\n}\n",".list {\n position: relative;\n z-index: 1;\n pointer-events: auto;\n display: inline-flex;\n gap: 5px;\n align-items: center;\n vertical-align: middle;\n transform: translateY(-3px);\n}\n\n.pill {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n height: 36px;\n padding: 7px 9px;\n border: none;\n border-radius: 6px;\n background: var(--ac-color-background-supportive, #313255);\n color: var(--ac-color-text-muted, #c1c4cb);\n font-family: \"IBM Plex Sans\", sans-serif;\n font-size: 21px;\n line-height: 30px;\n cursor: pointer;\n white-space: nowrap;\n animation: fadeIn 400ms cubic-bezier(0.4, 0, 0.2, 1) forwards;\n}\n\n.pill:hover {\n filter: brightness(1.2);\n}\n\n.active {\n outline: 1px solid #5a5b8a;\n}\n\n@keyframes fadeIn {\n from {\n opacity: 0;\n }\n}\n","import type { Suggestion } from \"../types\";\nimport styles from \"./PillList.module.css\";\n\ninterface PillListProps {\n pills: Suggestion[];\n activePillIndex: number;\n onSelectPill: (index: number) => void;\n}\n\nfunction getPillOpacity(index: number): number {\n if (index === 0) return 0.4;\n if (index === 1) return 0.3;\n return 0.15;\n}\n\nexport function PillList({ pills, activePillIndex, onSelectPill }: PillListProps) {\n return (\n <span className={styles.list}>\n {pills.map((pill, i) => (\n <button\n key={`${pill.type}-${pill.text}`}\n type=\"button\"\n className={`${styles.pill} ${i === activePillIndex ? styles.active : \"\"}`}\n style={{ opacity: getPillOpacity(i) }}\n onClick={() => onSelectPill(i)}\n >\n {pill.text}\n </button>\n ))}\n </span>\n );\n}\n","import { type ChangeEvent, useCallback, useMemo, useRef, useState } from \"react\";\nimport type {\n CompletedParamState,\n Suggestion,\n SuggestionOption,\n UseAIAutocompleteOptions,\n UseAIAutocompleteReturn,\n} from \"../types\";\nimport { filterOptions, findExactMatch } from \"../utils/filtering\";\nimport { deriveSegments, reconcileParams } from \"../utils/segments\";\nimport { useAutocompleteFetch, useDebouncedFetch } from \"./useAutocompleteFetch\";\nimport { useAutocompleteKeyboard } from \"./useAutocompleteKeyboard\";\nimport { useAutocompletePills } from \"./useAutocompletePills\";\n\nlet idCounter = 0;\nfunction useStableId(): string {\n const id = useRef<string | null>(null);\n if (id.current === null) {\n id.current = `:ac-${++idCounter}:`;\n }\n return id.current;\n}\n\nexport function useAIAutocomplete({\n onSubmit,\n onError,\n optionOverrides,\n maskCompletedText,\n placeholder: customPlaceholder,\n apiConfig,\n columns = 2,\n eagerSuggestions = true,\n value: controlledValue,\n completedParams: controlledParams,\n onChange: onChangeProp,\n onParamsChange,\n}: UseAIAutocompleteOptions): UseAIAutocompleteReturn {\n // === Controlled / uncontrolled state ===\n const isTextControlled = controlledValue !== undefined;\n const isParamsControlled = controlledParams !== undefined;\n\n const [internalText, setInternalText] = useState(\"\");\n const [internalCompletedParams, setInternalCompletedParams] = useState<CompletedParamState[]>([]);\n const [suggestions, setSuggestions] = useState<Suggestion[]>([]);\n const [activeDropdownIndex, setActiveDropdownIndex] = useState(-1);\n\n const text = isTextControlled ? controlledValue : internalText;\n const completedParams = isParamsControlled ? controlledParams : internalCompletedParams;\n\n // === Refs — must be before setText/setCompletedParams which read them ===\n const onSubmitRef = useRef(onSubmit);\n onSubmitRef.current = onSubmit;\n const onChangeRef = useRef(onChangeProp);\n onChangeRef.current = onChangeProp;\n const onParamsChangeRef = useRef(onParamsChange);\n onParamsChangeRef.current = onParamsChange;\n const controlledValueRef = useRef(controlledValue);\n controlledValueRef.current = controlledValue;\n const controlledParamsRef = useRef(controlledParams);\n controlledParamsRef.current = controlledParams;\n\n // Stable setText that reads controlled value + callback via refs (no stale closures)\n const setText = useCallback(\n (value: string | ((prev: string) => string)) => {\n if (typeof value === \"function\") {\n if (isTextControlled) {\n const newVal = value(controlledValueRef.current ?? \"\");\n onChangeRef.current?.(newVal);\n } else {\n setInternalText((prev) => {\n const newVal = value(prev);\n onChangeRef.current?.(newVal);\n return newVal;\n });\n }\n } else {\n if (!isTextControlled) setInternalText(value);\n onChangeRef.current?.(value);\n }\n },\n [isTextControlled],\n );\n\n // Stable setCompletedParams — same ref pattern\n const setCompletedParams = useCallback(\n (value: CompletedParamState[] | ((prev: CompletedParamState[]) => CompletedParamState[])) => {\n if (typeof value === \"function\") {\n if (isParamsControlled) {\n const newVal = value(controlledParamsRef.current ?? []);\n onParamsChangeRef.current?.(newVal);\n } else {\n setInternalCompletedParams((prev) => {\n const newVal = value(prev);\n onParamsChangeRef.current?.(newVal);\n return newVal;\n });\n }\n } else {\n if (!isParamsControlled) setInternalCompletedParams(value);\n onParamsChangeRef.current?.(value);\n }\n },\n [isParamsControlled],\n );\n const onErrorRef = useRef(onError);\n onErrorRef.current = onError;\n const optionOverridesRef = useRef(optionOverrides);\n optionOverridesRef.current = optionOverrides;\n const maskCompletedTextRef = useRef(maskCompletedText);\n maskCompletedTextRef.current = maskCompletedText;\n const apiConfigRef = useRef(apiConfig);\n apiConfigRef.current = apiConfig;\n const textRef = useRef(text);\n textRef.current = text;\n const suggestionsRef = useRef(suggestions);\n suggestionsRef.current = suggestions;\n const filterBaseRef = useRef(0);\n const pillTappedRef = useRef(false);\n const skipNextFetchRef = useRef(false);\n const listboxId = useStableId();\n\n // === Fetch ===\n const { doFetch, isLoading, error, isReady, lastRawQueryRef } = useAutocompleteFetch({\n textRef,\n suggestionsRef,\n filterBaseRef,\n maskCompletedTextRef,\n apiConfigRef,\n optionOverridesRef,\n onErrorRef,\n setCompletedParams,\n setSuggestions,\n setActiveDropdownIndex,\n });\n\n useDebouncedFetch({\n text,\n completedParams,\n doFetch,\n filterBaseRef,\n skipNextFetchRef,\n suggestionsRef,\n lastRawQueryRef,\n });\n\n // === Derived ===\n const segments = useMemo(() => deriveSegments(text, completedParams), [text, completedParams]);\n\n filterBaseRef.current = Math.min(filterBaseRef.current, text.length);\n const filterQuery = text.slice(filterBaseRef.current);\n\n const placeholderText = useMemo(() => {\n const serverPlaceholder = suggestions\n .filter((s) => s.type === \"placeholder\")\n .map((s) => s.text)\n .join(\" \");\n return serverPlaceholder || customPlaceholder || \"\";\n }, [suggestions, customPlaceholder]);\n const actionableSuggestions = useMemo(\n () => suggestions.filter((s) => s.type !== \"placeholder\"),\n [suggestions],\n );\n const activeSuggestion: Suggestion | undefined = actionableSuggestions[0];\n const overrideFn = activeSuggestion ? optionOverrides?.[activeSuggestion.type] : undefined;\n const baseOptions = activeSuggestion\n ? overrideFn\n ? overrideFn(filterQuery.trim())\n : (activeSuggestion.options ?? [])\n : [];\n const filteredOptions = useMemo(\n () => filterOptions(baseOptions, filterQuery),\n [baseOptions, filterQuery],\n );\n const hasPlaceholder = placeholderText.length > 0;\n const isDropdownOpen =\n !isLoading &&\n filteredOptions.length > 0 &&\n (!!text || pillTappedRef.current || !hasPlaceholder);\n\n // === Option selection ===\n const selectOption = useCallback(\n (option: SuggestionOption) => {\n if (!activeSuggestion) return;\n\n const completed: CompletedParamState = {\n id: crypto.randomUUID(),\n placeholder: \"\",\n type: activeSuggestion.type,\n text: option.text,\n kind: option.kind,\n suggestionType: activeSuggestion.type,\n suggestionPlaceholder: activeSuggestion.text,\n options: activeSuggestion.options,\n metadata: option.metadata,\n };\n\n const base = filterBaseRef.current;\n let prefix = textRef.current.slice(0, base);\n if (prefix.length > 0 && !prefix.endsWith(\" \")) {\n const lastWord = prefix.split(/\\s+/).pop() ?? \"\";\n if (lastWord && option.text.toLowerCase().startsWith(lastWord.toLowerCase())) {\n prefix = prefix.slice(0, prefix.length - lastWord.length);\n }\n }\n\n const needsSpace = prefix.length > 0 && prefix[prefix.length - 1] !== \" \";\n const newText = prefix + (needsSpace ? \" \" : \"\") + option.text + \" \";\n setText(newText);\n filterBaseRef.current = newText.length;\n setCompletedParams((prev) => [...prev, completed]);\n pillTappedRef.current = false;\n setActiveDropdownIndex(-1);\n const remainingActionable = actionableSuggestions.length - 1;\n if (eagerSuggestions && remainingActionable > 0) {\n // Keep cached suggestions visible and skip the fetch\n setSuggestions((prev) => prev.filter((s) => s !== activeSuggestion));\n skipNextFetchRef.current = true;\n } else {\n // Clear all suggestions and let the debounced fetch bring fresh ones\n setSuggestions((prev) => prev.filter((s) => s.type === \"placeholder\"));\n }\n },\n [activeSuggestion, actionableSuggestions, eagerSuggestions, setText, setCompletedParams],\n );\n\n // === Text change handler ===\n const handleChange = useCallback(\n (e: ChangeEvent<HTMLTextAreaElement>) => {\n const raw = e.target.value;\n const shouldCapitalize =\n raw.length > 0 &&\n !(e.nativeEvent as InputEvent)?.isComposing &&\n raw[0] !== raw[0].toUpperCase();\n const newValue = shouldCapitalize ? raw[0].toUpperCase() + raw.slice(1) : raw;\n setText(newValue);\n pillTappedRef.current = false;\n setActiveDropdownIndex(-1);\n\n const { valid, invalid } = reconcileParams(newValue, completedParams);\n if (invalid.length > 0) {\n setCompletedParams(() => valid);\n for (const param of invalid) {\n setSuggestions((prev) => [\n {\n type: param.suggestionType,\n text: param.suggestionPlaceholder,\n required: true,\n options: param.options,\n },\n ...prev,\n ]);\n }\n }\n\n if (activeSuggestion && invalid.length === 0) {\n const newFilterQuery = newValue.slice(filterBaseRef.current);\n const match = findExactMatch(activeSuggestion.options, newFilterQuery);\n if (match) {\n setCompletedParams((prev) => [\n ...prev,\n {\n id: crypto.randomUUID(),\n placeholder: \"\",\n type: activeSuggestion.type,\n text: match.text,\n kind: match.kind,\n suggestionType: activeSuggestion.type,\n suggestionPlaceholder: activeSuggestion.text,\n options: activeSuggestion.options,\n metadata: match.metadata,\n },\n ]);\n setSuggestions((prev) => prev.filter((s) => s !== activeSuggestion));\n }\n }\n },\n [completedParams, activeSuggestion, setText, setCompletedParams],\n );\n\n // === Keyboard ===\n const { handleKeyDown } = useAutocompleteKeyboard({\n activeDropdownIndex,\n setActiveDropdownIndex,\n filteredOptions,\n selectOption,\n onSubmitRef,\n text,\n completedParams,\n isDropdownOpen,\n hasPlaceholder,\n placeholderText,\n suggestions,\n filterBaseRef,\n columns,\n setText,\n setCompletedParams,\n setSuggestions,\n });\n\n // === Pills ===\n const pills = useAutocompletePills({\n completedParams,\n suggestions,\n setCompletedParams,\n setSuggestions,\n setActiveDropdownIndex,\n filterBaseRef,\n pillTappedRef,\n });\n\n const reEditParam = useCallback(\n (param: CompletedParamState) => {\n pills.reEditParam(param).apply(setText);\n },\n [pills, setText],\n );\n\n // === Reset ===\n const reset = useCallback(() => {\n setText(\"\");\n setCompletedParams(() => []);\n setSuggestions([]);\n setActiveDropdownIndex(-1);\n filterBaseRef.current = 0;\n lastRawQueryRef.current = \"\";\n doFetch(\"\", []);\n }, [doFetch, setText, setCompletedParams, lastRawQueryRef]);\n\n const activeDescendantId =\n activeDropdownIndex >= 0 ? `${listboxId}-option-${activeDropdownIndex}` : undefined;\n\n return {\n completedParams,\n suggestionPills: actionableSuggestions,\n setActivePill: pills.setActivePill,\n removeLastParam: pills.removeLastParam,\n reEditParam,\n segments,\n suggestions,\n activeIndex: activeDropdownIndex,\n isReady,\n isLoading,\n error,\n inputProps: {\n value: text,\n placeholder: placeholderText || undefined,\n onChange: handleChange,\n onKeyDown: handleKeyDown,\n role: \"combobox\" as const,\n \"aria-expanded\": isDropdownOpen,\n \"aria-activedescendant\": activeDescendantId,\n \"aria-autocomplete\": \"list\" as const,\n \"aria-controls\": listboxId,\n },\n reset,\n dropdownProps: {\n suggestions: activeSuggestion ? [{ ...activeSuggestion, options: filteredOptions }] : [],\n activeIndex: activeDropdownIndex,\n onSelect: selectOption,\n onHighlight: setActiveDropdownIndex,\n isOpen: isDropdownOpen,\n id: listboxId,\n },\n };\n}\n","import type { SuggestionOption } from \"../types\";\n\n/**\n * Filters options using partial substring match on the text after the last completed param.\n */\nexport function filterOptions(options: SuggestionOption[], query: string): SuggestionOption[] {\n const trimmed = query.trimStart();\n if (!trimmed) return options;\n const lower = trimmed.toLowerCase();\n return options.filter((o) => !o.is_tappable || o.text.toLowerCase().includes(lower));\n}\n\n/**\n * Finds an exact match for the trimmed filter query against options.\n */\nexport function findExactMatch(\n options: SuggestionOption[],\n query: string,\n): SuggestionOption | null {\n const trimmed = query.trim();\n if (!trimmed) return null;\n const lower = trimmed.toLowerCase();\n return options.find((o) => o.is_tappable && o.text.toLowerCase() === lower) ?? null;\n}\n","import type { CompletedParamState, Segment } from \"../types\";\n\n/**\n * Derives segments for overlay rendering by matching completed params in text.\n */\nexport function deriveSegments(text: string, completedParams: CompletedParamState[]): Segment[] {\n const result: Segment[] = [];\n let pos = 0;\n\n for (const param of completedParams) {\n const idx = text.indexOf(param.text, pos);\n if (idx === -1) continue;\n if (idx > pos) {\n result.push({ type: \"text\", value: text.slice(pos, idx) });\n }\n result.push({ type: \"completed\", value: param.text, param });\n pos = idx + param.text.length;\n }\n\n const remaining = text.slice(pos);\n if (remaining) {\n result.push({ type: \"text\", value: remaining });\n }\n\n return result;\n}\n\n/**\n * Checks which completed params still exist in the new text.\n */\nexport function reconcileParams(\n text: string,\n completedParams: CompletedParamState[],\n): { valid: CompletedParamState[]; invalid: CompletedParamState[] } {\n const valid: CompletedParamState[] = [];\n const invalid: CompletedParamState[] = [];\n let pos = 0;\n\n for (const param of completedParams) {\n const idx = text.indexOf(param.text, pos);\n if (idx === -1) {\n invalid.push(param);\n } else {\n valid.push(param);\n pos = idx + param.text.length;\n }\n }\n\n return { valid, invalid };\n}\n","import { type MutableRefObject, useCallback, useEffect, useRef, useState } from \"react\";\nimport type {\n APIConfig,\n CompletedParamState,\n OptionOverrides,\n Suggestion,\n SuggestionOption,\n} from \"../types\";\nimport { fetchSuggestions } from \"../utils/api\";\nimport { buildQuery } from \"../utils/buildQuery\";\nimport { filterOptions, findExactMatch } from \"../utils/filtering\";\nimport { applyOptionOverrides } from \"../utils/overrides\";\n\nconst DEBOUNCE_MS = 100;\nconst SLOW_DEBOUNCE_MS = 300;\nconst MIN_CHARS_DIFF = 2;\n\ninterface UseAutocompleteFetchOptions {\n textRef: MutableRefObject<string>;\n suggestionsRef: MutableRefObject<Suggestion[]>;\n filterBaseRef: MutableRefObject<number>;\n maskCompletedTextRef: MutableRefObject<boolean | undefined>;\n apiConfigRef: MutableRefObject<APIConfig | undefined>;\n optionOverridesRef: MutableRefObject<OptionOverrides | undefined>;\n onErrorRef: MutableRefObject<((error: Error) => void) | undefined>;\n setCompletedParams: (fn: (prev: CompletedParamState[]) => CompletedParamState[]) => void;\n setSuggestions: (fn: Suggestion[] | ((prev: Suggestion[]) => Suggestion[])) => void;\n setActiveDropdownIndex: (index: number) => void;\n}\n\ninterface UseAutocompleteFetchReturn {\n doFetch: (rawQuery: string, completed: CompletedParamState[]) => Promise<void>;\n isLoading: boolean;\n error: Error | null;\n isReady: boolean;\n lastRawQueryRef: MutableRefObject<string>;\n}\n\nexport function useAutocompleteFetch({\n textRef,\n suggestionsRef,\n filterBaseRef,\n maskCompletedTextRef,\n apiConfigRef,\n optionOverridesRef,\n onErrorRef,\n setCompletedParams,\n setSuggestions,\n setActiveDropdownIndex,\n}: UseAutocompleteFetchOptions): UseAutocompleteFetchReturn {\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [isReady, setIsReady] = useState(false);\n const fetchVersionRef = useRef(0);\n const abortRef = useRef<AbortController | null>(null);\n const lastRawQueryRef = useRef(\"\");\n\n const doFetch = useCallback(\n async (rawQuery: string, completed: CompletedParamState[]) => {\n abortRef.current?.abort();\n const controller = new AbortController();\n abortRef.current = controller;\n const version = ++fetchVersionRef.current;\n const textAtRequest = textRef.current.length;\n\n const hasSuggestions = suggestionsRef.current.some((s) => s.type !== \"placeholder\");\n if (!hasSuggestions) setIsLoading(true);\n setError(null);\n try {\n const res = await fetchSuggestions(rawQuery, completed, {\n maskCompletedText: maskCompletedTextRef.current,\n signal: controller.signal,\n apiConfig: apiConfigRef.current,\n });\n\n if (version !== fetchVersionRef.current) return;\n\n let newSuggestions = applyOptionOverrides(\n res.data.suggestions ?? [],\n optionOverridesRef.current,\n );\n setIsReady(res.data.is_ready ?? false);\n lastRawQueryRef.current = rawQuery;\n\n const input = res.data.input ?? [];\n const lastInput = input[input.length - 1];\n const currentText = textRef.current;\n if (lastInput?.state === \"in_progress\") {\n const inProgressIdx = currentText.toLowerCase().lastIndexOf(lastInput.text.toLowerCase());\n if (inProgressIdx !== -1) {\n filterBaseRef.current = inProgressIdx;\n } else {\n filterBaseRef.current = textAtRequest;\n }\n } else {\n filterBaseRef.current = textAtRequest;\n }\n\n // Check if the user already typed an exact match while waiting\n const actionable = newSuggestions.filter((s) => s.type !== \"placeholder\");\n const active = actionable[0];\n if (active) {\n const query = currentText.slice(filterBaseRef.current);\n const match = findExactMatch(active.options, query);\n if (match) {\n setCompletedParams((prev) => [\n ...prev,\n {\n id: crypto.randomUUID(),\n placeholder: \"\",\n type: active.type,\n text: match.text,\n kind: match.kind,\n suggestionType: active.type,\n suggestionPlaceholder: active.text,\n options: active.options,\n metadata: match.metadata,\n },\n ]);\n newSuggestions = newSuggestions.filter((s) => s !== active);\n }\n }\n\n setSuggestions(newSuggestions);\n setIsLoading(false);\n setActiveDropdownIndex(-1);\n } catch (err) {\n if (version === fetchVersionRef.current) {\n const caughtError = err instanceof Error ? err : new Error(String(err));\n setError(caughtError);\n setIsLoading(false);\n onErrorRef.current?.(caughtError);\n }\n }\n },\n [\n textRef,\n suggestionsRef,\n filterBaseRef,\n maskCompletedTextRef,\n apiConfigRef,\n optionOverridesRef,\n onErrorRef,\n setCompletedParams,\n setSuggestions,\n setActiveDropdownIndex,\n ],\n );\n\n // Mount fetch\n useEffect(() => {\n doFetch(\"\", []);\n return () => {\n abortRef.current?.abort();\n };\n }, [doFetch]);\n\n return { doFetch, isLoading, error, isReady, lastRawQueryRef };\n}\n\ninterface UseDebouncedFetchOptions {\n text: string;\n completedParams: CompletedParamState[];\n doFetch: (rawQuery: string, completed: CompletedParamState[]) => Promise<void>;\n filterBaseRef: MutableRefObject<number>;\n skipNextFetchRef: MutableRefObject<boolean>;\n suggestionsRef: MutableRefObject<Suggestion[]>;\n lastRawQueryRef: MutableRefObject<string>;\n}\n\nexport function useDebouncedFetch({\n text,\n completedParams,\n doFetch,\n filterBaseRef,\n skipNextFetchRef,\n suggestionsRef,\n lastRawQueryRef,\n}: UseDebouncedFetchOptions): void {\n const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const slowDebounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const hasFetchedRef = useRef(true);\n\n useEffect(() => {\n if (debounceRef.current) clearTimeout(debounceRef.current);\n if (slowDebounceRef.current) clearTimeout(slowDebounceRef.current);\n\n const attemptFetch = (minDiff: number): boolean => {\n if (skipNextFetchRef.current) {\n skipNextFetchRef.current = false;\n return false;\n }\n\n if (!text && completedParams.length === 0) {\n if (!hasFetchedRef.current) {\n hasFetchedRef.current = true;\n return false;\n }\n doFetch(\"\", []);\n return true;\n }\n\n const currentQuery = text.slice(filterBaseRef.current);\n const currentSuggestions = suggestionsRef.current;\n const actionable = currentSuggestions.filter((s: Suggestion) => s.type !== \"placeholder\");\n const active = actionable[0];\n const currentFiltered = active ? filterOptions(active.options, currentQuery) : [];\n const tappableFiltered = currentFiltered.filter((o: SuggestionOption) => o.is_tappable);\n const hasExactMatch = active ? findExactMatch(active.options, currentQuery) !== null : false;\n\n const isInFilterZone = currentQuery.trim().length > 0;\n if (tappableFiltered.length > 0 && !hasExactMatch && isInFilterZone) return false;\n\n const { rawQuery, completedParams: updatedParams } = buildQuery(text, completedParams);\n const isDeleting = rawQuery.length < lastRawQueryRef.current.length;\n const charDiff = Math.abs(rawQuery.length - lastRawQueryRef.current.length);\n if (isDeleting || charDiff >= minDiff) {\n doFetch(rawQuery, updatedParams);\n return true;\n }\n return false;\n };\n\n debounceRef.current = setTimeout(() => {\n if (attemptFetch(MIN_CHARS_DIFF)) {\n if (slowDebounceRef.current) clearTimeout(slowDebounceRef.current);\n }\n }, DEBOUNCE_MS);\n\n slowDebounceRef.current = setTimeout(() => attemptFetch(1), SLOW_DEBOUNCE_MS);\n\n return () => {\n if (debounceRef.current) clearTimeout(debounceRef.current);\n if (slowDebounceRef.current) clearTimeout(slowDebounceRef.current);\n };\n }, [\n text,\n completedParams,\n doFetch,\n filterBaseRef,\n skipNextFetchRef,\n suggestionsRef,\n lastRawQueryRef,\n ]);\n}\n","import type {\n APIConfig,\n AutocompleteRequest,\n AutocompleteResponse,\n CompletedParam,\n CompletedParamState,\n} from \"../types\";\n\n// Replaced at build time by tsup/vitest `define` config with the package.json version.\ndeclare const __SDK_VERSION__: string;\nconst SDK_VERSION = __SDK_VERSION__;\n\n// process.env.* values are replaced at build time by the bundler's `define` config.\n// Use optional chaining (process?.env), NOT `typeof process !== \"undefined\"` guards —\n// the typeof guard prevents the replacement from taking effect in browsers.\ndeclare const process: { env: Record<string, string | undefined> } | undefined;\nconst DEFAULT_ENDPOINT = process?.env.MAGICX_API_ENDPOINT || \"/ac/suggest\";\n\nlet hasWarnedMissingKey = false;\n\nfunction getApiKey(config?: APIConfig): string {\n const key = config?.apiKey || process?.env.MAGICX_AI_AUTOCOMPLETE_API_KEY || \"\";\n if (!key && !hasWarnedMissingKey) {\n hasWarnedMissingKey = true;\n // biome-ignore lint/suspicious/noConsole: intentional dev warning\n console.warn(\n \"[AIAutocomplete] No API key set (MAGICX_AI_AUTOCOMPLETE_API_KEY). \" +\n \"Requests will be sent without an Authorization header.\",\n );\n }\n return key;\n}\n\nfunction getAuthScheme(config?: APIConfig): \"Bearer\" | \"Basic\" {\n if (config?.authScheme) return config.authScheme;\n const scheme = process?.env.MAGICX_AUTH_SCHEME;\n return scheme === \"Basic\" ? \"Basic\" : \"Bearer\";\n}\n\nfunction generateRequestId(): string {\n return crypto.randomUUID();\n}\n\nfunction toWireParam(param: CompletedParamState, includeText: boolean): CompletedParam {\n return {\n placeholder: param.placeholder,\n type: param.type,\n ...(includeText && { text: param.text }),\n kind: param.kind,\n };\n}\n\nexport async function fetchSuggestions(\n rawQuery: string,\n completedParams: CompletedParamState[],\n options?: {\n maskCompletedText?: boolean;\n signal?: AbortSignal;\n apiConfig?: APIConfig;\n },\n): Promise<AutocompleteResponse> {\n const apiConfig = options?.apiConfig;\n const apiKey = getApiKey(apiConfig);\n const authScheme = getAuthScheme(apiConfig);\n const includeText = !options?.maskCompletedText;\n\n const rawCount = completedParams.find(\n (p) => p.type === \"contact\" && p.metadata?.contact_account_count,\n )?.metadata?.contact_account_count;\n const contactAccountCount = typeof rawCount === \"number\" ? rawCount : undefined;\n\n const body: AutocompleteRequest = {\n data: {\n raw_query: rawQuery,\n completed_params: completedParams.map((p) => toWireParam(p, includeText)),\n ...(contactAccountCount != null && { contact_account_count: contactAccountCount }),\n },\n meta: {\n request_id: generateRequestId(),\n request_at: new Date().toISOString(),\n language: typeof navigator !== \"undefined\" ? navigator.language : \"en-US\",\n client_version: SDK_VERSION,\n },\n };\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"X-App-Identifier\": process?.env.MAGICX_APP_IDENTIFIER || \"active-campaign-demo\",\n ...apiConfig?.headers,\n };\n if (apiKey) {\n headers.Authorization = authScheme === \"Basic\" ? `Basic ${btoa(apiKey)}` : `Bearer ${apiKey}`;\n }\n\n const response = await fetch(apiConfig?.endpoint || DEFAULT_ENDPOINT, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n signal: options?.signal,\n });\n\n if (!response.ok) {\n throw new Error(`API error: ${response.status} ${response.statusText}`);\n }\n\n return response.json() as Promise<AutocompleteResponse>;\n}\n","import type { CompletedParamState } from \"../types\";\n\ninterface BuildQueryResult {\n rawQuery: string;\n completedParams: CompletedParamState[];\n}\n\n/**\n * Takes the raw input text and completed params (without placeholders),\n * replaces each completed param's text in the string with a {{TYPE_N}} token,\n * and returns the transformed query + params with placeholders filled in.\n *\n * Replacements happen left-to-right, first occurrence only per param.\n * Counter is per-type (e.g. {{TASK_1}}, {{GOAL_1}}, {{GOAL_2}}).\n */\nexport function buildQuery(text: string, completedParams: CompletedParamState[]): BuildQueryResult {\n let result = text;\n const typeCounts: Record<string, number> = {};\n const updatedParams: CompletedParamState[] = [];\n\n for (const param of completedParams) {\n const count = (typeCounts[param.type] ?? 0) + 1;\n typeCounts[param.type] = count;\n\n const typeKey = param.type.toUpperCase().replace(/\\s+/g, \"_\");\n const placeholder = `{{${typeKey}_${count}}}`;\n\n // Replace first occurrence of the param text in the string\n const index = result.indexOf(param.text);\n if (index !== -1) {\n result = result.slice(0, index) + placeholder + result.slice(index + param.text.length);\n }\n\n updatedParams.push({ ...param, placeholder });\n }\n\n return { rawQuery: result, completedParams: updatedParams };\n}\n","import type { Suggestion, SuggestionOption } from \"../types\";\n\n/**\n * Applies option overrides by calling the override function with an empty query,\n * then prepending results to suggestions that match by type, deduplicating by text.\n */\nexport function applyOptionOverrides(\n suggestions: Suggestion[],\n overrides?: Record<string, (query: string) => SuggestionOption[]>,\n): Suggestion[] {\n if (!overrides) return suggestions;\n return suggestions.map((s) => {\n const fn = overrides[s.type];\n if (!fn) return s;\n const extra = fn(\"\");\n if (extra.length === 0) return s;\n const existingTexts = new Set(extra.map((o) => o.text));\n const deduped = (s.options ?? []).filter((o) => !existingTexts.has(o.text));\n return { ...s, options: [...extra, ...deduped] };\n });\n}\n","import { type KeyboardEvent, type MutableRefObject, useCallback } from \"react\";\nimport type {\n AutocompleteResult,\n CompletedParamState,\n Suggestion,\n SuggestionOption,\n} from \"../types\";\nimport { buildQuery } from \"../utils/buildQuery\";\n\ninterface UseAutocompleteKeyboardOptions {\n activeDropdownIndex: number;\n setActiveDropdownIndex: (index: number) => void;\n filteredOptions: SuggestionOption[];\n selectOption: (option: SuggestionOption) => void;\n onSubmitRef: MutableRefObject<((result: AutocompleteResult) => void) | undefined>;\n text: string;\n completedParams: CompletedParamState[];\n isDropdownOpen: boolean;\n hasPlaceholder: boolean;\n placeholderText: string;\n suggestions: Suggestion[];\n filterBaseRef: MutableRefObject<number>;\n columns: number;\n setText: (value: string) => void;\n setCompletedParams: (fn: (prev: CompletedParamState[]) => CompletedParamState[]) => void;\n setSuggestions: (fn: (prev: Suggestion[]) => Suggestion[]) => void;\n}\n\nexport function useAutocompleteKeyboard({\n activeDropdownIndex,\n setActiveDropdownIndex,\n filteredOptions,\n selectOption,\n onSubmitRef,\n text,\n completedParams,\n isDropdownOpen,\n hasPlaceholder,\n placeholderText,\n suggestions,\n filterBaseRef,\n columns,\n setText,\n setCompletedParams,\n setSuggestions,\n}: UseAutocompleteKeyboardOptions) {\n const getTappableIndices = useCallback(() => {\n const tappable = filteredOptions\n .map((o, i) => (o.is_tappable ? i : -1))\n .filter((i) => i !== -1);\n // Column-first ordering: all column-0 items, then column-1, etc.\n const buckets: number[][] = Array.from({ length: columns }, () => []);\n for (const i of tappable) buckets[i % columns].push(i);\n return buckets.flat();\n }, [filteredOptions, columns]);\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent<HTMLTextAreaElement>) => {\n const tappableIndices = getTappableIndices();\n\n switch (e.key) {\n case \"ArrowDown\": {\n e.preventDefault();\n if (tappableIndices.length === 0) return;\n const currentPos = tappableIndices.indexOf(activeDropdownIndex);\n const nextPos = currentPos < tappableIndices.length - 1 ? currentPos + 1 : 0;\n setActiveDropdownIndex(tappableIndices[nextPos]);\n break;\n }\n case \"ArrowUp\": {\n e.preventDefault();\n if (tappableIndices.length === 0) return;\n const currentPos = tappableIndices.indexOf(activeDropdownIndex);\n const prevPos = currentPos > 0 ? currentPos - 1 : tappableIndices.length - 1;\n setActiveDropdownIndex(tappableIndices[prevPos]);\n break;\n }\n case \"ArrowRight\": {\n if (activeDropdownIndex < 0) break;\n const col = activeDropdownIndex % columns;\n if (col < columns - 1) {\n const rightNeighbor = activeDropdownIndex + 1;\n if (\n rightNeighbor < filteredOptions.length &&\n filteredOptions[rightNeighbor]?.is_tappable\n ) {\n e.preventDefault();\n setActiveDropdownIndex(rightNeighbor);\n }\n }\n break;\n }\n case \"ArrowLeft\": {\n if (activeDropdownIndex < 0) break;\n if (activeDropdownIndex % columns > 0) {\n const leftNeighbor = activeDropdownIndex - 1;\n if (leftNeighbor >= 0 && filteredOptions[leftNeighbor]?.is_tappable) {\n e.preventDefault();\n setActiveDropdownIndex(leftNeighbor);\n }\n }\n break;\n }\n case \"Enter\": {\n e.preventDefault();\n if (activeDropdownIndex >= 0 && filteredOptions[activeDropdownIndex]?.is_tappable) {\n selectOption(filteredOptions[activeDropdownIndex]);\n } else if (onSubmitRef.current) {\n const { rawQuery, completedParams: finalParams } = buildQuery(text, completedParams);\n const result: AutocompleteResult = {\n query: text.trim(),\n raw_query: rawQuery,\n completed_params: finalParams,\n };\n onSubmitRef.current(result);\n }\n break;\n }\n case \"Tab\": {\n if (activeDropdownIndex >= 0 && filteredOptions[activeDropdownIndex]?.is_tappable) {\n e.preventDefault();\n selectOption(filteredOptions[activeDropdownIndex]);\n } else if (isDropdownOpen) {\n const firstTappable = filteredOptions.find((o) => o.is_tappable);\n if (firstTappable) {\n e.preventDefault();\n selectOption(firstTappable);\n }\n } else if (!text && hasPlaceholder) {\n e.preventDefault();\n const placeholderSuggestion = suggestions.find((s) => s.type === \"placeholder\");\n if (placeholderSuggestion) {\n setText(placeholderText);\n filterBaseRef.current = placeholderText.length;\n setCompletedParams((prev) => [\n ...prev,\n {\n id: crypto.randomUUID(),\n placeholder: \"\",\n type: placeholderSuggestion.type,\n text: placeholderText,\n kind: null,\n suggestionType: placeholderSuggestion.type,\n suggestionPlaceholder: placeholderSuggestion.text,\n options: placeholderSuggestion.options,\n },\n ]);\n setSuggestions((prev) => prev.filter((s) => s !== placeholderSuggestion));\n } else {\n setText(placeholderText);\n filterBaseRef.current = placeholderText.length;\n }\n }\n break;\n }\n case \"Escape\":\n setActiveDropdownIndex(-1);\n break;\n }\n },\n [\n activeDropdownIndex,\n columns,\n completedParams,\n filteredOptions,\n filterBaseRef,\n getTappableIndices,\n hasPlaceholder,\n isDropdownOpen,\n onSubmitRef,\n placeholderText,\n selectOption,\n setActiveDropdownIndex,\n setCompletedParams,\n setSuggestions,\n setText,\n suggestions,\n text,\n ],\n );\n\n return { handleKeyDown, getTappableIndices };\n}\n","import { type MutableRefObject, useCallback } from \"react\";\nimport type { CompletedParamState, Suggestion } from \"../types\";\n\ninterface UseAutocompletePillsOptions {\n completedParams: CompletedParamState[];\n suggestions: Suggestion[];\n setCompletedParams: (fn: (prev: CompletedParamState[]) => CompletedParamState[]) => void;\n setSuggestions: (fn: Suggestion[] | ((prev: Suggestion[]) => Suggestion[])) => void;\n setActiveDropdownIndex: (index: number) => void;\n filterBaseRef: MutableRefObject<number>;\n pillTappedRef: MutableRefObject<boolean>;\n}\n\nexport function useAutocompletePills({\n completedParams,\n suggestions,\n setCompletedParams,\n setSuggestions,\n setActiveDropdownIndex,\n filterBaseRef,\n pillTappedRef,\n}: UseAutocompletePillsOptions) {\n const setActivePill = useCallback(\n (index: number) => {\n const actionable = suggestions.filter((s) => s.type !== \"placeholder\");\n if (index < 0 || index >= actionable.length) return;\n const moved = actionable[index];\n const rest = actionable.filter((_, i) => i !== index);\n const placeholders = suggestions.filter((s) => s.type === \"placeholder\");\n setSuggestions([...placeholders, moved, ...rest]);\n pillTappedRef.current = true;\n setActiveDropdownIndex(-1);\n },\n [suggestions, setSuggestions, setActiveDropdownIndex, pillTappedRef],\n );\n\n const removeLastParam = useCallback(() => {\n if (completedParams.length === 0) return;\n const lastParam = completedParams[completedParams.length - 1];\n const restoredSuggestion: Suggestion = {\n type: lastParam.suggestionType,\n text: lastParam.suggestionPlaceholder,\n required: true,\n options: lastParam.options,\n };\n setCompletedParams((prev) => prev.slice(0, -1));\n setSuggestions((prev: Suggestion[]) => [restoredSuggestion, ...prev]);\n setActiveDropdownIndex(-1);\n }, [completedParams, setCompletedParams, setSuggestions, setActiveDropdownIndex]);\n\n const reEditParam = useCallback(\n (param: CompletedParamState) => {\n const restoredSuggestion: Suggestion = {\n type: param.suggestionType,\n text: param.suggestionPlaceholder,\n required: true,\n options: param.options,\n };\n return {\n apply: (setText: (fn: (prev: string) => string) => void) => {\n setText((prev) => {\n let pos = 0;\n for (const p of completedParams) {\n const idx = prev.indexOf(p.text, pos);\n if (idx === -1) continue;\n if (p.id === param.id) {\n const before = prev.slice(0, idx);\n const after = prev.slice(idx + param.text.length);\n const cleaned = (before + after).replace(/ {2,}/g, \" \");\n filterBaseRef.current = Math.min(filterBaseRef.current, cleaned.length);\n return cleaned;\n }\n pos = idx + p.text.length;\n }\n return prev;\n });\n setCompletedParams((prev) => prev.filter((p) => p.id !== param.id));\n setSuggestions((prev: Suggestion[]) => [restoredSuggestion, ...prev]);\n setActiveDropdownIndex(-1);\n pillTappedRef.current = true;\n },\n };\n },\n [\n completedParams,\n setCompletedParams,\n setSuggestions,\n setActiveDropdownIndex,\n filterBaseRef,\n pillTappedRef,\n ],\n );\n\n return { setActivePill, removeLastParam, reEditParam };\n}\n"],"mappings":"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","names":["forwardRef","useCallback","useEffect","useImperativeHandle","useRef","useState","AIAutocomplete_default","AIAutocompleteDropdown_default","SuggestionGrid_default","SuggestionItem_default","jsx","jsxs","SuggestionItem","option","isHighlighted","onSelect","onHighlight","id","className","SuggestionItem_default","e","jsx","SuggestionGrid","options","activeIndex","onSelect","onHighlight","listboxId","SuggestionGrid_default","option","SuggestionItem","jsx","AIAutocompleteDropdown","suggestions","activeIndex","onSelect","onHighlight","isOpen","id","className","activeSuggestion","hasOptions","AIAutocompleteDropdown_default","e","SuggestionGrid","PillList_default","jsx","getPillOpacity","index","PillList","pills","activePillIndex","onSelectPill","PillList_default","pill","i","useCallback","useMemo","useRef","useState","filterOptions","options","query","trimmed","lower","o","findExactMatch","deriveSegments","text","completedParams","result","pos","param","idx","remaining","reconcileParams","valid","invalid","useCallback","useEffect","useRef","useState","SDK_VERSION","DEFAULT_ENDPOINT","hasWarnedMissingKey","getApiKey","config","key","getAuthScheme","generateRequestId","toWireParam","param","includeText","fetchSuggestions","rawQuery","completedParams","options","apiConfig","apiKey","authScheme","rawCount","p","contactAccountCount","body","headers","response","buildQuery","text","completedParams","result","typeCounts","updatedParams","param","count","placeholder","index","applyOptionOverrides","suggestions","overrides","s","fn","extra","existingTexts","o","deduped","DEBOUNCE_MS","SLOW_DEBOUNCE_MS","MIN_CHARS_DIFF","useAutocompleteFetch","textRef","suggestionsRef","filterBaseRef","maskCompletedTextRef","apiConfigRef","optionOverridesRef","onErrorRef","setCompletedParams","setSuggestions","setActiveDropdownIndex","isLoading","setIsLoading","useState","error","setError","isReady","setIsReady","fetchVersionRef","useRef","abortRef","lastRawQueryRef","doFetch","useCallback","rawQuery","completed","controller","version","textAtRequest","s","res","fetchSuggestions","newSuggestions","applyOptionOverrides","input","lastInput","currentText","inProgressIdx","active","query","match","findExactMatch","prev","err","caughtError","useEffect","useDebouncedFetch","text","completedParams","skipNextFetchRef","debounceRef","slowDebounceRef","hasFetchedRef","attemptFetch","minDiff","currentQuery","tappableFiltered","filterOptions","o","hasExactMatch","isInFilterZone","updatedParams","buildQuery","isDeleting","charDiff","useCallback","useAutocompleteKeyboard","activeDropdownIndex","setActiveDropdownIndex","filteredOptions","selectOption","onSubmitRef","text","completedParams","isDropdownOpen","hasPlaceholder","placeholderText","suggestions","filterBaseRef","columns","setText","setCompletedParams","setSuggestions","getTappableIndices","useCallback","tappable","o","i","buckets","e","tappableIndices","currentPos","nextPos","prevPos","rightNeighbor","leftNeighbor","rawQuery","finalParams","buildQuery","result","firstTappable","placeholderSuggestion","s","prev","useCallback","useAutocompletePills","completedParams","suggestions","setCompletedParams","setSuggestions","setActiveDropdownIndex","filterBaseRef","pillTappedRef","setActivePill","index","actionable","s","moved","rest","_","i","placeholders","removeLastParam","lastParam","restoredSuggestion","prev","reEditParam","param","setText","pos","p","idx","before","after","cleaned","idCounter","useStableId","id","useRef","useAIAutocomplete","onSubmit","onError","optionOverrides","maskCompletedText","customPlaceholder","apiConfig","columns","eagerSuggestions","controlledValue","controlledParams","onChangeProp","onParamsChange","isTextControlled","isParamsControlled","internalText","setInternalText","useState","internalCompletedParams","setInternalCompletedParams","suggestions","setSuggestions","activeDropdownIndex","setActiveDropdownIndex","text","completedParams","onSubmitRef","onChangeRef","onParamsChangeRef","controlledValueRef","controlledParamsRef","setText","useCallback","value","newVal","prev","setCompletedParams","onErrorRef","optionOverridesRef","maskCompletedTextRef","apiConfigRef","textRef","suggestionsRef","filterBaseRef","pillTappedRef","skipNextFetchRef","listboxId","doFetch","isLoading","error","isReady","lastRawQueryRef","useAutocompleteFetch","useDebouncedFetch","segments","useMemo","deriveSegments","filterQuery","placeholderText","s","actionableSuggestions","activeSuggestion","overrideFn","baseOptions","filteredOptions","filterOptions","hasPlaceholder","isDropdownOpen","selectOption","option","completed","base","prefix","lastWord","needsSpace","newText","remainingActionable","handleChange","e","raw","newValue","valid","invalid","reconcileParams","param","newFilterQuery","match","findExactMatch","handleKeyDown","useAutocompleteKeyboard","pills","useAutocompletePills","reEditParam","reset","activeDescendantId","jsx","jsxs","AIAutocomplete","forwardRef","onSubmit","onError","optionOverrides","maskCompletedText","placeholder","className","apiConfig","columns","eagerSuggestions","value","controlledParams","onChangeProp","onParamsChange","ref","textareaRef","useRef","showCheckmark","setShowCheckmark","useState","handleSubmitRef","checkmarkTimerRef","useEffect","completedParams","suggestionPills","setActivePill","segments","inputProps","dropdownProps","reset","useAIAutocomplete","result","useImperativeHandle","handleContainerClick","canSubmit","handleSubmit","useCallback","rawQuery","finalParams","buildQuery","onChange","inputPlaceholder","restProps","isEmpty","AIAutocomplete_default","AIAutocompleteDropdown","seg","i","PillList","e"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magicx-eng/ai-autocomplete-react",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "AI Autocomplete React SDK — guided autocomplete with pill-based input and dropdown suggestions",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -32,20 +32,21 @@
32
32
  "prepublishOnly": "pnpm lint && pnpm test && pnpm build"
33
33
  },
34
34
  "peerDependencies": {
35
- "react": ">=18.0.0",
36
- "react-dom": ">=18.0.0"
35
+ "react": ">=17.0.0",
36
+ "react-dom": ">=17.0.0"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@biomejs/biome": "^2.4.5",
40
40
  "@testing-library/jest-dom": "^6.9.1",
41
- "@testing-library/react": "^16.3.2",
42
- "@types/react": "^19.2.14",
43
- "@types/react-dom": "^19.2.3",
41
+ "@testing-library/react": "^12.1.5",
42
+ "@testing-library/react-hooks": "^8.0.1",
43
+ "@types/react": "^17.0.0",
44
+ "@types/react-dom": "^17.0.0",
44
45
  "@vitejs/plugin-react": "^5.1.4",
45
46
  "jsdom": "^28.1.0",
46
47
  "msw": "^2.12.10",
47
- "react": "^19.2.4",
48
- "react-dom": "^19.2.4",
48
+ "react": "^17.0.2",
49
+ "react-dom": "^17.0.2",
49
50
  "tsup": "^8.5.1",
50
51
  "typescript": "^5.9.3",
51
52
  "vite": "^7.3.1",