@liveblocks/react-ui 3.14.0-pre6 → 3.14.0-rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -62,4 +62,4 @@ learn more about
62
62
  Licensed under the Apache License 2.0, Copyright © 2021-present
63
63
  [Liveblocks](https://liveblocks.io).
64
64
 
65
- See [LICENSE](../../LICENSE) for more information.
65
+ See [LICENSE](../../licenses/LICENSE-APACHE-2.0) for more information.
@@ -278,9 +278,11 @@ const AiComposerEditor = react.forwardRef(
278
278
  [editor]
279
279
  );
280
280
  _private.useLayoutEffect(() => {
281
- if (autoFocus) {
282
- focus();
281
+ if (!autoFocus) {
282
+ return;
283
283
  }
284
+ const timeout = setTimeout(() => focus(), 0);
285
+ return () => clearTimeout(timeout);
284
286
  }, [autoFocus, editor, focus]);
285
287
  _private.useLayoutEffect(() => {
286
288
  if (isFocused && editor.selection === null) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../../src/primitives/AiComposer/index.tsx"],"sourcesContent":["import {\n type AiChatMessage,\n kInternal,\n Signal,\n type WithNavigation,\n} from \"@liveblocks/core\";\nimport { useClient } from \"@liveblocks/react\";\nimport { useLayoutEffect, useSignal } from \"@liveblocks/react/_private\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport type { FocusEvent, FormEvent, KeyboardEvent, MouseEvent } from \"react\";\nimport {\n forwardRef,\n useCallback,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport {\n createEditor,\n Editor as SlateEditor,\n Transforms as SlateTransforms,\n} from \"slate\";\nimport { withHistory } from \"slate-history\";\nimport {\n Editable,\n ReactEditor,\n type RenderPlaceholderProps,\n Slate,\n withReact,\n} from \"slate-react\";\n\nimport type { AiComposerBody } from \"../../types\";\nimport { requestSubmit } from \"../../utils/request-submit\";\nimport { useInitial } from \"../../utils/use-initial\";\nimport { withNormalize } from \"../slate/plugins/normalize\";\nimport { getDOMRange } from \"../slate/utils/get-dom-range\";\nimport { isEmpty } from \"../slate/utils/is-empty\";\nimport {\n AiComposerContext,\n AiComposerEditorContext,\n useAiComposer,\n useAiComposerEditorContext,\n} from \"./contexts\";\nimport type {\n AiComposerEditorProps,\n AiComposerFormProps,\n AiComposerSubmitProps,\n} from \"./types\";\n\nconst AI_COMPOSER_SUBMIT_NAME = \"AiComposerSubmit\";\nconst AI_COMPOSER_ABORT_NAME = \"AiComposerAbort\";\nconst AI_COMPOSER_EDITOR_NAME = \"AiComposerEditor\";\nconst AI_COMPOSER_FORM_NAME = \"AiComposerForm\";\n\ntype UiChatMessage = WithNavigation<AiChatMessage>;\n\n/* -------------------------------------------------------------------------------------------------\n * Form\n * -----------------------------------------------------------------------------------------------*/\n\nconst emptyMessagesΣ = new Signal<UiChatMessage[]>([]);\n\nfunction getLastMessageId(messages: UiChatMessage[]) {\n const lastMessage = messages[messages.length - 1];\n\n if (lastMessage === undefined) {\n return null;\n }\n\n return lastMessage.id;\n}\n\nfunction getAbortableMessageId(messages: UiChatMessage[]) {\n return messages.find(\n (message) =>\n message.role === \"assistant\" &&\n (message.status === \"generating\" || message.status === \"awaiting-tool\")\n )?.id;\n}\n\n/**\n * Surrounds the AI composer's content and handles submissions.\n *\n * @example\n * <AiComposer.Form onComposerSubmit={({ text }) => {}}>\n *\t <AiComposer.Editor />\n * <AiComposer.Submit />\n * </AiComposer.Form>\n */\nexport const AiComposerForm = forwardRef<HTMLFormElement, AiComposerFormProps>(\n (\n {\n onComposerSubmit,\n onSubmit,\n disabled,\n chatId,\n branchId,\n asChild,\n ...props\n },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"form\";\n const client = useClient();\n const formRef = useRef<HTMLFormElement | null>(null);\n const editor = useInitial(() =>\n withNormalize(withHistory(withReact(createEditor())))\n );\n const [isEditorEmpty, setEditorEmpty] = useState(true);\n const [isSubmitting, setSubmitting] = useState(false);\n const [isFocused, setFocused] = useState(false);\n const messagesΣ = chatId\n ? client[kInternal].ai.signals.getChatMessagesForBranchΣ(chatId, branchId)\n : emptyMessagesΣ;\n const lastMessageId = useSignal(messagesΣ, getLastMessageId);\n const abortableMessageId = useSignal(messagesΣ, getAbortableMessageId);\n const isAvailable = useSignal(\n // Subscribe to connection status signal\n client[kInternal].ai.signals.statusΣ,\n // \"Disconnected\" means the AI service is not available\n // as it represents a final error status.\n (status) => status !== \"disconnected\"\n );\n\n const isDisabled = isSubmitting || disabled === true;\n\n const canAbort = isAvailable && abortableMessageId !== undefined;\n const canSubmit = isAvailable && !isEditorEmpty && !canAbort;\n\n const clear = useCallback(() => {\n SlateTransforms.delete(editor, {\n at: {\n anchor: SlateEditor.start(editor, []),\n focus: SlateEditor.end(editor, []),\n },\n });\n }, [editor]);\n\n const select = useCallback(() => {\n SlateTransforms.select(editor, SlateEditor.end(editor, []));\n }, [editor]);\n\n const focus = useCallback(\n (resetSelection = true) => {\n try {\n // Slate's `ReactEditor.focus` method can use `setTimeout` internally\n // which prevents us from catching errors, so this is a reimplementation.\n // https://github.com/ianstormtaylor/slate/blob/main/packages/slate-dom/src/plugin/dom-editor.ts\n if (!ReactEditor.isFocused(editor)) {\n SlateTransforms.select(\n editor,\n resetSelection || !editor.selection\n ? SlateEditor.end(editor, [])\n : editor.selection\n );\n\n const element = ReactEditor.toDOMNode(editor, editor);\n\n if (editor.selection) {\n const domSelection = window.getSelection();\n const domRange = getDOMRange(editor, editor.selection);\n\n if (domRange) {\n domSelection?.removeAllRanges();\n domSelection?.addRange(domRange);\n }\n }\n\n element.focus({ preventScroll: true });\n }\n } catch {\n // Slate's DOM-specific methods will throw if the editor's DOM\n // node no longer exists. This action doesn't make sense on an\n // unmounted editor so we can safely ignore it.\n }\n },\n [editor]\n );\n\n const blur = useCallback(() => {\n try {\n ReactEditor.blur(editor);\n } catch {\n // Slate's DOM-specific methods will throw if the editor's DOM\n // node no longer exists. This action doesn't make sense on an\n // unmounted editor so we can safely ignore it.\n }\n }, [editor]);\n\n const onSubmitEnd = useCallback(() => {\n clear();\n setSubmitting(false);\n }, [clear]);\n\n const handleSubmit = useCallback(\n (event: FormEvent<HTMLFormElement>) => {\n if (disabled) {\n return;\n }\n\n // In some situations (e.g. pressing Enter while composing diacritics), it's possible\n // for the form to be submitted as empty even though we already checked whether the\n // editor was empty when handling the key press.\n const isEditorEmpty = isEmpty(editor, editor.children);\n\n // We even prevent the user's `onSubmit` handler from being called if the editor is empty.\n if (isEditorEmpty) {\n event.preventDefault();\n\n return;\n }\n\n onSubmit?.(event);\n\n if (onComposerSubmit === undefined || event.isDefaultPrevented()) {\n event.preventDefault();\n return;\n }\n\n // Extract the text content from the editor.\n const content = editor.children\n .map((block) => {\n if (\"type\" in block && block.type === \"paragraph\") {\n return block.children\n .map((child) => {\n if (\"text\" in child) {\n return child.text;\n }\n return \"\";\n })\n .join(\"\");\n }\n return \"\";\n })\n .join(\"\\n\");\n\n const promise = onComposerSubmit(\n { text: content, lastMessageId },\n event\n );\n\n event.preventDefault();\n\n if (promise) {\n setSubmitting(true);\n promise.then(onSubmitEnd);\n } else {\n onSubmitEnd();\n }\n },\n [disabled, editor, onSubmit, onComposerSubmit, onSubmitEnd, lastMessageId]\n );\n\n useLayoutEffect(() => {\n setEditorEmpty(isEmpty(editor, editor.children));\n }, [editor]);\n\n const handleEditorValueChange = useCallback(() => {\n setEditorEmpty(isEmpty(editor, editor.children));\n }, [editor]);\n\n const submit = useCallback(() => {\n if (!canSubmit) {\n return;\n }\n\n // We need to wait for the next frame in some cases like when composing diacritics,\n // we want any native handling to be done first while still being handled on `keydown`.\n requestAnimationFrame(() => {\n if (formRef.current) {\n requestSubmit(formRef.current);\n }\n });\n }, [canSubmit]);\n\n const abort = useCallback(() => {\n if (!canAbort || !abortableMessageId) {\n return;\n }\n\n client[kInternal].ai.abort(abortableMessageId);\n }, [canAbort, abortableMessageId, client]);\n\n useImperativeHandle<HTMLFormElement | null, HTMLFormElement | null>(\n forwardedRef,\n () => formRef.current,\n []\n );\n\n return (\n <AiComposerEditorContext.Provider\n value={{\n editor,\n onEditorValueChange: handleEditorValueChange,\n abortableMessageId,\n setFocused,\n }}\n >\n <AiComposerContext.Provider\n value={{\n isDisabled,\n isEmpty: isEditorEmpty,\n isFocused,\n canSubmit,\n canAbort,\n submit,\n abort,\n clear,\n focus,\n blur,\n select,\n }}\n >\n <Component onSubmit={handleSubmit} {...props} ref={formRef} />\n </AiComposerContext.Provider>\n </AiComposerEditorContext.Provider>\n );\n }\n);\n\n/* -------------------------------------------------------------------------------------------------\n * Editor\n * -----------------------------------------------------------------------------------------------*/\n\nfunction AiComposerEditorPlaceholder({\n attributes,\n children,\n}: RenderPlaceholderProps) {\n const { opacity: _opacity, ...style } = attributes.style;\n\n return (\n <span {...attributes} style={style} data-placeholder=\"\">\n {children}\n </span>\n );\n}\n\n/**\n * Displays the AI composer's editor.\n *\n * @example\n * <AiComposer.Editor placeholder=\"Write a message…\" />\n */\nconst AiComposerEditor = forwardRef<HTMLDivElement, AiComposerEditorProps>(\n (\n {\n defaultValue = \"\",\n onKeyDown,\n onFocus,\n onBlur,\n disabled,\n autoFocus,\n dir,\n ...props\n },\n forwardedRef\n ) => {\n const { editor, onEditorValueChange, setFocused } =\n useAiComposerEditorContext();\n const {\n submit,\n isDisabled: isComposerDisabled,\n isFocused,\n focus,\n blur,\n select,\n } = useAiComposer();\n const isDisabled = disabled || isComposerDisabled;\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLDivElement>) => {\n onKeyDown?.(event);\n if (event.isDefaultPrevented()) return;\n\n if (event.key === \"Enter\" && !event.shiftKey) {\n event.preventDefault();\n submit();\n } else if (event.key === \"Enter\" && event.shiftKey) {\n event.preventDefault();\n editor.insertBreak();\n } else if (event.key === \"Escape\") {\n blur();\n }\n },\n [editor, onKeyDown, submit, blur]\n );\n\n const handleFocus = useCallback(\n (event: FocusEvent<HTMLDivElement>) => {\n onFocus?.(event);\n\n if (!event.isDefaultPrevented()) {\n setFocused(true);\n }\n },\n [onFocus, setFocused]\n );\n\n const handleBlur = useCallback(\n (event: FocusEvent<HTMLDivElement>) => {\n onBlur?.(event);\n\n if (!event.isDefaultPrevented()) {\n setFocused(false);\n }\n },\n [onBlur, setFocused]\n );\n\n useImperativeHandle(\n forwardedRef,\n () => ReactEditor.toDOMNode(editor, editor) as HTMLDivElement,\n [editor]\n );\n\n // Manually focus the editor when `autoFocus` is true\n useLayoutEffect(() => {\n if (autoFocus) {\n focus();\n }\n }, [autoFocus, editor, focus]);\n\n // Manually add a selection in the editor if the selection\n // is still empty after being focused\n useLayoutEffect(() => {\n if (isFocused && editor.selection === null) {\n select();\n }\n }, [editor, select, isFocused]);\n\n const initialValue: AiComposerBody = useMemo(() => {\n return defaultValue\n .split(\"\\n\")\n .map((text) => ({ type: \"paragraph\", children: [{ text }] }));\n }, [defaultValue]);\n\n return (\n <Slate\n editor={editor}\n initialValue={initialValue}\n onValueChange={onEditorValueChange}\n >\n <Editable\n dir={dir}\n enterKeyHint=\"send\"\n autoCapitalize=\"sentences\"\n aria-label=\"Composer editor\"\n onKeyDown={handleKeyDown}\n onFocus={handleFocus}\n onBlur={handleBlur}\n data-focused={isFocused || undefined}\n data-disabled={isDisabled || undefined}\n {...props}\n readOnly={isDisabled}\n disabled={isDisabled}\n renderPlaceholder={AiComposerEditorPlaceholder}\n />\n </Slate>\n );\n }\n);\n\n/* -------------------------------------------------------------------------------------------------\n * Submit\n * -----------------------------------------------------------------------------------------------*/\n\n/**\n * A button to submit the AI composer's content.\n *\n * @example\n * <AiComposer.Submit>Send</AiComposer.Submit>\n */\nexport const AiComposerSubmit = forwardRef<\n HTMLButtonElement,\n AiComposerSubmitProps\n>(({ disabled, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"button\";\n const { isDisabled: isComposerDisabled, canSubmit } = useAiComposer();\n const isDisabled = isComposerDisabled || disabled || !canSubmit;\n\n return (\n <Component\n type=\"submit\"\n {...props}\n ref={forwardedRef}\n disabled={isDisabled}\n />\n );\n});\n\n/* -------------------------------------------------------------------------------------------------\n * Abort\n * -----------------------------------------------------------------------------------------------*/\n\n/**\n * A button to abort a response related to the AI composer.\n *\n * @example\n * <AiComposer.Abort>Cancel</AiComposer.Abort>\n */\nexport const AiComposerAbort = forwardRef<\n HTMLButtonElement,\n AiComposerSubmitProps\n>(({ disabled, onClick, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"button\";\n const { isDisabled: isComposerDisabled, canAbort, abort } = useAiComposer();\n const isDisabled = isComposerDisabled || disabled || !canAbort;\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n abort();\n },\n [abort, onClick]\n );\n\n return (\n <Component\n type=\"button\"\n {...props}\n ref={forwardedRef}\n disabled={isDisabled}\n onClick={handleClick}\n />\n );\n});\n\nif (process.env.NODE_ENV !== \"production\") {\n AiComposerEditor.displayName = AI_COMPOSER_EDITOR_NAME;\n AiComposerForm.displayName = AI_COMPOSER_FORM_NAME;\n AiComposerSubmit.displayName = AI_COMPOSER_SUBMIT_NAME;\n AiComposerAbort.displayName = AI_COMPOSER_ABORT_NAME;\n}\n\n// NOTE: Every export from this file will be available publicly as AiComposer.*\nexport {\n AiComposerAbort as Abort,\n AiComposerEditor as Editor,\n AiComposerForm as Form,\n AiComposerSubmit as Submit,\n};\n"],"names":["Signal","forwardRef","Slot","useClient","useRef","useInitial","withNormalize","withHistory","withReact","createEditor","useState","kInternal","useSignal","useCallback","SlateTransforms","SlateEditor","ReactEditor","getDOMRange","isEditorEmpty","isEmpty","useLayoutEffect","requestSubmit","useImperativeHandle","jsx","AiComposerEditorContext","AiComposerContext","useAiComposerEditorContext","useAiComposer","useMemo","Slate","Editable"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../../../src/primitives/AiComposer/index.tsx"],"sourcesContent":["import {\n type AiChatMessage,\n kInternal,\n Signal,\n type WithNavigation,\n} from \"@liveblocks/core\";\nimport { useClient } from \"@liveblocks/react\";\nimport { useLayoutEffect, useSignal } from \"@liveblocks/react/_private\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport type { FocusEvent, FormEvent, KeyboardEvent, MouseEvent } from \"react\";\nimport {\n forwardRef,\n useCallback,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport {\n createEditor,\n Editor as SlateEditor,\n Transforms as SlateTransforms,\n} from \"slate\";\nimport { withHistory } from \"slate-history\";\nimport {\n Editable,\n ReactEditor,\n type RenderPlaceholderProps,\n Slate,\n withReact,\n} from \"slate-react\";\n\nimport type { AiComposerBody } from \"../../types\";\nimport { requestSubmit } from \"../../utils/request-submit\";\nimport { useInitial } from \"../../utils/use-initial\";\nimport { withNormalize } from \"../slate/plugins/normalize\";\nimport { getDOMRange } from \"../slate/utils/get-dom-range\";\nimport { isEmpty } from \"../slate/utils/is-empty\";\nimport {\n AiComposerContext,\n AiComposerEditorContext,\n useAiComposer,\n useAiComposerEditorContext,\n} from \"./contexts\";\nimport type {\n AiComposerEditorProps,\n AiComposerFormProps,\n AiComposerSubmitProps,\n} from \"./types\";\n\nconst AI_COMPOSER_SUBMIT_NAME = \"AiComposerSubmit\";\nconst AI_COMPOSER_ABORT_NAME = \"AiComposerAbort\";\nconst AI_COMPOSER_EDITOR_NAME = \"AiComposerEditor\";\nconst AI_COMPOSER_FORM_NAME = \"AiComposerForm\";\n\ntype UiChatMessage = WithNavigation<AiChatMessage>;\n\n/* -------------------------------------------------------------------------------------------------\n * Form\n * -----------------------------------------------------------------------------------------------*/\n\nconst emptyMessagesΣ = new Signal<UiChatMessage[]>([]);\n\nfunction getLastMessageId(messages: UiChatMessage[]) {\n const lastMessage = messages[messages.length - 1];\n\n if (lastMessage === undefined) {\n return null;\n }\n\n return lastMessage.id;\n}\n\nfunction getAbortableMessageId(messages: UiChatMessage[]) {\n return messages.find(\n (message) =>\n message.role === \"assistant\" &&\n (message.status === \"generating\" || message.status === \"awaiting-tool\")\n )?.id;\n}\n\n/**\n * Surrounds the AI composer's content and handles submissions.\n *\n * @example\n * <AiComposer.Form onComposerSubmit={({ text }) => {}}>\n *\t <AiComposer.Editor />\n * <AiComposer.Submit />\n * </AiComposer.Form>\n */\nexport const AiComposerForm = forwardRef<HTMLFormElement, AiComposerFormProps>(\n (\n {\n onComposerSubmit,\n onSubmit,\n disabled,\n chatId,\n branchId,\n asChild,\n ...props\n },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"form\";\n const client = useClient();\n const formRef = useRef<HTMLFormElement | null>(null);\n const editor = useInitial(() =>\n withNormalize(withHistory(withReact(createEditor())))\n );\n const [isEditorEmpty, setEditorEmpty] = useState(true);\n const [isSubmitting, setSubmitting] = useState(false);\n const [isFocused, setFocused] = useState(false);\n const messagesΣ = chatId\n ? client[kInternal].ai.signals.getChatMessagesForBranchΣ(chatId, branchId)\n : emptyMessagesΣ;\n const lastMessageId = useSignal(messagesΣ, getLastMessageId);\n const abortableMessageId = useSignal(messagesΣ, getAbortableMessageId);\n const isAvailable = useSignal(\n // Subscribe to connection status signal\n client[kInternal].ai.signals.statusΣ,\n // \"Disconnected\" means the AI service is not available\n // as it represents a final error status.\n (status) => status !== \"disconnected\"\n );\n\n const isDisabled = isSubmitting || disabled === true;\n\n const canAbort = isAvailable && abortableMessageId !== undefined;\n const canSubmit = isAvailable && !isEditorEmpty && !canAbort;\n\n const clear = useCallback(() => {\n SlateTransforms.delete(editor, {\n at: {\n anchor: SlateEditor.start(editor, []),\n focus: SlateEditor.end(editor, []),\n },\n });\n }, [editor]);\n\n const select = useCallback(() => {\n SlateTransforms.select(editor, SlateEditor.end(editor, []));\n }, [editor]);\n\n const focus = useCallback(\n (resetSelection = true) => {\n try {\n // Slate's `ReactEditor.focus` method can use `setTimeout` internally\n // which prevents us from catching errors, so this is a reimplementation.\n // https://github.com/ianstormtaylor/slate/blob/main/packages/slate-dom/src/plugin/dom-editor.ts\n if (!ReactEditor.isFocused(editor)) {\n SlateTransforms.select(\n editor,\n resetSelection || !editor.selection\n ? SlateEditor.end(editor, [])\n : editor.selection\n );\n\n const element = ReactEditor.toDOMNode(editor, editor);\n\n if (editor.selection) {\n const domSelection = window.getSelection();\n const domRange = getDOMRange(editor, editor.selection);\n\n if (domRange) {\n domSelection?.removeAllRanges();\n domSelection?.addRange(domRange);\n }\n }\n\n element.focus({ preventScroll: true });\n }\n } catch {\n // Slate's DOM-specific methods will throw if the editor's DOM\n // node no longer exists. This action doesn't make sense on an\n // unmounted editor so we can safely ignore it.\n }\n },\n [editor]\n );\n\n const blur = useCallback(() => {\n try {\n ReactEditor.blur(editor);\n } catch {\n // Slate's DOM-specific methods will throw if the editor's DOM\n // node no longer exists. This action doesn't make sense on an\n // unmounted editor so we can safely ignore it.\n }\n }, [editor]);\n\n const onSubmitEnd = useCallback(() => {\n clear();\n setSubmitting(false);\n }, [clear]);\n\n const handleSubmit = useCallback(\n (event: FormEvent<HTMLFormElement>) => {\n if (disabled) {\n return;\n }\n\n // In some situations (e.g. pressing Enter while composing diacritics), it's possible\n // for the form to be submitted as empty even though we already checked whether the\n // editor was empty when handling the key press.\n const isEditorEmpty = isEmpty(editor, editor.children);\n\n // We even prevent the user's `onSubmit` handler from being called if the editor is empty.\n if (isEditorEmpty) {\n event.preventDefault();\n\n return;\n }\n\n onSubmit?.(event);\n\n if (onComposerSubmit === undefined || event.isDefaultPrevented()) {\n event.preventDefault();\n return;\n }\n\n // Extract the text content from the editor.\n const content = editor.children\n .map((block) => {\n if (\"type\" in block && block.type === \"paragraph\") {\n return block.children\n .map((child) => {\n if (\"text\" in child) {\n return child.text;\n }\n return \"\";\n })\n .join(\"\");\n }\n return \"\";\n })\n .join(\"\\n\");\n\n const promise = onComposerSubmit(\n { text: content, lastMessageId },\n event\n );\n\n event.preventDefault();\n\n if (promise) {\n setSubmitting(true);\n promise.then(onSubmitEnd);\n } else {\n onSubmitEnd();\n }\n },\n [disabled, editor, onSubmit, onComposerSubmit, onSubmitEnd, lastMessageId]\n );\n\n useLayoutEffect(() => {\n setEditorEmpty(isEmpty(editor, editor.children));\n }, [editor]);\n\n const handleEditorValueChange = useCallback(() => {\n setEditorEmpty(isEmpty(editor, editor.children));\n }, [editor]);\n\n const submit = useCallback(() => {\n if (!canSubmit) {\n return;\n }\n\n // We need to wait for the next frame in some cases like when composing diacritics,\n // we want any native handling to be done first while still being handled on `keydown`.\n requestAnimationFrame(() => {\n if (formRef.current) {\n requestSubmit(formRef.current);\n }\n });\n }, [canSubmit]);\n\n const abort = useCallback(() => {\n if (!canAbort || !abortableMessageId) {\n return;\n }\n\n client[kInternal].ai.abort(abortableMessageId);\n }, [canAbort, abortableMessageId, client]);\n\n useImperativeHandle<HTMLFormElement | null, HTMLFormElement | null>(\n forwardedRef,\n () => formRef.current,\n []\n );\n\n return (\n <AiComposerEditorContext.Provider\n value={{\n editor,\n onEditorValueChange: handleEditorValueChange,\n abortableMessageId,\n setFocused,\n }}\n >\n <AiComposerContext.Provider\n value={{\n isDisabled,\n isEmpty: isEditorEmpty,\n isFocused,\n canSubmit,\n canAbort,\n submit,\n abort,\n clear,\n focus,\n blur,\n select,\n }}\n >\n <Component onSubmit={handleSubmit} {...props} ref={formRef} />\n </AiComposerContext.Provider>\n </AiComposerEditorContext.Provider>\n );\n }\n);\n\n/* -------------------------------------------------------------------------------------------------\n * Editor\n * -----------------------------------------------------------------------------------------------*/\n\nfunction AiComposerEditorPlaceholder({\n attributes,\n children,\n}: RenderPlaceholderProps) {\n const { opacity: _opacity, ...style } = attributes.style;\n\n return (\n <span {...attributes} style={style} data-placeholder=\"\">\n {children}\n </span>\n );\n}\n\n/**\n * Displays the AI composer's editor.\n *\n * @example\n * <AiComposer.Editor placeholder=\"Write a message…\" />\n */\nconst AiComposerEditor = forwardRef<HTMLDivElement, AiComposerEditorProps>(\n (\n {\n defaultValue = \"\",\n onKeyDown,\n onFocus,\n onBlur,\n disabled,\n autoFocus,\n dir,\n ...props\n },\n forwardedRef\n ) => {\n const { editor, onEditorValueChange, setFocused } =\n useAiComposerEditorContext();\n const {\n submit,\n isDisabled: isComposerDisabled,\n isFocused,\n focus,\n blur,\n select,\n } = useAiComposer();\n const isDisabled = disabled || isComposerDisabled;\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLDivElement>) => {\n onKeyDown?.(event);\n if (event.isDefaultPrevented()) return;\n\n if (event.key === \"Enter\" && !event.shiftKey) {\n event.preventDefault();\n submit();\n } else if (event.key === \"Enter\" && event.shiftKey) {\n event.preventDefault();\n editor.insertBreak();\n } else if (event.key === \"Escape\") {\n blur();\n }\n },\n [editor, onKeyDown, submit, blur]\n );\n\n const handleFocus = useCallback(\n (event: FocusEvent<HTMLDivElement>) => {\n onFocus?.(event);\n\n if (!event.isDefaultPrevented()) {\n setFocused(true);\n }\n },\n [onFocus, setFocused]\n );\n\n const handleBlur = useCallback(\n (event: FocusEvent<HTMLDivElement>) => {\n onBlur?.(event);\n\n if (!event.isDefaultPrevented()) {\n setFocused(false);\n }\n },\n [onBlur, setFocused]\n );\n\n useImperativeHandle(\n forwardedRef,\n () => ReactEditor.toDOMNode(editor, editor) as HTMLDivElement,\n [editor]\n );\n\n // Manually focus the editor when `autoFocus` is true\n useLayoutEffect(() => {\n if (!autoFocus) {\n return;\n }\n\n // `focus` needs to be synchronous to ensure its errors can be caught\n // but the triggering of `focus` on mount itself can be asynchronous.\n // This brings back the same timing behavior as Slate's `ReactEditor.focus`\n // (which uses `setTimeout` internally) while still allowing us to catch errors.\n const timeout = setTimeout(() => focus(), 0);\n\n return () => clearTimeout(timeout);\n }, [autoFocus, editor, focus]);\n\n // Manually add a selection in the editor if the selection\n // is still empty after being focused\n useLayoutEffect(() => {\n if (isFocused && editor.selection === null) {\n select();\n }\n }, [editor, select, isFocused]);\n\n const initialValue: AiComposerBody = useMemo(() => {\n return defaultValue\n .split(\"\\n\")\n .map((text) => ({ type: \"paragraph\", children: [{ text }] }));\n }, [defaultValue]);\n\n return (\n <Slate\n editor={editor}\n initialValue={initialValue}\n onValueChange={onEditorValueChange}\n >\n <Editable\n dir={dir}\n enterKeyHint=\"send\"\n autoCapitalize=\"sentences\"\n aria-label=\"Composer editor\"\n onKeyDown={handleKeyDown}\n onFocus={handleFocus}\n onBlur={handleBlur}\n data-focused={isFocused || undefined}\n data-disabled={isDisabled || undefined}\n {...props}\n readOnly={isDisabled}\n disabled={isDisabled}\n renderPlaceholder={AiComposerEditorPlaceholder}\n />\n </Slate>\n );\n }\n);\n\n/* -------------------------------------------------------------------------------------------------\n * Submit\n * -----------------------------------------------------------------------------------------------*/\n\n/**\n * A button to submit the AI composer's content.\n *\n * @example\n * <AiComposer.Submit>Send</AiComposer.Submit>\n */\nexport const AiComposerSubmit = forwardRef<\n HTMLButtonElement,\n AiComposerSubmitProps\n>(({ disabled, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"button\";\n const { isDisabled: isComposerDisabled, canSubmit } = useAiComposer();\n const isDisabled = isComposerDisabled || disabled || !canSubmit;\n\n return (\n <Component\n type=\"submit\"\n {...props}\n ref={forwardedRef}\n disabled={isDisabled}\n />\n );\n});\n\n/* -------------------------------------------------------------------------------------------------\n * Abort\n * -----------------------------------------------------------------------------------------------*/\n\n/**\n * A button to abort a response related to the AI composer.\n *\n * @example\n * <AiComposer.Abort>Cancel</AiComposer.Abort>\n */\nexport const AiComposerAbort = forwardRef<\n HTMLButtonElement,\n AiComposerSubmitProps\n>(({ disabled, onClick, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"button\";\n const { isDisabled: isComposerDisabled, canAbort, abort } = useAiComposer();\n const isDisabled = isComposerDisabled || disabled || !canAbort;\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n abort();\n },\n [abort, onClick]\n );\n\n return (\n <Component\n type=\"button\"\n {...props}\n ref={forwardedRef}\n disabled={isDisabled}\n onClick={handleClick}\n />\n );\n});\n\nif (process.env.NODE_ENV !== \"production\") {\n AiComposerEditor.displayName = AI_COMPOSER_EDITOR_NAME;\n AiComposerForm.displayName = AI_COMPOSER_FORM_NAME;\n AiComposerSubmit.displayName = AI_COMPOSER_SUBMIT_NAME;\n AiComposerAbort.displayName = AI_COMPOSER_ABORT_NAME;\n}\n\n// NOTE: Every export from this file will be available publicly as AiComposer.*\nexport {\n AiComposerAbort as Abort,\n AiComposerEditor as Editor,\n AiComposerForm as Form,\n AiComposerSubmit as Submit,\n};\n"],"names":["Signal","forwardRef","Slot","useClient","useRef","useInitial","withNormalize","withHistory","withReact","createEditor","useState","kInternal","useSignal","useCallback","SlateTransforms","SlateEditor","ReactEditor","getDOMRange","isEditorEmpty","isEmpty","useLayoutEffect","requestSubmit","useImperativeHandle","jsx","AiComposerEditorContext","AiComposerContext","useAiComposerEditorContext","useAiComposer","useMemo","Slate","Editable"],"mappings":";;;;;;;;;;;;;;;;;;AAkDA,MAAM,uBAA0B,GAAA,kBAAA,CAAA;AAChC,MAAM,sBAAyB,GAAA,iBAAA,CAAA;AAC/B,MAAM,uBAA0B,GAAA,kBAAA,CAAA;AAChC,MAAM,qBAAwB,GAAA,gBAAA,CAAA;AAQ9B,MAAM,mBAAiB,GAAA,IAAIA,WAAwB,CAAA,EAAE,CAAA,CAAA;AAErD,SAAS,iBAAiB,QAA2B,EAAA;AACnD,EAAA,MAAM,WAAc,GAAA,QAAA,CAAS,QAAS,CAAA,MAAA,GAAS,CAAC,CAAA,CAAA;AAEhD,EAAA,IAAI,gBAAgB,KAAW,CAAA,EAAA;AAC7B,IAAO,OAAA,IAAA,CAAA;AAAA,GACT;AAEA,EAAA,OAAO,WAAY,CAAA,EAAA,CAAA;AACrB,CAAA;AAEA,SAAS,sBAAsB,QAA2B,EAAA;AACxD,EAAA,OAAO,QAAS,CAAA,IAAA;AAAA,IACd,CAAC,YACC,OAAQ,CAAA,IAAA,KAAS,gBAChB,OAAQ,CAAA,MAAA,KAAW,YAAgB,IAAA,OAAA,CAAQ,MAAW,KAAA,eAAA,CAAA;AAAA,GACxD,EAAA,EAAA,CAAA;AACL,CAAA;AAWO,MAAM,cAAiB,GAAAC,gBAAA;AAAA,EAC5B,CACE;AAAA,IACE,gBAAA;AAAA,IACA,QAAA;AAAA,IACA,QAAA;AAAA,IACA,MAAA;AAAA,IACA,QAAA;AAAA,IACA,OAAA;AAAA,IACA,GAAG,KAAA;AAAA,KAEL,YACG,KAAA;AACH,IAAM,MAAA,SAAA,GAAY,UAAUC,cAAO,GAAA,MAAA,CAAA;AACnC,IAAA,MAAM,SAASC,iBAAU,EAAA,CAAA;AACzB,IAAM,MAAA,OAAA,GAAUC,aAA+B,IAAI,CAAA,CAAA;AACnD,IAAA,MAAM,MAAS,GAAAC,qBAAA;AAAA,MAAW,MACxBC,uBAAc,CAAAC,wBAAA,CAAYC,qBAAUC,kBAAa,EAAC,CAAC,CAAC,CAAA;AAAA,KACtD,CAAA;AACA,IAAA,MAAM,CAAC,aAAA,EAAe,cAAc,CAAA,GAAIC,eAAS,IAAI,CAAA,CAAA;AACrD,IAAA,MAAM,CAAC,YAAA,EAAc,aAAa,CAAA,GAAIA,eAAS,KAAK,CAAA,CAAA;AACpD,IAAA,MAAM,CAAC,SAAA,EAAW,UAAU,CAAA,GAAIA,eAAS,KAAK,CAAA,CAAA;AAC9C,IAAM,MAAA,cAAA,GAAY,MACd,GAAA,MAAA,CAAOC,cAAS,CAAA,CAAE,GAAG,OAAQ,CAAA,8BAAA,CAA0B,MAAQ,EAAA,QAAQ,CACvE,GAAA,mBAAA,CAAA;AACJ,IAAM,MAAA,aAAA,GAAgBC,kBAAU,CAAA,cAAA,EAAW,gBAAgB,CAAA,CAAA;AAC3D,IAAM,MAAA,kBAAA,GAAqBA,kBAAU,CAAA,cAAA,EAAW,qBAAqB,CAAA,CAAA;AACrE,IAAA,MAAM,WAAc,GAAAA,kBAAA;AAAA;AAAA,MAElB,MAAO,CAAAD,cAAS,CAAE,CAAA,EAAA,CAAG,OAAQ,CAAA,YAAA;AAAA;AAAA;AAAA,MAG7B,CAAC,WAAW,MAAW,KAAA,cAAA;AAAA,KACzB,CAAA;AAEA,IAAM,MAAA,UAAA,GAAa,gBAAgB,QAAa,KAAA,IAAA,CAAA;AAEhD,IAAM,MAAA,QAAA,GAAW,eAAe,kBAAuB,KAAA,KAAA,CAAA,CAAA;AACvD,IAAA,MAAM,SAAY,GAAA,WAAA,IAAe,CAAC,aAAA,IAAiB,CAAC,QAAA,CAAA;AAEpD,IAAM,MAAA,KAAA,GAAQE,kBAAY,MAAM;AAC9B,MAAAC,gBAAA,CAAgB,OAAO,MAAQ,EAAA;AAAA,QAC7B,EAAI,EAAA;AAAA,UACF,MAAQ,EAAAC,YAAA,CAAY,KAAM,CAAA,MAAA,EAAQ,EAAE,CAAA;AAAA,UACpC,KAAO,EAAAA,YAAA,CAAY,GAAI,CAAA,MAAA,EAAQ,EAAE,CAAA;AAAA,SACnC;AAAA,OACD,CAAA,CAAA;AAAA,KACH,EAAG,CAAC,MAAM,CAAC,CAAA,CAAA;AAEX,IAAM,MAAA,MAAA,GAASF,kBAAY,MAAM;AAC/B,MAAAC,gBAAA,CAAgB,OAAO,MAAQ,EAAAC,YAAA,CAAY,IAAI,MAAQ,EAAA,EAAE,CAAC,CAAA,CAAA;AAAA,KAC5D,EAAG,CAAC,MAAM,CAAC,CAAA,CAAA;AAEX,IAAA,MAAM,KAAQ,GAAAF,iBAAA;AAAA,MACZ,CAAC,iBAAiB,IAAS,KAAA;AACzB,QAAI,IAAA;AAIF,UAAA,IAAI,CAACG,sBAAA,CAAY,SAAU,CAAA,MAAM,CAAG,EAAA;AAClC,YAAgBF,gBAAA,CAAA,MAAA;AAAA,cACd,MAAA;AAAA,cACA,cAAA,IAAkB,CAAC,MAAA,CAAO,SACtB,GAAAC,YAAA,CAAY,IAAI,MAAQ,EAAA,EAAE,CAAA,GAC1B,MAAO,CAAA,SAAA;AAAA,aACb,CAAA;AAEA,YAAA,MAAM,OAAU,GAAAC,sBAAA,CAAY,SAAU,CAAA,MAAA,EAAQ,MAAM,CAAA,CAAA;AAEpD,YAAA,IAAI,OAAO,SAAW,EAAA;AACpB,cAAM,MAAA,YAAA,GAAe,OAAO,YAAa,EAAA,CAAA;AACzC,cAAA,MAAM,QAAW,GAAAC,uBAAA,CAAY,MAAQ,EAAA,MAAA,CAAO,SAAS,CAAA,CAAA;AAErD,cAAA,IAAI,QAAU,EAAA;AACZ,gBAAA,YAAA,EAAc,eAAgB,EAAA,CAAA;AAC9B,gBAAA,YAAA,EAAc,SAAS,QAAQ,CAAA,CAAA;AAAA,eACjC;AAAA,aACF;AAEA,YAAA,OAAA,CAAQ,KAAM,CAAA,EAAE,aAAe,EAAA,IAAA,EAAM,CAAA,CAAA;AAAA,WACvC;AAAA,SACM,CAAA,MAAA;AAAA,SAIR;AAAA,OACF;AAAA,MACA,CAAC,MAAM,CAAA;AAAA,KACT,CAAA;AAEA,IAAM,MAAA,IAAA,GAAOJ,kBAAY,MAAM;AAC7B,MAAI,IAAA;AACF,QAAAG,sBAAA,CAAY,KAAK,MAAM,CAAA,CAAA;AAAA,OACjB,CAAA,MAAA;AAAA,OAIR;AAAA,KACF,EAAG,CAAC,MAAM,CAAC,CAAA,CAAA;AAEX,IAAM,MAAA,WAAA,GAAcH,kBAAY,MAAM;AACpC,MAAM,KAAA,EAAA,CAAA;AACN,MAAA,aAAA,CAAc,KAAK,CAAA,CAAA;AAAA,KACrB,EAAG,CAAC,KAAK,CAAC,CAAA,CAAA;AAEV,IAAA,MAAM,YAAe,GAAAA,iBAAA;AAAA,MACnB,CAAC,KAAsC,KAAA;AACrC,QAAA,IAAI,QAAU,EAAA;AACZ,UAAA,OAAA;AAAA,SACF;AAKA,QAAA,MAAMK,cAAgB,GAAAC,eAAA,CAAQ,MAAQ,EAAA,MAAA,CAAO,QAAQ,CAAA,CAAA;AAGrD,QAAA,IAAID,cAAe,EAAA;AACjB,UAAA,KAAA,CAAM,cAAe,EAAA,CAAA;AAErB,UAAA,OAAA;AAAA,SACF;AAEA,QAAA,QAAA,GAAW,KAAK,CAAA,CAAA;AAEhB,QAAA,IAAI,gBAAqB,KAAA,KAAA,CAAA,IAAa,KAAM,CAAA,kBAAA,EAAsB,EAAA;AAChE,UAAA,KAAA,CAAM,cAAe,EAAA,CAAA;AACrB,UAAA,OAAA;AAAA,SACF;AAGA,QAAA,MAAM,OAAU,GAAA,MAAA,CAAO,QACpB,CAAA,GAAA,CAAI,CAAC,KAAU,KAAA;AACd,UAAA,IAAI,MAAU,IAAA,KAAA,IAAS,KAAM,CAAA,IAAA,KAAS,WAAa,EAAA;AACjD,YAAA,OAAO,KAAM,CAAA,QAAA,CACV,GAAI,CAAA,CAAC,KAAU,KAAA;AACd,cAAA,IAAI,UAAU,KAAO,EAAA;AACnB,gBAAA,OAAO,KAAM,CAAA,IAAA,CAAA;AAAA,eACf;AACA,cAAO,OAAA,EAAA,CAAA;AAAA,aACR,CACA,CAAA,IAAA,CAAK,EAAE,CAAA,CAAA;AAAA,WACZ;AACA,UAAO,OAAA,EAAA,CAAA;AAAA,SACR,CACA,CAAA,IAAA,CAAK,IAAI,CAAA,CAAA;AAEZ,QAAA,MAAM,OAAU,GAAA,gBAAA;AAAA,UACd,EAAE,IAAM,EAAA,OAAA,EAAS,aAAc,EAAA;AAAA,UAC/B,KAAA;AAAA,SACF,CAAA;AAEA,QAAA,KAAA,CAAM,cAAe,EAAA,CAAA;AAErB,QAAA,IAAI,OAAS,EAAA;AACX,UAAA,aAAA,CAAc,IAAI,CAAA,CAAA;AAClB,UAAA,OAAA,CAAQ,KAAK,WAAW,CAAA,CAAA;AAAA,SACnB,MAAA;AACL,UAAY,WAAA,EAAA,CAAA;AAAA,SACd;AAAA,OACF;AAAA,MACA,CAAC,QAAU,EAAA,MAAA,EAAQ,QAAU,EAAA,gBAAA,EAAkB,aAAa,aAAa,CAAA;AAAA,KAC3E,CAAA;AAEA,IAAAE,wBAAA,CAAgB,MAAM;AACpB,MAAA,cAAA,CAAeD,eAAQ,CAAA,MAAA,EAAQ,MAAO,CAAA,QAAQ,CAAC,CAAA,CAAA;AAAA,KACjD,EAAG,CAAC,MAAM,CAAC,CAAA,CAAA;AAEX,IAAM,MAAA,uBAAA,GAA0BN,kBAAY,MAAM;AAChD,MAAA,cAAA,CAAeM,eAAQ,CAAA,MAAA,EAAQ,MAAO,CAAA,QAAQ,CAAC,CAAA,CAAA;AAAA,KACjD,EAAG,CAAC,MAAM,CAAC,CAAA,CAAA;AAEX,IAAM,MAAA,MAAA,GAASN,kBAAY,MAAM;AAC/B,MAAA,IAAI,CAAC,SAAW,EAAA;AACd,QAAA,OAAA;AAAA,OACF;AAIA,MAAA,qBAAA,CAAsB,MAAM;AAC1B,QAAA,IAAI,QAAQ,OAAS,EAAA;AACnB,UAAAQ,2BAAA,CAAc,QAAQ,OAAO,CAAA,CAAA;AAAA,SAC/B;AAAA,OACD,CAAA,CAAA;AAAA,KACH,EAAG,CAAC,SAAS,CAAC,CAAA,CAAA;AAEd,IAAM,MAAA,KAAA,GAAQR,kBAAY,MAAM;AAC9B,MAAI,IAAA,CAAC,QAAY,IAAA,CAAC,kBAAoB,EAAA;AACpC,QAAA,OAAA;AAAA,OACF;AAEA,MAAA,MAAA,CAAOF,cAAS,CAAA,CAAE,EAAG,CAAA,KAAA,CAAM,kBAAkB,CAAA,CAAA;AAAA,KAC5C,EAAA,CAAC,QAAU,EAAA,kBAAA,EAAoB,MAAM,CAAC,CAAA,CAAA;AAEzC,IAAAW,yBAAA;AAAA,MACE,YAAA;AAAA,MACA,MAAM,OAAQ,CAAA,OAAA;AAAA,MACd,EAAC;AAAA,KACH,CAAA;AAEA,IACE,uBAAAC,cAAA;AAAA,MAACC,gCAAwB,CAAA,QAAA;AAAA,MAAxB;AAAA,QACC,KAAO,EAAA;AAAA,UACL,MAAA;AAAA,UACA,mBAAqB,EAAA,uBAAA;AAAA,UACrB,kBAAA;AAAA,UACA,UAAA;AAAA,SACF;AAAA,QAEA,QAAA,kBAAAD,cAAA;AAAA,UAACE,0BAAkB,CAAA,QAAA;AAAA,UAAlB;AAAA,YACC,KAAO,EAAA;AAAA,cACL,UAAA;AAAA,cACA,OAAS,EAAA,aAAA;AAAA,cACT,SAAA;AAAA,cACA,SAAA;AAAA,cACA,QAAA;AAAA,cACA,MAAA;AAAA,cACA,KAAA;AAAA,cACA,KAAA;AAAA,cACA,KAAA;AAAA,cACA,IAAA;AAAA,cACA,MAAA;AAAA,aACF;AAAA,YAEA,yCAAC,SAAU,EAAA,EAAA,QAAA,EAAU,cAAe,GAAG,KAAA,EAAO,KAAK,OAAS,EAAA,CAAA;AAAA,WAAA;AAAA,SAC9D;AAAA,OAAA;AAAA,KACF,CAAA;AAAA,GAEJ;AACF,EAAA;AAMA,SAAS,2BAA4B,CAAA;AAAA,EACnC,UAAA;AAAA,EACA,QAAA;AACF,CAA2B,EAAA;AACzB,EAAA,MAAM,EAAE,OAAS,EAAA,QAAA,EAAU,GAAG,KAAA,KAAU,UAAW,CAAA,KAAA,CAAA;AAEnD,EAAA,sCACG,MAAM,EAAA,EAAA,GAAG,YAAY,KAAc,EAAA,kBAAA,EAAiB,IAClD,QACH,EAAA,CAAA,CAAA;AAEJ,CAAA;AAQA,MAAM,gBAAmB,GAAAxB,gBAAA;AAAA,EACvB,CACE;AAAA,IACE,YAAe,GAAA,EAAA;AAAA,IACf,SAAA;AAAA,IACA,OAAA;AAAA,IACA,MAAA;AAAA,IACA,QAAA;AAAA,IACA,SAAA;AAAA,IACA,GAAA;AAAA,IACA,GAAG,KAAA;AAAA,KAEL,YACG,KAAA;AACH,IAAA,MAAM,EAAE,MAAA,EAAQ,mBAAqB,EAAA,UAAA,KACnCyB,mCAA2B,EAAA,CAAA;AAC7B,IAAM,MAAA;AAAA,MACJ,MAAA;AAAA,MACA,UAAY,EAAA,kBAAA;AAAA,MACZ,SAAA;AAAA,MACA,KAAA;AAAA,MACA,IAAA;AAAA,MACA,MAAA;AAAA,QACEC,sBAAc,EAAA,CAAA;AAClB,IAAA,MAAM,aAAa,QAAY,IAAA,kBAAA,CAAA;AAE/B,IAAA,MAAM,aAAgB,GAAAd,iBAAA;AAAA,MACpB,CAAC,KAAyC,KAAA;AACxC,QAAA,SAAA,GAAY,KAAK,CAAA,CAAA;AACjB,QAAA,IAAI,MAAM,kBAAmB,EAAA;AAAG,UAAA,OAAA;AAEhC,QAAA,IAAI,KAAM,CAAA,GAAA,KAAQ,OAAW,IAAA,CAAC,MAAM,QAAU,EAAA;AAC5C,UAAA,KAAA,CAAM,cAAe,EAAA,CAAA;AACrB,UAAO,MAAA,EAAA,CAAA;AAAA,SACE,MAAA,IAAA,KAAA,CAAM,GAAQ,KAAA,OAAA,IAAW,MAAM,QAAU,EAAA;AAClD,UAAA,KAAA,CAAM,cAAe,EAAA,CAAA;AACrB,UAAA,MAAA,CAAO,WAAY,EAAA,CAAA;AAAA,SACrB,MAAA,IAAW,KAAM,CAAA,GAAA,KAAQ,QAAU,EAAA;AACjC,UAAK,IAAA,EAAA,CAAA;AAAA,SACP;AAAA,OACF;AAAA,MACA,CAAC,MAAA,EAAQ,SAAW,EAAA,MAAA,EAAQ,IAAI,CAAA;AAAA,KAClC,CAAA;AAEA,IAAA,MAAM,WAAc,GAAAA,iBAAA;AAAA,MAClB,CAAC,KAAsC,KAAA;AACrC,QAAA,OAAA,GAAU,KAAK,CAAA,CAAA;AAEf,QAAI,IAAA,CAAC,KAAM,CAAA,kBAAA,EAAsB,EAAA;AAC/B,UAAA,UAAA,CAAW,IAAI,CAAA,CAAA;AAAA,SACjB;AAAA,OACF;AAAA,MACA,CAAC,SAAS,UAAU,CAAA;AAAA,KACtB,CAAA;AAEA,IAAA,MAAM,UAAa,GAAAA,iBAAA;AAAA,MACjB,CAAC,KAAsC,KAAA;AACrC,QAAA,MAAA,GAAS,KAAK,CAAA,CAAA;AAEd,QAAI,IAAA,CAAC,KAAM,CAAA,kBAAA,EAAsB,EAAA;AAC/B,UAAA,UAAA,CAAW,KAAK,CAAA,CAAA;AAAA,SAClB;AAAA,OACF;AAAA,MACA,CAAC,QAAQ,UAAU,CAAA;AAAA,KACrB,CAAA;AAEA,IAAAS,yBAAA;AAAA,MACE,YAAA;AAAA,MACA,MAAMN,sBAAA,CAAY,SAAU,CAAA,MAAA,EAAQ,MAAM,CAAA;AAAA,MAC1C,CAAC,MAAM,CAAA;AAAA,KACT,CAAA;AAGA,IAAAI,wBAAA,CAAgB,MAAM;AACpB,MAAA,IAAI,CAAC,SAAW,EAAA;AACd,QAAA,OAAA;AAAA,OACF;AAMA,MAAA,MAAM,OAAU,GAAA,UAAA,CAAW,MAAM,KAAA,IAAS,CAAC,CAAA,CAAA;AAE3C,MAAO,OAAA,MAAM,aAAa,OAAO,CAAA,CAAA;AAAA,KAChC,EAAA,CAAC,SAAW,EAAA,MAAA,EAAQ,KAAK,CAAC,CAAA,CAAA;AAI7B,IAAAA,wBAAA,CAAgB,MAAM;AACpB,MAAI,IAAA,SAAA,IAAa,MAAO,CAAA,SAAA,KAAc,IAAM,EAAA;AAC1C,QAAO,MAAA,EAAA,CAAA;AAAA,OACT;AAAA,KACC,EAAA,CAAC,MAAQ,EAAA,MAAA,EAAQ,SAAS,CAAC,CAAA,CAAA;AAE9B,IAAM,MAAA,YAAA,GAA+BQ,cAAQ,MAAM;AACjD,MAAA,OAAO,aACJ,KAAM,CAAA,IAAI,CACV,CAAA,GAAA,CAAI,CAAC,IAAU,MAAA,EAAE,IAAM,EAAA,WAAA,EAAa,UAAU,CAAC,EAAE,IAAK,EAAC,GAAI,CAAA,CAAA,CAAA;AAAA,KAChE,EAAG,CAAC,YAAY,CAAC,CAAA,CAAA;AAEjB,IACE,uBAAAL,cAAA;AAAA,MAACM,gBAAA;AAAA,MAAA;AAAA,QACC,MAAA;AAAA,QACA,YAAA;AAAA,QACA,aAAe,EAAA,mBAAA;AAAA,QAEf,QAAA,kBAAAN,cAAA;AAAA,UAACO,mBAAA;AAAA,UAAA;AAAA,YACC,GAAA;AAAA,YACA,YAAa,EAAA,MAAA;AAAA,YACb,cAAe,EAAA,WAAA;AAAA,YACf,YAAW,EAAA,iBAAA;AAAA,YACX,SAAW,EAAA,aAAA;AAAA,YACX,OAAS,EAAA,WAAA;AAAA,YACT,MAAQ,EAAA,UAAA;AAAA,YACR,gBAAc,SAAa,IAAA,KAAA,CAAA;AAAA,YAC3B,iBAAe,UAAc,IAAA,KAAA,CAAA;AAAA,YAC5B,GAAG,KAAA;AAAA,YACJ,QAAU,EAAA,UAAA;AAAA,YACV,QAAU,EAAA,UAAA;AAAA,YACV,iBAAmB,EAAA,2BAAA;AAAA,WAAA;AAAA,SACrB;AAAA,OAAA;AAAA,KACF,CAAA;AAAA,GAEJ;AACF,EAAA;AAYa,MAAA,gBAAA,GAAmB7B,iBAG9B,CAAC,EAAE,UAAU,OAAS,EAAA,GAAG,KAAM,EAAA,EAAG,YAAiB,KAAA;AACnD,EAAM,MAAA,SAAA,GAAY,UAAUC,cAAO,GAAA,QAAA,CAAA;AACnC,EAAA,MAAM,EAAE,UAAA,EAAY,kBAAoB,EAAA,SAAA,KAAcyB,sBAAc,EAAA,CAAA;AACpE,EAAM,MAAA,UAAA,GAAa,kBAAsB,IAAA,QAAA,IAAY,CAAC,SAAA,CAAA;AAEtD,EACE,uBAAAJ,cAAA;AAAA,IAAC,SAAA;AAAA,IAAA;AAAA,MACC,IAAK,EAAA,QAAA;AAAA,MACJ,GAAG,KAAA;AAAA,MACJ,GAAK,EAAA,YAAA;AAAA,MACL,QAAU,EAAA,UAAA;AAAA,KAAA;AAAA,GACZ,CAAA;AAEJ,CAAC,EAAA;AAYY,MAAA,eAAA,GAAkBtB,gBAG7B,CAAA,CAAC,EAAE,QAAA,EAAU,SAAS,OAAS,EAAA,GAAG,KAAM,EAAA,EAAG,YAAiB,KAAA;AAC5D,EAAM,MAAA,SAAA,GAAY,UAAUC,cAAO,GAAA,QAAA,CAAA;AACnC,EAAA,MAAM,EAAE,UAAY,EAAA,kBAAA,EAAoB,QAAU,EAAA,KAAA,KAAUyB,sBAAc,EAAA,CAAA;AAC1E,EAAM,MAAA,UAAA,GAAa,kBAAsB,IAAA,QAAA,IAAY,CAAC,QAAA,CAAA;AAEtD,EAAA,MAAM,WAAc,GAAAd,iBAAA;AAAA,IAClB,CAAC,KAAyC,KAAA;AACxC,MAAA,OAAA,GAAU,KAAK,CAAA,CAAA;AAEf,MAAI,IAAA,KAAA,CAAM,oBAAsB,EAAA;AAC9B,QAAA,OAAA;AAAA,OACF;AAEA,MAAM,KAAA,EAAA,CAAA;AAAA,KACR;AAAA,IACA,CAAC,OAAO,OAAO,CAAA;AAAA,GACjB,CAAA;AAEA,EACE,uBAAAU,cAAA;AAAA,IAAC,SAAA;AAAA,IAAA;AAAA,MACC,IAAK,EAAA,QAAA;AAAA,MACJ,GAAG,KAAA;AAAA,MACJ,GAAK,EAAA,YAAA;AAAA,MACL,QAAU,EAAA,UAAA;AAAA,MACV,OAAS,EAAA,WAAA;AAAA,KAAA;AAAA,GACX,CAAA;AAEJ,CAAC,EAAA;AAED,IAAI,OAAA,CAAQ,GAAI,CAAA,QAAA,KAAa,YAAc,EAAA;AACzC,EAAA,gBAAA,CAAiB,WAAc,GAAA,uBAAA,CAAA;AAC/B,EAAA,cAAA,CAAe,WAAc,GAAA,qBAAA,CAAA;AAC7B,EAAA,gBAAA,CAAiB,WAAc,GAAA,uBAAA,CAAA;AAC/B,EAAA,eAAA,CAAgB,WAAc,GAAA,sBAAA,CAAA;AAChC;;;;;;;;;;"}
@@ -276,9 +276,11 @@ const AiComposerEditor = forwardRef(
276
276
  [editor]
277
277
  );
278
278
  useLayoutEffect(() => {
279
- if (autoFocus) {
280
- focus();
279
+ if (!autoFocus) {
280
+ return;
281
281
  }
282
+ const timeout = setTimeout(() => focus(), 0);
283
+ return () => clearTimeout(timeout);
282
284
  }, [autoFocus, editor, focus]);
283
285
  useLayoutEffect(() => {
284
286
  if (isFocused && editor.selection === null) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/primitives/AiComposer/index.tsx"],"sourcesContent":["import {\n type AiChatMessage,\n kInternal,\n Signal,\n type WithNavigation,\n} from \"@liveblocks/core\";\nimport { useClient } from \"@liveblocks/react\";\nimport { useLayoutEffect, useSignal } from \"@liveblocks/react/_private\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport type { FocusEvent, FormEvent, KeyboardEvent, MouseEvent } from \"react\";\nimport {\n forwardRef,\n useCallback,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport {\n createEditor,\n Editor as SlateEditor,\n Transforms as SlateTransforms,\n} from \"slate\";\nimport { withHistory } from \"slate-history\";\nimport {\n Editable,\n ReactEditor,\n type RenderPlaceholderProps,\n Slate,\n withReact,\n} from \"slate-react\";\n\nimport type { AiComposerBody } from \"../../types\";\nimport { requestSubmit } from \"../../utils/request-submit\";\nimport { useInitial } from \"../../utils/use-initial\";\nimport { withNormalize } from \"../slate/plugins/normalize\";\nimport { getDOMRange } from \"../slate/utils/get-dom-range\";\nimport { isEmpty } from \"../slate/utils/is-empty\";\nimport {\n AiComposerContext,\n AiComposerEditorContext,\n useAiComposer,\n useAiComposerEditorContext,\n} from \"./contexts\";\nimport type {\n AiComposerEditorProps,\n AiComposerFormProps,\n AiComposerSubmitProps,\n} from \"./types\";\n\nconst AI_COMPOSER_SUBMIT_NAME = \"AiComposerSubmit\";\nconst AI_COMPOSER_ABORT_NAME = \"AiComposerAbort\";\nconst AI_COMPOSER_EDITOR_NAME = \"AiComposerEditor\";\nconst AI_COMPOSER_FORM_NAME = \"AiComposerForm\";\n\ntype UiChatMessage = WithNavigation<AiChatMessage>;\n\n/* -------------------------------------------------------------------------------------------------\n * Form\n * -----------------------------------------------------------------------------------------------*/\n\nconst emptyMessagesΣ = new Signal<UiChatMessage[]>([]);\n\nfunction getLastMessageId(messages: UiChatMessage[]) {\n const lastMessage = messages[messages.length - 1];\n\n if (lastMessage === undefined) {\n return null;\n }\n\n return lastMessage.id;\n}\n\nfunction getAbortableMessageId(messages: UiChatMessage[]) {\n return messages.find(\n (message) =>\n message.role === \"assistant\" &&\n (message.status === \"generating\" || message.status === \"awaiting-tool\")\n )?.id;\n}\n\n/**\n * Surrounds the AI composer's content and handles submissions.\n *\n * @example\n * <AiComposer.Form onComposerSubmit={({ text }) => {}}>\n *\t <AiComposer.Editor />\n * <AiComposer.Submit />\n * </AiComposer.Form>\n */\nexport const AiComposerForm = forwardRef<HTMLFormElement, AiComposerFormProps>(\n (\n {\n onComposerSubmit,\n onSubmit,\n disabled,\n chatId,\n branchId,\n asChild,\n ...props\n },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"form\";\n const client = useClient();\n const formRef = useRef<HTMLFormElement | null>(null);\n const editor = useInitial(() =>\n withNormalize(withHistory(withReact(createEditor())))\n );\n const [isEditorEmpty, setEditorEmpty] = useState(true);\n const [isSubmitting, setSubmitting] = useState(false);\n const [isFocused, setFocused] = useState(false);\n const messagesΣ = chatId\n ? client[kInternal].ai.signals.getChatMessagesForBranchΣ(chatId, branchId)\n : emptyMessagesΣ;\n const lastMessageId = useSignal(messagesΣ, getLastMessageId);\n const abortableMessageId = useSignal(messagesΣ, getAbortableMessageId);\n const isAvailable = useSignal(\n // Subscribe to connection status signal\n client[kInternal].ai.signals.statusΣ,\n // \"Disconnected\" means the AI service is not available\n // as it represents a final error status.\n (status) => status !== \"disconnected\"\n );\n\n const isDisabled = isSubmitting || disabled === true;\n\n const canAbort = isAvailable && abortableMessageId !== undefined;\n const canSubmit = isAvailable && !isEditorEmpty && !canAbort;\n\n const clear = useCallback(() => {\n SlateTransforms.delete(editor, {\n at: {\n anchor: SlateEditor.start(editor, []),\n focus: SlateEditor.end(editor, []),\n },\n });\n }, [editor]);\n\n const select = useCallback(() => {\n SlateTransforms.select(editor, SlateEditor.end(editor, []));\n }, [editor]);\n\n const focus = useCallback(\n (resetSelection = true) => {\n try {\n // Slate's `ReactEditor.focus` method can use `setTimeout` internally\n // which prevents us from catching errors, so this is a reimplementation.\n // https://github.com/ianstormtaylor/slate/blob/main/packages/slate-dom/src/plugin/dom-editor.ts\n if (!ReactEditor.isFocused(editor)) {\n SlateTransforms.select(\n editor,\n resetSelection || !editor.selection\n ? SlateEditor.end(editor, [])\n : editor.selection\n );\n\n const element = ReactEditor.toDOMNode(editor, editor);\n\n if (editor.selection) {\n const domSelection = window.getSelection();\n const domRange = getDOMRange(editor, editor.selection);\n\n if (domRange) {\n domSelection?.removeAllRanges();\n domSelection?.addRange(domRange);\n }\n }\n\n element.focus({ preventScroll: true });\n }\n } catch {\n // Slate's DOM-specific methods will throw if the editor's DOM\n // node no longer exists. This action doesn't make sense on an\n // unmounted editor so we can safely ignore it.\n }\n },\n [editor]\n );\n\n const blur = useCallback(() => {\n try {\n ReactEditor.blur(editor);\n } catch {\n // Slate's DOM-specific methods will throw if the editor's DOM\n // node no longer exists. This action doesn't make sense on an\n // unmounted editor so we can safely ignore it.\n }\n }, [editor]);\n\n const onSubmitEnd = useCallback(() => {\n clear();\n setSubmitting(false);\n }, [clear]);\n\n const handleSubmit = useCallback(\n (event: FormEvent<HTMLFormElement>) => {\n if (disabled) {\n return;\n }\n\n // In some situations (e.g. pressing Enter while composing diacritics), it's possible\n // for the form to be submitted as empty even though we already checked whether the\n // editor was empty when handling the key press.\n const isEditorEmpty = isEmpty(editor, editor.children);\n\n // We even prevent the user's `onSubmit` handler from being called if the editor is empty.\n if (isEditorEmpty) {\n event.preventDefault();\n\n return;\n }\n\n onSubmit?.(event);\n\n if (onComposerSubmit === undefined || event.isDefaultPrevented()) {\n event.preventDefault();\n return;\n }\n\n // Extract the text content from the editor.\n const content = editor.children\n .map((block) => {\n if (\"type\" in block && block.type === \"paragraph\") {\n return block.children\n .map((child) => {\n if (\"text\" in child) {\n return child.text;\n }\n return \"\";\n })\n .join(\"\");\n }\n return \"\";\n })\n .join(\"\\n\");\n\n const promise = onComposerSubmit(\n { text: content, lastMessageId },\n event\n );\n\n event.preventDefault();\n\n if (promise) {\n setSubmitting(true);\n promise.then(onSubmitEnd);\n } else {\n onSubmitEnd();\n }\n },\n [disabled, editor, onSubmit, onComposerSubmit, onSubmitEnd, lastMessageId]\n );\n\n useLayoutEffect(() => {\n setEditorEmpty(isEmpty(editor, editor.children));\n }, [editor]);\n\n const handleEditorValueChange = useCallback(() => {\n setEditorEmpty(isEmpty(editor, editor.children));\n }, [editor]);\n\n const submit = useCallback(() => {\n if (!canSubmit) {\n return;\n }\n\n // We need to wait for the next frame in some cases like when composing diacritics,\n // we want any native handling to be done first while still being handled on `keydown`.\n requestAnimationFrame(() => {\n if (formRef.current) {\n requestSubmit(formRef.current);\n }\n });\n }, [canSubmit]);\n\n const abort = useCallback(() => {\n if (!canAbort || !abortableMessageId) {\n return;\n }\n\n client[kInternal].ai.abort(abortableMessageId);\n }, [canAbort, abortableMessageId, client]);\n\n useImperativeHandle<HTMLFormElement | null, HTMLFormElement | null>(\n forwardedRef,\n () => formRef.current,\n []\n );\n\n return (\n <AiComposerEditorContext.Provider\n value={{\n editor,\n onEditorValueChange: handleEditorValueChange,\n abortableMessageId,\n setFocused,\n }}\n >\n <AiComposerContext.Provider\n value={{\n isDisabled,\n isEmpty: isEditorEmpty,\n isFocused,\n canSubmit,\n canAbort,\n submit,\n abort,\n clear,\n focus,\n blur,\n select,\n }}\n >\n <Component onSubmit={handleSubmit} {...props} ref={formRef} />\n </AiComposerContext.Provider>\n </AiComposerEditorContext.Provider>\n );\n }\n);\n\n/* -------------------------------------------------------------------------------------------------\n * Editor\n * -----------------------------------------------------------------------------------------------*/\n\nfunction AiComposerEditorPlaceholder({\n attributes,\n children,\n}: RenderPlaceholderProps) {\n const { opacity: _opacity, ...style } = attributes.style;\n\n return (\n <span {...attributes} style={style} data-placeholder=\"\">\n {children}\n </span>\n );\n}\n\n/**\n * Displays the AI composer's editor.\n *\n * @example\n * <AiComposer.Editor placeholder=\"Write a message…\" />\n */\nconst AiComposerEditor = forwardRef<HTMLDivElement, AiComposerEditorProps>(\n (\n {\n defaultValue = \"\",\n onKeyDown,\n onFocus,\n onBlur,\n disabled,\n autoFocus,\n dir,\n ...props\n },\n forwardedRef\n ) => {\n const { editor, onEditorValueChange, setFocused } =\n useAiComposerEditorContext();\n const {\n submit,\n isDisabled: isComposerDisabled,\n isFocused,\n focus,\n blur,\n select,\n } = useAiComposer();\n const isDisabled = disabled || isComposerDisabled;\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLDivElement>) => {\n onKeyDown?.(event);\n if (event.isDefaultPrevented()) return;\n\n if (event.key === \"Enter\" && !event.shiftKey) {\n event.preventDefault();\n submit();\n } else if (event.key === \"Enter\" && event.shiftKey) {\n event.preventDefault();\n editor.insertBreak();\n } else if (event.key === \"Escape\") {\n blur();\n }\n },\n [editor, onKeyDown, submit, blur]\n );\n\n const handleFocus = useCallback(\n (event: FocusEvent<HTMLDivElement>) => {\n onFocus?.(event);\n\n if (!event.isDefaultPrevented()) {\n setFocused(true);\n }\n },\n [onFocus, setFocused]\n );\n\n const handleBlur = useCallback(\n (event: FocusEvent<HTMLDivElement>) => {\n onBlur?.(event);\n\n if (!event.isDefaultPrevented()) {\n setFocused(false);\n }\n },\n [onBlur, setFocused]\n );\n\n useImperativeHandle(\n forwardedRef,\n () => ReactEditor.toDOMNode(editor, editor) as HTMLDivElement,\n [editor]\n );\n\n // Manually focus the editor when `autoFocus` is true\n useLayoutEffect(() => {\n if (autoFocus) {\n focus();\n }\n }, [autoFocus, editor, focus]);\n\n // Manually add a selection in the editor if the selection\n // is still empty after being focused\n useLayoutEffect(() => {\n if (isFocused && editor.selection === null) {\n select();\n }\n }, [editor, select, isFocused]);\n\n const initialValue: AiComposerBody = useMemo(() => {\n return defaultValue\n .split(\"\\n\")\n .map((text) => ({ type: \"paragraph\", children: [{ text }] }));\n }, [defaultValue]);\n\n return (\n <Slate\n editor={editor}\n initialValue={initialValue}\n onValueChange={onEditorValueChange}\n >\n <Editable\n dir={dir}\n enterKeyHint=\"send\"\n autoCapitalize=\"sentences\"\n aria-label=\"Composer editor\"\n onKeyDown={handleKeyDown}\n onFocus={handleFocus}\n onBlur={handleBlur}\n data-focused={isFocused || undefined}\n data-disabled={isDisabled || undefined}\n {...props}\n readOnly={isDisabled}\n disabled={isDisabled}\n renderPlaceholder={AiComposerEditorPlaceholder}\n />\n </Slate>\n );\n }\n);\n\n/* -------------------------------------------------------------------------------------------------\n * Submit\n * -----------------------------------------------------------------------------------------------*/\n\n/**\n * A button to submit the AI composer's content.\n *\n * @example\n * <AiComposer.Submit>Send</AiComposer.Submit>\n */\nexport const AiComposerSubmit = forwardRef<\n HTMLButtonElement,\n AiComposerSubmitProps\n>(({ disabled, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"button\";\n const { isDisabled: isComposerDisabled, canSubmit } = useAiComposer();\n const isDisabled = isComposerDisabled || disabled || !canSubmit;\n\n return (\n <Component\n type=\"submit\"\n {...props}\n ref={forwardedRef}\n disabled={isDisabled}\n />\n );\n});\n\n/* -------------------------------------------------------------------------------------------------\n * Abort\n * -----------------------------------------------------------------------------------------------*/\n\n/**\n * A button to abort a response related to the AI composer.\n *\n * @example\n * <AiComposer.Abort>Cancel</AiComposer.Abort>\n */\nexport const AiComposerAbort = forwardRef<\n HTMLButtonElement,\n AiComposerSubmitProps\n>(({ disabled, onClick, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"button\";\n const { isDisabled: isComposerDisabled, canAbort, abort } = useAiComposer();\n const isDisabled = isComposerDisabled || disabled || !canAbort;\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n abort();\n },\n [abort, onClick]\n );\n\n return (\n <Component\n type=\"button\"\n {...props}\n ref={forwardedRef}\n disabled={isDisabled}\n onClick={handleClick}\n />\n );\n});\n\nif (process.env.NODE_ENV !== \"production\") {\n AiComposerEditor.displayName = AI_COMPOSER_EDITOR_NAME;\n AiComposerForm.displayName = AI_COMPOSER_FORM_NAME;\n AiComposerSubmit.displayName = AI_COMPOSER_SUBMIT_NAME;\n AiComposerAbort.displayName = AI_COMPOSER_ABORT_NAME;\n}\n\n// NOTE: Every export from this file will be available publicly as AiComposer.*\nexport {\n AiComposerAbort as Abort,\n AiComposerEditor as Editor,\n AiComposerForm as Form,\n AiComposerSubmit as Submit,\n};\n"],"names":["SlateTransforms","SlateEditor","isEditorEmpty"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/primitives/AiComposer/index.tsx"],"sourcesContent":["import {\n type AiChatMessage,\n kInternal,\n Signal,\n type WithNavigation,\n} from \"@liveblocks/core\";\nimport { useClient } from \"@liveblocks/react\";\nimport { useLayoutEffect, useSignal } from \"@liveblocks/react/_private\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport type { FocusEvent, FormEvent, KeyboardEvent, MouseEvent } from \"react\";\nimport {\n forwardRef,\n useCallback,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport {\n createEditor,\n Editor as SlateEditor,\n Transforms as SlateTransforms,\n} from \"slate\";\nimport { withHistory } from \"slate-history\";\nimport {\n Editable,\n ReactEditor,\n type RenderPlaceholderProps,\n Slate,\n withReact,\n} from \"slate-react\";\n\nimport type { AiComposerBody } from \"../../types\";\nimport { requestSubmit } from \"../../utils/request-submit\";\nimport { useInitial } from \"../../utils/use-initial\";\nimport { withNormalize } from \"../slate/plugins/normalize\";\nimport { getDOMRange } from \"../slate/utils/get-dom-range\";\nimport { isEmpty } from \"../slate/utils/is-empty\";\nimport {\n AiComposerContext,\n AiComposerEditorContext,\n useAiComposer,\n useAiComposerEditorContext,\n} from \"./contexts\";\nimport type {\n AiComposerEditorProps,\n AiComposerFormProps,\n AiComposerSubmitProps,\n} from \"./types\";\n\nconst AI_COMPOSER_SUBMIT_NAME = \"AiComposerSubmit\";\nconst AI_COMPOSER_ABORT_NAME = \"AiComposerAbort\";\nconst AI_COMPOSER_EDITOR_NAME = \"AiComposerEditor\";\nconst AI_COMPOSER_FORM_NAME = \"AiComposerForm\";\n\ntype UiChatMessage = WithNavigation<AiChatMessage>;\n\n/* -------------------------------------------------------------------------------------------------\n * Form\n * -----------------------------------------------------------------------------------------------*/\n\nconst emptyMessagesΣ = new Signal<UiChatMessage[]>([]);\n\nfunction getLastMessageId(messages: UiChatMessage[]) {\n const lastMessage = messages[messages.length - 1];\n\n if (lastMessage === undefined) {\n return null;\n }\n\n return lastMessage.id;\n}\n\nfunction getAbortableMessageId(messages: UiChatMessage[]) {\n return messages.find(\n (message) =>\n message.role === \"assistant\" &&\n (message.status === \"generating\" || message.status === \"awaiting-tool\")\n )?.id;\n}\n\n/**\n * Surrounds the AI composer's content and handles submissions.\n *\n * @example\n * <AiComposer.Form onComposerSubmit={({ text }) => {}}>\n *\t <AiComposer.Editor />\n * <AiComposer.Submit />\n * </AiComposer.Form>\n */\nexport const AiComposerForm = forwardRef<HTMLFormElement, AiComposerFormProps>(\n (\n {\n onComposerSubmit,\n onSubmit,\n disabled,\n chatId,\n branchId,\n asChild,\n ...props\n },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"form\";\n const client = useClient();\n const formRef = useRef<HTMLFormElement | null>(null);\n const editor = useInitial(() =>\n withNormalize(withHistory(withReact(createEditor())))\n );\n const [isEditorEmpty, setEditorEmpty] = useState(true);\n const [isSubmitting, setSubmitting] = useState(false);\n const [isFocused, setFocused] = useState(false);\n const messagesΣ = chatId\n ? client[kInternal].ai.signals.getChatMessagesForBranchΣ(chatId, branchId)\n : emptyMessagesΣ;\n const lastMessageId = useSignal(messagesΣ, getLastMessageId);\n const abortableMessageId = useSignal(messagesΣ, getAbortableMessageId);\n const isAvailable = useSignal(\n // Subscribe to connection status signal\n client[kInternal].ai.signals.statusΣ,\n // \"Disconnected\" means the AI service is not available\n // as it represents a final error status.\n (status) => status !== \"disconnected\"\n );\n\n const isDisabled = isSubmitting || disabled === true;\n\n const canAbort = isAvailable && abortableMessageId !== undefined;\n const canSubmit = isAvailable && !isEditorEmpty && !canAbort;\n\n const clear = useCallback(() => {\n SlateTransforms.delete(editor, {\n at: {\n anchor: SlateEditor.start(editor, []),\n focus: SlateEditor.end(editor, []),\n },\n });\n }, [editor]);\n\n const select = useCallback(() => {\n SlateTransforms.select(editor, SlateEditor.end(editor, []));\n }, [editor]);\n\n const focus = useCallback(\n (resetSelection = true) => {\n try {\n // Slate's `ReactEditor.focus` method can use `setTimeout` internally\n // which prevents us from catching errors, so this is a reimplementation.\n // https://github.com/ianstormtaylor/slate/blob/main/packages/slate-dom/src/plugin/dom-editor.ts\n if (!ReactEditor.isFocused(editor)) {\n SlateTransforms.select(\n editor,\n resetSelection || !editor.selection\n ? SlateEditor.end(editor, [])\n : editor.selection\n );\n\n const element = ReactEditor.toDOMNode(editor, editor);\n\n if (editor.selection) {\n const domSelection = window.getSelection();\n const domRange = getDOMRange(editor, editor.selection);\n\n if (domRange) {\n domSelection?.removeAllRanges();\n domSelection?.addRange(domRange);\n }\n }\n\n element.focus({ preventScroll: true });\n }\n } catch {\n // Slate's DOM-specific methods will throw if the editor's DOM\n // node no longer exists. This action doesn't make sense on an\n // unmounted editor so we can safely ignore it.\n }\n },\n [editor]\n );\n\n const blur = useCallback(() => {\n try {\n ReactEditor.blur(editor);\n } catch {\n // Slate's DOM-specific methods will throw if the editor's DOM\n // node no longer exists. This action doesn't make sense on an\n // unmounted editor so we can safely ignore it.\n }\n }, [editor]);\n\n const onSubmitEnd = useCallback(() => {\n clear();\n setSubmitting(false);\n }, [clear]);\n\n const handleSubmit = useCallback(\n (event: FormEvent<HTMLFormElement>) => {\n if (disabled) {\n return;\n }\n\n // In some situations (e.g. pressing Enter while composing diacritics), it's possible\n // for the form to be submitted as empty even though we already checked whether the\n // editor was empty when handling the key press.\n const isEditorEmpty = isEmpty(editor, editor.children);\n\n // We even prevent the user's `onSubmit` handler from being called if the editor is empty.\n if (isEditorEmpty) {\n event.preventDefault();\n\n return;\n }\n\n onSubmit?.(event);\n\n if (onComposerSubmit === undefined || event.isDefaultPrevented()) {\n event.preventDefault();\n return;\n }\n\n // Extract the text content from the editor.\n const content = editor.children\n .map((block) => {\n if (\"type\" in block && block.type === \"paragraph\") {\n return block.children\n .map((child) => {\n if (\"text\" in child) {\n return child.text;\n }\n return \"\";\n })\n .join(\"\");\n }\n return \"\";\n })\n .join(\"\\n\");\n\n const promise = onComposerSubmit(\n { text: content, lastMessageId },\n event\n );\n\n event.preventDefault();\n\n if (promise) {\n setSubmitting(true);\n promise.then(onSubmitEnd);\n } else {\n onSubmitEnd();\n }\n },\n [disabled, editor, onSubmit, onComposerSubmit, onSubmitEnd, lastMessageId]\n );\n\n useLayoutEffect(() => {\n setEditorEmpty(isEmpty(editor, editor.children));\n }, [editor]);\n\n const handleEditorValueChange = useCallback(() => {\n setEditorEmpty(isEmpty(editor, editor.children));\n }, [editor]);\n\n const submit = useCallback(() => {\n if (!canSubmit) {\n return;\n }\n\n // We need to wait for the next frame in some cases like when composing diacritics,\n // we want any native handling to be done first while still being handled on `keydown`.\n requestAnimationFrame(() => {\n if (formRef.current) {\n requestSubmit(formRef.current);\n }\n });\n }, [canSubmit]);\n\n const abort = useCallback(() => {\n if (!canAbort || !abortableMessageId) {\n return;\n }\n\n client[kInternal].ai.abort(abortableMessageId);\n }, [canAbort, abortableMessageId, client]);\n\n useImperativeHandle<HTMLFormElement | null, HTMLFormElement | null>(\n forwardedRef,\n () => formRef.current,\n []\n );\n\n return (\n <AiComposerEditorContext.Provider\n value={{\n editor,\n onEditorValueChange: handleEditorValueChange,\n abortableMessageId,\n setFocused,\n }}\n >\n <AiComposerContext.Provider\n value={{\n isDisabled,\n isEmpty: isEditorEmpty,\n isFocused,\n canSubmit,\n canAbort,\n submit,\n abort,\n clear,\n focus,\n blur,\n select,\n }}\n >\n <Component onSubmit={handleSubmit} {...props} ref={formRef} />\n </AiComposerContext.Provider>\n </AiComposerEditorContext.Provider>\n );\n }\n);\n\n/* -------------------------------------------------------------------------------------------------\n * Editor\n * -----------------------------------------------------------------------------------------------*/\n\nfunction AiComposerEditorPlaceholder({\n attributes,\n children,\n}: RenderPlaceholderProps) {\n const { opacity: _opacity, ...style } = attributes.style;\n\n return (\n <span {...attributes} style={style} data-placeholder=\"\">\n {children}\n </span>\n );\n}\n\n/**\n * Displays the AI composer's editor.\n *\n * @example\n * <AiComposer.Editor placeholder=\"Write a message…\" />\n */\nconst AiComposerEditor = forwardRef<HTMLDivElement, AiComposerEditorProps>(\n (\n {\n defaultValue = \"\",\n onKeyDown,\n onFocus,\n onBlur,\n disabled,\n autoFocus,\n dir,\n ...props\n },\n forwardedRef\n ) => {\n const { editor, onEditorValueChange, setFocused } =\n useAiComposerEditorContext();\n const {\n submit,\n isDisabled: isComposerDisabled,\n isFocused,\n focus,\n blur,\n select,\n } = useAiComposer();\n const isDisabled = disabled || isComposerDisabled;\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLDivElement>) => {\n onKeyDown?.(event);\n if (event.isDefaultPrevented()) return;\n\n if (event.key === \"Enter\" && !event.shiftKey) {\n event.preventDefault();\n submit();\n } else if (event.key === \"Enter\" && event.shiftKey) {\n event.preventDefault();\n editor.insertBreak();\n } else if (event.key === \"Escape\") {\n blur();\n }\n },\n [editor, onKeyDown, submit, blur]\n );\n\n const handleFocus = useCallback(\n (event: FocusEvent<HTMLDivElement>) => {\n onFocus?.(event);\n\n if (!event.isDefaultPrevented()) {\n setFocused(true);\n }\n },\n [onFocus, setFocused]\n );\n\n const handleBlur = useCallback(\n (event: FocusEvent<HTMLDivElement>) => {\n onBlur?.(event);\n\n if (!event.isDefaultPrevented()) {\n setFocused(false);\n }\n },\n [onBlur, setFocused]\n );\n\n useImperativeHandle(\n forwardedRef,\n () => ReactEditor.toDOMNode(editor, editor) as HTMLDivElement,\n [editor]\n );\n\n // Manually focus the editor when `autoFocus` is true\n useLayoutEffect(() => {\n if (!autoFocus) {\n return;\n }\n\n // `focus` needs to be synchronous to ensure its errors can be caught\n // but the triggering of `focus` on mount itself can be asynchronous.\n // This brings back the same timing behavior as Slate's `ReactEditor.focus`\n // (which uses `setTimeout` internally) while still allowing us to catch errors.\n const timeout = setTimeout(() => focus(), 0);\n\n return () => clearTimeout(timeout);\n }, [autoFocus, editor, focus]);\n\n // Manually add a selection in the editor if the selection\n // is still empty after being focused\n useLayoutEffect(() => {\n if (isFocused && editor.selection === null) {\n select();\n }\n }, [editor, select, isFocused]);\n\n const initialValue: AiComposerBody = useMemo(() => {\n return defaultValue\n .split(\"\\n\")\n .map((text) => ({ type: \"paragraph\", children: [{ text }] }));\n }, [defaultValue]);\n\n return (\n <Slate\n editor={editor}\n initialValue={initialValue}\n onValueChange={onEditorValueChange}\n >\n <Editable\n dir={dir}\n enterKeyHint=\"send\"\n autoCapitalize=\"sentences\"\n aria-label=\"Composer editor\"\n onKeyDown={handleKeyDown}\n onFocus={handleFocus}\n onBlur={handleBlur}\n data-focused={isFocused || undefined}\n data-disabled={isDisabled || undefined}\n {...props}\n readOnly={isDisabled}\n disabled={isDisabled}\n renderPlaceholder={AiComposerEditorPlaceholder}\n />\n </Slate>\n );\n }\n);\n\n/* -------------------------------------------------------------------------------------------------\n * Submit\n * -----------------------------------------------------------------------------------------------*/\n\n/**\n * A button to submit the AI composer's content.\n *\n * @example\n * <AiComposer.Submit>Send</AiComposer.Submit>\n */\nexport const AiComposerSubmit = forwardRef<\n HTMLButtonElement,\n AiComposerSubmitProps\n>(({ disabled, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"button\";\n const { isDisabled: isComposerDisabled, canSubmit } = useAiComposer();\n const isDisabled = isComposerDisabled || disabled || !canSubmit;\n\n return (\n <Component\n type=\"submit\"\n {...props}\n ref={forwardedRef}\n disabled={isDisabled}\n />\n );\n});\n\n/* -------------------------------------------------------------------------------------------------\n * Abort\n * -----------------------------------------------------------------------------------------------*/\n\n/**\n * A button to abort a response related to the AI composer.\n *\n * @example\n * <AiComposer.Abort>Cancel</AiComposer.Abort>\n */\nexport const AiComposerAbort = forwardRef<\n HTMLButtonElement,\n AiComposerSubmitProps\n>(({ disabled, onClick, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"button\";\n const { isDisabled: isComposerDisabled, canAbort, abort } = useAiComposer();\n const isDisabled = isComposerDisabled || disabled || !canAbort;\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n abort();\n },\n [abort, onClick]\n );\n\n return (\n <Component\n type=\"button\"\n {...props}\n ref={forwardedRef}\n disabled={isDisabled}\n onClick={handleClick}\n />\n );\n});\n\nif (process.env.NODE_ENV !== \"production\") {\n AiComposerEditor.displayName = AI_COMPOSER_EDITOR_NAME;\n AiComposerForm.displayName = AI_COMPOSER_FORM_NAME;\n AiComposerSubmit.displayName = AI_COMPOSER_SUBMIT_NAME;\n AiComposerAbort.displayName = AI_COMPOSER_ABORT_NAME;\n}\n\n// NOTE: Every export from this file will be available publicly as AiComposer.*\nexport {\n AiComposerAbort as Abort,\n AiComposerEditor as Editor,\n AiComposerForm as Form,\n AiComposerSubmit as Submit,\n};\n"],"names":["SlateTransforms","SlateEditor","isEditorEmpty"],"mappings":"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"}
@@ -780,9 +780,11 @@ const ComposerEditor = react.forwardRef(
780
780
  return slateReact.ReactEditor.toDOMNode(editor, editor);
781
781
  }, [editor]);
782
782
  _private.useLayoutEffect(() => {
783
- if (autoFocus) {
784
- focus();
783
+ if (!autoFocus) {
784
+ return;
785
785
  }
786
+ const timeout = setTimeout(() => focus(), 0);
787
+ return () => clearTimeout(timeout);
786
788
  }, [autoFocus, editor, focus]);
787
789
  _private.useLayoutEffect(() => {
788
790
  if (isFocused && editor.selection === null) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../../src/primitives/Composer/index.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n type CommentAttachment,\n type CommentBody,\n type CommentLocalAttachment,\n createCommentAttachmentId,\n type EventSource,\n makeEventSource,\n MENTION_CHARACTER,\n type MentionData,\n sanitizeUrl,\n} from \"@liveblocks/core\";\nimport { useRoom } from \"@liveblocks/react\";\nimport {\n useClientOrNull,\n useLayoutEffect,\n useMentionSuggestions,\n useResolveMentionSuggestions,\n useSyncSource,\n} from \"@liveblocks/react/_private\";\nimport { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport * as TogglePrimitive from \"@radix-ui/react-toggle\";\nimport type {\n AriaAttributes,\n ChangeEvent,\n FocusEvent,\n FormEvent,\n KeyboardEvent,\n MouseEvent,\n PointerEvent,\n SyntheticEvent,\n} from \"react\";\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type {\n Descendant as SlateDescendant,\n Element as SlateElement,\n} from \"slate\";\nimport {\n createEditor,\n Editor as SlateEditor,\n insertText as insertSlateText,\n Range as SlateRange,\n Transforms as SlateTransforms,\n} from \"slate\";\nimport { withHistory } from \"slate-history\";\nimport type {\n RenderElementProps,\n RenderElementSpecificProps,\n RenderLeafSpecificProps,\n RenderPlaceholderProps,\n} from \"slate-react\";\nimport {\n Editable,\n ReactEditor,\n Slate,\n useSelected,\n useSlateStatic,\n withReact,\n} from \"slate-react\";\n\nimport type {\n ComposerBody as ComposerBodyData,\n ComposerBodyAutoLink,\n ComposerBodyCustomLink,\n ComposerBodyMark,\n ComposerBodyMarks,\n ComposerBodyMention,\n ComposerBodyText,\n} from \"../../types\";\nimport { isKey } from \"../../utils/is-key\";\nimport { Persist, useAnimationPersist, usePersist } from \"../../utils/Persist\";\nimport { Portal } from \"../../utils/Portal\";\nimport { requestSubmit } from \"../../utils/request-submit\";\nimport { useIndex } from \"../../utils/use-index\";\nimport { useInitial } from \"../../utils/use-initial\";\nimport { useObservable } from \"../../utils/use-observable\";\nimport { useRefs } from \"../../utils/use-refs\";\nimport { withEmptyClearFormatting } from \"../slate/plugins/empty-clear-formatting\";\nimport { withNormalize } from \"../slate/plugins/normalize\";\nimport { getDOMRange } from \"../slate/utils/get-dom-range\";\nimport { isEmpty as isEditorEmpty } from \"../slate/utils/is-empty\";\nimport {\n getComposerBodyMarks,\n leaveMarkEdge,\n toggleMark as toggleEditorMark,\n} from \"../slate/utils/marks\";\nimport {\n ComposerAttachmentsContext,\n ComposerContext,\n ComposerEditorContext,\n ComposerFloatingToolbarContext,\n ComposerSuggestionsContext,\n useComposer,\n useComposerAttachmentsContext,\n useComposerEditorContext,\n useComposerFloatingToolbarContext,\n useComposerSuggestionsContext,\n} from \"./contexts\";\nimport { withAutoFormatting } from \"./slate/plugins/auto-formatting\";\nimport { withAutoLinks } from \"./slate/plugins/auto-links\";\nimport { withCustomLinks } from \"./slate/plugins/custom-links\";\nimport type { MentionDraft } from \"./slate/plugins/mentions\";\nimport {\n getMentionDraftAtSelection,\n insertMention,\n insertMentionCharacter,\n withMentions,\n} from \"./slate/plugins/mentions\";\nimport { withPaste } from \"./slate/plugins/paste\";\nimport type {\n ComposerAttachFilesProps,\n ComposerAttachmentsDropAreaProps,\n ComposerEditorComponents,\n ComposerEditorElementProps,\n ComposerEditorFloatingToolbarWrapperProps,\n ComposerEditorLinkWrapperProps,\n ComposerEditorMentionSuggestionsWrapperProps,\n ComposerEditorMentionWrapperProps,\n ComposerEditorProps,\n ComposerFloatingToolbarProps,\n ComposerFormProps,\n ComposerLinkProps,\n ComposerMarkToggleProps,\n ComposerMentionProps,\n ComposerSubmitProps,\n ComposerSuggestionsListItemProps,\n ComposerSuggestionsListProps,\n ComposerSuggestionsProps,\n FloatingPosition,\n} from \"./types\";\nimport {\n commentBodyToComposerBody,\n composerBodyToCommentBody,\n getSideAndAlignFromFloatingPlacement,\n useComposerAttachmentsDropArea,\n useComposerAttachmentsManager,\n useContentZIndex,\n useFloatingWithOptions,\n} from \"./utils\";\n\nconst MENTION_SUGGESTIONS_POSITION: FloatingPosition = \"top\";\n\nconst FLOATING_TOOLBAR_POSITION: FloatingPosition = \"top\";\n\nconst COMPOSER_MENTION_NAME = \"ComposerMention\";\nconst COMPOSER_LINK_NAME = \"ComposerLink\";\nconst COMPOSER_FLOATING_TOOLBAR_NAME = \"ComposerFloatingToolbar\";\nconst COMPOSER_SUGGESTIONS_NAME = \"ComposerSuggestions\";\nconst COMPOSER_SUGGESTIONS_LIST_NAME = \"ComposerSuggestionsList\";\nconst COMPOSER_SUGGESTIONS_LIST_ITEM_NAME = \"ComposerSuggestionsListItem\";\nconst COMPOSER_SUBMIT_NAME = \"ComposerSubmit\";\nconst COMPOSER_EDITOR_NAME = \"ComposerEditor\";\nconst COMPOSER_ATTACH_FILES_NAME = \"ComposerAttachFiles\";\nconst COMPOSER_ATTACHMENTS_DROP_AREA_NAME = \"ComposerAttachmentsDropArea\";\nconst COMPOSER_MARK_TOGGLE_NAME = \"ComposerMarkToggle\";\nconst COMPOSER_FORM_NAME = \"ComposerForm\";\n\nconst emptyCommentBody: CommentBody = {\n version: 1,\n content: [{ type: \"paragraph\", children: [{ text: \"\" }] }],\n};\n\nfunction createComposerEditor({\n createAttachments,\n pasteFilesAsAttachments,\n}: {\n createAttachments: (files: File[]) => void;\n pasteFilesAsAttachments?: boolean;\n}) {\n return withNormalize(\n withMentions(\n withCustomLinks(\n withAutoLinks(\n withAutoFormatting(\n withEmptyClearFormatting(\n withPaste(withHistory(withReact(createEditor())), {\n createAttachments,\n pasteFilesAsAttachments,\n })\n )\n )\n )\n )\n )\n );\n}\n\nfunction ComposerEditorMentionWrapper({\n Mention,\n attributes,\n children,\n element,\n}: ComposerEditorMentionWrapperProps) {\n const isSelected = useSelected();\n const { children: _, ...mention } = element;\n\n return (\n <span {...attributes}>\n {element.id ? (\n <Mention mention={mention} isSelected={isSelected} />\n ) : null}\n {children}\n </span>\n );\n}\n\nfunction ComposerEditorLinkWrapper({\n Link,\n attributes,\n element,\n children,\n}: ComposerEditorLinkWrapperProps) {\n const href = useMemo(() => sanitizeUrl(element.url) ?? \"\", [element.url]);\n\n return (\n <span {...attributes}>\n <Link href={href}>{children}</Link>\n </span>\n );\n}\n\nfunction ComposerEditorMentionSuggestionsWrapper({\n id,\n itemId,\n mentions,\n selectedMentionId,\n setSelectedMentionId,\n mentionDraft,\n setMentionDraft,\n onItemSelect,\n position = MENTION_SUGGESTIONS_POSITION,\n dir,\n MentionSuggestions,\n}: ComposerEditorMentionSuggestionsWrapperProps) {\n const editor = useSlateStatic();\n const { onEditorChange } = useComposerEditorContext();\n const { isFocused } = useComposer();\n const [contentRef, contentZIndex] = useContentZIndex();\n const isOpen =\n isFocused && mentionDraft?.range !== undefined && mentions !== undefined;\n const {\n refs: { setReference, setFloating },\n strategy,\n isPositioned,\n placement,\n x,\n y,\n update,\n elements,\n } = useFloatingWithOptions({\n position,\n dir,\n alignment: \"start\",\n open: isOpen,\n });\n\n useObservable(onEditorChange, () => {\n setMentionDraft(getMentionDraftAtSelection(editor));\n });\n\n useLayoutEffect(() => {\n if (!mentionDraft) {\n setReference(null);\n\n return;\n }\n\n const domRange = getDOMRange(editor, mentionDraft.range);\n setReference(domRange ?? null);\n }, [setReference, editor, mentionDraft]);\n\n // Manually update the placement when the number of suggestions changes\n // This can prevent the list of suggestions from scrolling instead of moving to the other placement\n useLayoutEffect(() => {\n if (!isOpen) return;\n\n const mentionSuggestions = elements.floating?.firstChild as\n | HTMLElement\n | undefined;\n\n if (!mentionSuggestions) {\n return;\n }\n\n // Force the mention suggestions to grow instead of scrolling\n mentionSuggestions.style.overflowY = \"visible\";\n mentionSuggestions.style.maxHeight = \"none\";\n\n // Trigger a placement update\n update();\n\n // Reset the mention suggestions after the placement update\n const animationFrame = requestAnimationFrame(() => {\n mentionSuggestions.style.overflowY = \"auto\";\n mentionSuggestions.style.maxHeight =\n \"var(--lb-composer-floating-available-height)\";\n });\n\n return () => {\n cancelAnimationFrame(animationFrame);\n };\n }, [mentions?.length, isOpen, elements.floating, update]);\n\n return (\n <Persist>\n {isOpen ? (\n <ComposerSuggestionsContext.Provider\n value={{\n id,\n itemId,\n selectedValue: selectedMentionId,\n setSelectedValue: setSelectedMentionId,\n onItemSelect,\n placement,\n dir,\n ref: contentRef,\n }}\n >\n <Portal\n ref={setFloating}\n style={{\n position: strategy,\n top: 0,\n left: 0,\n transform: isPositioned\n ? `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`\n : \"translate3d(0, -200%, 0)\",\n minWidth: \"max-content\",\n zIndex: contentZIndex,\n }}\n >\n <MentionSuggestions\n mentions={mentions}\n selectedMentionId={selectedMentionId}\n />\n </Portal>\n </ComposerSuggestionsContext.Provider>\n ) : null}\n </Persist>\n );\n}\n\nfunction ComposerEditorFloatingToolbarWrapper({\n id,\n position = FLOATING_TOOLBAR_POSITION,\n dir,\n FloatingToolbar,\n hasFloatingToolbarRange,\n setHasFloatingToolbarRange,\n}: ComposerEditorFloatingToolbarWrapperProps) {\n const editor = useSlateStatic();\n const { onEditorChange } = useComposerEditorContext();\n const { isFocused } = useComposer();\n const [contentRef, contentZIndex] = useContentZIndex();\n const [isPointerDown, setPointerDown] = useState(false);\n const isOpen = isFocused && !isPointerDown && hasFloatingToolbarRange;\n const {\n refs: { setReference, setFloating },\n strategy,\n isPositioned,\n placement,\n x,\n y,\n } = useFloatingWithOptions({\n type: \"range\",\n position,\n dir,\n alignment: \"center\",\n open: isOpen,\n });\n\n useLayoutEffect(() => {\n if (!isFocused) {\n return;\n }\n\n const handlePointerDown = () => setPointerDown(true);\n const handlePointerUp = () => setPointerDown(false);\n\n document.addEventListener(\"pointerdown\", handlePointerDown);\n document.addEventListener(\"pointerup\", handlePointerUp);\n\n return () => {\n document.removeEventListener(\"pointerdown\", handlePointerDown);\n document.removeEventListener(\"pointerup\", handlePointerUp);\n };\n }, [isFocused]);\n\n useObservable(onEditorChange, () => {\n // Detach from previous selection range (if any) to avoid sudden jumps\n setReference(null);\n\n // Then, wait for the next render to ensure the selection is updated\n requestAnimationFrame(() => {\n const domSelection = window.getSelection();\n\n // Finally, show the toolbar if there's a selection range\n if (\n !editor.selection ||\n SlateRange.isCollapsed(editor.selection) ||\n !domSelection ||\n !domSelection.rangeCount\n ) {\n setHasFloatingToolbarRange(false);\n setReference(null);\n } else {\n setHasFloatingToolbarRange(true);\n\n const domRange = domSelection.getRangeAt(0);\n setReference(domRange);\n }\n });\n });\n\n return (\n <Persist>\n {isOpen ? (\n <ComposerFloatingToolbarContext.Provider\n value={{\n id,\n placement,\n dir,\n ref: contentRef,\n }}\n >\n <Portal\n ref={setFloating}\n style={{\n position: strategy,\n top: 0,\n left: 0,\n transform: isPositioned\n ? `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`\n : \"translate3d(0, -200%, 0)\",\n minWidth: \"max-content\",\n zIndex: contentZIndex,\n }}\n >\n <FloatingToolbar />\n </Portal>\n </ComposerFloatingToolbarContext.Provider>\n ) : null}\n </Persist>\n );\n}\n\n/**\n * Displays a floating toolbar attached to the selection within `Composer.Editor`.\n *\n * @example\n * <Composer.FloatingToolbar>\n * <Composer.MarkToggle mark=\"bold\">Bold</Composer.MarkToggle>\n * <Composer.MarkToggle mark=\"italic\">Italic</Composer.MarkToggle>\n * </Composer.FloatingToolbar>\n */\nconst ComposerFloatingToolbar = forwardRef<\n HTMLDivElement,\n ComposerFloatingToolbarProps\n>(({ children, onPointerDown, style, asChild, ...props }, forwardedRef) => {\n const [isPresent] = usePersist();\n const ref = useRef<HTMLDivElement>(null);\n const {\n id,\n ref: contentRef,\n placement,\n dir,\n } = useComposerFloatingToolbarContext(COMPOSER_FLOATING_TOOLBAR_NAME);\n const mergedRefs = useRefs(forwardedRef, contentRef, ref);\n const [side, align] = useMemo(\n () => getSideAndAlignFromFloatingPlacement(placement),\n [placement]\n );\n const Component = asChild ? Slot : \"div\";\n useAnimationPersist(ref);\n\n const handlePointerDown = useCallback(\n (event: PointerEvent<HTMLDivElement>) => {\n onPointerDown?.(event);\n\n event.preventDefault();\n event.stopPropagation();\n },\n [onPointerDown]\n );\n\n return (\n <Component\n dir={dir}\n role=\"toolbar\"\n id={id}\n aria-label=\"Floating toolbar\"\n {...props}\n onPointerDown={handlePointerDown}\n data-state={isPresent ? \"open\" : \"closed\"}\n data-side={side}\n data-align={align}\n style={{\n display: \"flex\",\n flexDirection: \"row\",\n maxWidth: \"var(--lb-composer-floating-available-width)\",\n overflowX: \"auto\",\n ...style,\n }}\n ref={mergedRefs}\n >\n {children}\n </Component>\n );\n});\n\nfunction ComposerEditorElement({\n Mention,\n Link,\n ...props\n}: ComposerEditorElementProps) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const { attributes, children, element } = props;\n\n switch (element.type) {\n case \"mention\":\n return (\n <ComposerEditorMentionWrapper\n Mention={Mention}\n {...(props as RenderElementSpecificProps<ComposerBodyMention>)}\n />\n );\n case \"auto-link\":\n case \"custom-link\":\n return (\n <ComposerEditorLinkWrapper\n Link={Link}\n {...(props as RenderElementSpecificProps<\n ComposerBodyAutoLink | ComposerBodyCustomLink\n >)}\n />\n );\n case \"paragraph\":\n return (\n <p {...attributes} style={{ position: \"relative\" }}>\n {children}\n </p>\n );\n default:\n return null;\n }\n}\n\n// <code><s><em><strong>text</strong></s></em></code>\nfunction ComposerEditorLeaf({\n attributes,\n children,\n leaf,\n}: RenderLeafSpecificProps<ComposerBodyText>) {\n if (leaf.bold) {\n children = <strong>{children}</strong>;\n }\n\n if (leaf.italic) {\n children = <em>{children}</em>;\n }\n\n if (leaf.strikethrough) {\n children = <s>{children}</s>;\n }\n\n if (leaf.code) {\n children = <code>{children}</code>;\n }\n\n return <span {...attributes}>{children}</span>;\n}\n\nfunction ComposerEditorPlaceholder({\n attributes,\n children,\n}: RenderPlaceholderProps) {\n const { opacity: _opacity, ...style } = attributes.style;\n\n return (\n <span {...attributes} style={style} data-placeholder=\"\">\n {children}\n </span>\n );\n}\n\n/**\n * Displays mentions within `Composer.Editor`.\n *\n * @example\n * <Composer.Mention>@{mention.id}</Composer.Mention>\n */\nconst ComposerMention = forwardRef<HTMLSpanElement, ComposerMentionProps>(\n ({ children, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"span\";\n const isSelected = useSelected();\n\n return (\n <Component\n data-selected={isSelected || undefined}\n {...props}\n ref={forwardedRef}\n >\n {children}\n </Component>\n );\n }\n);\n\n/**\n * Displays links within `Composer.Editor`.\n *\n * @example\n * <Composer.Link href={href}>{children}</Composer.Link>\n */\nconst ComposerLink = forwardRef<HTMLAnchorElement, ComposerLinkProps>(\n ({ children, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"a\";\n\n return (\n <Component\n target=\"_blank\"\n rel=\"noopener noreferrer nofollow\"\n {...props}\n ref={forwardedRef}\n >\n {children}\n </Component>\n );\n }\n);\n\n/**\n * Contains suggestions within `Composer.Editor`.\n */\nconst ComposerSuggestions = forwardRef<\n HTMLDivElement,\n ComposerSuggestionsProps\n>(({ children, style, asChild, ...props }, forwardedRef) => {\n const [isPresent] = usePersist();\n const ref = useRef<HTMLDivElement>(null);\n const {\n ref: contentRef,\n placement,\n dir,\n } = useComposerSuggestionsContext(COMPOSER_SUGGESTIONS_NAME);\n const mergedRefs = useRefs(forwardedRef, contentRef, ref);\n const [side, align] = useMemo(\n () => getSideAndAlignFromFloatingPlacement(placement),\n [placement]\n );\n const Component = asChild ? Slot : \"div\";\n useAnimationPersist(ref);\n\n return (\n <Component\n dir={dir}\n {...props}\n data-state={isPresent ? \"open\" : \"closed\"}\n data-side={side}\n data-align={align}\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n maxHeight: \"var(--lb-composer-floating-available-height)\",\n overflowY: \"auto\",\n ...style,\n }}\n ref={mergedRefs}\n >\n {children}\n </Component>\n );\n});\n\n/**\n * Displays a list of suggestions within `Composer.Editor`.\n *\n * @example\n * <Composer.SuggestionsList>\n * {mentions.map((mention) => (\n * <Composer.SuggestionsListItem key={mention.id} value={mention.id}>\n * @{mention.id}\n * </Composer.SuggestionsListItem>\n * ))}\n * </Composer.SuggestionsList>\n */\nconst ComposerSuggestionsList = forwardRef<\n HTMLUListElement,\n ComposerSuggestionsListProps\n>(({ children, asChild, ...props }, forwardedRef) => {\n const { id } = useComposerSuggestionsContext(COMPOSER_SUGGESTIONS_LIST_NAME);\n const Component = asChild ? Slot : \"ul\";\n\n return (\n <Component\n role=\"listbox\"\n id={id}\n aria-label=\"Suggestions list\"\n {...props}\n ref={forwardedRef}\n >\n {children}\n </Component>\n );\n});\n\n/**\n * Displays a suggestion within `Composer.SuggestionsList`.\n *\n * @example\n * <Composer.SuggestionsListItem key={mention.id} value={mention.id}>\n * @{mention.id}\n * </Composer.SuggestionsListItem>\n */\nconst ComposerSuggestionsListItem = forwardRef<\n HTMLLIElement,\n ComposerSuggestionsListItemProps\n>(\n (\n {\n value,\n children,\n onPointerMove,\n onPointerDown,\n onClick,\n asChild,\n ...props\n },\n forwardedRef\n ) => {\n const ref = useRef<HTMLLIElement>(null);\n const mergedRefs = useRefs(forwardedRef, ref);\n const { selectedValue, setSelectedValue, itemId, onItemSelect } =\n useComposerSuggestionsContext(COMPOSER_SUGGESTIONS_LIST_ITEM_NAME);\n const Component = asChild ? Slot : \"li\";\n const isSelected = useMemo(\n () => selectedValue === value,\n [selectedValue, value]\n );\n // TODO: Support props.id if provided, it will need to be sent up to Composer.Editor to use it in aria-activedescendant\n const id = useMemo(() => itemId(value), [itemId, value]);\n\n useEffect(() => {\n if (ref?.current && isSelected) {\n ref.current.scrollIntoView({ block: \"nearest\" });\n }\n }, [isSelected]);\n\n const handlePointerMove = useCallback(\n (event: PointerEvent<HTMLLIElement>) => {\n onPointerMove?.(event);\n\n if (!event.isDefaultPrevented()) {\n setSelectedValue(value);\n }\n },\n [onPointerMove, setSelectedValue, value]\n );\n\n const handlePointerDown = useCallback(\n (event: PointerEvent<HTMLLIElement>) => {\n onPointerDown?.(event);\n\n event.preventDefault();\n event.stopPropagation();\n },\n [onPointerDown]\n );\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLLIElement>) => {\n onClick?.(event);\n\n const wasDefaultPrevented = event.isDefaultPrevented();\n\n event.preventDefault();\n event.stopPropagation();\n\n if (!wasDefaultPrevented) {\n onItemSelect(value);\n }\n },\n [onClick, onItemSelect, value]\n );\n\n return (\n <Component\n role=\"option\"\n id={id}\n data-selected={isSelected || undefined}\n aria-selected={isSelected || undefined}\n onPointerMove={handlePointerMove}\n onPointerDown={handlePointerDown}\n onClick={handleClick}\n {...props}\n ref={mergedRefs}\n >\n {children}\n </Component>\n );\n }\n);\n\nconst defaultEditorComponents: ComposerEditorComponents = {\n Link: ({ href, children }) => {\n return <ComposerLink href={href}>{children}</ComposerLink>;\n },\n Mention: ({ mention }) => {\n return (\n <ComposerMention>\n {MENTION_CHARACTER}\n {mention.id}\n </ComposerMention>\n );\n },\n MentionSuggestions: ({ mentions }) => {\n return mentions.length > 0 ? (\n <ComposerSuggestions>\n <ComposerSuggestionsList>\n {mentions.map((mention) => (\n <ComposerSuggestionsListItem key={mention.id} value={mention.id}>\n {mention.id}\n </ComposerSuggestionsListItem>\n ))}\n </ComposerSuggestionsList>\n </ComposerSuggestions>\n ) : null;\n },\n};\n\n/**\n * Displays the composer's editor.\n *\n * @example\n * <Composer.Editor placeholder=\"Write a comment…\" />\n */\nconst ComposerEditor = forwardRef<HTMLDivElement, ComposerEditorProps>(\n (\n {\n defaultValue,\n onKeyDown,\n onFocus,\n onBlur,\n disabled,\n autoFocus,\n components,\n dir,\n ...props\n },\n forwardedRef\n ) => {\n const client = useClientOrNull();\n const { editor, validate, setFocused, onEditorChange, roomId } =\n useComposerEditorContext();\n const {\n submit,\n focus,\n blur,\n select,\n canSubmit,\n isDisabled: isComposerDisabled,\n isFocused,\n } = useComposer();\n const isDisabled = isComposerDisabled || disabled;\n const initialBody = useInitial(defaultValue ?? emptyCommentBody);\n const initialEditorValue = useMemo(() => {\n return commentBodyToComposerBody(initialBody);\n }, [initialBody]);\n const { Link, Mention, MentionSuggestions, FloatingToolbar } = useMemo(\n () => ({ ...defaultEditorComponents, ...components }),\n [components]\n );\n\n const [hasFloatingToolbarRange, setHasFloatingToolbarRange] =\n useState(false);\n // If used with LiveblocksProvider but without resolveMentionSuggestions,\n // we can skip the mention suggestions logic entirely\n const resolveMentionSuggestions = useResolveMentionSuggestions();\n const hasResolveMentionSuggestions = client\n ? resolveMentionSuggestions\n : true;\n const [mentionDraft, setMentionDraft] = useState<MentionDraft>();\n const mentionSuggestions = useMentionSuggestions(\n roomId,\n mentionDraft?.text\n );\n const [\n selectedMentionSuggestionIndex,\n setPreviousSelectedMentionSuggestionIndex,\n setNextSelectedMentionSuggestionIndex,\n setSelectedMentionSuggestionIndex,\n ] = useIndex(0, mentionSuggestions?.length ?? 0);\n const id = useId();\n const floatingToolbarId = `liveblocks-floating-toolbar-${id}`;\n const suggestionsListId = `liveblocks-suggestions-list-${id}`;\n const suggestionsListItemId = useCallback(\n (mentionId?: string) =>\n mentionId\n ? `liveblocks-suggestions-list-item-${id}-${mentionId}`\n : undefined,\n [id]\n );\n\n const renderElement = useCallback(\n (props: RenderElementProps) => {\n return (\n <ComposerEditorElement Mention={Mention} Link={Link} {...props} />\n );\n },\n [Link, Mention]\n );\n\n const handleChange = useCallback(\n (value: SlateDescendant[]) => {\n validate(value as SlateElement[]);\n\n // Our multi-component setup requires us to instantiate the editor in `Composer.Form`\n // but we can only listen to changes here in `Composer.Editor` via `Slate`, so we use\n // an event source to notify `Composer.Form` of changes.\n onEditorChange.notify();\n },\n [validate, onEditorChange]\n );\n\n const createMention = useCallback(\n (mention?: MentionData) => {\n if (!mentionDraft || !mention) {\n return;\n }\n\n SlateTransforms.select(editor, mentionDraft.range);\n insertMention(editor, mention);\n setMentionDraft(undefined);\n setSelectedMentionSuggestionIndex(0);\n },\n [editor, mentionDraft, setSelectedMentionSuggestionIndex]\n );\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLDivElement>) => {\n onKeyDown?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n // Allow leaving marks with ArrowLeft\n if (isKey(event, \"ArrowLeft\")) {\n leaveMarkEdge(editor, \"start\");\n }\n\n // Allow leaving marks with ArrowRight\n if (isKey(event, \"ArrowRight\")) {\n leaveMarkEdge(editor, \"end\");\n }\n\n if (mentionDraft && mentionSuggestions?.length) {\n // Select the next mention suggestion on ArrowDown\n if (isKey(event, \"ArrowDown\")) {\n event.preventDefault();\n setNextSelectedMentionSuggestionIndex();\n }\n\n // Select the previous mention suggestion on ArrowUp\n if (isKey(event, \"ArrowUp\")) {\n event.preventDefault();\n setPreviousSelectedMentionSuggestionIndex();\n }\n\n // Create a mention on Enter/Tab\n if (isKey(event, \"Enter\") || isKey(event, \"Tab\")) {\n event.preventDefault();\n\n const mention =\n mentionSuggestions?.[selectedMentionSuggestionIndex];\n createMention(mention);\n }\n\n // Close the suggestions on Escape\n if (isKey(event, \"Escape\")) {\n event.preventDefault();\n setMentionDraft(undefined);\n setSelectedMentionSuggestionIndex(0);\n }\n } else {\n if (hasFloatingToolbarRange) {\n // Close the floating toolbar on Escape\n if (isKey(event, \"Escape\")) {\n event.preventDefault();\n setHasFloatingToolbarRange(false);\n }\n }\n\n // Blur the editor on Escape\n if (isKey(event, \"Escape\")) {\n blur();\n }\n\n // Submit the editor on Enter\n if (isKey(event, \"Enter\", { shift: false })) {\n // Even if submitting is not possible, don't do anything else on Enter. (e.g. creating a new line)\n event.preventDefault();\n\n if (canSubmit) {\n submit();\n }\n }\n\n // Create a new line on Shift + Enter\n if (isKey(event, \"Enter\", { shift: true })) {\n event.preventDefault();\n editor.insertBreak();\n }\n\n // Toggle bold on Command/Control + B\n if (isKey(event, \"b\", { mod: true })) {\n event.preventDefault();\n toggleEditorMark(editor, \"bold\");\n }\n\n // Toggle italic on Command/Control + I\n if (isKey(event, \"i\", { mod: true })) {\n event.preventDefault();\n toggleEditorMark(editor, \"italic\");\n }\n\n // Toggle strikethrough on Command/Control + Shift + S\n if (isKey(event, \"s\", { mod: true, shift: true })) {\n event.preventDefault();\n toggleEditorMark(editor, \"strikethrough\");\n }\n\n // Toggle code on Command/Control + E\n if (isKey(event, \"e\", { mod: true })) {\n event.preventDefault();\n toggleEditorMark(editor, \"code\");\n }\n }\n },\n [\n onKeyDown,\n mentionDraft,\n mentionSuggestions,\n hasFloatingToolbarRange,\n editor,\n setNextSelectedMentionSuggestionIndex,\n setPreviousSelectedMentionSuggestionIndex,\n selectedMentionSuggestionIndex,\n createMention,\n setSelectedMentionSuggestionIndex,\n blur,\n canSubmit,\n submit,\n ]\n );\n\n const handleFocus = useCallback(\n (event: FocusEvent<HTMLDivElement>) => {\n onFocus?.(event);\n\n if (!event.isDefaultPrevented()) {\n setFocused(true);\n }\n },\n [onFocus, setFocused]\n );\n\n const handleBlur = useCallback(\n (event: FocusEvent<HTMLDivElement>) => {\n onBlur?.(event);\n\n if (!event.isDefaultPrevented()) {\n setFocused(false);\n }\n },\n [onBlur, setFocused]\n );\n\n const selectedMention =\n mentionSuggestions?.[selectedMentionSuggestionIndex];\n const selectedMentionId = selectedMention?.id;\n const setSelectedMentionId = useCallback(\n (mentionId: string) => {\n const index = mentionSuggestions?.findIndex(\n (mention) => mention.id === mentionId\n );\n\n if (index !== undefined && index >= 0) {\n setSelectedMentionSuggestionIndex(index);\n }\n },\n [setSelectedMentionSuggestionIndex, mentionSuggestions]\n );\n\n const additionalProps: AriaAttributes = useMemo(\n () =>\n mentionDraft\n ? {\n role: \"combobox\",\n \"aria-autocomplete\": \"list\",\n \"aria-expanded\": true,\n \"aria-controls\": suggestionsListId,\n \"aria-activedescendant\": suggestionsListItemId(selectedMentionId),\n }\n : hasFloatingToolbarRange\n ? {\n \"aria-haspopup\": true,\n \"aria-controls\": floatingToolbarId,\n }\n : {},\n [\n mentionDraft,\n suggestionsListId,\n suggestionsListItemId,\n selectedMentionId,\n hasFloatingToolbarRange,\n floatingToolbarId,\n ]\n );\n\n useImperativeHandle(forwardedRef, () => {\n return ReactEditor.toDOMNode(editor, editor) as HTMLDivElement;\n }, [editor]);\n\n // Manually focus the editor when `autoFocus` is true\n useLayoutEffect(() => {\n if (autoFocus) {\n focus();\n }\n }, [autoFocus, editor, focus]);\n\n // Manually add a selection in the editor if the selection\n // is still empty after being focused\n useLayoutEffect(() => {\n if (isFocused && editor.selection === null) {\n select();\n }\n }, [editor, select, isFocused]);\n\n const handleMentionSelect = useCallback(\n (mentionId: string) => {\n const mention = mentionSuggestions?.find(\n (mention) => mention.id === mentionId\n );\n\n createMention(mention);\n },\n [createMention, mentionSuggestions]\n );\n\n return (\n <Slate\n editor={editor}\n initialValue={initialEditorValue}\n onChange={handleChange}\n >\n <Editable\n dir={dir}\n enterKeyHint={mentionDraft ? \"enter\" : \"send\"}\n autoCapitalize=\"sentences\"\n aria-label=\"Composer editor\"\n data-focused={isFocused || undefined}\n data-disabled={isDisabled || undefined}\n {...additionalProps}\n {...props}\n readOnly={isDisabled}\n disabled={isDisabled}\n onKeyDown={handleKeyDown}\n onFocus={handleFocus}\n onBlur={handleBlur}\n renderElement={renderElement}\n renderLeaf={ComposerEditorLeaf}\n renderPlaceholder={ComposerEditorPlaceholder}\n />\n {hasResolveMentionSuggestions && (\n <ComposerEditorMentionSuggestionsWrapper\n dir={dir}\n mentionDraft={mentionDraft}\n setMentionDraft={setMentionDraft}\n selectedMentionId={selectedMentionId}\n setSelectedMentionId={setSelectedMentionId}\n mentions={mentionSuggestions}\n id={suggestionsListId}\n itemId={suggestionsListItemId}\n onItemSelect={handleMentionSelect}\n MentionSuggestions={MentionSuggestions}\n />\n )}\n {FloatingToolbar && (\n <ComposerEditorFloatingToolbarWrapper\n dir={dir}\n id={floatingToolbarId}\n hasFloatingToolbarRange={hasFloatingToolbarRange}\n setHasFloatingToolbarRange={setHasFloatingToolbarRange}\n FloatingToolbar={FloatingToolbar}\n />\n )}\n </Slate>\n );\n }\n);\n\nconst MAX_ATTACHMENTS = 10;\nconst MAX_ATTACHMENT_SIZE = 1024 * 1024 * 1024; // 1 GB\n\nfunction prepareAttachment(file: File): CommentLocalAttachment {\n return {\n type: \"localAttachment\",\n status: \"idle\",\n id: createCommentAttachmentId(),\n name: file.name,\n size: file.size,\n mimeType: file.type,\n file,\n };\n}\n\n/**\n * Surrounds the composer's content and handles submissions.\n *\n * @example\n * <Composer.Form onComposerSubmit={({ body }) => {}}>\n *\t <Composer.Editor />\n * <Composer.Submit />\n * </Composer.Form>\n */\nconst ComposerForm = forwardRef<HTMLFormElement, ComposerFormProps>(\n (\n {\n children,\n onSubmit,\n onComposerSubmit,\n defaultAttachments = [],\n pasteFilesAsAttachments,\n blurOnSubmit = true,\n preventUnsavedChanges = true,\n disabled,\n asChild,\n roomId: _roomId,\n ...props\n },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"form\";\n const [isEmpty, setEmpty] = useState(true);\n const [isSubmitting, setSubmitting] = useState(false);\n const [isFocused, setFocused] = useState(false);\n const room = useRoom({ allowOutsideRoom: true });\n\n const roomId = _roomId !== undefined ? _roomId : room?.id;\n if (roomId === undefined) {\n throw new Error(\"Composer.Form must be a descendant of RoomProvider.\");\n }\n\n // Later: Offer as Composer.Form props: { maxAttachments: number; maxAttachmentSize: number; supportedAttachmentMimeTypes: string[]; }\n const maxAttachments = MAX_ATTACHMENTS;\n const maxAttachmentSize = MAX_ATTACHMENT_SIZE;\n\n const {\n attachments,\n isUploadingAttachments,\n addAttachments,\n removeAttachment,\n clearAttachments,\n } = useComposerAttachmentsManager(defaultAttachments, {\n maxFileSize: maxAttachmentSize,\n roomId,\n });\n const numberOfAttachments = attachments.length;\n const hasMaxAttachments = numberOfAttachments >= maxAttachments;\n\n const isDisabled = useMemo(() => {\n return isSubmitting || disabled === true;\n }, [isSubmitting, disabled]);\n const canSubmit = useMemo(() => {\n return !isEmpty && !isUploadingAttachments;\n }, [isEmpty, isUploadingAttachments]);\n const [marks, setMarks] = useState<ComposerBodyMarks>(getComposerBodyMarks);\n\n const ref = useRef<HTMLFormElement>(null);\n const mergedRefs = useRefs(forwardedRef, ref);\n const fileInputRef = useRef<HTMLInputElement>(null);\n const syncSource = useSyncSource();\n\n // Mark the composer as a pending update when it has unsubmitted (draft)\n // text or attachments\n const isPending = !preventUnsavedChanges\n ? false\n : !isEmpty || isUploadingAttachments || attachments.length > 0;\n\n useEffect(() => {\n syncSource?.setSyncStatus(\n isPending ? \"has-local-changes\" : \"synchronized\"\n );\n }, [syncSource, isPending]);\n\n const createAttachments = useCallback(\n (files: File[]) => {\n if (!files.length) {\n return;\n }\n\n const numberOfAcceptedFiles = Math.max(\n 0,\n maxAttachments - numberOfAttachments\n );\n\n files.splice(numberOfAcceptedFiles);\n\n const attachments = files.map((file) => prepareAttachment(file));\n\n addAttachments(attachments);\n },\n [addAttachments, maxAttachments, numberOfAttachments]\n );\n\n const createAttachmentsRef = useRef(createAttachments);\n\n useEffect(() => {\n createAttachmentsRef.current = createAttachments;\n }, [createAttachments]);\n\n const stableCreateAttachments = useCallback((files: File[]) => {\n createAttachmentsRef.current(files);\n }, []);\n\n const editor = useInitial(() =>\n createComposerEditor({\n createAttachments: stableCreateAttachments,\n pasteFilesAsAttachments,\n })\n );\n const onEditorChange = useInitial(makeEventSource) as EventSource<void>;\n\n const validate = useCallback(\n (value: SlateElement[]) => {\n setEmpty(isEditorEmpty(editor, value));\n },\n [editor]\n );\n\n const submit = useCallback(() => {\n if (!canSubmit) {\n return;\n }\n\n // We need to wait for the next frame in some cases like when composing diacritics,\n // we want any native handling to be done first while still being handled on `keydown`.\n requestAnimationFrame(() => {\n if (ref.current) {\n requestSubmit(ref.current);\n }\n });\n }, [canSubmit]);\n\n const clear = useCallback(() => {\n SlateTransforms.delete(editor, {\n at: {\n anchor: SlateEditor.start(editor, []),\n focus: SlateEditor.end(editor, []),\n },\n });\n }, [editor]);\n\n const select = useCallback(() => {\n SlateTransforms.select(editor, SlateEditor.end(editor, []));\n }, [editor]);\n\n const focus = useCallback(\n (resetSelection = true) => {\n try {\n // Slate's `ReactEditor.focus` method can use `setTimeout` internally\n // which prevents us from catching errors, so this is a reimplementation.\n // https://github.com/ianstormtaylor/slate/blob/main/packages/slate-dom/src/plugin/dom-editor.ts\n if (!ReactEditor.isFocused(editor)) {\n SlateTransforms.select(\n editor,\n resetSelection || !editor.selection\n ? SlateEditor.end(editor, [])\n : editor.selection\n );\n\n const element = ReactEditor.toDOMNode(editor, editor);\n\n if (editor.selection) {\n const domSelection = window.getSelection();\n const domRange = getDOMRange(editor, editor.selection);\n\n if (domRange) {\n domSelection?.removeAllRanges();\n domSelection?.addRange(domRange);\n }\n }\n\n element.focus({ preventScroll: true });\n }\n } catch {\n // Slate's DOM-specific methods will throw if the editor's DOM\n // node no longer exists. This action doesn't make sense on an\n // unmounted editor so we can safely ignore it.\n }\n },\n [editor]\n );\n\n const blur = useCallback(() => {\n try {\n ReactEditor.blur(editor);\n } catch {\n // Slate's DOM-specific methods will throw if the editor's DOM\n // node no longer exists. This action doesn't make sense on an\n // unmounted editor so we can safely ignore it.\n }\n }, [editor]);\n\n const createMention = useCallback(() => {\n if (disabled) {\n return;\n }\n\n focus();\n insertMentionCharacter(editor);\n }, [disabled, editor, focus]);\n\n const insertText = useCallback(\n (text: string) => {\n if (disabled) {\n return;\n }\n\n focus(false);\n insertSlateText(editor, text);\n },\n [disabled, editor, focus]\n );\n\n const attachFiles = useCallback(() => {\n if (disabled) {\n return;\n }\n\n if (fileInputRef.current) {\n fileInputRef.current.click();\n }\n }, [disabled]);\n\n const handleAttachmentsInputChange = useCallback(\n (event: ChangeEvent<HTMLInputElement>) => {\n if (disabled) {\n return;\n }\n\n if (event.target.files) {\n createAttachments(Array.from(event.target.files));\n\n // Reset the input value to allow selecting the same file(s) again\n event.target.value = \"\";\n }\n },\n [createAttachments, disabled]\n );\n\n const onSubmitEnd = useCallback(() => {\n clear();\n clearAttachments();\n setSubmitting(false);\n\n if (blurOnSubmit) {\n blur();\n }\n }, [blur, blurOnSubmit, clear, clearAttachments]);\n\n const handleSubmit = useCallback(\n (event: FormEvent<HTMLFormElement>) => {\n if (disabled) {\n return;\n }\n\n // In some situations (e.g. pressing Enter while composing diacritics), it's possible\n // for the form to be submitted as empty even though we already checked whether the\n // editor was empty when handling the key press.\n const isEmpty = isEditorEmpty(editor, editor.children);\n\n // We even prevent the user's `onSubmit` handler from being called if the editor is empty.\n if (isEmpty) {\n event.preventDefault();\n\n return;\n }\n\n onSubmit?.(event);\n\n if (!onComposerSubmit || event.isDefaultPrevented()) {\n event.preventDefault();\n\n return;\n }\n\n const body = composerBodyToCommentBody(\n editor.children as ComposerBodyData\n );\n // Only non-local attachments are included to be submitted.\n const commentAttachments: CommentAttachment[] = attachments\n .filter(\n (attachment) =>\n attachment.type === \"attachment\" ||\n (attachment.type === \"localAttachment\" &&\n attachment.status === \"uploaded\")\n )\n .map((attachment) => {\n return {\n id: attachment.id,\n type: \"attachment\",\n mimeType: attachment.mimeType,\n size: attachment.size,\n name: attachment.name,\n };\n });\n\n const promise = onComposerSubmit(\n { body, attachments: commentAttachments },\n event\n );\n\n event.preventDefault();\n\n if (promise) {\n setSubmitting(true);\n promise.then(onSubmitEnd);\n } else {\n onSubmitEnd();\n }\n },\n [disabled, editor, attachments, onComposerSubmit, onSubmit, onSubmitEnd]\n );\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n const toggleMark = useCallback(\n (mark: ComposerBodyMark) => {\n toggleEditorMark(editor, mark);\n },\n [editor]\n );\n\n useObservable(onEditorChange, () => {\n setMarks(getComposerBodyMarks(editor));\n });\n\n return (\n <ComposerEditorContext.Provider\n value={{\n editor,\n validate,\n setFocused,\n onEditorChange,\n roomId,\n }}\n >\n <ComposerAttachmentsContext.Provider\n value={{\n createAttachments,\n isUploadingAttachments,\n hasMaxAttachments,\n maxAttachments,\n maxAttachmentSize,\n }}\n >\n <ComposerContext.Provider\n value={{\n isDisabled,\n isFocused,\n isEmpty,\n canSubmit,\n submit,\n clear,\n select,\n focus,\n blur,\n createMention,\n insertText,\n attachments,\n attachFiles,\n removeAttachment,\n toggleMark,\n marks,\n }}\n >\n <Component {...props} onSubmit={handleSubmit} ref={mergedRefs}>\n <input\n type=\"file\"\n multiple\n ref={fileInputRef}\n onChange={handleAttachmentsInputChange}\n onClick={stopPropagation}\n tabIndex={-1}\n style={{ display: \"none\" }}\n />\n <Slottable>{children}</Slottable>\n </Component>\n </ComposerContext.Provider>\n </ComposerAttachmentsContext.Provider>\n </ComposerEditorContext.Provider>\n );\n }\n);\n\n/**\n * A button to submit the composer.\n *\n * @example\n * <Composer.Submit>Send</Composer.Submit>\n */\nconst ComposerSubmit = forwardRef<HTMLButtonElement, ComposerSubmitProps>(\n ({ children, disabled, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"button\";\n const { canSubmit, isDisabled: isComposerDisabled } = useComposer();\n const isDisabled = isComposerDisabled || disabled || !canSubmit;\n\n return (\n <Component\n type=\"submit\"\n {...props}\n ref={forwardedRef}\n disabled={isDisabled}\n >\n {children}\n </Component>\n );\n }\n);\n\n/**\n * A button which opens a file picker to create attachments.\n *\n * @example\n * <Composer.AttachFiles>Attach files</Composer.AttachFiles>\n */\nconst ComposerAttachFiles = forwardRef<\n HTMLButtonElement,\n ComposerAttachFilesProps\n>(({ children, onClick, disabled, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"button\";\n const { hasMaxAttachments } = useComposerAttachmentsContext();\n const { isDisabled: isComposerDisabled, attachFiles } = useComposer();\n const isDisabled = isComposerDisabled || hasMaxAttachments || disabled;\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n\n if (!event.isDefaultPrevented()) {\n attachFiles();\n }\n },\n [attachFiles, onClick]\n );\n\n return (\n <Component\n type=\"button\"\n {...props}\n onClick={handleClick}\n ref={forwardedRef}\n disabled={isDisabled}\n >\n {children}\n </Component>\n );\n});\n\n/**\n * A drop area which accepts files to create attachments.\n *\n * @example\n * <Composer.AttachmentsDropArea>\n * Drop files here\n * </Composer.AttachmentsDropArea>\n */\nconst ComposerAttachmentsDropArea = forwardRef<\n HTMLDivElement,\n ComposerAttachmentsDropAreaProps\n>(\n (\n {\n onDragEnter,\n onDragLeave,\n onDragOver,\n onDrop,\n disabled,\n asChild,\n ...props\n },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"div\";\n const { isDisabled: isComposerDisabled } = useComposer();\n const isDisabled = isComposerDisabled || disabled;\n const [, dropAreaProps] = useComposerAttachmentsDropArea({\n onDragEnter,\n onDragLeave,\n onDragOver,\n onDrop,\n disabled: isDisabled,\n });\n\n return (\n <Component\n {...dropAreaProps}\n data-disabled={isDisabled ? \"\" : undefined}\n {...props}\n ref={forwardedRef}\n />\n );\n }\n);\n\n/**\n * A toggle button which toggles a specific text mark.\n *\n * @example\n * <Composer.MarkToggle mark=\"bold\">\n * Bold\n * </Composer.MarkToggle>\n */\nconst ComposerMarkToggle = forwardRef<\n HTMLButtonElement,\n ComposerMarkToggleProps\n>(\n (\n {\n children,\n mark,\n onValueChange,\n onClick,\n onPointerDown,\n asChild,\n ...props\n },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"button\";\n const { marks, toggleMark } = useComposer();\n\n const handlePointerDown = useCallback(\n (event: PointerEvent<HTMLButtonElement>) => {\n onPointerDown?.(event);\n\n event.preventDefault();\n event.stopPropagation();\n },\n [onPointerDown]\n );\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n\n if (!event.isDefaultPrevented()) {\n event.preventDefault();\n event.stopPropagation();\n\n toggleMark(mark);\n onValueChange?.(mark);\n }\n },\n [mark, onClick, onValueChange, toggleMark]\n );\n\n return (\n <TogglePrimitive.Root\n asChild\n pressed={marks[mark]}\n onClick={handleClick}\n onPointerDown={handlePointerDown}\n {...props}\n >\n <Component {...props} ref={forwardedRef}>\n {children}\n </Component>\n </TogglePrimitive.Root>\n );\n }\n);\n\nif (process.env.NODE_ENV !== \"production\") {\n ComposerAttachFiles.displayName = COMPOSER_ATTACH_FILES_NAME;\n ComposerAttachmentsDropArea.displayName = COMPOSER_ATTACHMENTS_DROP_AREA_NAME;\n ComposerEditor.displayName = COMPOSER_EDITOR_NAME;\n ComposerFloatingToolbar.displayName = COMPOSER_FLOATING_TOOLBAR_NAME;\n ComposerForm.displayName = COMPOSER_FORM_NAME;\n ComposerMention.displayName = COMPOSER_MENTION_NAME;\n ComposerLink.displayName = COMPOSER_LINK_NAME;\n ComposerSubmit.displayName = COMPOSER_SUBMIT_NAME;\n ComposerSuggestions.displayName = COMPOSER_SUGGESTIONS_NAME;\n ComposerSuggestionsList.displayName = COMPOSER_SUGGESTIONS_LIST_NAME;\n ComposerSuggestionsListItem.displayName = COMPOSER_SUGGESTIONS_LIST_ITEM_NAME;\n ComposerMarkToggle.displayName = COMPOSER_MARK_TOGGLE_NAME;\n}\n\n// NOTE: Every export from this file will be available publicly as Composer.*\nexport {\n ComposerAttachFiles as AttachFiles,\n ComposerAttachmentsDropArea as AttachmentsDropArea,\n ComposerEditor as Editor,\n ComposerFloatingToolbar as FloatingToolbar,\n ComposerForm as Form,\n ComposerLink as Link,\n ComposerMarkToggle as MarkToggle,\n ComposerMention as Mention,\n ComposerSubmit as Submit,\n ComposerSuggestions as Suggestions,\n ComposerSuggestionsList as SuggestionsList,\n ComposerSuggestionsListItem as SuggestionsListItem,\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsJA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAsC;AAC3B;AAEX;AAEA;AAA8B;AAC5B;AAEF;AAIE;AAAO;AACL;AACE;AACE;AACE;AACE;AACoD;AAChD;AACA;AACD;AACH;AACF;AACF;AACF;AACF;AAEJ;AAEA;AAAsC;AACpC;AACA;AACA;AAEF;AACE;AACA;AAEA;AAEK;AAEG;AACH;AAGP;AAEA;AAAmC;AACjC;AACA;AACA;AAEF;AACE;AAEA;AAKF;AAEA;AAAiD;AAC/C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACW;AACX;AAEF;AACE;AACA;AACA;AACA;AACA;AAEA;AAAM;AAC8B;AAClC;AACA;AACA;AACA;AACA;AACA;AACA;AACyB;AACzB;AACA;AACW;AACL;AAGR;AACE;AAAkD;AAGpD;AACE;AACE;AAEA;AAAA;AAGF;AACA;AAA6B;AAK/B;AACE;AAAa;AAEb;AAIA;AACE;AAAA;AAIF;AACA;AAGA;AAGA;AACE;AACA;AACE;AAGJ;AACE;AAAmC;AACrC;AAGF;AAGM;AAA4B;AAA3B;AACQ;AACL;AACA;AACe;AACG;AAClB;AACA;AACA;AACK;AACP;AAEA;AAAC;AAAA;AACM;AACE;AACK;AACL;AACC;AAGF;AACM;AACF;AACV;AAEA;AAAC;AAAA;AACC;AACA;AAAA;AACF;AAAA;AACF;AAAA;AAKV;AAEA;AAA8C;AAC5C;AACW;AACX;AACA;AACA;AAEF;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AAAM;AAC8B;AAClC;AACA;AACA;AACA;AACA;AACyB;AACnB;AACN;AACA;AACW;AACL;AAGR;AACE;AACE;AAAA;AAGF;AACA;AAEA;AACA;AAEA;AACE;AACA;AAAyD;AAC3D;AAGF;AAEE;AAGA;AACE;AAGA;AAME;AACA;AAAiB;AAEjB;AAEA;AACA;AAAqB;AACvB;AACD;AAGH;AAGM;AAAgC;AAA/B;AACQ;AACL;AACA;AACA;AACK;AACP;AAEA;AAAC;AAAA;AACM;AACE;AACK;AACL;AACC;AAGF;AACM;AACF;AACV;AAEiB;AAAA;AACnB;AAAA;AAKV;AAWM;AAIJ;AACA;AACA;AAAM;AACJ;AACK;AACL;AACA;AAEF;AACA;AAAsB;AACgC;AAC1C;AAEZ;AACA;AAEA;AAA0B;AAEtB;AAEA;AACA;AAAsB;AACxB;AACc;AAGhB;AACE;AAAC;AAAA;AACC;AACK;AACL;AACW;AACP;AACW;AACkB;AACtB;AACC;AACL;AACI;AACM;AACL;AACC;AACR;AACL;AACK;AAEJ;AAAA;AAGP;AAEA;AAA+B;AAC7B;AACA;AAEF;AAEE;AAEA;AAAsB;AAElB;AACE;AAAC;AAAA;AACC;AACK;AAAA;AACP;AAEC;AAEH;AACE;AAAC;AAAA;AACC;AACK;AAAA;AAGP;AAGF;AAGE;AAGF;AAAO;AAEb;AAGA;AAA4B;AAC1B;AACA;AAEF;AACE;AACE;AAA6B;AAG/B;AACE;AAAyB;AAG3B;AACE;AAAwB;AAG1B;AACE;AAA2B;AAG7B;AACF;AAEA;AAAmC;AACjC;AAEF;AACE;AAEA;AAKF;AAQA;AAAwB;AAEpB;AACA;AAEA;AACE;AAAC;AAAA;AAC8B;AACzB;AACC;AAEJ;AAAA;AACH;AAGN;AAQA;AAAqB;AAEjB;AAEA;AACE;AAAC;AAAA;AACQ;AACH;AACA;AACC;AAEJ;AAAA;AACH;AAGN;AAKM;AAIJ;AACA;AACA;AAAM;AACC;AACL;AACA;AAEF;AACA;AAAsB;AACgC;AAC1C;AAEZ;AACA;AAEA;AACE;AAAC;AAAA;AACC;AACI;AAC6B;AACtB;AACC;AACL;AACI;AACM;AACJ;AACA;AACR;AACL;AACK;AAEJ;AAAA;AAGP;AAcM;AAIJ;AACA;AAEA;AACE;AAAC;AAAA;AACM;AACL;AACW;AACP;AACC;AAEJ;AAAA;AAGP;AAUA;AAAoC;AAKhC;AACE;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AAEA;AACA;AAAmB;AACO;AACH;AAGvB;AAEA;AACE;AACE;AAA+C;AACjD;AAGF;AAA0B;AAEtB;AAEA;AACE;AAAsB;AACxB;AACF;AACuC;AAGzC;AAA0B;AAEtB;AAEA;AACA;AAAsB;AACxB;AACc;AAGhB;AAAoB;AAEhB;AAEA;AAEA;AACA;AAEA;AACE;AAAkB;AACpB;AACF;AAC6B;AAG/B;AACE;AAAC;AAAA;AACM;AACL;AAC6B;AACA;AACd;AACA;AACN;AACL;AACC;AAEJ;AAAA;AACH;AAGN;AAEA;AAA0D;AAEtD;AAA2C;AAC7C;AAEE;AAEK;AAAA;AACQ;AACX;AAEJ;AAEE;AAUI;AAER;AAQA;AAAuB;AAEnB;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AAEA;AAAM;AACJ;AACA;AACA;AACA;AACA;AACY;AACZ;AAEF;AACA;AACA;AACE;AAA4C;AAE9C;AAA+D;AACV;AACxC;AAGb;AAIA;AACA;AAGA;AACA;AAA2B;AACzB;AACc;AAEhB;AAAM;AACJ;AACA;AACA;AACA;AAEF;AACA;AACA;AACA;AAA8B;AAItB;AACH;AAGL;AAAsB;AAElB;AACkE;AAEpE;AACc;AAGhB;AAAqB;AAEjB;AAKA;AAAsB;AACxB;AACyB;AAG3B;AAAsB;AAElB;AACE;AAAA;AAGF;AACA;AACA;AACA;AAAmC;AACrC;AACwD;AAG1D;AAAsB;AAElB;AAEA;AACE;AAAA;AAIF;AACE;AAA6B;AAI/B;AACE;AAA2B;AAG7B;AAEE;AACE;AACA;AAAsC;AAIxC;AACE;AACA;AAA0C;AAI5C;AACE;AAEA;AAEA;AAAqB;AAIvB;AACE;AACA;AACA;AAAmC;AACrC;AAEA;AAEE;AACE;AACA;AAAgC;AAClC;AAIF;AACE;AAAK;AAIP;AAEE;AAEA;AACE;AAAO;AACT;AAIF;AACE;AACA;AAAmB;AAIrB;AACE;AACA;AAA+B;AAIjC;AACE;AACA;AAAiC;AAInC;AACE;AACA;AAAwC;AAI1C;AACE;AACA;AAA+B;AACjC;AACF;AACF;AACA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;AAGF;AAAoB;AAEhB;AAEA;AACE;AAAe;AACjB;AACF;AACoB;AAGtB;AAAmB;AAEf;AAEA;AACE;AAAgB;AAClB;AACF;AACmB;AAGrB;AAEA;AACA;AAA6B;AAEzB;AAAkC;AACJ;AAG9B;AACE;AAAuC;AACzC;AACF;AACsD;AAGxD;AAAwC;AAGhC;AACQ;AACe;AACJ;AACA;AAC+C;AAGhE;AACmB;AACA;AAElB;AACT;AACE;AACA;AACA;AACA;AACA;AACA;AACF;AAGF;AACE;AAA2C;AAI7C;AACE;AACE;AAAM;AACR;AAKF;AACE;AACE;AAAO;AACT;AAGF;AAA4B;AAExB;AAAoC;AACN;AAG9B;AAAqB;AACvB;AACkC;AAGpC;AACE;AAAC;AAAA;AACC;AACc;AACJ;AAEV;AAAA;AAAC;AAAA;AACC;AACuC;AACxB;AACJ;AACgB;AACE;AACzB;AACA;AACM;AACA;AACC;AACF;AACD;AACR;AACY;AACO;AAAA;AACrB;AAEE;AAAC;AAAA;AACC;AACA;AACA;AACA;AACA;AACU;AACN;AACI;AACM;AACd;AAAA;AACF;AAGA;AAAC;AAAA;AACC;AACI;AACJ;AACA;AACA;AAAA;AACF;AAAA;AAAA;AAEJ;AAGN;AAEA;AACA;AAEA;AACE;AAAO;AACC;AACE;AACsB;AACnB;AACA;AACI;AACf;AAEJ;AAWA;AAAqB;AAEjB;AACE;AACA;AACA;AACsB;AACtB;AACe;AACS;AACxB;AACA;AACQ;AACL;AAIL;AACA;AACA;AACA;AACA;AAEA;AACA;AACE;AAAqE;AAIvE;AACA;AAEA;AAAM;AACJ;AACA;AACA;AACA;AACA;AACoD;AACvC;AACb;AAEF;AACA;AAEA;AACE;AAAoC;AAEtC;AACE;AAAoB;AAEtB;AAEA;AACA;AACA;AACA;AAIA;AAIA;AACE;AAAY;AACwB;AACpC;AAGF;AAA0B;AAEtB;AACE;AAAA;AAGF;AAAmC;AACjC;AACiB;AAGnB;AAEA;AAEA;AAA0B;AAC5B;AACoD;AAGtD;AAEA;AACE;AAA+B;AAGjC;AACE;AAAkC;AAGpC;AAAe;AACQ;AACA;AACnB;AACD;AAEH;AAEA;AAAiB;AAEb;AAAqC;AACvC;AACO;AAGT;AACE;AACE;AAAA;AAKF;AACE;AACE;AAAyB;AAC3B;AACD;AAGH;AACE;AAA+B;AACzB;AACkC;AACH;AACnC;AACD;AAGH;AACE;AAA0D;AAG5D;AAAc;AAEV;AAIE;AACE;AAAgB;AACd;AAGW;AAGb;AAEA;AACE;AACA;AAEA;AACE;AACA;AAA+B;AACjC;AAGF;AAAqC;AACvC;AACM;AAIR;AACF;AACO;AAGT;AACE;AACE;AAAuB;AACjB;AAIR;AAGF;AACE;AACE;AAAA;AAGF;AACA;AAA6B;AAG/B;AAAmB;AAEf;AACE;AAAA;AAGF;AACA;AAA4B;AAC9B;AACwB;AAG1B;AACE;AACE;AAAA;AAGF;AACE;AAA2B;AAC7B;AAGF;AAAqC;AAEjC;AACE;AAAA;AAGF;AACE;AAGA;AAAqB;AACvB;AACF;AAC4B;AAG9B;AACE;AACA;AACA;AAEA;AACE;AAAK;AACP;AAGF;AAAqB;AAEjB;AACE;AAAA;AAMF;AAGA;AACE;AAEA;AAAA;AAGF;AAEA;AACE;AAEA;AAAA;AAGF;AAAa;AACJ;AAGT;AACG;AAI2B;AAG1B;AAAO;AACU;AACT;AACe;AACJ;AACA;AACnB;AAGJ;AAAgB;AAC0B;AACxC;AAGF;AAEA;AACE;AACA;AAAwB;AAExB;AAAY;AACd;AACF;AACuE;AAGzE;AACE;AAAsB;AAGxB;AAAmB;AAEf;AAA6B;AAC/B;AACO;AAGT;AACE;AAAqC;AAGvC;AACE;AAAuB;AAAtB;AACQ;AACL;AACA;AACA;AACA;AACA;AACF;AAEA;AAA4B;AAA3B;AACQ;AACL;AACA;AACA;AACA;AACA;AACF;AAEA;AAAiB;AAAhB;AACQ;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;AAGE;AAAA;AAAC;AAAA;AACM;AACG;AACH;AACK;AACD;AACC;AACe;AAAA;AAC3B;AACqB;AACvB;AAAA;AACF;AAAA;AACF;AAAA;AACF;AAGN;AAQA;AAAuB;AAEnB;AACA;AACA;AAEA;AACE;AAAC;AAAA;AACM;AACD;AACC;AACK;AAET;AAAA;AACH;AAGN;AAQM;AAIJ;AACA;AACA;AACA;AAEA;AAAoB;AAEhB;AAEA;AACE;AAAY;AACd;AACF;AACqB;AAGvB;AACE;AAAC;AAAA;AACM;AACD;AACK;AACJ;AACK;AAET;AAAA;AAGP;AAUA;AAAoC;AAKhC;AACE;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AAAyD;AACvD;AACA;AACA;AACA;AACU;AAGZ;AACE;AAAC;AAAA;AACK;AAC6B;AAC7B;AACC;AAAA;AACP;AAGN;AAUA;AAA2B;AAKvB;AACE;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AAEA;AAA0B;AAEtB;AAEA;AACA;AAAsB;AACxB;AACc;AAGhB;AAAoB;AAEhB;AAEA;AACE;AACA;AAEA;AACA;AAAoB;AACtB;AACF;AACyC;AAG3C;AACE;AAAiB;AAAhB;AACQ;AACY;AACV;AACM;AACX;AAIJ;AAAA;AACF;AAGN;AAEA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":["../../../src/primitives/Composer/index.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n type CommentAttachment,\n type CommentBody,\n type CommentLocalAttachment,\n createCommentAttachmentId,\n type EventSource,\n makeEventSource,\n MENTION_CHARACTER,\n type MentionData,\n sanitizeUrl,\n} from \"@liveblocks/core\";\nimport { useRoom } from \"@liveblocks/react\";\nimport {\n useClientOrNull,\n useLayoutEffect,\n useMentionSuggestions,\n useResolveMentionSuggestions,\n useSyncSource,\n} from \"@liveblocks/react/_private\";\nimport { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport * as TogglePrimitive from \"@radix-ui/react-toggle\";\nimport type {\n AriaAttributes,\n ChangeEvent,\n FocusEvent,\n FormEvent,\n KeyboardEvent,\n MouseEvent,\n PointerEvent,\n SyntheticEvent,\n} from \"react\";\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type {\n Descendant as SlateDescendant,\n Element as SlateElement,\n} from \"slate\";\nimport {\n createEditor,\n Editor as SlateEditor,\n insertText as insertSlateText,\n Range as SlateRange,\n Transforms as SlateTransforms,\n} from \"slate\";\nimport { withHistory } from \"slate-history\";\nimport type {\n RenderElementProps,\n RenderElementSpecificProps,\n RenderLeafSpecificProps,\n RenderPlaceholderProps,\n} from \"slate-react\";\nimport {\n Editable,\n ReactEditor,\n Slate,\n useSelected,\n useSlateStatic,\n withReact,\n} from \"slate-react\";\n\nimport type {\n ComposerBody as ComposerBodyData,\n ComposerBodyAutoLink,\n ComposerBodyCustomLink,\n ComposerBodyMark,\n ComposerBodyMarks,\n ComposerBodyMention,\n ComposerBodyText,\n} from \"../../types\";\nimport { isKey } from \"../../utils/is-key\";\nimport { Persist, useAnimationPersist, usePersist } from \"../../utils/Persist\";\nimport { Portal } from \"../../utils/Portal\";\nimport { requestSubmit } from \"../../utils/request-submit\";\nimport { useIndex } from \"../../utils/use-index\";\nimport { useInitial } from \"../../utils/use-initial\";\nimport { useObservable } from \"../../utils/use-observable\";\nimport { useRefs } from \"../../utils/use-refs\";\nimport { withEmptyClearFormatting } from \"../slate/plugins/empty-clear-formatting\";\nimport { withNormalize } from \"../slate/plugins/normalize\";\nimport { getDOMRange } from \"../slate/utils/get-dom-range\";\nimport { isEmpty as isEditorEmpty } from \"../slate/utils/is-empty\";\nimport {\n getComposerBodyMarks,\n leaveMarkEdge,\n toggleMark as toggleEditorMark,\n} from \"../slate/utils/marks\";\nimport {\n ComposerAttachmentsContext,\n ComposerContext,\n ComposerEditorContext,\n ComposerFloatingToolbarContext,\n ComposerSuggestionsContext,\n useComposer,\n useComposerAttachmentsContext,\n useComposerEditorContext,\n useComposerFloatingToolbarContext,\n useComposerSuggestionsContext,\n} from \"./contexts\";\nimport { withAutoFormatting } from \"./slate/plugins/auto-formatting\";\nimport { withAutoLinks } from \"./slate/plugins/auto-links\";\nimport { withCustomLinks } from \"./slate/plugins/custom-links\";\nimport type { MentionDraft } from \"./slate/plugins/mentions\";\nimport {\n getMentionDraftAtSelection,\n insertMention,\n insertMentionCharacter,\n withMentions,\n} from \"./slate/plugins/mentions\";\nimport { withPaste } from \"./slate/plugins/paste\";\nimport type {\n ComposerAttachFilesProps,\n ComposerAttachmentsDropAreaProps,\n ComposerEditorComponents,\n ComposerEditorElementProps,\n ComposerEditorFloatingToolbarWrapperProps,\n ComposerEditorLinkWrapperProps,\n ComposerEditorMentionSuggestionsWrapperProps,\n ComposerEditorMentionWrapperProps,\n ComposerEditorProps,\n ComposerFloatingToolbarProps,\n ComposerFormProps,\n ComposerLinkProps,\n ComposerMarkToggleProps,\n ComposerMentionProps,\n ComposerSubmitProps,\n ComposerSuggestionsListItemProps,\n ComposerSuggestionsListProps,\n ComposerSuggestionsProps,\n FloatingPosition,\n} from \"./types\";\nimport {\n commentBodyToComposerBody,\n composerBodyToCommentBody,\n getSideAndAlignFromFloatingPlacement,\n useComposerAttachmentsDropArea,\n useComposerAttachmentsManager,\n useContentZIndex,\n useFloatingWithOptions,\n} from \"./utils\";\n\nconst MENTION_SUGGESTIONS_POSITION: FloatingPosition = \"top\";\n\nconst FLOATING_TOOLBAR_POSITION: FloatingPosition = \"top\";\n\nconst COMPOSER_MENTION_NAME = \"ComposerMention\";\nconst COMPOSER_LINK_NAME = \"ComposerLink\";\nconst COMPOSER_FLOATING_TOOLBAR_NAME = \"ComposerFloatingToolbar\";\nconst COMPOSER_SUGGESTIONS_NAME = \"ComposerSuggestions\";\nconst COMPOSER_SUGGESTIONS_LIST_NAME = \"ComposerSuggestionsList\";\nconst COMPOSER_SUGGESTIONS_LIST_ITEM_NAME = \"ComposerSuggestionsListItem\";\nconst COMPOSER_SUBMIT_NAME = \"ComposerSubmit\";\nconst COMPOSER_EDITOR_NAME = \"ComposerEditor\";\nconst COMPOSER_ATTACH_FILES_NAME = \"ComposerAttachFiles\";\nconst COMPOSER_ATTACHMENTS_DROP_AREA_NAME = \"ComposerAttachmentsDropArea\";\nconst COMPOSER_MARK_TOGGLE_NAME = \"ComposerMarkToggle\";\nconst COMPOSER_FORM_NAME = \"ComposerForm\";\n\nconst emptyCommentBody: CommentBody = {\n version: 1,\n content: [{ type: \"paragraph\", children: [{ text: \"\" }] }],\n};\n\nfunction createComposerEditor({\n createAttachments,\n pasteFilesAsAttachments,\n}: {\n createAttachments: (files: File[]) => void;\n pasteFilesAsAttachments?: boolean;\n}) {\n return withNormalize(\n withMentions(\n withCustomLinks(\n withAutoLinks(\n withAutoFormatting(\n withEmptyClearFormatting(\n withPaste(withHistory(withReact(createEditor())), {\n createAttachments,\n pasteFilesAsAttachments,\n })\n )\n )\n )\n )\n )\n );\n}\n\nfunction ComposerEditorMentionWrapper({\n Mention,\n attributes,\n children,\n element,\n}: ComposerEditorMentionWrapperProps) {\n const isSelected = useSelected();\n const { children: _, ...mention } = element;\n\n return (\n <span {...attributes}>\n {element.id ? (\n <Mention mention={mention} isSelected={isSelected} />\n ) : null}\n {children}\n </span>\n );\n}\n\nfunction ComposerEditorLinkWrapper({\n Link,\n attributes,\n element,\n children,\n}: ComposerEditorLinkWrapperProps) {\n const href = useMemo(() => sanitizeUrl(element.url) ?? \"\", [element.url]);\n\n return (\n <span {...attributes}>\n <Link href={href}>{children}</Link>\n </span>\n );\n}\n\nfunction ComposerEditorMentionSuggestionsWrapper({\n id,\n itemId,\n mentions,\n selectedMentionId,\n setSelectedMentionId,\n mentionDraft,\n setMentionDraft,\n onItemSelect,\n position = MENTION_SUGGESTIONS_POSITION,\n dir,\n MentionSuggestions,\n}: ComposerEditorMentionSuggestionsWrapperProps) {\n const editor = useSlateStatic();\n const { onEditorChange } = useComposerEditorContext();\n const { isFocused } = useComposer();\n const [contentRef, contentZIndex] = useContentZIndex();\n const isOpen =\n isFocused && mentionDraft?.range !== undefined && mentions !== undefined;\n const {\n refs: { setReference, setFloating },\n strategy,\n isPositioned,\n placement,\n x,\n y,\n update,\n elements,\n } = useFloatingWithOptions({\n position,\n dir,\n alignment: \"start\",\n open: isOpen,\n });\n\n useObservable(onEditorChange, () => {\n setMentionDraft(getMentionDraftAtSelection(editor));\n });\n\n useLayoutEffect(() => {\n if (!mentionDraft) {\n setReference(null);\n\n return;\n }\n\n const domRange = getDOMRange(editor, mentionDraft.range);\n setReference(domRange ?? null);\n }, [setReference, editor, mentionDraft]);\n\n // Manually update the placement when the number of suggestions changes\n // This can prevent the list of suggestions from scrolling instead of moving to the other placement\n useLayoutEffect(() => {\n if (!isOpen) return;\n\n const mentionSuggestions = elements.floating?.firstChild as\n | HTMLElement\n | undefined;\n\n if (!mentionSuggestions) {\n return;\n }\n\n // Force the mention suggestions to grow instead of scrolling\n mentionSuggestions.style.overflowY = \"visible\";\n mentionSuggestions.style.maxHeight = \"none\";\n\n // Trigger a placement update\n update();\n\n // Reset the mention suggestions after the placement update\n const animationFrame = requestAnimationFrame(() => {\n mentionSuggestions.style.overflowY = \"auto\";\n mentionSuggestions.style.maxHeight =\n \"var(--lb-composer-floating-available-height)\";\n });\n\n return () => {\n cancelAnimationFrame(animationFrame);\n };\n }, [mentions?.length, isOpen, elements.floating, update]);\n\n return (\n <Persist>\n {isOpen ? (\n <ComposerSuggestionsContext.Provider\n value={{\n id,\n itemId,\n selectedValue: selectedMentionId,\n setSelectedValue: setSelectedMentionId,\n onItemSelect,\n placement,\n dir,\n ref: contentRef,\n }}\n >\n <Portal\n ref={setFloating}\n style={{\n position: strategy,\n top: 0,\n left: 0,\n transform: isPositioned\n ? `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`\n : \"translate3d(0, -200%, 0)\",\n minWidth: \"max-content\",\n zIndex: contentZIndex,\n }}\n >\n <MentionSuggestions\n mentions={mentions}\n selectedMentionId={selectedMentionId}\n />\n </Portal>\n </ComposerSuggestionsContext.Provider>\n ) : null}\n </Persist>\n );\n}\n\nfunction ComposerEditorFloatingToolbarWrapper({\n id,\n position = FLOATING_TOOLBAR_POSITION,\n dir,\n FloatingToolbar,\n hasFloatingToolbarRange,\n setHasFloatingToolbarRange,\n}: ComposerEditorFloatingToolbarWrapperProps) {\n const editor = useSlateStatic();\n const { onEditorChange } = useComposerEditorContext();\n const { isFocused } = useComposer();\n const [contentRef, contentZIndex] = useContentZIndex();\n const [isPointerDown, setPointerDown] = useState(false);\n const isOpen = isFocused && !isPointerDown && hasFloatingToolbarRange;\n const {\n refs: { setReference, setFloating },\n strategy,\n isPositioned,\n placement,\n x,\n y,\n } = useFloatingWithOptions({\n type: \"range\",\n position,\n dir,\n alignment: \"center\",\n open: isOpen,\n });\n\n useLayoutEffect(() => {\n if (!isFocused) {\n return;\n }\n\n const handlePointerDown = () => setPointerDown(true);\n const handlePointerUp = () => setPointerDown(false);\n\n document.addEventListener(\"pointerdown\", handlePointerDown);\n document.addEventListener(\"pointerup\", handlePointerUp);\n\n return () => {\n document.removeEventListener(\"pointerdown\", handlePointerDown);\n document.removeEventListener(\"pointerup\", handlePointerUp);\n };\n }, [isFocused]);\n\n useObservable(onEditorChange, () => {\n // Detach from previous selection range (if any) to avoid sudden jumps\n setReference(null);\n\n // Then, wait for the next render to ensure the selection is updated\n requestAnimationFrame(() => {\n const domSelection = window.getSelection();\n\n // Finally, show the toolbar if there's a selection range\n if (\n !editor.selection ||\n SlateRange.isCollapsed(editor.selection) ||\n !domSelection ||\n !domSelection.rangeCount\n ) {\n setHasFloatingToolbarRange(false);\n setReference(null);\n } else {\n setHasFloatingToolbarRange(true);\n\n const domRange = domSelection.getRangeAt(0);\n setReference(domRange);\n }\n });\n });\n\n return (\n <Persist>\n {isOpen ? (\n <ComposerFloatingToolbarContext.Provider\n value={{\n id,\n placement,\n dir,\n ref: contentRef,\n }}\n >\n <Portal\n ref={setFloating}\n style={{\n position: strategy,\n top: 0,\n left: 0,\n transform: isPositioned\n ? `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`\n : \"translate3d(0, -200%, 0)\",\n minWidth: \"max-content\",\n zIndex: contentZIndex,\n }}\n >\n <FloatingToolbar />\n </Portal>\n </ComposerFloatingToolbarContext.Provider>\n ) : null}\n </Persist>\n );\n}\n\n/**\n * Displays a floating toolbar attached to the selection within `Composer.Editor`.\n *\n * @example\n * <Composer.FloatingToolbar>\n * <Composer.MarkToggle mark=\"bold\">Bold</Composer.MarkToggle>\n * <Composer.MarkToggle mark=\"italic\">Italic</Composer.MarkToggle>\n * </Composer.FloatingToolbar>\n */\nconst ComposerFloatingToolbar = forwardRef<\n HTMLDivElement,\n ComposerFloatingToolbarProps\n>(({ children, onPointerDown, style, asChild, ...props }, forwardedRef) => {\n const [isPresent] = usePersist();\n const ref = useRef<HTMLDivElement>(null);\n const {\n id,\n ref: contentRef,\n placement,\n dir,\n } = useComposerFloatingToolbarContext(COMPOSER_FLOATING_TOOLBAR_NAME);\n const mergedRefs = useRefs(forwardedRef, contentRef, ref);\n const [side, align] = useMemo(\n () => getSideAndAlignFromFloatingPlacement(placement),\n [placement]\n );\n const Component = asChild ? Slot : \"div\";\n useAnimationPersist(ref);\n\n const handlePointerDown = useCallback(\n (event: PointerEvent<HTMLDivElement>) => {\n onPointerDown?.(event);\n\n event.preventDefault();\n event.stopPropagation();\n },\n [onPointerDown]\n );\n\n return (\n <Component\n dir={dir}\n role=\"toolbar\"\n id={id}\n aria-label=\"Floating toolbar\"\n {...props}\n onPointerDown={handlePointerDown}\n data-state={isPresent ? \"open\" : \"closed\"}\n data-side={side}\n data-align={align}\n style={{\n display: \"flex\",\n flexDirection: \"row\",\n maxWidth: \"var(--lb-composer-floating-available-width)\",\n overflowX: \"auto\",\n ...style,\n }}\n ref={mergedRefs}\n >\n {children}\n </Component>\n );\n});\n\nfunction ComposerEditorElement({\n Mention,\n Link,\n ...props\n}: ComposerEditorElementProps) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const { attributes, children, element } = props;\n\n switch (element.type) {\n case \"mention\":\n return (\n <ComposerEditorMentionWrapper\n Mention={Mention}\n {...(props as RenderElementSpecificProps<ComposerBodyMention>)}\n />\n );\n case \"auto-link\":\n case \"custom-link\":\n return (\n <ComposerEditorLinkWrapper\n Link={Link}\n {...(props as RenderElementSpecificProps<\n ComposerBodyAutoLink | ComposerBodyCustomLink\n >)}\n />\n );\n case \"paragraph\":\n return (\n <p {...attributes} style={{ position: \"relative\" }}>\n {children}\n </p>\n );\n default:\n return null;\n }\n}\n\n// <code><s><em><strong>text</strong></s></em></code>\nfunction ComposerEditorLeaf({\n attributes,\n children,\n leaf,\n}: RenderLeafSpecificProps<ComposerBodyText>) {\n if (leaf.bold) {\n children = <strong>{children}</strong>;\n }\n\n if (leaf.italic) {\n children = <em>{children}</em>;\n }\n\n if (leaf.strikethrough) {\n children = <s>{children}</s>;\n }\n\n if (leaf.code) {\n children = <code>{children}</code>;\n }\n\n return <span {...attributes}>{children}</span>;\n}\n\nfunction ComposerEditorPlaceholder({\n attributes,\n children,\n}: RenderPlaceholderProps) {\n const { opacity: _opacity, ...style } = attributes.style;\n\n return (\n <span {...attributes} style={style} data-placeholder=\"\">\n {children}\n </span>\n );\n}\n\n/**\n * Displays mentions within `Composer.Editor`.\n *\n * @example\n * <Composer.Mention>@{mention.id}</Composer.Mention>\n */\nconst ComposerMention = forwardRef<HTMLSpanElement, ComposerMentionProps>(\n ({ children, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"span\";\n const isSelected = useSelected();\n\n return (\n <Component\n data-selected={isSelected || undefined}\n {...props}\n ref={forwardedRef}\n >\n {children}\n </Component>\n );\n }\n);\n\n/**\n * Displays links within `Composer.Editor`.\n *\n * @example\n * <Composer.Link href={href}>{children}</Composer.Link>\n */\nconst ComposerLink = forwardRef<HTMLAnchorElement, ComposerLinkProps>(\n ({ children, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"a\";\n\n return (\n <Component\n target=\"_blank\"\n rel=\"noopener noreferrer nofollow\"\n {...props}\n ref={forwardedRef}\n >\n {children}\n </Component>\n );\n }\n);\n\n/**\n * Contains suggestions within `Composer.Editor`.\n */\nconst ComposerSuggestions = forwardRef<\n HTMLDivElement,\n ComposerSuggestionsProps\n>(({ children, style, asChild, ...props }, forwardedRef) => {\n const [isPresent] = usePersist();\n const ref = useRef<HTMLDivElement>(null);\n const {\n ref: contentRef,\n placement,\n dir,\n } = useComposerSuggestionsContext(COMPOSER_SUGGESTIONS_NAME);\n const mergedRefs = useRefs(forwardedRef, contentRef, ref);\n const [side, align] = useMemo(\n () => getSideAndAlignFromFloatingPlacement(placement),\n [placement]\n );\n const Component = asChild ? Slot : \"div\";\n useAnimationPersist(ref);\n\n return (\n <Component\n dir={dir}\n {...props}\n data-state={isPresent ? \"open\" : \"closed\"}\n data-side={side}\n data-align={align}\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n maxHeight: \"var(--lb-composer-floating-available-height)\",\n overflowY: \"auto\",\n ...style,\n }}\n ref={mergedRefs}\n >\n {children}\n </Component>\n );\n});\n\n/**\n * Displays a list of suggestions within `Composer.Editor`.\n *\n * @example\n * <Composer.SuggestionsList>\n * {mentions.map((mention) => (\n * <Composer.SuggestionsListItem key={mention.id} value={mention.id}>\n * @{mention.id}\n * </Composer.SuggestionsListItem>\n * ))}\n * </Composer.SuggestionsList>\n */\nconst ComposerSuggestionsList = forwardRef<\n HTMLUListElement,\n ComposerSuggestionsListProps\n>(({ children, asChild, ...props }, forwardedRef) => {\n const { id } = useComposerSuggestionsContext(COMPOSER_SUGGESTIONS_LIST_NAME);\n const Component = asChild ? Slot : \"ul\";\n\n return (\n <Component\n role=\"listbox\"\n id={id}\n aria-label=\"Suggestions list\"\n {...props}\n ref={forwardedRef}\n >\n {children}\n </Component>\n );\n});\n\n/**\n * Displays a suggestion within `Composer.SuggestionsList`.\n *\n * @example\n * <Composer.SuggestionsListItem key={mention.id} value={mention.id}>\n * @{mention.id}\n * </Composer.SuggestionsListItem>\n */\nconst ComposerSuggestionsListItem = forwardRef<\n HTMLLIElement,\n ComposerSuggestionsListItemProps\n>(\n (\n {\n value,\n children,\n onPointerMove,\n onPointerDown,\n onClick,\n asChild,\n ...props\n },\n forwardedRef\n ) => {\n const ref = useRef<HTMLLIElement>(null);\n const mergedRefs = useRefs(forwardedRef, ref);\n const { selectedValue, setSelectedValue, itemId, onItemSelect } =\n useComposerSuggestionsContext(COMPOSER_SUGGESTIONS_LIST_ITEM_NAME);\n const Component = asChild ? Slot : \"li\";\n const isSelected = useMemo(\n () => selectedValue === value,\n [selectedValue, value]\n );\n // TODO: Support props.id if provided, it will need to be sent up to Composer.Editor to use it in aria-activedescendant\n const id = useMemo(() => itemId(value), [itemId, value]);\n\n useEffect(() => {\n if (ref?.current && isSelected) {\n ref.current.scrollIntoView({ block: \"nearest\" });\n }\n }, [isSelected]);\n\n const handlePointerMove = useCallback(\n (event: PointerEvent<HTMLLIElement>) => {\n onPointerMove?.(event);\n\n if (!event.isDefaultPrevented()) {\n setSelectedValue(value);\n }\n },\n [onPointerMove, setSelectedValue, value]\n );\n\n const handlePointerDown = useCallback(\n (event: PointerEvent<HTMLLIElement>) => {\n onPointerDown?.(event);\n\n event.preventDefault();\n event.stopPropagation();\n },\n [onPointerDown]\n );\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLLIElement>) => {\n onClick?.(event);\n\n const wasDefaultPrevented = event.isDefaultPrevented();\n\n event.preventDefault();\n event.stopPropagation();\n\n if (!wasDefaultPrevented) {\n onItemSelect(value);\n }\n },\n [onClick, onItemSelect, value]\n );\n\n return (\n <Component\n role=\"option\"\n id={id}\n data-selected={isSelected || undefined}\n aria-selected={isSelected || undefined}\n onPointerMove={handlePointerMove}\n onPointerDown={handlePointerDown}\n onClick={handleClick}\n {...props}\n ref={mergedRefs}\n >\n {children}\n </Component>\n );\n }\n);\n\nconst defaultEditorComponents: ComposerEditorComponents = {\n Link: ({ href, children }) => {\n return <ComposerLink href={href}>{children}</ComposerLink>;\n },\n Mention: ({ mention }) => {\n return (\n <ComposerMention>\n {MENTION_CHARACTER}\n {mention.id}\n </ComposerMention>\n );\n },\n MentionSuggestions: ({ mentions }) => {\n return mentions.length > 0 ? (\n <ComposerSuggestions>\n <ComposerSuggestionsList>\n {mentions.map((mention) => (\n <ComposerSuggestionsListItem key={mention.id} value={mention.id}>\n {mention.id}\n </ComposerSuggestionsListItem>\n ))}\n </ComposerSuggestionsList>\n </ComposerSuggestions>\n ) : null;\n },\n};\n\n/**\n * Displays the composer's editor.\n *\n * @example\n * <Composer.Editor placeholder=\"Write a comment…\" />\n */\nconst ComposerEditor = forwardRef<HTMLDivElement, ComposerEditorProps>(\n (\n {\n defaultValue,\n onKeyDown,\n onFocus,\n onBlur,\n disabled,\n autoFocus,\n components,\n dir,\n ...props\n },\n forwardedRef\n ) => {\n const client = useClientOrNull();\n const { editor, validate, setFocused, onEditorChange, roomId } =\n useComposerEditorContext();\n const {\n submit,\n focus,\n blur,\n select,\n canSubmit,\n isDisabled: isComposerDisabled,\n isFocused,\n } = useComposer();\n const isDisabled = isComposerDisabled || disabled;\n const initialBody = useInitial(defaultValue ?? emptyCommentBody);\n const initialEditorValue = useMemo(() => {\n return commentBodyToComposerBody(initialBody);\n }, [initialBody]);\n const { Link, Mention, MentionSuggestions, FloatingToolbar } = useMemo(\n () => ({ ...defaultEditorComponents, ...components }),\n [components]\n );\n\n const [hasFloatingToolbarRange, setHasFloatingToolbarRange] =\n useState(false);\n // If used with LiveblocksProvider but without resolveMentionSuggestions,\n // we can skip the mention suggestions logic entirely\n const resolveMentionSuggestions = useResolveMentionSuggestions();\n const hasResolveMentionSuggestions = client\n ? resolveMentionSuggestions\n : true;\n const [mentionDraft, setMentionDraft] = useState<MentionDraft>();\n const mentionSuggestions = useMentionSuggestions(\n roomId,\n mentionDraft?.text\n );\n const [\n selectedMentionSuggestionIndex,\n setPreviousSelectedMentionSuggestionIndex,\n setNextSelectedMentionSuggestionIndex,\n setSelectedMentionSuggestionIndex,\n ] = useIndex(0, mentionSuggestions?.length ?? 0);\n const id = useId();\n const floatingToolbarId = `liveblocks-floating-toolbar-${id}`;\n const suggestionsListId = `liveblocks-suggestions-list-${id}`;\n const suggestionsListItemId = useCallback(\n (mentionId?: string) =>\n mentionId\n ? `liveblocks-suggestions-list-item-${id}-${mentionId}`\n : undefined,\n [id]\n );\n\n const renderElement = useCallback(\n (props: RenderElementProps) => {\n return (\n <ComposerEditorElement Mention={Mention} Link={Link} {...props} />\n );\n },\n [Link, Mention]\n );\n\n const handleChange = useCallback(\n (value: SlateDescendant[]) => {\n validate(value as SlateElement[]);\n\n // Our multi-component setup requires us to instantiate the editor in `Composer.Form`\n // but we can only listen to changes here in `Composer.Editor` via `Slate`, so we use\n // an event source to notify `Composer.Form` of changes.\n onEditorChange.notify();\n },\n [validate, onEditorChange]\n );\n\n const createMention = useCallback(\n (mention?: MentionData) => {\n if (!mentionDraft || !mention) {\n return;\n }\n\n SlateTransforms.select(editor, mentionDraft.range);\n insertMention(editor, mention);\n setMentionDraft(undefined);\n setSelectedMentionSuggestionIndex(0);\n },\n [editor, mentionDraft, setSelectedMentionSuggestionIndex]\n );\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLDivElement>) => {\n onKeyDown?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n // Allow leaving marks with ArrowLeft\n if (isKey(event, \"ArrowLeft\")) {\n leaveMarkEdge(editor, \"start\");\n }\n\n // Allow leaving marks with ArrowRight\n if (isKey(event, \"ArrowRight\")) {\n leaveMarkEdge(editor, \"end\");\n }\n\n if (mentionDraft && mentionSuggestions?.length) {\n // Select the next mention suggestion on ArrowDown\n if (isKey(event, \"ArrowDown\")) {\n event.preventDefault();\n setNextSelectedMentionSuggestionIndex();\n }\n\n // Select the previous mention suggestion on ArrowUp\n if (isKey(event, \"ArrowUp\")) {\n event.preventDefault();\n setPreviousSelectedMentionSuggestionIndex();\n }\n\n // Create a mention on Enter/Tab\n if (isKey(event, \"Enter\") || isKey(event, \"Tab\")) {\n event.preventDefault();\n\n const mention =\n mentionSuggestions?.[selectedMentionSuggestionIndex];\n createMention(mention);\n }\n\n // Close the suggestions on Escape\n if (isKey(event, \"Escape\")) {\n event.preventDefault();\n setMentionDraft(undefined);\n setSelectedMentionSuggestionIndex(0);\n }\n } else {\n if (hasFloatingToolbarRange) {\n // Close the floating toolbar on Escape\n if (isKey(event, \"Escape\")) {\n event.preventDefault();\n setHasFloatingToolbarRange(false);\n }\n }\n\n // Blur the editor on Escape\n if (isKey(event, \"Escape\")) {\n blur();\n }\n\n // Submit the editor on Enter\n if (isKey(event, \"Enter\", { shift: false })) {\n // Even if submitting is not possible, don't do anything else on Enter. (e.g. creating a new line)\n event.preventDefault();\n\n if (canSubmit) {\n submit();\n }\n }\n\n // Create a new line on Shift + Enter\n if (isKey(event, \"Enter\", { shift: true })) {\n event.preventDefault();\n editor.insertBreak();\n }\n\n // Toggle bold on Command/Control + B\n if (isKey(event, \"b\", { mod: true })) {\n event.preventDefault();\n toggleEditorMark(editor, \"bold\");\n }\n\n // Toggle italic on Command/Control + I\n if (isKey(event, \"i\", { mod: true })) {\n event.preventDefault();\n toggleEditorMark(editor, \"italic\");\n }\n\n // Toggle strikethrough on Command/Control + Shift + S\n if (isKey(event, \"s\", { mod: true, shift: true })) {\n event.preventDefault();\n toggleEditorMark(editor, \"strikethrough\");\n }\n\n // Toggle code on Command/Control + E\n if (isKey(event, \"e\", { mod: true })) {\n event.preventDefault();\n toggleEditorMark(editor, \"code\");\n }\n }\n },\n [\n onKeyDown,\n mentionDraft,\n mentionSuggestions,\n hasFloatingToolbarRange,\n editor,\n setNextSelectedMentionSuggestionIndex,\n setPreviousSelectedMentionSuggestionIndex,\n selectedMentionSuggestionIndex,\n createMention,\n setSelectedMentionSuggestionIndex,\n blur,\n canSubmit,\n submit,\n ]\n );\n\n const handleFocus = useCallback(\n (event: FocusEvent<HTMLDivElement>) => {\n onFocus?.(event);\n\n if (!event.isDefaultPrevented()) {\n setFocused(true);\n }\n },\n [onFocus, setFocused]\n );\n\n const handleBlur = useCallback(\n (event: FocusEvent<HTMLDivElement>) => {\n onBlur?.(event);\n\n if (!event.isDefaultPrevented()) {\n setFocused(false);\n }\n },\n [onBlur, setFocused]\n );\n\n const selectedMention =\n mentionSuggestions?.[selectedMentionSuggestionIndex];\n const selectedMentionId = selectedMention?.id;\n const setSelectedMentionId = useCallback(\n (mentionId: string) => {\n const index = mentionSuggestions?.findIndex(\n (mention) => mention.id === mentionId\n );\n\n if (index !== undefined && index >= 0) {\n setSelectedMentionSuggestionIndex(index);\n }\n },\n [setSelectedMentionSuggestionIndex, mentionSuggestions]\n );\n\n const additionalProps: AriaAttributes = useMemo(\n () =>\n mentionDraft\n ? {\n role: \"combobox\",\n \"aria-autocomplete\": \"list\",\n \"aria-expanded\": true,\n \"aria-controls\": suggestionsListId,\n \"aria-activedescendant\": suggestionsListItemId(selectedMentionId),\n }\n : hasFloatingToolbarRange\n ? {\n \"aria-haspopup\": true,\n \"aria-controls\": floatingToolbarId,\n }\n : {},\n [\n mentionDraft,\n suggestionsListId,\n suggestionsListItemId,\n selectedMentionId,\n hasFloatingToolbarRange,\n floatingToolbarId,\n ]\n );\n\n useImperativeHandle(forwardedRef, () => {\n return ReactEditor.toDOMNode(editor, editor) as HTMLDivElement;\n }, [editor]);\n\n // Manually focus the editor when `autoFocus` is true\n useLayoutEffect(() => {\n if (!autoFocus) {\n return;\n }\n\n // `focus` needs to be synchronous to ensure its errors can be caught\n // but the triggering of `focus` on mount itself can be asynchronous.\n // This brings back the same timing behavior as Slate's `ReactEditor.focus`\n // (which uses `setTimeout` internally) while still allowing us to catch errors.\n const timeout = setTimeout(() => focus(), 0);\n\n return () => clearTimeout(timeout);\n }, [autoFocus, editor, focus]);\n\n // Manually add a selection in the editor if the selection\n // is still empty after being focused\n useLayoutEffect(() => {\n if (isFocused && editor.selection === null) {\n select();\n }\n }, [editor, select, isFocused]);\n\n const handleMentionSelect = useCallback(\n (mentionId: string) => {\n const mention = mentionSuggestions?.find(\n (mention) => mention.id === mentionId\n );\n\n createMention(mention);\n },\n [createMention, mentionSuggestions]\n );\n\n return (\n <Slate\n editor={editor}\n initialValue={initialEditorValue}\n onChange={handleChange}\n >\n <Editable\n dir={dir}\n enterKeyHint={mentionDraft ? \"enter\" : \"send\"}\n autoCapitalize=\"sentences\"\n aria-label=\"Composer editor\"\n data-focused={isFocused || undefined}\n data-disabled={isDisabled || undefined}\n {...additionalProps}\n {...props}\n readOnly={isDisabled}\n disabled={isDisabled}\n onKeyDown={handleKeyDown}\n onFocus={handleFocus}\n onBlur={handleBlur}\n renderElement={renderElement}\n renderLeaf={ComposerEditorLeaf}\n renderPlaceholder={ComposerEditorPlaceholder}\n />\n {hasResolveMentionSuggestions && (\n <ComposerEditorMentionSuggestionsWrapper\n dir={dir}\n mentionDraft={mentionDraft}\n setMentionDraft={setMentionDraft}\n selectedMentionId={selectedMentionId}\n setSelectedMentionId={setSelectedMentionId}\n mentions={mentionSuggestions}\n id={suggestionsListId}\n itemId={suggestionsListItemId}\n onItemSelect={handleMentionSelect}\n MentionSuggestions={MentionSuggestions}\n />\n )}\n {FloatingToolbar && (\n <ComposerEditorFloatingToolbarWrapper\n dir={dir}\n id={floatingToolbarId}\n hasFloatingToolbarRange={hasFloatingToolbarRange}\n setHasFloatingToolbarRange={setHasFloatingToolbarRange}\n FloatingToolbar={FloatingToolbar}\n />\n )}\n </Slate>\n );\n }\n);\n\nconst MAX_ATTACHMENTS = 10;\nconst MAX_ATTACHMENT_SIZE = 1024 * 1024 * 1024; // 1 GB\n\nfunction prepareAttachment(file: File): CommentLocalAttachment {\n return {\n type: \"localAttachment\",\n status: \"idle\",\n id: createCommentAttachmentId(),\n name: file.name,\n size: file.size,\n mimeType: file.type,\n file,\n };\n}\n\n/**\n * Surrounds the composer's content and handles submissions.\n *\n * @example\n * <Composer.Form onComposerSubmit={({ body }) => {}}>\n *\t <Composer.Editor />\n * <Composer.Submit />\n * </Composer.Form>\n */\nconst ComposerForm = forwardRef<HTMLFormElement, ComposerFormProps>(\n (\n {\n children,\n onSubmit,\n onComposerSubmit,\n defaultAttachments = [],\n pasteFilesAsAttachments,\n blurOnSubmit = true,\n preventUnsavedChanges = true,\n disabled,\n asChild,\n roomId: _roomId,\n ...props\n },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"form\";\n const [isEmpty, setEmpty] = useState(true);\n const [isSubmitting, setSubmitting] = useState(false);\n const [isFocused, setFocused] = useState(false);\n const room = useRoom({ allowOutsideRoom: true });\n\n const roomId = _roomId !== undefined ? _roomId : room?.id;\n if (roomId === undefined) {\n throw new Error(\"Composer.Form must be a descendant of RoomProvider.\");\n }\n\n // Later: Offer as Composer.Form props: { maxAttachments: number; maxAttachmentSize: number; supportedAttachmentMimeTypes: string[]; }\n const maxAttachments = MAX_ATTACHMENTS;\n const maxAttachmentSize = MAX_ATTACHMENT_SIZE;\n\n const {\n attachments,\n isUploadingAttachments,\n addAttachments,\n removeAttachment,\n clearAttachments,\n } = useComposerAttachmentsManager(defaultAttachments, {\n maxFileSize: maxAttachmentSize,\n roomId,\n });\n const numberOfAttachments = attachments.length;\n const hasMaxAttachments = numberOfAttachments >= maxAttachments;\n\n const isDisabled = useMemo(() => {\n return isSubmitting || disabled === true;\n }, [isSubmitting, disabled]);\n const canSubmit = useMemo(() => {\n return !isEmpty && !isUploadingAttachments;\n }, [isEmpty, isUploadingAttachments]);\n const [marks, setMarks] = useState<ComposerBodyMarks>(getComposerBodyMarks);\n\n const ref = useRef<HTMLFormElement>(null);\n const mergedRefs = useRefs(forwardedRef, ref);\n const fileInputRef = useRef<HTMLInputElement>(null);\n const syncSource = useSyncSource();\n\n // Mark the composer as a pending update when it has unsubmitted (draft)\n // text or attachments\n const isPending = !preventUnsavedChanges\n ? false\n : !isEmpty || isUploadingAttachments || attachments.length > 0;\n\n useEffect(() => {\n syncSource?.setSyncStatus(\n isPending ? \"has-local-changes\" : \"synchronized\"\n );\n }, [syncSource, isPending]);\n\n const createAttachments = useCallback(\n (files: File[]) => {\n if (!files.length) {\n return;\n }\n\n const numberOfAcceptedFiles = Math.max(\n 0,\n maxAttachments - numberOfAttachments\n );\n\n files.splice(numberOfAcceptedFiles);\n\n const attachments = files.map((file) => prepareAttachment(file));\n\n addAttachments(attachments);\n },\n [addAttachments, maxAttachments, numberOfAttachments]\n );\n\n const createAttachmentsRef = useRef(createAttachments);\n\n useEffect(() => {\n createAttachmentsRef.current = createAttachments;\n }, [createAttachments]);\n\n const stableCreateAttachments = useCallback((files: File[]) => {\n createAttachmentsRef.current(files);\n }, []);\n\n const editor = useInitial(() =>\n createComposerEditor({\n createAttachments: stableCreateAttachments,\n pasteFilesAsAttachments,\n })\n );\n const onEditorChange = useInitial(makeEventSource) as EventSource<void>;\n\n const validate = useCallback(\n (value: SlateElement[]) => {\n setEmpty(isEditorEmpty(editor, value));\n },\n [editor]\n );\n\n const submit = useCallback(() => {\n if (!canSubmit) {\n return;\n }\n\n // We need to wait for the next frame in some cases like when composing diacritics,\n // we want any native handling to be done first while still being handled on `keydown`.\n requestAnimationFrame(() => {\n if (ref.current) {\n requestSubmit(ref.current);\n }\n });\n }, [canSubmit]);\n\n const clear = useCallback(() => {\n SlateTransforms.delete(editor, {\n at: {\n anchor: SlateEditor.start(editor, []),\n focus: SlateEditor.end(editor, []),\n },\n });\n }, [editor]);\n\n const select = useCallback(() => {\n SlateTransforms.select(editor, SlateEditor.end(editor, []));\n }, [editor]);\n\n const focus = useCallback(\n (resetSelection = true) => {\n try {\n // Slate's `ReactEditor.focus` method can use `setTimeout` internally\n // which prevents us from catching errors, so this is a reimplementation.\n // https://github.com/ianstormtaylor/slate/blob/main/packages/slate-dom/src/plugin/dom-editor.ts\n if (!ReactEditor.isFocused(editor)) {\n SlateTransforms.select(\n editor,\n resetSelection || !editor.selection\n ? SlateEditor.end(editor, [])\n : editor.selection\n );\n\n const element = ReactEditor.toDOMNode(editor, editor);\n\n if (editor.selection) {\n const domSelection = window.getSelection();\n const domRange = getDOMRange(editor, editor.selection);\n\n if (domRange) {\n domSelection?.removeAllRanges();\n domSelection?.addRange(domRange);\n }\n }\n\n element.focus({ preventScroll: true });\n }\n } catch {\n // Slate's DOM-specific methods will throw if the editor's DOM\n // node no longer exists. This action doesn't make sense on an\n // unmounted editor so we can safely ignore it.\n }\n },\n [editor]\n );\n\n const blur = useCallback(() => {\n try {\n ReactEditor.blur(editor);\n } catch {\n // Slate's DOM-specific methods will throw if the editor's DOM\n // node no longer exists. This action doesn't make sense on an\n // unmounted editor so we can safely ignore it.\n }\n }, [editor]);\n\n const createMention = useCallback(() => {\n if (disabled) {\n return;\n }\n\n focus();\n insertMentionCharacter(editor);\n }, [disabled, editor, focus]);\n\n const insertText = useCallback(\n (text: string) => {\n if (disabled) {\n return;\n }\n\n focus(false);\n insertSlateText(editor, text);\n },\n [disabled, editor, focus]\n );\n\n const attachFiles = useCallback(() => {\n if (disabled) {\n return;\n }\n\n if (fileInputRef.current) {\n fileInputRef.current.click();\n }\n }, [disabled]);\n\n const handleAttachmentsInputChange = useCallback(\n (event: ChangeEvent<HTMLInputElement>) => {\n if (disabled) {\n return;\n }\n\n if (event.target.files) {\n createAttachments(Array.from(event.target.files));\n\n // Reset the input value to allow selecting the same file(s) again\n event.target.value = \"\";\n }\n },\n [createAttachments, disabled]\n );\n\n const onSubmitEnd = useCallback(() => {\n clear();\n clearAttachments();\n setSubmitting(false);\n\n if (blurOnSubmit) {\n blur();\n }\n }, [blur, blurOnSubmit, clear, clearAttachments]);\n\n const handleSubmit = useCallback(\n (event: FormEvent<HTMLFormElement>) => {\n if (disabled) {\n return;\n }\n\n // In some situations (e.g. pressing Enter while composing diacritics), it's possible\n // for the form to be submitted as empty even though we already checked whether the\n // editor was empty when handling the key press.\n const isEmpty = isEditorEmpty(editor, editor.children);\n\n // We even prevent the user's `onSubmit` handler from being called if the editor is empty.\n if (isEmpty) {\n event.preventDefault();\n\n return;\n }\n\n onSubmit?.(event);\n\n if (!onComposerSubmit || event.isDefaultPrevented()) {\n event.preventDefault();\n\n return;\n }\n\n const body = composerBodyToCommentBody(\n editor.children as ComposerBodyData\n );\n // Only non-local attachments are included to be submitted.\n const commentAttachments: CommentAttachment[] = attachments\n .filter(\n (attachment) =>\n attachment.type === \"attachment\" ||\n (attachment.type === \"localAttachment\" &&\n attachment.status === \"uploaded\")\n )\n .map((attachment) => {\n return {\n id: attachment.id,\n type: \"attachment\",\n mimeType: attachment.mimeType,\n size: attachment.size,\n name: attachment.name,\n };\n });\n\n const promise = onComposerSubmit(\n { body, attachments: commentAttachments },\n event\n );\n\n event.preventDefault();\n\n if (promise) {\n setSubmitting(true);\n promise.then(onSubmitEnd);\n } else {\n onSubmitEnd();\n }\n },\n [disabled, editor, attachments, onComposerSubmit, onSubmit, onSubmitEnd]\n );\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n const toggleMark = useCallback(\n (mark: ComposerBodyMark) => {\n toggleEditorMark(editor, mark);\n },\n [editor]\n );\n\n useObservable(onEditorChange, () => {\n setMarks(getComposerBodyMarks(editor));\n });\n\n return (\n <ComposerEditorContext.Provider\n value={{\n editor,\n validate,\n setFocused,\n onEditorChange,\n roomId,\n }}\n >\n <ComposerAttachmentsContext.Provider\n value={{\n createAttachments,\n isUploadingAttachments,\n hasMaxAttachments,\n maxAttachments,\n maxAttachmentSize,\n }}\n >\n <ComposerContext.Provider\n value={{\n isDisabled,\n isFocused,\n isEmpty,\n canSubmit,\n submit,\n clear,\n select,\n focus,\n blur,\n createMention,\n insertText,\n attachments,\n attachFiles,\n removeAttachment,\n toggleMark,\n marks,\n }}\n >\n <Component {...props} onSubmit={handleSubmit} ref={mergedRefs}>\n <input\n type=\"file\"\n multiple\n ref={fileInputRef}\n onChange={handleAttachmentsInputChange}\n onClick={stopPropagation}\n tabIndex={-1}\n style={{ display: \"none\" }}\n />\n <Slottable>{children}</Slottable>\n </Component>\n </ComposerContext.Provider>\n </ComposerAttachmentsContext.Provider>\n </ComposerEditorContext.Provider>\n );\n }\n);\n\n/**\n * A button to submit the composer.\n *\n * @example\n * <Composer.Submit>Send</Composer.Submit>\n */\nconst ComposerSubmit = forwardRef<HTMLButtonElement, ComposerSubmitProps>(\n ({ children, disabled, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"button\";\n const { canSubmit, isDisabled: isComposerDisabled } = useComposer();\n const isDisabled = isComposerDisabled || disabled || !canSubmit;\n\n return (\n <Component\n type=\"submit\"\n {...props}\n ref={forwardedRef}\n disabled={isDisabled}\n >\n {children}\n </Component>\n );\n }\n);\n\n/**\n * A button which opens a file picker to create attachments.\n *\n * @example\n * <Composer.AttachFiles>Attach files</Composer.AttachFiles>\n */\nconst ComposerAttachFiles = forwardRef<\n HTMLButtonElement,\n ComposerAttachFilesProps\n>(({ children, onClick, disabled, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"button\";\n const { hasMaxAttachments } = useComposerAttachmentsContext();\n const { isDisabled: isComposerDisabled, attachFiles } = useComposer();\n const isDisabled = isComposerDisabled || hasMaxAttachments || disabled;\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n\n if (!event.isDefaultPrevented()) {\n attachFiles();\n }\n },\n [attachFiles, onClick]\n );\n\n return (\n <Component\n type=\"button\"\n {...props}\n onClick={handleClick}\n ref={forwardedRef}\n disabled={isDisabled}\n >\n {children}\n </Component>\n );\n});\n\n/**\n * A drop area which accepts files to create attachments.\n *\n * @example\n * <Composer.AttachmentsDropArea>\n * Drop files here\n * </Composer.AttachmentsDropArea>\n */\nconst ComposerAttachmentsDropArea = forwardRef<\n HTMLDivElement,\n ComposerAttachmentsDropAreaProps\n>(\n (\n {\n onDragEnter,\n onDragLeave,\n onDragOver,\n onDrop,\n disabled,\n asChild,\n ...props\n },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"div\";\n const { isDisabled: isComposerDisabled } = useComposer();\n const isDisabled = isComposerDisabled || disabled;\n const [, dropAreaProps] = useComposerAttachmentsDropArea({\n onDragEnter,\n onDragLeave,\n onDragOver,\n onDrop,\n disabled: isDisabled,\n });\n\n return (\n <Component\n {...dropAreaProps}\n data-disabled={isDisabled ? \"\" : undefined}\n {...props}\n ref={forwardedRef}\n />\n );\n }\n);\n\n/**\n * A toggle button which toggles a specific text mark.\n *\n * @example\n * <Composer.MarkToggle mark=\"bold\">\n * Bold\n * </Composer.MarkToggle>\n */\nconst ComposerMarkToggle = forwardRef<\n HTMLButtonElement,\n ComposerMarkToggleProps\n>(\n (\n {\n children,\n mark,\n onValueChange,\n onClick,\n onPointerDown,\n asChild,\n ...props\n },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"button\";\n const { marks, toggleMark } = useComposer();\n\n const handlePointerDown = useCallback(\n (event: PointerEvent<HTMLButtonElement>) => {\n onPointerDown?.(event);\n\n event.preventDefault();\n event.stopPropagation();\n },\n [onPointerDown]\n );\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n\n if (!event.isDefaultPrevented()) {\n event.preventDefault();\n event.stopPropagation();\n\n toggleMark(mark);\n onValueChange?.(mark);\n }\n },\n [mark, onClick, onValueChange, toggleMark]\n );\n\n return (\n <TogglePrimitive.Root\n asChild\n pressed={marks[mark]}\n onClick={handleClick}\n onPointerDown={handlePointerDown}\n {...props}\n >\n <Component {...props} ref={forwardedRef}>\n {children}\n </Component>\n </TogglePrimitive.Root>\n );\n }\n);\n\nif (process.env.NODE_ENV !== \"production\") {\n ComposerAttachFiles.displayName = COMPOSER_ATTACH_FILES_NAME;\n ComposerAttachmentsDropArea.displayName = COMPOSER_ATTACHMENTS_DROP_AREA_NAME;\n ComposerEditor.displayName = COMPOSER_EDITOR_NAME;\n ComposerFloatingToolbar.displayName = COMPOSER_FLOATING_TOOLBAR_NAME;\n ComposerForm.displayName = COMPOSER_FORM_NAME;\n ComposerMention.displayName = COMPOSER_MENTION_NAME;\n ComposerLink.displayName = COMPOSER_LINK_NAME;\n ComposerSubmit.displayName = COMPOSER_SUBMIT_NAME;\n ComposerSuggestions.displayName = COMPOSER_SUGGESTIONS_NAME;\n ComposerSuggestionsList.displayName = COMPOSER_SUGGESTIONS_LIST_NAME;\n ComposerSuggestionsListItem.displayName = COMPOSER_SUGGESTIONS_LIST_ITEM_NAME;\n ComposerMarkToggle.displayName = COMPOSER_MARK_TOGGLE_NAME;\n}\n\n// NOTE: Every export from this file will be available publicly as Composer.*\nexport {\n ComposerAttachFiles as AttachFiles,\n ComposerAttachmentsDropArea as AttachmentsDropArea,\n ComposerEditor as Editor,\n ComposerFloatingToolbar as FloatingToolbar,\n ComposerForm as Form,\n ComposerLink as Link,\n ComposerMarkToggle as MarkToggle,\n ComposerMention as Mention,\n ComposerSubmit as Submit,\n ComposerSuggestions as Suggestions,\n ComposerSuggestionsList as SuggestionsList,\n ComposerSuggestionsListItem as SuggestionsListItem,\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsJA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAsC;AAC3B;AAEX;AAEA;AAA8B;AAC5B;AAEF;AAIE;AAAO;AACL;AACE;AACE;AACE;AACE;AACoD;AAChD;AACA;AACD;AACH;AACF;AACF;AACF;AACF;AAEJ;AAEA;AAAsC;AACpC;AACA;AACA;AAEF;AACE;AACA;AAEA;AAEK;AAEG;AACH;AAGP;AAEA;AAAmC;AACjC;AACA;AACA;AAEF;AACE;AAEA;AAKF;AAEA;AAAiD;AAC/C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACW;AACX;AAEF;AACE;AACA;AACA;AACA;AACA;AAEA;AAAM;AAC8B;AAClC;AACA;AACA;AACA;AACA;AACA;AACA;AACyB;AACzB;AACA;AACW;AACL;AAGR;AACE;AAAkD;AAGpD;AACE;AACE;AAEA;AAAA;AAGF;AACA;AAA6B;AAK/B;AACE;AAAa;AAEb;AAIA;AACE;AAAA;AAIF;AACA;AAGA;AAGA;AACE;AACA;AACE;AAGJ;AACE;AAAmC;AACrC;AAGF;AAGM;AAA4B;AAA3B;AACQ;AACL;AACA;AACe;AACG;AAClB;AACA;AACA;AACK;AACP;AAEA;AAAC;AAAA;AACM;AACE;AACK;AACL;AACC;AAGF;AACM;AACF;AACV;AAEA;AAAC;AAAA;AACC;AACA;AAAA;AACF;AAAA;AACF;AAAA;AAKV;AAEA;AAA8C;AAC5C;AACW;AACX;AACA;AACA;AAEF;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AAAM;AAC8B;AAClC;AACA;AACA;AACA;AACA;AACyB;AACnB;AACN;AACA;AACW;AACL;AAGR;AACE;AACE;AAAA;AAGF;AACA;AAEA;AACA;AAEA;AACE;AACA;AAAyD;AAC3D;AAGF;AAEE;AAGA;AACE;AAGA;AAME;AACA;AAAiB;AAEjB;AAEA;AACA;AAAqB;AACvB;AACD;AAGH;AAGM;AAAgC;AAA/B;AACQ;AACL;AACA;AACA;AACK;AACP;AAEA;AAAC;AAAA;AACM;AACE;AACK;AACL;AACC;AAGF;AACM;AACF;AACV;AAEiB;AAAA;AACnB;AAAA;AAKV;AAWM;AAIJ;AACA;AACA;AAAM;AACJ;AACK;AACL;AACA;AAEF;AACA;AAAsB;AACgC;AAC1C;AAEZ;AACA;AAEA;AAA0B;AAEtB;AAEA;AACA;AAAsB;AACxB;AACc;AAGhB;AACE;AAAC;AAAA;AACC;AACK;AACL;AACW;AACP;AACW;AACkB;AACtB;AACC;AACL;AACI;AACM;AACL;AACC;AACR;AACL;AACK;AAEJ;AAAA;AAGP;AAEA;AAA+B;AAC7B;AACA;AAEF;AAEE;AAEA;AAAsB;AAElB;AACE;AAAC;AAAA;AACC;AACK;AAAA;AACP;AAEC;AAEH;AACE;AAAC;AAAA;AACC;AACK;AAAA;AAGP;AAGF;AAGE;AAGF;AAAO;AAEb;AAGA;AAA4B;AAC1B;AACA;AAEF;AACE;AACE;AAA6B;AAG/B;AACE;AAAyB;AAG3B;AACE;AAAwB;AAG1B;AACE;AAA2B;AAG7B;AACF;AAEA;AAAmC;AACjC;AAEF;AACE;AAEA;AAKF;AAQA;AAAwB;AAEpB;AACA;AAEA;AACE;AAAC;AAAA;AAC8B;AACzB;AACC;AAEJ;AAAA;AACH;AAGN;AAQA;AAAqB;AAEjB;AAEA;AACE;AAAC;AAAA;AACQ;AACH;AACA;AACC;AAEJ;AAAA;AACH;AAGN;AAKM;AAIJ;AACA;AACA;AAAM;AACC;AACL;AACA;AAEF;AACA;AAAsB;AACgC;AAC1C;AAEZ;AACA;AAEA;AACE;AAAC;AAAA;AACC;AACI;AAC6B;AACtB;AACC;AACL;AACI;AACM;AACJ;AACA;AACR;AACL;AACK;AAEJ;AAAA;AAGP;AAcM;AAIJ;AACA;AAEA;AACE;AAAC;AAAA;AACM;AACL;AACW;AACP;AACC;AAEJ;AAAA;AAGP;AAUA;AAAoC;AAKhC;AACE;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AAEA;AACA;AAAmB;AACO;AACH;AAGvB;AAEA;AACE;AACE;AAA+C;AACjD;AAGF;AAA0B;AAEtB;AAEA;AACE;AAAsB;AACxB;AACF;AACuC;AAGzC;AAA0B;AAEtB;AAEA;AACA;AAAsB;AACxB;AACc;AAGhB;AAAoB;AAEhB;AAEA;AAEA;AACA;AAEA;AACE;AAAkB;AACpB;AACF;AAC6B;AAG/B;AACE;AAAC;AAAA;AACM;AACL;AAC6B;AACA;AACd;AACA;AACN;AACL;AACC;AAEJ;AAAA;AACH;AAGN;AAEA;AAA0D;AAEtD;AAA2C;AAC7C;AAEE;AAEK;AAAA;AACQ;AACX;AAEJ;AAEE;AAUI;AAER;AAQA;AAAuB;AAEnB;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AAEA;AAAM;AACJ;AACA;AACA;AACA;AACA;AACY;AACZ;AAEF;AACA;AACA;AACE;AAA4C;AAE9C;AAA+D;AACV;AACxC;AAGb;AAIA;AACA;AAGA;AACA;AAA2B;AACzB;AACc;AAEhB;AAAM;AACJ;AACA;AACA;AACA;AAEF;AACA;AACA;AACA;AAA8B;AAItB;AACH;AAGL;AAAsB;AAElB;AACkE;AAEpE;AACc;AAGhB;AAAqB;AAEjB;AAKA;AAAsB;AACxB;AACyB;AAG3B;AAAsB;AAElB;AACE;AAAA;AAGF;AACA;AACA;AACA;AAAmC;AACrC;AACwD;AAG1D;AAAsB;AAElB;AAEA;AACE;AAAA;AAIF;AACE;AAA6B;AAI/B;AACE;AAA2B;AAG7B;AAEE;AACE;AACA;AAAsC;AAIxC;AACE;AACA;AAA0C;AAI5C;AACE;AAEA;AAEA;AAAqB;AAIvB;AACE;AACA;AACA;AAAmC;AACrC;AAEA;AAEE;AACE;AACA;AAAgC;AAClC;AAIF;AACE;AAAK;AAIP;AAEE;AAEA;AACE;AAAO;AACT;AAIF;AACE;AACA;AAAmB;AAIrB;AACE;AACA;AAA+B;AAIjC;AACE;AACA;AAAiC;AAInC;AACE;AACA;AAAwC;AAI1C;AACE;AACA;AAA+B;AACjC;AACF;AACF;AACA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;AAGF;AAAoB;AAEhB;AAEA;AACE;AAAe;AACjB;AACF;AACoB;AAGtB;AAAmB;AAEf;AAEA;AACE;AAAgB;AAClB;AACF;AACmB;AAGrB;AAEA;AACA;AAA6B;AAEzB;AAAkC;AACJ;AAG9B;AACE;AAAuC;AACzC;AACF;AACsD;AAGxD;AAAwC;AAGhC;AACQ;AACe;AACJ;AACA;AAC+C;AAGhE;AACmB;AACA;AAElB;AACT;AACE;AACA;AACA;AACA;AACA;AACA;AACF;AAGF;AACE;AAA2C;AAI7C;AACE;AACE;AAAA;AAOF;AAEA;AAAiC;AAKnC;AACE;AACE;AAAO;AACT;AAGF;AAA4B;AAExB;AAAoC;AACN;AAG9B;AAAqB;AACvB;AACkC;AAGpC;AACE;AAAC;AAAA;AACC;AACc;AACJ;AAEV;AAAA;AAAC;AAAA;AACC;AACuC;AACxB;AACJ;AACgB;AACE;AACzB;AACA;AACM;AACA;AACC;AACF;AACD;AACR;AACY;AACO;AAAA;AACrB;AAEE;AAAC;AAAA;AACC;AACA;AACA;AACA;AACA;AACU;AACN;AACI;AACM;AACd;AAAA;AACF;AAGA;AAAC;AAAA;AACC;AACI;AACJ;AACA;AACA;AAAA;AACF;AAAA;AAAA;AAEJ;AAGN;AAEA;AACA;AAEA;AACE;AAAO;AACC;AACE;AACsB;AACnB;AACA;AACI;AACf;AAEJ;AAWA;AAAqB;AAEjB;AACE;AACA;AACA;AACsB;AACtB;AACe;AACS;AACxB;AACA;AACQ;AACL;AAIL;AACA;AACA;AACA;AACA;AAEA;AACA;AACE;AAAqE;AAIvE;AACA;AAEA;AAAM;AACJ;AACA;AACA;AACA;AACA;AACoD;AACvC;AACb;AAEF;AACA;AAEA;AACE;AAAoC;AAEtC;AACE;AAAoB;AAEtB;AAEA;AACA;AACA;AACA;AAIA;AAIA;AACE;AAAY;AACwB;AACpC;AAGF;AAA0B;AAEtB;AACE;AAAA;AAGF;AAAmC;AACjC;AACiB;AAGnB;AAEA;AAEA;AAA0B;AAC5B;AACoD;AAGtD;AAEA;AACE;AAA+B;AAGjC;AACE;AAAkC;AAGpC;AAAe;AACQ;AACA;AACnB;AACD;AAEH;AAEA;AAAiB;AAEb;AAAqC;AACvC;AACO;AAGT;AACE;AACE;AAAA;AAKF;AACE;AACE;AAAyB;AAC3B;AACD;AAGH;AACE;AAA+B;AACzB;AACkC;AACH;AACnC;AACD;AAGH;AACE;AAA0D;AAG5D;AAAc;AAEV;AAIE;AACE;AAAgB;AACd;AAGW;AAGb;AAEA;AACE;AACA;AAEA;AACE;AACA;AAA+B;AACjC;AAGF;AAAqC;AACvC;AACM;AAIR;AACF;AACO;AAGT;AACE;AACE;AAAuB;AACjB;AAIR;AAGF;AACE;AACE;AAAA;AAGF;AACA;AAA6B;AAG/B;AAAmB;AAEf;AACE;AAAA;AAGF;AACA;AAA4B;AAC9B;AACwB;AAG1B;AACE;AACE;AAAA;AAGF;AACE;AAA2B;AAC7B;AAGF;AAAqC;AAEjC;AACE;AAAA;AAGF;AACE;AAGA;AAAqB;AACvB;AACF;AAC4B;AAG9B;AACE;AACA;AACA;AAEA;AACE;AAAK;AACP;AAGF;AAAqB;AAEjB;AACE;AAAA;AAMF;AAGA;AACE;AAEA;AAAA;AAGF;AAEA;AACE;AAEA;AAAA;AAGF;AAAa;AACJ;AAGT;AACG;AAI2B;AAG1B;AAAO;AACU;AACT;AACe;AACJ;AACA;AACnB;AAGJ;AAAgB;AAC0B;AACxC;AAGF;AAEA;AACE;AACA;AAAwB;AAExB;AAAY;AACd;AACF;AACuE;AAGzE;AACE;AAAsB;AAGxB;AAAmB;AAEf;AAA6B;AAC/B;AACO;AAGT;AACE;AAAqC;AAGvC;AACE;AAAuB;AAAtB;AACQ;AACL;AACA;AACA;AACA;AACA;AACF;AAEA;AAA4B;AAA3B;AACQ;AACL;AACA;AACA;AACA;AACA;AACF;AAEA;AAAiB;AAAhB;AACQ;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;AAGE;AAAA;AAAC;AAAA;AACM;AACG;AACH;AACK;AACD;AACC;AACe;AAAA;AAC3B;AACqB;AACvB;AAAA;AACF;AAAA;AACF;AAAA;AACF;AAGN;AAQA;AAAuB;AAEnB;AACA;AACA;AAEA;AACE;AAAC;AAAA;AACM;AACD;AACC;AACK;AAET;AAAA;AACH;AAGN;AAQM;AAIJ;AACA;AACA;AACA;AAEA;AAAoB;AAEhB;AAEA;AACE;AAAY;AACd;AACF;AACqB;AAGvB;AACE;AAAC;AAAA;AACM;AACD;AACK;AACJ;AACK;AAET;AAAA;AAGP;AAUA;AAAoC;AAKhC;AACE;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AAAyD;AACvD;AACA;AACA;AACA;AACU;AAGZ;AACE;AAAC;AAAA;AACK;AAC6B;AAC7B;AACC;AAAA;AACP;AAGN;AAUA;AAA2B;AAKvB;AACE;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AAEA;AAA0B;AAEtB;AAEA;AACA;AAAsB;AACxB;AACc;AAGhB;AAAoB;AAEhB;AAEA;AACE;AACA;AAEA;AACA;AAAoB;AACtB;AACF;AACyC;AAG3C;AACE;AAAiB;AAAhB;AACQ;AACY;AACV;AACM;AACX;AAIJ;AAAA;AACF;AAGN;AAEA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;;;;;;;;;;;;;"}
@@ -759,9 +759,11 @@ const ComposerEditor = forwardRef(
759
759
  return ReactEditor.toDOMNode(editor, editor);
760
760
  }, [editor]);
761
761
  useLayoutEffect(() => {
762
- if (autoFocus) {
763
- focus();
762
+ if (!autoFocus) {
763
+ return;
764
764
  }
765
+ const timeout = setTimeout(() => focus(), 0);
766
+ return () => clearTimeout(timeout);
765
767
  }, [autoFocus, editor, focus]);
766
768
  useLayoutEffect(() => {
767
769
  if (isFocused && editor.selection === null) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/primitives/Composer/index.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n type CommentAttachment,\n type CommentBody,\n type CommentLocalAttachment,\n createCommentAttachmentId,\n type EventSource,\n makeEventSource,\n MENTION_CHARACTER,\n type MentionData,\n sanitizeUrl,\n} from \"@liveblocks/core\";\nimport { useRoom } from \"@liveblocks/react\";\nimport {\n useClientOrNull,\n useLayoutEffect,\n useMentionSuggestions,\n useResolveMentionSuggestions,\n useSyncSource,\n} from \"@liveblocks/react/_private\";\nimport { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport * as TogglePrimitive from \"@radix-ui/react-toggle\";\nimport type {\n AriaAttributes,\n ChangeEvent,\n FocusEvent,\n FormEvent,\n KeyboardEvent,\n MouseEvent,\n PointerEvent,\n SyntheticEvent,\n} from \"react\";\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type {\n Descendant as SlateDescendant,\n Element as SlateElement,\n} from \"slate\";\nimport {\n createEditor,\n Editor as SlateEditor,\n insertText as insertSlateText,\n Range as SlateRange,\n Transforms as SlateTransforms,\n} from \"slate\";\nimport { withHistory } from \"slate-history\";\nimport type {\n RenderElementProps,\n RenderElementSpecificProps,\n RenderLeafSpecificProps,\n RenderPlaceholderProps,\n} from \"slate-react\";\nimport {\n Editable,\n ReactEditor,\n Slate,\n useSelected,\n useSlateStatic,\n withReact,\n} from \"slate-react\";\n\nimport type {\n ComposerBody as ComposerBodyData,\n ComposerBodyAutoLink,\n ComposerBodyCustomLink,\n ComposerBodyMark,\n ComposerBodyMarks,\n ComposerBodyMention,\n ComposerBodyText,\n} from \"../../types\";\nimport { isKey } from \"../../utils/is-key\";\nimport { Persist, useAnimationPersist, usePersist } from \"../../utils/Persist\";\nimport { Portal } from \"../../utils/Portal\";\nimport { requestSubmit } from \"../../utils/request-submit\";\nimport { useIndex } from \"../../utils/use-index\";\nimport { useInitial } from \"../../utils/use-initial\";\nimport { useObservable } from \"../../utils/use-observable\";\nimport { useRefs } from \"../../utils/use-refs\";\nimport { withEmptyClearFormatting } from \"../slate/plugins/empty-clear-formatting\";\nimport { withNormalize } from \"../slate/plugins/normalize\";\nimport { getDOMRange } from \"../slate/utils/get-dom-range\";\nimport { isEmpty as isEditorEmpty } from \"../slate/utils/is-empty\";\nimport {\n getComposerBodyMarks,\n leaveMarkEdge,\n toggleMark as toggleEditorMark,\n} from \"../slate/utils/marks\";\nimport {\n ComposerAttachmentsContext,\n ComposerContext,\n ComposerEditorContext,\n ComposerFloatingToolbarContext,\n ComposerSuggestionsContext,\n useComposer,\n useComposerAttachmentsContext,\n useComposerEditorContext,\n useComposerFloatingToolbarContext,\n useComposerSuggestionsContext,\n} from \"./contexts\";\nimport { withAutoFormatting } from \"./slate/plugins/auto-formatting\";\nimport { withAutoLinks } from \"./slate/plugins/auto-links\";\nimport { withCustomLinks } from \"./slate/plugins/custom-links\";\nimport type { MentionDraft } from \"./slate/plugins/mentions\";\nimport {\n getMentionDraftAtSelection,\n insertMention,\n insertMentionCharacter,\n withMentions,\n} from \"./slate/plugins/mentions\";\nimport { withPaste } from \"./slate/plugins/paste\";\nimport type {\n ComposerAttachFilesProps,\n ComposerAttachmentsDropAreaProps,\n ComposerEditorComponents,\n ComposerEditorElementProps,\n ComposerEditorFloatingToolbarWrapperProps,\n ComposerEditorLinkWrapperProps,\n ComposerEditorMentionSuggestionsWrapperProps,\n ComposerEditorMentionWrapperProps,\n ComposerEditorProps,\n ComposerFloatingToolbarProps,\n ComposerFormProps,\n ComposerLinkProps,\n ComposerMarkToggleProps,\n ComposerMentionProps,\n ComposerSubmitProps,\n ComposerSuggestionsListItemProps,\n ComposerSuggestionsListProps,\n ComposerSuggestionsProps,\n FloatingPosition,\n} from \"./types\";\nimport {\n commentBodyToComposerBody,\n composerBodyToCommentBody,\n getSideAndAlignFromFloatingPlacement,\n useComposerAttachmentsDropArea,\n useComposerAttachmentsManager,\n useContentZIndex,\n useFloatingWithOptions,\n} from \"./utils\";\n\nconst MENTION_SUGGESTIONS_POSITION: FloatingPosition = \"top\";\n\nconst FLOATING_TOOLBAR_POSITION: FloatingPosition = \"top\";\n\nconst COMPOSER_MENTION_NAME = \"ComposerMention\";\nconst COMPOSER_LINK_NAME = \"ComposerLink\";\nconst COMPOSER_FLOATING_TOOLBAR_NAME = \"ComposerFloatingToolbar\";\nconst COMPOSER_SUGGESTIONS_NAME = \"ComposerSuggestions\";\nconst COMPOSER_SUGGESTIONS_LIST_NAME = \"ComposerSuggestionsList\";\nconst COMPOSER_SUGGESTIONS_LIST_ITEM_NAME = \"ComposerSuggestionsListItem\";\nconst COMPOSER_SUBMIT_NAME = \"ComposerSubmit\";\nconst COMPOSER_EDITOR_NAME = \"ComposerEditor\";\nconst COMPOSER_ATTACH_FILES_NAME = \"ComposerAttachFiles\";\nconst COMPOSER_ATTACHMENTS_DROP_AREA_NAME = \"ComposerAttachmentsDropArea\";\nconst COMPOSER_MARK_TOGGLE_NAME = \"ComposerMarkToggle\";\nconst COMPOSER_FORM_NAME = \"ComposerForm\";\n\nconst emptyCommentBody: CommentBody = {\n version: 1,\n content: [{ type: \"paragraph\", children: [{ text: \"\" }] }],\n};\n\nfunction createComposerEditor({\n createAttachments,\n pasteFilesAsAttachments,\n}: {\n createAttachments: (files: File[]) => void;\n pasteFilesAsAttachments?: boolean;\n}) {\n return withNormalize(\n withMentions(\n withCustomLinks(\n withAutoLinks(\n withAutoFormatting(\n withEmptyClearFormatting(\n withPaste(withHistory(withReact(createEditor())), {\n createAttachments,\n pasteFilesAsAttachments,\n })\n )\n )\n )\n )\n )\n );\n}\n\nfunction ComposerEditorMentionWrapper({\n Mention,\n attributes,\n children,\n element,\n}: ComposerEditorMentionWrapperProps) {\n const isSelected = useSelected();\n const { children: _, ...mention } = element;\n\n return (\n <span {...attributes}>\n {element.id ? (\n <Mention mention={mention} isSelected={isSelected} />\n ) : null}\n {children}\n </span>\n );\n}\n\nfunction ComposerEditorLinkWrapper({\n Link,\n attributes,\n element,\n children,\n}: ComposerEditorLinkWrapperProps) {\n const href = useMemo(() => sanitizeUrl(element.url) ?? \"\", [element.url]);\n\n return (\n <span {...attributes}>\n <Link href={href}>{children}</Link>\n </span>\n );\n}\n\nfunction ComposerEditorMentionSuggestionsWrapper({\n id,\n itemId,\n mentions,\n selectedMentionId,\n setSelectedMentionId,\n mentionDraft,\n setMentionDraft,\n onItemSelect,\n position = MENTION_SUGGESTIONS_POSITION,\n dir,\n MentionSuggestions,\n}: ComposerEditorMentionSuggestionsWrapperProps) {\n const editor = useSlateStatic();\n const { onEditorChange } = useComposerEditorContext();\n const { isFocused } = useComposer();\n const [contentRef, contentZIndex] = useContentZIndex();\n const isOpen =\n isFocused && mentionDraft?.range !== undefined && mentions !== undefined;\n const {\n refs: { setReference, setFloating },\n strategy,\n isPositioned,\n placement,\n x,\n y,\n update,\n elements,\n } = useFloatingWithOptions({\n position,\n dir,\n alignment: \"start\",\n open: isOpen,\n });\n\n useObservable(onEditorChange, () => {\n setMentionDraft(getMentionDraftAtSelection(editor));\n });\n\n useLayoutEffect(() => {\n if (!mentionDraft) {\n setReference(null);\n\n return;\n }\n\n const domRange = getDOMRange(editor, mentionDraft.range);\n setReference(domRange ?? null);\n }, [setReference, editor, mentionDraft]);\n\n // Manually update the placement when the number of suggestions changes\n // This can prevent the list of suggestions from scrolling instead of moving to the other placement\n useLayoutEffect(() => {\n if (!isOpen) return;\n\n const mentionSuggestions = elements.floating?.firstChild as\n | HTMLElement\n | undefined;\n\n if (!mentionSuggestions) {\n return;\n }\n\n // Force the mention suggestions to grow instead of scrolling\n mentionSuggestions.style.overflowY = \"visible\";\n mentionSuggestions.style.maxHeight = \"none\";\n\n // Trigger a placement update\n update();\n\n // Reset the mention suggestions after the placement update\n const animationFrame = requestAnimationFrame(() => {\n mentionSuggestions.style.overflowY = \"auto\";\n mentionSuggestions.style.maxHeight =\n \"var(--lb-composer-floating-available-height)\";\n });\n\n return () => {\n cancelAnimationFrame(animationFrame);\n };\n }, [mentions?.length, isOpen, elements.floating, update]);\n\n return (\n <Persist>\n {isOpen ? (\n <ComposerSuggestionsContext.Provider\n value={{\n id,\n itemId,\n selectedValue: selectedMentionId,\n setSelectedValue: setSelectedMentionId,\n onItemSelect,\n placement,\n dir,\n ref: contentRef,\n }}\n >\n <Portal\n ref={setFloating}\n style={{\n position: strategy,\n top: 0,\n left: 0,\n transform: isPositioned\n ? `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`\n : \"translate3d(0, -200%, 0)\",\n minWidth: \"max-content\",\n zIndex: contentZIndex,\n }}\n >\n <MentionSuggestions\n mentions={mentions}\n selectedMentionId={selectedMentionId}\n />\n </Portal>\n </ComposerSuggestionsContext.Provider>\n ) : null}\n </Persist>\n );\n}\n\nfunction ComposerEditorFloatingToolbarWrapper({\n id,\n position = FLOATING_TOOLBAR_POSITION,\n dir,\n FloatingToolbar,\n hasFloatingToolbarRange,\n setHasFloatingToolbarRange,\n}: ComposerEditorFloatingToolbarWrapperProps) {\n const editor = useSlateStatic();\n const { onEditorChange } = useComposerEditorContext();\n const { isFocused } = useComposer();\n const [contentRef, contentZIndex] = useContentZIndex();\n const [isPointerDown, setPointerDown] = useState(false);\n const isOpen = isFocused && !isPointerDown && hasFloatingToolbarRange;\n const {\n refs: { setReference, setFloating },\n strategy,\n isPositioned,\n placement,\n x,\n y,\n } = useFloatingWithOptions({\n type: \"range\",\n position,\n dir,\n alignment: \"center\",\n open: isOpen,\n });\n\n useLayoutEffect(() => {\n if (!isFocused) {\n return;\n }\n\n const handlePointerDown = () => setPointerDown(true);\n const handlePointerUp = () => setPointerDown(false);\n\n document.addEventListener(\"pointerdown\", handlePointerDown);\n document.addEventListener(\"pointerup\", handlePointerUp);\n\n return () => {\n document.removeEventListener(\"pointerdown\", handlePointerDown);\n document.removeEventListener(\"pointerup\", handlePointerUp);\n };\n }, [isFocused]);\n\n useObservable(onEditorChange, () => {\n // Detach from previous selection range (if any) to avoid sudden jumps\n setReference(null);\n\n // Then, wait for the next render to ensure the selection is updated\n requestAnimationFrame(() => {\n const domSelection = window.getSelection();\n\n // Finally, show the toolbar if there's a selection range\n if (\n !editor.selection ||\n SlateRange.isCollapsed(editor.selection) ||\n !domSelection ||\n !domSelection.rangeCount\n ) {\n setHasFloatingToolbarRange(false);\n setReference(null);\n } else {\n setHasFloatingToolbarRange(true);\n\n const domRange = domSelection.getRangeAt(0);\n setReference(domRange);\n }\n });\n });\n\n return (\n <Persist>\n {isOpen ? (\n <ComposerFloatingToolbarContext.Provider\n value={{\n id,\n placement,\n dir,\n ref: contentRef,\n }}\n >\n <Portal\n ref={setFloating}\n style={{\n position: strategy,\n top: 0,\n left: 0,\n transform: isPositioned\n ? `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`\n : \"translate3d(0, -200%, 0)\",\n minWidth: \"max-content\",\n zIndex: contentZIndex,\n }}\n >\n <FloatingToolbar />\n </Portal>\n </ComposerFloatingToolbarContext.Provider>\n ) : null}\n </Persist>\n );\n}\n\n/**\n * Displays a floating toolbar attached to the selection within `Composer.Editor`.\n *\n * @example\n * <Composer.FloatingToolbar>\n * <Composer.MarkToggle mark=\"bold\">Bold</Composer.MarkToggle>\n * <Composer.MarkToggle mark=\"italic\">Italic</Composer.MarkToggle>\n * </Composer.FloatingToolbar>\n */\nconst ComposerFloatingToolbar = forwardRef<\n HTMLDivElement,\n ComposerFloatingToolbarProps\n>(({ children, onPointerDown, style, asChild, ...props }, forwardedRef) => {\n const [isPresent] = usePersist();\n const ref = useRef<HTMLDivElement>(null);\n const {\n id,\n ref: contentRef,\n placement,\n dir,\n } = useComposerFloatingToolbarContext(COMPOSER_FLOATING_TOOLBAR_NAME);\n const mergedRefs = useRefs(forwardedRef, contentRef, ref);\n const [side, align] = useMemo(\n () => getSideAndAlignFromFloatingPlacement(placement),\n [placement]\n );\n const Component = asChild ? Slot : \"div\";\n useAnimationPersist(ref);\n\n const handlePointerDown = useCallback(\n (event: PointerEvent<HTMLDivElement>) => {\n onPointerDown?.(event);\n\n event.preventDefault();\n event.stopPropagation();\n },\n [onPointerDown]\n );\n\n return (\n <Component\n dir={dir}\n role=\"toolbar\"\n id={id}\n aria-label=\"Floating toolbar\"\n {...props}\n onPointerDown={handlePointerDown}\n data-state={isPresent ? \"open\" : \"closed\"}\n data-side={side}\n data-align={align}\n style={{\n display: \"flex\",\n flexDirection: \"row\",\n maxWidth: \"var(--lb-composer-floating-available-width)\",\n overflowX: \"auto\",\n ...style,\n }}\n ref={mergedRefs}\n >\n {children}\n </Component>\n );\n});\n\nfunction ComposerEditorElement({\n Mention,\n Link,\n ...props\n}: ComposerEditorElementProps) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const { attributes, children, element } = props;\n\n switch (element.type) {\n case \"mention\":\n return (\n <ComposerEditorMentionWrapper\n Mention={Mention}\n {...(props as RenderElementSpecificProps<ComposerBodyMention>)}\n />\n );\n case \"auto-link\":\n case \"custom-link\":\n return (\n <ComposerEditorLinkWrapper\n Link={Link}\n {...(props as RenderElementSpecificProps<\n ComposerBodyAutoLink | ComposerBodyCustomLink\n >)}\n />\n );\n case \"paragraph\":\n return (\n <p {...attributes} style={{ position: \"relative\" }}>\n {children}\n </p>\n );\n default:\n return null;\n }\n}\n\n// <code><s><em><strong>text</strong></s></em></code>\nfunction ComposerEditorLeaf({\n attributes,\n children,\n leaf,\n}: RenderLeafSpecificProps<ComposerBodyText>) {\n if (leaf.bold) {\n children = <strong>{children}</strong>;\n }\n\n if (leaf.italic) {\n children = <em>{children}</em>;\n }\n\n if (leaf.strikethrough) {\n children = <s>{children}</s>;\n }\n\n if (leaf.code) {\n children = <code>{children}</code>;\n }\n\n return <span {...attributes}>{children}</span>;\n}\n\nfunction ComposerEditorPlaceholder({\n attributes,\n children,\n}: RenderPlaceholderProps) {\n const { opacity: _opacity, ...style } = attributes.style;\n\n return (\n <span {...attributes} style={style} data-placeholder=\"\">\n {children}\n </span>\n );\n}\n\n/**\n * Displays mentions within `Composer.Editor`.\n *\n * @example\n * <Composer.Mention>@{mention.id}</Composer.Mention>\n */\nconst ComposerMention = forwardRef<HTMLSpanElement, ComposerMentionProps>(\n ({ children, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"span\";\n const isSelected = useSelected();\n\n return (\n <Component\n data-selected={isSelected || undefined}\n {...props}\n ref={forwardedRef}\n >\n {children}\n </Component>\n );\n }\n);\n\n/**\n * Displays links within `Composer.Editor`.\n *\n * @example\n * <Composer.Link href={href}>{children}</Composer.Link>\n */\nconst ComposerLink = forwardRef<HTMLAnchorElement, ComposerLinkProps>(\n ({ children, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"a\";\n\n return (\n <Component\n target=\"_blank\"\n rel=\"noopener noreferrer nofollow\"\n {...props}\n ref={forwardedRef}\n >\n {children}\n </Component>\n );\n }\n);\n\n/**\n * Contains suggestions within `Composer.Editor`.\n */\nconst ComposerSuggestions = forwardRef<\n HTMLDivElement,\n ComposerSuggestionsProps\n>(({ children, style, asChild, ...props }, forwardedRef) => {\n const [isPresent] = usePersist();\n const ref = useRef<HTMLDivElement>(null);\n const {\n ref: contentRef,\n placement,\n dir,\n } = useComposerSuggestionsContext(COMPOSER_SUGGESTIONS_NAME);\n const mergedRefs = useRefs(forwardedRef, contentRef, ref);\n const [side, align] = useMemo(\n () => getSideAndAlignFromFloatingPlacement(placement),\n [placement]\n );\n const Component = asChild ? Slot : \"div\";\n useAnimationPersist(ref);\n\n return (\n <Component\n dir={dir}\n {...props}\n data-state={isPresent ? \"open\" : \"closed\"}\n data-side={side}\n data-align={align}\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n maxHeight: \"var(--lb-composer-floating-available-height)\",\n overflowY: \"auto\",\n ...style,\n }}\n ref={mergedRefs}\n >\n {children}\n </Component>\n );\n});\n\n/**\n * Displays a list of suggestions within `Composer.Editor`.\n *\n * @example\n * <Composer.SuggestionsList>\n * {mentions.map((mention) => (\n * <Composer.SuggestionsListItem key={mention.id} value={mention.id}>\n * @{mention.id}\n * </Composer.SuggestionsListItem>\n * ))}\n * </Composer.SuggestionsList>\n */\nconst ComposerSuggestionsList = forwardRef<\n HTMLUListElement,\n ComposerSuggestionsListProps\n>(({ children, asChild, ...props }, forwardedRef) => {\n const { id } = useComposerSuggestionsContext(COMPOSER_SUGGESTIONS_LIST_NAME);\n const Component = asChild ? Slot : \"ul\";\n\n return (\n <Component\n role=\"listbox\"\n id={id}\n aria-label=\"Suggestions list\"\n {...props}\n ref={forwardedRef}\n >\n {children}\n </Component>\n );\n});\n\n/**\n * Displays a suggestion within `Composer.SuggestionsList`.\n *\n * @example\n * <Composer.SuggestionsListItem key={mention.id} value={mention.id}>\n * @{mention.id}\n * </Composer.SuggestionsListItem>\n */\nconst ComposerSuggestionsListItem = forwardRef<\n HTMLLIElement,\n ComposerSuggestionsListItemProps\n>(\n (\n {\n value,\n children,\n onPointerMove,\n onPointerDown,\n onClick,\n asChild,\n ...props\n },\n forwardedRef\n ) => {\n const ref = useRef<HTMLLIElement>(null);\n const mergedRefs = useRefs(forwardedRef, ref);\n const { selectedValue, setSelectedValue, itemId, onItemSelect } =\n useComposerSuggestionsContext(COMPOSER_SUGGESTIONS_LIST_ITEM_NAME);\n const Component = asChild ? Slot : \"li\";\n const isSelected = useMemo(\n () => selectedValue === value,\n [selectedValue, value]\n );\n // TODO: Support props.id if provided, it will need to be sent up to Composer.Editor to use it in aria-activedescendant\n const id = useMemo(() => itemId(value), [itemId, value]);\n\n useEffect(() => {\n if (ref?.current && isSelected) {\n ref.current.scrollIntoView({ block: \"nearest\" });\n }\n }, [isSelected]);\n\n const handlePointerMove = useCallback(\n (event: PointerEvent<HTMLLIElement>) => {\n onPointerMove?.(event);\n\n if (!event.isDefaultPrevented()) {\n setSelectedValue(value);\n }\n },\n [onPointerMove, setSelectedValue, value]\n );\n\n const handlePointerDown = useCallback(\n (event: PointerEvent<HTMLLIElement>) => {\n onPointerDown?.(event);\n\n event.preventDefault();\n event.stopPropagation();\n },\n [onPointerDown]\n );\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLLIElement>) => {\n onClick?.(event);\n\n const wasDefaultPrevented = event.isDefaultPrevented();\n\n event.preventDefault();\n event.stopPropagation();\n\n if (!wasDefaultPrevented) {\n onItemSelect(value);\n }\n },\n [onClick, onItemSelect, value]\n );\n\n return (\n <Component\n role=\"option\"\n id={id}\n data-selected={isSelected || undefined}\n aria-selected={isSelected || undefined}\n onPointerMove={handlePointerMove}\n onPointerDown={handlePointerDown}\n onClick={handleClick}\n {...props}\n ref={mergedRefs}\n >\n {children}\n </Component>\n );\n }\n);\n\nconst defaultEditorComponents: ComposerEditorComponents = {\n Link: ({ href, children }) => {\n return <ComposerLink href={href}>{children}</ComposerLink>;\n },\n Mention: ({ mention }) => {\n return (\n <ComposerMention>\n {MENTION_CHARACTER}\n {mention.id}\n </ComposerMention>\n );\n },\n MentionSuggestions: ({ mentions }) => {\n return mentions.length > 0 ? (\n <ComposerSuggestions>\n <ComposerSuggestionsList>\n {mentions.map((mention) => (\n <ComposerSuggestionsListItem key={mention.id} value={mention.id}>\n {mention.id}\n </ComposerSuggestionsListItem>\n ))}\n </ComposerSuggestionsList>\n </ComposerSuggestions>\n ) : null;\n },\n};\n\n/**\n * Displays the composer's editor.\n *\n * @example\n * <Composer.Editor placeholder=\"Write a comment…\" />\n */\nconst ComposerEditor = forwardRef<HTMLDivElement, ComposerEditorProps>(\n (\n {\n defaultValue,\n onKeyDown,\n onFocus,\n onBlur,\n disabled,\n autoFocus,\n components,\n dir,\n ...props\n },\n forwardedRef\n ) => {\n const client = useClientOrNull();\n const { editor, validate, setFocused, onEditorChange, roomId } =\n useComposerEditorContext();\n const {\n submit,\n focus,\n blur,\n select,\n canSubmit,\n isDisabled: isComposerDisabled,\n isFocused,\n } = useComposer();\n const isDisabled = isComposerDisabled || disabled;\n const initialBody = useInitial(defaultValue ?? emptyCommentBody);\n const initialEditorValue = useMemo(() => {\n return commentBodyToComposerBody(initialBody);\n }, [initialBody]);\n const { Link, Mention, MentionSuggestions, FloatingToolbar } = useMemo(\n () => ({ ...defaultEditorComponents, ...components }),\n [components]\n );\n\n const [hasFloatingToolbarRange, setHasFloatingToolbarRange] =\n useState(false);\n // If used with LiveblocksProvider but without resolveMentionSuggestions,\n // we can skip the mention suggestions logic entirely\n const resolveMentionSuggestions = useResolveMentionSuggestions();\n const hasResolveMentionSuggestions = client\n ? resolveMentionSuggestions\n : true;\n const [mentionDraft, setMentionDraft] = useState<MentionDraft>();\n const mentionSuggestions = useMentionSuggestions(\n roomId,\n mentionDraft?.text\n );\n const [\n selectedMentionSuggestionIndex,\n setPreviousSelectedMentionSuggestionIndex,\n setNextSelectedMentionSuggestionIndex,\n setSelectedMentionSuggestionIndex,\n ] = useIndex(0, mentionSuggestions?.length ?? 0);\n const id = useId();\n const floatingToolbarId = `liveblocks-floating-toolbar-${id}`;\n const suggestionsListId = `liveblocks-suggestions-list-${id}`;\n const suggestionsListItemId = useCallback(\n (mentionId?: string) =>\n mentionId\n ? `liveblocks-suggestions-list-item-${id}-${mentionId}`\n : undefined,\n [id]\n );\n\n const renderElement = useCallback(\n (props: RenderElementProps) => {\n return (\n <ComposerEditorElement Mention={Mention} Link={Link} {...props} />\n );\n },\n [Link, Mention]\n );\n\n const handleChange = useCallback(\n (value: SlateDescendant[]) => {\n validate(value as SlateElement[]);\n\n // Our multi-component setup requires us to instantiate the editor in `Composer.Form`\n // but we can only listen to changes here in `Composer.Editor` via `Slate`, so we use\n // an event source to notify `Composer.Form` of changes.\n onEditorChange.notify();\n },\n [validate, onEditorChange]\n );\n\n const createMention = useCallback(\n (mention?: MentionData) => {\n if (!mentionDraft || !mention) {\n return;\n }\n\n SlateTransforms.select(editor, mentionDraft.range);\n insertMention(editor, mention);\n setMentionDraft(undefined);\n setSelectedMentionSuggestionIndex(0);\n },\n [editor, mentionDraft, setSelectedMentionSuggestionIndex]\n );\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLDivElement>) => {\n onKeyDown?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n // Allow leaving marks with ArrowLeft\n if (isKey(event, \"ArrowLeft\")) {\n leaveMarkEdge(editor, \"start\");\n }\n\n // Allow leaving marks with ArrowRight\n if (isKey(event, \"ArrowRight\")) {\n leaveMarkEdge(editor, \"end\");\n }\n\n if (mentionDraft && mentionSuggestions?.length) {\n // Select the next mention suggestion on ArrowDown\n if (isKey(event, \"ArrowDown\")) {\n event.preventDefault();\n setNextSelectedMentionSuggestionIndex();\n }\n\n // Select the previous mention suggestion on ArrowUp\n if (isKey(event, \"ArrowUp\")) {\n event.preventDefault();\n setPreviousSelectedMentionSuggestionIndex();\n }\n\n // Create a mention on Enter/Tab\n if (isKey(event, \"Enter\") || isKey(event, \"Tab\")) {\n event.preventDefault();\n\n const mention =\n mentionSuggestions?.[selectedMentionSuggestionIndex];\n createMention(mention);\n }\n\n // Close the suggestions on Escape\n if (isKey(event, \"Escape\")) {\n event.preventDefault();\n setMentionDraft(undefined);\n setSelectedMentionSuggestionIndex(0);\n }\n } else {\n if (hasFloatingToolbarRange) {\n // Close the floating toolbar on Escape\n if (isKey(event, \"Escape\")) {\n event.preventDefault();\n setHasFloatingToolbarRange(false);\n }\n }\n\n // Blur the editor on Escape\n if (isKey(event, \"Escape\")) {\n blur();\n }\n\n // Submit the editor on Enter\n if (isKey(event, \"Enter\", { shift: false })) {\n // Even if submitting is not possible, don't do anything else on Enter. (e.g. creating a new line)\n event.preventDefault();\n\n if (canSubmit) {\n submit();\n }\n }\n\n // Create a new line on Shift + Enter\n if (isKey(event, \"Enter\", { shift: true })) {\n event.preventDefault();\n editor.insertBreak();\n }\n\n // Toggle bold on Command/Control + B\n if (isKey(event, \"b\", { mod: true })) {\n event.preventDefault();\n toggleEditorMark(editor, \"bold\");\n }\n\n // Toggle italic on Command/Control + I\n if (isKey(event, \"i\", { mod: true })) {\n event.preventDefault();\n toggleEditorMark(editor, \"italic\");\n }\n\n // Toggle strikethrough on Command/Control + Shift + S\n if (isKey(event, \"s\", { mod: true, shift: true })) {\n event.preventDefault();\n toggleEditorMark(editor, \"strikethrough\");\n }\n\n // Toggle code on Command/Control + E\n if (isKey(event, \"e\", { mod: true })) {\n event.preventDefault();\n toggleEditorMark(editor, \"code\");\n }\n }\n },\n [\n onKeyDown,\n mentionDraft,\n mentionSuggestions,\n hasFloatingToolbarRange,\n editor,\n setNextSelectedMentionSuggestionIndex,\n setPreviousSelectedMentionSuggestionIndex,\n selectedMentionSuggestionIndex,\n createMention,\n setSelectedMentionSuggestionIndex,\n blur,\n canSubmit,\n submit,\n ]\n );\n\n const handleFocus = useCallback(\n (event: FocusEvent<HTMLDivElement>) => {\n onFocus?.(event);\n\n if (!event.isDefaultPrevented()) {\n setFocused(true);\n }\n },\n [onFocus, setFocused]\n );\n\n const handleBlur = useCallback(\n (event: FocusEvent<HTMLDivElement>) => {\n onBlur?.(event);\n\n if (!event.isDefaultPrevented()) {\n setFocused(false);\n }\n },\n [onBlur, setFocused]\n );\n\n const selectedMention =\n mentionSuggestions?.[selectedMentionSuggestionIndex];\n const selectedMentionId = selectedMention?.id;\n const setSelectedMentionId = useCallback(\n (mentionId: string) => {\n const index = mentionSuggestions?.findIndex(\n (mention) => mention.id === mentionId\n );\n\n if (index !== undefined && index >= 0) {\n setSelectedMentionSuggestionIndex(index);\n }\n },\n [setSelectedMentionSuggestionIndex, mentionSuggestions]\n );\n\n const additionalProps: AriaAttributes = useMemo(\n () =>\n mentionDraft\n ? {\n role: \"combobox\",\n \"aria-autocomplete\": \"list\",\n \"aria-expanded\": true,\n \"aria-controls\": suggestionsListId,\n \"aria-activedescendant\": suggestionsListItemId(selectedMentionId),\n }\n : hasFloatingToolbarRange\n ? {\n \"aria-haspopup\": true,\n \"aria-controls\": floatingToolbarId,\n }\n : {},\n [\n mentionDraft,\n suggestionsListId,\n suggestionsListItemId,\n selectedMentionId,\n hasFloatingToolbarRange,\n floatingToolbarId,\n ]\n );\n\n useImperativeHandle(forwardedRef, () => {\n return ReactEditor.toDOMNode(editor, editor) as HTMLDivElement;\n }, [editor]);\n\n // Manually focus the editor when `autoFocus` is true\n useLayoutEffect(() => {\n if (autoFocus) {\n focus();\n }\n }, [autoFocus, editor, focus]);\n\n // Manually add a selection in the editor if the selection\n // is still empty after being focused\n useLayoutEffect(() => {\n if (isFocused && editor.selection === null) {\n select();\n }\n }, [editor, select, isFocused]);\n\n const handleMentionSelect = useCallback(\n (mentionId: string) => {\n const mention = mentionSuggestions?.find(\n (mention) => mention.id === mentionId\n );\n\n createMention(mention);\n },\n [createMention, mentionSuggestions]\n );\n\n return (\n <Slate\n editor={editor}\n initialValue={initialEditorValue}\n onChange={handleChange}\n >\n <Editable\n dir={dir}\n enterKeyHint={mentionDraft ? \"enter\" : \"send\"}\n autoCapitalize=\"sentences\"\n aria-label=\"Composer editor\"\n data-focused={isFocused || undefined}\n data-disabled={isDisabled || undefined}\n {...additionalProps}\n {...props}\n readOnly={isDisabled}\n disabled={isDisabled}\n onKeyDown={handleKeyDown}\n onFocus={handleFocus}\n onBlur={handleBlur}\n renderElement={renderElement}\n renderLeaf={ComposerEditorLeaf}\n renderPlaceholder={ComposerEditorPlaceholder}\n />\n {hasResolveMentionSuggestions && (\n <ComposerEditorMentionSuggestionsWrapper\n dir={dir}\n mentionDraft={mentionDraft}\n setMentionDraft={setMentionDraft}\n selectedMentionId={selectedMentionId}\n setSelectedMentionId={setSelectedMentionId}\n mentions={mentionSuggestions}\n id={suggestionsListId}\n itemId={suggestionsListItemId}\n onItemSelect={handleMentionSelect}\n MentionSuggestions={MentionSuggestions}\n />\n )}\n {FloatingToolbar && (\n <ComposerEditorFloatingToolbarWrapper\n dir={dir}\n id={floatingToolbarId}\n hasFloatingToolbarRange={hasFloatingToolbarRange}\n setHasFloatingToolbarRange={setHasFloatingToolbarRange}\n FloatingToolbar={FloatingToolbar}\n />\n )}\n </Slate>\n );\n }\n);\n\nconst MAX_ATTACHMENTS = 10;\nconst MAX_ATTACHMENT_SIZE = 1024 * 1024 * 1024; // 1 GB\n\nfunction prepareAttachment(file: File): CommentLocalAttachment {\n return {\n type: \"localAttachment\",\n status: \"idle\",\n id: createCommentAttachmentId(),\n name: file.name,\n size: file.size,\n mimeType: file.type,\n file,\n };\n}\n\n/**\n * Surrounds the composer's content and handles submissions.\n *\n * @example\n * <Composer.Form onComposerSubmit={({ body }) => {}}>\n *\t <Composer.Editor />\n * <Composer.Submit />\n * </Composer.Form>\n */\nconst ComposerForm = forwardRef<HTMLFormElement, ComposerFormProps>(\n (\n {\n children,\n onSubmit,\n onComposerSubmit,\n defaultAttachments = [],\n pasteFilesAsAttachments,\n blurOnSubmit = true,\n preventUnsavedChanges = true,\n disabled,\n asChild,\n roomId: _roomId,\n ...props\n },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"form\";\n const [isEmpty, setEmpty] = useState(true);\n const [isSubmitting, setSubmitting] = useState(false);\n const [isFocused, setFocused] = useState(false);\n const room = useRoom({ allowOutsideRoom: true });\n\n const roomId = _roomId !== undefined ? _roomId : room?.id;\n if (roomId === undefined) {\n throw new Error(\"Composer.Form must be a descendant of RoomProvider.\");\n }\n\n // Later: Offer as Composer.Form props: { maxAttachments: number; maxAttachmentSize: number; supportedAttachmentMimeTypes: string[]; }\n const maxAttachments = MAX_ATTACHMENTS;\n const maxAttachmentSize = MAX_ATTACHMENT_SIZE;\n\n const {\n attachments,\n isUploadingAttachments,\n addAttachments,\n removeAttachment,\n clearAttachments,\n } = useComposerAttachmentsManager(defaultAttachments, {\n maxFileSize: maxAttachmentSize,\n roomId,\n });\n const numberOfAttachments = attachments.length;\n const hasMaxAttachments = numberOfAttachments >= maxAttachments;\n\n const isDisabled = useMemo(() => {\n return isSubmitting || disabled === true;\n }, [isSubmitting, disabled]);\n const canSubmit = useMemo(() => {\n return !isEmpty && !isUploadingAttachments;\n }, [isEmpty, isUploadingAttachments]);\n const [marks, setMarks] = useState<ComposerBodyMarks>(getComposerBodyMarks);\n\n const ref = useRef<HTMLFormElement>(null);\n const mergedRefs = useRefs(forwardedRef, ref);\n const fileInputRef = useRef<HTMLInputElement>(null);\n const syncSource = useSyncSource();\n\n // Mark the composer as a pending update when it has unsubmitted (draft)\n // text or attachments\n const isPending = !preventUnsavedChanges\n ? false\n : !isEmpty || isUploadingAttachments || attachments.length > 0;\n\n useEffect(() => {\n syncSource?.setSyncStatus(\n isPending ? \"has-local-changes\" : \"synchronized\"\n );\n }, [syncSource, isPending]);\n\n const createAttachments = useCallback(\n (files: File[]) => {\n if (!files.length) {\n return;\n }\n\n const numberOfAcceptedFiles = Math.max(\n 0,\n maxAttachments - numberOfAttachments\n );\n\n files.splice(numberOfAcceptedFiles);\n\n const attachments = files.map((file) => prepareAttachment(file));\n\n addAttachments(attachments);\n },\n [addAttachments, maxAttachments, numberOfAttachments]\n );\n\n const createAttachmentsRef = useRef(createAttachments);\n\n useEffect(() => {\n createAttachmentsRef.current = createAttachments;\n }, [createAttachments]);\n\n const stableCreateAttachments = useCallback((files: File[]) => {\n createAttachmentsRef.current(files);\n }, []);\n\n const editor = useInitial(() =>\n createComposerEditor({\n createAttachments: stableCreateAttachments,\n pasteFilesAsAttachments,\n })\n );\n const onEditorChange = useInitial(makeEventSource) as EventSource<void>;\n\n const validate = useCallback(\n (value: SlateElement[]) => {\n setEmpty(isEditorEmpty(editor, value));\n },\n [editor]\n );\n\n const submit = useCallback(() => {\n if (!canSubmit) {\n return;\n }\n\n // We need to wait for the next frame in some cases like when composing diacritics,\n // we want any native handling to be done first while still being handled on `keydown`.\n requestAnimationFrame(() => {\n if (ref.current) {\n requestSubmit(ref.current);\n }\n });\n }, [canSubmit]);\n\n const clear = useCallback(() => {\n SlateTransforms.delete(editor, {\n at: {\n anchor: SlateEditor.start(editor, []),\n focus: SlateEditor.end(editor, []),\n },\n });\n }, [editor]);\n\n const select = useCallback(() => {\n SlateTransforms.select(editor, SlateEditor.end(editor, []));\n }, [editor]);\n\n const focus = useCallback(\n (resetSelection = true) => {\n try {\n // Slate's `ReactEditor.focus` method can use `setTimeout` internally\n // which prevents us from catching errors, so this is a reimplementation.\n // https://github.com/ianstormtaylor/slate/blob/main/packages/slate-dom/src/plugin/dom-editor.ts\n if (!ReactEditor.isFocused(editor)) {\n SlateTransforms.select(\n editor,\n resetSelection || !editor.selection\n ? SlateEditor.end(editor, [])\n : editor.selection\n );\n\n const element = ReactEditor.toDOMNode(editor, editor);\n\n if (editor.selection) {\n const domSelection = window.getSelection();\n const domRange = getDOMRange(editor, editor.selection);\n\n if (domRange) {\n domSelection?.removeAllRanges();\n domSelection?.addRange(domRange);\n }\n }\n\n element.focus({ preventScroll: true });\n }\n } catch {\n // Slate's DOM-specific methods will throw if the editor's DOM\n // node no longer exists. This action doesn't make sense on an\n // unmounted editor so we can safely ignore it.\n }\n },\n [editor]\n );\n\n const blur = useCallback(() => {\n try {\n ReactEditor.blur(editor);\n } catch {\n // Slate's DOM-specific methods will throw if the editor's DOM\n // node no longer exists. This action doesn't make sense on an\n // unmounted editor so we can safely ignore it.\n }\n }, [editor]);\n\n const createMention = useCallback(() => {\n if (disabled) {\n return;\n }\n\n focus();\n insertMentionCharacter(editor);\n }, [disabled, editor, focus]);\n\n const insertText = useCallback(\n (text: string) => {\n if (disabled) {\n return;\n }\n\n focus(false);\n insertSlateText(editor, text);\n },\n [disabled, editor, focus]\n );\n\n const attachFiles = useCallback(() => {\n if (disabled) {\n return;\n }\n\n if (fileInputRef.current) {\n fileInputRef.current.click();\n }\n }, [disabled]);\n\n const handleAttachmentsInputChange = useCallback(\n (event: ChangeEvent<HTMLInputElement>) => {\n if (disabled) {\n return;\n }\n\n if (event.target.files) {\n createAttachments(Array.from(event.target.files));\n\n // Reset the input value to allow selecting the same file(s) again\n event.target.value = \"\";\n }\n },\n [createAttachments, disabled]\n );\n\n const onSubmitEnd = useCallback(() => {\n clear();\n clearAttachments();\n setSubmitting(false);\n\n if (blurOnSubmit) {\n blur();\n }\n }, [blur, blurOnSubmit, clear, clearAttachments]);\n\n const handleSubmit = useCallback(\n (event: FormEvent<HTMLFormElement>) => {\n if (disabled) {\n return;\n }\n\n // In some situations (e.g. pressing Enter while composing diacritics), it's possible\n // for the form to be submitted as empty even though we already checked whether the\n // editor was empty when handling the key press.\n const isEmpty = isEditorEmpty(editor, editor.children);\n\n // We even prevent the user's `onSubmit` handler from being called if the editor is empty.\n if (isEmpty) {\n event.preventDefault();\n\n return;\n }\n\n onSubmit?.(event);\n\n if (!onComposerSubmit || event.isDefaultPrevented()) {\n event.preventDefault();\n\n return;\n }\n\n const body = composerBodyToCommentBody(\n editor.children as ComposerBodyData\n );\n // Only non-local attachments are included to be submitted.\n const commentAttachments: CommentAttachment[] = attachments\n .filter(\n (attachment) =>\n attachment.type === \"attachment\" ||\n (attachment.type === \"localAttachment\" &&\n attachment.status === \"uploaded\")\n )\n .map((attachment) => {\n return {\n id: attachment.id,\n type: \"attachment\",\n mimeType: attachment.mimeType,\n size: attachment.size,\n name: attachment.name,\n };\n });\n\n const promise = onComposerSubmit(\n { body, attachments: commentAttachments },\n event\n );\n\n event.preventDefault();\n\n if (promise) {\n setSubmitting(true);\n promise.then(onSubmitEnd);\n } else {\n onSubmitEnd();\n }\n },\n [disabled, editor, attachments, onComposerSubmit, onSubmit, onSubmitEnd]\n );\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n const toggleMark = useCallback(\n (mark: ComposerBodyMark) => {\n toggleEditorMark(editor, mark);\n },\n [editor]\n );\n\n useObservable(onEditorChange, () => {\n setMarks(getComposerBodyMarks(editor));\n });\n\n return (\n <ComposerEditorContext.Provider\n value={{\n editor,\n validate,\n setFocused,\n onEditorChange,\n roomId,\n }}\n >\n <ComposerAttachmentsContext.Provider\n value={{\n createAttachments,\n isUploadingAttachments,\n hasMaxAttachments,\n maxAttachments,\n maxAttachmentSize,\n }}\n >\n <ComposerContext.Provider\n value={{\n isDisabled,\n isFocused,\n isEmpty,\n canSubmit,\n submit,\n clear,\n select,\n focus,\n blur,\n createMention,\n insertText,\n attachments,\n attachFiles,\n removeAttachment,\n toggleMark,\n marks,\n }}\n >\n <Component {...props} onSubmit={handleSubmit} ref={mergedRefs}>\n <input\n type=\"file\"\n multiple\n ref={fileInputRef}\n onChange={handleAttachmentsInputChange}\n onClick={stopPropagation}\n tabIndex={-1}\n style={{ display: \"none\" }}\n />\n <Slottable>{children}</Slottable>\n </Component>\n </ComposerContext.Provider>\n </ComposerAttachmentsContext.Provider>\n </ComposerEditorContext.Provider>\n );\n }\n);\n\n/**\n * A button to submit the composer.\n *\n * @example\n * <Composer.Submit>Send</Composer.Submit>\n */\nconst ComposerSubmit = forwardRef<HTMLButtonElement, ComposerSubmitProps>(\n ({ children, disabled, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"button\";\n const { canSubmit, isDisabled: isComposerDisabled } = useComposer();\n const isDisabled = isComposerDisabled || disabled || !canSubmit;\n\n return (\n <Component\n type=\"submit\"\n {...props}\n ref={forwardedRef}\n disabled={isDisabled}\n >\n {children}\n </Component>\n );\n }\n);\n\n/**\n * A button which opens a file picker to create attachments.\n *\n * @example\n * <Composer.AttachFiles>Attach files</Composer.AttachFiles>\n */\nconst ComposerAttachFiles = forwardRef<\n HTMLButtonElement,\n ComposerAttachFilesProps\n>(({ children, onClick, disabled, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"button\";\n const { hasMaxAttachments } = useComposerAttachmentsContext();\n const { isDisabled: isComposerDisabled, attachFiles } = useComposer();\n const isDisabled = isComposerDisabled || hasMaxAttachments || disabled;\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n\n if (!event.isDefaultPrevented()) {\n attachFiles();\n }\n },\n [attachFiles, onClick]\n );\n\n return (\n <Component\n type=\"button\"\n {...props}\n onClick={handleClick}\n ref={forwardedRef}\n disabled={isDisabled}\n >\n {children}\n </Component>\n );\n});\n\n/**\n * A drop area which accepts files to create attachments.\n *\n * @example\n * <Composer.AttachmentsDropArea>\n * Drop files here\n * </Composer.AttachmentsDropArea>\n */\nconst ComposerAttachmentsDropArea = forwardRef<\n HTMLDivElement,\n ComposerAttachmentsDropAreaProps\n>(\n (\n {\n onDragEnter,\n onDragLeave,\n onDragOver,\n onDrop,\n disabled,\n asChild,\n ...props\n },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"div\";\n const { isDisabled: isComposerDisabled } = useComposer();\n const isDisabled = isComposerDisabled || disabled;\n const [, dropAreaProps] = useComposerAttachmentsDropArea({\n onDragEnter,\n onDragLeave,\n onDragOver,\n onDrop,\n disabled: isDisabled,\n });\n\n return (\n <Component\n {...dropAreaProps}\n data-disabled={isDisabled ? \"\" : undefined}\n {...props}\n ref={forwardedRef}\n />\n );\n }\n);\n\n/**\n * A toggle button which toggles a specific text mark.\n *\n * @example\n * <Composer.MarkToggle mark=\"bold\">\n * Bold\n * </Composer.MarkToggle>\n */\nconst ComposerMarkToggle = forwardRef<\n HTMLButtonElement,\n ComposerMarkToggleProps\n>(\n (\n {\n children,\n mark,\n onValueChange,\n onClick,\n onPointerDown,\n asChild,\n ...props\n },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"button\";\n const { marks, toggleMark } = useComposer();\n\n const handlePointerDown = useCallback(\n (event: PointerEvent<HTMLButtonElement>) => {\n onPointerDown?.(event);\n\n event.preventDefault();\n event.stopPropagation();\n },\n [onPointerDown]\n );\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n\n if (!event.isDefaultPrevented()) {\n event.preventDefault();\n event.stopPropagation();\n\n toggleMark(mark);\n onValueChange?.(mark);\n }\n },\n [mark, onClick, onValueChange, toggleMark]\n );\n\n return (\n <TogglePrimitive.Root\n asChild\n pressed={marks[mark]}\n onClick={handleClick}\n onPointerDown={handlePointerDown}\n {...props}\n >\n <Component {...props} ref={forwardedRef}>\n {children}\n </Component>\n </TogglePrimitive.Root>\n );\n }\n);\n\nif (process.env.NODE_ENV !== \"production\") {\n ComposerAttachFiles.displayName = COMPOSER_ATTACH_FILES_NAME;\n ComposerAttachmentsDropArea.displayName = COMPOSER_ATTACHMENTS_DROP_AREA_NAME;\n ComposerEditor.displayName = COMPOSER_EDITOR_NAME;\n ComposerFloatingToolbar.displayName = COMPOSER_FLOATING_TOOLBAR_NAME;\n ComposerForm.displayName = COMPOSER_FORM_NAME;\n ComposerMention.displayName = COMPOSER_MENTION_NAME;\n ComposerLink.displayName = COMPOSER_LINK_NAME;\n ComposerSubmit.displayName = COMPOSER_SUBMIT_NAME;\n ComposerSuggestions.displayName = COMPOSER_SUGGESTIONS_NAME;\n ComposerSuggestionsList.displayName = COMPOSER_SUGGESTIONS_LIST_NAME;\n ComposerSuggestionsListItem.displayName = COMPOSER_SUGGESTIONS_LIST_ITEM_NAME;\n ComposerMarkToggle.displayName = COMPOSER_MARK_TOGGLE_NAME;\n}\n\n// NOTE: Every export from this file will be available publicly as Composer.*\nexport {\n ComposerAttachFiles as AttachFiles,\n ComposerAttachmentsDropArea as AttachmentsDropArea,\n ComposerEditor as Editor,\n ComposerFloatingToolbar as FloatingToolbar,\n ComposerForm as Form,\n ComposerLink as Link,\n ComposerMarkToggle as MarkToggle,\n ComposerMention as Mention,\n ComposerSubmit as Submit,\n ComposerSuggestions as Suggestions,\n ComposerSuggestionsList as SuggestionsList,\n ComposerSuggestionsListItem as SuggestionsListItem,\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsJA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAsC;AAC3B;AAEX;AAEA;AAA8B;AAC5B;AAEF;AAIE;AAAO;AACL;AACE;AACE;AACE;AACE;AACoD;AAChD;AACA;AACD;AACH;AACF;AACF;AACF;AACF;AAEJ;AAEA;AAAsC;AACpC;AACA;AACA;AAEF;AACE;AACA;AAEA;AAEK;AAEG;AACH;AAGP;AAEA;AAAmC;AACjC;AACA;AACA;AAEF;AACE;AAEA;AAKF;AAEA;AAAiD;AAC/C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACW;AACX;AAEF;AACE;AACA;AACA;AACA;AACA;AAEA;AAAM;AAC8B;AAClC;AACA;AACA;AACA;AACA;AACA;AACA;AACyB;AACzB;AACA;AACW;AACL;AAGR;AACE;AAAkD;AAGpD;AACE;AACE;AAEA;AAAA;AAGF;AACA;AAA6B;AAK/B;AACE;AAAa;AAEb;AAIA;AACE;AAAA;AAIF;AACA;AAGA;AAGA;AACE;AACA;AACE;AAGJ;AACE;AAAmC;AACrC;AAGF;AAGM;AAA4B;AAA3B;AACQ;AACL;AACA;AACe;AACG;AAClB;AACA;AACA;AACK;AACP;AAEA;AAAC;AAAA;AACM;AACE;AACK;AACL;AACC;AAGF;AACM;AACF;AACV;AAEA;AAAC;AAAA;AACC;AACA;AAAA;AACF;AAAA;AACF;AAAA;AAKV;AAEA;AAA8C;AAC5C;AACW;AACX;AACA;AACA;AAEF;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AAAM;AAC8B;AAClC;AACA;AACA;AACA;AACA;AACyB;AACnB;AACN;AACA;AACW;AACL;AAGR;AACE;AACE;AAAA;AAGF;AACA;AAEA;AACA;AAEA;AACE;AACA;AAAyD;AAC3D;AAGF;AAEE;AAGA;AACE;AAGA;AAME;AACA;AAAiB;AAEjB;AAEA;AACA;AAAqB;AACvB;AACD;AAGH;AAGM;AAAgC;AAA/B;AACQ;AACL;AACA;AACA;AACK;AACP;AAEA;AAAC;AAAA;AACM;AACE;AACK;AACL;AACC;AAGF;AACM;AACF;AACV;AAEiB;AAAA;AACnB;AAAA;AAKV;AAWM;AAIJ;AACA;AACA;AAAM;AACJ;AACK;AACL;AACA;AAEF;AACA;AAAsB;AACgC;AAC1C;AAEZ;AACA;AAEA;AAA0B;AAEtB;AAEA;AACA;AAAsB;AACxB;AACc;AAGhB;AACE;AAAC;AAAA;AACC;AACK;AACL;AACW;AACP;AACW;AACkB;AACtB;AACC;AACL;AACI;AACM;AACL;AACC;AACR;AACL;AACK;AAEJ;AAAA;AAGP;AAEA;AAA+B;AAC7B;AACA;AAEF;AAEE;AAEA;AAAsB;AAElB;AACE;AAAC;AAAA;AACC;AACK;AAAA;AACP;AAEC;AAEH;AACE;AAAC;AAAA;AACC;AACK;AAAA;AAGP;AAGF;AAGE;AAGF;AAAO;AAEb;AAGA;AAA4B;AAC1B;AACA;AAEF;AACE;AACE;AAA6B;AAG/B;AACE;AAAyB;AAG3B;AACE;AAAwB;AAG1B;AACE;AAA2B;AAG7B;AACF;AAEA;AAAmC;AACjC;AAEF;AACE;AAEA;AAKF;AAQA;AAAwB;AAEpB;AACA;AAEA;AACE;AAAC;AAAA;AAC8B;AACzB;AACC;AAEJ;AAAA;AACH;AAGN;AAQA;AAAqB;AAEjB;AAEA;AACE;AAAC;AAAA;AACQ;AACH;AACA;AACC;AAEJ;AAAA;AACH;AAGN;AAKM;AAIJ;AACA;AACA;AAAM;AACC;AACL;AACA;AAEF;AACA;AAAsB;AACgC;AAC1C;AAEZ;AACA;AAEA;AACE;AAAC;AAAA;AACC;AACI;AAC6B;AACtB;AACC;AACL;AACI;AACM;AACJ;AACA;AACR;AACL;AACK;AAEJ;AAAA;AAGP;AAcM;AAIJ;AACA;AAEA;AACE;AAAC;AAAA;AACM;AACL;AACW;AACP;AACC;AAEJ;AAAA;AAGP;AAUA;AAAoC;AAKhC;AACE;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AAEA;AACA;AAAmB;AACO;AACH;AAGvB;AAEA;AACE;AACE;AAA+C;AACjD;AAGF;AAA0B;AAEtB;AAEA;AACE;AAAsB;AACxB;AACF;AACuC;AAGzC;AAA0B;AAEtB;AAEA;AACA;AAAsB;AACxB;AACc;AAGhB;AAAoB;AAEhB;AAEA;AAEA;AACA;AAEA;AACE;AAAkB;AACpB;AACF;AAC6B;AAG/B;AACE;AAAC;AAAA;AACM;AACL;AAC6B;AACA;AACd;AACA;AACN;AACL;AACC;AAEJ;AAAA;AACH;AAGN;AAEA;AAA0D;AAEtD;AAA2C;AAC7C;AAEE;AAEK;AAAA;AACQ;AACX;AAEJ;AAEE;AAUI;AAER;AAQA;AAAuB;AAEnB;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AAEA;AAAM;AACJ;AACA;AACA;AACA;AACA;AACY;AACZ;AAEF;AACA;AACA;AACE;AAA4C;AAE9C;AAA+D;AACV;AACxC;AAGb;AAIA;AACA;AAGA;AACA;AAA2B;AACzB;AACc;AAEhB;AAAM;AACJ;AACA;AACA;AACA;AAEF;AACA;AACA;AACA;AAA8B;AAItB;AACH;AAGL;AAAsB;AAElB;AACkE;AAEpE;AACc;AAGhB;AAAqB;AAEjB;AAKA;AAAsB;AACxB;AACyB;AAG3B;AAAsB;AAElB;AACE;AAAA;AAGF;AACA;AACA;AACA;AAAmC;AACrC;AACwD;AAG1D;AAAsB;AAElB;AAEA;AACE;AAAA;AAIF;AACE;AAA6B;AAI/B;AACE;AAA2B;AAG7B;AAEE;AACE;AACA;AAAsC;AAIxC;AACE;AACA;AAA0C;AAI5C;AACE;AAEA;AAEA;AAAqB;AAIvB;AACE;AACA;AACA;AAAmC;AACrC;AAEA;AAEE;AACE;AACA;AAAgC;AAClC;AAIF;AACE;AAAK;AAIP;AAEE;AAEA;AACE;AAAO;AACT;AAIF;AACE;AACA;AAAmB;AAIrB;AACE;AACA;AAA+B;AAIjC;AACE;AACA;AAAiC;AAInC;AACE;AACA;AAAwC;AAI1C;AACE;AACA;AAA+B;AACjC;AACF;AACF;AACA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;AAGF;AAAoB;AAEhB;AAEA;AACE;AAAe;AACjB;AACF;AACoB;AAGtB;AAAmB;AAEf;AAEA;AACE;AAAgB;AAClB;AACF;AACmB;AAGrB;AAEA;AACA;AAA6B;AAEzB;AAAkC;AACJ;AAG9B;AACE;AAAuC;AACzC;AACF;AACsD;AAGxD;AAAwC;AAGhC;AACQ;AACe;AACJ;AACA;AAC+C;AAGhE;AACmB;AACA;AAElB;AACT;AACE;AACA;AACA;AACA;AACA;AACA;AACF;AAGF;AACE;AAA2C;AAI7C;AACE;AACE;AAAM;AACR;AAKF;AACE;AACE;AAAO;AACT;AAGF;AAA4B;AAExB;AAAoC;AACN;AAG9B;AAAqB;AACvB;AACkC;AAGpC;AACE;AAAC;AAAA;AACC;AACc;AACJ;AAEV;AAAA;AAAC;AAAA;AACC;AACuC;AACxB;AACJ;AACgB;AACE;AACzB;AACA;AACM;AACA;AACC;AACF;AACD;AACR;AACY;AACO;AAAA;AACrB;AAEE;AAAC;AAAA;AACC;AACA;AACA;AACA;AACA;AACU;AACN;AACI;AACM;AACd;AAAA;AACF;AAGA;AAAC;AAAA;AACC;AACI;AACJ;AACA;AACA;AAAA;AACF;AAAA;AAAA;AAEJ;AAGN;AAEA;AACA;AAEA;AACE;AAAO;AACC;AACE;AACsB;AACnB;AACA;AACI;AACf;AAEJ;AAWA;AAAqB;AAEjB;AACE;AACA;AACA;AACsB;AACtB;AACe;AACS;AACxB;AACA;AACQ;AACL;AAIL;AACA;AACA;AACA;AACA;AAEA;AACA;AACE;AAAqE;AAIvE;AACA;AAEA;AAAM;AACJ;AACA;AACA;AACA;AACA;AACoD;AACvC;AACb;AAEF;AACA;AAEA;AACE;AAAoC;AAEtC;AACE;AAAoB;AAEtB;AAEA;AACA;AACA;AACA;AAIA;AAIA;AACE;AAAY;AACwB;AACpC;AAGF;AAA0B;AAEtB;AACE;AAAA;AAGF;AAAmC;AACjC;AACiB;AAGnB;AAEA;AAEA;AAA0B;AAC5B;AACoD;AAGtD;AAEA;AACE;AAA+B;AAGjC;AACE;AAAkC;AAGpC;AAAe;AACQ;AACA;AACnB;AACD;AAEH;AAEA;AAAiB;AAEb;AAAqC;AACvC;AACO;AAGT;AACE;AACE;AAAA;AAKF;AACE;AACE;AAAyB;AAC3B;AACD;AAGH;AACE;AAA+B;AACzB;AACkC;AACH;AACnC;AACD;AAGH;AACE;AAA0D;AAG5D;AAAc;AAEV;AAIE;AACE;AAAgB;AACd;AAGW;AAGb;AAEA;AACE;AACA;AAEA;AACE;AACA;AAA+B;AACjC;AAGF;AAAqC;AACvC;AACM;AAIR;AACF;AACO;AAGT;AACE;AACE;AAAuB;AACjB;AAIR;AAGF;AACE;AACE;AAAA;AAGF;AACA;AAA6B;AAG/B;AAAmB;AAEf;AACE;AAAA;AAGF;AACA;AAA4B;AAC9B;AACwB;AAG1B;AACE;AACE;AAAA;AAGF;AACE;AAA2B;AAC7B;AAGF;AAAqC;AAEjC;AACE;AAAA;AAGF;AACE;AAGA;AAAqB;AACvB;AACF;AAC4B;AAG9B;AACE;AACA;AACA;AAEA;AACE;AAAK;AACP;AAGF;AAAqB;AAEjB;AACE;AAAA;AAMF;AAGA;AACE;AAEA;AAAA;AAGF;AAEA;AACE;AAEA;AAAA;AAGF;AAAa;AACJ;AAGT;AACG;AAI2B;AAG1B;AAAO;AACU;AACT;AACe;AACJ;AACA;AACnB;AAGJ;AAAgB;AAC0B;AACxC;AAGF;AAEA;AACE;AACA;AAAwB;AAExB;AAAY;AACd;AACF;AACuE;AAGzE;AACE;AAAsB;AAGxB;AAAmB;AAEf;AAA6B;AAC/B;AACO;AAGT;AACE;AAAqC;AAGvC;AACE;AAAuB;AAAtB;AACQ;AACL;AACA;AACA;AACA;AACA;AACF;AAEA;AAA4B;AAA3B;AACQ;AACL;AACA;AACA;AACA;AACA;AACF;AAEA;AAAiB;AAAhB;AACQ;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;AAGE;AAAA;AAAC;AAAA;AACM;AACG;AACH;AACK;AACD;AACC;AACe;AAAA;AAC3B;AACqB;AACvB;AAAA;AACF;AAAA;AACF;AAAA;AACF;AAGN;AAQA;AAAuB;AAEnB;AACA;AACA;AAEA;AACE;AAAC;AAAA;AACM;AACD;AACC;AACK;AAET;AAAA;AACH;AAGN;AAQM;AAIJ;AACA;AACA;AACA;AAEA;AAAoB;AAEhB;AAEA;AACE;AAAY;AACd;AACF;AACqB;AAGvB;AACE;AAAC;AAAA;AACM;AACD;AACK;AACJ;AACK;AAET;AAAA;AAGP;AAUA;AAAoC;AAKhC;AACE;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AAAyD;AACvD;AACA;AACA;AACA;AACU;AAGZ;AACE;AAAC;AAAA;AACK;AAC6B;AAC7B;AACC;AAAA;AACP;AAGN;AAUA;AAA2B;AAKvB;AACE;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AAEA;AAA0B;AAEtB;AAEA;AACA;AAAsB;AACxB;AACc;AAGhB;AAAoB;AAEhB;AAEA;AACE;AACA;AAEA;AACA;AAAoB;AACtB;AACF;AACyC;AAG3C;AACE;AAAiB;AAAhB;AACQ;AACY;AACV;AACM;AACX;AAIJ;AAAA;AACF;AAGN;AAEA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;;"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/primitives/Composer/index.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n type CommentAttachment,\n type CommentBody,\n type CommentLocalAttachment,\n createCommentAttachmentId,\n type EventSource,\n makeEventSource,\n MENTION_CHARACTER,\n type MentionData,\n sanitizeUrl,\n} from \"@liveblocks/core\";\nimport { useRoom } from \"@liveblocks/react\";\nimport {\n useClientOrNull,\n useLayoutEffect,\n useMentionSuggestions,\n useResolveMentionSuggestions,\n useSyncSource,\n} from \"@liveblocks/react/_private\";\nimport { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport * as TogglePrimitive from \"@radix-ui/react-toggle\";\nimport type {\n AriaAttributes,\n ChangeEvent,\n FocusEvent,\n FormEvent,\n KeyboardEvent,\n MouseEvent,\n PointerEvent,\n SyntheticEvent,\n} from \"react\";\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type {\n Descendant as SlateDescendant,\n Element as SlateElement,\n} from \"slate\";\nimport {\n createEditor,\n Editor as SlateEditor,\n insertText as insertSlateText,\n Range as SlateRange,\n Transforms as SlateTransforms,\n} from \"slate\";\nimport { withHistory } from \"slate-history\";\nimport type {\n RenderElementProps,\n RenderElementSpecificProps,\n RenderLeafSpecificProps,\n RenderPlaceholderProps,\n} from \"slate-react\";\nimport {\n Editable,\n ReactEditor,\n Slate,\n useSelected,\n useSlateStatic,\n withReact,\n} from \"slate-react\";\n\nimport type {\n ComposerBody as ComposerBodyData,\n ComposerBodyAutoLink,\n ComposerBodyCustomLink,\n ComposerBodyMark,\n ComposerBodyMarks,\n ComposerBodyMention,\n ComposerBodyText,\n} from \"../../types\";\nimport { isKey } from \"../../utils/is-key\";\nimport { Persist, useAnimationPersist, usePersist } from \"../../utils/Persist\";\nimport { Portal } from \"../../utils/Portal\";\nimport { requestSubmit } from \"../../utils/request-submit\";\nimport { useIndex } from \"../../utils/use-index\";\nimport { useInitial } from \"../../utils/use-initial\";\nimport { useObservable } from \"../../utils/use-observable\";\nimport { useRefs } from \"../../utils/use-refs\";\nimport { withEmptyClearFormatting } from \"../slate/plugins/empty-clear-formatting\";\nimport { withNormalize } from \"../slate/plugins/normalize\";\nimport { getDOMRange } from \"../slate/utils/get-dom-range\";\nimport { isEmpty as isEditorEmpty } from \"../slate/utils/is-empty\";\nimport {\n getComposerBodyMarks,\n leaveMarkEdge,\n toggleMark as toggleEditorMark,\n} from \"../slate/utils/marks\";\nimport {\n ComposerAttachmentsContext,\n ComposerContext,\n ComposerEditorContext,\n ComposerFloatingToolbarContext,\n ComposerSuggestionsContext,\n useComposer,\n useComposerAttachmentsContext,\n useComposerEditorContext,\n useComposerFloatingToolbarContext,\n useComposerSuggestionsContext,\n} from \"./contexts\";\nimport { withAutoFormatting } from \"./slate/plugins/auto-formatting\";\nimport { withAutoLinks } from \"./slate/plugins/auto-links\";\nimport { withCustomLinks } from \"./slate/plugins/custom-links\";\nimport type { MentionDraft } from \"./slate/plugins/mentions\";\nimport {\n getMentionDraftAtSelection,\n insertMention,\n insertMentionCharacter,\n withMentions,\n} from \"./slate/plugins/mentions\";\nimport { withPaste } from \"./slate/plugins/paste\";\nimport type {\n ComposerAttachFilesProps,\n ComposerAttachmentsDropAreaProps,\n ComposerEditorComponents,\n ComposerEditorElementProps,\n ComposerEditorFloatingToolbarWrapperProps,\n ComposerEditorLinkWrapperProps,\n ComposerEditorMentionSuggestionsWrapperProps,\n ComposerEditorMentionWrapperProps,\n ComposerEditorProps,\n ComposerFloatingToolbarProps,\n ComposerFormProps,\n ComposerLinkProps,\n ComposerMarkToggleProps,\n ComposerMentionProps,\n ComposerSubmitProps,\n ComposerSuggestionsListItemProps,\n ComposerSuggestionsListProps,\n ComposerSuggestionsProps,\n FloatingPosition,\n} from \"./types\";\nimport {\n commentBodyToComposerBody,\n composerBodyToCommentBody,\n getSideAndAlignFromFloatingPlacement,\n useComposerAttachmentsDropArea,\n useComposerAttachmentsManager,\n useContentZIndex,\n useFloatingWithOptions,\n} from \"./utils\";\n\nconst MENTION_SUGGESTIONS_POSITION: FloatingPosition = \"top\";\n\nconst FLOATING_TOOLBAR_POSITION: FloatingPosition = \"top\";\n\nconst COMPOSER_MENTION_NAME = \"ComposerMention\";\nconst COMPOSER_LINK_NAME = \"ComposerLink\";\nconst COMPOSER_FLOATING_TOOLBAR_NAME = \"ComposerFloatingToolbar\";\nconst COMPOSER_SUGGESTIONS_NAME = \"ComposerSuggestions\";\nconst COMPOSER_SUGGESTIONS_LIST_NAME = \"ComposerSuggestionsList\";\nconst COMPOSER_SUGGESTIONS_LIST_ITEM_NAME = \"ComposerSuggestionsListItem\";\nconst COMPOSER_SUBMIT_NAME = \"ComposerSubmit\";\nconst COMPOSER_EDITOR_NAME = \"ComposerEditor\";\nconst COMPOSER_ATTACH_FILES_NAME = \"ComposerAttachFiles\";\nconst COMPOSER_ATTACHMENTS_DROP_AREA_NAME = \"ComposerAttachmentsDropArea\";\nconst COMPOSER_MARK_TOGGLE_NAME = \"ComposerMarkToggle\";\nconst COMPOSER_FORM_NAME = \"ComposerForm\";\n\nconst emptyCommentBody: CommentBody = {\n version: 1,\n content: [{ type: \"paragraph\", children: [{ text: \"\" }] }],\n};\n\nfunction createComposerEditor({\n createAttachments,\n pasteFilesAsAttachments,\n}: {\n createAttachments: (files: File[]) => void;\n pasteFilesAsAttachments?: boolean;\n}) {\n return withNormalize(\n withMentions(\n withCustomLinks(\n withAutoLinks(\n withAutoFormatting(\n withEmptyClearFormatting(\n withPaste(withHistory(withReact(createEditor())), {\n createAttachments,\n pasteFilesAsAttachments,\n })\n )\n )\n )\n )\n )\n );\n}\n\nfunction ComposerEditorMentionWrapper({\n Mention,\n attributes,\n children,\n element,\n}: ComposerEditorMentionWrapperProps) {\n const isSelected = useSelected();\n const { children: _, ...mention } = element;\n\n return (\n <span {...attributes}>\n {element.id ? (\n <Mention mention={mention} isSelected={isSelected} />\n ) : null}\n {children}\n </span>\n );\n}\n\nfunction ComposerEditorLinkWrapper({\n Link,\n attributes,\n element,\n children,\n}: ComposerEditorLinkWrapperProps) {\n const href = useMemo(() => sanitizeUrl(element.url) ?? \"\", [element.url]);\n\n return (\n <span {...attributes}>\n <Link href={href}>{children}</Link>\n </span>\n );\n}\n\nfunction ComposerEditorMentionSuggestionsWrapper({\n id,\n itemId,\n mentions,\n selectedMentionId,\n setSelectedMentionId,\n mentionDraft,\n setMentionDraft,\n onItemSelect,\n position = MENTION_SUGGESTIONS_POSITION,\n dir,\n MentionSuggestions,\n}: ComposerEditorMentionSuggestionsWrapperProps) {\n const editor = useSlateStatic();\n const { onEditorChange } = useComposerEditorContext();\n const { isFocused } = useComposer();\n const [contentRef, contentZIndex] = useContentZIndex();\n const isOpen =\n isFocused && mentionDraft?.range !== undefined && mentions !== undefined;\n const {\n refs: { setReference, setFloating },\n strategy,\n isPositioned,\n placement,\n x,\n y,\n update,\n elements,\n } = useFloatingWithOptions({\n position,\n dir,\n alignment: \"start\",\n open: isOpen,\n });\n\n useObservable(onEditorChange, () => {\n setMentionDraft(getMentionDraftAtSelection(editor));\n });\n\n useLayoutEffect(() => {\n if (!mentionDraft) {\n setReference(null);\n\n return;\n }\n\n const domRange = getDOMRange(editor, mentionDraft.range);\n setReference(domRange ?? null);\n }, [setReference, editor, mentionDraft]);\n\n // Manually update the placement when the number of suggestions changes\n // This can prevent the list of suggestions from scrolling instead of moving to the other placement\n useLayoutEffect(() => {\n if (!isOpen) return;\n\n const mentionSuggestions = elements.floating?.firstChild as\n | HTMLElement\n | undefined;\n\n if (!mentionSuggestions) {\n return;\n }\n\n // Force the mention suggestions to grow instead of scrolling\n mentionSuggestions.style.overflowY = \"visible\";\n mentionSuggestions.style.maxHeight = \"none\";\n\n // Trigger a placement update\n update();\n\n // Reset the mention suggestions after the placement update\n const animationFrame = requestAnimationFrame(() => {\n mentionSuggestions.style.overflowY = \"auto\";\n mentionSuggestions.style.maxHeight =\n \"var(--lb-composer-floating-available-height)\";\n });\n\n return () => {\n cancelAnimationFrame(animationFrame);\n };\n }, [mentions?.length, isOpen, elements.floating, update]);\n\n return (\n <Persist>\n {isOpen ? (\n <ComposerSuggestionsContext.Provider\n value={{\n id,\n itemId,\n selectedValue: selectedMentionId,\n setSelectedValue: setSelectedMentionId,\n onItemSelect,\n placement,\n dir,\n ref: contentRef,\n }}\n >\n <Portal\n ref={setFloating}\n style={{\n position: strategy,\n top: 0,\n left: 0,\n transform: isPositioned\n ? `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`\n : \"translate3d(0, -200%, 0)\",\n minWidth: \"max-content\",\n zIndex: contentZIndex,\n }}\n >\n <MentionSuggestions\n mentions={mentions}\n selectedMentionId={selectedMentionId}\n />\n </Portal>\n </ComposerSuggestionsContext.Provider>\n ) : null}\n </Persist>\n );\n}\n\nfunction ComposerEditorFloatingToolbarWrapper({\n id,\n position = FLOATING_TOOLBAR_POSITION,\n dir,\n FloatingToolbar,\n hasFloatingToolbarRange,\n setHasFloatingToolbarRange,\n}: ComposerEditorFloatingToolbarWrapperProps) {\n const editor = useSlateStatic();\n const { onEditorChange } = useComposerEditorContext();\n const { isFocused } = useComposer();\n const [contentRef, contentZIndex] = useContentZIndex();\n const [isPointerDown, setPointerDown] = useState(false);\n const isOpen = isFocused && !isPointerDown && hasFloatingToolbarRange;\n const {\n refs: { setReference, setFloating },\n strategy,\n isPositioned,\n placement,\n x,\n y,\n } = useFloatingWithOptions({\n type: \"range\",\n position,\n dir,\n alignment: \"center\",\n open: isOpen,\n });\n\n useLayoutEffect(() => {\n if (!isFocused) {\n return;\n }\n\n const handlePointerDown = () => setPointerDown(true);\n const handlePointerUp = () => setPointerDown(false);\n\n document.addEventListener(\"pointerdown\", handlePointerDown);\n document.addEventListener(\"pointerup\", handlePointerUp);\n\n return () => {\n document.removeEventListener(\"pointerdown\", handlePointerDown);\n document.removeEventListener(\"pointerup\", handlePointerUp);\n };\n }, [isFocused]);\n\n useObservable(onEditorChange, () => {\n // Detach from previous selection range (if any) to avoid sudden jumps\n setReference(null);\n\n // Then, wait for the next render to ensure the selection is updated\n requestAnimationFrame(() => {\n const domSelection = window.getSelection();\n\n // Finally, show the toolbar if there's a selection range\n if (\n !editor.selection ||\n SlateRange.isCollapsed(editor.selection) ||\n !domSelection ||\n !domSelection.rangeCount\n ) {\n setHasFloatingToolbarRange(false);\n setReference(null);\n } else {\n setHasFloatingToolbarRange(true);\n\n const domRange = domSelection.getRangeAt(0);\n setReference(domRange);\n }\n });\n });\n\n return (\n <Persist>\n {isOpen ? (\n <ComposerFloatingToolbarContext.Provider\n value={{\n id,\n placement,\n dir,\n ref: contentRef,\n }}\n >\n <Portal\n ref={setFloating}\n style={{\n position: strategy,\n top: 0,\n left: 0,\n transform: isPositioned\n ? `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`\n : \"translate3d(0, -200%, 0)\",\n minWidth: \"max-content\",\n zIndex: contentZIndex,\n }}\n >\n <FloatingToolbar />\n </Portal>\n </ComposerFloatingToolbarContext.Provider>\n ) : null}\n </Persist>\n );\n}\n\n/**\n * Displays a floating toolbar attached to the selection within `Composer.Editor`.\n *\n * @example\n * <Composer.FloatingToolbar>\n * <Composer.MarkToggle mark=\"bold\">Bold</Composer.MarkToggle>\n * <Composer.MarkToggle mark=\"italic\">Italic</Composer.MarkToggle>\n * </Composer.FloatingToolbar>\n */\nconst ComposerFloatingToolbar = forwardRef<\n HTMLDivElement,\n ComposerFloatingToolbarProps\n>(({ children, onPointerDown, style, asChild, ...props }, forwardedRef) => {\n const [isPresent] = usePersist();\n const ref = useRef<HTMLDivElement>(null);\n const {\n id,\n ref: contentRef,\n placement,\n dir,\n } = useComposerFloatingToolbarContext(COMPOSER_FLOATING_TOOLBAR_NAME);\n const mergedRefs = useRefs(forwardedRef, contentRef, ref);\n const [side, align] = useMemo(\n () => getSideAndAlignFromFloatingPlacement(placement),\n [placement]\n );\n const Component = asChild ? Slot : \"div\";\n useAnimationPersist(ref);\n\n const handlePointerDown = useCallback(\n (event: PointerEvent<HTMLDivElement>) => {\n onPointerDown?.(event);\n\n event.preventDefault();\n event.stopPropagation();\n },\n [onPointerDown]\n );\n\n return (\n <Component\n dir={dir}\n role=\"toolbar\"\n id={id}\n aria-label=\"Floating toolbar\"\n {...props}\n onPointerDown={handlePointerDown}\n data-state={isPresent ? \"open\" : \"closed\"}\n data-side={side}\n data-align={align}\n style={{\n display: \"flex\",\n flexDirection: \"row\",\n maxWidth: \"var(--lb-composer-floating-available-width)\",\n overflowX: \"auto\",\n ...style,\n }}\n ref={mergedRefs}\n >\n {children}\n </Component>\n );\n});\n\nfunction ComposerEditorElement({\n Mention,\n Link,\n ...props\n}: ComposerEditorElementProps) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const { attributes, children, element } = props;\n\n switch (element.type) {\n case \"mention\":\n return (\n <ComposerEditorMentionWrapper\n Mention={Mention}\n {...(props as RenderElementSpecificProps<ComposerBodyMention>)}\n />\n );\n case \"auto-link\":\n case \"custom-link\":\n return (\n <ComposerEditorLinkWrapper\n Link={Link}\n {...(props as RenderElementSpecificProps<\n ComposerBodyAutoLink | ComposerBodyCustomLink\n >)}\n />\n );\n case \"paragraph\":\n return (\n <p {...attributes} style={{ position: \"relative\" }}>\n {children}\n </p>\n );\n default:\n return null;\n }\n}\n\n// <code><s><em><strong>text</strong></s></em></code>\nfunction ComposerEditorLeaf({\n attributes,\n children,\n leaf,\n}: RenderLeafSpecificProps<ComposerBodyText>) {\n if (leaf.bold) {\n children = <strong>{children}</strong>;\n }\n\n if (leaf.italic) {\n children = <em>{children}</em>;\n }\n\n if (leaf.strikethrough) {\n children = <s>{children}</s>;\n }\n\n if (leaf.code) {\n children = <code>{children}</code>;\n }\n\n return <span {...attributes}>{children}</span>;\n}\n\nfunction ComposerEditorPlaceholder({\n attributes,\n children,\n}: RenderPlaceholderProps) {\n const { opacity: _opacity, ...style } = attributes.style;\n\n return (\n <span {...attributes} style={style} data-placeholder=\"\">\n {children}\n </span>\n );\n}\n\n/**\n * Displays mentions within `Composer.Editor`.\n *\n * @example\n * <Composer.Mention>@{mention.id}</Composer.Mention>\n */\nconst ComposerMention = forwardRef<HTMLSpanElement, ComposerMentionProps>(\n ({ children, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"span\";\n const isSelected = useSelected();\n\n return (\n <Component\n data-selected={isSelected || undefined}\n {...props}\n ref={forwardedRef}\n >\n {children}\n </Component>\n );\n }\n);\n\n/**\n * Displays links within `Composer.Editor`.\n *\n * @example\n * <Composer.Link href={href}>{children}</Composer.Link>\n */\nconst ComposerLink = forwardRef<HTMLAnchorElement, ComposerLinkProps>(\n ({ children, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"a\";\n\n return (\n <Component\n target=\"_blank\"\n rel=\"noopener noreferrer nofollow\"\n {...props}\n ref={forwardedRef}\n >\n {children}\n </Component>\n );\n }\n);\n\n/**\n * Contains suggestions within `Composer.Editor`.\n */\nconst ComposerSuggestions = forwardRef<\n HTMLDivElement,\n ComposerSuggestionsProps\n>(({ children, style, asChild, ...props }, forwardedRef) => {\n const [isPresent] = usePersist();\n const ref = useRef<HTMLDivElement>(null);\n const {\n ref: contentRef,\n placement,\n dir,\n } = useComposerSuggestionsContext(COMPOSER_SUGGESTIONS_NAME);\n const mergedRefs = useRefs(forwardedRef, contentRef, ref);\n const [side, align] = useMemo(\n () => getSideAndAlignFromFloatingPlacement(placement),\n [placement]\n );\n const Component = asChild ? Slot : \"div\";\n useAnimationPersist(ref);\n\n return (\n <Component\n dir={dir}\n {...props}\n data-state={isPresent ? \"open\" : \"closed\"}\n data-side={side}\n data-align={align}\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n maxHeight: \"var(--lb-composer-floating-available-height)\",\n overflowY: \"auto\",\n ...style,\n }}\n ref={mergedRefs}\n >\n {children}\n </Component>\n );\n});\n\n/**\n * Displays a list of suggestions within `Composer.Editor`.\n *\n * @example\n * <Composer.SuggestionsList>\n * {mentions.map((mention) => (\n * <Composer.SuggestionsListItem key={mention.id} value={mention.id}>\n * @{mention.id}\n * </Composer.SuggestionsListItem>\n * ))}\n * </Composer.SuggestionsList>\n */\nconst ComposerSuggestionsList = forwardRef<\n HTMLUListElement,\n ComposerSuggestionsListProps\n>(({ children, asChild, ...props }, forwardedRef) => {\n const { id } = useComposerSuggestionsContext(COMPOSER_SUGGESTIONS_LIST_NAME);\n const Component = asChild ? Slot : \"ul\";\n\n return (\n <Component\n role=\"listbox\"\n id={id}\n aria-label=\"Suggestions list\"\n {...props}\n ref={forwardedRef}\n >\n {children}\n </Component>\n );\n});\n\n/**\n * Displays a suggestion within `Composer.SuggestionsList`.\n *\n * @example\n * <Composer.SuggestionsListItem key={mention.id} value={mention.id}>\n * @{mention.id}\n * </Composer.SuggestionsListItem>\n */\nconst ComposerSuggestionsListItem = forwardRef<\n HTMLLIElement,\n ComposerSuggestionsListItemProps\n>(\n (\n {\n value,\n children,\n onPointerMove,\n onPointerDown,\n onClick,\n asChild,\n ...props\n },\n forwardedRef\n ) => {\n const ref = useRef<HTMLLIElement>(null);\n const mergedRefs = useRefs(forwardedRef, ref);\n const { selectedValue, setSelectedValue, itemId, onItemSelect } =\n useComposerSuggestionsContext(COMPOSER_SUGGESTIONS_LIST_ITEM_NAME);\n const Component = asChild ? Slot : \"li\";\n const isSelected = useMemo(\n () => selectedValue === value,\n [selectedValue, value]\n );\n // TODO: Support props.id if provided, it will need to be sent up to Composer.Editor to use it in aria-activedescendant\n const id = useMemo(() => itemId(value), [itemId, value]);\n\n useEffect(() => {\n if (ref?.current && isSelected) {\n ref.current.scrollIntoView({ block: \"nearest\" });\n }\n }, [isSelected]);\n\n const handlePointerMove = useCallback(\n (event: PointerEvent<HTMLLIElement>) => {\n onPointerMove?.(event);\n\n if (!event.isDefaultPrevented()) {\n setSelectedValue(value);\n }\n },\n [onPointerMove, setSelectedValue, value]\n );\n\n const handlePointerDown = useCallback(\n (event: PointerEvent<HTMLLIElement>) => {\n onPointerDown?.(event);\n\n event.preventDefault();\n event.stopPropagation();\n },\n [onPointerDown]\n );\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLLIElement>) => {\n onClick?.(event);\n\n const wasDefaultPrevented = event.isDefaultPrevented();\n\n event.preventDefault();\n event.stopPropagation();\n\n if (!wasDefaultPrevented) {\n onItemSelect(value);\n }\n },\n [onClick, onItemSelect, value]\n );\n\n return (\n <Component\n role=\"option\"\n id={id}\n data-selected={isSelected || undefined}\n aria-selected={isSelected || undefined}\n onPointerMove={handlePointerMove}\n onPointerDown={handlePointerDown}\n onClick={handleClick}\n {...props}\n ref={mergedRefs}\n >\n {children}\n </Component>\n );\n }\n);\n\nconst defaultEditorComponents: ComposerEditorComponents = {\n Link: ({ href, children }) => {\n return <ComposerLink href={href}>{children}</ComposerLink>;\n },\n Mention: ({ mention }) => {\n return (\n <ComposerMention>\n {MENTION_CHARACTER}\n {mention.id}\n </ComposerMention>\n );\n },\n MentionSuggestions: ({ mentions }) => {\n return mentions.length > 0 ? (\n <ComposerSuggestions>\n <ComposerSuggestionsList>\n {mentions.map((mention) => (\n <ComposerSuggestionsListItem key={mention.id} value={mention.id}>\n {mention.id}\n </ComposerSuggestionsListItem>\n ))}\n </ComposerSuggestionsList>\n </ComposerSuggestions>\n ) : null;\n },\n};\n\n/**\n * Displays the composer's editor.\n *\n * @example\n * <Composer.Editor placeholder=\"Write a comment…\" />\n */\nconst ComposerEditor = forwardRef<HTMLDivElement, ComposerEditorProps>(\n (\n {\n defaultValue,\n onKeyDown,\n onFocus,\n onBlur,\n disabled,\n autoFocus,\n components,\n dir,\n ...props\n },\n forwardedRef\n ) => {\n const client = useClientOrNull();\n const { editor, validate, setFocused, onEditorChange, roomId } =\n useComposerEditorContext();\n const {\n submit,\n focus,\n blur,\n select,\n canSubmit,\n isDisabled: isComposerDisabled,\n isFocused,\n } = useComposer();\n const isDisabled = isComposerDisabled || disabled;\n const initialBody = useInitial(defaultValue ?? emptyCommentBody);\n const initialEditorValue = useMemo(() => {\n return commentBodyToComposerBody(initialBody);\n }, [initialBody]);\n const { Link, Mention, MentionSuggestions, FloatingToolbar } = useMemo(\n () => ({ ...defaultEditorComponents, ...components }),\n [components]\n );\n\n const [hasFloatingToolbarRange, setHasFloatingToolbarRange] =\n useState(false);\n // If used with LiveblocksProvider but without resolveMentionSuggestions,\n // we can skip the mention suggestions logic entirely\n const resolveMentionSuggestions = useResolveMentionSuggestions();\n const hasResolveMentionSuggestions = client\n ? resolveMentionSuggestions\n : true;\n const [mentionDraft, setMentionDraft] = useState<MentionDraft>();\n const mentionSuggestions = useMentionSuggestions(\n roomId,\n mentionDraft?.text\n );\n const [\n selectedMentionSuggestionIndex,\n setPreviousSelectedMentionSuggestionIndex,\n setNextSelectedMentionSuggestionIndex,\n setSelectedMentionSuggestionIndex,\n ] = useIndex(0, mentionSuggestions?.length ?? 0);\n const id = useId();\n const floatingToolbarId = `liveblocks-floating-toolbar-${id}`;\n const suggestionsListId = `liveblocks-suggestions-list-${id}`;\n const suggestionsListItemId = useCallback(\n (mentionId?: string) =>\n mentionId\n ? `liveblocks-suggestions-list-item-${id}-${mentionId}`\n : undefined,\n [id]\n );\n\n const renderElement = useCallback(\n (props: RenderElementProps) => {\n return (\n <ComposerEditorElement Mention={Mention} Link={Link} {...props} />\n );\n },\n [Link, Mention]\n );\n\n const handleChange = useCallback(\n (value: SlateDescendant[]) => {\n validate(value as SlateElement[]);\n\n // Our multi-component setup requires us to instantiate the editor in `Composer.Form`\n // but we can only listen to changes here in `Composer.Editor` via `Slate`, so we use\n // an event source to notify `Composer.Form` of changes.\n onEditorChange.notify();\n },\n [validate, onEditorChange]\n );\n\n const createMention = useCallback(\n (mention?: MentionData) => {\n if (!mentionDraft || !mention) {\n return;\n }\n\n SlateTransforms.select(editor, mentionDraft.range);\n insertMention(editor, mention);\n setMentionDraft(undefined);\n setSelectedMentionSuggestionIndex(0);\n },\n [editor, mentionDraft, setSelectedMentionSuggestionIndex]\n );\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLDivElement>) => {\n onKeyDown?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n // Allow leaving marks with ArrowLeft\n if (isKey(event, \"ArrowLeft\")) {\n leaveMarkEdge(editor, \"start\");\n }\n\n // Allow leaving marks with ArrowRight\n if (isKey(event, \"ArrowRight\")) {\n leaveMarkEdge(editor, \"end\");\n }\n\n if (mentionDraft && mentionSuggestions?.length) {\n // Select the next mention suggestion on ArrowDown\n if (isKey(event, \"ArrowDown\")) {\n event.preventDefault();\n setNextSelectedMentionSuggestionIndex();\n }\n\n // Select the previous mention suggestion on ArrowUp\n if (isKey(event, \"ArrowUp\")) {\n event.preventDefault();\n setPreviousSelectedMentionSuggestionIndex();\n }\n\n // Create a mention on Enter/Tab\n if (isKey(event, \"Enter\") || isKey(event, \"Tab\")) {\n event.preventDefault();\n\n const mention =\n mentionSuggestions?.[selectedMentionSuggestionIndex];\n createMention(mention);\n }\n\n // Close the suggestions on Escape\n if (isKey(event, \"Escape\")) {\n event.preventDefault();\n setMentionDraft(undefined);\n setSelectedMentionSuggestionIndex(0);\n }\n } else {\n if (hasFloatingToolbarRange) {\n // Close the floating toolbar on Escape\n if (isKey(event, \"Escape\")) {\n event.preventDefault();\n setHasFloatingToolbarRange(false);\n }\n }\n\n // Blur the editor on Escape\n if (isKey(event, \"Escape\")) {\n blur();\n }\n\n // Submit the editor on Enter\n if (isKey(event, \"Enter\", { shift: false })) {\n // Even if submitting is not possible, don't do anything else on Enter. (e.g. creating a new line)\n event.preventDefault();\n\n if (canSubmit) {\n submit();\n }\n }\n\n // Create a new line on Shift + Enter\n if (isKey(event, \"Enter\", { shift: true })) {\n event.preventDefault();\n editor.insertBreak();\n }\n\n // Toggle bold on Command/Control + B\n if (isKey(event, \"b\", { mod: true })) {\n event.preventDefault();\n toggleEditorMark(editor, \"bold\");\n }\n\n // Toggle italic on Command/Control + I\n if (isKey(event, \"i\", { mod: true })) {\n event.preventDefault();\n toggleEditorMark(editor, \"italic\");\n }\n\n // Toggle strikethrough on Command/Control + Shift + S\n if (isKey(event, \"s\", { mod: true, shift: true })) {\n event.preventDefault();\n toggleEditorMark(editor, \"strikethrough\");\n }\n\n // Toggle code on Command/Control + E\n if (isKey(event, \"e\", { mod: true })) {\n event.preventDefault();\n toggleEditorMark(editor, \"code\");\n }\n }\n },\n [\n onKeyDown,\n mentionDraft,\n mentionSuggestions,\n hasFloatingToolbarRange,\n editor,\n setNextSelectedMentionSuggestionIndex,\n setPreviousSelectedMentionSuggestionIndex,\n selectedMentionSuggestionIndex,\n createMention,\n setSelectedMentionSuggestionIndex,\n blur,\n canSubmit,\n submit,\n ]\n );\n\n const handleFocus = useCallback(\n (event: FocusEvent<HTMLDivElement>) => {\n onFocus?.(event);\n\n if (!event.isDefaultPrevented()) {\n setFocused(true);\n }\n },\n [onFocus, setFocused]\n );\n\n const handleBlur = useCallback(\n (event: FocusEvent<HTMLDivElement>) => {\n onBlur?.(event);\n\n if (!event.isDefaultPrevented()) {\n setFocused(false);\n }\n },\n [onBlur, setFocused]\n );\n\n const selectedMention =\n mentionSuggestions?.[selectedMentionSuggestionIndex];\n const selectedMentionId = selectedMention?.id;\n const setSelectedMentionId = useCallback(\n (mentionId: string) => {\n const index = mentionSuggestions?.findIndex(\n (mention) => mention.id === mentionId\n );\n\n if (index !== undefined && index >= 0) {\n setSelectedMentionSuggestionIndex(index);\n }\n },\n [setSelectedMentionSuggestionIndex, mentionSuggestions]\n );\n\n const additionalProps: AriaAttributes = useMemo(\n () =>\n mentionDraft\n ? {\n role: \"combobox\",\n \"aria-autocomplete\": \"list\",\n \"aria-expanded\": true,\n \"aria-controls\": suggestionsListId,\n \"aria-activedescendant\": suggestionsListItemId(selectedMentionId),\n }\n : hasFloatingToolbarRange\n ? {\n \"aria-haspopup\": true,\n \"aria-controls\": floatingToolbarId,\n }\n : {},\n [\n mentionDraft,\n suggestionsListId,\n suggestionsListItemId,\n selectedMentionId,\n hasFloatingToolbarRange,\n floatingToolbarId,\n ]\n );\n\n useImperativeHandle(forwardedRef, () => {\n return ReactEditor.toDOMNode(editor, editor) as HTMLDivElement;\n }, [editor]);\n\n // Manually focus the editor when `autoFocus` is true\n useLayoutEffect(() => {\n if (!autoFocus) {\n return;\n }\n\n // `focus` needs to be synchronous to ensure its errors can be caught\n // but the triggering of `focus` on mount itself can be asynchronous.\n // This brings back the same timing behavior as Slate's `ReactEditor.focus`\n // (which uses `setTimeout` internally) while still allowing us to catch errors.\n const timeout = setTimeout(() => focus(), 0);\n\n return () => clearTimeout(timeout);\n }, [autoFocus, editor, focus]);\n\n // Manually add a selection in the editor if the selection\n // is still empty after being focused\n useLayoutEffect(() => {\n if (isFocused && editor.selection === null) {\n select();\n }\n }, [editor, select, isFocused]);\n\n const handleMentionSelect = useCallback(\n (mentionId: string) => {\n const mention = mentionSuggestions?.find(\n (mention) => mention.id === mentionId\n );\n\n createMention(mention);\n },\n [createMention, mentionSuggestions]\n );\n\n return (\n <Slate\n editor={editor}\n initialValue={initialEditorValue}\n onChange={handleChange}\n >\n <Editable\n dir={dir}\n enterKeyHint={mentionDraft ? \"enter\" : \"send\"}\n autoCapitalize=\"sentences\"\n aria-label=\"Composer editor\"\n data-focused={isFocused || undefined}\n data-disabled={isDisabled || undefined}\n {...additionalProps}\n {...props}\n readOnly={isDisabled}\n disabled={isDisabled}\n onKeyDown={handleKeyDown}\n onFocus={handleFocus}\n onBlur={handleBlur}\n renderElement={renderElement}\n renderLeaf={ComposerEditorLeaf}\n renderPlaceholder={ComposerEditorPlaceholder}\n />\n {hasResolveMentionSuggestions && (\n <ComposerEditorMentionSuggestionsWrapper\n dir={dir}\n mentionDraft={mentionDraft}\n setMentionDraft={setMentionDraft}\n selectedMentionId={selectedMentionId}\n setSelectedMentionId={setSelectedMentionId}\n mentions={mentionSuggestions}\n id={suggestionsListId}\n itemId={suggestionsListItemId}\n onItemSelect={handleMentionSelect}\n MentionSuggestions={MentionSuggestions}\n />\n )}\n {FloatingToolbar && (\n <ComposerEditorFloatingToolbarWrapper\n dir={dir}\n id={floatingToolbarId}\n hasFloatingToolbarRange={hasFloatingToolbarRange}\n setHasFloatingToolbarRange={setHasFloatingToolbarRange}\n FloatingToolbar={FloatingToolbar}\n />\n )}\n </Slate>\n );\n }\n);\n\nconst MAX_ATTACHMENTS = 10;\nconst MAX_ATTACHMENT_SIZE = 1024 * 1024 * 1024; // 1 GB\n\nfunction prepareAttachment(file: File): CommentLocalAttachment {\n return {\n type: \"localAttachment\",\n status: \"idle\",\n id: createCommentAttachmentId(),\n name: file.name,\n size: file.size,\n mimeType: file.type,\n file,\n };\n}\n\n/**\n * Surrounds the composer's content and handles submissions.\n *\n * @example\n * <Composer.Form onComposerSubmit={({ body }) => {}}>\n *\t <Composer.Editor />\n * <Composer.Submit />\n * </Composer.Form>\n */\nconst ComposerForm = forwardRef<HTMLFormElement, ComposerFormProps>(\n (\n {\n children,\n onSubmit,\n onComposerSubmit,\n defaultAttachments = [],\n pasteFilesAsAttachments,\n blurOnSubmit = true,\n preventUnsavedChanges = true,\n disabled,\n asChild,\n roomId: _roomId,\n ...props\n },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"form\";\n const [isEmpty, setEmpty] = useState(true);\n const [isSubmitting, setSubmitting] = useState(false);\n const [isFocused, setFocused] = useState(false);\n const room = useRoom({ allowOutsideRoom: true });\n\n const roomId = _roomId !== undefined ? _roomId : room?.id;\n if (roomId === undefined) {\n throw new Error(\"Composer.Form must be a descendant of RoomProvider.\");\n }\n\n // Later: Offer as Composer.Form props: { maxAttachments: number; maxAttachmentSize: number; supportedAttachmentMimeTypes: string[]; }\n const maxAttachments = MAX_ATTACHMENTS;\n const maxAttachmentSize = MAX_ATTACHMENT_SIZE;\n\n const {\n attachments,\n isUploadingAttachments,\n addAttachments,\n removeAttachment,\n clearAttachments,\n } = useComposerAttachmentsManager(defaultAttachments, {\n maxFileSize: maxAttachmentSize,\n roomId,\n });\n const numberOfAttachments = attachments.length;\n const hasMaxAttachments = numberOfAttachments >= maxAttachments;\n\n const isDisabled = useMemo(() => {\n return isSubmitting || disabled === true;\n }, [isSubmitting, disabled]);\n const canSubmit = useMemo(() => {\n return !isEmpty && !isUploadingAttachments;\n }, [isEmpty, isUploadingAttachments]);\n const [marks, setMarks] = useState<ComposerBodyMarks>(getComposerBodyMarks);\n\n const ref = useRef<HTMLFormElement>(null);\n const mergedRefs = useRefs(forwardedRef, ref);\n const fileInputRef = useRef<HTMLInputElement>(null);\n const syncSource = useSyncSource();\n\n // Mark the composer as a pending update when it has unsubmitted (draft)\n // text or attachments\n const isPending = !preventUnsavedChanges\n ? false\n : !isEmpty || isUploadingAttachments || attachments.length > 0;\n\n useEffect(() => {\n syncSource?.setSyncStatus(\n isPending ? \"has-local-changes\" : \"synchronized\"\n );\n }, [syncSource, isPending]);\n\n const createAttachments = useCallback(\n (files: File[]) => {\n if (!files.length) {\n return;\n }\n\n const numberOfAcceptedFiles = Math.max(\n 0,\n maxAttachments - numberOfAttachments\n );\n\n files.splice(numberOfAcceptedFiles);\n\n const attachments = files.map((file) => prepareAttachment(file));\n\n addAttachments(attachments);\n },\n [addAttachments, maxAttachments, numberOfAttachments]\n );\n\n const createAttachmentsRef = useRef(createAttachments);\n\n useEffect(() => {\n createAttachmentsRef.current = createAttachments;\n }, [createAttachments]);\n\n const stableCreateAttachments = useCallback((files: File[]) => {\n createAttachmentsRef.current(files);\n }, []);\n\n const editor = useInitial(() =>\n createComposerEditor({\n createAttachments: stableCreateAttachments,\n pasteFilesAsAttachments,\n })\n );\n const onEditorChange = useInitial(makeEventSource) as EventSource<void>;\n\n const validate = useCallback(\n (value: SlateElement[]) => {\n setEmpty(isEditorEmpty(editor, value));\n },\n [editor]\n );\n\n const submit = useCallback(() => {\n if (!canSubmit) {\n return;\n }\n\n // We need to wait for the next frame in some cases like when composing diacritics,\n // we want any native handling to be done first while still being handled on `keydown`.\n requestAnimationFrame(() => {\n if (ref.current) {\n requestSubmit(ref.current);\n }\n });\n }, [canSubmit]);\n\n const clear = useCallback(() => {\n SlateTransforms.delete(editor, {\n at: {\n anchor: SlateEditor.start(editor, []),\n focus: SlateEditor.end(editor, []),\n },\n });\n }, [editor]);\n\n const select = useCallback(() => {\n SlateTransforms.select(editor, SlateEditor.end(editor, []));\n }, [editor]);\n\n const focus = useCallback(\n (resetSelection = true) => {\n try {\n // Slate's `ReactEditor.focus` method can use `setTimeout` internally\n // which prevents us from catching errors, so this is a reimplementation.\n // https://github.com/ianstormtaylor/slate/blob/main/packages/slate-dom/src/plugin/dom-editor.ts\n if (!ReactEditor.isFocused(editor)) {\n SlateTransforms.select(\n editor,\n resetSelection || !editor.selection\n ? SlateEditor.end(editor, [])\n : editor.selection\n );\n\n const element = ReactEditor.toDOMNode(editor, editor);\n\n if (editor.selection) {\n const domSelection = window.getSelection();\n const domRange = getDOMRange(editor, editor.selection);\n\n if (domRange) {\n domSelection?.removeAllRanges();\n domSelection?.addRange(domRange);\n }\n }\n\n element.focus({ preventScroll: true });\n }\n } catch {\n // Slate's DOM-specific methods will throw if the editor's DOM\n // node no longer exists. This action doesn't make sense on an\n // unmounted editor so we can safely ignore it.\n }\n },\n [editor]\n );\n\n const blur = useCallback(() => {\n try {\n ReactEditor.blur(editor);\n } catch {\n // Slate's DOM-specific methods will throw if the editor's DOM\n // node no longer exists. This action doesn't make sense on an\n // unmounted editor so we can safely ignore it.\n }\n }, [editor]);\n\n const createMention = useCallback(() => {\n if (disabled) {\n return;\n }\n\n focus();\n insertMentionCharacter(editor);\n }, [disabled, editor, focus]);\n\n const insertText = useCallback(\n (text: string) => {\n if (disabled) {\n return;\n }\n\n focus(false);\n insertSlateText(editor, text);\n },\n [disabled, editor, focus]\n );\n\n const attachFiles = useCallback(() => {\n if (disabled) {\n return;\n }\n\n if (fileInputRef.current) {\n fileInputRef.current.click();\n }\n }, [disabled]);\n\n const handleAttachmentsInputChange = useCallback(\n (event: ChangeEvent<HTMLInputElement>) => {\n if (disabled) {\n return;\n }\n\n if (event.target.files) {\n createAttachments(Array.from(event.target.files));\n\n // Reset the input value to allow selecting the same file(s) again\n event.target.value = \"\";\n }\n },\n [createAttachments, disabled]\n );\n\n const onSubmitEnd = useCallback(() => {\n clear();\n clearAttachments();\n setSubmitting(false);\n\n if (blurOnSubmit) {\n blur();\n }\n }, [blur, blurOnSubmit, clear, clearAttachments]);\n\n const handleSubmit = useCallback(\n (event: FormEvent<HTMLFormElement>) => {\n if (disabled) {\n return;\n }\n\n // In some situations (e.g. pressing Enter while composing diacritics), it's possible\n // for the form to be submitted as empty even though we already checked whether the\n // editor was empty when handling the key press.\n const isEmpty = isEditorEmpty(editor, editor.children);\n\n // We even prevent the user's `onSubmit` handler from being called if the editor is empty.\n if (isEmpty) {\n event.preventDefault();\n\n return;\n }\n\n onSubmit?.(event);\n\n if (!onComposerSubmit || event.isDefaultPrevented()) {\n event.preventDefault();\n\n return;\n }\n\n const body = composerBodyToCommentBody(\n editor.children as ComposerBodyData\n );\n // Only non-local attachments are included to be submitted.\n const commentAttachments: CommentAttachment[] = attachments\n .filter(\n (attachment) =>\n attachment.type === \"attachment\" ||\n (attachment.type === \"localAttachment\" &&\n attachment.status === \"uploaded\")\n )\n .map((attachment) => {\n return {\n id: attachment.id,\n type: \"attachment\",\n mimeType: attachment.mimeType,\n size: attachment.size,\n name: attachment.name,\n };\n });\n\n const promise = onComposerSubmit(\n { body, attachments: commentAttachments },\n event\n );\n\n event.preventDefault();\n\n if (promise) {\n setSubmitting(true);\n promise.then(onSubmitEnd);\n } else {\n onSubmitEnd();\n }\n },\n [disabled, editor, attachments, onComposerSubmit, onSubmit, onSubmitEnd]\n );\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n const toggleMark = useCallback(\n (mark: ComposerBodyMark) => {\n toggleEditorMark(editor, mark);\n },\n [editor]\n );\n\n useObservable(onEditorChange, () => {\n setMarks(getComposerBodyMarks(editor));\n });\n\n return (\n <ComposerEditorContext.Provider\n value={{\n editor,\n validate,\n setFocused,\n onEditorChange,\n roomId,\n }}\n >\n <ComposerAttachmentsContext.Provider\n value={{\n createAttachments,\n isUploadingAttachments,\n hasMaxAttachments,\n maxAttachments,\n maxAttachmentSize,\n }}\n >\n <ComposerContext.Provider\n value={{\n isDisabled,\n isFocused,\n isEmpty,\n canSubmit,\n submit,\n clear,\n select,\n focus,\n blur,\n createMention,\n insertText,\n attachments,\n attachFiles,\n removeAttachment,\n toggleMark,\n marks,\n }}\n >\n <Component {...props} onSubmit={handleSubmit} ref={mergedRefs}>\n <input\n type=\"file\"\n multiple\n ref={fileInputRef}\n onChange={handleAttachmentsInputChange}\n onClick={stopPropagation}\n tabIndex={-1}\n style={{ display: \"none\" }}\n />\n <Slottable>{children}</Slottable>\n </Component>\n </ComposerContext.Provider>\n </ComposerAttachmentsContext.Provider>\n </ComposerEditorContext.Provider>\n );\n }\n);\n\n/**\n * A button to submit the composer.\n *\n * @example\n * <Composer.Submit>Send</Composer.Submit>\n */\nconst ComposerSubmit = forwardRef<HTMLButtonElement, ComposerSubmitProps>(\n ({ children, disabled, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"button\";\n const { canSubmit, isDisabled: isComposerDisabled } = useComposer();\n const isDisabled = isComposerDisabled || disabled || !canSubmit;\n\n return (\n <Component\n type=\"submit\"\n {...props}\n ref={forwardedRef}\n disabled={isDisabled}\n >\n {children}\n </Component>\n );\n }\n);\n\n/**\n * A button which opens a file picker to create attachments.\n *\n * @example\n * <Composer.AttachFiles>Attach files</Composer.AttachFiles>\n */\nconst ComposerAttachFiles = forwardRef<\n HTMLButtonElement,\n ComposerAttachFilesProps\n>(({ children, onClick, disabled, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"button\";\n const { hasMaxAttachments } = useComposerAttachmentsContext();\n const { isDisabled: isComposerDisabled, attachFiles } = useComposer();\n const isDisabled = isComposerDisabled || hasMaxAttachments || disabled;\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n\n if (!event.isDefaultPrevented()) {\n attachFiles();\n }\n },\n [attachFiles, onClick]\n );\n\n return (\n <Component\n type=\"button\"\n {...props}\n onClick={handleClick}\n ref={forwardedRef}\n disabled={isDisabled}\n >\n {children}\n </Component>\n );\n});\n\n/**\n * A drop area which accepts files to create attachments.\n *\n * @example\n * <Composer.AttachmentsDropArea>\n * Drop files here\n * </Composer.AttachmentsDropArea>\n */\nconst ComposerAttachmentsDropArea = forwardRef<\n HTMLDivElement,\n ComposerAttachmentsDropAreaProps\n>(\n (\n {\n onDragEnter,\n onDragLeave,\n onDragOver,\n onDrop,\n disabled,\n asChild,\n ...props\n },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"div\";\n const { isDisabled: isComposerDisabled } = useComposer();\n const isDisabled = isComposerDisabled || disabled;\n const [, dropAreaProps] = useComposerAttachmentsDropArea({\n onDragEnter,\n onDragLeave,\n onDragOver,\n onDrop,\n disabled: isDisabled,\n });\n\n return (\n <Component\n {...dropAreaProps}\n data-disabled={isDisabled ? \"\" : undefined}\n {...props}\n ref={forwardedRef}\n />\n );\n }\n);\n\n/**\n * A toggle button which toggles a specific text mark.\n *\n * @example\n * <Composer.MarkToggle mark=\"bold\">\n * Bold\n * </Composer.MarkToggle>\n */\nconst ComposerMarkToggle = forwardRef<\n HTMLButtonElement,\n ComposerMarkToggleProps\n>(\n (\n {\n children,\n mark,\n onValueChange,\n onClick,\n onPointerDown,\n asChild,\n ...props\n },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"button\";\n const { marks, toggleMark } = useComposer();\n\n const handlePointerDown = useCallback(\n (event: PointerEvent<HTMLButtonElement>) => {\n onPointerDown?.(event);\n\n event.preventDefault();\n event.stopPropagation();\n },\n [onPointerDown]\n );\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n\n if (!event.isDefaultPrevented()) {\n event.preventDefault();\n event.stopPropagation();\n\n toggleMark(mark);\n onValueChange?.(mark);\n }\n },\n [mark, onClick, onValueChange, toggleMark]\n );\n\n return (\n <TogglePrimitive.Root\n asChild\n pressed={marks[mark]}\n onClick={handleClick}\n onPointerDown={handlePointerDown}\n {...props}\n >\n <Component {...props} ref={forwardedRef}>\n {children}\n </Component>\n </TogglePrimitive.Root>\n );\n }\n);\n\nif (process.env.NODE_ENV !== \"production\") {\n ComposerAttachFiles.displayName = COMPOSER_ATTACH_FILES_NAME;\n ComposerAttachmentsDropArea.displayName = COMPOSER_ATTACHMENTS_DROP_AREA_NAME;\n ComposerEditor.displayName = COMPOSER_EDITOR_NAME;\n ComposerFloatingToolbar.displayName = COMPOSER_FLOATING_TOOLBAR_NAME;\n ComposerForm.displayName = COMPOSER_FORM_NAME;\n ComposerMention.displayName = COMPOSER_MENTION_NAME;\n ComposerLink.displayName = COMPOSER_LINK_NAME;\n ComposerSubmit.displayName = COMPOSER_SUBMIT_NAME;\n ComposerSuggestions.displayName = COMPOSER_SUGGESTIONS_NAME;\n ComposerSuggestionsList.displayName = COMPOSER_SUGGESTIONS_LIST_NAME;\n ComposerSuggestionsListItem.displayName = COMPOSER_SUGGESTIONS_LIST_ITEM_NAME;\n ComposerMarkToggle.displayName = COMPOSER_MARK_TOGGLE_NAME;\n}\n\n// NOTE: Every export from this file will be available publicly as Composer.*\nexport {\n ComposerAttachFiles as AttachFiles,\n ComposerAttachmentsDropArea as AttachmentsDropArea,\n ComposerEditor as Editor,\n ComposerFloatingToolbar as FloatingToolbar,\n ComposerForm as Form,\n ComposerLink as Link,\n ComposerMarkToggle as MarkToggle,\n ComposerMention as Mention,\n ComposerSubmit as Submit,\n ComposerSuggestions as Suggestions,\n ComposerSuggestionsList as SuggestionsList,\n ComposerSuggestionsListItem as SuggestionsListItem,\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsJA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAsC;AAC3B;AAEX;AAEA;AAA8B;AAC5B;AAEF;AAIE;AAAO;AACL;AACE;AACE;AACE;AACE;AACoD;AAChD;AACA;AACD;AACH;AACF;AACF;AACF;AACF;AAEJ;AAEA;AAAsC;AACpC;AACA;AACA;AAEF;AACE;AACA;AAEA;AAEK;AAEG;AACH;AAGP;AAEA;AAAmC;AACjC;AACA;AACA;AAEF;AACE;AAEA;AAKF;AAEA;AAAiD;AAC/C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACW;AACX;AAEF;AACE;AACA;AACA;AACA;AACA;AAEA;AAAM;AAC8B;AAClC;AACA;AACA;AACA;AACA;AACA;AACA;AACyB;AACzB;AACA;AACW;AACL;AAGR;AACE;AAAkD;AAGpD;AACE;AACE;AAEA;AAAA;AAGF;AACA;AAA6B;AAK/B;AACE;AAAa;AAEb;AAIA;AACE;AAAA;AAIF;AACA;AAGA;AAGA;AACE;AACA;AACE;AAGJ;AACE;AAAmC;AACrC;AAGF;AAGM;AAA4B;AAA3B;AACQ;AACL;AACA;AACe;AACG;AAClB;AACA;AACA;AACK;AACP;AAEA;AAAC;AAAA;AACM;AACE;AACK;AACL;AACC;AAGF;AACM;AACF;AACV;AAEA;AAAC;AAAA;AACC;AACA;AAAA;AACF;AAAA;AACF;AAAA;AAKV;AAEA;AAA8C;AAC5C;AACW;AACX;AACA;AACA;AAEF;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AAAM;AAC8B;AAClC;AACA;AACA;AACA;AACA;AACyB;AACnB;AACN;AACA;AACW;AACL;AAGR;AACE;AACE;AAAA;AAGF;AACA;AAEA;AACA;AAEA;AACE;AACA;AAAyD;AAC3D;AAGF;AAEE;AAGA;AACE;AAGA;AAME;AACA;AAAiB;AAEjB;AAEA;AACA;AAAqB;AACvB;AACD;AAGH;AAGM;AAAgC;AAA/B;AACQ;AACL;AACA;AACA;AACK;AACP;AAEA;AAAC;AAAA;AACM;AACE;AACK;AACL;AACC;AAGF;AACM;AACF;AACV;AAEiB;AAAA;AACnB;AAAA;AAKV;AAWM;AAIJ;AACA;AACA;AAAM;AACJ;AACK;AACL;AACA;AAEF;AACA;AAAsB;AACgC;AAC1C;AAEZ;AACA;AAEA;AAA0B;AAEtB;AAEA;AACA;AAAsB;AACxB;AACc;AAGhB;AACE;AAAC;AAAA;AACC;AACK;AACL;AACW;AACP;AACW;AACkB;AACtB;AACC;AACL;AACI;AACM;AACL;AACC;AACR;AACL;AACK;AAEJ;AAAA;AAGP;AAEA;AAA+B;AAC7B;AACA;AAEF;AAEE;AAEA;AAAsB;AAElB;AACE;AAAC;AAAA;AACC;AACK;AAAA;AACP;AAEC;AAEH;AACE;AAAC;AAAA;AACC;AACK;AAAA;AAGP;AAGF;AAGE;AAGF;AAAO;AAEb;AAGA;AAA4B;AAC1B;AACA;AAEF;AACE;AACE;AAA6B;AAG/B;AACE;AAAyB;AAG3B;AACE;AAAwB;AAG1B;AACE;AAA2B;AAG7B;AACF;AAEA;AAAmC;AACjC;AAEF;AACE;AAEA;AAKF;AAQA;AAAwB;AAEpB;AACA;AAEA;AACE;AAAC;AAAA;AAC8B;AACzB;AACC;AAEJ;AAAA;AACH;AAGN;AAQA;AAAqB;AAEjB;AAEA;AACE;AAAC;AAAA;AACQ;AACH;AACA;AACC;AAEJ;AAAA;AACH;AAGN;AAKM;AAIJ;AACA;AACA;AAAM;AACC;AACL;AACA;AAEF;AACA;AAAsB;AACgC;AAC1C;AAEZ;AACA;AAEA;AACE;AAAC;AAAA;AACC;AACI;AAC6B;AACtB;AACC;AACL;AACI;AACM;AACJ;AACA;AACR;AACL;AACK;AAEJ;AAAA;AAGP;AAcM;AAIJ;AACA;AAEA;AACE;AAAC;AAAA;AACM;AACL;AACW;AACP;AACC;AAEJ;AAAA;AAGP;AAUA;AAAoC;AAKhC;AACE;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AAEA;AACA;AAAmB;AACO;AACH;AAGvB;AAEA;AACE;AACE;AAA+C;AACjD;AAGF;AAA0B;AAEtB;AAEA;AACE;AAAsB;AACxB;AACF;AACuC;AAGzC;AAA0B;AAEtB;AAEA;AACA;AAAsB;AACxB;AACc;AAGhB;AAAoB;AAEhB;AAEA;AAEA;AACA;AAEA;AACE;AAAkB;AACpB;AACF;AAC6B;AAG/B;AACE;AAAC;AAAA;AACM;AACL;AAC6B;AACA;AACd;AACA;AACN;AACL;AACC;AAEJ;AAAA;AACH;AAGN;AAEA;AAA0D;AAEtD;AAA2C;AAC7C;AAEE;AAEK;AAAA;AACQ;AACX;AAEJ;AAEE;AAUI;AAER;AAQA;AAAuB;AAEnB;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AAEA;AAAM;AACJ;AACA;AACA;AACA;AACA;AACY;AACZ;AAEF;AACA;AACA;AACE;AAA4C;AAE9C;AAA+D;AACV;AACxC;AAGb;AAIA;AACA;AAGA;AACA;AAA2B;AACzB;AACc;AAEhB;AAAM;AACJ;AACA;AACA;AACA;AAEF;AACA;AACA;AACA;AAA8B;AAItB;AACH;AAGL;AAAsB;AAElB;AACkE;AAEpE;AACc;AAGhB;AAAqB;AAEjB;AAKA;AAAsB;AACxB;AACyB;AAG3B;AAAsB;AAElB;AACE;AAAA;AAGF;AACA;AACA;AACA;AAAmC;AACrC;AACwD;AAG1D;AAAsB;AAElB;AAEA;AACE;AAAA;AAIF;AACE;AAA6B;AAI/B;AACE;AAA2B;AAG7B;AAEE;AACE;AACA;AAAsC;AAIxC;AACE;AACA;AAA0C;AAI5C;AACE;AAEA;AAEA;AAAqB;AAIvB;AACE;AACA;AACA;AAAmC;AACrC;AAEA;AAEE;AACE;AACA;AAAgC;AAClC;AAIF;AACE;AAAK;AAIP;AAEE;AAEA;AACE;AAAO;AACT;AAIF;AACE;AACA;AAAmB;AAIrB;AACE;AACA;AAA+B;AAIjC;AACE;AACA;AAAiC;AAInC;AACE;AACA;AAAwC;AAI1C;AACE;AACA;AAA+B;AACjC;AACF;AACF;AACA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;AAGF;AAAoB;AAEhB;AAEA;AACE;AAAe;AACjB;AACF;AACoB;AAGtB;AAAmB;AAEf;AAEA;AACE;AAAgB;AAClB;AACF;AACmB;AAGrB;AAEA;AACA;AAA6B;AAEzB;AAAkC;AACJ;AAG9B;AACE;AAAuC;AACzC;AACF;AACsD;AAGxD;AAAwC;AAGhC;AACQ;AACe;AACJ;AACA;AAC+C;AAGhE;AACmB;AACA;AAElB;AACT;AACE;AACA;AACA;AACA;AACA;AACA;AACF;AAGF;AACE;AAA2C;AAI7C;AACE;AACE;AAAA;AAOF;AAEA;AAAiC;AAKnC;AACE;AACE;AAAO;AACT;AAGF;AAA4B;AAExB;AAAoC;AACN;AAG9B;AAAqB;AACvB;AACkC;AAGpC;AACE;AAAC;AAAA;AACC;AACc;AACJ;AAEV;AAAA;AAAC;AAAA;AACC;AACuC;AACxB;AACJ;AACgB;AACE;AACzB;AACA;AACM;AACA;AACC;AACF;AACD;AACR;AACY;AACO;AAAA;AACrB;AAEE;AAAC;AAAA;AACC;AACA;AACA;AACA;AACA;AACU;AACN;AACI;AACM;AACd;AAAA;AACF;AAGA;AAAC;AAAA;AACC;AACI;AACJ;AACA;AACA;AAAA;AACF;AAAA;AAAA;AAEJ;AAGN;AAEA;AACA;AAEA;AACE;AAAO;AACC;AACE;AACsB;AACnB;AACA;AACI;AACf;AAEJ;AAWA;AAAqB;AAEjB;AACE;AACA;AACA;AACsB;AACtB;AACe;AACS;AACxB;AACA;AACQ;AACL;AAIL;AACA;AACA;AACA;AACA;AAEA;AACA;AACE;AAAqE;AAIvE;AACA;AAEA;AAAM;AACJ;AACA;AACA;AACA;AACA;AACoD;AACvC;AACb;AAEF;AACA;AAEA;AACE;AAAoC;AAEtC;AACE;AAAoB;AAEtB;AAEA;AACA;AACA;AACA;AAIA;AAIA;AACE;AAAY;AACwB;AACpC;AAGF;AAA0B;AAEtB;AACE;AAAA;AAGF;AAAmC;AACjC;AACiB;AAGnB;AAEA;AAEA;AAA0B;AAC5B;AACoD;AAGtD;AAEA;AACE;AAA+B;AAGjC;AACE;AAAkC;AAGpC;AAAe;AACQ;AACA;AACnB;AACD;AAEH;AAEA;AAAiB;AAEb;AAAqC;AACvC;AACO;AAGT;AACE;AACE;AAAA;AAKF;AACE;AACE;AAAyB;AAC3B;AACD;AAGH;AACE;AAA+B;AACzB;AACkC;AACH;AACnC;AACD;AAGH;AACE;AAA0D;AAG5D;AAAc;AAEV;AAIE;AACE;AAAgB;AACd;AAGW;AAGb;AAEA;AACE;AACA;AAEA;AACE;AACA;AAA+B;AACjC;AAGF;AAAqC;AACvC;AACM;AAIR;AACF;AACO;AAGT;AACE;AACE;AAAuB;AACjB;AAIR;AAGF;AACE;AACE;AAAA;AAGF;AACA;AAA6B;AAG/B;AAAmB;AAEf;AACE;AAAA;AAGF;AACA;AAA4B;AAC9B;AACwB;AAG1B;AACE;AACE;AAAA;AAGF;AACE;AAA2B;AAC7B;AAGF;AAAqC;AAEjC;AACE;AAAA;AAGF;AACE;AAGA;AAAqB;AACvB;AACF;AAC4B;AAG9B;AACE;AACA;AACA;AAEA;AACE;AAAK;AACP;AAGF;AAAqB;AAEjB;AACE;AAAA;AAMF;AAGA;AACE;AAEA;AAAA;AAGF;AAEA;AACE;AAEA;AAAA;AAGF;AAAa;AACJ;AAGT;AACG;AAI2B;AAG1B;AAAO;AACU;AACT;AACe;AACJ;AACA;AACnB;AAGJ;AAAgB;AAC0B;AACxC;AAGF;AAEA;AACE;AACA;AAAwB;AAExB;AAAY;AACd;AACF;AACuE;AAGzE;AACE;AAAsB;AAGxB;AAAmB;AAEf;AAA6B;AAC/B;AACO;AAGT;AACE;AAAqC;AAGvC;AACE;AAAuB;AAAtB;AACQ;AACL;AACA;AACA;AACA;AACA;AACF;AAEA;AAA4B;AAA3B;AACQ;AACL;AACA;AACA;AACA;AACA;AACF;AAEA;AAAiB;AAAhB;AACQ;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;AAGE;AAAA;AAAC;AAAA;AACM;AACG;AACH;AACK;AACD;AACC;AACe;AAAA;AAC3B;AACqB;AACvB;AAAA;AACF;AAAA;AACF;AAAA;AACF;AAGN;AAQA;AAAuB;AAEnB;AACA;AACA;AAEA;AACE;AAAC;AAAA;AACM;AACD;AACC;AACK;AAET;AAAA;AACH;AAGN;AAQM;AAIJ;AACA;AACA;AACA;AAEA;AAAoB;AAEhB;AAEA;AACE;AAAY;AACd;AACF;AACqB;AAGvB;AACE;AAAC;AAAA;AACM;AACD;AACK;AACJ;AACK;AAET;AAAA;AAGP;AAUA;AAAoC;AAKhC;AACE;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AAAyD;AACvD;AACA;AACA;AACA;AACU;AAGZ;AACE;AAAC;AAAA;AACK;AAC6B;AAC7B;AACC;AAAA;AACP;AAGN;AAUA;AAA2B;AAKvB;AACE;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AAEA;AAA0B;AAEtB;AAEA;AACA;AAAsB;AACxB;AACc;AAGhB;AAAoB;AAEhB;AAEA;AACE;AACA;AAEA;AACA;AAAoB;AACtB;AACF;AACyC;AAG3C;AACE;AAAiB;AAAhB;AACQ;AACY;AACV;AACM;AACX;AAIJ;AAAA;AACF;AAGN;AAEA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;;"}
package/dist/version.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const PKG_NAME = "@liveblocks/react-ui";
4
- const PKG_VERSION = typeof "3.14.0-pre6" === "string" && "3.14.0-pre6";
4
+ const PKG_VERSION = typeof "3.14.0-rc1" === "string" && "3.14.0-rc1";
5
5
  const PKG_FORMAT = typeof "cjs" === "string" && "cjs";
6
6
 
7
7
  exports.PKG_FORMAT = PKG_FORMAT;
@@ -1 +1 @@
1
- {"version":3,"file":"version.cjs","sources":["../src/version.ts"],"sourcesContent":["declare const __VERSION__: string;\ndeclare const ROLLUP_FORMAT: string;\n\nexport const PKG_NAME = \"@liveblocks/react-ui\";\nexport const PKG_VERSION = typeof __VERSION__ === \"string\" && __VERSION__;\nexport const PKG_FORMAT = typeof ROLLUP_FORMAT === \"string\" && ROLLUP_FORMAT;\n"],"names":[],"mappings":";;AAGO,MAAM,QAAW,GAAA,uBAAA;AACX,MAAA,WAAA,GAAc,OAAO,aAAA,KAAgB,QAAY,IAAA,cAAA;AACjD,MAAA,UAAA,GAAa,OAAO,KAAA,KAAkB,QAAY,IAAA;;;;;;"}
1
+ {"version":3,"file":"version.cjs","sources":["../src/version.ts"],"sourcesContent":["declare const __VERSION__: string;\ndeclare const ROLLUP_FORMAT: string;\n\nexport const PKG_NAME = \"@liveblocks/react-ui\";\nexport const PKG_VERSION = typeof __VERSION__ === \"string\" && __VERSION__;\nexport const PKG_FORMAT = typeof ROLLUP_FORMAT === \"string\" && ROLLUP_FORMAT;\n"],"names":[],"mappings":";;AAGO,MAAM,QAAW,GAAA,uBAAA;AACX,MAAA,WAAA,GAAc,OAAO,YAAA,KAAgB,QAAY,IAAA,aAAA;AACjD,MAAA,UAAA,GAAa,OAAO,KAAA,KAAkB,QAAY,IAAA;;;;;;"}
package/dist/version.js CHANGED
@@ -1,5 +1,5 @@
1
1
  const PKG_NAME = "@liveblocks/react-ui";
2
- const PKG_VERSION = typeof "3.14.0-pre6" === "string" && "3.14.0-pre6";
2
+ const PKG_VERSION = typeof "3.14.0-rc1" === "string" && "3.14.0-rc1";
3
3
  const PKG_FORMAT = typeof "esm" === "string" && "esm";
4
4
 
5
5
  export { PKG_FORMAT, PKG_NAME, PKG_VERSION };
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sources":["../src/version.ts"],"sourcesContent":["declare const __VERSION__: string;\ndeclare const ROLLUP_FORMAT: string;\n\nexport const PKG_NAME = \"@liveblocks/react-ui\";\nexport const PKG_VERSION = typeof __VERSION__ === \"string\" && __VERSION__;\nexport const PKG_FORMAT = typeof ROLLUP_FORMAT === \"string\" && ROLLUP_FORMAT;\n"],"names":[],"mappings":"AAGO,MAAM,QAAW,GAAA,uBAAA;AACX,MAAA,WAAA,GAAc,OAAO,aAAA,KAAgB,QAAY,IAAA,cAAA;AACjD,MAAA,UAAA,GAAa,OAAO,KAAA,KAAkB,QAAY,IAAA;;;;"}
1
+ {"version":3,"file":"version.js","sources":["../src/version.ts"],"sourcesContent":["declare const __VERSION__: string;\ndeclare const ROLLUP_FORMAT: string;\n\nexport const PKG_NAME = \"@liveblocks/react-ui\";\nexport const PKG_VERSION = typeof __VERSION__ === \"string\" && __VERSION__;\nexport const PKG_FORMAT = typeof ROLLUP_FORMAT === \"string\" && ROLLUP_FORMAT;\n"],"names":[],"mappings":"AAGO,MAAM,QAAW,GAAA,uBAAA;AACX,MAAA,WAAA,GAAc,OAAO,YAAA,KAAgB,QAAY,IAAA,aAAA;AACjD,MAAA,UAAA,GAAa,OAAO,KAAA,KAAkB,QAAY,IAAA;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@liveblocks/react-ui",
3
- "version": "3.14.0-pre6",
3
+ "version": "3.14.0-rc1",
4
4
  "description": "A set of React pre-built components for the Liveblocks products. Liveblocks is the all-in-one toolkit to build collaborative products like Figma, Notion, and more.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -76,9 +76,9 @@
76
76
  },
77
77
  "dependencies": {
78
78
  "@floating-ui/react-dom": "^2.1.2",
79
- "@liveblocks/client": "3.14.0-pre6",
80
- "@liveblocks/core": "3.14.0-pre6",
81
- "@liveblocks/react": "3.14.0-pre6",
79
+ "@liveblocks/client": "3.14.0-rc1",
80
+ "@liveblocks/core": "3.14.0-rc1",
81
+ "@liveblocks/react": "3.14.0-rc1",
82
82
  "@radix-ui/react-dropdown-menu": "^2.1.2",
83
83
  "@radix-ui/react-popover": "^1.1.2",
84
84
  "@radix-ui/react-slot": "^1.1.0",