{"version":3,"sources":["../src/hooks/base-ai-textarea-implementation/use-autosuggestions.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useState } from \"react\";\nimport { Debouncer } from \"../../lib/debouncer\";\nimport { nullableCompatibleEqualityCheck } from \"../../lib/utils\";\nimport { AutosuggestionsBareFunction } from \"../../types/base\";\nimport { AutosuggestionState } from \"../../types/base/autosuggestion-state\";\nimport {\n  EditorAutocompleteState,\n  areEqual_autocompleteState,\n} from \"../../types/base/editor-autocomplete-state\";\n\nexport interface UseAutosuggestionsResult {\n  currentAutocompleteSuggestion: AutosuggestionState | null;\n  onChangeHandler: (newEditorState: EditorAutocompleteState | null) => void;\n  onKeyDownHandler: (event: React.KeyboardEvent<HTMLDivElement>) => void;\n  onTouchStartHandler: (event: React.TouchEvent<HTMLDivElement>) => void;\n}\n\nexport function useAutosuggestions(\n  debounceTime: number,\n  shouldAcceptAutosuggestionOnKeyPress: (event: React.KeyboardEvent<HTMLDivElement>) => boolean,\n  shouldAcceptAutosuggestionOnTouch: (event: React.TouchEvent<HTMLDivElement>) => boolean,\n  autosuggestionFunction: AutosuggestionsBareFunction,\n  insertAutocompleteSuggestion: (suggestion: AutosuggestionState) => void,\n  disableWhenEmpty: boolean,\n  disabled: boolean,\n): UseAutosuggestionsResult {\n  const [previousAutocompleteState, setPreviousAutocompleteState] =\n    useState<EditorAutocompleteState | null>(null);\n\n  const [currentAutocompleteSuggestion, setCurrentAutocompleteSuggestion] =\n    useState<AutosuggestionState | null>(null);\n\n  const awaitForAndAppendSuggestion: (\n    editorAutocompleteState: EditorAutocompleteState,\n    abortSignal: AbortSignal,\n  ) => Promise<void> = useCallback(\n    async (editorAutocompleteState: EditorAutocompleteState, abortSignal: AbortSignal) => {\n      // early return if disabled\n      if (disabled) {\n        return;\n      }\n\n      if (\n        disableWhenEmpty &&\n        editorAutocompleteState.textBeforeCursor === \"\" &&\n        editorAutocompleteState.textAfterCursor === \"\"\n      ) {\n        return;\n      }\n\n      // fetch the suggestion\n      const suggestion = await autosuggestionFunction(editorAutocompleteState, abortSignal);\n\n      // We'll assume for now that the autocomplete function might or might not respect the abort signal.\n      if (!suggestion || abortSignal.aborted) {\n        throw new DOMException(\"Aborted\", \"AbortError\");\n      }\n\n      setCurrentAutocompleteSuggestion({\n        text: suggestion,\n        point: editorAutocompleteState.cursorPoint,\n      });\n    },\n    [autosuggestionFunction, setCurrentAutocompleteSuggestion, disableWhenEmpty, disabled],\n  );\n\n  const debouncedFunction = useMemo(\n    () => new Debouncer<[editorAutocompleteState: EditorAutocompleteState]>(debounceTime),\n    [debounceTime],\n  );\n\n  // clean current state when unmounting or disabling\n  useEffect(() => {\n    return () => {\n      debouncedFunction.cancel();\n      setCurrentAutocompleteSuggestion(null);\n    };\n  }, [debouncedFunction, disabled]);\n\n  const onChange = useCallback(\n    (newEditorState: EditorAutocompleteState | null) => {\n      const editorStateHasChanged = !nullableCompatibleEqualityCheck(\n        areEqual_autocompleteState,\n        previousAutocompleteState,\n        newEditorState,\n      );\n      setPreviousAutocompleteState(newEditorState);\n\n      // if no change, do nothing\n      if (!editorStateHasChanged) {\n        return;\n      }\n\n      // if change, then first null out the current suggestion\n      setCurrentAutocompleteSuggestion(null);\n\n      // then try to get a new suggestion, debouncing to avoid too many requests while typing\n      if (newEditorState) {\n        debouncedFunction.debounce(awaitForAndAppendSuggestion, newEditorState);\n      } else {\n        debouncedFunction.cancel();\n      }\n    },\n    [\n      previousAutocompleteState,\n      setPreviousAutocompleteState,\n      debouncedFunction,\n      awaitForAndAppendSuggestion,\n      setCurrentAutocompleteSuggestion,\n    ],\n  );\n\n  const keyDownOrTouchHandler = useCallback(\n    (event: React.KeyboardEvent<HTMLDivElement> | React.TouchEvent<HTMLDivElement>) => {\n      if (currentAutocompleteSuggestion) {\n        const shouldAcceptSuggestion =\n          event.type === \"touchstart\"\n            ? shouldAcceptAutosuggestionOnTouch(event as React.TouchEvent<HTMLDivElement>)\n            : shouldAcceptAutosuggestionOnKeyPress(event as React.KeyboardEvent<HTMLDivElement>);\n\n        if (shouldAcceptSuggestion) {\n          event.preventDefault();\n          insertAutocompleteSuggestion(currentAutocompleteSuggestion);\n          setCurrentAutocompleteSuggestion(null);\n        }\n      }\n    },\n    [\n      currentAutocompleteSuggestion,\n      setCurrentAutocompleteSuggestion,\n      insertAutocompleteSuggestion,\n      shouldAcceptAutosuggestionOnKeyPress,\n    ],\n  );\n\n  return {\n    currentAutocompleteSuggestion,\n    onChangeHandler: onChange,\n    onKeyDownHandler: keyDownOrTouchHandler,\n    onTouchStartHandler: keyDownOrTouchHandler,\n  };\n}\n"],"mappings":";;;;;;;;;;;;;;AAAA,SAAS,aAAa,WAAW,SAAS,gBAAgB;AAiBnD,SAAS,mBACd,cACA,sCACA,mCACA,wBACA,8BACA,kBACA,UAC0B;AAC1B,QAAM,CAAC,2BAA2B,4BAA4B,IAC5D,SAAyC,IAAI;AAE/C,QAAM,CAAC,+BAA+B,gCAAgC,IACpE,SAAqC,IAAI;AAE3C,QAAM,8BAGe;AAAA,IACnB,CAAO,yBAAkD,gBAA6B;AAEpF,UAAI,UAAU;AACZ;AAAA,MACF;AAEA,UACE,oBACA,wBAAwB,qBAAqB,MAC7C,wBAAwB,oBAAoB,IAC5C;AACA;AAAA,MACF;AAGA,YAAM,aAAa,MAAM,uBAAuB,yBAAyB,WAAW;AAGpF,UAAI,CAAC,cAAc,YAAY,SAAS;AACtC,cAAM,IAAI,aAAa,WAAW,YAAY;AAAA,MAChD;AAEA,uCAAiC;AAAA,QAC/B,MAAM;AAAA,QACN,OAAO,wBAAwB;AAAA,MACjC,CAAC;AAAA,IACH;AAAA,IACA,CAAC,wBAAwB,kCAAkC,kBAAkB,QAAQ;AAAA,EACvF;AAEA,QAAM,oBAAoB;AAAA,IACxB,MAAM,IAAI,UAA8D,YAAY;AAAA,IACpF,CAAC,YAAY;AAAA,EACf;AAGA,YAAU,MAAM;AACd,WAAO,MAAM;AACX,wBAAkB,OAAO;AACzB,uCAAiC,IAAI;AAAA,IACvC;AAAA,EACF,GAAG,CAAC,mBAAmB,QAAQ,CAAC;AAEhC,QAAM,WAAW;AAAA,IACf,CAAC,mBAAmD;AAClD,YAAM,wBAAwB,CAAC;AAAA,QAC7B;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA,mCAA6B,cAAc;AAG3C,UAAI,CAAC,uBAAuB;AAC1B;AAAA,MACF;AAGA,uCAAiC,IAAI;AAGrC,UAAI,gBAAgB;AAClB,0BAAkB,SAAS,6BAA6B,cAAc;AAAA,MACxE,OAAO;AACL,0BAAkB,OAAO;AAAA,MAC3B;AAAA,IACF;AAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,wBAAwB;AAAA,IAC5B,CAAC,UAAkF;AACjF,UAAI,+BAA+B;AACjC,cAAM,yBACJ,MAAM,SAAS,eACX,kCAAkC,KAAyC,IAC3E,qCAAqC,KAA4C;AAEvF,YAAI,wBAAwB;AAC1B,gBAAM,eAAe;AACrB,uCAA6B,6BAA6B;AAC1D,2CAAiC,IAAI;AAAA,QACvC;AAAA,MACF;AAAA,IACF;AAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA,iBAAiB;AAAA,IACjB,kBAAkB;AAAA,IAClB,qBAAqB;AAAA,EACvB;AACF;","names":[]}