{"version":3,"sources":["../src/components/base-ai-textarea/base-ai-textarea.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useMemo, useState } from \"react\";\nimport { Descendant, Editor } from \"slate\";\nimport { Editable, Slate } from \"slate-react\";\nimport { twMerge } from \"tailwind-merge\";\nimport { useAutosuggestions } from \"../../hooks/base-ai-textarea-implementation/use-autosuggestions\";\nimport { useAiTextareaEditor } from \"../../hooks/base-ai-textarea-implementation/use-ai-textarea-editor\";\nimport { usePopulateAiTextareaRef } from \"../../hooks/base-ai-textarea-implementation/use-populate-ai-textarea-ref\";\nimport {\n  getFullEditorTextWithNewlines,\n  getTextAroundCollapsedCursor,\n} from \"../../lib/get-text-around-cursor\";\nimport { addAutocompletionsToEditor } from \"../../lib/slatejs-edits/add-autocompletions\";\nimport { clearAutocompletionsFromEditor } from \"../../lib/slatejs-edits/clear-autocompletions\";\nimport { replaceEditorText } from \"../../lib/slatejs-edits/replace-text\";\nimport { BaseAutosuggestionsConfig, defaultBaseAutosuggestionsConfig } from \"../../types/base\";\nimport { AutosuggestionState } from \"../../types/base/autosuggestion-state\";\nimport { BaseAiTextareaProps } from \"../../types/base/base-ai-textarea-props\";\nimport \"./base-ai-textarea.css\";\nimport { HoveringToolbar } from \"../hovering-toolbar/hovering-toolbar\";\nimport { makeRenderElementFunction } from \"./render-element\";\nimport { makeRenderPlaceholderFunction } from \"./render-placeholder\";\nimport { useAddBrandingCss } from \"./use-add-branding-css\";\nimport {\n  HoveringEditorProvider,\n  useHoveringEditorContext,\n} from \"../hovering-toolbar/hovering-editor-provider\";\nimport { TrackerTextEditedSinceLastCursorMovement } from \"./track-cursor-moved-since-last-text-change\";\n\n/**\n * Purpose: to be used as the `ref` type for `AiTextarea` and `BaseAiTextarea`.\n *\n * This interface extends `HTMLElement`, and is the subset of `HTMLTextAreaElement` that \"actually matters\".\n * It provides the core functionality that consumers of `HTMLTextAreaElement` need 99.9% of the time:\n * - `value`: the current value of the textarea\n * - `focus`: make the textarea focused\n * - `blur`: make the textarea unfocused\n */\nexport interface HTMLAiTextAreaElement extends HTMLElement {\n  /**\n   * The current value of the textarea.\n   */\n  value: string;\n\n  /**\n   * focus on the textarea\n   */\n  focus: () => void;\n\n  /**\n   * unfocus the textarea.\n   *\n   * Called `blur` for syntactic compatibility with `HTMLTextAreaElement`.\n   */\n  blur: () => void;\n}\n\n/**\n * Not intended for direct use. Use AiTextarea instead.\n *\n * The `BaseAiTextarea` includes the basic UX component,\n * without the business logic / AI logic that makes the content useful and coherent.\n *\n * It is useful if you want to build your own backend, with fully custom business logic\n * for figuring out which contnet to fill in.\n */\nexport const BaseAiTextarea = React.forwardRef(\n  (props: BaseAiTextareaProps, ref: React.Ref<HTMLAiTextAreaElement>) => {\n    return (\n      <HoveringEditorProvider>\n        <BaseAiTextareaWithHoveringContext {...props} ref={ref} />\n      </HoveringEditorProvider>\n    );\n  },\n);\n\n/**\n * Not intended for direct use. Use `AiTextarea` instead.\n *\n * This is the private core of the `BaseAiTextarea` component.\n * For practical purposes the implementation is cleaner assuming containment in a `HoveringEditorProviderContext`.\n *\n * Therefore we separate the core logic into this component,\n * and wrap it in a `HoveringEditorProviderContext` in `BaseAiTextarea`.\n */\nconst BaseAiTextareaWithHoveringContext = React.forwardRef(\n  (props: BaseAiTextareaProps, ref: React.Ref<HTMLAiTextAreaElement>) => {\n    const autosuggestionsConfig: BaseAutosuggestionsConfig = {\n      ...defaultBaseAutosuggestionsConfig,\n      ...props.baseAutosuggestionsConfig,\n    };\n\n    const valueOnInitialRender = useMemo(() => props.value ?? \"\", []);\n    const [lastKnownFullEditorText, setLastKnownFullEditorText] = useState(valueOnInitialRender);\n    const [cursorMovedSinceLastTextChange, setCursorMovedSinceLastTextChange] = useState(false);\n    const [isUserInputActive, setIsUserInputActive] = useState(false);\n\n    // // When the editor text changes, we want to reset the `textEditedSinceLastCursorMovement` state.\n    // useEffect(() => {\n    //   setCursorMovedSinceLastTextChange(false);\n    // }, [lastKnownFullEditorText]);\n\n    const initialValue: Descendant[] = useMemo(() => {\n      return [\n        {\n          type: \"paragraph\",\n          children: [{ text: valueOnInitialRender }],\n        },\n      ];\n    }, [valueOnInitialRender]);\n\n    const editor = useAiTextareaEditor();\n\n    const { isDisplayed: hoveringEditorIsDisplayed, setIsDisplayed: setHoveringEditorIsDisplayed } =\n      useHoveringEditorContext();\n\n    const insertText = useCallback(\n      (autosuggestion: AutosuggestionState) => {\n        Editor.insertText(editor, autosuggestion.text, {\n          at: autosuggestion.point,\n        });\n      },\n      [editor],\n    );\n\n    const shouldDisableAutosuggestions =\n      // textarea is manually disabled:\n      autosuggestionsConfig.disabled ||\n      // hovering editor is displayed:\n      hoveringEditorIsDisplayed ||\n      // the cursor has moved since the last text change AND we are configured to disable autosuggestions in this case:\n      (cursorMovedSinceLastTextChange &&\n        autosuggestionsConfig.temporarilyDisableWhenMovingCursorWithoutChangingText) ||\n      // not user input and we want to disable non-trusted events (like text insertion from autocomplete plugins):\n      (!isUserInputActive && autosuggestionsConfig.temporarilyDisableNotTrustedEvents);\n\n    const {\n      currentAutocompleteSuggestion,\n      onChangeHandler: onChangeHandlerForAutocomplete,\n      onKeyDownHandler: onKeyDownHandlerForAutocomplete,\n      onTouchStartHandler: onTouchStartHandlerForAutocomplete,\n    } = useAutosuggestions(\n      autosuggestionsConfig.debounceTime,\n      autosuggestionsConfig.shouldAcceptAutosuggestionOnKeyPress,\n      autosuggestionsConfig.shouldAcceptAutosuggestionOnTouch,\n      autosuggestionsConfig.apiConfig.autosuggestionsFunction,\n      insertText,\n      autosuggestionsConfig.disableWhenEmpty,\n      shouldDisableAutosuggestions,\n    );\n\n    const onKeyDownHandlerForHoveringEditor = useCallback(\n      (event: React.KeyboardEvent<HTMLDivElement>) => {\n        if (\n          autosuggestionsConfig.shouldToggleHoveringEditorOnKeyPress(event, props.shortcut ?? \"k\")\n        ) {\n          event.preventDefault();\n          setHoveringEditorIsDisplayed(!hoveringEditorIsDisplayed);\n        }\n      },\n      [\n        hoveringEditorIsDisplayed,\n        setHoveringEditorIsDisplayed,\n        autosuggestionsConfig.shouldToggleHoveringEditorOnKeyPress,\n      ],\n    );\n\n    // sync autosuggestions state with the editor\n    useEffect(() => {\n      clearAutocompletionsFromEditor(editor);\n      if (currentAutocompleteSuggestion) {\n        addAutocompletionsToEditor(\n          editor,\n          currentAutocompleteSuggestion.text,\n          currentAutocompleteSuggestion.point,\n        );\n      }\n    }, [currentAutocompleteSuggestion]);\n\n    const suggestionStyleAugmented: React.CSSProperties = useMemo(() => {\n      return {\n        fontStyle: \"italic\",\n        color: \"gray\",\n        ...props.suggestionsStyle,\n      };\n    }, [props.suggestionsStyle]);\n\n    const renderElementMemoized = useMemo(() => {\n      return makeRenderElementFunction(suggestionStyleAugmented);\n    }, [suggestionStyleAugmented]);\n\n    const renderPlaceholderMemoized = useMemo(() => {\n      // For some reason slateJS specifies a top value of 0, which makes for strange styling. We override this here.\n      const placeholderStyleSlatejsOverrides: React.CSSProperties = {\n        top: undefined,\n      };\n\n      const placeholderStyleAugmented: React.CSSProperties = {\n        ...placeholderStyleSlatejsOverrides,\n        ...props.placeholderStyle,\n      };\n\n      return makeRenderPlaceholderFunction(placeholderStyleAugmented);\n    }, [props.placeholderStyle]);\n\n    // update the editor text, but only when the value changes from outside the component\n    useEffect(() => {\n      if (props.value === lastKnownFullEditorText) {\n        return;\n      }\n\n      setLastKnownFullEditorText(props.value ?? \"\");\n      replaceEditorText(editor, props.value ?? \"\");\n    }, [props.value]);\n\n    // separate into TextareaHTMLAttributes<HTMLDivElement> and AiTextareaProps\n    const {\n      placeholderStyle,\n      value,\n      hoverMenuClassname,\n      onValueChange,\n      baseAutosuggestionsConfig: autosuggestionsConfigFromProps,\n      className,\n      onChange,\n      onKeyDown,\n      disableBranding,\n      ...propsToForward\n    } = props;\n\n    useAddBrandingCss(suggestionStyleAugmented, disableBranding);\n    usePopulateAiTextareaRef(editor, ref);\n\n    const moddedClassName = (() => {\n      const baseClassName = \"ai-textarea\";\n      const brandingClass = disableBranding ? \"no-branding\" : \"with-branding\";\n      const defaultTailwindClassName = \"bg-white overflow-y-auto resize-y\";\n      const mergedClassName = twMerge(defaultTailwindClassName, className ?? \"\");\n      return `${baseClassName} ${brandingClass} ${mergedClassName}`;\n    })();\n\n    return (\n      <Slate\n        editor={editor}\n        initialValue={initialValue}\n        onChange={(value) => {\n          const newEditorState = getTextAroundCollapsedCursor(editor);\n\n          const fullEditorText = newEditorState\n            ? newEditorState.textBeforeCursor + newEditorState.textAfterCursor\n            : getFullEditorTextWithNewlines(editor); // we don't double-parse the editor. When `newEditorState` is null, we didn't parse the editor yet.\n\n          setLastKnownFullEditorText((prev) => {\n            if (prev !== fullEditorText) {\n              setCursorMovedSinceLastTextChange(false);\n            }\n            return fullEditorText;\n          });\n\n          onChangeHandlerForAutocomplete(newEditorState);\n\n          props.onValueChange?.(fullEditorText);\n          props.onChange?.(makeSemiFakeReactTextAreaEvent(fullEditorText));\n        }}\n      >\n        <TrackerTextEditedSinceLastCursorMovement\n          setCursorMovedSinceLastTextChange={setCursorMovedSinceLastTextChange}\n        />\n        <HoveringToolbar\n          apiConfig={autosuggestionsConfig.apiConfig}\n          contextCategories={autosuggestionsConfig.contextCategories}\n          hoverMenuClassname={hoverMenuClassname}\n        />\n        <Editable\n          renderElement={renderElementMemoized}\n          renderPlaceholder={renderPlaceholderMemoized}\n          onKeyDown={(event) => {\n            setIsUserInputActive(true);\n            onKeyDownHandlerForHoveringEditor(event); // forward the event for internal use\n            onKeyDownHandlerForAutocomplete(event); // forward the event for internal use\n            props.onKeyDown?.(event); // forward the event for external use\n          }}\n          onTouchStart={(event) => {\n            onTouchStartHandlerForAutocomplete(event); // forward the event for internal use\n          }}\n          data-testid=\"ai-textarea-editable\"\n          className={moddedClassName}\n          onBlur={(ev) => {\n            // clear autocompletion on blur\n            props.onBlur?.(ev);\n            clearAutocompletionsFromEditor(editor);\n            setIsUserInputActive(false);\n          }}\n          {...propsToForward}\n        />\n      </Slate>\n    );\n  },\n);\n\n// Consumers of <textarea> expect a `onChange: (React.ChangeEvent<HTMLTextAreaElement>) => void` event handler to be passed in.\n// This is *extremely* common, and we want to support it.\n//\n// We can't support the full functionality, but in 99% of cases, the consumer only cares about the `event.target.value` property --\n// that's how they get the new value of the textarea.\n//\n// So, the tradeoff we are making is minimizing compiler complaint, with a small chance of runtime error.\n// The alternative would be defining a different onChange entrypoint (we actually do have that in `onValueChange`),\n// And starting to explain subtleties to users the moment they try to use the component for the first time for very basic functionality.\n//\n// If this proves problematic, we can always revisit this decision.\nfunction makeSemiFakeReactTextAreaEvent(\n  currentText: string,\n): React.ChangeEvent<HTMLTextAreaElement> {\n  return {\n    target: {\n      value: currentText,\n      type: \"ai-textarea\",\n    },\n    currentTarget: {\n      value: currentText,\n      type: \"ai-textarea\",\n    },\n  } as React.ChangeEvent<HTMLTextAreaElement>;\n}\n"],"mappings":"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","names":["_a","value"]}