({
inputValue,
cursorPosition,
slashCommands,
localAgents,
fileTree,
disableAgentSuggestions = false,
currentAgentMode,
}: SuggestionEngineOptions)
| 584 | } |
| 585 | |
| 586 | export const useSuggestionEngine = ({ |
| 587 | inputValue, |
| 588 | cursorPosition, |
| 589 | slashCommands, |
| 590 | localAgents, |
| 591 | fileTree, |
| 592 | disableAgentSuggestions = false, |
| 593 | currentAgentMode, |
| 594 | }: SuggestionEngineOptions): SuggestionEngineResult => { |
| 595 | const deferredInput = useDeferredValue(inputValue) |
| 596 | const slashCacheRef = useRef<Map<string, MatchedSlashCommand[]>>( |
| 597 | new Map<string, SlashCommand[]>(), |
| 598 | ) |
| 599 | const agentCacheRef = useRef<Map<string, MatchedAgentInfo[]>>( |
| 600 | new Map<string, MatchedAgentInfo[]>(), |
| 601 | ) |
| 602 | const fileCacheRef = useRef<Map<string, MatchedFileInfo[]>>( |
| 603 | new Map<string, MatchedFileInfo[]>(), |
| 604 | ) |
| 605 | const fileRefreshIdRef = useRef(0) |
| 606 | const [filePaths, setFilePaths] = useState<PathInfo[]>(() => |
| 607 | flattenFileTree(fileTree), |
| 608 | ) |
| 609 | |
| 610 | useEffect(() => { |
| 611 | slashCacheRef.current.clear() |
| 612 | }, [slashCommands]) |
| 613 | |
| 614 | useEffect(() => { |
| 615 | agentCacheRef.current.clear() |
| 616 | }, [localAgents]) |
| 617 | |
| 618 | useEffect(() => { |
| 619 | fileCacheRef.current.clear() |
| 620 | }, [filePaths]) |
| 621 | |
| 622 | useEffect(() => { |
| 623 | setFilePaths(flattenFileTree(fileTree)) |
| 624 | }, [fileTree]) |
| 625 | |
| 626 | const slashContext = useMemo( |
| 627 | () => parseSlashContext(deferredInput), |
| 628 | [deferredInput], |
| 629 | ) |
| 630 | |
| 631 | // Note: mentionContext uses inputValue directly (not deferredInput) because |
| 632 | // the cursor position must match the text being parsed. Using deferredInput |
| 633 | // with current cursorPosition causes desync during heavy renders, making the |
| 634 | // @ menu fail to appear intermittently (especially after long conversations). |
| 635 | const mentionContext = useMemo( |
| 636 | () => parseMentionContext(inputValue, cursorPosition), |
| 637 | [inputValue, cursorPosition], |
| 638 | ) |
| 639 | |
| 640 | useEffect(() => { |
| 641 | if (!mentionContext.active) { |
| 642 | return |
| 643 | } |
no test coverage detected