@liveblocks/react-ui 2.25.0-aiprivatebeta10 → 2.25.0-aiprivatebeta12

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.
Files changed (52) hide show
  1. package/dist/components/AiChat.cjs +11 -17
  2. package/dist/components/AiChat.cjs.map +1 -1
  3. package/dist/components/AiChat.js +12 -18
  4. package/dist/components/AiChat.js.map +1 -1
  5. package/dist/components/AiTool.cjs +36 -23
  6. package/dist/components/AiTool.cjs.map +1 -1
  7. package/dist/components/AiTool.js +37 -24
  8. package/dist/components/AiTool.js.map +1 -1
  9. package/dist/components/Composer.cjs +3 -3
  10. package/dist/components/Composer.cjs.map +1 -1
  11. package/dist/components/Composer.js +3 -3
  12. package/dist/components/Composer.js.map +1 -1
  13. package/dist/components/internal/AiChatAssistantMessage.cjs +8 -4
  14. package/dist/components/internal/AiChatAssistantMessage.cjs.map +1 -1
  15. package/dist/components/internal/AiChatAssistantMessage.js +9 -5
  16. package/dist/components/internal/AiChatAssistantMessage.js.map +1 -1
  17. package/dist/components/internal/AiChatComposer.cjs.map +1 -1
  18. package/dist/components/internal/AiChatComposer.js.map +1 -1
  19. package/dist/components/internal/AiChatUserMessage.cjs.map +1 -1
  20. package/dist/components/internal/AiChatUserMessage.js.map +1 -1
  21. package/dist/components/internal/CodeBlock.cjs +6 -3
  22. package/dist/components/internal/CodeBlock.cjs.map +1 -1
  23. package/dist/components/internal/CodeBlock.js +6 -3
  24. package/dist/components/internal/CodeBlock.js.map +1 -1
  25. package/dist/index.d.cts +20 -2
  26. package/dist/index.d.ts +20 -2
  27. package/dist/overrides.cjs +5 -1
  28. package/dist/overrides.cjs.map +1 -1
  29. package/dist/overrides.js +5 -1
  30. package/dist/overrides.js.map +1 -1
  31. package/dist/primitives/AiMessage/index.cjs +24 -19
  32. package/dist/primitives/AiMessage/index.cjs.map +1 -1
  33. package/dist/primitives/AiMessage/index.js +25 -20
  34. package/dist/primitives/AiMessage/index.js.map +1 -1
  35. package/dist/primitives/Collapsible/index.cjs +3 -3
  36. package/dist/primitives/Collapsible/index.cjs.map +1 -1
  37. package/dist/primitives/Collapsible/index.js +3 -3
  38. package/dist/primitives/Collapsible/index.js.map +1 -1
  39. package/dist/utils/use-controllable-state.cjs +25 -2
  40. package/dist/utils/use-controllable-state.cjs.map +1 -1
  41. package/dist/utils/use-controllable-state.js +25 -3
  42. package/dist/utils/use-controllable-state.js.map +1 -1
  43. package/dist/utils/use-visible.cjs +3 -1
  44. package/dist/utils/use-visible.cjs.map +1 -1
  45. package/dist/utils/use-visible.js +3 -1
  46. package/dist/utils/use-visible.js.map +1 -1
  47. package/dist/version.cjs +1 -1
  48. package/dist/version.js +1 -1
  49. package/package.json +4 -4
  50. package/src/styles/index.css +1 -1
  51. package/styles.css +1 -1
  52. package/styles.css.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"AiTool.js","sources":["../../src/components/AiTool.tsx"],"sourcesContent":["import {\n type AiToolExecuteCallback,\n type AiToolTypePack,\n type JsonObject,\n kInternal,\n type NoInfr,\n type ToolResultData,\n} from \"@liveblocks/core\";\nimport type { ComponentProps, ReactNode } from \"react\";\nimport { Children, forwardRef, useCallback, useMemo, useState } from \"react\";\n\nimport { Button } from \"../_private\";\nimport { CheckCircleFillIcon, ChevronRightIcon, SpinnerIcon } from \"../icons\";\nimport { useAiToolInvocationContext } from \"../primitives/AiMessage/contexts\";\nimport * as Collapsible from \"../primitives/Collapsible\";\nimport { classNames } from \"../utils/class-names\";\nimport { CodeBlock } from \"./internal/CodeBlock\";\n\nexport interface AiToolProps\n extends Omit<ComponentProps<\"div\">, \"title\" | \"children\"> {\n /**\n * The tool's title.\n *\n * By default, a human-readable version of the tool's name is used:\n * - `\"showTodo\"` → \"Show todo\"\n * - `\"get_weather\"` → \"Get weather\"\n */\n title?: string;\n\n /**\n * An optional icon displayed next to the title.\n */\n icon?: ReactNode;\n\n /**\n * The content shown in the tool.\n */\n children?: ReactNode;\n}\n\nexport type AiToolIconProps = ComponentProps<\"div\">;\n\nexport type AiToolInspectorProps = ComponentProps<\"div\">;\n\n// TODO: AiToolConfirmationProps might need a generic since we're outside of the\n// tool definition so things like inferred args and result types are not\n// available here for `confirm` and `cancel`\n\n/**\n * @private This API will change, and is not considered stable. DO NOT RELY on it.\n *\n * This component can be used to build human-in-the-loop interfaces.\n */\nexport interface AiToolConfirmationProps<\n A extends JsonObject,\n R extends ToolResultData,\n> extends ComponentProps<\"div\"> {\n types?: NoInfr<AiToolTypePack<A, R>>;\n args?: A;\n confirm: AiToolExecuteCallback<A, R>;\n cancel: AiToolExecuteCallback<A, R>;\n variant?: \"default\" | \"destructive\";\n\n // TODO: Use existing overrides API to allow customizing the \"Confirm\" and \"Cancel\" labels\n // overrides?: Partial<GlobalOverrides & AiToolConfirmationOverrides>;\n}\n\nfunction AiToolIcon({ className, ...props }: AiToolIconProps) {\n return (\n <div className={classNames(\"lb-ai-tool-icon\", className)} {...props} />\n );\n}\n\nfunction AiToolInspector({ className, ...props }: AiToolInspectorProps) {\n const { args, partialArgs, result } = useAiToolInvocationContext();\n\n return (\n <div className={classNames(\"lb-ai-tool-inspector\", className)} {...props}>\n <CodeBlock\n title=\"Arguments\"\n code={JSON.stringify(args ?? partialArgs, null, 2)}\n />\n {result !== undefined ? (\n <CodeBlock title=\"Result\" code={JSON.stringify(result, null, 2)} />\n ) : null}\n </div>\n );\n}\n\nfunction AiToolConfirmation<\n TPack extends AiToolTypePack,\n A extends JsonObject = TPack[\"A\"],\n R extends ToolResultData = TPack[\"R\"],\n>({\n children,\n variant = \"default\",\n confirm,\n cancel,\n className,\n ...props\n}: AiToolConfirmationProps<A, R>) {\n const { status, args, respond, toolName, toolCallId } =\n useAiToolInvocationContext();\n\n const enabled = status === \"executing\";\n\n const context = useMemo(\n () => ({ toolName, toolCallId }),\n [toolName, toolCallId]\n );\n\n const onConfirmClick = useCallback(async () => {\n if (enabled) {\n respond(await confirm(args as A, context));\n }\n }, [enabled, args, confirm, respond, context]);\n\n const onCancelClick = useCallback(async () => {\n if (enabled) {\n respond(await cancel(args as A, context));\n }\n }, [enabled, args, cancel, respond, context]);\n\n if (status === \"executed\") {\n return null;\n }\n\n return (\n <div\n className={classNames(\"lb-ai-tool-confirmation\", className)}\n {...props}\n >\n {children ? (\n <div className=\"lb-ai-tool-confirmation-content\">{children}</div>\n ) : null}\n <div className=\"lb-ai-tool-confirmation-footer\">\n <div className=\"lb-ai-tool-confirmation-actions\">\n <Button\n disabled={!enabled}\n onClick={onConfirmClick}\n variant={variant === \"destructive\" ? \"destructive\" : \"primary\"}\n >\n Confirm\n </Button>\n <Button\n disabled={!enabled}\n onClick={onCancelClick}\n variant=\"secondary\"\n >\n Cancel\n </Button>\n </div>\n </div>\n </div>\n );\n}\n\nfunction prettifyString(string: string) {\n return (\n string\n // Convert camelCase to spaces\n .replace(/([a-z])([A-Z])/g, \"$1 $2\")\n // Convert snake_case and kebab-case to spaces\n .replace(/[_-]+/g, \" \")\n // Collapse multiple following spaces\n .replace(/\\s+/g, \" \")\n // Trim leading and trailing spaces\n .trim()\n // Capitalize first word\n .toLowerCase()\n .replace(/^\\w/, (character) => character.toUpperCase())\n );\n}\n\nconst noop = () => {};\n\nexport const AiTool = Object.assign(\n forwardRef<HTMLDivElement, AiToolProps>(\n ({ children, title, icon, className, ...props }, forwardedRef) => {\n const {\n status,\n toolName,\n [kInternal]: { execute },\n } = useAiToolInvocationContext();\n const [isOpen, setIsOpen] = useState(true);\n // TODO: This check won't work for cases like:\n // <AiTool>\n // <ComponentThatRendersNull />\n // <ComponentThatAlsoRendersNull />\n // </AiTool>\n // One solution could be to check the DOM on every render with `useLayoutEffect`\n // to see if there's any actual content.\n const hasContent = Children.count(children) > 0;\n const resolvedTitle = useMemo(() => {\n return title ?? prettifyString(toolName);\n }, [title, toolName]);\n\n return (\n <Collapsible.Root\n ref={forwardedRef}\n className={classNames(\"lb-collapsible lb-ai-tool\", className)}\n {...props}\n open={hasContent ? isOpen : false}\n onOpenChange={hasContent ? setIsOpen : noop}\n disabled={!hasContent}\n >\n <Collapsible.Trigger className=\"lb-collapsible-trigger lb-ai-tool-header\">\n {icon ? (\n <div className=\"lb-ai-tool-header-icon-container\">{icon}</div>\n ) : null}\n <span className=\"lb-ai-tool-header-title\">{resolvedTitle}</span>\n {hasContent ? (\n <span className=\"lb-collapsible-chevron lb-icon-container\">\n <ChevronRightIcon />\n </span>\n ) : null}\n <div className=\"lb-ai-tool-header-status\">\n {status === \"executed\" ? (\n <CheckCircleFillIcon />\n ) : execute !== undefined ? (\n // Only show a spinner if the tool has an `execute` method.\n <SpinnerIcon />\n ) : null}\n </div>\n </Collapsible.Trigger>\n\n {children ? (\n <Collapsible.Content className=\"lb-collapsible-content lb-ai-tool-content-container\">\n <div className=\"lb-ai-tool-content\">{children}</div>\n </Collapsible.Content>\n ) : null}\n </Collapsible.Root>\n );\n }\n ),\n {\n Icon: AiToolIcon,\n Inspector: AiToolInspector,\n Confirmation: AiToolConfirmation,\n }\n);\n"],"names":["Collapsible.Root","Collapsible.Trigger","Collapsible.Content"],"mappings":"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"}
1
+ {"version":3,"file":"AiTool.js","sources":["../../src/components/AiTool.tsx"],"sourcesContent":["import {\n type AiToolExecuteCallback,\n type AiToolTypePack,\n type JsonObject,\n kInternal,\n type NoInfr,\n type ToolResultData,\n} from \"@liveblocks/core\";\nimport type { ComponentProps, ReactNode } from \"react\";\nimport { Children, forwardRef, useCallback, useMemo } from \"react\";\n\nimport { Button } from \"../_private\";\nimport { CheckCircleFillIcon, ChevronRightIcon, SpinnerIcon } from \"../icons\";\nimport {\n type AiToolConfirmationOverrides,\n type GlobalOverrides,\n useOverrides,\n} from \"../overrides\";\nimport { useAiToolInvocationContext } from \"../primitives/AiMessage/contexts\";\nimport * as Collapsible from \"../primitives/Collapsible\";\nimport { classNames } from \"../utils/class-names\";\nimport { useSemiControllableState } from \"../utils/use-controllable-state\";\nimport { CodeBlock } from \"./internal/CodeBlock\";\n\nexport interface AiToolProps\n extends Omit<ComponentProps<\"div\">, \"title\" | \"children\"> {\n /**\n * The tool's title.\n *\n * By default, a human-readable version of the tool's name is used:\n * - `\"showTodo\"` → \"Show todo\"\n * - `\"get_weather\"` → \"Get weather\"\n */\n title?: string;\n\n /**\n * An optional icon displayed next to the title.\n */\n icon?: ReactNode;\n\n /**\n * The content shown in the tool.\n */\n children?: ReactNode;\n\n /**\n * Whether the content is currently collapsed.\n * It is not a traditional controlled value, as in if you set it to `true` it would only stay expanded.\n * Instead, it is \"semi-controlled\", meaning that setting it to `true` will expand it, but it\n * can still be collapsed/expanded by clicking on it.\n */\n collapsed?: boolean;\n\n /**\n * The event handler called when the content is collapsed or expanded by clicking on it.\n */\n onCollapsedChange?: (collapsed: boolean) => void;\n}\n\nexport type AiToolIconProps = ComponentProps<\"div\">;\n\nexport type AiToolInspectorProps = ComponentProps<\"div\">;\n\n/**\n * @private This API will change, and is not considered stable. DO NOT RELY on it.\n *\n * This component can be used to build human-in-the-loop interfaces.\n */\nexport interface AiToolConfirmationProps<\n A extends JsonObject,\n R extends ToolResultData,\n> extends ComponentProps<\"div\"> {\n types?: NoInfr<AiToolTypePack<A, R>>;\n args?: A;\n confirm: AiToolExecuteCallback<A, R>;\n cancel: AiToolExecuteCallback<A, R>;\n variant?: \"default\" | \"destructive\";\n overrides?: Partial<GlobalOverrides & AiToolConfirmationOverrides>;\n}\n\nfunction AiToolIcon({ className, ...props }: AiToolIconProps) {\n return (\n <div className={classNames(\"lb-ai-tool-icon\", className)} {...props} />\n );\n}\n\nfunction AiToolInspector({ className, ...props }: AiToolInspectorProps) {\n const { args, partialArgs, result } = useAiToolInvocationContext();\n\n return (\n <div className={classNames(\"lb-ai-tool-inspector\", className)} {...props}>\n <CodeBlock\n title=\"Arguments\"\n code={JSON.stringify(args ?? partialArgs, null, 2)}\n />\n {result !== undefined ? (\n <CodeBlock title=\"Result\" code={JSON.stringify(result, null, 2)} />\n ) : null}\n </div>\n );\n}\n\nfunction AiToolConfirmation<\n TPack extends AiToolTypePack,\n A extends JsonObject = TPack[\"A\"],\n R extends ToolResultData = TPack[\"R\"],\n>({\n children,\n variant = \"default\",\n confirm,\n cancel,\n overrides,\n className,\n ...props\n}: AiToolConfirmationProps<A, R>) {\n const { status, args, respond, name, invocationId } =\n useAiToolInvocationContext();\n const $ = useOverrides(overrides);\n\n const enabled = status === \"executing\";\n\n const context = useMemo(() => ({ name, invocationId }), [name, invocationId]);\n\n const onConfirmClick = useCallback(async () => {\n if (enabled) {\n respond(await confirm(args as A, context));\n }\n }, [enabled, args, confirm, respond, context]);\n\n const onCancelClick = useCallback(async () => {\n if (enabled) {\n respond(await cancel(args as A, context));\n }\n }, [enabled, args, cancel, respond, context]);\n\n // If there's no content and the tool has been executed (so there's no\n // confirmation UI displayed either), don't render anything.\n if (status === \"executed\" && !children) {\n return null;\n }\n\n return (\n <div\n className={classNames(\"lb-ai-tool-confirmation\", className)}\n {...props}\n >\n {children ? (\n <div className=\"lb-ai-tool-confirmation-content\">{children}</div>\n ) : null}\n {status !== \"executed\" && (\n <div className=\"lb-ai-tool-confirmation-footer\">\n <div className=\"lb-ai-tool-confirmation-actions\">\n <Button\n disabled={!enabled}\n onClick={onCancelClick}\n variant=\"secondary\"\n >\n {$.AI_TOOL_CONFIRMATION_CANCEL}\n </Button>\n <Button\n disabled={!enabled}\n onClick={onConfirmClick}\n variant={variant === \"destructive\" ? \"destructive\" : \"primary\"}\n >\n {$.AI_TOOL_CONFIRMATION_CONFIRM}\n </Button>\n </div>\n </div>\n )}\n </div>\n );\n}\n\nfunction prettifyString(string: string) {\n return (\n string\n // Convert camelCase to spaces\n .replace(/([a-z])([A-Z])/g, \"$1 $2\")\n // Convert snake_case and kebab-case to spaces\n .replace(/[_-]+/g, \" \")\n // Collapse multiple following spaces\n .replace(/\\s+/g, \" \")\n // Trim leading and trailing spaces\n .trim()\n // Capitalize first word\n .toLowerCase()\n .replace(/^\\w/, (character) => character.toUpperCase())\n );\n}\n\nexport const AiTool = Object.assign(\n forwardRef<HTMLDivElement, AiToolProps>(\n (\n {\n children,\n title,\n icon,\n collapsed,\n onCollapsedChange,\n className,\n ...props\n },\n forwardedRef\n ) => {\n const {\n status,\n name,\n [kInternal]: { execute },\n } = useAiToolInvocationContext();\n const [semiControlledCollapsed, onSemiControlledCollapsed] =\n useSemiControllableState(collapsed ?? false, onCollapsedChange);\n // TODO: This check won't work for cases like:\n // <AiTool>\n // <ComponentThatRendersNull />\n // <ComponentThatAlsoRendersNull />\n // </AiTool>\n // One solution could be to check the DOM on every render with `useLayoutEffect`\n // to see if there's any actual content.\n // For now we're limiting the visual issues caused by the above by using CSS's\n // `:empty` pseudo-class to make the content 0px high if it's actually empty.\n const hasContent = Children.count(children) > 0;\n const resolvedTitle = useMemo(() => {\n return title ?? prettifyString(name);\n }, [title, name]);\n\n // `AiTool` uses \"collapsed\" instead of \"open\" (like the `Composer` component) because \"open\"\n // makes sense next to something called \"Collapsible\" but less so for something called \"AiTool\".\n const handleCollapsibleOpenChange = useCallback(\n (open: boolean) => {\n onSemiControlledCollapsed(!open);\n },\n [onSemiControlledCollapsed]\n );\n\n return (\n <Collapsible.Root\n ref={forwardedRef}\n className={classNames(\"lb-collapsible lb-ai-tool\", className)}\n {...props}\n // Regardless of `semiControlledCollapsed`, the collapsible is closed if there's no content.\n open={hasContent ? !semiControlledCollapsed : false}\n onOpenChange={handleCollapsibleOpenChange}\n disabled={!hasContent}\n >\n <Collapsible.Trigger className=\"lb-collapsible-trigger lb-ai-tool-header\">\n {icon ? (\n <div className=\"lb-ai-tool-header-icon-container\">{icon}</div>\n ) : null}\n <span className=\"lb-ai-tool-header-title\">{resolvedTitle}</span>\n {hasContent ? (\n <span className=\"lb-collapsible-chevron lb-icon-container\">\n <ChevronRightIcon />\n </span>\n ) : null}\n <div className=\"lb-ai-tool-header-status\">\n {status === \"executed\" ? (\n <CheckCircleFillIcon />\n ) : execute !== undefined ? (\n // Only show a spinner if the tool has an `execute` method.\n <SpinnerIcon />\n ) : null}\n </div>\n </Collapsible.Trigger>\n\n {hasContent ? (\n <Collapsible.Content className=\"lb-collapsible-content lb-ai-tool-content-container\">\n <div className=\"lb-ai-tool-content\">{children}</div>\n </Collapsible.Content>\n ) : null}\n </Collapsible.Root>\n );\n }\n ),\n {\n Icon: AiToolIcon,\n Inspector: AiToolInspector,\n Confirmation: AiToolConfirmation,\n }\n);\n"],"names":["Collapsible.Root","Collapsible.Trigger","Collapsible.Content"],"mappings":"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"}
@@ -425,9 +425,9 @@ const Composer = react.forwardRef(
425
425
  const isEmojiPickerOpenRef = react.useRef(false);
426
426
  const $ = overrides.useOverrides(overrides$1);
427
427
  const [isCollapsed, onCollapsedChange] = useControllableState.useControllableState(
428
- controlledCollapsed === void 0 && defaultCollapsed === void 0 ? false : controlledCollapsed,
429
- controlledOnCollapsedChange,
430
- defaultCollapsed
428
+ defaultCollapsed ?? false,
429
+ controlledCollapsed,
430
+ controlledOnCollapsedChange
431
431
  );
432
432
  const canCommentFallback = react.useSyncExternalStore(
433
433
  react.useCallback(
@@ -1 +1 @@
1
- {"version":3,"file":"Composer.cjs","sources":["../../src/components/Composer.tsx"],"sourcesContent":["\"use client\";\n\nimport type {\n BaseMetadata,\n CommentAttachment,\n CommentMixedAttachment,\n DM,\n} from \"@liveblocks/core\";\nimport { Permission } from \"@liveblocks/core\";\nimport { useRoom } from \"@liveblocks/react\";\nimport {\n useCreateRoomComment,\n useCreateRoomThread,\n useEditRoomComment,\n useLayoutEffect,\n useResolveMentionSuggestions,\n useRoomPermissions,\n} from \"@liveblocks/react/_private\";\nimport type {\n ComponentPropsWithoutRef,\n ComponentType,\n FocusEvent,\n FormEvent,\n ForwardedRef,\n MouseEvent,\n PropsWithChildren,\n ReactNode,\n RefAttributes,\n SyntheticEvent,\n} from \"react\";\nimport {\n createContext,\n forwardRef,\n useCallback,\n useMemo,\n useRef,\n useSyncExternalStore,\n} from \"react\";\n\nimport { useLiveblocksUIConfig } from \"../config\";\nimport { FLOATING_ELEMENT_SIDE_OFFSET, MENTION_CHARACTER } from \"../constants\";\nimport { AttachmentIcon } from \"../icons/Attachment\";\nimport { BoldIcon } from \"../icons/Bold\";\nimport { CodeIcon } from \"../icons/Code\";\nimport { EmojiIcon } from \"../icons/Emoji\";\nimport { ItalicIcon } from \"../icons/Italic\";\nimport { MentionIcon } from \"../icons/Mention\";\nimport { SendIcon } from \"../icons/Send\";\nimport { StrikethroughIcon } from \"../icons/Strikethrough\";\nimport type { ComposerOverrides, GlobalOverrides } from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport * as ComposerPrimitive from \"../primitives/Composer\";\nimport {\n useComposer,\n useComposerAttachmentsContext,\n useComposerEditorContext,\n} from \"../primitives/Composer/contexts\";\nimport type {\n ComposerEditorComponents,\n ComposerEditorLinkProps,\n ComposerEditorMentionProps,\n ComposerEditorMentionSuggestionsProps,\n ComposerEditorProps,\n ComposerFormProps,\n ComposerMarkToggleProps,\n ComposerSubmitComment,\n} from \"../primitives/Composer/types\";\nimport { useComposerAttachmentsDropArea } from \"../primitives/Composer/utils\";\nimport type { ComposerBodyMark } from \"../types\";\nimport { classNames } from \"../utils/class-names\";\nimport { useControllableState } from \"../utils/use-controllable-state\";\nimport { FileAttachment } from \"./internal/Attachment\";\nimport { Attribution } from \"./internal/Attribution\";\nimport { Avatar } from \"./internal/Avatar\";\nimport { Button } from \"./internal/Button\";\nimport type { EmojiPickerProps } from \"./internal/EmojiPicker\";\nimport { EmojiPicker, EmojiPickerTrigger } from \"./internal/EmojiPicker\";\nimport { ShortcutTooltip, Tooltip, TooltipProvider } from \"./internal/Tooltip\";\nimport { User } from \"./internal/User\";\n\ninterface EditorActionProps extends ComponentPropsWithoutRef<\"button\"> {\n label: string;\n tooltipLabel?: string;\n}\n\ninterface EmojiEditorActionProps extends EditorActionProps {\n onPickerOpenChange?: EmojiPickerProps[\"onOpenChange\"];\n}\n\ninterface MarkToggleProps extends ComposerMarkToggleProps {\n icon?: ReactNode;\n shortcut?: string;\n}\n\ntype ComposerCreateThreadProps<M extends BaseMetadata> = {\n threadId?: never;\n commentId?: never;\n\n /**\n * The metadata of the thread to create.\n */\n metadata?: M;\n};\n\ntype ComposerCreateCommentProps = {\n /**\n * The ID of the thread to reply to.\n */\n threadId: string;\n commentId?: never;\n metadata?: never;\n};\n\ntype ComposerEditCommentProps = {\n /**\n * The ID of the thread to edit a comment in.\n */\n threadId: string;\n\n /**\n * The ID of the comment to edit.\n */\n commentId: string;\n metadata?: never;\n};\n\nexport type ComposerProps<M extends BaseMetadata = DM> = Omit<\n ComponentPropsWithoutRef<\"form\">,\n \"defaultValue\"\n> &\n (\n | ComposerCreateThreadProps<M>\n | ComposerCreateCommentProps\n | ComposerEditCommentProps\n ) & {\n /**\n * The event handler called when the composer is submitted.\n */\n onComposerSubmit?: (\n comment: ComposerSubmitComment,\n event: FormEvent<HTMLFormElement>\n ) => Promise<void> | void;\n\n /**\n * The composer's initial value.\n */\n defaultValue?: ComposerEditorProps[\"defaultValue\"];\n\n /**\n * The composer's initial attachments.\n */\n defaultAttachments?: CommentAttachment[];\n\n /**\n * Whether the composer is collapsed. Setting a value will make the composer controlled.\n */\n collapsed?: boolean;\n\n /**\n * The event handler called when the collapsed state of the composer changes.\n */\n onCollapsedChange?: (collapsed: boolean) => void;\n\n /**\n * Whether the composer is initially collapsed. Setting a value will make the composer uncontrolled.\n */\n defaultCollapsed?: boolean;\n\n /**\n * Whether to show and allow adding attachments.\n */\n showAttachments?: boolean;\n\n /**\n * Whether to show formatting controls (e.g. a floating toolbar with formatting toggles when selecting text)\n */\n showFormattingControls?: boolean;\n\n /**\n * Whether the composer is disabled.\n */\n disabled?: ComposerFormProps[\"disabled\"];\n\n /**\n * Whether to focus the composer on mount.\n */\n autoFocus?: ComposerEditorProps[\"autoFocus\"];\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<GlobalOverrides & ComposerOverrides>;\n\n /**\n * @internal\n */\n actions?: ReactNode;\n\n /**\n * @internal\n */\n showAttribution?: boolean;\n\n /**\n * @internal\n */\n roomId?: string;\n };\n\ninterface ComposerEditorContainerProps\n extends Pick<\n ComposerProps,\n | \"defaultValue\"\n | \"showAttachments\"\n | \"showFormattingControls\"\n | \"showAttribution\"\n | \"overrides\"\n | \"actions\"\n | \"autoFocus\"\n | \"disabled\"\n > {\n isCollapsed: boolean | undefined;\n onEmptyChange: (isEmpty: boolean) => void;\n hasResolveMentionSuggestions: boolean;\n onEmojiPickerOpenChange: (isOpen: boolean) => void;\n onEditorClick: (event: MouseEvent<HTMLDivElement>) => void;\n}\n\nfunction ComposerInsertMentionEditorAction({\n label,\n tooltipLabel,\n className,\n onClick,\n ...props\n}: EditorActionProps) {\n const { createMention } = useComposer();\n\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n\n if (!event.isDefaultPrevented()) {\n event.stopPropagation();\n createMention();\n }\n },\n [createMention, onClick]\n );\n\n return (\n <Tooltip content={tooltipLabel ?? label}>\n <Button\n className={classNames(\"lb-composer-editor-action\", className)}\n onPointerDown={preventDefault}\n onClick={handleClick}\n aria-label={label}\n icon={<MentionIcon />}\n {...props}\n />\n </Tooltip>\n );\n}\n\nfunction ComposerInsertEmojiEditorAction({\n label,\n tooltipLabel,\n onPickerOpenChange,\n className,\n ...props\n}: EmojiEditorActionProps) {\n const { insertText } = useComposer();\n\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n return (\n <EmojiPicker onEmojiSelect={insertText} onOpenChange={onPickerOpenChange}>\n <Tooltip content={tooltipLabel ?? label}>\n <EmojiPickerTrigger asChild>\n <Button\n className={classNames(\"lb-composer-editor-action\", className)}\n onPointerDown={preventDefault}\n onClick={stopPropagation}\n aria-label={label}\n icon={<EmojiIcon />}\n {...props}\n />\n </EmojiPickerTrigger>\n </Tooltip>\n </EmojiPicker>\n );\n}\n\nfunction ComposerAttachFilesEditorAction({\n label,\n tooltipLabel,\n className,\n ...props\n}: EditorActionProps) {\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n return (\n <Tooltip content={tooltipLabel ?? label}>\n <ComposerPrimitive.AttachFiles asChild>\n <Button\n className={classNames(\"lb-composer-editor-action\", className)}\n onPointerDown={preventDefault}\n onClick={stopPropagation}\n aria-label={label}\n icon={<AttachmentIcon />}\n {...props}\n />\n </ComposerPrimitive.AttachFiles>\n </Tooltip>\n );\n}\n\nfunction ComposerMention({ userId }: ComposerEditorMentionProps) {\n return (\n <ComposerPrimitive.Mention className=\"lb-composer-mention\">\n {MENTION_CHARACTER}\n <User userId={userId} />\n </ComposerPrimitive.Mention>\n );\n}\n\nfunction ComposerMentionSuggestions({\n userIds,\n}: ComposerEditorMentionSuggestionsProps) {\n return userIds.length > 0 ? (\n <ComposerPrimitive.Suggestions className=\"lb-root lb-portal lb-elevation lb-composer-suggestions lb-composer-mention-suggestions\">\n <ComposerPrimitive.SuggestionsList className=\"lb-composer-suggestions-list lb-composer-mention-suggestions-list\">\n {userIds.map((userId) => (\n <ComposerPrimitive.SuggestionsListItem\n key={userId}\n className=\"lb-composer-suggestions-list-item lb-composer-mention-suggestion\"\n value={userId}\n >\n <Avatar\n userId={userId}\n className=\"lb-composer-mention-suggestion-avatar\"\n />\n <User\n userId={userId}\n className=\"lb-composer-mention-suggestion-user\"\n />\n </ComposerPrimitive.SuggestionsListItem>\n ))}\n </ComposerPrimitive.SuggestionsList>\n </ComposerPrimitive.Suggestions>\n ) : null;\n}\n\nfunction MarkToggle({\n mark,\n icon,\n shortcut,\n children,\n ...props\n}: MarkToggleProps) {\n const $ = useOverrides();\n const label = useMemo(() => {\n return $.COMPOSER_TOGGLE_MARK(mark);\n }, [$, mark]);\n\n return (\n <ShortcutTooltip\n content={label}\n shortcut={shortcut}\n sideOffset={FLOATING_ELEMENT_SIDE_OFFSET + 2}\n >\n <ComposerPrimitive.MarkToggle mark={mark} asChild {...props}>\n <Button aria-label={label} variant=\"toolbar\" icon={icon}>\n {children}\n </Button>\n </ComposerPrimitive.MarkToggle>\n </ShortcutTooltip>\n );\n}\n\ntype MarkToggles = {\n [K in ComposerBodyMark]: ComponentType<PropsWithChildren>;\n};\n\nconst markToggles: MarkToggles = {\n bold: () => <MarkToggle mark=\"bold\" shortcut=\"Mod-B\" icon={<BoldIcon />} />,\n italic: () => (\n <MarkToggle mark=\"italic\" shortcut=\"Mod-I\" icon={<ItalicIcon />} />\n ),\n strikethrough: () => (\n <MarkToggle\n mark=\"strikethrough\"\n shortcut=\"Mod-Shift-S\"\n icon={<StrikethroughIcon />}\n />\n ),\n code: () => <MarkToggle mark=\"code\" shortcut=\"Mod-E\" icon={<CodeIcon />} />,\n};\n\nconst markTogglesList = Object.entries(markToggles).map(([mark, Toggle]) => (\n <Toggle key={mark} />\n));\n\nfunction ComposerFloatingToolbar() {\n return (\n <ComposerPrimitive.FloatingToolbar className=\"lb-root lb-portal lb-elevation lb-composer-floating-toolbar\">\n {markTogglesList}\n </ComposerPrimitive.FloatingToolbar>\n );\n}\n\nfunction ComposerLink({ href, children }: ComposerEditorLinkProps) {\n return (\n <ComposerPrimitive.Link href={href} className=\"lb-composer-link\">\n {children}\n </ComposerPrimitive.Link>\n );\n}\n\ninterface ComposerAttachmentsProps extends ComponentPropsWithoutRef<\"div\"> {\n overrides?: Partial<GlobalOverrides & ComposerOverrides>;\n}\n\ninterface ComposerFileAttachmentProps extends ComponentPropsWithoutRef<\"div\"> {\n attachment: CommentMixedAttachment;\n overrides?: Partial<GlobalOverrides & ComposerOverrides>;\n}\n\nfunction ComposerFileAttachment({\n attachment,\n className,\n overrides,\n ...props\n}: ComposerFileAttachmentProps) {\n const { removeAttachment } = useComposer();\n const { roomId } = useComposerEditorContext();\n\n const handleDeleteClick = useCallback(() => {\n removeAttachment(attachment.id);\n }, [attachment.id, removeAttachment]);\n\n return (\n <FileAttachment\n className={classNames(\"lb-composer-attachment\", className)}\n {...props}\n attachment={attachment}\n onDeleteClick={handleDeleteClick}\n preventFocusOnDelete\n overrides={overrides}\n roomId={roomId}\n />\n );\n}\n\nfunction ComposerAttachments({\n overrides,\n className,\n ...props\n}: ComposerAttachmentsProps) {\n const { attachments } = useComposer();\n\n if (attachments.length === 0) {\n return null;\n }\n\n return (\n <div\n className={classNames(\"lb-composer-attachments\", className)}\n {...props}\n >\n <div className=\"lb-attachments\">\n {attachments.map((attachment) => {\n return (\n <ComposerFileAttachment\n key={attachment.id}\n attachment={attachment}\n overrides={overrides}\n />\n );\n })}\n </div>\n </div>\n );\n}\n\nconst editorRequiredComponents: ComposerEditorComponents = {\n Mention: ComposerMention,\n MentionSuggestions: ComposerMentionSuggestions,\n Link: ComposerLink,\n};\n\nfunction ComposerEditorContainer({\n showAttachments = true,\n showFormattingControls = true,\n showAttribution,\n defaultValue,\n isCollapsed,\n overrides,\n actions,\n autoFocus,\n disabled,\n hasResolveMentionSuggestions,\n onEmojiPickerOpenChange,\n onEmptyChange,\n onEditorClick,\n}: ComposerEditorContainerProps) {\n const { isEmpty } = useComposer();\n const { hasMaxAttachments } = useComposerAttachmentsContext();\n const $ = useOverrides(overrides);\n const components = useMemo(() => {\n return {\n ...editorRequiredComponents,\n FloatingToolbar: showFormattingControls\n ? ComposerFloatingToolbar\n : undefined,\n };\n }, [showFormattingControls]);\n\n const [isDraggingOver, dropAreaProps] = useComposerAttachmentsDropArea({\n disabled: disabled || hasMaxAttachments,\n });\n\n useLayoutEffect(() => {\n onEmptyChange(isEmpty);\n }, [isEmpty, onEmptyChange]);\n\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n return (\n <div className=\"lb-composer-editor-container\" {...dropAreaProps}>\n <ComposerPrimitive.Editor\n className=\"lb-composer-editor\"\n onClick={onEditorClick}\n placeholder={$.COMPOSER_PLACEHOLDER}\n defaultValue={defaultValue}\n autoFocus={autoFocus}\n components={components}\n disabled={disabled}\n dir={$.dir}\n />\n {showAttachments && <ComposerAttachments overrides={overrides} />}\n {(!isCollapsed || isDraggingOver) && (\n <div className=\"lb-composer-footer\">\n <div className=\"lb-composer-editor-actions\">\n {hasResolveMentionSuggestions && (\n <ComposerInsertMentionEditorAction\n label={$.COMPOSER_INSERT_MENTION}\n disabled={disabled}\n />\n )}\n <ComposerInsertEmojiEditorAction\n label={$.COMPOSER_INSERT_EMOJI}\n onPickerOpenChange={onEmojiPickerOpenChange}\n disabled={disabled}\n />\n {showAttachments && (\n <ComposerAttachFilesEditorAction\n label={$.COMPOSER_ATTACH_FILES}\n disabled={disabled}\n />\n )}\n </div>\n {showAttribution && <Attribution />}\n <div className=\"lb-composer-actions\">\n {actions ?? (\n <>\n <ShortcutTooltip content={$.COMPOSER_SEND} shortcut=\"Enter\">\n <ComposerPrimitive.Submit asChild>\n <Button\n onPointerDown={preventDefault}\n onClick={stopPropagation}\n className=\"lb-composer-action\"\n variant=\"primary\"\n aria-label={$.COMPOSER_SEND}\n icon={<SendIcon />}\n />\n </ComposerPrimitive.Submit>\n </ShortcutTooltip>\n </>\n )}\n </div>\n </div>\n )}\n {showAttachments && isDraggingOver && (\n <div className=\"lb-composer-attachments-drop-area\">\n <div className=\"lb-composer-attachments-drop-area-label\">\n <AttachmentIcon />\n {$.COMPOSER_ATTACH_FILES}\n </div>\n </div>\n )}\n </div>\n );\n}\n\nexport const ComposerRoomIdContext = createContext<string | null>(null);\n\n/**\n * Displays a composer to create comments.\n *\n * @example\n * <Composer />\n */\nexport const Composer = forwardRef(\n <M extends BaseMetadata = DM>(\n {\n threadId,\n commentId,\n metadata,\n defaultValue,\n defaultAttachments,\n onComposerSubmit,\n collapsed: controlledCollapsed,\n defaultCollapsed,\n onCollapsedChange: controlledOnCollapsedChange,\n overrides,\n actions,\n onBlur,\n className,\n onFocus,\n autoFocus,\n disabled,\n showAttachments = true,\n showFormattingControls = true,\n showAttribution,\n roomId: _roomId,\n ...props\n }: ComposerProps<M>,\n forwardedRef: ForwardedRef<HTMLFormElement>\n ) => {\n const room = useRoom({ allowOutsideRoom: true });\n\n const roomId = _roomId !== undefined ? _roomId : room?.id;\n if (roomId === undefined) {\n throw new Error(\n \"Composer must be a descendant of RoomProvider component\"\n );\n }\n\n const createThread = useCreateRoomThread(roomId);\n const createComment = useCreateRoomComment(roomId);\n const editComment = useEditRoomComment(roomId);\n const { preventUnsavedComposerChanges } = useLiveblocksUIConfig();\n const hasResolveMentionSuggestions =\n useResolveMentionSuggestions() !== undefined;\n const isEmptyRef = useRef(true);\n const isEmojiPickerOpenRef = useRef(false);\n const $ = useOverrides(overrides);\n const [isCollapsed, onCollapsedChange] = useControllableState(\n // If the composer is neither controlled nor uncontrolled, it defaults to controlled as uncollapsed.\n controlledCollapsed === undefined && defaultCollapsed === undefined\n ? false\n : controlledCollapsed,\n controlledOnCollapsedChange,\n defaultCollapsed\n );\n\n const canCommentFallback = useSyncExternalStore(\n useCallback(\n (callback) => {\n if (room === null) return () => {};\n return room.events.self.subscribeOnce(callback);\n },\n [room]\n ),\n useCallback(() => {\n return room?.getSelf()?.canComment ?? true;\n }, [room]),\n useCallback(() => true, [])\n );\n\n const permissions = useRoomPermissions(roomId);\n const canComment =\n permissions.size > 0\n ? permissions.has(Permission.CommentsWrite) ||\n permissions.has(Permission.Write)\n : canCommentFallback;\n\n const setEmptyRef = useCallback((isEmpty: boolean) => {\n isEmptyRef.current = isEmpty;\n }, []);\n\n const setEmojiPickerOpenRef = useCallback((isEmojiPickerOpen: boolean) => {\n isEmojiPickerOpenRef.current = isEmojiPickerOpen;\n }, []);\n\n const handleFocus = useCallback(\n (event: FocusEvent<HTMLFormElement>) => {\n onFocus?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n if (isEmptyRef.current && canComment) {\n onCollapsedChange?.(false);\n }\n },\n [onCollapsedChange, onFocus, canComment]\n );\n\n const handleBlur = useCallback(\n (event: FocusEvent<HTMLFormElement>) => {\n onBlur?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n const isOutside = !event.currentTarget.contains(\n event.relatedTarget ?? document.activeElement\n );\n\n if (isOutside && isEmptyRef.current && !isEmojiPickerOpenRef.current) {\n onCollapsedChange?.(true);\n }\n },\n [onBlur, onCollapsedChange]\n );\n\n const handleEditorClick = useCallback(\n (event: MouseEvent<HTMLDivElement>) => {\n event.stopPropagation();\n\n if (isEmptyRef.current && canComment) {\n onCollapsedChange?.(false);\n }\n },\n [onCollapsedChange, canComment]\n );\n\n const handleComposerSubmit = useCallback(\n (comment: ComposerSubmitComment, event: FormEvent<HTMLFormElement>) => {\n onComposerSubmit?.(comment, event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n event.stopPropagation();\n\n if (commentId && threadId) {\n editComment({\n commentId,\n threadId,\n body: comment.body,\n attachments: comment.attachments,\n });\n } else if (threadId) {\n createComment({\n threadId,\n body: comment.body,\n attachments: comment.attachments,\n });\n } else {\n createThread({\n body: comment.body,\n metadata: metadata ?? {},\n attachments: comment.attachments,\n });\n }\n },\n [\n commentId,\n createComment,\n createThread,\n editComment,\n metadata,\n onComposerSubmit,\n threadId,\n ]\n );\n\n return (\n <TooltipProvider>\n <ComposerPrimitive.Form\n onComposerSubmit={handleComposerSubmit}\n className={classNames(\n \"lb-root lb-composer lb-composer-form\",\n className\n )}\n dir={$.dir}\n {...props}\n ref={forwardedRef}\n data-collapsed={isCollapsed ? \"\" : undefined}\n onFocus={handleFocus}\n onBlur={handleBlur}\n disabled={disabled || !canComment}\n defaultAttachments={defaultAttachments}\n pasteFilesAsAttachments={showAttachments}\n preventUnsavedChanges={preventUnsavedComposerChanges}\n roomId={roomId}\n >\n <ComposerEditorContainer\n defaultValue={defaultValue}\n actions={actions}\n overrides={overrides}\n isCollapsed={isCollapsed}\n showAttachments={showAttachments}\n showAttribution={showAttribution}\n showFormattingControls={showFormattingControls}\n hasResolveMentionSuggestions={hasResolveMentionSuggestions}\n onEmptyChange={setEmptyRef}\n onEmojiPickerOpenChange={setEmojiPickerOpenRef}\n onEditorClick={handleEditorClick}\n autoFocus={autoFocus}\n disabled={disabled}\n />\n </ComposerPrimitive.Form>\n </TooltipProvider>\n );\n }\n) as <M extends BaseMetadata = DM>(\n props: ComposerProps<M> & RefAttributes<HTMLFormElement>\n) => JSX.Element;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoOA;AAA2C;AACzC;AACA;AACA;AACA;AAEF;AACE;AAEA;AACE;AAAqB;AAGvB;AAAoB;AAEhB;AAEA;AACE;AACA;AAAc;AAChB;AACF;AACuB;AAGzB;AACG;AAAiC;AAC/B;AAC6D;AAC7C;AACN;AACG;AACO;AACf;AACN;AAGN;AAEA;AAAyC;AACvC;AACA;AACA;AACA;AAEF;AACE;AAEA;AACE;AAAqB;AAGvB;AACE;AAAsB;AAGxB;AACG;AAA2B;AAA0B;AACnD;AAAiC;AAC/B;AAA0B;AACxB;AAC6D;AAC7C;AACN;AACG;AACK;AACb;AACN;AACF;AACF;AAGN;AAEA;AAAyC;AACvC;AACA;AACA;AAEF;AACE;AACE;AAAqB;AAGvB;AACE;AAAsB;AAGxB;AACG;AAAiC;AAC/B;AAAqC;AACnC;AAC6D;AAC7C;AACN;AACG;AACU;AAClB;AACN;AACF;AAGN;AAEA;AACE;AACG;AAAoC;AAClC;AAAA;AACA;AAAK;AAAgB;AAAA;AAG5B;AAEA;AAAoC;AAEpC;AACE;AACG;AAAwC;AACtC;AAA4C;AAExC;AAEW;AACH;AAEP;AAAC;AACC;AACU;AACZ;AACC;AACC;AACU;AACZ;AAAA;AAEH;AACH;AAGN;AAEA;AAAoB;AAClB;AACA;AACA;AACA;AAEF;AACE;AACA;AACE;AAAkC;AAGpC;AACG;AACU;AACT;AAC2C;AAE1C;AAA6B;AAAmB;AAAK;AACnD;AAAmB;AAAe;AAAU;AAC1C;AACH;AACF;AAGN;AAMA;AAAiC;AAClB;AAAgB;AAAgB;AAAwB;AAAI;AAEtE;AAAgB;AAAkB;AAA0B;AAAI;AAGhE;AACM;AACI;AACgB;AAC3B;AAEW;AAAgB;AAAgB;AAAwB;AACvE;AAEA;AAIA;AACE;AACG;AAA4C;AAC1C;AAGP;AAEA;AACE;AACG;AAAuB;AAAsB;AAC3C;AAGP;AAWA;AAAgC;AAC9B;AACA;AACA;AAEF;AACE;AACA;AAEA;AACE;AAA8B;AAGhC;AACG;AAC0D;AACrD;AACJ;AACe;AACK;AACpB;AACA;AAGN;AAEA;AAA6B;AAC3B;AACA;AAEF;AACE;AAEA;AACE;AAAO;AAGT;AACG;AAC2D;AACtD;AAEH;AAAc;AAEX;AACG;AAEC;AACA;AACF;AAEH;AACH;AAGN;AAEA;AAA2D;AAChD;AACW;AAEtB;AAEA;AAAiC;AACb;AACO;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEF;AACE;AACA;AACA;AACA;AACE;AAAO;AACF;AAGC;AACN;AAGF;AAAuE;AAC/C;AAGxB;AACE;AAAqB;AAGvB;AACE;AAAqB;AAGvB;AACE;AAAsB;AAGxB;AACG;AAAc;AAAmC;AAChD;AAAC;AACW;AACD;AACM;AACf;AACA;AACA;AACA;AACO;AACT;AACqB;AAAoB;AAAsB;AAE5D;AAAc;AACb;AAAC;AAAc;AACZ;AACE;AACU;AACT;AACF;AAED;AACU;AACW;AACpB;AACF;AAEG;AACU;AACT;AACF;AAAA;AAEJ;AACiC;AAChC;AAAc;AAEX;AACG;AAA2B;AAAwB;AACjD;AAAgC;AAC9B;AACgB;AACN;AACC;AACF;AACM;AACE;AAClB;AACF;AACF;AACF;AAEJ;AAAA;AACF;AAGC;AAAc;AACZ;AAAc;AACb;AAAgB;AACb;AAAA;AACL;AACF;AAAA;AAIR;AAEa;AAQN;AAAiB;AAEpB;AACE;AACA;AACA;AACA;AACA;AACA;AACW;AACX;AACmB;AACnB;AACA;AACA;AACA;AACA;AACA;AACA;AACkB;AACO;AACzB;AACQ;AACL;AAIL;AAEA;AACA;AACE;AAAU;AACR;AACF;AAGF;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAyC;AAInC;AACJ;AACA;AAGF;AAA2B;AACzB;AAEI;AAAmB;AAAa;AAChC;AAA8C;AAChD;AACK;AACP;AAEE;AAAsC;AAC/B;AACiB;AAG5B;AACA;AAMA;AACE;AAAqB;AAGvB;AACE;AAA+B;AAGjC;AAAoB;AAEhB;AAEA;AACE;AAAA;AAGF;AACE;AAAyB;AAC3B;AACF;AACuC;AAGzC;AAAmB;AAEf;AAEA;AACE;AAAA;AAGF;AAAuC;AACL;AAGlC;AACE;AAAwB;AAC1B;AACF;AAC0B;AAG5B;AAA0B;AAEtB;AAEA;AACE;AAAyB;AAC3B;AACF;AAC8B;AAGhC;AAA6B;AAEzB;AAEA;AACE;AAAA;AAGF;AAEA;AACE;AAAY;AACV;AACA;AACc;AACO;AACtB;AAED;AAAc;AACZ;AACc;AACO;AACtB;AAED;AAAa;AACG;AACS;AACF;AACtB;AACH;AACF;AACA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACF;AAGF;AACG;AACE;AACmB;AACP;AACT;AACA;AACF;AACO;AACH;AACC;AAC8B;AAC1B;AACD;AACe;AACvB;AACyB;AACF;AACvB;AAEC;AACC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACe;AACU;AACV;AACf;AACA;AACF;AACF;AACF;AAGN;;;"}
1
+ {"version":3,"file":"Composer.cjs","sources":["../../src/components/Composer.tsx"],"sourcesContent":["\"use client\";\n\nimport type {\n BaseMetadata,\n CommentAttachment,\n CommentMixedAttachment,\n DM,\n} from \"@liveblocks/core\";\nimport { Permission } from \"@liveblocks/core\";\nimport { useRoom } from \"@liveblocks/react\";\nimport {\n useCreateRoomComment,\n useCreateRoomThread,\n useEditRoomComment,\n useLayoutEffect,\n useResolveMentionSuggestions,\n useRoomPermissions,\n} from \"@liveblocks/react/_private\";\nimport type {\n ComponentPropsWithoutRef,\n ComponentType,\n FocusEvent,\n FormEvent,\n ForwardedRef,\n MouseEvent,\n PropsWithChildren,\n ReactNode,\n RefAttributes,\n SyntheticEvent,\n} from \"react\";\nimport {\n createContext,\n forwardRef,\n useCallback,\n useMemo,\n useRef,\n useSyncExternalStore,\n} from \"react\";\n\nimport { useLiveblocksUIConfig } from \"../config\";\nimport { FLOATING_ELEMENT_SIDE_OFFSET, MENTION_CHARACTER } from \"../constants\";\nimport { AttachmentIcon } from \"../icons/Attachment\";\nimport { BoldIcon } from \"../icons/Bold\";\nimport { CodeIcon } from \"../icons/Code\";\nimport { EmojiIcon } from \"../icons/Emoji\";\nimport { ItalicIcon } from \"../icons/Italic\";\nimport { MentionIcon } from \"../icons/Mention\";\nimport { SendIcon } from \"../icons/Send\";\nimport { StrikethroughIcon } from \"../icons/Strikethrough\";\nimport type { ComposerOverrides, GlobalOverrides } from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport * as ComposerPrimitive from \"../primitives/Composer\";\nimport {\n useComposer,\n useComposerAttachmentsContext,\n useComposerEditorContext,\n} from \"../primitives/Composer/contexts\";\nimport type {\n ComposerEditorComponents,\n ComposerEditorLinkProps,\n ComposerEditorMentionProps,\n ComposerEditorMentionSuggestionsProps,\n ComposerEditorProps,\n ComposerFormProps,\n ComposerMarkToggleProps,\n ComposerSubmitComment,\n} from \"../primitives/Composer/types\";\nimport { useComposerAttachmentsDropArea } from \"../primitives/Composer/utils\";\nimport type { ComposerBodyMark } from \"../types\";\nimport { classNames } from \"../utils/class-names\";\nimport { useControllableState } from \"../utils/use-controllable-state\";\nimport { FileAttachment } from \"./internal/Attachment\";\nimport { Attribution } from \"./internal/Attribution\";\nimport { Avatar } from \"./internal/Avatar\";\nimport { Button } from \"./internal/Button\";\nimport type { EmojiPickerProps } from \"./internal/EmojiPicker\";\nimport { EmojiPicker, EmojiPickerTrigger } from \"./internal/EmojiPicker\";\nimport { ShortcutTooltip, Tooltip, TooltipProvider } from \"./internal/Tooltip\";\nimport { User } from \"./internal/User\";\n\ninterface EditorActionProps extends ComponentPropsWithoutRef<\"button\"> {\n label: string;\n tooltipLabel?: string;\n}\n\ninterface EmojiEditorActionProps extends EditorActionProps {\n onPickerOpenChange?: EmojiPickerProps[\"onOpenChange\"];\n}\n\ninterface MarkToggleProps extends ComposerMarkToggleProps {\n icon?: ReactNode;\n shortcut?: string;\n}\n\ntype ComposerCreateThreadProps<M extends BaseMetadata> = {\n threadId?: never;\n commentId?: never;\n\n /**\n * The metadata of the thread to create.\n */\n metadata?: M;\n};\n\ntype ComposerCreateCommentProps = {\n /**\n * The ID of the thread to reply to.\n */\n threadId: string;\n commentId?: never;\n metadata?: never;\n};\n\ntype ComposerEditCommentProps = {\n /**\n * The ID of the thread to edit a comment in.\n */\n threadId: string;\n\n /**\n * The ID of the comment to edit.\n */\n commentId: string;\n metadata?: never;\n};\n\nexport type ComposerProps<M extends BaseMetadata = DM> = Omit<\n ComponentPropsWithoutRef<\"form\">,\n \"defaultValue\"\n> &\n (\n | ComposerCreateThreadProps<M>\n | ComposerCreateCommentProps\n | ComposerEditCommentProps\n ) & {\n /**\n * The event handler called when the composer is submitted.\n */\n onComposerSubmit?: (\n comment: ComposerSubmitComment,\n event: FormEvent<HTMLFormElement>\n ) => Promise<void> | void;\n\n /**\n * The composer's initial value.\n */\n defaultValue?: ComposerEditorProps[\"defaultValue\"];\n\n /**\n * The composer's initial attachments.\n */\n defaultAttachments?: CommentAttachment[];\n\n /**\n * Whether the composer is collapsed. Setting a value will make the composer controlled.\n */\n collapsed?: boolean;\n\n /**\n * The event handler called when the collapsed state of the composer changes.\n */\n onCollapsedChange?: (collapsed: boolean) => void;\n\n /**\n * Whether the composer is initially collapsed. Setting a value will make the composer uncontrolled.\n */\n defaultCollapsed?: boolean;\n\n /**\n * Whether to show and allow adding attachments.\n */\n showAttachments?: boolean;\n\n /**\n * Whether to show formatting controls (e.g. a floating toolbar with formatting toggles when selecting text)\n */\n showFormattingControls?: boolean;\n\n /**\n * Whether the composer is disabled.\n */\n disabled?: ComposerFormProps[\"disabled\"];\n\n /**\n * Whether to focus the composer on mount.\n */\n autoFocus?: ComposerEditorProps[\"autoFocus\"];\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<GlobalOverrides & ComposerOverrides>;\n\n /**\n * @internal\n */\n actions?: ReactNode;\n\n /**\n * @internal\n */\n showAttribution?: boolean;\n\n /**\n * @internal\n */\n roomId?: string;\n };\n\ninterface ComposerEditorContainerProps\n extends Pick<\n ComposerProps,\n | \"defaultValue\"\n | \"showAttachments\"\n | \"showFormattingControls\"\n | \"showAttribution\"\n | \"overrides\"\n | \"actions\"\n | \"autoFocus\"\n | \"disabled\"\n > {\n isCollapsed: boolean | undefined;\n onEmptyChange: (isEmpty: boolean) => void;\n hasResolveMentionSuggestions: boolean;\n onEmojiPickerOpenChange: (isOpen: boolean) => void;\n onEditorClick: (event: MouseEvent<HTMLDivElement>) => void;\n}\n\nfunction ComposerInsertMentionEditorAction({\n label,\n tooltipLabel,\n className,\n onClick,\n ...props\n}: EditorActionProps) {\n const { createMention } = useComposer();\n\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n\n if (!event.isDefaultPrevented()) {\n event.stopPropagation();\n createMention();\n }\n },\n [createMention, onClick]\n );\n\n return (\n <Tooltip content={tooltipLabel ?? label}>\n <Button\n className={classNames(\"lb-composer-editor-action\", className)}\n onPointerDown={preventDefault}\n onClick={handleClick}\n aria-label={label}\n icon={<MentionIcon />}\n {...props}\n />\n </Tooltip>\n );\n}\n\nfunction ComposerInsertEmojiEditorAction({\n label,\n tooltipLabel,\n onPickerOpenChange,\n className,\n ...props\n}: EmojiEditorActionProps) {\n const { insertText } = useComposer();\n\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n return (\n <EmojiPicker onEmojiSelect={insertText} onOpenChange={onPickerOpenChange}>\n <Tooltip content={tooltipLabel ?? label}>\n <EmojiPickerTrigger asChild>\n <Button\n className={classNames(\"lb-composer-editor-action\", className)}\n onPointerDown={preventDefault}\n onClick={stopPropagation}\n aria-label={label}\n icon={<EmojiIcon />}\n {...props}\n />\n </EmojiPickerTrigger>\n </Tooltip>\n </EmojiPicker>\n );\n}\n\nfunction ComposerAttachFilesEditorAction({\n label,\n tooltipLabel,\n className,\n ...props\n}: EditorActionProps) {\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n return (\n <Tooltip content={tooltipLabel ?? label}>\n <ComposerPrimitive.AttachFiles asChild>\n <Button\n className={classNames(\"lb-composer-editor-action\", className)}\n onPointerDown={preventDefault}\n onClick={stopPropagation}\n aria-label={label}\n icon={<AttachmentIcon />}\n {...props}\n />\n </ComposerPrimitive.AttachFiles>\n </Tooltip>\n );\n}\n\nfunction ComposerMention({ userId }: ComposerEditorMentionProps) {\n return (\n <ComposerPrimitive.Mention className=\"lb-composer-mention\">\n {MENTION_CHARACTER}\n <User userId={userId} />\n </ComposerPrimitive.Mention>\n );\n}\n\nfunction ComposerMentionSuggestions({\n userIds,\n}: ComposerEditorMentionSuggestionsProps) {\n return userIds.length > 0 ? (\n <ComposerPrimitive.Suggestions className=\"lb-root lb-portal lb-elevation lb-composer-suggestions lb-composer-mention-suggestions\">\n <ComposerPrimitive.SuggestionsList className=\"lb-composer-suggestions-list lb-composer-mention-suggestions-list\">\n {userIds.map((userId) => (\n <ComposerPrimitive.SuggestionsListItem\n key={userId}\n className=\"lb-composer-suggestions-list-item lb-composer-mention-suggestion\"\n value={userId}\n >\n <Avatar\n userId={userId}\n className=\"lb-composer-mention-suggestion-avatar\"\n />\n <User\n userId={userId}\n className=\"lb-composer-mention-suggestion-user\"\n />\n </ComposerPrimitive.SuggestionsListItem>\n ))}\n </ComposerPrimitive.SuggestionsList>\n </ComposerPrimitive.Suggestions>\n ) : null;\n}\n\nfunction MarkToggle({\n mark,\n icon,\n shortcut,\n children,\n ...props\n}: MarkToggleProps) {\n const $ = useOverrides();\n const label = useMemo(() => {\n return $.COMPOSER_TOGGLE_MARK(mark);\n }, [$, mark]);\n\n return (\n <ShortcutTooltip\n content={label}\n shortcut={shortcut}\n sideOffset={FLOATING_ELEMENT_SIDE_OFFSET + 2}\n >\n <ComposerPrimitive.MarkToggle mark={mark} asChild {...props}>\n <Button aria-label={label} variant=\"toolbar\" icon={icon}>\n {children}\n </Button>\n </ComposerPrimitive.MarkToggle>\n </ShortcutTooltip>\n );\n}\n\ntype MarkToggles = {\n [K in ComposerBodyMark]: ComponentType<PropsWithChildren>;\n};\n\nconst markToggles: MarkToggles = {\n bold: () => <MarkToggle mark=\"bold\" shortcut=\"Mod-B\" icon={<BoldIcon />} />,\n italic: () => (\n <MarkToggle mark=\"italic\" shortcut=\"Mod-I\" icon={<ItalicIcon />} />\n ),\n strikethrough: () => (\n <MarkToggle\n mark=\"strikethrough\"\n shortcut=\"Mod-Shift-S\"\n icon={<StrikethroughIcon />}\n />\n ),\n code: () => <MarkToggle mark=\"code\" shortcut=\"Mod-E\" icon={<CodeIcon />} />,\n};\n\nconst markTogglesList = Object.entries(markToggles).map(([mark, Toggle]) => (\n <Toggle key={mark} />\n));\n\nfunction ComposerFloatingToolbar() {\n return (\n <ComposerPrimitive.FloatingToolbar className=\"lb-root lb-portal lb-elevation lb-composer-floating-toolbar\">\n {markTogglesList}\n </ComposerPrimitive.FloatingToolbar>\n );\n}\n\nfunction ComposerLink({ href, children }: ComposerEditorLinkProps) {\n return (\n <ComposerPrimitive.Link href={href} className=\"lb-composer-link\">\n {children}\n </ComposerPrimitive.Link>\n );\n}\n\ninterface ComposerAttachmentsProps extends ComponentPropsWithoutRef<\"div\"> {\n overrides?: Partial<GlobalOverrides & ComposerOverrides>;\n}\n\ninterface ComposerFileAttachmentProps extends ComponentPropsWithoutRef<\"div\"> {\n attachment: CommentMixedAttachment;\n overrides?: Partial<GlobalOverrides & ComposerOverrides>;\n}\n\nfunction ComposerFileAttachment({\n attachment,\n className,\n overrides,\n ...props\n}: ComposerFileAttachmentProps) {\n const { removeAttachment } = useComposer();\n const { roomId } = useComposerEditorContext();\n\n const handleDeleteClick = useCallback(() => {\n removeAttachment(attachment.id);\n }, [attachment.id, removeAttachment]);\n\n return (\n <FileAttachment\n className={classNames(\"lb-composer-attachment\", className)}\n {...props}\n attachment={attachment}\n onDeleteClick={handleDeleteClick}\n preventFocusOnDelete\n overrides={overrides}\n roomId={roomId}\n />\n );\n}\n\nfunction ComposerAttachments({\n overrides,\n className,\n ...props\n}: ComposerAttachmentsProps) {\n const { attachments } = useComposer();\n\n if (attachments.length === 0) {\n return null;\n }\n\n return (\n <div\n className={classNames(\"lb-composer-attachments\", className)}\n {...props}\n >\n <div className=\"lb-attachments\">\n {attachments.map((attachment) => {\n return (\n <ComposerFileAttachment\n key={attachment.id}\n attachment={attachment}\n overrides={overrides}\n />\n );\n })}\n </div>\n </div>\n );\n}\n\nconst editorRequiredComponents: ComposerEditorComponents = {\n Mention: ComposerMention,\n MentionSuggestions: ComposerMentionSuggestions,\n Link: ComposerLink,\n};\n\nfunction ComposerEditorContainer({\n showAttachments = true,\n showFormattingControls = true,\n showAttribution,\n defaultValue,\n isCollapsed,\n overrides,\n actions,\n autoFocus,\n disabled,\n hasResolveMentionSuggestions,\n onEmojiPickerOpenChange,\n onEmptyChange,\n onEditorClick,\n}: ComposerEditorContainerProps) {\n const { isEmpty } = useComposer();\n const { hasMaxAttachments } = useComposerAttachmentsContext();\n const $ = useOverrides(overrides);\n const components = useMemo(() => {\n return {\n ...editorRequiredComponents,\n FloatingToolbar: showFormattingControls\n ? ComposerFloatingToolbar\n : undefined,\n };\n }, [showFormattingControls]);\n\n const [isDraggingOver, dropAreaProps] = useComposerAttachmentsDropArea({\n disabled: disabled || hasMaxAttachments,\n });\n\n useLayoutEffect(() => {\n onEmptyChange(isEmpty);\n }, [isEmpty, onEmptyChange]);\n\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n return (\n <div className=\"lb-composer-editor-container\" {...dropAreaProps}>\n <ComposerPrimitive.Editor\n className=\"lb-composer-editor\"\n onClick={onEditorClick}\n placeholder={$.COMPOSER_PLACEHOLDER}\n defaultValue={defaultValue}\n autoFocus={autoFocus}\n components={components}\n disabled={disabled}\n dir={$.dir}\n />\n {showAttachments && <ComposerAttachments overrides={overrides} />}\n {(!isCollapsed || isDraggingOver) && (\n <div className=\"lb-composer-footer\">\n <div className=\"lb-composer-editor-actions\">\n {hasResolveMentionSuggestions && (\n <ComposerInsertMentionEditorAction\n label={$.COMPOSER_INSERT_MENTION}\n disabled={disabled}\n />\n )}\n <ComposerInsertEmojiEditorAction\n label={$.COMPOSER_INSERT_EMOJI}\n onPickerOpenChange={onEmojiPickerOpenChange}\n disabled={disabled}\n />\n {showAttachments && (\n <ComposerAttachFilesEditorAction\n label={$.COMPOSER_ATTACH_FILES}\n disabled={disabled}\n />\n )}\n </div>\n {showAttribution && <Attribution />}\n <div className=\"lb-composer-actions\">\n {actions ?? (\n <>\n <ShortcutTooltip content={$.COMPOSER_SEND} shortcut=\"Enter\">\n <ComposerPrimitive.Submit asChild>\n <Button\n onPointerDown={preventDefault}\n onClick={stopPropagation}\n className=\"lb-composer-action\"\n variant=\"primary\"\n aria-label={$.COMPOSER_SEND}\n icon={<SendIcon />}\n />\n </ComposerPrimitive.Submit>\n </ShortcutTooltip>\n </>\n )}\n </div>\n </div>\n )}\n {showAttachments && isDraggingOver && (\n <div className=\"lb-composer-attachments-drop-area\">\n <div className=\"lb-composer-attachments-drop-area-label\">\n <AttachmentIcon />\n {$.COMPOSER_ATTACH_FILES}\n </div>\n </div>\n )}\n </div>\n );\n}\n\nexport const ComposerRoomIdContext = createContext<string | null>(null);\n\n/**\n * Displays a composer to create comments.\n *\n * @example\n * <Composer />\n */\nexport const Composer = forwardRef(\n <M extends BaseMetadata = DM>(\n {\n threadId,\n commentId,\n metadata,\n defaultValue,\n defaultAttachments,\n onComposerSubmit,\n collapsed: controlledCollapsed,\n defaultCollapsed,\n onCollapsedChange: controlledOnCollapsedChange,\n overrides,\n actions,\n onBlur,\n className,\n onFocus,\n autoFocus,\n disabled,\n showAttachments = true,\n showFormattingControls = true,\n showAttribution,\n roomId: _roomId,\n ...props\n }: ComposerProps<M>,\n forwardedRef: ForwardedRef<HTMLFormElement>\n ) => {\n const room = useRoom({ allowOutsideRoom: true });\n\n const roomId = _roomId !== undefined ? _roomId : room?.id;\n if (roomId === undefined) {\n throw new Error(\n \"Composer must be a descendant of RoomProvider component\"\n );\n }\n\n const createThread = useCreateRoomThread(roomId);\n const createComment = useCreateRoomComment(roomId);\n const editComment = useEditRoomComment(roomId);\n const { preventUnsavedComposerChanges } = useLiveblocksUIConfig();\n const hasResolveMentionSuggestions =\n useResolveMentionSuggestions() !== undefined;\n const isEmptyRef = useRef(true);\n const isEmojiPickerOpenRef = useRef(false);\n const $ = useOverrides(overrides);\n const [isCollapsed, onCollapsedChange] = useControllableState(\n defaultCollapsed ?? false,\n controlledCollapsed,\n controlledOnCollapsedChange\n );\n\n const canCommentFallback = useSyncExternalStore(\n useCallback(\n (callback) => {\n if (room === null) return () => {};\n return room.events.self.subscribeOnce(callback);\n },\n [room]\n ),\n useCallback(() => {\n return room?.getSelf()?.canComment ?? true;\n }, [room]),\n useCallback(() => true, [])\n );\n\n const permissions = useRoomPermissions(roomId);\n const canComment =\n permissions.size > 0\n ? permissions.has(Permission.CommentsWrite) ||\n permissions.has(Permission.Write)\n : canCommentFallback;\n\n const setEmptyRef = useCallback((isEmpty: boolean) => {\n isEmptyRef.current = isEmpty;\n }, []);\n\n const setEmojiPickerOpenRef = useCallback((isEmojiPickerOpen: boolean) => {\n isEmojiPickerOpenRef.current = isEmojiPickerOpen;\n }, []);\n\n const handleFocus = useCallback(\n (event: FocusEvent<HTMLFormElement>) => {\n onFocus?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n if (isEmptyRef.current && canComment) {\n onCollapsedChange?.(false);\n }\n },\n [onCollapsedChange, onFocus, canComment]\n );\n\n const handleBlur = useCallback(\n (event: FocusEvent<HTMLFormElement>) => {\n onBlur?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n const isOutside = !event.currentTarget.contains(\n event.relatedTarget ?? document.activeElement\n );\n\n if (isOutside && isEmptyRef.current && !isEmojiPickerOpenRef.current) {\n onCollapsedChange?.(true);\n }\n },\n [onBlur, onCollapsedChange]\n );\n\n const handleEditorClick = useCallback(\n (event: MouseEvent<HTMLDivElement>) => {\n event.stopPropagation();\n\n if (isEmptyRef.current && canComment) {\n onCollapsedChange?.(false);\n }\n },\n [onCollapsedChange, canComment]\n );\n\n const handleComposerSubmit = useCallback(\n (comment: ComposerSubmitComment, event: FormEvent<HTMLFormElement>) => {\n onComposerSubmit?.(comment, event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n event.stopPropagation();\n\n if (commentId && threadId) {\n editComment({\n commentId,\n threadId,\n body: comment.body,\n attachments: comment.attachments,\n });\n } else if (threadId) {\n createComment({\n threadId,\n body: comment.body,\n attachments: comment.attachments,\n });\n } else {\n createThread({\n body: comment.body,\n metadata: metadata ?? {},\n attachments: comment.attachments,\n });\n }\n },\n [\n commentId,\n createComment,\n createThread,\n editComment,\n metadata,\n onComposerSubmit,\n threadId,\n ]\n );\n\n return (\n <TooltipProvider>\n <ComposerPrimitive.Form\n onComposerSubmit={handleComposerSubmit}\n className={classNames(\n \"lb-root lb-composer lb-composer-form\",\n className\n )}\n dir={$.dir}\n {...props}\n ref={forwardedRef}\n data-collapsed={isCollapsed ? \"\" : undefined}\n onFocus={handleFocus}\n onBlur={handleBlur}\n disabled={disabled || !canComment}\n defaultAttachments={defaultAttachments}\n pasteFilesAsAttachments={showAttachments}\n preventUnsavedChanges={preventUnsavedComposerChanges}\n roomId={roomId}\n >\n <ComposerEditorContainer\n defaultValue={defaultValue}\n actions={actions}\n overrides={overrides}\n isCollapsed={isCollapsed}\n showAttachments={showAttachments}\n showAttribution={showAttribution}\n showFormattingControls={showFormattingControls}\n hasResolveMentionSuggestions={hasResolveMentionSuggestions}\n onEmptyChange={setEmptyRef}\n onEmojiPickerOpenChange={setEmojiPickerOpenRef}\n onEditorClick={handleEditorClick}\n autoFocus={autoFocus}\n disabled={disabled}\n />\n </ComposerPrimitive.Form>\n </TooltipProvider>\n );\n }\n) as <M extends BaseMetadata = DM>(\n props: ComposerProps<M> & RefAttributes<HTMLFormElement>\n) => JSX.Element;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoOA;AAA2C;AACzC;AACA;AACA;AACA;AAEF;AACE;AAEA;AACE;AAAqB;AAGvB;AAAoB;AAEhB;AAEA;AACE;AACA;AAAc;AAChB;AACF;AACuB;AAGzB;AACG;AAAiC;AAC/B;AAC6D;AAC7C;AACN;AACG;AACO;AACf;AACN;AAGN;AAEA;AAAyC;AACvC;AACA;AACA;AACA;AAEF;AACE;AAEA;AACE;AAAqB;AAGvB;AACE;AAAsB;AAGxB;AACG;AAA2B;AAA0B;AACnD;AAAiC;AAC/B;AAA0B;AACxB;AAC6D;AAC7C;AACN;AACG;AACK;AACb;AACN;AACF;AACF;AAGN;AAEA;AAAyC;AACvC;AACA;AACA;AAEF;AACE;AACE;AAAqB;AAGvB;AACE;AAAsB;AAGxB;AACG;AAAiC;AAC/B;AAAqC;AACnC;AAC6D;AAC7C;AACN;AACG;AACU;AAClB;AACN;AACF;AAGN;AAEA;AACE;AACG;AAAoC;AAClC;AAAA;AACA;AAAK;AAAgB;AAAA;AAG5B;AAEA;AAAoC;AAEpC;AACE;AACG;AAAwC;AACtC;AAA4C;AAExC;AAEW;AACH;AAEP;AAAC;AACC;AACU;AACZ;AACC;AACC;AACU;AACZ;AAAA;AAEH;AACH;AAGN;AAEA;AAAoB;AAClB;AACA;AACA;AACA;AAEF;AACE;AACA;AACE;AAAkC;AAGpC;AACG;AACU;AACT;AAC2C;AAE1C;AAA6B;AAAmB;AAAK;AACnD;AAAmB;AAAe;AAAU;AAC1C;AACH;AACF;AAGN;AAMA;AAAiC;AAClB;AAAgB;AAAgB;AAAwB;AAAI;AAEtE;AAAgB;AAAkB;AAA0B;AAAI;AAGhE;AACM;AACI;AACgB;AAC3B;AAEW;AAAgB;AAAgB;AAAwB;AACvE;AAEA;AAIA;AACE;AACG;AAA4C;AAC1C;AAGP;AAEA;AACE;AACG;AAAuB;AAAsB;AAC3C;AAGP;AAWA;AAAgC;AAC9B;AACA;AACA;AAEF;AACE;AACA;AAEA;AACE;AAA8B;AAGhC;AACG;AAC0D;AACrD;AACJ;AACe;AACK;AACpB;AACA;AAGN;AAEA;AAA6B;AAC3B;AACA;AAEF;AACE;AAEA;AACE;AAAO;AAGT;AACG;AAC2D;AACtD;AAEH;AAAc;AAEX;AACG;AAEC;AACA;AACF;AAEH;AACH;AAGN;AAEA;AAA2D;AAChD;AACW;AAEtB;AAEA;AAAiC;AACb;AACO;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEF;AACE;AACA;AACA;AACA;AACE;AAAO;AACF;AAGC;AACN;AAGF;AAAuE;AAC/C;AAGxB;AACE;AAAqB;AAGvB;AACE;AAAqB;AAGvB;AACE;AAAsB;AAGxB;AACG;AAAc;AAAmC;AAChD;AAAC;AACW;AACD;AACM;AACf;AACA;AACA;AACA;AACO;AACT;AACqB;AAAoB;AAAsB;AAE5D;AAAc;AACb;AAAC;AAAc;AACZ;AACE;AACU;AACT;AACF;AAED;AACU;AACW;AACpB;AACF;AAEG;AACU;AACT;AACF;AAAA;AAEJ;AACiC;AAChC;AAAc;AAEX;AACG;AAA2B;AAAwB;AACjD;AAAgC;AAC9B;AACgB;AACN;AACC;AACF;AACM;AACE;AAClB;AACF;AACF;AACF;AAEJ;AAAA;AACF;AAGC;AAAc;AACZ;AAAc;AACb;AAAgB;AACb;AAAA;AACL;AACF;AAAA;AAIR;AAEa;AAQN;AAAiB;AAEpB;AACE;AACA;AACA;AACA;AACA;AACA;AACW;AACX;AACmB;AACnB;AACA;AACA;AACA;AACA;AACA;AACA;AACkB;AACO;AACzB;AACQ;AACL;AAIL;AAEA;AACA;AACE;AAAU;AACR;AACF;AAGF;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAyC;AACnB;AACpB;AACA;AAGF;AAA2B;AACzB;AAEI;AAAmB;AAAa;AAChC;AAA8C;AAChD;AACK;AACP;AAEE;AAAsC;AAC/B;AACiB;AAG5B;AACA;AAMA;AACE;AAAqB;AAGvB;AACE;AAA+B;AAGjC;AAAoB;AAEhB;AAEA;AACE;AAAA;AAGF;AACE;AAAyB;AAC3B;AACF;AACuC;AAGzC;AAAmB;AAEf;AAEA;AACE;AAAA;AAGF;AAAuC;AACL;AAGlC;AACE;AAAwB;AAC1B;AACF;AAC0B;AAG5B;AAA0B;AAEtB;AAEA;AACE;AAAyB;AAC3B;AACF;AAC8B;AAGhC;AAA6B;AAEzB;AAEA;AACE;AAAA;AAGF;AAEA;AACE;AAAY;AACV;AACA;AACc;AACO;AACtB;AAED;AAAc;AACZ;AACc;AACO;AACtB;AAED;AAAa;AACG;AACS;AACF;AACtB;AACH;AACF;AACA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACF;AAGF;AACG;AACE;AACmB;AACP;AACT;AACA;AACF;AACO;AACH;AACC;AAC8B;AAC1B;AACD;AACe;AACvB;AACyB;AACF;AACvB;AAEC;AACC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACe;AACU;AACV;AACf;AACA;AACF;AACF;AACF;AAGN;;;"}
@@ -423,9 +423,9 @@ const Composer = forwardRef(
423
423
  const isEmojiPickerOpenRef = useRef(false);
424
424
  const $ = useOverrides(overrides);
425
425
  const [isCollapsed, onCollapsedChange] = useControllableState(
426
- controlledCollapsed === void 0 && defaultCollapsed === void 0 ? false : controlledCollapsed,
427
- controlledOnCollapsedChange,
428
- defaultCollapsed
426
+ defaultCollapsed ?? false,
427
+ controlledCollapsed,
428
+ controlledOnCollapsedChange
429
429
  );
430
430
  const canCommentFallback = useSyncExternalStore(
431
431
  useCallback(
@@ -1 +1 @@
1
- {"version":3,"file":"Composer.js","sources":["../../src/components/Composer.tsx"],"sourcesContent":["\"use client\";\n\nimport type {\n BaseMetadata,\n CommentAttachment,\n CommentMixedAttachment,\n DM,\n} from \"@liveblocks/core\";\nimport { Permission } from \"@liveblocks/core\";\nimport { useRoom } from \"@liveblocks/react\";\nimport {\n useCreateRoomComment,\n useCreateRoomThread,\n useEditRoomComment,\n useLayoutEffect,\n useResolveMentionSuggestions,\n useRoomPermissions,\n} from \"@liveblocks/react/_private\";\nimport type {\n ComponentPropsWithoutRef,\n ComponentType,\n FocusEvent,\n FormEvent,\n ForwardedRef,\n MouseEvent,\n PropsWithChildren,\n ReactNode,\n RefAttributes,\n SyntheticEvent,\n} from \"react\";\nimport {\n createContext,\n forwardRef,\n useCallback,\n useMemo,\n useRef,\n useSyncExternalStore,\n} from \"react\";\n\nimport { useLiveblocksUIConfig } from \"../config\";\nimport { FLOATING_ELEMENT_SIDE_OFFSET, MENTION_CHARACTER } from \"../constants\";\nimport { AttachmentIcon } from \"../icons/Attachment\";\nimport { BoldIcon } from \"../icons/Bold\";\nimport { CodeIcon } from \"../icons/Code\";\nimport { EmojiIcon } from \"../icons/Emoji\";\nimport { ItalicIcon } from \"../icons/Italic\";\nimport { MentionIcon } from \"../icons/Mention\";\nimport { SendIcon } from \"../icons/Send\";\nimport { StrikethroughIcon } from \"../icons/Strikethrough\";\nimport type { ComposerOverrides, GlobalOverrides } from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport * as ComposerPrimitive from \"../primitives/Composer\";\nimport {\n useComposer,\n useComposerAttachmentsContext,\n useComposerEditorContext,\n} from \"../primitives/Composer/contexts\";\nimport type {\n ComposerEditorComponents,\n ComposerEditorLinkProps,\n ComposerEditorMentionProps,\n ComposerEditorMentionSuggestionsProps,\n ComposerEditorProps,\n ComposerFormProps,\n ComposerMarkToggleProps,\n ComposerSubmitComment,\n} from \"../primitives/Composer/types\";\nimport { useComposerAttachmentsDropArea } from \"../primitives/Composer/utils\";\nimport type { ComposerBodyMark } from \"../types\";\nimport { classNames } from \"../utils/class-names\";\nimport { useControllableState } from \"../utils/use-controllable-state\";\nimport { FileAttachment } from \"./internal/Attachment\";\nimport { Attribution } from \"./internal/Attribution\";\nimport { Avatar } from \"./internal/Avatar\";\nimport { Button } from \"./internal/Button\";\nimport type { EmojiPickerProps } from \"./internal/EmojiPicker\";\nimport { EmojiPicker, EmojiPickerTrigger } from \"./internal/EmojiPicker\";\nimport { ShortcutTooltip, Tooltip, TooltipProvider } from \"./internal/Tooltip\";\nimport { User } from \"./internal/User\";\n\ninterface EditorActionProps extends ComponentPropsWithoutRef<\"button\"> {\n label: string;\n tooltipLabel?: string;\n}\n\ninterface EmojiEditorActionProps extends EditorActionProps {\n onPickerOpenChange?: EmojiPickerProps[\"onOpenChange\"];\n}\n\ninterface MarkToggleProps extends ComposerMarkToggleProps {\n icon?: ReactNode;\n shortcut?: string;\n}\n\ntype ComposerCreateThreadProps<M extends BaseMetadata> = {\n threadId?: never;\n commentId?: never;\n\n /**\n * The metadata of the thread to create.\n */\n metadata?: M;\n};\n\ntype ComposerCreateCommentProps = {\n /**\n * The ID of the thread to reply to.\n */\n threadId: string;\n commentId?: never;\n metadata?: never;\n};\n\ntype ComposerEditCommentProps = {\n /**\n * The ID of the thread to edit a comment in.\n */\n threadId: string;\n\n /**\n * The ID of the comment to edit.\n */\n commentId: string;\n metadata?: never;\n};\n\nexport type ComposerProps<M extends BaseMetadata = DM> = Omit<\n ComponentPropsWithoutRef<\"form\">,\n \"defaultValue\"\n> &\n (\n | ComposerCreateThreadProps<M>\n | ComposerCreateCommentProps\n | ComposerEditCommentProps\n ) & {\n /**\n * The event handler called when the composer is submitted.\n */\n onComposerSubmit?: (\n comment: ComposerSubmitComment,\n event: FormEvent<HTMLFormElement>\n ) => Promise<void> | void;\n\n /**\n * The composer's initial value.\n */\n defaultValue?: ComposerEditorProps[\"defaultValue\"];\n\n /**\n * The composer's initial attachments.\n */\n defaultAttachments?: CommentAttachment[];\n\n /**\n * Whether the composer is collapsed. Setting a value will make the composer controlled.\n */\n collapsed?: boolean;\n\n /**\n * The event handler called when the collapsed state of the composer changes.\n */\n onCollapsedChange?: (collapsed: boolean) => void;\n\n /**\n * Whether the composer is initially collapsed. Setting a value will make the composer uncontrolled.\n */\n defaultCollapsed?: boolean;\n\n /**\n * Whether to show and allow adding attachments.\n */\n showAttachments?: boolean;\n\n /**\n * Whether to show formatting controls (e.g. a floating toolbar with formatting toggles when selecting text)\n */\n showFormattingControls?: boolean;\n\n /**\n * Whether the composer is disabled.\n */\n disabled?: ComposerFormProps[\"disabled\"];\n\n /**\n * Whether to focus the composer on mount.\n */\n autoFocus?: ComposerEditorProps[\"autoFocus\"];\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<GlobalOverrides & ComposerOverrides>;\n\n /**\n * @internal\n */\n actions?: ReactNode;\n\n /**\n * @internal\n */\n showAttribution?: boolean;\n\n /**\n * @internal\n */\n roomId?: string;\n };\n\ninterface ComposerEditorContainerProps\n extends Pick<\n ComposerProps,\n | \"defaultValue\"\n | \"showAttachments\"\n | \"showFormattingControls\"\n | \"showAttribution\"\n | \"overrides\"\n | \"actions\"\n | \"autoFocus\"\n | \"disabled\"\n > {\n isCollapsed: boolean | undefined;\n onEmptyChange: (isEmpty: boolean) => void;\n hasResolveMentionSuggestions: boolean;\n onEmojiPickerOpenChange: (isOpen: boolean) => void;\n onEditorClick: (event: MouseEvent<HTMLDivElement>) => void;\n}\n\nfunction ComposerInsertMentionEditorAction({\n label,\n tooltipLabel,\n className,\n onClick,\n ...props\n}: EditorActionProps) {\n const { createMention } = useComposer();\n\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n\n if (!event.isDefaultPrevented()) {\n event.stopPropagation();\n createMention();\n }\n },\n [createMention, onClick]\n );\n\n return (\n <Tooltip content={tooltipLabel ?? label}>\n <Button\n className={classNames(\"lb-composer-editor-action\", className)}\n onPointerDown={preventDefault}\n onClick={handleClick}\n aria-label={label}\n icon={<MentionIcon />}\n {...props}\n />\n </Tooltip>\n );\n}\n\nfunction ComposerInsertEmojiEditorAction({\n label,\n tooltipLabel,\n onPickerOpenChange,\n className,\n ...props\n}: EmojiEditorActionProps) {\n const { insertText } = useComposer();\n\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n return (\n <EmojiPicker onEmojiSelect={insertText} onOpenChange={onPickerOpenChange}>\n <Tooltip content={tooltipLabel ?? label}>\n <EmojiPickerTrigger asChild>\n <Button\n className={classNames(\"lb-composer-editor-action\", className)}\n onPointerDown={preventDefault}\n onClick={stopPropagation}\n aria-label={label}\n icon={<EmojiIcon />}\n {...props}\n />\n </EmojiPickerTrigger>\n </Tooltip>\n </EmojiPicker>\n );\n}\n\nfunction ComposerAttachFilesEditorAction({\n label,\n tooltipLabel,\n className,\n ...props\n}: EditorActionProps) {\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n return (\n <Tooltip content={tooltipLabel ?? label}>\n <ComposerPrimitive.AttachFiles asChild>\n <Button\n className={classNames(\"lb-composer-editor-action\", className)}\n onPointerDown={preventDefault}\n onClick={stopPropagation}\n aria-label={label}\n icon={<AttachmentIcon />}\n {...props}\n />\n </ComposerPrimitive.AttachFiles>\n </Tooltip>\n );\n}\n\nfunction ComposerMention({ userId }: ComposerEditorMentionProps) {\n return (\n <ComposerPrimitive.Mention className=\"lb-composer-mention\">\n {MENTION_CHARACTER}\n <User userId={userId} />\n </ComposerPrimitive.Mention>\n );\n}\n\nfunction ComposerMentionSuggestions({\n userIds,\n}: ComposerEditorMentionSuggestionsProps) {\n return userIds.length > 0 ? (\n <ComposerPrimitive.Suggestions className=\"lb-root lb-portal lb-elevation lb-composer-suggestions lb-composer-mention-suggestions\">\n <ComposerPrimitive.SuggestionsList className=\"lb-composer-suggestions-list lb-composer-mention-suggestions-list\">\n {userIds.map((userId) => (\n <ComposerPrimitive.SuggestionsListItem\n key={userId}\n className=\"lb-composer-suggestions-list-item lb-composer-mention-suggestion\"\n value={userId}\n >\n <Avatar\n userId={userId}\n className=\"lb-composer-mention-suggestion-avatar\"\n />\n <User\n userId={userId}\n className=\"lb-composer-mention-suggestion-user\"\n />\n </ComposerPrimitive.SuggestionsListItem>\n ))}\n </ComposerPrimitive.SuggestionsList>\n </ComposerPrimitive.Suggestions>\n ) : null;\n}\n\nfunction MarkToggle({\n mark,\n icon,\n shortcut,\n children,\n ...props\n}: MarkToggleProps) {\n const $ = useOverrides();\n const label = useMemo(() => {\n return $.COMPOSER_TOGGLE_MARK(mark);\n }, [$, mark]);\n\n return (\n <ShortcutTooltip\n content={label}\n shortcut={shortcut}\n sideOffset={FLOATING_ELEMENT_SIDE_OFFSET + 2}\n >\n <ComposerPrimitive.MarkToggle mark={mark} asChild {...props}>\n <Button aria-label={label} variant=\"toolbar\" icon={icon}>\n {children}\n </Button>\n </ComposerPrimitive.MarkToggle>\n </ShortcutTooltip>\n );\n}\n\ntype MarkToggles = {\n [K in ComposerBodyMark]: ComponentType<PropsWithChildren>;\n};\n\nconst markToggles: MarkToggles = {\n bold: () => <MarkToggle mark=\"bold\" shortcut=\"Mod-B\" icon={<BoldIcon />} />,\n italic: () => (\n <MarkToggle mark=\"italic\" shortcut=\"Mod-I\" icon={<ItalicIcon />} />\n ),\n strikethrough: () => (\n <MarkToggle\n mark=\"strikethrough\"\n shortcut=\"Mod-Shift-S\"\n icon={<StrikethroughIcon />}\n />\n ),\n code: () => <MarkToggle mark=\"code\" shortcut=\"Mod-E\" icon={<CodeIcon />} />,\n};\n\nconst markTogglesList = Object.entries(markToggles).map(([mark, Toggle]) => (\n <Toggle key={mark} />\n));\n\nfunction ComposerFloatingToolbar() {\n return (\n <ComposerPrimitive.FloatingToolbar className=\"lb-root lb-portal lb-elevation lb-composer-floating-toolbar\">\n {markTogglesList}\n </ComposerPrimitive.FloatingToolbar>\n );\n}\n\nfunction ComposerLink({ href, children }: ComposerEditorLinkProps) {\n return (\n <ComposerPrimitive.Link href={href} className=\"lb-composer-link\">\n {children}\n </ComposerPrimitive.Link>\n );\n}\n\ninterface ComposerAttachmentsProps extends ComponentPropsWithoutRef<\"div\"> {\n overrides?: Partial<GlobalOverrides & ComposerOverrides>;\n}\n\ninterface ComposerFileAttachmentProps extends ComponentPropsWithoutRef<\"div\"> {\n attachment: CommentMixedAttachment;\n overrides?: Partial<GlobalOverrides & ComposerOverrides>;\n}\n\nfunction ComposerFileAttachment({\n attachment,\n className,\n overrides,\n ...props\n}: ComposerFileAttachmentProps) {\n const { removeAttachment } = useComposer();\n const { roomId } = useComposerEditorContext();\n\n const handleDeleteClick = useCallback(() => {\n removeAttachment(attachment.id);\n }, [attachment.id, removeAttachment]);\n\n return (\n <FileAttachment\n className={classNames(\"lb-composer-attachment\", className)}\n {...props}\n attachment={attachment}\n onDeleteClick={handleDeleteClick}\n preventFocusOnDelete\n overrides={overrides}\n roomId={roomId}\n />\n );\n}\n\nfunction ComposerAttachments({\n overrides,\n className,\n ...props\n}: ComposerAttachmentsProps) {\n const { attachments } = useComposer();\n\n if (attachments.length === 0) {\n return null;\n }\n\n return (\n <div\n className={classNames(\"lb-composer-attachments\", className)}\n {...props}\n >\n <div className=\"lb-attachments\">\n {attachments.map((attachment) => {\n return (\n <ComposerFileAttachment\n key={attachment.id}\n attachment={attachment}\n overrides={overrides}\n />\n );\n })}\n </div>\n </div>\n );\n}\n\nconst editorRequiredComponents: ComposerEditorComponents = {\n Mention: ComposerMention,\n MentionSuggestions: ComposerMentionSuggestions,\n Link: ComposerLink,\n};\n\nfunction ComposerEditorContainer({\n showAttachments = true,\n showFormattingControls = true,\n showAttribution,\n defaultValue,\n isCollapsed,\n overrides,\n actions,\n autoFocus,\n disabled,\n hasResolveMentionSuggestions,\n onEmojiPickerOpenChange,\n onEmptyChange,\n onEditorClick,\n}: ComposerEditorContainerProps) {\n const { isEmpty } = useComposer();\n const { hasMaxAttachments } = useComposerAttachmentsContext();\n const $ = useOverrides(overrides);\n const components = useMemo(() => {\n return {\n ...editorRequiredComponents,\n FloatingToolbar: showFormattingControls\n ? ComposerFloatingToolbar\n : undefined,\n };\n }, [showFormattingControls]);\n\n const [isDraggingOver, dropAreaProps] = useComposerAttachmentsDropArea({\n disabled: disabled || hasMaxAttachments,\n });\n\n useLayoutEffect(() => {\n onEmptyChange(isEmpty);\n }, [isEmpty, onEmptyChange]);\n\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n return (\n <div className=\"lb-composer-editor-container\" {...dropAreaProps}>\n <ComposerPrimitive.Editor\n className=\"lb-composer-editor\"\n onClick={onEditorClick}\n placeholder={$.COMPOSER_PLACEHOLDER}\n defaultValue={defaultValue}\n autoFocus={autoFocus}\n components={components}\n disabled={disabled}\n dir={$.dir}\n />\n {showAttachments && <ComposerAttachments overrides={overrides} />}\n {(!isCollapsed || isDraggingOver) && (\n <div className=\"lb-composer-footer\">\n <div className=\"lb-composer-editor-actions\">\n {hasResolveMentionSuggestions && (\n <ComposerInsertMentionEditorAction\n label={$.COMPOSER_INSERT_MENTION}\n disabled={disabled}\n />\n )}\n <ComposerInsertEmojiEditorAction\n label={$.COMPOSER_INSERT_EMOJI}\n onPickerOpenChange={onEmojiPickerOpenChange}\n disabled={disabled}\n />\n {showAttachments && (\n <ComposerAttachFilesEditorAction\n label={$.COMPOSER_ATTACH_FILES}\n disabled={disabled}\n />\n )}\n </div>\n {showAttribution && <Attribution />}\n <div className=\"lb-composer-actions\">\n {actions ?? (\n <>\n <ShortcutTooltip content={$.COMPOSER_SEND} shortcut=\"Enter\">\n <ComposerPrimitive.Submit asChild>\n <Button\n onPointerDown={preventDefault}\n onClick={stopPropagation}\n className=\"lb-composer-action\"\n variant=\"primary\"\n aria-label={$.COMPOSER_SEND}\n icon={<SendIcon />}\n />\n </ComposerPrimitive.Submit>\n </ShortcutTooltip>\n </>\n )}\n </div>\n </div>\n )}\n {showAttachments && isDraggingOver && (\n <div className=\"lb-composer-attachments-drop-area\">\n <div className=\"lb-composer-attachments-drop-area-label\">\n <AttachmentIcon />\n {$.COMPOSER_ATTACH_FILES}\n </div>\n </div>\n )}\n </div>\n );\n}\n\nexport const ComposerRoomIdContext = createContext<string | null>(null);\n\n/**\n * Displays a composer to create comments.\n *\n * @example\n * <Composer />\n */\nexport const Composer = forwardRef(\n <M extends BaseMetadata = DM>(\n {\n threadId,\n commentId,\n metadata,\n defaultValue,\n defaultAttachments,\n onComposerSubmit,\n collapsed: controlledCollapsed,\n defaultCollapsed,\n onCollapsedChange: controlledOnCollapsedChange,\n overrides,\n actions,\n onBlur,\n className,\n onFocus,\n autoFocus,\n disabled,\n showAttachments = true,\n showFormattingControls = true,\n showAttribution,\n roomId: _roomId,\n ...props\n }: ComposerProps<M>,\n forwardedRef: ForwardedRef<HTMLFormElement>\n ) => {\n const room = useRoom({ allowOutsideRoom: true });\n\n const roomId = _roomId !== undefined ? _roomId : room?.id;\n if (roomId === undefined) {\n throw new Error(\n \"Composer must be a descendant of RoomProvider component\"\n );\n }\n\n const createThread = useCreateRoomThread(roomId);\n const createComment = useCreateRoomComment(roomId);\n const editComment = useEditRoomComment(roomId);\n const { preventUnsavedComposerChanges } = useLiveblocksUIConfig();\n const hasResolveMentionSuggestions =\n useResolveMentionSuggestions() !== undefined;\n const isEmptyRef = useRef(true);\n const isEmojiPickerOpenRef = useRef(false);\n const $ = useOverrides(overrides);\n const [isCollapsed, onCollapsedChange] = useControllableState(\n // If the composer is neither controlled nor uncontrolled, it defaults to controlled as uncollapsed.\n controlledCollapsed === undefined && defaultCollapsed === undefined\n ? false\n : controlledCollapsed,\n controlledOnCollapsedChange,\n defaultCollapsed\n );\n\n const canCommentFallback = useSyncExternalStore(\n useCallback(\n (callback) => {\n if (room === null) return () => {};\n return room.events.self.subscribeOnce(callback);\n },\n [room]\n ),\n useCallback(() => {\n return room?.getSelf()?.canComment ?? true;\n }, [room]),\n useCallback(() => true, [])\n );\n\n const permissions = useRoomPermissions(roomId);\n const canComment =\n permissions.size > 0\n ? permissions.has(Permission.CommentsWrite) ||\n permissions.has(Permission.Write)\n : canCommentFallback;\n\n const setEmptyRef = useCallback((isEmpty: boolean) => {\n isEmptyRef.current = isEmpty;\n }, []);\n\n const setEmojiPickerOpenRef = useCallback((isEmojiPickerOpen: boolean) => {\n isEmojiPickerOpenRef.current = isEmojiPickerOpen;\n }, []);\n\n const handleFocus = useCallback(\n (event: FocusEvent<HTMLFormElement>) => {\n onFocus?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n if (isEmptyRef.current && canComment) {\n onCollapsedChange?.(false);\n }\n },\n [onCollapsedChange, onFocus, canComment]\n );\n\n const handleBlur = useCallback(\n (event: FocusEvent<HTMLFormElement>) => {\n onBlur?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n const isOutside = !event.currentTarget.contains(\n event.relatedTarget ?? document.activeElement\n );\n\n if (isOutside && isEmptyRef.current && !isEmojiPickerOpenRef.current) {\n onCollapsedChange?.(true);\n }\n },\n [onBlur, onCollapsedChange]\n );\n\n const handleEditorClick = useCallback(\n (event: MouseEvent<HTMLDivElement>) => {\n event.stopPropagation();\n\n if (isEmptyRef.current && canComment) {\n onCollapsedChange?.(false);\n }\n },\n [onCollapsedChange, canComment]\n );\n\n const handleComposerSubmit = useCallback(\n (comment: ComposerSubmitComment, event: FormEvent<HTMLFormElement>) => {\n onComposerSubmit?.(comment, event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n event.stopPropagation();\n\n if (commentId && threadId) {\n editComment({\n commentId,\n threadId,\n body: comment.body,\n attachments: comment.attachments,\n });\n } else if (threadId) {\n createComment({\n threadId,\n body: comment.body,\n attachments: comment.attachments,\n });\n } else {\n createThread({\n body: comment.body,\n metadata: metadata ?? {},\n attachments: comment.attachments,\n });\n }\n },\n [\n commentId,\n createComment,\n createThread,\n editComment,\n metadata,\n onComposerSubmit,\n threadId,\n ]\n );\n\n return (\n <TooltipProvider>\n <ComposerPrimitive.Form\n onComposerSubmit={handleComposerSubmit}\n className={classNames(\n \"lb-root lb-composer lb-composer-form\",\n className\n )}\n dir={$.dir}\n {...props}\n ref={forwardedRef}\n data-collapsed={isCollapsed ? \"\" : undefined}\n onFocus={handleFocus}\n onBlur={handleBlur}\n disabled={disabled || !canComment}\n defaultAttachments={defaultAttachments}\n pasteFilesAsAttachments={showAttachments}\n preventUnsavedChanges={preventUnsavedComposerChanges}\n roomId={roomId}\n >\n <ComposerEditorContainer\n defaultValue={defaultValue}\n actions={actions}\n overrides={overrides}\n isCollapsed={isCollapsed}\n showAttachments={showAttachments}\n showAttribution={showAttribution}\n showFormattingControls={showFormattingControls}\n hasResolveMentionSuggestions={hasResolveMentionSuggestions}\n onEmptyChange={setEmptyRef}\n onEmojiPickerOpenChange={setEmojiPickerOpenRef}\n onEditorClick={handleEditorClick}\n autoFocus={autoFocus}\n disabled={disabled}\n />\n </ComposerPrimitive.Form>\n </TooltipProvider>\n );\n }\n) as <M extends BaseMetadata = DM>(\n props: ComposerProps<M> & RefAttributes<HTMLFormElement>\n) => JSX.Element;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoOA;AAA2C;AACzC;AACA;AACA;AACA;AAEF;AACE;AAEA;AACE;AAAqB;AAGvB;AAAoB;AAEhB;AAEA;AACE;AACA;AAAc;AAChB;AACF;AACuB;AAGzB;AACG;AAAiC;AAC/B;AAC6D;AAC7C;AACN;AACG;AACO;AACf;AACN;AAGN;AAEA;AAAyC;AACvC;AACA;AACA;AACA;AAEF;AACE;AAEA;AACE;AAAqB;AAGvB;AACE;AAAsB;AAGxB;AACG;AAA2B;AAA0B;AACnD;AAAiC;AAC/B;AAA0B;AACxB;AAC6D;AAC7C;AACN;AACG;AACK;AACb;AACN;AACF;AACF;AAGN;AAEA;AAAyC;AACvC;AACA;AACA;AAEF;AACE;AACE;AAAqB;AAGvB;AACE;AAAsB;AAGxB;AACG;AAAiC;AAC/B;AAAqC;AACnC;AAC6D;AAC7C;AACN;AACG;AACU;AAClB;AACN;AACF;AAGN;AAEA;AACE;AACG;AAAoC;AAClC;AAAA;AACA;AAAK;AAAgB;AAAA;AAG5B;AAEA;AAAoC;AAEpC;AACE;AACG;AAAwC;AACtC;AAA4C;AAExC;AAEW;AACH;AAEP;AAAC;AACC;AACU;AACZ;AACC;AACC;AACU;AACZ;AAAA;AAEH;AACH;AAGN;AAEA;AAAoB;AAClB;AACA;AACA;AACA;AAEF;AACE;AACA;AACE;AAAkC;AAGpC;AACG;AACU;AACT;AAC2C;AAE1C;AAA6B;AAAmB;AAAK;AACnD;AAAmB;AAAe;AAAU;AAC1C;AACH;AACF;AAGN;AAMA;AAAiC;AAClB;AAAgB;AAAgB;AAAwB;AAAI;AAEtE;AAAgB;AAAkB;AAA0B;AAAI;AAGhE;AACM;AACI;AACgB;AAC3B;AAEW;AAAgB;AAAgB;AAAwB;AACvE;AAEA;AAIA;AACE;AACG;AAA4C;AAC1C;AAGP;AAEA;AACE;AACG;AAAuB;AAAsB;AAC3C;AAGP;AAWA;AAAgC;AAC9B;AACA;AACA;AAEF;AACE;AACA;AAEA;AACE;AAA8B;AAGhC;AACG;AAC0D;AACrD;AACJ;AACe;AACK;AACpB;AACA;AAGN;AAEA;AAA6B;AAC3B;AACA;AAEF;AACE;AAEA;AACE;AAAO;AAGT;AACG;AAC2D;AACtD;AAEH;AAAc;AAEX;AACG;AAEC;AACA;AACF;AAEH;AACH;AAGN;AAEA;AAA2D;AAChD;AACW;AAEtB;AAEA;AAAiC;AACb;AACO;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEF;AACE;AACA;AACA;AACA;AACE;AAAO;AACF;AAGC;AACN;AAGF;AAAuE;AAC/C;AAGxB;AACE;AAAqB;AAGvB;AACE;AAAqB;AAGvB;AACE;AAAsB;AAGxB;AACG;AAAc;AAAmC;AAChD;AAAC;AACW;AACD;AACM;AACf;AACA;AACA;AACA;AACO;AACT;AACqB;AAAoB;AAAsB;AAE5D;AAAc;AACb;AAAC;AAAc;AACZ;AACE;AACU;AACT;AACF;AAED;AACU;AACW;AACpB;AACF;AAEG;AACU;AACT;AACF;AAAA;AAEJ;AACiC;AAChC;AAAc;AAEX;AACG;AAA2B;AAAwB;AACjD;AAAgC;AAC9B;AACgB;AACN;AACC;AACF;AACM;AACE;AAClB;AACF;AACF;AACF;AAEJ;AAAA;AACF;AAGC;AAAc;AACZ;AAAc;AACb;AAAgB;AACb;AAAA;AACL;AACF;AAAA;AAIR;AAEa;AAQN;AAAiB;AAEpB;AACE;AACA;AACA;AACA;AACA;AACA;AACW;AACX;AACmB;AACnB;AACA;AACA;AACA;AACA;AACA;AACA;AACkB;AACO;AACzB;AACQ;AACL;AAIL;AAEA;AACA;AACE;AAAU;AACR;AACF;AAGF;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAyC;AAInC;AACJ;AACA;AAGF;AAA2B;AACzB;AAEI;AAAmB;AAAa;AAChC;AAA8C;AAChD;AACK;AACP;AAEE;AAAsC;AAC/B;AACiB;AAG5B;AACA;AAMA;AACE;AAAqB;AAGvB;AACE;AAA+B;AAGjC;AAAoB;AAEhB;AAEA;AACE;AAAA;AAGF;AACE;AAAyB;AAC3B;AACF;AACuC;AAGzC;AAAmB;AAEf;AAEA;AACE;AAAA;AAGF;AAAuC;AACL;AAGlC;AACE;AAAwB;AAC1B;AACF;AAC0B;AAG5B;AAA0B;AAEtB;AAEA;AACE;AAAyB;AAC3B;AACF;AAC8B;AAGhC;AAA6B;AAEzB;AAEA;AACE;AAAA;AAGF;AAEA;AACE;AAAY;AACV;AACA;AACc;AACO;AACtB;AAED;AAAc;AACZ;AACc;AACO;AACtB;AAED;AAAa;AACG;AACS;AACF;AACtB;AACH;AACF;AACA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACF;AAGF;AACG;AACE;AACmB;AACP;AACT;AACA;AACF;AACO;AACH;AACC;AAC8B;AAC1B;AACD;AACe;AACvB;AACyB;AACF;AACvB;AAEC;AACC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACe;AACU;AACV;AACf;AACA;AACF;AACF;AACF;AAGN;;"}
1
+ {"version":3,"file":"Composer.js","sources":["../../src/components/Composer.tsx"],"sourcesContent":["\"use client\";\n\nimport type {\n BaseMetadata,\n CommentAttachment,\n CommentMixedAttachment,\n DM,\n} from \"@liveblocks/core\";\nimport { Permission } from \"@liveblocks/core\";\nimport { useRoom } from \"@liveblocks/react\";\nimport {\n useCreateRoomComment,\n useCreateRoomThread,\n useEditRoomComment,\n useLayoutEffect,\n useResolveMentionSuggestions,\n useRoomPermissions,\n} from \"@liveblocks/react/_private\";\nimport type {\n ComponentPropsWithoutRef,\n ComponentType,\n FocusEvent,\n FormEvent,\n ForwardedRef,\n MouseEvent,\n PropsWithChildren,\n ReactNode,\n RefAttributes,\n SyntheticEvent,\n} from \"react\";\nimport {\n createContext,\n forwardRef,\n useCallback,\n useMemo,\n useRef,\n useSyncExternalStore,\n} from \"react\";\n\nimport { useLiveblocksUIConfig } from \"../config\";\nimport { FLOATING_ELEMENT_SIDE_OFFSET, MENTION_CHARACTER } from \"../constants\";\nimport { AttachmentIcon } from \"../icons/Attachment\";\nimport { BoldIcon } from \"../icons/Bold\";\nimport { CodeIcon } from \"../icons/Code\";\nimport { EmojiIcon } from \"../icons/Emoji\";\nimport { ItalicIcon } from \"../icons/Italic\";\nimport { MentionIcon } from \"../icons/Mention\";\nimport { SendIcon } from \"../icons/Send\";\nimport { StrikethroughIcon } from \"../icons/Strikethrough\";\nimport type { ComposerOverrides, GlobalOverrides } from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport * as ComposerPrimitive from \"../primitives/Composer\";\nimport {\n useComposer,\n useComposerAttachmentsContext,\n useComposerEditorContext,\n} from \"../primitives/Composer/contexts\";\nimport type {\n ComposerEditorComponents,\n ComposerEditorLinkProps,\n ComposerEditorMentionProps,\n ComposerEditorMentionSuggestionsProps,\n ComposerEditorProps,\n ComposerFormProps,\n ComposerMarkToggleProps,\n ComposerSubmitComment,\n} from \"../primitives/Composer/types\";\nimport { useComposerAttachmentsDropArea } from \"../primitives/Composer/utils\";\nimport type { ComposerBodyMark } from \"../types\";\nimport { classNames } from \"../utils/class-names\";\nimport { useControllableState } from \"../utils/use-controllable-state\";\nimport { FileAttachment } from \"./internal/Attachment\";\nimport { Attribution } from \"./internal/Attribution\";\nimport { Avatar } from \"./internal/Avatar\";\nimport { Button } from \"./internal/Button\";\nimport type { EmojiPickerProps } from \"./internal/EmojiPicker\";\nimport { EmojiPicker, EmojiPickerTrigger } from \"./internal/EmojiPicker\";\nimport { ShortcutTooltip, Tooltip, TooltipProvider } from \"./internal/Tooltip\";\nimport { User } from \"./internal/User\";\n\ninterface EditorActionProps extends ComponentPropsWithoutRef<\"button\"> {\n label: string;\n tooltipLabel?: string;\n}\n\ninterface EmojiEditorActionProps extends EditorActionProps {\n onPickerOpenChange?: EmojiPickerProps[\"onOpenChange\"];\n}\n\ninterface MarkToggleProps extends ComposerMarkToggleProps {\n icon?: ReactNode;\n shortcut?: string;\n}\n\ntype ComposerCreateThreadProps<M extends BaseMetadata> = {\n threadId?: never;\n commentId?: never;\n\n /**\n * The metadata of the thread to create.\n */\n metadata?: M;\n};\n\ntype ComposerCreateCommentProps = {\n /**\n * The ID of the thread to reply to.\n */\n threadId: string;\n commentId?: never;\n metadata?: never;\n};\n\ntype ComposerEditCommentProps = {\n /**\n * The ID of the thread to edit a comment in.\n */\n threadId: string;\n\n /**\n * The ID of the comment to edit.\n */\n commentId: string;\n metadata?: never;\n};\n\nexport type ComposerProps<M extends BaseMetadata = DM> = Omit<\n ComponentPropsWithoutRef<\"form\">,\n \"defaultValue\"\n> &\n (\n | ComposerCreateThreadProps<M>\n | ComposerCreateCommentProps\n | ComposerEditCommentProps\n ) & {\n /**\n * The event handler called when the composer is submitted.\n */\n onComposerSubmit?: (\n comment: ComposerSubmitComment,\n event: FormEvent<HTMLFormElement>\n ) => Promise<void> | void;\n\n /**\n * The composer's initial value.\n */\n defaultValue?: ComposerEditorProps[\"defaultValue\"];\n\n /**\n * The composer's initial attachments.\n */\n defaultAttachments?: CommentAttachment[];\n\n /**\n * Whether the composer is collapsed. Setting a value will make the composer controlled.\n */\n collapsed?: boolean;\n\n /**\n * The event handler called when the collapsed state of the composer changes.\n */\n onCollapsedChange?: (collapsed: boolean) => void;\n\n /**\n * Whether the composer is initially collapsed. Setting a value will make the composer uncontrolled.\n */\n defaultCollapsed?: boolean;\n\n /**\n * Whether to show and allow adding attachments.\n */\n showAttachments?: boolean;\n\n /**\n * Whether to show formatting controls (e.g. a floating toolbar with formatting toggles when selecting text)\n */\n showFormattingControls?: boolean;\n\n /**\n * Whether the composer is disabled.\n */\n disabled?: ComposerFormProps[\"disabled\"];\n\n /**\n * Whether to focus the composer on mount.\n */\n autoFocus?: ComposerEditorProps[\"autoFocus\"];\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<GlobalOverrides & ComposerOverrides>;\n\n /**\n * @internal\n */\n actions?: ReactNode;\n\n /**\n * @internal\n */\n showAttribution?: boolean;\n\n /**\n * @internal\n */\n roomId?: string;\n };\n\ninterface ComposerEditorContainerProps\n extends Pick<\n ComposerProps,\n | \"defaultValue\"\n | \"showAttachments\"\n | \"showFormattingControls\"\n | \"showAttribution\"\n | \"overrides\"\n | \"actions\"\n | \"autoFocus\"\n | \"disabled\"\n > {\n isCollapsed: boolean | undefined;\n onEmptyChange: (isEmpty: boolean) => void;\n hasResolveMentionSuggestions: boolean;\n onEmojiPickerOpenChange: (isOpen: boolean) => void;\n onEditorClick: (event: MouseEvent<HTMLDivElement>) => void;\n}\n\nfunction ComposerInsertMentionEditorAction({\n label,\n tooltipLabel,\n className,\n onClick,\n ...props\n}: EditorActionProps) {\n const { createMention } = useComposer();\n\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n\n if (!event.isDefaultPrevented()) {\n event.stopPropagation();\n createMention();\n }\n },\n [createMention, onClick]\n );\n\n return (\n <Tooltip content={tooltipLabel ?? label}>\n <Button\n className={classNames(\"lb-composer-editor-action\", className)}\n onPointerDown={preventDefault}\n onClick={handleClick}\n aria-label={label}\n icon={<MentionIcon />}\n {...props}\n />\n </Tooltip>\n );\n}\n\nfunction ComposerInsertEmojiEditorAction({\n label,\n tooltipLabel,\n onPickerOpenChange,\n className,\n ...props\n}: EmojiEditorActionProps) {\n const { insertText } = useComposer();\n\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n return (\n <EmojiPicker onEmojiSelect={insertText} onOpenChange={onPickerOpenChange}>\n <Tooltip content={tooltipLabel ?? label}>\n <EmojiPickerTrigger asChild>\n <Button\n className={classNames(\"lb-composer-editor-action\", className)}\n onPointerDown={preventDefault}\n onClick={stopPropagation}\n aria-label={label}\n icon={<EmojiIcon />}\n {...props}\n />\n </EmojiPickerTrigger>\n </Tooltip>\n </EmojiPicker>\n );\n}\n\nfunction ComposerAttachFilesEditorAction({\n label,\n tooltipLabel,\n className,\n ...props\n}: EditorActionProps) {\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n return (\n <Tooltip content={tooltipLabel ?? label}>\n <ComposerPrimitive.AttachFiles asChild>\n <Button\n className={classNames(\"lb-composer-editor-action\", className)}\n onPointerDown={preventDefault}\n onClick={stopPropagation}\n aria-label={label}\n icon={<AttachmentIcon />}\n {...props}\n />\n </ComposerPrimitive.AttachFiles>\n </Tooltip>\n );\n}\n\nfunction ComposerMention({ userId }: ComposerEditorMentionProps) {\n return (\n <ComposerPrimitive.Mention className=\"lb-composer-mention\">\n {MENTION_CHARACTER}\n <User userId={userId} />\n </ComposerPrimitive.Mention>\n );\n}\n\nfunction ComposerMentionSuggestions({\n userIds,\n}: ComposerEditorMentionSuggestionsProps) {\n return userIds.length > 0 ? (\n <ComposerPrimitive.Suggestions className=\"lb-root lb-portal lb-elevation lb-composer-suggestions lb-composer-mention-suggestions\">\n <ComposerPrimitive.SuggestionsList className=\"lb-composer-suggestions-list lb-composer-mention-suggestions-list\">\n {userIds.map((userId) => (\n <ComposerPrimitive.SuggestionsListItem\n key={userId}\n className=\"lb-composer-suggestions-list-item lb-composer-mention-suggestion\"\n value={userId}\n >\n <Avatar\n userId={userId}\n className=\"lb-composer-mention-suggestion-avatar\"\n />\n <User\n userId={userId}\n className=\"lb-composer-mention-suggestion-user\"\n />\n </ComposerPrimitive.SuggestionsListItem>\n ))}\n </ComposerPrimitive.SuggestionsList>\n </ComposerPrimitive.Suggestions>\n ) : null;\n}\n\nfunction MarkToggle({\n mark,\n icon,\n shortcut,\n children,\n ...props\n}: MarkToggleProps) {\n const $ = useOverrides();\n const label = useMemo(() => {\n return $.COMPOSER_TOGGLE_MARK(mark);\n }, [$, mark]);\n\n return (\n <ShortcutTooltip\n content={label}\n shortcut={shortcut}\n sideOffset={FLOATING_ELEMENT_SIDE_OFFSET + 2}\n >\n <ComposerPrimitive.MarkToggle mark={mark} asChild {...props}>\n <Button aria-label={label} variant=\"toolbar\" icon={icon}>\n {children}\n </Button>\n </ComposerPrimitive.MarkToggle>\n </ShortcutTooltip>\n );\n}\n\ntype MarkToggles = {\n [K in ComposerBodyMark]: ComponentType<PropsWithChildren>;\n};\n\nconst markToggles: MarkToggles = {\n bold: () => <MarkToggle mark=\"bold\" shortcut=\"Mod-B\" icon={<BoldIcon />} />,\n italic: () => (\n <MarkToggle mark=\"italic\" shortcut=\"Mod-I\" icon={<ItalicIcon />} />\n ),\n strikethrough: () => (\n <MarkToggle\n mark=\"strikethrough\"\n shortcut=\"Mod-Shift-S\"\n icon={<StrikethroughIcon />}\n />\n ),\n code: () => <MarkToggle mark=\"code\" shortcut=\"Mod-E\" icon={<CodeIcon />} />,\n};\n\nconst markTogglesList = Object.entries(markToggles).map(([mark, Toggle]) => (\n <Toggle key={mark} />\n));\n\nfunction ComposerFloatingToolbar() {\n return (\n <ComposerPrimitive.FloatingToolbar className=\"lb-root lb-portal lb-elevation lb-composer-floating-toolbar\">\n {markTogglesList}\n </ComposerPrimitive.FloatingToolbar>\n );\n}\n\nfunction ComposerLink({ href, children }: ComposerEditorLinkProps) {\n return (\n <ComposerPrimitive.Link href={href} className=\"lb-composer-link\">\n {children}\n </ComposerPrimitive.Link>\n );\n}\n\ninterface ComposerAttachmentsProps extends ComponentPropsWithoutRef<\"div\"> {\n overrides?: Partial<GlobalOverrides & ComposerOverrides>;\n}\n\ninterface ComposerFileAttachmentProps extends ComponentPropsWithoutRef<\"div\"> {\n attachment: CommentMixedAttachment;\n overrides?: Partial<GlobalOverrides & ComposerOverrides>;\n}\n\nfunction ComposerFileAttachment({\n attachment,\n className,\n overrides,\n ...props\n}: ComposerFileAttachmentProps) {\n const { removeAttachment } = useComposer();\n const { roomId } = useComposerEditorContext();\n\n const handleDeleteClick = useCallback(() => {\n removeAttachment(attachment.id);\n }, [attachment.id, removeAttachment]);\n\n return (\n <FileAttachment\n className={classNames(\"lb-composer-attachment\", className)}\n {...props}\n attachment={attachment}\n onDeleteClick={handleDeleteClick}\n preventFocusOnDelete\n overrides={overrides}\n roomId={roomId}\n />\n );\n}\n\nfunction ComposerAttachments({\n overrides,\n className,\n ...props\n}: ComposerAttachmentsProps) {\n const { attachments } = useComposer();\n\n if (attachments.length === 0) {\n return null;\n }\n\n return (\n <div\n className={classNames(\"lb-composer-attachments\", className)}\n {...props}\n >\n <div className=\"lb-attachments\">\n {attachments.map((attachment) => {\n return (\n <ComposerFileAttachment\n key={attachment.id}\n attachment={attachment}\n overrides={overrides}\n />\n );\n })}\n </div>\n </div>\n );\n}\n\nconst editorRequiredComponents: ComposerEditorComponents = {\n Mention: ComposerMention,\n MentionSuggestions: ComposerMentionSuggestions,\n Link: ComposerLink,\n};\n\nfunction ComposerEditorContainer({\n showAttachments = true,\n showFormattingControls = true,\n showAttribution,\n defaultValue,\n isCollapsed,\n overrides,\n actions,\n autoFocus,\n disabled,\n hasResolveMentionSuggestions,\n onEmojiPickerOpenChange,\n onEmptyChange,\n onEditorClick,\n}: ComposerEditorContainerProps) {\n const { isEmpty } = useComposer();\n const { hasMaxAttachments } = useComposerAttachmentsContext();\n const $ = useOverrides(overrides);\n const components = useMemo(() => {\n return {\n ...editorRequiredComponents,\n FloatingToolbar: showFormattingControls\n ? ComposerFloatingToolbar\n : undefined,\n };\n }, [showFormattingControls]);\n\n const [isDraggingOver, dropAreaProps] = useComposerAttachmentsDropArea({\n disabled: disabled || hasMaxAttachments,\n });\n\n useLayoutEffect(() => {\n onEmptyChange(isEmpty);\n }, [isEmpty, onEmptyChange]);\n\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n return (\n <div className=\"lb-composer-editor-container\" {...dropAreaProps}>\n <ComposerPrimitive.Editor\n className=\"lb-composer-editor\"\n onClick={onEditorClick}\n placeholder={$.COMPOSER_PLACEHOLDER}\n defaultValue={defaultValue}\n autoFocus={autoFocus}\n components={components}\n disabled={disabled}\n dir={$.dir}\n />\n {showAttachments && <ComposerAttachments overrides={overrides} />}\n {(!isCollapsed || isDraggingOver) && (\n <div className=\"lb-composer-footer\">\n <div className=\"lb-composer-editor-actions\">\n {hasResolveMentionSuggestions && (\n <ComposerInsertMentionEditorAction\n label={$.COMPOSER_INSERT_MENTION}\n disabled={disabled}\n />\n )}\n <ComposerInsertEmojiEditorAction\n label={$.COMPOSER_INSERT_EMOJI}\n onPickerOpenChange={onEmojiPickerOpenChange}\n disabled={disabled}\n />\n {showAttachments && (\n <ComposerAttachFilesEditorAction\n label={$.COMPOSER_ATTACH_FILES}\n disabled={disabled}\n />\n )}\n </div>\n {showAttribution && <Attribution />}\n <div className=\"lb-composer-actions\">\n {actions ?? (\n <>\n <ShortcutTooltip content={$.COMPOSER_SEND} shortcut=\"Enter\">\n <ComposerPrimitive.Submit asChild>\n <Button\n onPointerDown={preventDefault}\n onClick={stopPropagation}\n className=\"lb-composer-action\"\n variant=\"primary\"\n aria-label={$.COMPOSER_SEND}\n icon={<SendIcon />}\n />\n </ComposerPrimitive.Submit>\n </ShortcutTooltip>\n </>\n )}\n </div>\n </div>\n )}\n {showAttachments && isDraggingOver && (\n <div className=\"lb-composer-attachments-drop-area\">\n <div className=\"lb-composer-attachments-drop-area-label\">\n <AttachmentIcon />\n {$.COMPOSER_ATTACH_FILES}\n </div>\n </div>\n )}\n </div>\n );\n}\n\nexport const ComposerRoomIdContext = createContext<string | null>(null);\n\n/**\n * Displays a composer to create comments.\n *\n * @example\n * <Composer />\n */\nexport const Composer = forwardRef(\n <M extends BaseMetadata = DM>(\n {\n threadId,\n commentId,\n metadata,\n defaultValue,\n defaultAttachments,\n onComposerSubmit,\n collapsed: controlledCollapsed,\n defaultCollapsed,\n onCollapsedChange: controlledOnCollapsedChange,\n overrides,\n actions,\n onBlur,\n className,\n onFocus,\n autoFocus,\n disabled,\n showAttachments = true,\n showFormattingControls = true,\n showAttribution,\n roomId: _roomId,\n ...props\n }: ComposerProps<M>,\n forwardedRef: ForwardedRef<HTMLFormElement>\n ) => {\n const room = useRoom({ allowOutsideRoom: true });\n\n const roomId = _roomId !== undefined ? _roomId : room?.id;\n if (roomId === undefined) {\n throw new Error(\n \"Composer must be a descendant of RoomProvider component\"\n );\n }\n\n const createThread = useCreateRoomThread(roomId);\n const createComment = useCreateRoomComment(roomId);\n const editComment = useEditRoomComment(roomId);\n const { preventUnsavedComposerChanges } = useLiveblocksUIConfig();\n const hasResolveMentionSuggestions =\n useResolveMentionSuggestions() !== undefined;\n const isEmptyRef = useRef(true);\n const isEmojiPickerOpenRef = useRef(false);\n const $ = useOverrides(overrides);\n const [isCollapsed, onCollapsedChange] = useControllableState(\n defaultCollapsed ?? false,\n controlledCollapsed,\n controlledOnCollapsedChange\n );\n\n const canCommentFallback = useSyncExternalStore(\n useCallback(\n (callback) => {\n if (room === null) return () => {};\n return room.events.self.subscribeOnce(callback);\n },\n [room]\n ),\n useCallback(() => {\n return room?.getSelf()?.canComment ?? true;\n }, [room]),\n useCallback(() => true, [])\n );\n\n const permissions = useRoomPermissions(roomId);\n const canComment =\n permissions.size > 0\n ? permissions.has(Permission.CommentsWrite) ||\n permissions.has(Permission.Write)\n : canCommentFallback;\n\n const setEmptyRef = useCallback((isEmpty: boolean) => {\n isEmptyRef.current = isEmpty;\n }, []);\n\n const setEmojiPickerOpenRef = useCallback((isEmojiPickerOpen: boolean) => {\n isEmojiPickerOpenRef.current = isEmojiPickerOpen;\n }, []);\n\n const handleFocus = useCallback(\n (event: FocusEvent<HTMLFormElement>) => {\n onFocus?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n if (isEmptyRef.current && canComment) {\n onCollapsedChange?.(false);\n }\n },\n [onCollapsedChange, onFocus, canComment]\n );\n\n const handleBlur = useCallback(\n (event: FocusEvent<HTMLFormElement>) => {\n onBlur?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n const isOutside = !event.currentTarget.contains(\n event.relatedTarget ?? document.activeElement\n );\n\n if (isOutside && isEmptyRef.current && !isEmojiPickerOpenRef.current) {\n onCollapsedChange?.(true);\n }\n },\n [onBlur, onCollapsedChange]\n );\n\n const handleEditorClick = useCallback(\n (event: MouseEvent<HTMLDivElement>) => {\n event.stopPropagation();\n\n if (isEmptyRef.current && canComment) {\n onCollapsedChange?.(false);\n }\n },\n [onCollapsedChange, canComment]\n );\n\n const handleComposerSubmit = useCallback(\n (comment: ComposerSubmitComment, event: FormEvent<HTMLFormElement>) => {\n onComposerSubmit?.(comment, event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n event.stopPropagation();\n\n if (commentId && threadId) {\n editComment({\n commentId,\n threadId,\n body: comment.body,\n attachments: comment.attachments,\n });\n } else if (threadId) {\n createComment({\n threadId,\n body: comment.body,\n attachments: comment.attachments,\n });\n } else {\n createThread({\n body: comment.body,\n metadata: metadata ?? {},\n attachments: comment.attachments,\n });\n }\n },\n [\n commentId,\n createComment,\n createThread,\n editComment,\n metadata,\n onComposerSubmit,\n threadId,\n ]\n );\n\n return (\n <TooltipProvider>\n <ComposerPrimitive.Form\n onComposerSubmit={handleComposerSubmit}\n className={classNames(\n \"lb-root lb-composer lb-composer-form\",\n className\n )}\n dir={$.dir}\n {...props}\n ref={forwardedRef}\n data-collapsed={isCollapsed ? \"\" : undefined}\n onFocus={handleFocus}\n onBlur={handleBlur}\n disabled={disabled || !canComment}\n defaultAttachments={defaultAttachments}\n pasteFilesAsAttachments={showAttachments}\n preventUnsavedChanges={preventUnsavedComposerChanges}\n roomId={roomId}\n >\n <ComposerEditorContainer\n defaultValue={defaultValue}\n actions={actions}\n overrides={overrides}\n isCollapsed={isCollapsed}\n showAttachments={showAttachments}\n showAttribution={showAttribution}\n showFormattingControls={showFormattingControls}\n hasResolveMentionSuggestions={hasResolveMentionSuggestions}\n onEmptyChange={setEmptyRef}\n onEmojiPickerOpenChange={setEmojiPickerOpenRef}\n onEditorClick={handleEditorClick}\n autoFocus={autoFocus}\n disabled={disabled}\n />\n </ComposerPrimitive.Form>\n </TooltipProvider>\n );\n }\n) as <M extends BaseMetadata = DM>(\n props: ComposerProps<M> & RefAttributes<HTMLFormElement>\n) => JSX.Element;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoOA;AAA2C;AACzC;AACA;AACA;AACA;AAEF;AACE;AAEA;AACE;AAAqB;AAGvB;AAAoB;AAEhB;AAEA;AACE;AACA;AAAc;AAChB;AACF;AACuB;AAGzB;AACG;AAAiC;AAC/B;AAC6D;AAC7C;AACN;AACG;AACO;AACf;AACN;AAGN;AAEA;AAAyC;AACvC;AACA;AACA;AACA;AAEF;AACE;AAEA;AACE;AAAqB;AAGvB;AACE;AAAsB;AAGxB;AACG;AAA2B;AAA0B;AACnD;AAAiC;AAC/B;AAA0B;AACxB;AAC6D;AAC7C;AACN;AACG;AACK;AACb;AACN;AACF;AACF;AAGN;AAEA;AAAyC;AACvC;AACA;AACA;AAEF;AACE;AACE;AAAqB;AAGvB;AACE;AAAsB;AAGxB;AACG;AAAiC;AAC/B;AAAqC;AACnC;AAC6D;AAC7C;AACN;AACG;AACU;AAClB;AACN;AACF;AAGN;AAEA;AACE;AACG;AAAoC;AAClC;AAAA;AACA;AAAK;AAAgB;AAAA;AAG5B;AAEA;AAAoC;AAEpC;AACE;AACG;AAAwC;AACtC;AAA4C;AAExC;AAEW;AACH;AAEP;AAAC;AACC;AACU;AACZ;AACC;AACC;AACU;AACZ;AAAA;AAEH;AACH;AAGN;AAEA;AAAoB;AAClB;AACA;AACA;AACA;AAEF;AACE;AACA;AACE;AAAkC;AAGpC;AACG;AACU;AACT;AAC2C;AAE1C;AAA6B;AAAmB;AAAK;AACnD;AAAmB;AAAe;AAAU;AAC1C;AACH;AACF;AAGN;AAMA;AAAiC;AAClB;AAAgB;AAAgB;AAAwB;AAAI;AAEtE;AAAgB;AAAkB;AAA0B;AAAI;AAGhE;AACM;AACI;AACgB;AAC3B;AAEW;AAAgB;AAAgB;AAAwB;AACvE;AAEA;AAIA;AACE;AACG;AAA4C;AAC1C;AAGP;AAEA;AACE;AACG;AAAuB;AAAsB;AAC3C;AAGP;AAWA;AAAgC;AAC9B;AACA;AACA;AAEF;AACE;AACA;AAEA;AACE;AAA8B;AAGhC;AACG;AAC0D;AACrD;AACJ;AACe;AACK;AACpB;AACA;AAGN;AAEA;AAA6B;AAC3B;AACA;AAEF;AACE;AAEA;AACE;AAAO;AAGT;AACG;AAC2D;AACtD;AAEH;AAAc;AAEX;AACG;AAEC;AACA;AACF;AAEH;AACH;AAGN;AAEA;AAA2D;AAChD;AACW;AAEtB;AAEA;AAAiC;AACb;AACO;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEF;AACE;AACA;AACA;AACA;AACE;AAAO;AACF;AAGC;AACN;AAGF;AAAuE;AAC/C;AAGxB;AACE;AAAqB;AAGvB;AACE;AAAqB;AAGvB;AACE;AAAsB;AAGxB;AACG;AAAc;AAAmC;AAChD;AAAC;AACW;AACD;AACM;AACf;AACA;AACA;AACA;AACO;AACT;AACqB;AAAoB;AAAsB;AAE5D;AAAc;AACb;AAAC;AAAc;AACZ;AACE;AACU;AACT;AACF;AAED;AACU;AACW;AACpB;AACF;AAEG;AACU;AACT;AACF;AAAA;AAEJ;AACiC;AAChC;AAAc;AAEX;AACG;AAA2B;AAAwB;AACjD;AAAgC;AAC9B;AACgB;AACN;AACC;AACF;AACM;AACE;AAClB;AACF;AACF;AACF;AAEJ;AAAA;AACF;AAGC;AAAc;AACZ;AAAc;AACb;AAAgB;AACb;AAAA;AACL;AACF;AAAA;AAIR;AAEa;AAQN;AAAiB;AAEpB;AACE;AACA;AACA;AACA;AACA;AACA;AACW;AACX;AACmB;AACnB;AACA;AACA;AACA;AACA;AACA;AACA;AACkB;AACO;AACzB;AACQ;AACL;AAIL;AAEA;AACA;AACE;AAAU;AACR;AACF;AAGF;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAyC;AACnB;AACpB;AACA;AAGF;AAA2B;AACzB;AAEI;AAAmB;AAAa;AAChC;AAA8C;AAChD;AACK;AACP;AAEE;AAAsC;AAC/B;AACiB;AAG5B;AACA;AAMA;AACE;AAAqB;AAGvB;AACE;AAA+B;AAGjC;AAAoB;AAEhB;AAEA;AACE;AAAA;AAGF;AACE;AAAyB;AAC3B;AACF;AACuC;AAGzC;AAAmB;AAEf;AAEA;AACE;AAAA;AAGF;AAAuC;AACL;AAGlC;AACE;AAAwB;AAC1B;AACF;AAC0B;AAG5B;AAA0B;AAEtB;AAEA;AACE;AAAyB;AAC3B;AACF;AAC8B;AAGhC;AAA6B;AAEzB;AAEA;AACE;AAAA;AAGF;AAEA;AACE;AAAY;AACV;AACA;AACc;AACO;AACtB;AAED;AAAc;AACZ;AACc;AACO;AACtB;AAED;AAAa;AACG;AACS;AACF;AACtB;AACH;AACF;AACA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACF;AAGF;AACG;AACE;AACmB;AACP;AACT;AACA;AACF;AACO;AACH;AACC;AAC8B;AAC1B;AACD;AACe;AACvB;AACyB;AACF;AACvB;AAEC;AACC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACe;AACU;AACV;AACf;AACA;AACF;AACF;AACF;AAGN;;"}
@@ -68,9 +68,12 @@ const AiChatAssistantMessage = react.memo(
68
68
  ),
69
69
  ...props,
70
70
  ref: forwardedRef,
71
- children: /* @__PURE__ */ jsxRuntime.jsx(components.ComponentsProvider, {
72
- components: components$1,
73
- children
71
+ children: /* @__PURE__ */ jsxRuntime.jsx(overrides.OverridesProvider, {
72
+ overrides: overrides$1,
73
+ children: /* @__PURE__ */ jsxRuntime.jsx(components.ComponentsProvider, {
74
+ components: components$1,
75
+ children
76
+ })
74
77
  })
75
78
  });
76
79
  }
@@ -98,6 +101,7 @@ function ReasoningPart({
98
101
  isStreaming
99
102
  }) {
100
103
  const [isOpen, setIsOpen] = react.useState(false);
104
+ const $ = overrides.useOverrides();
101
105
  return /* @__PURE__ */ jsxRuntime.jsxs(index$1.Root, {
102
106
  className: "lb-collapsible lb-ai-chat-message-reasoning",
103
107
  open: isOpen,
@@ -109,7 +113,7 @@ function ReasoningPart({
109
113
  isStreaming && "lb-ai-chat-pending"
110
114
  ),
111
115
  children: [
112
- "Reasoning",
116
+ $.AI_CHAT_MESSAGE_REASONING(isStreaming),
113
117
  /* @__PURE__ */ jsxRuntime.jsx("span", {
114
118
  className: "lb-collapsible-chevron lb-icon-container",
115
119
  children: /* @__PURE__ */ jsxRuntime.jsx(ChevronRight.ChevronRightIcon, {})
@@ -1 +1 @@
1
- {"version":3,"file":"AiChatAssistantMessage.cjs","sources":["../../../src/components/internal/AiChatAssistantMessage.tsx"],"sourcesContent":["import type { UiAssistantMessage } from \"@liveblocks/core\";\nimport {\n type ComponentProps,\n forwardRef,\n memo,\n type PropsWithChildren,\n type ReactNode,\n useState,\n} from \"react\";\n\nimport { ComponentsProvider, type GlobalComponents } from \"../../components\";\nimport { ChevronRightIcon } from \"../../icons/ChevronRight\";\nimport { WarningIcon } from \"../../icons/Warning\";\nimport {\n type AiChatMessageOverrides,\n type GlobalOverrides,\n useOverrides,\n} from \"../../overrides\";\nimport * as AiMessage from \"../../primitives/AiMessage\";\nimport type {\n AiMessageContentReasoningPartProps,\n AiMessageContentTextPartProps,\n} from \"../../primitives/AiMessage/types\";\nimport * as Collapsible from \"../../primitives/Collapsible\";\nimport { classNames } from \"../../utils/class-names\";\nimport { Prose } from \"./Prose\";\n\n/* -------------------------------------------------------------------------------------------------\n * AiChatAssistantMessage\n * -----------------------------------------------------------------------------------------------*/\nexport interface AiChatAssistantMessageProps extends ComponentProps<\"div\"> {\n /**\n * The message to display.\n */\n message: UiAssistantMessage;\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<GlobalOverrides & AiChatMessageOverrides>;\n\n /**\n * Override the component's components.\n */\n components?: Partial<GlobalComponents>;\n}\n\nexport const AiChatAssistantMessage = memo(\n forwardRef<HTMLDivElement, AiChatAssistantMessageProps>(\n ({ message, className, overrides, components, ...props }, forwardedRef) => {\n const $ = useOverrides(overrides);\n\n let children: ReactNode = null;\n\n if (message.deletedAt !== undefined) {\n children = (\n <div className=\"lb-ai-chat-message-deleted\">\n {$.AI_CHAT_MESSAGE_DELETED}\n </div>\n );\n } else if (\n message.status === \"generating\" ||\n message.status === \"awaiting-tool\"\n ) {\n if (message.contentSoFar.length === 0) {\n children = (\n <div className=\"lb-ai-chat-message-thinking lb-ai-chat-pending\">\n {$.AI_CHAT_MESSAGE_THINKING}\n </div>\n );\n } else {\n children = <AssistantMessageContent message={message} />;\n }\n } else if (message.status === \"completed\") {\n children = <AssistantMessageContent message={message} />;\n } else if (message.status === \"failed\") {\n // Do not include the error message if the user aborted the request.\n if (message.errorReason === \"Aborted by user\") {\n children = <AssistantMessageContent message={message} />;\n } else {\n children = (\n <>\n <AssistantMessageContent message={message} />\n\n <div className=\"lb-ai-chat-message-error\">\n <span className=\"lb-icon-container\">\n <WarningIcon />\n </span>\n {message.errorReason}\n </div>\n </>\n );\n }\n }\n\n return (\n <div\n className={classNames(\n \"lb-ai-chat-message lb-ai-chat-assistant-message\",\n className\n )}\n {...props}\n ref={forwardedRef}\n >\n <ComponentsProvider components={components}>\n {children}\n </ComponentsProvider>\n </div>\n );\n }\n )\n);\n\nfunction AssistantMessageContent({ message }: { message: UiAssistantMessage }) {\n return (\n <AiMessage.Content\n message={message}\n components={{\n TextPart,\n ReasoningPart,\n ToolInvocationPart,\n }}\n className=\"lb-ai-chat-message-content\"\n />\n );\n}\n\n/* -------------------------------------------------------------------------------------------------\n * TextPart\n * -----------------------------------------------------------------------------------------------*/\nfunction TextPart({ part }: AiMessageContentTextPartProps) {\n return <Prose content={part.text} className=\"lb-ai-chat-message-text\" />;\n}\n\n/* -------------------------------------------------------------------------------------------------\n * ReasoningPart\n * -----------------------------------------------------------------------------------------------*/\nfunction ReasoningPart({\n part,\n isStreaming,\n}: AiMessageContentReasoningPartProps) {\n const [isOpen, setIsOpen] = useState(false);\n return (\n <Collapsible.Root\n className=\"lb-collapsible lb-ai-chat-message-reasoning\"\n open={isOpen}\n onOpenChange={setIsOpen}\n >\n <Collapsible.Trigger\n className={classNames(\n \"lb-collapsible-trigger\",\n isStreaming && \"lb-ai-chat-pending\"\n )}\n >\n {/* TODO: If `isStreaming` is true, show \"Reasoning…\"/\"Thinking…\", otherwise show \"Reasoned/thought for x seconds\"? */}\n Reasoning\n <span className=\"lb-collapsible-chevron lb-icon-container\">\n <ChevronRightIcon />\n </span>\n </Collapsible.Trigger>\n\n <Collapsible.Content className=\"lb-collapsible-content\">\n <Prose content={part.text} />\n </Collapsible.Content>\n </Collapsible.Root>\n );\n}\n\n/* -------------------------------------------------------------------------------------------------\n * ToolInvocationPart\n * -----------------------------------------------------------------------------------------------*/\nfunction ToolInvocationPart({ children }: PropsWithChildren) {\n return <div className=\"lb-ai-chat-message-tool-invocation\">{children}</div>;\n}\n"],"names":["memo","forwardRef","overrides","components","useOverrides","jsx","jsxs","Fragment","WarningIcon","classNames","ComponentsProvider","AiMessage.Content","Prose","useState","Collapsible.Root","Collapsible.Trigger","ChevronRightIcon","Collapsible.Content"],"mappings":"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"}
1
+ {"version":3,"file":"AiChatAssistantMessage.cjs","sources":["../../../src/components/internal/AiChatAssistantMessage.tsx"],"sourcesContent":["import type { AiAssistantMessage, WithNavigation } from \"@liveblocks/core\";\nimport {\n type ComponentProps,\n forwardRef,\n memo,\n type PropsWithChildren,\n type ReactNode,\n useState,\n} from \"react\";\n\nimport { ComponentsProvider, type GlobalComponents } from \"../../components\";\nimport { ChevronRightIcon } from \"../../icons/ChevronRight\";\nimport { WarningIcon } from \"../../icons/Warning\";\nimport {\n type AiChatMessageOverrides,\n type GlobalOverrides,\n OverridesProvider,\n useOverrides,\n} from \"../../overrides\";\nimport * as AiMessage from \"../../primitives/AiMessage\";\nimport type {\n AiMessageContentReasoningPartProps,\n AiMessageContentTextPartProps,\n} from \"../../primitives/AiMessage/types\";\nimport * as Collapsible from \"../../primitives/Collapsible\";\nimport { classNames } from \"../../utils/class-names\";\nimport { Prose } from \"./Prose\";\n\ntype UiAssistantMessage = WithNavigation<AiAssistantMessage>;\n\n/* -------------------------------------------------------------------------------------------------\n * AiChatAssistantMessage\n * -----------------------------------------------------------------------------------------------*/\nexport interface AiChatAssistantMessageProps extends ComponentProps<\"div\"> {\n /**\n * The message to display.\n */\n message: UiAssistantMessage;\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<GlobalOverrides & AiChatMessageOverrides>;\n\n /**\n * Override the component's components.\n */\n components?: Partial<GlobalComponents>;\n}\n\nexport const AiChatAssistantMessage = memo(\n forwardRef<HTMLDivElement, AiChatAssistantMessageProps>(\n ({ message, className, overrides, components, ...props }, forwardedRef) => {\n const $ = useOverrides(overrides);\n\n let children: ReactNode = null;\n\n if (message.deletedAt !== undefined) {\n children = (\n <div className=\"lb-ai-chat-message-deleted\">\n {$.AI_CHAT_MESSAGE_DELETED}\n </div>\n );\n } else if (\n message.status === \"generating\" ||\n message.status === \"awaiting-tool\"\n ) {\n if (message.contentSoFar.length === 0) {\n children = (\n <div className=\"lb-ai-chat-message-thinking lb-ai-chat-pending\">\n {$.AI_CHAT_MESSAGE_THINKING}\n </div>\n );\n } else {\n children = <AssistantMessageContent message={message} />;\n }\n } else if (message.status === \"completed\") {\n children = <AssistantMessageContent message={message} />;\n } else if (message.status === \"failed\") {\n // Do not include the error message if the user aborted the request.\n if (message.errorReason === \"Aborted by user\") {\n children = <AssistantMessageContent message={message} />;\n } else {\n children = (\n <>\n <AssistantMessageContent message={message} />\n\n <div className=\"lb-ai-chat-message-error\">\n <span className=\"lb-icon-container\">\n <WarningIcon />\n </span>\n {message.errorReason}\n </div>\n </>\n );\n }\n }\n\n return (\n <div\n className={classNames(\n \"lb-ai-chat-message lb-ai-chat-assistant-message\",\n className\n )}\n {...props}\n ref={forwardedRef}\n >\n <OverridesProvider overrides={overrides}>\n <ComponentsProvider components={components}>\n {children}\n </ComponentsProvider>\n </OverridesProvider>\n </div>\n );\n }\n )\n);\n\nfunction AssistantMessageContent({ message }: { message: UiAssistantMessage }) {\n return (\n <AiMessage.Content\n message={message}\n components={{\n TextPart,\n ReasoningPart,\n ToolInvocationPart,\n }}\n className=\"lb-ai-chat-message-content\"\n />\n );\n}\n\n/* -------------------------------------------------------------------------------------------------\n * TextPart\n * -----------------------------------------------------------------------------------------------*/\nfunction TextPart({ part }: AiMessageContentTextPartProps) {\n return <Prose content={part.text} className=\"lb-ai-chat-message-text\" />;\n}\n\n/* -------------------------------------------------------------------------------------------------\n * ReasoningPart\n * -----------------------------------------------------------------------------------------------*/\nfunction ReasoningPart({\n part,\n isStreaming,\n}: AiMessageContentReasoningPartProps) {\n const [isOpen, setIsOpen] = useState(false);\n const $ = useOverrides();\n\n return (\n <Collapsible.Root\n className=\"lb-collapsible lb-ai-chat-message-reasoning\"\n open={isOpen}\n onOpenChange={setIsOpen}\n >\n <Collapsible.Trigger\n className={classNames(\n \"lb-collapsible-trigger\",\n isStreaming && \"lb-ai-chat-pending\"\n )}\n >\n {/* TODO: Show duration as \"Reasoned for x seconds\"? */}\n {$.AI_CHAT_MESSAGE_REASONING(isStreaming)}\n <span className=\"lb-collapsible-chevron lb-icon-container\">\n <ChevronRightIcon />\n </span>\n </Collapsible.Trigger>\n\n <Collapsible.Content className=\"lb-collapsible-content\">\n <Prose content={part.text} />\n </Collapsible.Content>\n </Collapsible.Root>\n );\n}\n\n/* -------------------------------------------------------------------------------------------------\n * ToolInvocationPart\n * -----------------------------------------------------------------------------------------------*/\nfunction ToolInvocationPart({ children }: PropsWithChildren) {\n return <div className=\"lb-ai-chat-message-tool-invocation\">{children}</div>;\n}\n"],"names":["memo","forwardRef","overrides","components","useOverrides","jsx","jsxs","Fragment","WarningIcon","classNames","OverridesProvider","ComponentsProvider","AiMessage.Content","Prose","useState","Collapsible.Root","Collapsible.Trigger","ChevronRightIcon","Collapsible.Content"],"mappings":"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"}
@@ -3,7 +3,7 @@ import { memo, forwardRef, useState } from 'react';
3
3
  import { ComponentsProvider } from '../../components.js';
4
4
  import { ChevronRightIcon } from '../../icons/ChevronRight.js';
5
5
  import { WarningIcon } from '../../icons/Warning.js';
6
- import { useOverrides } from '../../overrides.js';
6
+ import { useOverrides, OverridesProvider } from '../../overrides.js';
7
7
  import { Content as AiMessageContent } from '../../primitives/AiMessage/index.js';
8
8
  import { Root as CollapsibleRoot, Trigger as CollapsibleTrigger, Content as CollapsibleContent } from '../../primitives/Collapsible/index.js';
9
9
  import { classNames } from '../../utils/class-names.js';
@@ -66,9 +66,12 @@ const AiChatAssistantMessage = memo(
66
66
  ),
67
67
  ...props,
68
68
  ref: forwardedRef,
69
- children: /* @__PURE__ */ jsx(ComponentsProvider, {
70
- components,
71
- children
69
+ children: /* @__PURE__ */ jsx(OverridesProvider, {
70
+ overrides,
71
+ children: /* @__PURE__ */ jsx(ComponentsProvider, {
72
+ components,
73
+ children
74
+ })
72
75
  })
73
76
  });
74
77
  }
@@ -96,6 +99,7 @@ function ReasoningPart({
96
99
  isStreaming
97
100
  }) {
98
101
  const [isOpen, setIsOpen] = useState(false);
102
+ const $ = useOverrides();
99
103
  return /* @__PURE__ */ jsxs(CollapsibleRoot, {
100
104
  className: "lb-collapsible lb-ai-chat-message-reasoning",
101
105
  open: isOpen,
@@ -107,7 +111,7 @@ function ReasoningPart({
107
111
  isStreaming && "lb-ai-chat-pending"
108
112
  ),
109
113
  children: [
110
- "Reasoning",
114
+ $.AI_CHAT_MESSAGE_REASONING(isStreaming),
111
115
  /* @__PURE__ */ jsx("span", {
112
116
  className: "lb-collapsible-chevron lb-icon-container",
113
117
  children: /* @__PURE__ */ jsx(ChevronRightIcon, {})
@@ -1 +1 @@
1
- {"version":3,"file":"AiChatAssistantMessage.js","sources":["../../../src/components/internal/AiChatAssistantMessage.tsx"],"sourcesContent":["import type { UiAssistantMessage } from \"@liveblocks/core\";\nimport {\n type ComponentProps,\n forwardRef,\n memo,\n type PropsWithChildren,\n type ReactNode,\n useState,\n} from \"react\";\n\nimport { ComponentsProvider, type GlobalComponents } from \"../../components\";\nimport { ChevronRightIcon } from \"../../icons/ChevronRight\";\nimport { WarningIcon } from \"../../icons/Warning\";\nimport {\n type AiChatMessageOverrides,\n type GlobalOverrides,\n useOverrides,\n} from \"../../overrides\";\nimport * as AiMessage from \"../../primitives/AiMessage\";\nimport type {\n AiMessageContentReasoningPartProps,\n AiMessageContentTextPartProps,\n} from \"../../primitives/AiMessage/types\";\nimport * as Collapsible from \"../../primitives/Collapsible\";\nimport { classNames } from \"../../utils/class-names\";\nimport { Prose } from \"./Prose\";\n\n/* -------------------------------------------------------------------------------------------------\n * AiChatAssistantMessage\n * -----------------------------------------------------------------------------------------------*/\nexport interface AiChatAssistantMessageProps extends ComponentProps<\"div\"> {\n /**\n * The message to display.\n */\n message: UiAssistantMessage;\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<GlobalOverrides & AiChatMessageOverrides>;\n\n /**\n * Override the component's components.\n */\n components?: Partial<GlobalComponents>;\n}\n\nexport const AiChatAssistantMessage = memo(\n forwardRef<HTMLDivElement, AiChatAssistantMessageProps>(\n ({ message, className, overrides, components, ...props }, forwardedRef) => {\n const $ = useOverrides(overrides);\n\n let children: ReactNode = null;\n\n if (message.deletedAt !== undefined) {\n children = (\n <div className=\"lb-ai-chat-message-deleted\">\n {$.AI_CHAT_MESSAGE_DELETED}\n </div>\n );\n } else if (\n message.status === \"generating\" ||\n message.status === \"awaiting-tool\"\n ) {\n if (message.contentSoFar.length === 0) {\n children = (\n <div className=\"lb-ai-chat-message-thinking lb-ai-chat-pending\">\n {$.AI_CHAT_MESSAGE_THINKING}\n </div>\n );\n } else {\n children = <AssistantMessageContent message={message} />;\n }\n } else if (message.status === \"completed\") {\n children = <AssistantMessageContent message={message} />;\n } else if (message.status === \"failed\") {\n // Do not include the error message if the user aborted the request.\n if (message.errorReason === \"Aborted by user\") {\n children = <AssistantMessageContent message={message} />;\n } else {\n children = (\n <>\n <AssistantMessageContent message={message} />\n\n <div className=\"lb-ai-chat-message-error\">\n <span className=\"lb-icon-container\">\n <WarningIcon />\n </span>\n {message.errorReason}\n </div>\n </>\n );\n }\n }\n\n return (\n <div\n className={classNames(\n \"lb-ai-chat-message lb-ai-chat-assistant-message\",\n className\n )}\n {...props}\n ref={forwardedRef}\n >\n <ComponentsProvider components={components}>\n {children}\n </ComponentsProvider>\n </div>\n );\n }\n )\n);\n\nfunction AssistantMessageContent({ message }: { message: UiAssistantMessage }) {\n return (\n <AiMessage.Content\n message={message}\n components={{\n TextPart,\n ReasoningPart,\n ToolInvocationPart,\n }}\n className=\"lb-ai-chat-message-content\"\n />\n );\n}\n\n/* -------------------------------------------------------------------------------------------------\n * TextPart\n * -----------------------------------------------------------------------------------------------*/\nfunction TextPart({ part }: AiMessageContentTextPartProps) {\n return <Prose content={part.text} className=\"lb-ai-chat-message-text\" />;\n}\n\n/* -------------------------------------------------------------------------------------------------\n * ReasoningPart\n * -----------------------------------------------------------------------------------------------*/\nfunction ReasoningPart({\n part,\n isStreaming,\n}: AiMessageContentReasoningPartProps) {\n const [isOpen, setIsOpen] = useState(false);\n return (\n <Collapsible.Root\n className=\"lb-collapsible lb-ai-chat-message-reasoning\"\n open={isOpen}\n onOpenChange={setIsOpen}\n >\n <Collapsible.Trigger\n className={classNames(\n \"lb-collapsible-trigger\",\n isStreaming && \"lb-ai-chat-pending\"\n )}\n >\n {/* TODO: If `isStreaming` is true, show \"Reasoning…\"/\"Thinking…\", otherwise show \"Reasoned/thought for x seconds\"? */}\n Reasoning\n <span className=\"lb-collapsible-chevron lb-icon-container\">\n <ChevronRightIcon />\n </span>\n </Collapsible.Trigger>\n\n <Collapsible.Content className=\"lb-collapsible-content\">\n <Prose content={part.text} />\n </Collapsible.Content>\n </Collapsible.Root>\n );\n}\n\n/* -------------------------------------------------------------------------------------------------\n * ToolInvocationPart\n * -----------------------------------------------------------------------------------------------*/\nfunction ToolInvocationPart({ children }: PropsWithChildren) {\n return <div className=\"lb-ai-chat-message-tool-invocation\">{children}</div>;\n}\n"],"names":["AiMessage.Content","Collapsible.Root","Collapsible.Trigger","Collapsible.Content"],"mappings":";;;;;;;;;;;AA+CO,MAAM,sBAAyB,GAAA,IAAA;AAAA,EACpC,UAAA;AAAA,IACE,CAAC,EAAE,OAAS,EAAA,SAAA,EAAW,WAAW,UAAe,EAAA,GAAA,KAAA,IAAS,YAAiB,KAAA;AACzE,MAAM,MAAA,CAAA,GAAI,aAAa,SAAS,CAAA,CAAA;AAEhC,MAAA,IAAI,QAAsB,GAAA,IAAA,CAAA;AAE1B,MAAI,IAAA,OAAA,CAAQ,cAAc,KAAW,CAAA,EAAA;AACnC,QAAA,QAAA,mBACG,GAAA,CAAA,KAAA,EAAA;AAAA,UAAI,SAAU,EAAA,4BAAA;AAAA,UACZ,QAAE,EAAA,CAAA,CAAA,uBAAA;AAAA,SACL,CAAA,CAAA;AAAA,iBAGF,OAAQ,CAAA,MAAA,KAAW,YACnB,IAAA,OAAA,CAAQ,WAAW,eACnB,EAAA;AACA,QAAI,IAAA,OAAA,CAAQ,YAAa,CAAA,MAAA,KAAW,CAAG,EAAA;AACrC,UAAA,QAAA,mBACG,GAAA,CAAA,KAAA,EAAA;AAAA,YAAI,SAAU,EAAA,gDAAA;AAAA,YACZ,QAAE,EAAA,CAAA,CAAA,wBAAA;AAAA,WACL,CAAA,CAAA;AAAA,SAEG,MAAA;AACL,UAAA,QAAA,mBAAY,GAAA,CAAA,uBAAA,EAAA;AAAA,YAAwB,OAAA;AAAA,WAAkB,CAAA,CAAA;AAAA,SACxD;AAAA,OACF,MAAA,IAAW,OAAQ,CAAA,MAAA,KAAW,WAAa,EAAA;AACzC,QAAA,QAAA,mBAAY,GAAA,CAAA,uBAAA,EAAA;AAAA,UAAwB,OAAA;AAAA,SAAkB,CAAA,CAAA;AAAA,OACxD,MAAA,IAAW,OAAQ,CAAA,MAAA,KAAW,QAAU,EAAA;AAEtC,QAAI,IAAA,OAAA,CAAQ,gBAAgB,iBAAmB,EAAA;AAC7C,UAAA,QAAA,mBAAY,GAAA,CAAA,uBAAA,EAAA;AAAA,YAAwB,OAAA;AAAA,WAAkB,CAAA,CAAA;AAAA,SACjD,MAAA;AACL,UACE,QAAA,mBAAA,IAAA,CAAA,QAAA,EAAA;AAAA,YACE,QAAA,EAAA;AAAA,8BAAC,GAAA,CAAA,uBAAA,EAAA;AAAA,gBAAwB,OAAA;AAAA,eAAkB,CAAA;AAAA,8BAE1C,IAAA,CAAA,KAAA,EAAA;AAAA,gBAAI,SAAU,EAAA,0BAAA;AAAA,gBACb,QAAA,EAAA;AAAA,kCAAC,GAAA,CAAA,MAAA,EAAA;AAAA,oBAAK,SAAU,EAAA,mBAAA;AAAA,oBACd,8BAAC,WAAY,EAAA,EAAA,CAAA;AAAA,mBACf,CAAA;AAAA,kBACC,OAAQ,CAAA,WAAA;AAAA,iBAAA;AAAA,eACX,CAAA;AAAA,aAAA;AAAA,WACF,CAAA,CAAA;AAAA,SAEJ;AAAA,OACF;AAEA,MAAA,uBACG,GAAA,CAAA,KAAA,EAAA;AAAA,QACC,SAAW,EAAA,UAAA;AAAA,UACT,iDAAA;AAAA,UACA,SAAA;AAAA,SACF;AAAA,QACC,GAAG,KAAA;AAAA,QACJ,GAAK,EAAA,YAAA;AAAA,QAEL,QAAC,kBAAA,GAAA,CAAA,kBAAA,EAAA;AAAA,UAAmB,UAAA;AAAA,UACjB,QAAA;AAAA,SACH,CAAA;AAAA,OACF,CAAA,CAAA;AAAA,KAEJ;AAAA,GACF;AACF,EAAA;AAEA,SAAS,uBAAA,CAAwB,EAAE,OAAA,EAA4C,EAAA;AAC7E,EACE,uBAAA,GAAA,CAACA,gBAAA,EAAA;AAAA,IACC,OAAA;AAAA,IACA,UAAY,EAAA;AAAA,MACV,QAAA;AAAA,MACA,aAAA;AAAA,MACA,kBAAA;AAAA,KACF;AAAA,IACA,SAAU,EAAA,4BAAA;AAAA,GACZ,CAAA,CAAA;AAEJ,CAAA;AAKA,SAAS,QAAA,CAAS,EAAE,IAAA,EAAuC,EAAA;AACzD,EAAA,uBAAQ,GAAA,CAAA,KAAA,EAAA;AAAA,IAAM,SAAS,IAAK,CAAA,IAAA;AAAA,IAAM,SAAU,EAAA,yBAAA;AAAA,GAA0B,CAAA,CAAA;AACxE,CAAA;AAKA,SAAS,aAAc,CAAA;AAAA,EACrB,IAAA;AAAA,EACA,WAAA;AACF,CAAuC,EAAA;AACrC,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAI,SAAS,KAAK,CAAA,CAAA;AAC1C,EACE,uBAAA,IAAA,CAACC,eAAA,EAAA;AAAA,IACC,SAAU,EAAA,6CAAA;AAAA,IACV,IAAM,EAAA,MAAA;AAAA,IACN,YAAc,EAAA,SAAA;AAAA,IAEd,QAAA,EAAA;AAAA,sBAAA,IAAA,CAACC,kBAAA,EAAA;AAAA,QACC,SAAW,EAAA,UAAA;AAAA,UACT,wBAAA;AAAA,UACA,WAAe,IAAA,oBAAA;AAAA,SACjB;AAAA,QAEuH,QAAA,EAAA;AAAA,UAAA,WAAA;AAAA,0BAEtH,GAAA,CAAA,MAAA,EAAA;AAAA,YAAK,SAAU,EAAA,0CAAA;AAAA,YACd,8BAAC,gBAAiB,EAAA,EAAA,CAAA;AAAA,WACpB,CAAA;AAAA,SAAA;AAAA,OACF,CAAA;AAAA,sBAEA,GAAA,CAACC,kBAAA,EAAA;AAAA,QAAoB,SAAU,EAAA,wBAAA;AAAA,QAC7B,QAAC,kBAAA,GAAA,CAAA,KAAA,EAAA;AAAA,UAAM,SAAS,IAAK,CAAA,IAAA;AAAA,SAAM,CAAA;AAAA,OAC7B,CAAA;AAAA,KAAA;AAAA,GACF,CAAA,CAAA;AAEJ,CAAA;AAKA,SAAS,kBAAA,CAAmB,EAAE,QAAA,EAA+B,EAAA;AAC3D,EAAA,uBAAQ,GAAA,CAAA,KAAA,EAAA;AAAA,IAAI,SAAU,EAAA,oCAAA;AAAA,IAAsC,QAAA;AAAA,GAAS,CAAA,CAAA;AACvE;;;;"}
1
+ {"version":3,"file":"AiChatAssistantMessage.js","sources":["../../../src/components/internal/AiChatAssistantMessage.tsx"],"sourcesContent":["import type { AiAssistantMessage, WithNavigation } from \"@liveblocks/core\";\nimport {\n type ComponentProps,\n forwardRef,\n memo,\n type PropsWithChildren,\n type ReactNode,\n useState,\n} from \"react\";\n\nimport { ComponentsProvider, type GlobalComponents } from \"../../components\";\nimport { ChevronRightIcon } from \"../../icons/ChevronRight\";\nimport { WarningIcon } from \"../../icons/Warning\";\nimport {\n type AiChatMessageOverrides,\n type GlobalOverrides,\n OverridesProvider,\n useOverrides,\n} from \"../../overrides\";\nimport * as AiMessage from \"../../primitives/AiMessage\";\nimport type {\n AiMessageContentReasoningPartProps,\n AiMessageContentTextPartProps,\n} from \"../../primitives/AiMessage/types\";\nimport * as Collapsible from \"../../primitives/Collapsible\";\nimport { classNames } from \"../../utils/class-names\";\nimport { Prose } from \"./Prose\";\n\ntype UiAssistantMessage = WithNavigation<AiAssistantMessage>;\n\n/* -------------------------------------------------------------------------------------------------\n * AiChatAssistantMessage\n * -----------------------------------------------------------------------------------------------*/\nexport interface AiChatAssistantMessageProps extends ComponentProps<\"div\"> {\n /**\n * The message to display.\n */\n message: UiAssistantMessage;\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<GlobalOverrides & AiChatMessageOverrides>;\n\n /**\n * Override the component's components.\n */\n components?: Partial<GlobalComponents>;\n}\n\nexport const AiChatAssistantMessage = memo(\n forwardRef<HTMLDivElement, AiChatAssistantMessageProps>(\n ({ message, className, overrides, components, ...props }, forwardedRef) => {\n const $ = useOverrides(overrides);\n\n let children: ReactNode = null;\n\n if (message.deletedAt !== undefined) {\n children = (\n <div className=\"lb-ai-chat-message-deleted\">\n {$.AI_CHAT_MESSAGE_DELETED}\n </div>\n );\n } else if (\n message.status === \"generating\" ||\n message.status === \"awaiting-tool\"\n ) {\n if (message.contentSoFar.length === 0) {\n children = (\n <div className=\"lb-ai-chat-message-thinking lb-ai-chat-pending\">\n {$.AI_CHAT_MESSAGE_THINKING}\n </div>\n );\n } else {\n children = <AssistantMessageContent message={message} />;\n }\n } else if (message.status === \"completed\") {\n children = <AssistantMessageContent message={message} />;\n } else if (message.status === \"failed\") {\n // Do not include the error message if the user aborted the request.\n if (message.errorReason === \"Aborted by user\") {\n children = <AssistantMessageContent message={message} />;\n } else {\n children = (\n <>\n <AssistantMessageContent message={message} />\n\n <div className=\"lb-ai-chat-message-error\">\n <span className=\"lb-icon-container\">\n <WarningIcon />\n </span>\n {message.errorReason}\n </div>\n </>\n );\n }\n }\n\n return (\n <div\n className={classNames(\n \"lb-ai-chat-message lb-ai-chat-assistant-message\",\n className\n )}\n {...props}\n ref={forwardedRef}\n >\n <OverridesProvider overrides={overrides}>\n <ComponentsProvider components={components}>\n {children}\n </ComponentsProvider>\n </OverridesProvider>\n </div>\n );\n }\n )\n);\n\nfunction AssistantMessageContent({ message }: { message: UiAssistantMessage }) {\n return (\n <AiMessage.Content\n message={message}\n components={{\n TextPart,\n ReasoningPart,\n ToolInvocationPart,\n }}\n className=\"lb-ai-chat-message-content\"\n />\n );\n}\n\n/* -------------------------------------------------------------------------------------------------\n * TextPart\n * -----------------------------------------------------------------------------------------------*/\nfunction TextPart({ part }: AiMessageContentTextPartProps) {\n return <Prose content={part.text} className=\"lb-ai-chat-message-text\" />;\n}\n\n/* -------------------------------------------------------------------------------------------------\n * ReasoningPart\n * -----------------------------------------------------------------------------------------------*/\nfunction ReasoningPart({\n part,\n isStreaming,\n}: AiMessageContentReasoningPartProps) {\n const [isOpen, setIsOpen] = useState(false);\n const $ = useOverrides();\n\n return (\n <Collapsible.Root\n className=\"lb-collapsible lb-ai-chat-message-reasoning\"\n open={isOpen}\n onOpenChange={setIsOpen}\n >\n <Collapsible.Trigger\n className={classNames(\n \"lb-collapsible-trigger\",\n isStreaming && \"lb-ai-chat-pending\"\n )}\n >\n {/* TODO: Show duration as \"Reasoned for x seconds\"? */}\n {$.AI_CHAT_MESSAGE_REASONING(isStreaming)}\n <span className=\"lb-collapsible-chevron lb-icon-container\">\n <ChevronRightIcon />\n </span>\n </Collapsible.Trigger>\n\n <Collapsible.Content className=\"lb-collapsible-content\">\n <Prose content={part.text} />\n </Collapsible.Content>\n </Collapsible.Root>\n );\n}\n\n/* -------------------------------------------------------------------------------------------------\n * ToolInvocationPart\n * -----------------------------------------------------------------------------------------------*/\nfunction ToolInvocationPart({ children }: PropsWithChildren) {\n return <div className=\"lb-ai-chat-message-tool-invocation\">{children}</div>;\n}\n"],"names":["AiMessage.Content","Collapsible.Root","Collapsible.Trigger","Collapsible.Content"],"mappings":"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"}