@liveblocks/react-ui 3.5.2 → 3.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_private/index.d.cts +4 -0
- package/dist/_private/index.d.ts +4 -0
- package/dist/components/Thread.cjs.map +1 -1
- package/dist/components/Thread.js.map +1 -1
- package/dist/components/internal/AiChatAssistantMessage.cjs +29 -9
- package/dist/components/internal/AiChatAssistantMessage.cjs.map +1 -1
- package/dist/components/internal/AiChatAssistantMessage.js +30 -10
- package/dist/components/internal/AiChatAssistantMessage.js.map +1 -1
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/overrides.cjs +42 -1
- package/dist/overrides.cjs.map +1 -1
- package/dist/overrides.js +42 -1
- package/dist/overrides.js.map +1 -1
- package/dist/primitives/AiMessage/index.cjs +7 -1
- package/dist/primitives/AiMessage/index.cjs.map +1 -1
- package/dist/primitives/AiMessage/index.js +7 -1
- package/dist/primitives/AiMessage/index.js.map +1 -1
- package/dist/primitives/AiMessage/tool-invocation.cjs.map +1 -1
- package/dist/primitives/AiMessage/tool-invocation.js.map +1 -1
- package/dist/primitives/Duration.cjs +195 -0
- package/dist/primitives/Duration.cjs.map +1 -0
- package/dist/primitives/Duration.js +192 -0
- package/dist/primitives/Duration.js.map +1 -0
- package/dist/primitives/Timestamp.cjs +4 -3
- package/dist/primitives/Timestamp.cjs.map +1 -1
- package/dist/primitives/Timestamp.js +4 -4
- package/dist/primitives/Timestamp.js.map +1 -1
- package/dist/primitives/index.cjs +2 -0
- package/dist/primitives/index.cjs.map +1 -1
- package/dist/primitives/index.d.cts +59 -4
- package/dist/primitives/index.d.ts +59 -4
- package/dist/primitives/index.js +1 -0
- package/dist/primitives/index.js.map +1 -1
- package/dist/version.cjs +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -4
- package/src/styles/index.css +20 -2
- package/styles.css +1 -1
- package/styles.css.map +1 -1
|
@@ -620,6 +620,10 @@ interface AiMessageContentComponents {
|
|
|
620
620
|
* The component used to display reasoning parts.
|
|
621
621
|
*/
|
|
622
622
|
ReasoningPart: ComponentType<AiMessageContentReasoningPartProps>;
|
|
623
|
+
/**
|
|
624
|
+
* The component used to display knowledge retrieval parts.
|
|
625
|
+
*/
|
|
626
|
+
RetrievalPart: ComponentType<AiMessageContentRetrievalPartProps>;
|
|
623
627
|
}
|
|
624
628
|
interface AiMessageContentProps extends ComponentPropsWithSlot<"div"> {
|
|
625
629
|
/**
|
package/dist/_private/index.d.ts
CHANGED
|
@@ -620,6 +620,10 @@ interface AiMessageContentComponents {
|
|
|
620
620
|
* The component used to display reasoning parts.
|
|
621
621
|
*/
|
|
622
622
|
ReasoningPart: ComponentType<AiMessageContentReasoningPartProps>;
|
|
623
|
+
/**
|
|
624
|
+
* The component used to display knowledge retrieval parts.
|
|
625
|
+
*/
|
|
626
|
+
RetrievalPart: ComponentType<AiMessageContentRetrievalPartProps>;
|
|
623
627
|
}
|
|
624
628
|
interface AiMessageContentProps extends ComponentPropsWithSlot<"div"> {
|
|
625
629
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Thread.cjs","sources":["../../src/components/Thread.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n type BaseMetadata,\n type CommentData,\n type DM,\n Permission,\n type ThreadData,\n} from \"@liveblocks/core\";\nimport {\n useMarkRoomThreadAsResolved,\n useMarkRoomThreadAsUnresolved,\n useRoomPermissions,\n useRoomThreadSubscription,\n} from \"@liveblocks/react/_private\";\nimport * as TogglePrimitive from \"@radix-ui/react-toggle\";\nimport type {\n ComponentPropsWithoutRef,\n ForwardedRef,\n RefAttributes,\n SyntheticEvent,\n} from \"react\";\nimport {\n forwardRef,\n Fragment,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from \"react\";\n\nimport type { GlobalComponents } from \"../components\";\nimport { ArrowDownIcon } from \"../icons/ArrowDown\";\nimport { BellIcon } from \"../icons/Bell\";\nimport { BellCrossedIcon } from \"../icons/BellCrossed\";\nimport { CheckCircleIcon } from \"../icons/CheckCircle\";\nimport { CheckCircleFillIcon } from \"../icons/CheckCircleFill\";\nimport type {\n CommentOverrides,\n ComposerOverrides,\n GlobalOverrides,\n ThreadOverrides,\n} from \"../overrides\";\nimport { findLastIndex } from \"@liveblocks/core\";\n\nimport { useOverrides } from \"../overrides\";\nimport { cn } from \"../utils/cn\";\nimport type { CommentProps } from \"./Comment\";\nimport { Comment } from \"./Comment\";\nimport type { ComposerProps } from \"./Composer\";\nimport { Composer } from \"./Composer\";\nimport { Button } from \"./internal/Button\";\nimport { DropdownItem } from \"./internal/Dropdown\";\nimport { Tooltip, TooltipProvider } from \"./internal/Tooltip\";\n\nexport interface ThreadProps<M extends BaseMetadata = DM>\n extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * The thread to display.\n */\n thread: ThreadData<M>;\n\n /**\n * How to show or hide the composer to reply to the thread.\n */\n showComposer?: boolean | \"collapsed\";\n\n /**\n * Whether to show the action to resolve the thread.\n */\n showResolveAction?: boolean;\n\n /**\n * How to show or hide the actions.\n */\n showActions?: CommentProps[\"showActions\"];\n\n /**\n * Whether to show reactions.\n */\n showReactions?: CommentProps[\"showReactions\"];\n\n /**\n * Whether to show the composer's formatting controls.\n */\n showComposerFormattingControls?: ComposerProps[\"showFormattingControls\"];\n\n /**\n * The maximum number of comments to show.\n *\n * The first and last comments are always shown and by default if some comments\n * are hidden, only the first comment will be shown before the \"show more\" button\n * and after it will be shown all the newest comments to fit the limit set.\n *\n * It's possible to customize this by setting `maxVisibleComments` to an object:\n *\n * @example\n * // Only show the last comment, and all the older ones to fit the limit.\n * <Thread maxVisibleComments={{ max: 5, show: \"oldest\" }} />\n *\n * @example\n * // Show as many old comments as new ones to fit the limit.\n * <Thread maxVisibleComments={{ max: 5, show: \"both\" }} />\n */\n maxVisibleComments?:\n | number\n | { max: number; show: \"oldest\" | \"both\" | \"newest\" };\n\n /**\n * Whether to blur the composer editor when the composer is submitted.\n */\n blurComposerOnSubmit?: ComposerProps[\"blurOnSubmit\"];\n\n /**\n * Whether to indent the comments' content.\n */\n indentCommentContent?: CommentProps[\"indentContent\"];\n\n /**\n * Whether to show deleted comments.\n */\n showDeletedComments?: CommentProps[\"showDeleted\"];\n\n /**\n * Whether to show attachments.\n */\n showAttachments?: boolean;\n\n /**\n * The event handler called when changing the resolved status.\n */\n onResolvedChange?: (resolved: boolean) => void;\n\n /**\n * The event handler called when a comment is edited.\n */\n onCommentEdit?: CommentProps[\"onCommentEdit\"];\n\n /**\n * The event handler called when a comment is deleted.\n */\n onCommentDelete?: CommentProps[\"onCommentDelete\"];\n\n /**\n * The event handler called when the thread is deleted.\n * A thread is deleted when all its comments are deleted.\n */\n onThreadDelete?: (thread: ThreadData<M>) => void;\n\n /**\n * The event handler called when clicking on a comment's author.\n */\n onAuthorClick?: CommentProps[\"onAuthorClick\"];\n\n /**\n * The event handler called when clicking on a mention.\n */\n onMentionClick?: CommentProps[\"onMentionClick\"];\n\n /**\n * The event handler called when clicking on a comment's attachment.\n */\n onAttachmentClick?: CommentProps[\"onAttachmentClick\"];\n\n /**\n * The event handler called when the composer is submitted.\n */\n onComposerSubmit?: ComposerProps[\"onComposerSubmit\"];\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<\n GlobalOverrides & ThreadOverrides & CommentOverrides & ComposerOverrides\n >;\n\n /**\n * Override the component's components.\n */\n components?: Partial<GlobalComponents>;\n}\n\n/**\n * Displays a thread of comments, with a composer to reply\n * to it.\n *\n * @example\n * <>\n * {threads.map((thread) => (\n * <Thread key={thread.id} thread={thread} />\n * ))}\n * </>\n */\nexport const Thread = forwardRef(\n <M extends BaseMetadata = DM>(\n {\n thread,\n indentCommentContent = true,\n showActions = \"hover\",\n showDeletedComments,\n showResolveAction = true,\n showReactions = true,\n showComposer = \"collapsed\",\n showAttachments = true,\n showComposerFormattingControls = true,\n maxVisibleComments,\n onResolvedChange,\n onCommentEdit,\n onCommentDelete,\n onThreadDelete,\n onAuthorClick,\n onMentionClick,\n onAttachmentClick,\n onComposerSubmit,\n blurComposerOnSubmit,\n overrides,\n components,\n className,\n ...props\n }: ThreadProps<M>,\n forwardedRef: ForwardedRef<HTMLDivElement>\n ) => {\n const markThreadAsResolved = useMarkRoomThreadAsResolved(thread.roomId);\n const markThreadAsUnresolved = useMarkRoomThreadAsUnresolved(thread.roomId);\n const $ = useOverrides(overrides);\n const [showAllComments, setShowAllComments] = useState(false);\n const firstCommentIndex = useMemo(() => {\n return showDeletedComments\n ? 0\n : thread.comments.findIndex((comment) => comment.body);\n }, [showDeletedComments, thread.comments]);\n const lastCommentIndex = useMemo(() => {\n return showDeletedComments\n ? thread.comments.length - 1\n : findLastIndex(thread.comments, (comment) => Boolean(comment.body));\n }, [showDeletedComments, thread.comments]);\n const hiddenComments = useMemo(() => {\n const maxVisibleCommentsCount =\n typeof maxVisibleComments === \"number\"\n ? maxVisibleComments\n : maxVisibleComments?.max;\n const visibleCommentsShow =\n (typeof maxVisibleComments === \"object\"\n ? maxVisibleComments?.show\n : undefined) ?? \"newest\";\n\n // If we explicitly want to show all comments or there's no limit set,\n // no need to hide any comments.\n if (showAllComments || maxVisibleCommentsCount === undefined) {\n return;\n }\n\n const comments = thread.comments\n .map((comment, index) => ({ comment, index }))\n .filter(({ comment }) => showDeletedComments || comment.body);\n\n // There aren't enough comments so no need to hide any.\n if (comments.length <= Math.max(maxVisibleCommentsCount, 2)) {\n return;\n }\n\n const firstVisibleComment = comments[0]!;\n const lastVisibleComment = comments[comments.length - 1]!;\n\n // Always show the first and last comments even if the limit is set to lower than 2.\n if (maxVisibleCommentsCount <= 2) {\n const firstHiddenCommentIndex =\n comments[1]?.index ?? firstVisibleComment.index;\n const lastHiddenCommentIndex =\n comments[comments.length - 2]?.index ?? lastVisibleComment.index;\n\n return {\n firstIndex: firstHiddenCommentIndex,\n lastIndex: lastHiddenCommentIndex,\n count: comments.slice(1, comments.length - 1).length,\n };\n }\n\n const remainingVisibleCommentsCount = maxVisibleCommentsCount - 2;\n\n // Split the remaining visible comments before, after, or equally.\n const beforeVisibleCommentsCount =\n visibleCommentsShow === \"oldest\"\n ? remainingVisibleCommentsCount\n : visibleCommentsShow === \"newest\"\n ? 0\n : Math.floor(remainingVisibleCommentsCount / 2);\n const afterVisibleCommentsCount =\n visibleCommentsShow === \"oldest\"\n ? 0\n : visibleCommentsShow === \"newest\"\n ? remainingVisibleCommentsCount\n : Math.ceil(remainingVisibleCommentsCount / 2);\n\n // The first comment is always visible so `+ 1` to skip it.\n const firstHiddenComment = comments[1 + beforeVisibleCommentsCount];\n // The last comment is always visible so `- 2` to skip it.\n const lastHiddenComment =\n comments[comments.length - 2 - afterVisibleCommentsCount];\n\n // There aren't any comments to hide besides the first and last ones.\n if (\n !firstHiddenComment ||\n !lastHiddenComment ||\n firstHiddenComment.index > lastHiddenComment.index\n ) {\n return;\n }\n\n return {\n firstIndex: firstHiddenComment.index,\n lastIndex: lastHiddenComment.index,\n count: thread.comments\n .slice(firstHiddenComment.index, lastHiddenComment.index + 1)\n .filter((comment) => showDeletedComments || comment.body).length,\n };\n }, [\n maxVisibleComments,\n showAllComments,\n showDeletedComments,\n thread.comments,\n ]);\n const {\n status: subscriptionStatus,\n unreadSince,\n subscribe,\n unsubscribe,\n } = useRoomThreadSubscription(thread.roomId, thread.id);\n const unreadIndex = useMemo(() => {\n // The user is not subscribed to this thread.\n if (subscriptionStatus !== \"subscribed\") {\n return;\n }\n\n // The user hasn't read the thread yet, so all comments are unread.\n if (unreadSince === null) {\n return firstCommentIndex;\n }\n\n // The user has read the thread, so we find the first unread comment.\n const unreadIndex = thread.comments.findIndex(\n (comment) =>\n (showDeletedComments ? true : comment.body) &&\n comment.createdAt > unreadSince\n );\n\n return unreadIndex >= 0 && unreadIndex < thread.comments.length\n ? unreadIndex\n : undefined;\n }, [\n firstCommentIndex,\n showDeletedComments,\n subscriptionStatus,\n thread.comments,\n unreadSince,\n ]);\n const [newIndex, setNewIndex] = useState<number>();\n const newIndicatorIndex = newIndex === undefined ? unreadIndex : newIndex;\n\n useEffect(() => {\n if (unreadIndex) {\n // Keep the \"new\" indicator at the lowest unread index.\n setNewIndex((persistedUnreadIndex) =>\n Math.min(persistedUnreadIndex ?? Infinity, unreadIndex)\n );\n }\n }, [unreadIndex]);\n\n const permissions = useRoomPermissions(thread.roomId);\n const canComment =\n permissions.size > 0\n ? permissions.has(Permission.CommentsWrite) ||\n permissions.has(Permission.Write)\n : true;\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n const handleResolvedChange = useCallback(\n (resolved: boolean) => {\n onResolvedChange?.(resolved);\n\n if (resolved) {\n markThreadAsResolved(thread.id);\n } else {\n markThreadAsUnresolved(thread.id);\n }\n },\n [\n markThreadAsResolved,\n markThreadAsUnresolved,\n onResolvedChange,\n thread.id,\n ]\n );\n\n const handleCommentDelete = useCallback(\n (comment: CommentData) => {\n onCommentDelete?.(comment);\n\n const filteredComments = thread.comments.filter(\n (comment) => comment.body\n );\n\n if (filteredComments.length <= 1) {\n onThreadDelete?.(thread);\n }\n },\n [onCommentDelete, onThreadDelete, thread]\n );\n\n const handleSubscribeChange = useCallback(() => {\n if (subscriptionStatus === \"subscribed\") {\n unsubscribe();\n } else {\n subscribe();\n }\n }, [subscriptionStatus, subscribe, unsubscribe]);\n\n return (\n <TooltipProvider>\n <div\n className={cn(\n \"lb-root lb-thread\",\n showActions === \"hover\" && \"lb-thread:show-actions-hover\",\n className\n )}\n data-resolved={thread.resolved ? \"\" : undefined}\n data-unread={unreadIndex !== undefined ? \"\" : undefined}\n dir={$.dir}\n {...props}\n ref={forwardedRef}\n >\n <div className=\"lb-thread-comments\">\n {thread.comments.map((comment, index) => {\n const isFirstComment = index === firstCommentIndex;\n const isUnread =\n unreadIndex !== undefined && index >= unreadIndex;\n const isHidden =\n hiddenComments &&\n index >= hiddenComments.firstIndex &&\n index <= hiddenComments.lastIndex;\n const isFirstHiddenComment =\n isHidden && index === hiddenComments.firstIndex;\n\n if (isFirstHiddenComment) {\n return (\n <div\n key={`${comment.id}-show-more`}\n className=\"lb-thread-show-more\"\n >\n <Button\n variant=\"ghost\"\n className=\"lb-thread-show-more-button\"\n onClick={() => setShowAllComments(true)}\n >\n {$.THREAD_SHOW_MORE_COMMENTS(hiddenComments.count)}\n </Button>\n </div>\n );\n }\n\n if (isHidden) {\n return null;\n }\n\n const children = (\n <Comment\n key={comment.id}\n overrides={overrides}\n className=\"lb-thread-comment\"\n data-unread={isUnread ? \"\" : undefined}\n comment={comment}\n indentContent={indentCommentContent}\n showDeleted={showDeletedComments}\n showActions={showActions}\n showReactions={showReactions}\n showAttachments={showAttachments}\n showComposerFormattingControls={\n showComposerFormattingControls\n }\n onCommentEdit={onCommentEdit}\n onCommentDelete={handleCommentDelete}\n onAuthorClick={onAuthorClick}\n onMentionClick={onMentionClick}\n onAttachmentClick={onAttachmentClick}\n components={components}\n autoMarkReadThreadId={\n index === lastCommentIndex && isUnread\n ? thread.id\n : undefined\n }\n additionalActionsClassName={\n isFirstComment ? \"lb-thread-actions\" : undefined\n }\n additionalActions={\n isFirstComment && showResolveAction ? (\n <Tooltip\n content={\n thread.resolved\n ? $.THREAD_UNRESOLVE\n : $.THREAD_RESOLVE\n }\n >\n <TogglePrimitive.Root\n pressed={thread.resolved}\n onPressedChange={handleResolvedChange}\n asChild\n >\n <Button\n className=\"lb-comment-action\"\n onClick={stopPropagation}\n aria-label={\n thread.resolved\n ? $.THREAD_UNRESOLVE\n : $.THREAD_RESOLVE\n }\n icon={\n thread.resolved ? (\n <CheckCircleFillIcon />\n ) : (\n <CheckCircleIcon />\n )\n }\n disabled={!canComment}\n />\n </TogglePrimitive.Root>\n </Tooltip>\n ) : null\n }\n additionalDropdownItemsBefore={\n isFirstComment ? (\n <DropdownItem\n onSelect={handleSubscribeChange}\n onClick={stopPropagation}\n icon={\n subscriptionStatus === \"subscribed\" ? (\n <BellCrossedIcon />\n ) : (\n <BellIcon />\n )\n }\n >\n {subscriptionStatus === \"subscribed\"\n ? $.THREAD_UNSUBSCRIBE\n : $.THREAD_SUBSCRIBE}\n </DropdownItem>\n ) : null\n }\n />\n );\n\n return index === newIndicatorIndex &&\n newIndicatorIndex !== firstCommentIndex &&\n newIndicatorIndex <= lastCommentIndex ? (\n <Fragment key={comment.id}>\n <div\n className=\"lb-thread-new-indicator\"\n aria-label={$.THREAD_NEW_INDICATOR_DESCRIPTION}\n >\n <span className=\"lb-thread-new-indicator-label\">\n <ArrowDownIcon className=\"lb-thread-new-indicator-label-icon\" />\n {$.THREAD_NEW_INDICATOR}\n </span>\n </div>\n {children}\n </Fragment>\n ) : (\n children\n );\n })}\n </div>\n {showComposer && (\n <Composer\n className=\"lb-thread-composer\"\n threadId={thread.id}\n defaultCollapsed={showComposer === \"collapsed\" ? true : undefined}\n showAttachments={showAttachments}\n showFormattingControls={showComposerFormattingControls}\n onComposerSubmit={onComposerSubmit}\n blurOnSubmit={blurComposerOnSubmit}\n overrides={{\n COMPOSER_PLACEHOLDER: $.THREAD_COMPOSER_PLACEHOLDER,\n COMPOSER_SEND: $.THREAD_COMPOSER_SEND,\n ...overrides,\n }}\n roomId={thread.roomId}\n />\n )}\n </div>\n </TooltipProvider>\n );\n }\n) as <M extends BaseMetadata = DM>(\n props: ThreadProps<M> & RefAttributes<HTMLDivElement>\n) => JSX.Element;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiMO;AAAe;AAElB;AACE;AACuB;AACT;AACd;AACoB;AACJ;AACD;AACG;AACe;AACjC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AACA;AACE;AAEuD;AAEzD;AACE;AAEqE;AAEvE;AACE;AAIA;AAOA;AACE;AAAA;AAGF;AAKA;AACE;AAAA;AAGF;AACA;AAGA;AACE;AAEA;AAGA;AAAO;AACO;AACD;AACmC;AAChD;AAGF;AAGA;AAMA;AAQA;AAEA;AAIA;AAKE;AAAA;AAGF;AAAO;AAC0B;AACF;AAG+B;AAC9D;AACC;AACD;AACA;AACA;AACO;AAET;AAAM;AACI;AACR;AACA;AACA;AAEF;AAEE;AACE;AAAA;AAIF;AACE;AAAO;AAIT;AAAoC;AAGZ;AAGxB;AAEI;AACH;AACD;AACA;AACA;AACO;AACP;AAEF;AACA;AAEA;AACE;AAEE;AAAA;AACwD;AACxD;AACF;AAGF;AACA;AAMA;AACE;AAAsB;AAGxB;AAA6B;AAEzB;AAEA;AACE;AAA8B;AAE9B;AAAgC;AAClC;AACF;AACA;AACE;AACA;AACA;AACO;AACT;AAGF;AAA4B;AAExB;AAEA;AAAyC;AAClB;AAGvB;AACE;AAAuB;AACzB;AACF;AACwC;AAG1C;AACE;AACE;AAAY;AAEZ;AAAU;AACZ;AAGF;AACG;AACE;AACY;AACT;AAC2B;AAC3B;AACF;AACsC;AACQ;AACvC;AACH;AACC;AAEL;AAAC;AAAc;AAEX;AACA;AAEA;AAIA;AAGA;AACE;AACG;AAEW;AAET;AACS;AACE;AAC4B;AAEW;AACnD;AACF;AAIJ;AACE;AAAO;AAGT;AACG;AAEC;AACU;AACmB;AAC7B;AACe;AACF;AACb;AACA;AACA;AACA;AAGA;AACiB;AACjB;AACA;AACA;AACA;AAIM;AAGmC;AAIpC;AAIS;AAGP;AACiB;AACC;AACV;AAEN;AACW;AACD;AAID;AAMa;AAGV;AACb;AACF;AAEA;AAID;AACW;AACD;AAKK;AAMR;AAEN;AAKV;AAGG;AACC;AAAC;AACW;AACI;AAEb;AAAe;AACd;AAAC;AAAwB;AAAqC;AAC3D;AAAA;AACL;AACF;AACC;AAAA;AAGH;AAEH;AACH;AAEG;AACW;AACO;AACuC;AACxD;AACwB;AACxB;AACc;AACH;AACe;AACP;AACd;AACL;AACe;AACjB;AAAA;AAEJ;AACF;AAGN;;"}
|
|
1
|
+
{"version":3,"file":"Thread.cjs","sources":["../../src/components/Thread.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n type BaseMetadata,\n type CommentData,\n type DM,\n Permission,\n type ThreadData,\n} from \"@liveblocks/core\";\nimport { findLastIndex } from \"@liveblocks/core\";\nimport {\n useMarkRoomThreadAsResolved,\n useMarkRoomThreadAsUnresolved,\n useRoomPermissions,\n useRoomThreadSubscription,\n} from \"@liveblocks/react/_private\";\nimport * as TogglePrimitive from \"@radix-ui/react-toggle\";\nimport type {\n ComponentPropsWithoutRef,\n ForwardedRef,\n RefAttributes,\n SyntheticEvent,\n} from \"react\";\nimport {\n forwardRef,\n Fragment,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from \"react\";\n\nimport type { GlobalComponents } from \"../components\";\nimport { ArrowDownIcon } from \"../icons/ArrowDown\";\nimport { BellIcon } from \"../icons/Bell\";\nimport { BellCrossedIcon } from \"../icons/BellCrossed\";\nimport { CheckCircleIcon } from \"../icons/CheckCircle\";\nimport { CheckCircleFillIcon } from \"../icons/CheckCircleFill\";\nimport type {\n CommentOverrides,\n ComposerOverrides,\n GlobalOverrides,\n ThreadOverrides,\n} from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport { cn } from \"../utils/cn\";\nimport type { CommentProps } from \"./Comment\";\nimport { Comment } from \"./Comment\";\nimport type { ComposerProps } from \"./Composer\";\nimport { Composer } from \"./Composer\";\nimport { Button } from \"./internal/Button\";\nimport { DropdownItem } from \"./internal/Dropdown\";\nimport { Tooltip, TooltipProvider } from \"./internal/Tooltip\";\n\nexport interface ThreadProps<M extends BaseMetadata = DM>\n extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * The thread to display.\n */\n thread: ThreadData<M>;\n\n /**\n * How to show or hide the composer to reply to the thread.\n */\n showComposer?: boolean | \"collapsed\";\n\n /**\n * Whether to show the action to resolve the thread.\n */\n showResolveAction?: boolean;\n\n /**\n * How to show or hide the actions.\n */\n showActions?: CommentProps[\"showActions\"];\n\n /**\n * Whether to show reactions.\n */\n showReactions?: CommentProps[\"showReactions\"];\n\n /**\n * Whether to show the composer's formatting controls.\n */\n showComposerFormattingControls?: ComposerProps[\"showFormattingControls\"];\n\n /**\n * The maximum number of comments to show.\n *\n * The first and last comments are always shown and by default if some comments\n * are hidden, only the first comment will be shown before the \"show more\" button\n * and after it will be shown all the newest comments to fit the limit set.\n *\n * It's possible to customize this by setting `maxVisibleComments` to an object:\n *\n * @example\n * // Only show the last comment, and all the older ones to fit the limit.\n * <Thread maxVisibleComments={{ max: 5, show: \"oldest\" }} />\n *\n * @example\n * // Show as many old comments as new ones to fit the limit.\n * <Thread maxVisibleComments={{ max: 5, show: \"both\" }} />\n */\n maxVisibleComments?:\n | number\n | { max: number; show: \"oldest\" | \"both\" | \"newest\" };\n\n /**\n * Whether to blur the composer editor when the composer is submitted.\n */\n blurComposerOnSubmit?: ComposerProps[\"blurOnSubmit\"];\n\n /**\n * Whether to indent the comments' content.\n */\n indentCommentContent?: CommentProps[\"indentContent\"];\n\n /**\n * Whether to show deleted comments.\n */\n showDeletedComments?: CommentProps[\"showDeleted\"];\n\n /**\n * Whether to show attachments.\n */\n showAttachments?: boolean;\n\n /**\n * The event handler called when changing the resolved status.\n */\n onResolvedChange?: (resolved: boolean) => void;\n\n /**\n * The event handler called when a comment is edited.\n */\n onCommentEdit?: CommentProps[\"onCommentEdit\"];\n\n /**\n * The event handler called when a comment is deleted.\n */\n onCommentDelete?: CommentProps[\"onCommentDelete\"];\n\n /**\n * The event handler called when the thread is deleted.\n * A thread is deleted when all its comments are deleted.\n */\n onThreadDelete?: (thread: ThreadData<M>) => void;\n\n /**\n * The event handler called when clicking on a comment's author.\n */\n onAuthorClick?: CommentProps[\"onAuthorClick\"];\n\n /**\n * The event handler called when clicking on a mention.\n */\n onMentionClick?: CommentProps[\"onMentionClick\"];\n\n /**\n * The event handler called when clicking on a comment's attachment.\n */\n onAttachmentClick?: CommentProps[\"onAttachmentClick\"];\n\n /**\n * The event handler called when the composer is submitted.\n */\n onComposerSubmit?: ComposerProps[\"onComposerSubmit\"];\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<\n GlobalOverrides & ThreadOverrides & CommentOverrides & ComposerOverrides\n >;\n\n /**\n * Override the component's components.\n */\n components?: Partial<GlobalComponents>;\n}\n\n/**\n * Displays a thread of comments, with a composer to reply\n * to it.\n *\n * @example\n * <>\n * {threads.map((thread) => (\n * <Thread key={thread.id} thread={thread} />\n * ))}\n * </>\n */\nexport const Thread = forwardRef(\n <M extends BaseMetadata = DM>(\n {\n thread,\n indentCommentContent = true,\n showActions = \"hover\",\n showDeletedComments,\n showResolveAction = true,\n showReactions = true,\n showComposer = \"collapsed\",\n showAttachments = true,\n showComposerFormattingControls = true,\n maxVisibleComments,\n onResolvedChange,\n onCommentEdit,\n onCommentDelete,\n onThreadDelete,\n onAuthorClick,\n onMentionClick,\n onAttachmentClick,\n onComposerSubmit,\n blurComposerOnSubmit,\n overrides,\n components,\n className,\n ...props\n }: ThreadProps<M>,\n forwardedRef: ForwardedRef<HTMLDivElement>\n ) => {\n const markThreadAsResolved = useMarkRoomThreadAsResolved(thread.roomId);\n const markThreadAsUnresolved = useMarkRoomThreadAsUnresolved(thread.roomId);\n const $ = useOverrides(overrides);\n const [showAllComments, setShowAllComments] = useState(false);\n const firstCommentIndex = useMemo(() => {\n return showDeletedComments\n ? 0\n : thread.comments.findIndex((comment) => comment.body);\n }, [showDeletedComments, thread.comments]);\n const lastCommentIndex = useMemo(() => {\n return showDeletedComments\n ? thread.comments.length - 1\n : findLastIndex(thread.comments, (comment) => Boolean(comment.body));\n }, [showDeletedComments, thread.comments]);\n const hiddenComments = useMemo(() => {\n const maxVisibleCommentsCount =\n typeof maxVisibleComments === \"number\"\n ? maxVisibleComments\n : maxVisibleComments?.max;\n const visibleCommentsShow =\n (typeof maxVisibleComments === \"object\"\n ? maxVisibleComments?.show\n : undefined) ?? \"newest\";\n\n // If we explicitly want to show all comments or there's no limit set,\n // no need to hide any comments.\n if (showAllComments || maxVisibleCommentsCount === undefined) {\n return;\n }\n\n const comments = thread.comments\n .map((comment, index) => ({ comment, index }))\n .filter(({ comment }) => showDeletedComments || comment.body);\n\n // There aren't enough comments so no need to hide any.\n if (comments.length <= Math.max(maxVisibleCommentsCount, 2)) {\n return;\n }\n\n const firstVisibleComment = comments[0]!;\n const lastVisibleComment = comments[comments.length - 1]!;\n\n // Always show the first and last comments even if the limit is set to lower than 2.\n if (maxVisibleCommentsCount <= 2) {\n const firstHiddenCommentIndex =\n comments[1]?.index ?? firstVisibleComment.index;\n const lastHiddenCommentIndex =\n comments[comments.length - 2]?.index ?? lastVisibleComment.index;\n\n return {\n firstIndex: firstHiddenCommentIndex,\n lastIndex: lastHiddenCommentIndex,\n count: comments.slice(1, comments.length - 1).length,\n };\n }\n\n const remainingVisibleCommentsCount = maxVisibleCommentsCount - 2;\n\n // Split the remaining visible comments before, after, or equally.\n const beforeVisibleCommentsCount =\n visibleCommentsShow === \"oldest\"\n ? remainingVisibleCommentsCount\n : visibleCommentsShow === \"newest\"\n ? 0\n : Math.floor(remainingVisibleCommentsCount / 2);\n const afterVisibleCommentsCount =\n visibleCommentsShow === \"oldest\"\n ? 0\n : visibleCommentsShow === \"newest\"\n ? remainingVisibleCommentsCount\n : Math.ceil(remainingVisibleCommentsCount / 2);\n\n // The first comment is always visible so `+ 1` to skip it.\n const firstHiddenComment = comments[1 + beforeVisibleCommentsCount];\n // The last comment is always visible so `- 2` to skip it.\n const lastHiddenComment =\n comments[comments.length - 2 - afterVisibleCommentsCount];\n\n // There aren't any comments to hide besides the first and last ones.\n if (\n !firstHiddenComment ||\n !lastHiddenComment ||\n firstHiddenComment.index > lastHiddenComment.index\n ) {\n return;\n }\n\n return {\n firstIndex: firstHiddenComment.index,\n lastIndex: lastHiddenComment.index,\n count: thread.comments\n .slice(firstHiddenComment.index, lastHiddenComment.index + 1)\n .filter((comment) => showDeletedComments || comment.body).length,\n };\n }, [\n maxVisibleComments,\n showAllComments,\n showDeletedComments,\n thread.comments,\n ]);\n const {\n status: subscriptionStatus,\n unreadSince,\n subscribe,\n unsubscribe,\n } = useRoomThreadSubscription(thread.roomId, thread.id);\n const unreadIndex = useMemo(() => {\n // The user is not subscribed to this thread.\n if (subscriptionStatus !== \"subscribed\") {\n return;\n }\n\n // The user hasn't read the thread yet, so all comments are unread.\n if (unreadSince === null) {\n return firstCommentIndex;\n }\n\n // The user has read the thread, so we find the first unread comment.\n const unreadIndex = thread.comments.findIndex(\n (comment) =>\n (showDeletedComments ? true : comment.body) &&\n comment.createdAt > unreadSince\n );\n\n return unreadIndex >= 0 && unreadIndex < thread.comments.length\n ? unreadIndex\n : undefined;\n }, [\n firstCommentIndex,\n showDeletedComments,\n subscriptionStatus,\n thread.comments,\n unreadSince,\n ]);\n const [newIndex, setNewIndex] = useState<number>();\n const newIndicatorIndex = newIndex === undefined ? unreadIndex : newIndex;\n\n useEffect(() => {\n if (unreadIndex) {\n // Keep the \"new\" indicator at the lowest unread index.\n setNewIndex((persistedUnreadIndex) =>\n Math.min(persistedUnreadIndex ?? Infinity, unreadIndex)\n );\n }\n }, [unreadIndex]);\n\n const permissions = useRoomPermissions(thread.roomId);\n const canComment =\n permissions.size > 0\n ? permissions.has(Permission.CommentsWrite) ||\n permissions.has(Permission.Write)\n : true;\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n const handleResolvedChange = useCallback(\n (resolved: boolean) => {\n onResolvedChange?.(resolved);\n\n if (resolved) {\n markThreadAsResolved(thread.id);\n } else {\n markThreadAsUnresolved(thread.id);\n }\n },\n [\n markThreadAsResolved,\n markThreadAsUnresolved,\n onResolvedChange,\n thread.id,\n ]\n );\n\n const handleCommentDelete = useCallback(\n (comment: CommentData) => {\n onCommentDelete?.(comment);\n\n const filteredComments = thread.comments.filter(\n (comment) => comment.body\n );\n\n if (filteredComments.length <= 1) {\n onThreadDelete?.(thread);\n }\n },\n [onCommentDelete, onThreadDelete, thread]\n );\n\n const handleSubscribeChange = useCallback(() => {\n if (subscriptionStatus === \"subscribed\") {\n unsubscribe();\n } else {\n subscribe();\n }\n }, [subscriptionStatus, subscribe, unsubscribe]);\n\n return (\n <TooltipProvider>\n <div\n className={cn(\n \"lb-root lb-thread\",\n showActions === \"hover\" && \"lb-thread:show-actions-hover\",\n className\n )}\n data-resolved={thread.resolved ? \"\" : undefined}\n data-unread={unreadIndex !== undefined ? \"\" : undefined}\n dir={$.dir}\n {...props}\n ref={forwardedRef}\n >\n <div className=\"lb-thread-comments\">\n {thread.comments.map((comment, index) => {\n const isFirstComment = index === firstCommentIndex;\n const isUnread =\n unreadIndex !== undefined && index >= unreadIndex;\n const isHidden =\n hiddenComments &&\n index >= hiddenComments.firstIndex &&\n index <= hiddenComments.lastIndex;\n const isFirstHiddenComment =\n isHidden && index === hiddenComments.firstIndex;\n\n if (isFirstHiddenComment) {\n return (\n <div\n key={`${comment.id}-show-more`}\n className=\"lb-thread-show-more\"\n >\n <Button\n variant=\"ghost\"\n className=\"lb-thread-show-more-button\"\n onClick={() => setShowAllComments(true)}\n >\n {$.THREAD_SHOW_MORE_COMMENTS(hiddenComments.count)}\n </Button>\n </div>\n );\n }\n\n if (isHidden) {\n return null;\n }\n\n const children = (\n <Comment\n key={comment.id}\n overrides={overrides}\n className=\"lb-thread-comment\"\n data-unread={isUnread ? \"\" : undefined}\n comment={comment}\n indentContent={indentCommentContent}\n showDeleted={showDeletedComments}\n showActions={showActions}\n showReactions={showReactions}\n showAttachments={showAttachments}\n showComposerFormattingControls={\n showComposerFormattingControls\n }\n onCommentEdit={onCommentEdit}\n onCommentDelete={handleCommentDelete}\n onAuthorClick={onAuthorClick}\n onMentionClick={onMentionClick}\n onAttachmentClick={onAttachmentClick}\n components={components}\n autoMarkReadThreadId={\n index === lastCommentIndex && isUnread\n ? thread.id\n : undefined\n }\n additionalActionsClassName={\n isFirstComment ? \"lb-thread-actions\" : undefined\n }\n additionalActions={\n isFirstComment && showResolveAction ? (\n <Tooltip\n content={\n thread.resolved\n ? $.THREAD_UNRESOLVE\n : $.THREAD_RESOLVE\n }\n >\n <TogglePrimitive.Root\n pressed={thread.resolved}\n onPressedChange={handleResolvedChange}\n asChild\n >\n <Button\n className=\"lb-comment-action\"\n onClick={stopPropagation}\n aria-label={\n thread.resolved\n ? $.THREAD_UNRESOLVE\n : $.THREAD_RESOLVE\n }\n icon={\n thread.resolved ? (\n <CheckCircleFillIcon />\n ) : (\n <CheckCircleIcon />\n )\n }\n disabled={!canComment}\n />\n </TogglePrimitive.Root>\n </Tooltip>\n ) : null\n }\n additionalDropdownItemsBefore={\n isFirstComment ? (\n <DropdownItem\n onSelect={handleSubscribeChange}\n onClick={stopPropagation}\n icon={\n subscriptionStatus === \"subscribed\" ? (\n <BellCrossedIcon />\n ) : (\n <BellIcon />\n )\n }\n >\n {subscriptionStatus === \"subscribed\"\n ? $.THREAD_UNSUBSCRIBE\n : $.THREAD_SUBSCRIBE}\n </DropdownItem>\n ) : null\n }\n />\n );\n\n return index === newIndicatorIndex &&\n newIndicatorIndex !== firstCommentIndex &&\n newIndicatorIndex <= lastCommentIndex ? (\n <Fragment key={comment.id}>\n <div\n className=\"lb-thread-new-indicator\"\n aria-label={$.THREAD_NEW_INDICATOR_DESCRIPTION}\n >\n <span className=\"lb-thread-new-indicator-label\">\n <ArrowDownIcon className=\"lb-thread-new-indicator-label-icon\" />\n {$.THREAD_NEW_INDICATOR}\n </span>\n </div>\n {children}\n </Fragment>\n ) : (\n children\n );\n })}\n </div>\n {showComposer && (\n <Composer\n className=\"lb-thread-composer\"\n threadId={thread.id}\n defaultCollapsed={showComposer === \"collapsed\" ? true : undefined}\n showAttachments={showAttachments}\n showFormattingControls={showComposerFormattingControls}\n onComposerSubmit={onComposerSubmit}\n blurOnSubmit={blurComposerOnSubmit}\n overrides={{\n COMPOSER_PLACEHOLDER: $.THREAD_COMPOSER_PLACEHOLDER,\n COMPOSER_SEND: $.THREAD_COMPOSER_SEND,\n ...overrides,\n }}\n roomId={thread.roomId}\n />\n )}\n </div>\n </TooltipProvider>\n );\n }\n) as <M extends BaseMetadata = DM>(\n props: ThreadProps<M> & RefAttributes<HTMLDivElement>\n) => JSX.Element;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgMO;AAAe;AAElB;AACE;AACuB;AACT;AACd;AACoB;AACJ;AACD;AACG;AACe;AACjC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AACA;AACE;AAEuD;AAEzD;AACE;AAEqE;AAEvE;AACE;AAIA;AAOA;AACE;AAAA;AAGF;AAKA;AACE;AAAA;AAGF;AACA;AAGA;AACE;AAEA;AAGA;AAAO;AACO;AACD;AACmC;AAChD;AAGF;AAGA;AAMA;AAQA;AAEA;AAIA;AAKE;AAAA;AAGF;AAAO;AAC0B;AACF;AAG+B;AAC9D;AACC;AACD;AACA;AACA;AACO;AAET;AAAM;AACI;AACR;AACA;AACA;AAEF;AAEE;AACE;AAAA;AAIF;AACE;AAAO;AAIT;AAAoC;AAGZ;AAGxB;AAEI;AACH;AACD;AACA;AACA;AACO;AACP;AAEF;AACA;AAEA;AACE;AAEE;AAAA;AACwD;AACxD;AACF;AAGF;AACA;AAMA;AACE;AAAsB;AAGxB;AAA6B;AAEzB;AAEA;AACE;AAA8B;AAE9B;AAAgC;AAClC;AACF;AACA;AACE;AACA;AACA;AACO;AACT;AAGF;AAA4B;AAExB;AAEA;AAAyC;AAClB;AAGvB;AACE;AAAuB;AACzB;AACF;AACwC;AAG1C;AACE;AACE;AAAY;AAEZ;AAAU;AACZ;AAGF;AACG;AACE;AACY;AACT;AAC2B;AAC3B;AACF;AACsC;AACQ;AACvC;AACH;AACC;AAEL;AAAC;AAAc;AAEX;AACA;AAEA;AAIA;AAGA;AACE;AACG;AAEW;AAET;AACS;AACE;AAC4B;AAEW;AACnD;AACF;AAIJ;AACE;AAAO;AAGT;AACG;AAEC;AACU;AACmB;AAC7B;AACe;AACF;AACb;AACA;AACA;AACA;AAGA;AACiB;AACjB;AACA;AACA;AACA;AAIM;AAGmC;AAIpC;AAIS;AAGP;AACiB;AACC;AACV;AAEN;AACW;AACD;AAID;AAMa;AAGV;AACb;AACF;AAEA;AAID;AACW;AACD;AAKK;AAMR;AAEN;AAKV;AAGG;AACC;AAAC;AACW;AACI;AAEb;AAAe;AACd;AAAC;AAAwB;AAAqC;AAC3D;AAAA;AACL;AACF;AACC;AAAA;AAGH;AAEH;AACH;AAEG;AACW;AACO;AACuC;AACxD;AACwB;AACxB;AACc;AACH;AACe;AACP;AACd;AACL;AACe;AACjB;AAAA;AAEJ;AACF;AAGN;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Thread.js","sources":["../../src/components/Thread.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n type BaseMetadata,\n type CommentData,\n type DM,\n Permission,\n type ThreadData,\n} from \"@liveblocks/core\";\nimport {\n useMarkRoomThreadAsResolved,\n useMarkRoomThreadAsUnresolved,\n useRoomPermissions,\n useRoomThreadSubscription,\n} from \"@liveblocks/react/_private\";\nimport * as TogglePrimitive from \"@radix-ui/react-toggle\";\nimport type {\n ComponentPropsWithoutRef,\n ForwardedRef,\n RefAttributes,\n SyntheticEvent,\n} from \"react\";\nimport {\n forwardRef,\n Fragment,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from \"react\";\n\nimport type { GlobalComponents } from \"../components\";\nimport { ArrowDownIcon } from \"../icons/ArrowDown\";\nimport { BellIcon } from \"../icons/Bell\";\nimport { BellCrossedIcon } from \"../icons/BellCrossed\";\nimport { CheckCircleIcon } from \"../icons/CheckCircle\";\nimport { CheckCircleFillIcon } from \"../icons/CheckCircleFill\";\nimport type {\n CommentOverrides,\n ComposerOverrides,\n GlobalOverrides,\n ThreadOverrides,\n} from \"../overrides\";\nimport { findLastIndex } from \"@liveblocks/core\";\n\nimport { useOverrides } from \"../overrides\";\nimport { cn } from \"../utils/cn\";\nimport type { CommentProps } from \"./Comment\";\nimport { Comment } from \"./Comment\";\nimport type { ComposerProps } from \"./Composer\";\nimport { Composer } from \"./Composer\";\nimport { Button } from \"./internal/Button\";\nimport { DropdownItem } from \"./internal/Dropdown\";\nimport { Tooltip, TooltipProvider } from \"./internal/Tooltip\";\n\nexport interface ThreadProps<M extends BaseMetadata = DM>\n extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * The thread to display.\n */\n thread: ThreadData<M>;\n\n /**\n * How to show or hide the composer to reply to the thread.\n */\n showComposer?: boolean | \"collapsed\";\n\n /**\n * Whether to show the action to resolve the thread.\n */\n showResolveAction?: boolean;\n\n /**\n * How to show or hide the actions.\n */\n showActions?: CommentProps[\"showActions\"];\n\n /**\n * Whether to show reactions.\n */\n showReactions?: CommentProps[\"showReactions\"];\n\n /**\n * Whether to show the composer's formatting controls.\n */\n showComposerFormattingControls?: ComposerProps[\"showFormattingControls\"];\n\n /**\n * The maximum number of comments to show.\n *\n * The first and last comments are always shown and by default if some comments\n * are hidden, only the first comment will be shown before the \"show more\" button\n * and after it will be shown all the newest comments to fit the limit set.\n *\n * It's possible to customize this by setting `maxVisibleComments` to an object:\n *\n * @example\n * // Only show the last comment, and all the older ones to fit the limit.\n * <Thread maxVisibleComments={{ max: 5, show: \"oldest\" }} />\n *\n * @example\n * // Show as many old comments as new ones to fit the limit.\n * <Thread maxVisibleComments={{ max: 5, show: \"both\" }} />\n */\n maxVisibleComments?:\n | number\n | { max: number; show: \"oldest\" | \"both\" | \"newest\" };\n\n /**\n * Whether to blur the composer editor when the composer is submitted.\n */\n blurComposerOnSubmit?: ComposerProps[\"blurOnSubmit\"];\n\n /**\n * Whether to indent the comments' content.\n */\n indentCommentContent?: CommentProps[\"indentContent\"];\n\n /**\n * Whether to show deleted comments.\n */\n showDeletedComments?: CommentProps[\"showDeleted\"];\n\n /**\n * Whether to show attachments.\n */\n showAttachments?: boolean;\n\n /**\n * The event handler called when changing the resolved status.\n */\n onResolvedChange?: (resolved: boolean) => void;\n\n /**\n * The event handler called when a comment is edited.\n */\n onCommentEdit?: CommentProps[\"onCommentEdit\"];\n\n /**\n * The event handler called when a comment is deleted.\n */\n onCommentDelete?: CommentProps[\"onCommentDelete\"];\n\n /**\n * The event handler called when the thread is deleted.\n * A thread is deleted when all its comments are deleted.\n */\n onThreadDelete?: (thread: ThreadData<M>) => void;\n\n /**\n * The event handler called when clicking on a comment's author.\n */\n onAuthorClick?: CommentProps[\"onAuthorClick\"];\n\n /**\n * The event handler called when clicking on a mention.\n */\n onMentionClick?: CommentProps[\"onMentionClick\"];\n\n /**\n * The event handler called when clicking on a comment's attachment.\n */\n onAttachmentClick?: CommentProps[\"onAttachmentClick\"];\n\n /**\n * The event handler called when the composer is submitted.\n */\n onComposerSubmit?: ComposerProps[\"onComposerSubmit\"];\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<\n GlobalOverrides & ThreadOverrides & CommentOverrides & ComposerOverrides\n >;\n\n /**\n * Override the component's components.\n */\n components?: Partial<GlobalComponents>;\n}\n\n/**\n * Displays a thread of comments, with a composer to reply\n * to it.\n *\n * @example\n * <>\n * {threads.map((thread) => (\n * <Thread key={thread.id} thread={thread} />\n * ))}\n * </>\n */\nexport const Thread = forwardRef(\n <M extends BaseMetadata = DM>(\n {\n thread,\n indentCommentContent = true,\n showActions = \"hover\",\n showDeletedComments,\n showResolveAction = true,\n showReactions = true,\n showComposer = \"collapsed\",\n showAttachments = true,\n showComposerFormattingControls = true,\n maxVisibleComments,\n onResolvedChange,\n onCommentEdit,\n onCommentDelete,\n onThreadDelete,\n onAuthorClick,\n onMentionClick,\n onAttachmentClick,\n onComposerSubmit,\n blurComposerOnSubmit,\n overrides,\n components,\n className,\n ...props\n }: ThreadProps<M>,\n forwardedRef: ForwardedRef<HTMLDivElement>\n ) => {\n const markThreadAsResolved = useMarkRoomThreadAsResolved(thread.roomId);\n const markThreadAsUnresolved = useMarkRoomThreadAsUnresolved(thread.roomId);\n const $ = useOverrides(overrides);\n const [showAllComments, setShowAllComments] = useState(false);\n const firstCommentIndex = useMemo(() => {\n return showDeletedComments\n ? 0\n : thread.comments.findIndex((comment) => comment.body);\n }, [showDeletedComments, thread.comments]);\n const lastCommentIndex = useMemo(() => {\n return showDeletedComments\n ? thread.comments.length - 1\n : findLastIndex(thread.comments, (comment) => Boolean(comment.body));\n }, [showDeletedComments, thread.comments]);\n const hiddenComments = useMemo(() => {\n const maxVisibleCommentsCount =\n typeof maxVisibleComments === \"number\"\n ? maxVisibleComments\n : maxVisibleComments?.max;\n const visibleCommentsShow =\n (typeof maxVisibleComments === \"object\"\n ? maxVisibleComments?.show\n : undefined) ?? \"newest\";\n\n // If we explicitly want to show all comments or there's no limit set,\n // no need to hide any comments.\n if (showAllComments || maxVisibleCommentsCount === undefined) {\n return;\n }\n\n const comments = thread.comments\n .map((comment, index) => ({ comment, index }))\n .filter(({ comment }) => showDeletedComments || comment.body);\n\n // There aren't enough comments so no need to hide any.\n if (comments.length <= Math.max(maxVisibleCommentsCount, 2)) {\n return;\n }\n\n const firstVisibleComment = comments[0]!;\n const lastVisibleComment = comments[comments.length - 1]!;\n\n // Always show the first and last comments even if the limit is set to lower than 2.\n if (maxVisibleCommentsCount <= 2) {\n const firstHiddenCommentIndex =\n comments[1]?.index ?? firstVisibleComment.index;\n const lastHiddenCommentIndex =\n comments[comments.length - 2]?.index ?? lastVisibleComment.index;\n\n return {\n firstIndex: firstHiddenCommentIndex,\n lastIndex: lastHiddenCommentIndex,\n count: comments.slice(1, comments.length - 1).length,\n };\n }\n\n const remainingVisibleCommentsCount = maxVisibleCommentsCount - 2;\n\n // Split the remaining visible comments before, after, or equally.\n const beforeVisibleCommentsCount =\n visibleCommentsShow === \"oldest\"\n ? remainingVisibleCommentsCount\n : visibleCommentsShow === \"newest\"\n ? 0\n : Math.floor(remainingVisibleCommentsCount / 2);\n const afterVisibleCommentsCount =\n visibleCommentsShow === \"oldest\"\n ? 0\n : visibleCommentsShow === \"newest\"\n ? remainingVisibleCommentsCount\n : Math.ceil(remainingVisibleCommentsCount / 2);\n\n // The first comment is always visible so `+ 1` to skip it.\n const firstHiddenComment = comments[1 + beforeVisibleCommentsCount];\n // The last comment is always visible so `- 2` to skip it.\n const lastHiddenComment =\n comments[comments.length - 2 - afterVisibleCommentsCount];\n\n // There aren't any comments to hide besides the first and last ones.\n if (\n !firstHiddenComment ||\n !lastHiddenComment ||\n firstHiddenComment.index > lastHiddenComment.index\n ) {\n return;\n }\n\n return {\n firstIndex: firstHiddenComment.index,\n lastIndex: lastHiddenComment.index,\n count: thread.comments\n .slice(firstHiddenComment.index, lastHiddenComment.index + 1)\n .filter((comment) => showDeletedComments || comment.body).length,\n };\n }, [\n maxVisibleComments,\n showAllComments,\n showDeletedComments,\n thread.comments,\n ]);\n const {\n status: subscriptionStatus,\n unreadSince,\n subscribe,\n unsubscribe,\n } = useRoomThreadSubscription(thread.roomId, thread.id);\n const unreadIndex = useMemo(() => {\n // The user is not subscribed to this thread.\n if (subscriptionStatus !== \"subscribed\") {\n return;\n }\n\n // The user hasn't read the thread yet, so all comments are unread.\n if (unreadSince === null) {\n return firstCommentIndex;\n }\n\n // The user has read the thread, so we find the first unread comment.\n const unreadIndex = thread.comments.findIndex(\n (comment) =>\n (showDeletedComments ? true : comment.body) &&\n comment.createdAt > unreadSince\n );\n\n return unreadIndex >= 0 && unreadIndex < thread.comments.length\n ? unreadIndex\n : undefined;\n }, [\n firstCommentIndex,\n showDeletedComments,\n subscriptionStatus,\n thread.comments,\n unreadSince,\n ]);\n const [newIndex, setNewIndex] = useState<number>();\n const newIndicatorIndex = newIndex === undefined ? unreadIndex : newIndex;\n\n useEffect(() => {\n if (unreadIndex) {\n // Keep the \"new\" indicator at the lowest unread index.\n setNewIndex((persistedUnreadIndex) =>\n Math.min(persistedUnreadIndex ?? Infinity, unreadIndex)\n );\n }\n }, [unreadIndex]);\n\n const permissions = useRoomPermissions(thread.roomId);\n const canComment =\n permissions.size > 0\n ? permissions.has(Permission.CommentsWrite) ||\n permissions.has(Permission.Write)\n : true;\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n const handleResolvedChange = useCallback(\n (resolved: boolean) => {\n onResolvedChange?.(resolved);\n\n if (resolved) {\n markThreadAsResolved(thread.id);\n } else {\n markThreadAsUnresolved(thread.id);\n }\n },\n [\n markThreadAsResolved,\n markThreadAsUnresolved,\n onResolvedChange,\n thread.id,\n ]\n );\n\n const handleCommentDelete = useCallback(\n (comment: CommentData) => {\n onCommentDelete?.(comment);\n\n const filteredComments = thread.comments.filter(\n (comment) => comment.body\n );\n\n if (filteredComments.length <= 1) {\n onThreadDelete?.(thread);\n }\n },\n [onCommentDelete, onThreadDelete, thread]\n );\n\n const handleSubscribeChange = useCallback(() => {\n if (subscriptionStatus === \"subscribed\") {\n unsubscribe();\n } else {\n subscribe();\n }\n }, [subscriptionStatus, subscribe, unsubscribe]);\n\n return (\n <TooltipProvider>\n <div\n className={cn(\n \"lb-root lb-thread\",\n showActions === \"hover\" && \"lb-thread:show-actions-hover\",\n className\n )}\n data-resolved={thread.resolved ? \"\" : undefined}\n data-unread={unreadIndex !== undefined ? \"\" : undefined}\n dir={$.dir}\n {...props}\n ref={forwardedRef}\n >\n <div className=\"lb-thread-comments\">\n {thread.comments.map((comment, index) => {\n const isFirstComment = index === firstCommentIndex;\n const isUnread =\n unreadIndex !== undefined && index >= unreadIndex;\n const isHidden =\n hiddenComments &&\n index >= hiddenComments.firstIndex &&\n index <= hiddenComments.lastIndex;\n const isFirstHiddenComment =\n isHidden && index === hiddenComments.firstIndex;\n\n if (isFirstHiddenComment) {\n return (\n <div\n key={`${comment.id}-show-more`}\n className=\"lb-thread-show-more\"\n >\n <Button\n variant=\"ghost\"\n className=\"lb-thread-show-more-button\"\n onClick={() => setShowAllComments(true)}\n >\n {$.THREAD_SHOW_MORE_COMMENTS(hiddenComments.count)}\n </Button>\n </div>\n );\n }\n\n if (isHidden) {\n return null;\n }\n\n const children = (\n <Comment\n key={comment.id}\n overrides={overrides}\n className=\"lb-thread-comment\"\n data-unread={isUnread ? \"\" : undefined}\n comment={comment}\n indentContent={indentCommentContent}\n showDeleted={showDeletedComments}\n showActions={showActions}\n showReactions={showReactions}\n showAttachments={showAttachments}\n showComposerFormattingControls={\n showComposerFormattingControls\n }\n onCommentEdit={onCommentEdit}\n onCommentDelete={handleCommentDelete}\n onAuthorClick={onAuthorClick}\n onMentionClick={onMentionClick}\n onAttachmentClick={onAttachmentClick}\n components={components}\n autoMarkReadThreadId={\n index === lastCommentIndex && isUnread\n ? thread.id\n : undefined\n }\n additionalActionsClassName={\n isFirstComment ? \"lb-thread-actions\" : undefined\n }\n additionalActions={\n isFirstComment && showResolveAction ? (\n <Tooltip\n content={\n thread.resolved\n ? $.THREAD_UNRESOLVE\n : $.THREAD_RESOLVE\n }\n >\n <TogglePrimitive.Root\n pressed={thread.resolved}\n onPressedChange={handleResolvedChange}\n asChild\n >\n <Button\n className=\"lb-comment-action\"\n onClick={stopPropagation}\n aria-label={\n thread.resolved\n ? $.THREAD_UNRESOLVE\n : $.THREAD_RESOLVE\n }\n icon={\n thread.resolved ? (\n <CheckCircleFillIcon />\n ) : (\n <CheckCircleIcon />\n )\n }\n disabled={!canComment}\n />\n </TogglePrimitive.Root>\n </Tooltip>\n ) : null\n }\n additionalDropdownItemsBefore={\n isFirstComment ? (\n <DropdownItem\n onSelect={handleSubscribeChange}\n onClick={stopPropagation}\n icon={\n subscriptionStatus === \"subscribed\" ? (\n <BellCrossedIcon />\n ) : (\n <BellIcon />\n )\n }\n >\n {subscriptionStatus === \"subscribed\"\n ? $.THREAD_UNSUBSCRIBE\n : $.THREAD_SUBSCRIBE}\n </DropdownItem>\n ) : null\n }\n />\n );\n\n return index === newIndicatorIndex &&\n newIndicatorIndex !== firstCommentIndex &&\n newIndicatorIndex <= lastCommentIndex ? (\n <Fragment key={comment.id}>\n <div\n className=\"lb-thread-new-indicator\"\n aria-label={$.THREAD_NEW_INDICATOR_DESCRIPTION}\n >\n <span className=\"lb-thread-new-indicator-label\">\n <ArrowDownIcon className=\"lb-thread-new-indicator-label-icon\" />\n {$.THREAD_NEW_INDICATOR}\n </span>\n </div>\n {children}\n </Fragment>\n ) : (\n children\n );\n })}\n </div>\n {showComposer && (\n <Composer\n className=\"lb-thread-composer\"\n threadId={thread.id}\n defaultCollapsed={showComposer === \"collapsed\" ? true : undefined}\n showAttachments={showAttachments}\n showFormattingControls={showComposerFormattingControls}\n onComposerSubmit={onComposerSubmit}\n blurOnSubmit={blurComposerOnSubmit}\n overrides={{\n COMPOSER_PLACEHOLDER: $.THREAD_COMPOSER_PLACEHOLDER,\n COMPOSER_SEND: $.THREAD_COMPOSER_SEND,\n ...overrides,\n }}\n roomId={thread.roomId}\n />\n )}\n </div>\n </TooltipProvider>\n );\n }\n) as <M extends BaseMetadata = DM>(\n props: ThreadProps<M> & RefAttributes<HTMLDivElement>\n) => JSX.Element;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;AAiMO;AAAe;AAElB;AACE;AACuB;AACT;AACd;AACoB;AACJ;AACD;AACG;AACe;AACjC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AACA;AACE;AAEuD;AAEzD;AACE;AAEqE;AAEvE;AACE;AAIA;AAOA;AACE;AAAA;AAGF;AAKA;AACE;AAAA;AAGF;AACA;AAGA;AACE;AAEA;AAGA;AAAO;AACO;AACD;AACmC;AAChD;AAGF;AAGA;AAMA;AAQA;AAEA;AAIA;AAKE;AAAA;AAGF;AAAO;AAC0B;AACF;AAG+B;AAC9D;AACC;AACD;AACA;AACA;AACO;AAET;AAAM;AACI;AACR;AACA;AACA;AAEF;AAEE;AACE;AAAA;AAIF;AACE;AAAO;AAIT;AAAoC;AAGZ;AAGxB;AAEI;AACH;AACD;AACA;AACA;AACO;AACP;AAEF;AACA;AAEA;AACE;AAEE;AAAA;AACwD;AACxD;AACF;AAGF;AACA;AAMA;AACE;AAAsB;AAGxB;AAA6B;AAEzB;AAEA;AACE;AAA8B;AAE9B;AAAgC;AAClC;AACF;AACA;AACE;AACA;AACA;AACO;AACT;AAGF;AAA4B;AAExB;AAEA;AAAyC;AAClB;AAGvB;AACE;AAAuB;AACzB;AACF;AACwC;AAG1C;AACE;AACE;AAAY;AAEZ;AAAU;AACZ;AAGF;AACG;AACE;AACY;AACT;AAC2B;AAC3B;AACF;AACsC;AACQ;AACvC;AACH;AACC;AAEL;AAAC;AAAc;AAEX;AACA;AAEA;AAIA;AAGA;AACE;AACG;AAEW;AAET;AACS;AACE;AAC4B;AAEW;AACnD;AACF;AAIJ;AACE;AAAO;AAGT;AACG;AAEC;AACU;AACmB;AAC7B;AACe;AACF;AACb;AACA;AACA;AACA;AAGA;AACiB;AACjB;AACA;AACA;AACA;AAIM;AAGmC;AAIpC;AAIS;AAGP;AACiB;AACC;AACV;AAEN;AACW;AACD;AAID;AAMa;AAGV;AACb;AACF;AAEA;AAID;AACW;AACD;AAKK;AAMR;AAEN;AAKV;AAGG;AACC;AAAC;AACW;AACI;AAEb;AAAe;AACd;AAAC;AAAwB;AAAqC;AAC3D;AAAA;AACL;AACF;AACC;AAAA;AAGH;AAEH;AACH;AAEG;AACW;AACO;AACuC;AACxD;AACwB;AACxB;AACc;AACH;AACe;AACP;AACd;AACL;AACe;AACjB;AAAA;AAEJ;AACF;AAGN;;"}
|
|
1
|
+
{"version":3,"file":"Thread.js","sources":["../../src/components/Thread.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n type BaseMetadata,\n type CommentData,\n type DM,\n Permission,\n type ThreadData,\n} from \"@liveblocks/core\";\nimport { findLastIndex } from \"@liveblocks/core\";\nimport {\n useMarkRoomThreadAsResolved,\n useMarkRoomThreadAsUnresolved,\n useRoomPermissions,\n useRoomThreadSubscription,\n} from \"@liveblocks/react/_private\";\nimport * as TogglePrimitive from \"@radix-ui/react-toggle\";\nimport type {\n ComponentPropsWithoutRef,\n ForwardedRef,\n RefAttributes,\n SyntheticEvent,\n} from \"react\";\nimport {\n forwardRef,\n Fragment,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from \"react\";\n\nimport type { GlobalComponents } from \"../components\";\nimport { ArrowDownIcon } from \"../icons/ArrowDown\";\nimport { BellIcon } from \"../icons/Bell\";\nimport { BellCrossedIcon } from \"../icons/BellCrossed\";\nimport { CheckCircleIcon } from \"../icons/CheckCircle\";\nimport { CheckCircleFillIcon } from \"../icons/CheckCircleFill\";\nimport type {\n CommentOverrides,\n ComposerOverrides,\n GlobalOverrides,\n ThreadOverrides,\n} from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport { cn } from \"../utils/cn\";\nimport type { CommentProps } from \"./Comment\";\nimport { Comment } from \"./Comment\";\nimport type { ComposerProps } from \"./Composer\";\nimport { Composer } from \"./Composer\";\nimport { Button } from \"./internal/Button\";\nimport { DropdownItem } from \"./internal/Dropdown\";\nimport { Tooltip, TooltipProvider } from \"./internal/Tooltip\";\n\nexport interface ThreadProps<M extends BaseMetadata = DM>\n extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * The thread to display.\n */\n thread: ThreadData<M>;\n\n /**\n * How to show or hide the composer to reply to the thread.\n */\n showComposer?: boolean | \"collapsed\";\n\n /**\n * Whether to show the action to resolve the thread.\n */\n showResolveAction?: boolean;\n\n /**\n * How to show or hide the actions.\n */\n showActions?: CommentProps[\"showActions\"];\n\n /**\n * Whether to show reactions.\n */\n showReactions?: CommentProps[\"showReactions\"];\n\n /**\n * Whether to show the composer's formatting controls.\n */\n showComposerFormattingControls?: ComposerProps[\"showFormattingControls\"];\n\n /**\n * The maximum number of comments to show.\n *\n * The first and last comments are always shown and by default if some comments\n * are hidden, only the first comment will be shown before the \"show more\" button\n * and after it will be shown all the newest comments to fit the limit set.\n *\n * It's possible to customize this by setting `maxVisibleComments` to an object:\n *\n * @example\n * // Only show the last comment, and all the older ones to fit the limit.\n * <Thread maxVisibleComments={{ max: 5, show: \"oldest\" }} />\n *\n * @example\n * // Show as many old comments as new ones to fit the limit.\n * <Thread maxVisibleComments={{ max: 5, show: \"both\" }} />\n */\n maxVisibleComments?:\n | number\n | { max: number; show: \"oldest\" | \"both\" | \"newest\" };\n\n /**\n * Whether to blur the composer editor when the composer is submitted.\n */\n blurComposerOnSubmit?: ComposerProps[\"blurOnSubmit\"];\n\n /**\n * Whether to indent the comments' content.\n */\n indentCommentContent?: CommentProps[\"indentContent\"];\n\n /**\n * Whether to show deleted comments.\n */\n showDeletedComments?: CommentProps[\"showDeleted\"];\n\n /**\n * Whether to show attachments.\n */\n showAttachments?: boolean;\n\n /**\n * The event handler called when changing the resolved status.\n */\n onResolvedChange?: (resolved: boolean) => void;\n\n /**\n * The event handler called when a comment is edited.\n */\n onCommentEdit?: CommentProps[\"onCommentEdit\"];\n\n /**\n * The event handler called when a comment is deleted.\n */\n onCommentDelete?: CommentProps[\"onCommentDelete\"];\n\n /**\n * The event handler called when the thread is deleted.\n * A thread is deleted when all its comments are deleted.\n */\n onThreadDelete?: (thread: ThreadData<M>) => void;\n\n /**\n * The event handler called when clicking on a comment's author.\n */\n onAuthorClick?: CommentProps[\"onAuthorClick\"];\n\n /**\n * The event handler called when clicking on a mention.\n */\n onMentionClick?: CommentProps[\"onMentionClick\"];\n\n /**\n * The event handler called when clicking on a comment's attachment.\n */\n onAttachmentClick?: CommentProps[\"onAttachmentClick\"];\n\n /**\n * The event handler called when the composer is submitted.\n */\n onComposerSubmit?: ComposerProps[\"onComposerSubmit\"];\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<\n GlobalOverrides & ThreadOverrides & CommentOverrides & ComposerOverrides\n >;\n\n /**\n * Override the component's components.\n */\n components?: Partial<GlobalComponents>;\n}\n\n/**\n * Displays a thread of comments, with a composer to reply\n * to it.\n *\n * @example\n * <>\n * {threads.map((thread) => (\n * <Thread key={thread.id} thread={thread} />\n * ))}\n * </>\n */\nexport const Thread = forwardRef(\n <M extends BaseMetadata = DM>(\n {\n thread,\n indentCommentContent = true,\n showActions = \"hover\",\n showDeletedComments,\n showResolveAction = true,\n showReactions = true,\n showComposer = \"collapsed\",\n showAttachments = true,\n showComposerFormattingControls = true,\n maxVisibleComments,\n onResolvedChange,\n onCommentEdit,\n onCommentDelete,\n onThreadDelete,\n onAuthorClick,\n onMentionClick,\n onAttachmentClick,\n onComposerSubmit,\n blurComposerOnSubmit,\n overrides,\n components,\n className,\n ...props\n }: ThreadProps<M>,\n forwardedRef: ForwardedRef<HTMLDivElement>\n ) => {\n const markThreadAsResolved = useMarkRoomThreadAsResolved(thread.roomId);\n const markThreadAsUnresolved = useMarkRoomThreadAsUnresolved(thread.roomId);\n const $ = useOverrides(overrides);\n const [showAllComments, setShowAllComments] = useState(false);\n const firstCommentIndex = useMemo(() => {\n return showDeletedComments\n ? 0\n : thread.comments.findIndex((comment) => comment.body);\n }, [showDeletedComments, thread.comments]);\n const lastCommentIndex = useMemo(() => {\n return showDeletedComments\n ? thread.comments.length - 1\n : findLastIndex(thread.comments, (comment) => Boolean(comment.body));\n }, [showDeletedComments, thread.comments]);\n const hiddenComments = useMemo(() => {\n const maxVisibleCommentsCount =\n typeof maxVisibleComments === \"number\"\n ? maxVisibleComments\n : maxVisibleComments?.max;\n const visibleCommentsShow =\n (typeof maxVisibleComments === \"object\"\n ? maxVisibleComments?.show\n : undefined) ?? \"newest\";\n\n // If we explicitly want to show all comments or there's no limit set,\n // no need to hide any comments.\n if (showAllComments || maxVisibleCommentsCount === undefined) {\n return;\n }\n\n const comments = thread.comments\n .map((comment, index) => ({ comment, index }))\n .filter(({ comment }) => showDeletedComments || comment.body);\n\n // There aren't enough comments so no need to hide any.\n if (comments.length <= Math.max(maxVisibleCommentsCount, 2)) {\n return;\n }\n\n const firstVisibleComment = comments[0]!;\n const lastVisibleComment = comments[comments.length - 1]!;\n\n // Always show the first and last comments even if the limit is set to lower than 2.\n if (maxVisibleCommentsCount <= 2) {\n const firstHiddenCommentIndex =\n comments[1]?.index ?? firstVisibleComment.index;\n const lastHiddenCommentIndex =\n comments[comments.length - 2]?.index ?? lastVisibleComment.index;\n\n return {\n firstIndex: firstHiddenCommentIndex,\n lastIndex: lastHiddenCommentIndex,\n count: comments.slice(1, comments.length - 1).length,\n };\n }\n\n const remainingVisibleCommentsCount = maxVisibleCommentsCount - 2;\n\n // Split the remaining visible comments before, after, or equally.\n const beforeVisibleCommentsCount =\n visibleCommentsShow === \"oldest\"\n ? remainingVisibleCommentsCount\n : visibleCommentsShow === \"newest\"\n ? 0\n : Math.floor(remainingVisibleCommentsCount / 2);\n const afterVisibleCommentsCount =\n visibleCommentsShow === \"oldest\"\n ? 0\n : visibleCommentsShow === \"newest\"\n ? remainingVisibleCommentsCount\n : Math.ceil(remainingVisibleCommentsCount / 2);\n\n // The first comment is always visible so `+ 1` to skip it.\n const firstHiddenComment = comments[1 + beforeVisibleCommentsCount];\n // The last comment is always visible so `- 2` to skip it.\n const lastHiddenComment =\n comments[comments.length - 2 - afterVisibleCommentsCount];\n\n // There aren't any comments to hide besides the first and last ones.\n if (\n !firstHiddenComment ||\n !lastHiddenComment ||\n firstHiddenComment.index > lastHiddenComment.index\n ) {\n return;\n }\n\n return {\n firstIndex: firstHiddenComment.index,\n lastIndex: lastHiddenComment.index,\n count: thread.comments\n .slice(firstHiddenComment.index, lastHiddenComment.index + 1)\n .filter((comment) => showDeletedComments || comment.body).length,\n };\n }, [\n maxVisibleComments,\n showAllComments,\n showDeletedComments,\n thread.comments,\n ]);\n const {\n status: subscriptionStatus,\n unreadSince,\n subscribe,\n unsubscribe,\n } = useRoomThreadSubscription(thread.roomId, thread.id);\n const unreadIndex = useMemo(() => {\n // The user is not subscribed to this thread.\n if (subscriptionStatus !== \"subscribed\") {\n return;\n }\n\n // The user hasn't read the thread yet, so all comments are unread.\n if (unreadSince === null) {\n return firstCommentIndex;\n }\n\n // The user has read the thread, so we find the first unread comment.\n const unreadIndex = thread.comments.findIndex(\n (comment) =>\n (showDeletedComments ? true : comment.body) &&\n comment.createdAt > unreadSince\n );\n\n return unreadIndex >= 0 && unreadIndex < thread.comments.length\n ? unreadIndex\n : undefined;\n }, [\n firstCommentIndex,\n showDeletedComments,\n subscriptionStatus,\n thread.comments,\n unreadSince,\n ]);\n const [newIndex, setNewIndex] = useState<number>();\n const newIndicatorIndex = newIndex === undefined ? unreadIndex : newIndex;\n\n useEffect(() => {\n if (unreadIndex) {\n // Keep the \"new\" indicator at the lowest unread index.\n setNewIndex((persistedUnreadIndex) =>\n Math.min(persistedUnreadIndex ?? Infinity, unreadIndex)\n );\n }\n }, [unreadIndex]);\n\n const permissions = useRoomPermissions(thread.roomId);\n const canComment =\n permissions.size > 0\n ? permissions.has(Permission.CommentsWrite) ||\n permissions.has(Permission.Write)\n : true;\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n const handleResolvedChange = useCallback(\n (resolved: boolean) => {\n onResolvedChange?.(resolved);\n\n if (resolved) {\n markThreadAsResolved(thread.id);\n } else {\n markThreadAsUnresolved(thread.id);\n }\n },\n [\n markThreadAsResolved,\n markThreadAsUnresolved,\n onResolvedChange,\n thread.id,\n ]\n );\n\n const handleCommentDelete = useCallback(\n (comment: CommentData) => {\n onCommentDelete?.(comment);\n\n const filteredComments = thread.comments.filter(\n (comment) => comment.body\n );\n\n if (filteredComments.length <= 1) {\n onThreadDelete?.(thread);\n }\n },\n [onCommentDelete, onThreadDelete, thread]\n );\n\n const handleSubscribeChange = useCallback(() => {\n if (subscriptionStatus === \"subscribed\") {\n unsubscribe();\n } else {\n subscribe();\n }\n }, [subscriptionStatus, subscribe, unsubscribe]);\n\n return (\n <TooltipProvider>\n <div\n className={cn(\n \"lb-root lb-thread\",\n showActions === \"hover\" && \"lb-thread:show-actions-hover\",\n className\n )}\n data-resolved={thread.resolved ? \"\" : undefined}\n data-unread={unreadIndex !== undefined ? \"\" : undefined}\n dir={$.dir}\n {...props}\n ref={forwardedRef}\n >\n <div className=\"lb-thread-comments\">\n {thread.comments.map((comment, index) => {\n const isFirstComment = index === firstCommentIndex;\n const isUnread =\n unreadIndex !== undefined && index >= unreadIndex;\n const isHidden =\n hiddenComments &&\n index >= hiddenComments.firstIndex &&\n index <= hiddenComments.lastIndex;\n const isFirstHiddenComment =\n isHidden && index === hiddenComments.firstIndex;\n\n if (isFirstHiddenComment) {\n return (\n <div\n key={`${comment.id}-show-more`}\n className=\"lb-thread-show-more\"\n >\n <Button\n variant=\"ghost\"\n className=\"lb-thread-show-more-button\"\n onClick={() => setShowAllComments(true)}\n >\n {$.THREAD_SHOW_MORE_COMMENTS(hiddenComments.count)}\n </Button>\n </div>\n );\n }\n\n if (isHidden) {\n return null;\n }\n\n const children = (\n <Comment\n key={comment.id}\n overrides={overrides}\n className=\"lb-thread-comment\"\n data-unread={isUnread ? \"\" : undefined}\n comment={comment}\n indentContent={indentCommentContent}\n showDeleted={showDeletedComments}\n showActions={showActions}\n showReactions={showReactions}\n showAttachments={showAttachments}\n showComposerFormattingControls={\n showComposerFormattingControls\n }\n onCommentEdit={onCommentEdit}\n onCommentDelete={handleCommentDelete}\n onAuthorClick={onAuthorClick}\n onMentionClick={onMentionClick}\n onAttachmentClick={onAttachmentClick}\n components={components}\n autoMarkReadThreadId={\n index === lastCommentIndex && isUnread\n ? thread.id\n : undefined\n }\n additionalActionsClassName={\n isFirstComment ? \"lb-thread-actions\" : undefined\n }\n additionalActions={\n isFirstComment && showResolveAction ? (\n <Tooltip\n content={\n thread.resolved\n ? $.THREAD_UNRESOLVE\n : $.THREAD_RESOLVE\n }\n >\n <TogglePrimitive.Root\n pressed={thread.resolved}\n onPressedChange={handleResolvedChange}\n asChild\n >\n <Button\n className=\"lb-comment-action\"\n onClick={stopPropagation}\n aria-label={\n thread.resolved\n ? $.THREAD_UNRESOLVE\n : $.THREAD_RESOLVE\n }\n icon={\n thread.resolved ? (\n <CheckCircleFillIcon />\n ) : (\n <CheckCircleIcon />\n )\n }\n disabled={!canComment}\n />\n </TogglePrimitive.Root>\n </Tooltip>\n ) : null\n }\n additionalDropdownItemsBefore={\n isFirstComment ? (\n <DropdownItem\n onSelect={handleSubscribeChange}\n onClick={stopPropagation}\n icon={\n subscriptionStatus === \"subscribed\" ? (\n <BellCrossedIcon />\n ) : (\n <BellIcon />\n )\n }\n >\n {subscriptionStatus === \"subscribed\"\n ? $.THREAD_UNSUBSCRIBE\n : $.THREAD_SUBSCRIBE}\n </DropdownItem>\n ) : null\n }\n />\n );\n\n return index === newIndicatorIndex &&\n newIndicatorIndex !== firstCommentIndex &&\n newIndicatorIndex <= lastCommentIndex ? (\n <Fragment key={comment.id}>\n <div\n className=\"lb-thread-new-indicator\"\n aria-label={$.THREAD_NEW_INDICATOR_DESCRIPTION}\n >\n <span className=\"lb-thread-new-indicator-label\">\n <ArrowDownIcon className=\"lb-thread-new-indicator-label-icon\" />\n {$.THREAD_NEW_INDICATOR}\n </span>\n </div>\n {children}\n </Fragment>\n ) : (\n children\n );\n })}\n </div>\n {showComposer && (\n <Composer\n className=\"lb-thread-composer\"\n threadId={thread.id}\n defaultCollapsed={showComposer === \"collapsed\" ? true : undefined}\n showAttachments={showAttachments}\n showFormattingControls={showComposerFormattingControls}\n onComposerSubmit={onComposerSubmit}\n blurOnSubmit={blurComposerOnSubmit}\n overrides={{\n COMPOSER_PLACEHOLDER: $.THREAD_COMPOSER_PLACEHOLDER,\n COMPOSER_SEND: $.THREAD_COMPOSER_SEND,\n ...overrides,\n }}\n roomId={thread.roomId}\n />\n )}\n </div>\n </TooltipProvider>\n );\n }\n) as <M extends BaseMetadata = DM>(\n props: ThreadProps<M> & RefAttributes<HTMLDivElement>\n) => JSX.Element;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;AAgMO;AAAe;AAElB;AACE;AACuB;AACT;AACd;AACoB;AACJ;AACD;AACG;AACe;AACjC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AACA;AACE;AAEuD;AAEzD;AACE;AAEqE;AAEvE;AACE;AAIA;AAOA;AACE;AAAA;AAGF;AAKA;AACE;AAAA;AAGF;AACA;AAGA;AACE;AAEA;AAGA;AAAO;AACO;AACD;AACmC;AAChD;AAGF;AAGA;AAMA;AAQA;AAEA;AAIA;AAKE;AAAA;AAGF;AAAO;AAC0B;AACF;AAG+B;AAC9D;AACC;AACD;AACA;AACA;AACO;AAET;AAAM;AACI;AACR;AACA;AACA;AAEF;AAEE;AACE;AAAA;AAIF;AACE;AAAO;AAIT;AAAoC;AAGZ;AAGxB;AAEI;AACH;AACD;AACA;AACA;AACO;AACP;AAEF;AACA;AAEA;AACE;AAEE;AAAA;AACwD;AACxD;AACF;AAGF;AACA;AAMA;AACE;AAAsB;AAGxB;AAA6B;AAEzB;AAEA;AACE;AAA8B;AAE9B;AAAgC;AAClC;AACF;AACA;AACE;AACA;AACA;AACO;AACT;AAGF;AAA4B;AAExB;AAEA;AAAyC;AAClB;AAGvB;AACE;AAAuB;AACzB;AACF;AACwC;AAG1C;AACE;AACE;AAAY;AAEZ;AAAU;AACZ;AAGF;AACG;AACE;AACY;AACT;AAC2B;AAC3B;AACF;AACsC;AACQ;AACvC;AACH;AACC;AAEL;AAAC;AAAc;AAEX;AACA;AAEA;AAIA;AAGA;AACE;AACG;AAEW;AAET;AACS;AACE;AAC4B;AAEW;AACnD;AACF;AAIJ;AACE;AAAO;AAGT;AACG;AAEC;AACU;AACmB;AAC7B;AACe;AACF;AACb;AACA;AACA;AACA;AAGA;AACiB;AACjB;AACA;AACA;AACA;AAIM;AAGmC;AAIpC;AAIS;AAGP;AACiB;AACC;AACV;AAEN;AACW;AACD;AAID;AAMa;AAGV;AACb;AACF;AAEA;AAID;AACW;AACD;AAKK;AAMR;AAEN;AAKV;AAGG;AACC;AAAC;AACW;AACI;AAEb;AAAe;AACd;AAAC;AAAwB;AAAqC;AAC3D;AAAA;AACL;AACF;AACC;AAAA;AAGH;AAEH;AACH;AAEG;AACW;AACO;AACuC;AACxD;AACwB;AACxB;AACc;AACH;AACe;AACP;AACd;AACL;AACe;AACjB;AAAA;AAEJ;AACF;AAGN;;"}
|
|
@@ -85,17 +85,27 @@ function AssistantMessageContent({
|
|
|
85
85
|
message,
|
|
86
86
|
components
|
|
87
87
|
}) {
|
|
88
|
+
const ref = react.useRef(components);
|
|
89
|
+
const BoundTextPart = react.useMemo(
|
|
90
|
+
() => (props) => /* @__PURE__ */ jsxRuntime.jsx(TextPart, {
|
|
91
|
+
...props,
|
|
92
|
+
components: ref.current
|
|
93
|
+
}),
|
|
94
|
+
[]
|
|
95
|
+
);
|
|
96
|
+
const BoundReasoningPart = react.useMemo(
|
|
97
|
+
() => (props) => /* @__PURE__ */ jsxRuntime.jsx(ReasoningPart, {
|
|
98
|
+
...props,
|
|
99
|
+
components: ref.current
|
|
100
|
+
}),
|
|
101
|
+
[]
|
|
102
|
+
);
|
|
88
103
|
return /* @__PURE__ */ jsxRuntime.jsx(index.Content, {
|
|
89
104
|
message,
|
|
90
105
|
components: {
|
|
91
|
-
TextPart:
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}),
|
|
95
|
-
ReasoningPart: (props) => /* @__PURE__ */ jsxRuntime.jsx(ReasoningPart, {
|
|
96
|
-
...props,
|
|
97
|
-
components
|
|
98
|
-
}),
|
|
106
|
+
TextPart: BoundTextPart,
|
|
107
|
+
ReasoningPart: BoundReasoningPart,
|
|
108
|
+
RetrievalPart,
|
|
99
109
|
ToolInvocationPart
|
|
100
110
|
},
|
|
101
111
|
className: "lb-ai-chat-message-content"
|
|
@@ -128,7 +138,7 @@ function ReasoningPart({ part, isStreaming, components }) {
|
|
|
128
138
|
isStreaming && "lb-ai-chat-pending"
|
|
129
139
|
),
|
|
130
140
|
children: [
|
|
131
|
-
$.AI_CHAT_MESSAGE_REASONING(isStreaming),
|
|
141
|
+
$.AI_CHAT_MESSAGE_REASONING(isStreaming, part),
|
|
132
142
|
/* @__PURE__ */ jsxRuntime.jsx("span", {
|
|
133
143
|
className: "lb-collapsible-chevron lb-icon-container",
|
|
134
144
|
children: /* @__PURE__ */ jsxRuntime.jsx(ChevronRight.ChevronRightIcon, {})
|
|
@@ -146,6 +156,16 @@ function ReasoningPart({ part, isStreaming, components }) {
|
|
|
146
156
|
]
|
|
147
157
|
});
|
|
148
158
|
}
|
|
159
|
+
function RetrievalPart({ part, isStreaming }) {
|
|
160
|
+
const $ = overrides.useOverrides();
|
|
161
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
162
|
+
className: cn.cn(
|
|
163
|
+
"lb-ai-chat-message-retrieval",
|
|
164
|
+
isStreaming && "lb-ai-chat-pending"
|
|
165
|
+
),
|
|
166
|
+
children: $.AI_CHAT_MESSAGE_RETRIEVAL(isStreaming, part)
|
|
167
|
+
});
|
|
168
|
+
}
|
|
149
169
|
function ToolInvocationPart({
|
|
150
170
|
part,
|
|
151
171
|
message
|
|
@@ -1 +1 @@
|
|
|
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 ReactNode,\n useEffect,\n useState,\n} from \"react\";\n\nimport { 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 { AiMessageToolInvocation } from \"../../primitives/AiMessage/tool-invocation\";\nimport type {\n AiMessageContentReasoningPartProps,\n AiMessageContentTextPartProps,\n AiMessageContentToolInvocationPartProps,\n} from \"../../primitives/AiMessage/types\";\nimport * as Collapsible from \"../../primitives/Collapsible\";\nimport type { MarkdownComponents } from \"../../primitives/Markdown\";\nimport { cn } from \"../../utils/cn\";\nimport { ErrorBoundary } from \"../../utils/ErrorBoundary\";\nimport { Prose } from \"./Prose\";\n\ntype UiAssistantMessage = WithNavigation<AiAssistantMessage>;\n\ntype AiChatAssistantMessageComponents = {\n /**\n * The components used to render Markdown content.\n */\n markdown?: Partial<MarkdownComponents>;\n};\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 & AiChatAssistantMessageComponents>;\n}\n\ninterface TextPartProps extends AiMessageContentTextPartProps {\n components?: Partial<GlobalComponents & AiChatAssistantMessageComponents>;\n}\n\ninterface ReasoningPartProps extends AiMessageContentReasoningPartProps {\n components?: Partial<GlobalComponents & AiChatAssistantMessageComponents>;\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 = (\n <AssistantMessageContent\n message={message}\n components={components}\n />\n );\n }\n } else if (message.status === \"completed\") {\n children = (\n <AssistantMessageContent message={message} components={components} />\n );\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 = (\n <AssistantMessageContent\n message={message}\n components={components}\n />\n );\n } else {\n children = (\n <>\n <AssistantMessageContent\n message={message}\n components={components}\n />\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={cn(\n \"lb-ai-chat-message lb-ai-chat-assistant-message\",\n className\n )}\n {...props}\n ref={forwardedRef}\n >\n <OverridesProvider overrides={overrides}>\n {children}\n </OverridesProvider>\n </div>\n );\n }\n )\n);\n\nfunction AssistantMessageContent({\n message,\n components,\n}: {\n message: UiAssistantMessage;\n components?: Partial<GlobalComponents & AiChatAssistantMessageComponents>;\n}) {\n return (\n <AiMessage.Content\n message={message}\n components={{\n TextPart: (props) => <TextPart {...props} components={components} />,\n ReasoningPart: (props) => (\n <ReasoningPart {...props} components={components} />\n ),\n ToolInvocationPart,\n }}\n className=\"lb-ai-chat-message-content\"\n />\n );\n}\n\n/* -------------------------------------------------------------------------------------------------\n * TextPart\n * -----------------------------------------------------------------------------------------------*/\nfunction TextPart({ part, components, isStreaming }: TextPartProps) {\n return (\n <Prose\n content={part.text}\n className=\"lb-ai-chat-message-text\"\n components={components}\n partial={isStreaming}\n />\n );\n}\n\n/* -------------------------------------------------------------------------------------------------\n * ReasoningPart\n * -----------------------------------------------------------------------------------------------*/\nfunction ReasoningPart({ part, isStreaming, components }: ReasoningPartProps) {\n // Start collapsed if reasoning is already done.\n const [isOpen, setIsOpen] = useState(isStreaming);\n const $ = useOverrides();\n\n // Auto-collapse when reasoning is done, while still allowing the user to\n // open/collapse it manually during and after it's done.\n useEffect(() => {\n if (!isStreaming) {\n setIsOpen(false);\n }\n }, [isStreaming]);\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={cn(\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\n content={part.text}\n partial={isStreaming}\n components={components}\n />\n </Collapsible.Content>\n </Collapsible.Root>\n );\n}\n\n/* -------------------------------------------------------------------------------------------------\n * ToolInvocationPart\n * -----------------------------------------------------------------------------------------------*/\nfunction ToolInvocationPart({\n part,\n message,\n}: AiMessageContentToolInvocationPartProps) {\n return (\n <div className=\"lb-ai-chat-message-tool-invocation\">\n <ErrorBoundary\n fallback={\n <div className=\"lb-ai-chat-message-error\">\n <span className=\"lb-icon-container\">\n <WarningIcon />\n </span>\n <p>\n Failed to render tool call result for <code>{part.name}</code>.\n See console for details.\n </p>\n </div>\n }\n >\n <AiMessageToolInvocation part={part} message={message} />\n </ErrorBoundary>\n </div>\n );\n}\n"],"names":["memo","forwardRef","overrides","useOverrides","jsx","jsxs","Fragment","WarningIcon","cn","OverridesProvider","AiMessage.Content","Prose","useState","useEffect","Collapsible.Root","Collapsible.Trigger","ChevronRightIcon","Collapsible.Content","ErrorBoundary","AiMessageToolInvocation"],"mappings":";;;;;;;;;;;;;;AAqEO,MAAM,sBAAyB,GAAAA,UAAA;AAAA,EACpCC,gBAAA;AAAA,IACE,CAAC,EAAE,OAAS,EAAA,SAAA,aAAWC,aAAW,UAAe,EAAA,GAAA,KAAA,IAAS,YAAiB,KAAA;AACzE,MAAM,MAAA,CAAA,GAAIC,uBAAaD,WAAS,CAAA,CAAA;AAEhC,MAAA,IAAI,QAAsB,GAAA,IAAA,CAAA;AAE1B,MAAI,IAAA,OAAA,CAAQ,cAAc,KAAW,CAAA,EAAA;AACnC,QAAA,QAAA,mBACGE,cAAA,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,mBACGA,cAAA,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,mBACGA,cAAA,CAAA,uBAAA,EAAA;AAAA,YACC,OAAA;AAAA,YACA,UAAA;AAAA,WACF,CAAA,CAAA;AAAA,SAEJ;AAAA,OACF,MAAA,IAAW,OAAQ,CAAA,MAAA,KAAW,WAAa,EAAA;AACzC,QAAA,QAAA,mBACGA,cAAA,CAAA,uBAAA,EAAA;AAAA,UAAwB,OAAA;AAAA,UAAkB,UAAA;AAAA,SAAwB,CAAA,CAAA;AAAA,OAEvE,MAAA,IAAW,OAAQ,CAAA,MAAA,KAAW,QAAU,EAAA;AAEtC,QAAI,IAAA,OAAA,CAAQ,gBAAgB,iBAAmB,EAAA;AAC7C,UAAA,QAAA,mBACGA,cAAA,CAAA,uBAAA,EAAA;AAAA,YACC,OAAA;AAAA,YACA,UAAA;AAAA,WACF,CAAA,CAAA;AAAA,SAEG,MAAA;AACL,UACE,QAAA,mBAAAC,eAAA,CAAAC,mBAAA,EAAA;AAAA,YACE,QAAA,EAAA;AAAA,8BAACF,cAAA,CAAA,uBAAA,EAAA;AAAA,gBACC,OAAA;AAAA,gBACA,UAAA;AAAA,eACF,CAAA;AAAA,8BAECC,eAAA,CAAA,KAAA,EAAA;AAAA,gBAAI,SAAU,EAAA,0BAAA;AAAA,gBACb,QAAA,EAAA;AAAA,kCAACD,cAAA,CAAA,MAAA,EAAA;AAAA,oBAAK,SAAU,EAAA,mBAAA;AAAA,oBACd,yCAACG,mBAAY,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,uBACGH,cAAA,CAAA,KAAA,EAAA;AAAA,QACC,SAAW,EAAAI,KAAA;AAAA,UACT,iDAAA;AAAA,UACA,SAAA;AAAA,SACF;AAAA,QACC,GAAG,KAAA;AAAA,QACJ,GAAK,EAAA,YAAA;AAAA,QAEL,QAAC,kBAAAJ,cAAA,CAAAK,2BAAA,EAAA;AAAA,qBAAkBP,WAAA;AAAA,UAChB,QAAA;AAAA,SACH,CAAA;AAAA,OACF,CAAA,CAAA;AAAA,KAEJ;AAAA,GACF;AACF,EAAA;AAEA,SAAS,uBAAwB,CAAA;AAAA,EAC/B,OAAA;AAAA,EACA,UAAA;AACF,CAGG,EAAA;AACD,EACE,uBAAAE,cAAA,CAACM,aAAA,EAAA;AAAA,IACC,OAAA;AAAA,IACA,UAAY,EAAA;AAAA,MACV,QAAA,EAAU,CAAC,KAAA,qBAAWN,cAAA,CAAA,QAAA,EAAA;AAAA,QAAU,GAAG,KAAA;AAAA,QAAO,UAAA;AAAA,OAAwB,CAAA;AAAA,MAClE,aAAA,EAAe,CAAC,KAAA,qBACbA,cAAA,CAAA,aAAA,EAAA;AAAA,QAAe,GAAG,KAAA;AAAA,QAAO,UAAA;AAAA,OAAwB,CAAA;AAAA,MAEpD,kBAAA;AAAA,KACF;AAAA,IACA,SAAU,EAAA,4BAAA;AAAA,GACZ,CAAA,CAAA;AAEJ,CAAA;AAKA,SAAS,QAAS,CAAA,EAAE,IAAM,EAAA,UAAA,EAAY,aAA8B,EAAA;AAClE,EAAA,uBACGA,cAAA,CAAAO,WAAA,EAAA;AAAA,IACC,SAAS,IAAK,CAAA,IAAA;AAAA,IACd,SAAU,EAAA,yBAAA;AAAA,IACV,UAAA;AAAA,IACA,OAAS,EAAA,WAAA;AAAA,GACX,CAAA,CAAA;AAEJ,CAAA;AAKA,SAAS,aAAc,CAAA,EAAE,IAAM,EAAA,WAAA,EAAa,YAAkC,EAAA;AAE5E,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAIC,eAAS,WAAW,CAAA,CAAA;AAChD,EAAA,MAAM,IAAIT,sBAAa,EAAA,CAAA;AAIvB,EAAAU,eAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,WAAa,EAAA;AAChB,MAAA,SAAA,CAAU,KAAK,CAAA,CAAA;AAAA,KACjB;AAAA,GACF,EAAG,CAAC,WAAW,CAAC,CAAA,CAAA;AAEhB,EACE,uBAAAR,eAAA,CAACS,YAAA,EAAA;AAAA,IACC,SAAU,EAAA,6CAAA;AAAA,IACV,IAAM,EAAA,MAAA;AAAA,IACN,YAAc,EAAA,SAAA;AAAA,IAEd,QAAA,EAAA;AAAA,sBAAAT,eAAA,CAACU,eAAA,EAAA;AAAA,QACC,SAAW,EAAAP,KAAA;AAAA,UACT,wBAAA;AAAA,UACA,WAAe,IAAA,oBAAA;AAAA,SACjB;AAAA,QAGC,QAAA,EAAA;AAAA,UAAA,CAAA,CAAE,0BAA0B,WAAW,CAAA;AAAA,0BACvCJ,cAAA,CAAA,MAAA,EAAA;AAAA,YAAK,SAAU,EAAA,0CAAA;AAAA,YACd,yCAACY,6BAAiB,EAAA,EAAA,CAAA;AAAA,WACpB,CAAA;AAAA,SAAA;AAAA,OACF,CAAA;AAAA,sBAEAZ,cAAA,CAACa,eAAA,EAAA;AAAA,QAAoB,SAAU,EAAA,wBAAA;AAAA,QAC7B,QAAC,kBAAAb,cAAA,CAAAO,WAAA,EAAA;AAAA,UACC,SAAS,IAAK,CAAA,IAAA;AAAA,UACd,OAAS,EAAA,WAAA;AAAA,UACT,UAAA;AAAA,SACF,CAAA;AAAA,OACF,CAAA;AAAA,KAAA;AAAA,GACF,CAAA,CAAA;AAEJ,CAAA;AAKA,SAAS,kBAAmB,CAAA;AAAA,EAC1B,IAAA;AAAA,EACA,OAAA;AACF,CAA4C,EAAA;AAC1C,EAAA,uBACGP,cAAA,CAAA,KAAA,EAAA;AAAA,IAAI,SAAU,EAAA,oCAAA;AAAA,IACb,QAAC,kBAAAA,cAAA,CAAAc,2BAAA,EAAA;AAAA,MACC,0BACGb,eAAA,CAAA,KAAA,EAAA;AAAA,QAAI,SAAU,EAAA,0BAAA;AAAA,QACb,QAAA,EAAA;AAAA,0BAACD,cAAA,CAAA,MAAA,EAAA;AAAA,YAAK,SAAU,EAAA,mBAAA;AAAA,YACd,yCAACG,mBAAY,EAAA,EAAA,CAAA;AAAA,WACf,CAAA;AAAA,0BACCF,eAAA,CAAA,GAAA,EAAA;AAAA,YAAE,QAAA,EAAA;AAAA,cAAA,wCAAA;AAAA,8BACsCD,cAAA,CAAA,MAAA,EAAA;AAAA,gBAAM,QAAK,EAAA,IAAA,CAAA,IAAA;AAAA,eAAK,CAAA;AAAA,cAAO,4BAAA;AAAA,aAAA;AAAA,WAEhE,CAAA;AAAA,SAAA;AAAA,OACF,CAAA;AAAA,MAGF,QAAC,kBAAAA,cAAA,CAAAe,sCAAA,EAAA;AAAA,QAAwB,IAAA;AAAA,QAAY,OAAA;AAAA,OAAkB,CAAA;AAAA,KACzD,CAAA;AAAA,GACF,CAAA,CAAA;AAEJ;;;;"}
|
|
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 ReactNode,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nimport { 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 { AiMessageToolInvocation } from \"../../primitives/AiMessage/tool-invocation\";\nimport type {\n AiMessageContentReasoningPartProps,\n AiMessageContentRetrievalPartProps,\n AiMessageContentTextPartProps,\n AiMessageContentToolInvocationPartProps,\n} from \"../../primitives/AiMessage/types\";\nimport * as Collapsible from \"../../primitives/Collapsible\";\nimport type { MarkdownComponents } from \"../../primitives/Markdown\";\nimport { cn } from \"../../utils/cn\";\nimport { ErrorBoundary } from \"../../utils/ErrorBoundary\";\nimport { Prose } from \"./Prose\";\n\ntype UiAssistantMessage = WithNavigation<AiAssistantMessage>;\n\ntype AiChatAssistantMessageComponents = {\n /**\n * The components used to render Markdown content.\n */\n markdown?: Partial<MarkdownComponents>;\n};\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 & AiChatAssistantMessageComponents>;\n}\n\ninterface TextPartProps extends AiMessageContentTextPartProps {\n components?: Partial<GlobalComponents & AiChatAssistantMessageComponents>;\n}\n\ninterface ReasoningPartProps extends AiMessageContentReasoningPartProps {\n components?: Partial<GlobalComponents & AiChatAssistantMessageComponents>;\n}\n\ninterface RetrievalPartProps extends AiMessageContentRetrievalPartProps {\n components?: Partial<GlobalComponents & AiChatAssistantMessageComponents>;\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 = (\n <AssistantMessageContent\n message={message}\n components={components}\n />\n );\n }\n } else if (message.status === \"completed\") {\n children = (\n <AssistantMessageContent message={message} components={components} />\n );\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 = (\n <AssistantMessageContent\n message={message}\n components={components}\n />\n );\n } else {\n children = (\n <>\n <AssistantMessageContent\n message={message}\n components={components}\n />\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={cn(\n \"lb-ai-chat-message lb-ai-chat-assistant-message\",\n className\n )}\n {...props}\n ref={forwardedRef}\n >\n <OverridesProvider overrides={overrides}>\n {children}\n </OverridesProvider>\n </div>\n );\n }\n )\n);\n\nfunction AssistantMessageContent({\n message,\n components,\n}: {\n message: UiAssistantMessage;\n components?: Partial<GlobalComponents & AiChatAssistantMessageComponents>;\n}) {\n const ref = useRef(components);\n const BoundTextPart = useMemo(\n () => (props: TextPartProps) => (\n <TextPart {...props} components={ref.current} />\n ),\n []\n );\n const BoundReasoningPart = useMemo(\n () => (props: ReasoningPartProps) => (\n <ReasoningPart {...props} components={ref.current} />\n ),\n []\n );\n return (\n <AiMessage.Content\n message={message}\n components={{\n TextPart: BoundTextPart,\n ReasoningPart: BoundReasoningPart,\n RetrievalPart,\n ToolInvocationPart,\n }}\n className=\"lb-ai-chat-message-content\"\n />\n );\n}\n\n/* -------------------------------------------------------------------------------------------------\n * TextPart\n * -----------------------------------------------------------------------------------------------*/\nfunction TextPart({ part, components, isStreaming }: TextPartProps) {\n return (\n <Prose\n content={part.text}\n className=\"lb-ai-chat-message-text\"\n components={components}\n partial={isStreaming}\n />\n );\n}\n\n/* -------------------------------------------------------------------------------------------------\n * ReasoningPart\n * -----------------------------------------------------------------------------------------------*/\nfunction ReasoningPart({ part, isStreaming, components }: ReasoningPartProps) {\n // Start collapsed if reasoning is already done.\n const [isOpen, setIsOpen] = useState(isStreaming);\n const $ = useOverrides();\n\n // Auto-collapse when reasoning is done, while still allowing the user to\n // open/collapse it manually during and after it's done.\n useEffect(() => {\n if (!isStreaming) {\n setIsOpen(false);\n }\n }, [isStreaming]);\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={cn(\n \"lb-collapsible-trigger\",\n isStreaming && \"lb-ai-chat-pending\"\n )}\n >\n {$.AI_CHAT_MESSAGE_REASONING(isStreaming, part)}\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\n content={part.text}\n partial={isStreaming}\n components={components}\n />\n </Collapsible.Content>\n </Collapsible.Root>\n );\n}\n\n/* -------------------------------------------------------------------------------------------------\n * RetrievalPart\n * -----------------------------------------------------------------------------------------------*/\nfunction RetrievalPart({ part, isStreaming }: RetrievalPartProps) {\n const $ = useOverrides();\n\n return (\n <div\n className={cn(\n \"lb-ai-chat-message-retrieval\",\n isStreaming && \"lb-ai-chat-pending\"\n )}\n >\n {$.AI_CHAT_MESSAGE_RETRIEVAL(isStreaming, part)}\n </div>\n );\n}\n\n/* -------------------------------------------------------------------------------------------------\n * ToolInvocationPart\n * -----------------------------------------------------------------------------------------------*/\nfunction ToolInvocationPart({\n part,\n message,\n}: AiMessageContentToolInvocationPartProps) {\n return (\n <div className=\"lb-ai-chat-message-tool-invocation\">\n <ErrorBoundary\n fallback={\n <div className=\"lb-ai-chat-message-error\">\n <span className=\"lb-icon-container\">\n <WarningIcon />\n </span>\n <p>\n Failed to render tool call result for <code>{part.name}</code>.\n See console for details.\n </p>\n </div>\n }\n >\n <AiMessageToolInvocation part={part} message={message} />\n </ErrorBoundary>\n </div>\n );\n}\n"],"names":["memo","forwardRef","overrides","useOverrides","jsx","jsxs","Fragment","WarningIcon","cn","OverridesProvider","useRef","useMemo","AiMessage.Content","Prose","useState","useEffect","Collapsible.Root","Collapsible.Trigger","ChevronRightIcon","Collapsible.Content","ErrorBoundary","AiMessageToolInvocation"],"mappings":";;;;;;;;;;;;;;AA4EO,MAAM,sBAAyB,GAAAA,UAAA;AAAA,EACpCC,gBAAA;AAAA,IACE,CAAC,EAAE,OAAS,EAAA,SAAA,aAAWC,aAAW,UAAe,EAAA,GAAA,KAAA,IAAS,YAAiB,KAAA;AACzE,MAAM,MAAA,CAAA,GAAIC,uBAAaD,WAAS,CAAA,CAAA;AAEhC,MAAA,IAAI,QAAsB,GAAA,IAAA,CAAA;AAE1B,MAAI,IAAA,OAAA,CAAQ,cAAc,KAAW,CAAA,EAAA;AACnC,QAAA,QAAA,mBACGE,cAAA,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,mBACGA,cAAA,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,mBACGA,cAAA,CAAA,uBAAA,EAAA;AAAA,YACC,OAAA;AAAA,YACA,UAAA;AAAA,WACF,CAAA,CAAA;AAAA,SAEJ;AAAA,OACF,MAAA,IAAW,OAAQ,CAAA,MAAA,KAAW,WAAa,EAAA;AACzC,QAAA,QAAA,mBACGA,cAAA,CAAA,uBAAA,EAAA;AAAA,UAAwB,OAAA;AAAA,UAAkB,UAAA;AAAA,SAAwB,CAAA,CAAA;AAAA,OAEvE,MAAA,IAAW,OAAQ,CAAA,MAAA,KAAW,QAAU,EAAA;AAEtC,QAAI,IAAA,OAAA,CAAQ,gBAAgB,iBAAmB,EAAA;AAC7C,UAAA,QAAA,mBACGA,cAAA,CAAA,uBAAA,EAAA;AAAA,YACC,OAAA;AAAA,YACA,UAAA;AAAA,WACF,CAAA,CAAA;AAAA,SAEG,MAAA;AACL,UACE,QAAA,mBAAAC,eAAA,CAAAC,mBAAA,EAAA;AAAA,YACE,QAAA,EAAA;AAAA,8BAACF,cAAA,CAAA,uBAAA,EAAA;AAAA,gBACC,OAAA;AAAA,gBACA,UAAA;AAAA,eACF,CAAA;AAAA,8BAECC,eAAA,CAAA,KAAA,EAAA;AAAA,gBAAI,SAAU,EAAA,0BAAA;AAAA,gBACb,QAAA,EAAA;AAAA,kCAACD,cAAA,CAAA,MAAA,EAAA;AAAA,oBAAK,SAAU,EAAA,mBAAA;AAAA,oBACd,yCAACG,mBAAY,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,uBACGH,cAAA,CAAA,KAAA,EAAA;AAAA,QACC,SAAW,EAAAI,KAAA;AAAA,UACT,iDAAA;AAAA,UACA,SAAA;AAAA,SACF;AAAA,QACC,GAAG,KAAA;AAAA,QACJ,GAAK,EAAA,YAAA;AAAA,QAEL,QAAC,kBAAAJ,cAAA,CAAAK,2BAAA,EAAA;AAAA,qBAAkBP,WAAA;AAAA,UAChB,QAAA;AAAA,SACH,CAAA;AAAA,OACF,CAAA,CAAA;AAAA,KAEJ;AAAA,GACF;AACF,EAAA;AAEA,SAAS,uBAAwB,CAAA;AAAA,EAC/B,OAAA;AAAA,EACA,UAAA;AACF,CAGG,EAAA;AACD,EAAM,MAAA,GAAA,GAAMQ,aAAO,UAAU,CAAA,CAAA;AAC7B,EAAA,MAAM,aAAgB,GAAAC,aAAA;AAAA,IACpB,MAAM,CAAC,KAAA,qBACJP,cAAA,CAAA,QAAA,EAAA;AAAA,MAAU,GAAG,KAAA;AAAA,MAAO,YAAY,GAAI,CAAA,OAAA;AAAA,KAAS,CAAA;AAAA,IAEhD,EAAC;AAAA,GACH,CAAA;AACA,EAAA,MAAM,kBAAqB,GAAAO,aAAA;AAAA,IACzB,MAAM,CAAC,KAAA,qBACJP,cAAA,CAAA,aAAA,EAAA;AAAA,MAAe,GAAG,KAAA;AAAA,MAAO,YAAY,GAAI,CAAA,OAAA;AAAA,KAAS,CAAA;AAAA,IAErD,EAAC;AAAA,GACH,CAAA;AACA,EACE,uBAAAA,cAAA,CAACQ,aAAA,EAAA;AAAA,IACC,OAAA;AAAA,IACA,UAAY,EAAA;AAAA,MACV,QAAU,EAAA,aAAA;AAAA,MACV,aAAe,EAAA,kBAAA;AAAA,MACf,aAAA;AAAA,MACA,kBAAA;AAAA,KACF;AAAA,IACA,SAAU,EAAA,4BAAA;AAAA,GACZ,CAAA,CAAA;AAEJ,CAAA;AAKA,SAAS,QAAS,CAAA,EAAE,IAAM,EAAA,UAAA,EAAY,aAA8B,EAAA;AAClE,EAAA,uBACGR,cAAA,CAAAS,WAAA,EAAA;AAAA,IACC,SAAS,IAAK,CAAA,IAAA;AAAA,IACd,SAAU,EAAA,yBAAA;AAAA,IACV,UAAA;AAAA,IACA,OAAS,EAAA,WAAA;AAAA,GACX,CAAA,CAAA;AAEJ,CAAA;AAKA,SAAS,aAAc,CAAA,EAAE,IAAM,EAAA,WAAA,EAAa,YAAkC,EAAA;AAE5E,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAIC,eAAS,WAAW,CAAA,CAAA;AAChD,EAAA,MAAM,IAAIX,sBAAa,EAAA,CAAA;AAIvB,EAAAY,eAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,WAAa,EAAA;AAChB,MAAA,SAAA,CAAU,KAAK,CAAA,CAAA;AAAA,KACjB;AAAA,GACF,EAAG,CAAC,WAAW,CAAC,CAAA,CAAA;AAEhB,EACE,uBAAAV,eAAA,CAACW,YAAA,EAAA;AAAA,IACC,SAAU,EAAA,6CAAA;AAAA,IACV,IAAM,EAAA,MAAA;AAAA,IACN,YAAc,EAAA,SAAA;AAAA,IAEd,QAAA,EAAA;AAAA,sBAAAX,eAAA,CAACY,eAAA,EAAA;AAAA,QACC,SAAW,EAAAT,KAAA;AAAA,UACT,wBAAA;AAAA,UACA,WAAe,IAAA,oBAAA;AAAA,SACjB;AAAA,QAEC,QAAA,EAAA;AAAA,UAAE,CAAA,CAAA,yBAAA,CAA0B,aAAa,IAAI,CAAA;AAAA,0BAC7CJ,cAAA,CAAA,MAAA,EAAA;AAAA,YAAK,SAAU,EAAA,0CAAA;AAAA,YACd,yCAACc,6BAAiB,EAAA,EAAA,CAAA;AAAA,WACpB,CAAA;AAAA,SAAA;AAAA,OACF,CAAA;AAAA,sBAEAd,cAAA,CAACe,eAAA,EAAA;AAAA,QAAoB,SAAU,EAAA,wBAAA;AAAA,QAC7B,QAAC,kBAAAf,cAAA,CAAAS,WAAA,EAAA;AAAA,UACC,SAAS,IAAK,CAAA,IAAA;AAAA,UACd,OAAS,EAAA,WAAA;AAAA,UACT,UAAA;AAAA,SACF,CAAA;AAAA,OACF,CAAA;AAAA,KAAA;AAAA,GACF,CAAA,CAAA;AAEJ,CAAA;AAKA,SAAS,aAAc,CAAA,EAAE,IAAM,EAAA,WAAA,EAAmC,EAAA;AAChE,EAAA,MAAM,IAAIV,sBAAa,EAAA,CAAA;AAEvB,EAAA,uBACGC,cAAA,CAAA,KAAA,EAAA;AAAA,IACC,SAAW,EAAAI,KAAA;AAAA,MACT,8BAAA;AAAA,MACA,WAAe,IAAA,oBAAA;AAAA,KACjB;AAAA,IAEC,QAAA,EAAA,CAAA,CAAE,yBAA0B,CAAA,WAAA,EAAa,IAAI,CAAA;AAAA,GAChD,CAAA,CAAA;AAEJ,CAAA;AAKA,SAAS,kBAAmB,CAAA;AAAA,EAC1B,IAAA;AAAA,EACA,OAAA;AACF,CAA4C,EAAA;AAC1C,EAAA,uBACGJ,cAAA,CAAA,KAAA,EAAA;AAAA,IAAI,SAAU,EAAA,oCAAA;AAAA,IACb,QAAC,kBAAAA,cAAA,CAAAgB,2BAAA,EAAA;AAAA,MACC,0BACGf,eAAA,CAAA,KAAA,EAAA;AAAA,QAAI,SAAU,EAAA,0BAAA;AAAA,QACb,QAAA,EAAA;AAAA,0BAACD,cAAA,CAAA,MAAA,EAAA;AAAA,YAAK,SAAU,EAAA,mBAAA;AAAA,YACd,yCAACG,mBAAY,EAAA,EAAA,CAAA;AAAA,WACf,CAAA;AAAA,0BACCF,eAAA,CAAA,GAAA,EAAA;AAAA,YAAE,QAAA,EAAA;AAAA,cAAA,wCAAA;AAAA,8BACsCD,cAAA,CAAA,MAAA,EAAA;AAAA,gBAAM,QAAK,EAAA,IAAA,CAAA,IAAA;AAAA,eAAK,CAAA;AAAA,cAAO,4BAAA;AAAA,aAAA;AAAA,WAEhE,CAAA;AAAA,SAAA;AAAA,OACF,CAAA;AAAA,MAGF,QAAC,kBAAAA,cAAA,CAAAiB,sCAAA,EAAA;AAAA,QAAwB,IAAA;AAAA,QAAY,OAAA;AAAA,OAAkB,CAAA;AAAA,KACzD,CAAA;AAAA,GACF,CAAA,CAAA;AAEJ;;;;"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
|
-
import { memo, forwardRef, useState, useEffect } from 'react';
|
|
2
|
+
import { memo, forwardRef, useRef, useMemo, useState, useEffect } from 'react';
|
|
3
3
|
import { ChevronRightIcon } from '../../icons/ChevronRight.js';
|
|
4
4
|
import { WarningIcon } from '../../icons/Warning.js';
|
|
5
5
|
import { useOverrides, OverridesProvider } from '../../overrides.js';
|
|
@@ -83,17 +83,27 @@ function AssistantMessageContent({
|
|
|
83
83
|
message,
|
|
84
84
|
components
|
|
85
85
|
}) {
|
|
86
|
+
const ref = useRef(components);
|
|
87
|
+
const BoundTextPart = useMemo(
|
|
88
|
+
() => (props) => /* @__PURE__ */ jsx(TextPart, {
|
|
89
|
+
...props,
|
|
90
|
+
components: ref.current
|
|
91
|
+
}),
|
|
92
|
+
[]
|
|
93
|
+
);
|
|
94
|
+
const BoundReasoningPart = useMemo(
|
|
95
|
+
() => (props) => /* @__PURE__ */ jsx(ReasoningPart, {
|
|
96
|
+
...props,
|
|
97
|
+
components: ref.current
|
|
98
|
+
}),
|
|
99
|
+
[]
|
|
100
|
+
);
|
|
86
101
|
return /* @__PURE__ */ jsx(AiMessageContent, {
|
|
87
102
|
message,
|
|
88
103
|
components: {
|
|
89
|
-
TextPart:
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}),
|
|
93
|
-
ReasoningPart: (props) => /* @__PURE__ */ jsx(ReasoningPart, {
|
|
94
|
-
...props,
|
|
95
|
-
components
|
|
96
|
-
}),
|
|
104
|
+
TextPart: BoundTextPart,
|
|
105
|
+
ReasoningPart: BoundReasoningPart,
|
|
106
|
+
RetrievalPart,
|
|
97
107
|
ToolInvocationPart
|
|
98
108
|
},
|
|
99
109
|
className: "lb-ai-chat-message-content"
|
|
@@ -126,7 +136,7 @@ function ReasoningPart({ part, isStreaming, components }) {
|
|
|
126
136
|
isStreaming && "lb-ai-chat-pending"
|
|
127
137
|
),
|
|
128
138
|
children: [
|
|
129
|
-
$.AI_CHAT_MESSAGE_REASONING(isStreaming),
|
|
139
|
+
$.AI_CHAT_MESSAGE_REASONING(isStreaming, part),
|
|
130
140
|
/* @__PURE__ */ jsx("span", {
|
|
131
141
|
className: "lb-collapsible-chevron lb-icon-container",
|
|
132
142
|
children: /* @__PURE__ */ jsx(ChevronRightIcon, {})
|
|
@@ -144,6 +154,16 @@ function ReasoningPart({ part, isStreaming, components }) {
|
|
|
144
154
|
]
|
|
145
155
|
});
|
|
146
156
|
}
|
|
157
|
+
function RetrievalPart({ part, isStreaming }) {
|
|
158
|
+
const $ = useOverrides();
|
|
159
|
+
return /* @__PURE__ */ jsx("div", {
|
|
160
|
+
className: cn(
|
|
161
|
+
"lb-ai-chat-message-retrieval",
|
|
162
|
+
isStreaming && "lb-ai-chat-pending"
|
|
163
|
+
),
|
|
164
|
+
children: $.AI_CHAT_MESSAGE_RETRIEVAL(isStreaming, part)
|
|
165
|
+
});
|
|
166
|
+
}
|
|
147
167
|
function ToolInvocationPart({
|
|
148
168
|
part,
|
|
149
169
|
message
|
|
@@ -1 +1 @@
|
|
|
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 ReactNode,\n useEffect,\n useState,\n} from \"react\";\n\nimport { 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 { AiMessageToolInvocation } from \"../../primitives/AiMessage/tool-invocation\";\nimport type {\n AiMessageContentReasoningPartProps,\n AiMessageContentTextPartProps,\n AiMessageContentToolInvocationPartProps,\n} from \"../../primitives/AiMessage/types\";\nimport * as Collapsible from \"../../primitives/Collapsible\";\nimport type { MarkdownComponents } from \"../../primitives/Markdown\";\nimport { cn } from \"../../utils/cn\";\nimport { ErrorBoundary } from \"../../utils/ErrorBoundary\";\nimport { Prose } from \"./Prose\";\n\ntype UiAssistantMessage = WithNavigation<AiAssistantMessage>;\n\ntype AiChatAssistantMessageComponents = {\n /**\n * The components used to render Markdown content.\n */\n markdown?: Partial<MarkdownComponents>;\n};\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 & AiChatAssistantMessageComponents>;\n}\n\ninterface TextPartProps extends AiMessageContentTextPartProps {\n components?: Partial<GlobalComponents & AiChatAssistantMessageComponents>;\n}\n\ninterface ReasoningPartProps extends AiMessageContentReasoningPartProps {\n components?: Partial<GlobalComponents & AiChatAssistantMessageComponents>;\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 = (\n <AssistantMessageContent\n message={message}\n components={components}\n />\n );\n }\n } else if (message.status === \"completed\") {\n children = (\n <AssistantMessageContent message={message} components={components} />\n );\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 = (\n <AssistantMessageContent\n message={message}\n components={components}\n />\n );\n } else {\n children = (\n <>\n <AssistantMessageContent\n message={message}\n components={components}\n />\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={cn(\n \"lb-ai-chat-message lb-ai-chat-assistant-message\",\n className\n )}\n {...props}\n ref={forwardedRef}\n >\n <OverridesProvider overrides={overrides}>\n {children}\n </OverridesProvider>\n </div>\n );\n }\n )\n);\n\nfunction AssistantMessageContent({\n message,\n components,\n}: {\n message: UiAssistantMessage;\n components?: Partial<GlobalComponents & AiChatAssistantMessageComponents>;\n}) {\n return (\n <AiMessage.Content\n message={message}\n components={{\n TextPart: (props) => <TextPart {...props} components={components} />,\n ReasoningPart: (props) => (\n <ReasoningPart {...props} components={components} />\n ),\n ToolInvocationPart,\n }}\n className=\"lb-ai-chat-message-content\"\n />\n );\n}\n\n/* -------------------------------------------------------------------------------------------------\n * TextPart\n * -----------------------------------------------------------------------------------------------*/\nfunction TextPart({ part, components, isStreaming }: TextPartProps) {\n return (\n <Prose\n content={part.text}\n className=\"lb-ai-chat-message-text\"\n components={components}\n partial={isStreaming}\n />\n );\n}\n\n/* -------------------------------------------------------------------------------------------------\n * ReasoningPart\n * -----------------------------------------------------------------------------------------------*/\nfunction ReasoningPart({ part, isStreaming, components }: ReasoningPartProps) {\n // Start collapsed if reasoning is already done.\n const [isOpen, setIsOpen] = useState(isStreaming);\n const $ = useOverrides();\n\n // Auto-collapse when reasoning is done, while still allowing the user to\n // open/collapse it manually during and after it's done.\n useEffect(() => {\n if (!isStreaming) {\n setIsOpen(false);\n }\n }, [isStreaming]);\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={cn(\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\n content={part.text}\n partial={isStreaming}\n components={components}\n />\n </Collapsible.Content>\n </Collapsible.Root>\n );\n}\n\n/* -------------------------------------------------------------------------------------------------\n * ToolInvocationPart\n * -----------------------------------------------------------------------------------------------*/\nfunction ToolInvocationPart({\n part,\n message,\n}: AiMessageContentToolInvocationPartProps) {\n return (\n <div className=\"lb-ai-chat-message-tool-invocation\">\n <ErrorBoundary\n fallback={\n <div className=\"lb-ai-chat-message-error\">\n <span className=\"lb-icon-container\">\n <WarningIcon />\n </span>\n <p>\n Failed to render tool call result for <code>{part.name}</code>.\n See console for details.\n </p>\n </div>\n }\n >\n <AiMessageToolInvocation part={part} message={message} />\n </ErrorBoundary>\n </div>\n );\n}\n"],"names":["AiMessage.Content","Collapsible.Root","Collapsible.Trigger","Collapsible.Content"],"mappings":"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"}
|
|
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 ReactNode,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nimport { 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 { AiMessageToolInvocation } from \"../../primitives/AiMessage/tool-invocation\";\nimport type {\n AiMessageContentReasoningPartProps,\n AiMessageContentRetrievalPartProps,\n AiMessageContentTextPartProps,\n AiMessageContentToolInvocationPartProps,\n} from \"../../primitives/AiMessage/types\";\nimport * as Collapsible from \"../../primitives/Collapsible\";\nimport type { MarkdownComponents } from \"../../primitives/Markdown\";\nimport { cn } from \"../../utils/cn\";\nimport { ErrorBoundary } from \"../../utils/ErrorBoundary\";\nimport { Prose } from \"./Prose\";\n\ntype UiAssistantMessage = WithNavigation<AiAssistantMessage>;\n\ntype AiChatAssistantMessageComponents = {\n /**\n * The components used to render Markdown content.\n */\n markdown?: Partial<MarkdownComponents>;\n};\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 & AiChatAssistantMessageComponents>;\n}\n\ninterface TextPartProps extends AiMessageContentTextPartProps {\n components?: Partial<GlobalComponents & AiChatAssistantMessageComponents>;\n}\n\ninterface ReasoningPartProps extends AiMessageContentReasoningPartProps {\n components?: Partial<GlobalComponents & AiChatAssistantMessageComponents>;\n}\n\ninterface RetrievalPartProps extends AiMessageContentRetrievalPartProps {\n components?: Partial<GlobalComponents & AiChatAssistantMessageComponents>;\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 = (\n <AssistantMessageContent\n message={message}\n components={components}\n />\n );\n }\n } else if (message.status === \"completed\") {\n children = (\n <AssistantMessageContent message={message} components={components} />\n );\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 = (\n <AssistantMessageContent\n message={message}\n components={components}\n />\n );\n } else {\n children = (\n <>\n <AssistantMessageContent\n message={message}\n components={components}\n />\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={cn(\n \"lb-ai-chat-message lb-ai-chat-assistant-message\",\n className\n )}\n {...props}\n ref={forwardedRef}\n >\n <OverridesProvider overrides={overrides}>\n {children}\n </OverridesProvider>\n </div>\n );\n }\n )\n);\n\nfunction AssistantMessageContent({\n message,\n components,\n}: {\n message: UiAssistantMessage;\n components?: Partial<GlobalComponents & AiChatAssistantMessageComponents>;\n}) {\n const ref = useRef(components);\n const BoundTextPart = useMemo(\n () => (props: TextPartProps) => (\n <TextPart {...props} components={ref.current} />\n ),\n []\n );\n const BoundReasoningPart = useMemo(\n () => (props: ReasoningPartProps) => (\n <ReasoningPart {...props} components={ref.current} />\n ),\n []\n );\n return (\n <AiMessage.Content\n message={message}\n components={{\n TextPart: BoundTextPart,\n ReasoningPart: BoundReasoningPart,\n RetrievalPart,\n ToolInvocationPart,\n }}\n className=\"lb-ai-chat-message-content\"\n />\n );\n}\n\n/* -------------------------------------------------------------------------------------------------\n * TextPart\n * -----------------------------------------------------------------------------------------------*/\nfunction TextPart({ part, components, isStreaming }: TextPartProps) {\n return (\n <Prose\n content={part.text}\n className=\"lb-ai-chat-message-text\"\n components={components}\n partial={isStreaming}\n />\n );\n}\n\n/* -------------------------------------------------------------------------------------------------\n * ReasoningPart\n * -----------------------------------------------------------------------------------------------*/\nfunction ReasoningPart({ part, isStreaming, components }: ReasoningPartProps) {\n // Start collapsed if reasoning is already done.\n const [isOpen, setIsOpen] = useState(isStreaming);\n const $ = useOverrides();\n\n // Auto-collapse when reasoning is done, while still allowing the user to\n // open/collapse it manually during and after it's done.\n useEffect(() => {\n if (!isStreaming) {\n setIsOpen(false);\n }\n }, [isStreaming]);\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={cn(\n \"lb-collapsible-trigger\",\n isStreaming && \"lb-ai-chat-pending\"\n )}\n >\n {$.AI_CHAT_MESSAGE_REASONING(isStreaming, part)}\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\n content={part.text}\n partial={isStreaming}\n components={components}\n />\n </Collapsible.Content>\n </Collapsible.Root>\n );\n}\n\n/* -------------------------------------------------------------------------------------------------\n * RetrievalPart\n * -----------------------------------------------------------------------------------------------*/\nfunction RetrievalPart({ part, isStreaming }: RetrievalPartProps) {\n const $ = useOverrides();\n\n return (\n <div\n className={cn(\n \"lb-ai-chat-message-retrieval\",\n isStreaming && \"lb-ai-chat-pending\"\n )}\n >\n {$.AI_CHAT_MESSAGE_RETRIEVAL(isStreaming, part)}\n </div>\n );\n}\n\n/* -------------------------------------------------------------------------------------------------\n * ToolInvocationPart\n * -----------------------------------------------------------------------------------------------*/\nfunction ToolInvocationPart({\n part,\n message,\n}: AiMessageContentToolInvocationPartProps) {\n return (\n <div className=\"lb-ai-chat-message-tool-invocation\">\n <ErrorBoundary\n fallback={\n <div className=\"lb-ai-chat-message-error\">\n <span className=\"lb-icon-container\">\n <WarningIcon />\n </span>\n <p>\n Failed to render tool call result for <code>{part.name}</code>.\n See console for details.\n </p>\n </div>\n }\n >\n <AiMessageToolInvocation part={part} message={message} />\n </ErrorBoundary>\n </div>\n );\n}\n"],"names":["AiMessage.Content","Collapsible.Root","Collapsible.Trigger","Collapsible.Content"],"mappings":"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"}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ComponentType, ComponentPropsWithoutRef, ElementType, ReactNode, FormEvent, ComponentProps, RefAttributes, MouseEvent, PropsWithChildren } from 'react';
|
|
3
|
-
import { CommentAttachment, Relax, CopilotId, AiKnowledgeSource, AiOpaqueToolDefinition, AiToolTypePack, JsonObject, AiToolExecuteCallback, NoInfr, CommentBody, MentionData, BaseMetadata, DM, CommentData, HistoryVersion, InboxNotificationData, InboxNotificationThreadData, InboxNotificationTextMentionData, InboxNotificationCustomData, KDAD, ThreadData } from '@liveblocks/core';
|
|
3
|
+
import { CommentAttachment, AiReasoningPart, AiRetrievalPart, Relax, CopilotId, AiKnowledgeSource, AiOpaqueToolDefinition, AiToolTypePack, JsonObject, AiToolExecuteCallback, NoInfr, CommentBody, MentionData, BaseMetadata, DM, CommentData, HistoryVersion, InboxNotificationData, InboxNotificationThreadData, InboxNotificationTextMentionData, InboxNotificationCustomData, KDAD, ThreadData } from '@liveblocks/core';
|
|
4
4
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
5
5
|
|
|
6
6
|
interface GlobalComponents {
|
|
@@ -90,8 +90,9 @@ interface AiComposerOverrides {
|
|
|
90
90
|
}
|
|
91
91
|
interface AiChatMessageOverrides {
|
|
92
92
|
AI_CHAT_MESSAGE_DELETED: string;
|
|
93
|
-
AI_CHAT_MESSAGE_THINKING:
|
|
94
|
-
AI_CHAT_MESSAGE_REASONING: (isStreaming: boolean) =>
|
|
93
|
+
AI_CHAT_MESSAGE_THINKING: ReactNode;
|
|
94
|
+
AI_CHAT_MESSAGE_REASONING: (isStreaming: boolean, part: AiReasoningPart) => ReactNode;
|
|
95
|
+
AI_CHAT_MESSAGE_RETRIEVAL: (isStreaming: boolean, part: AiRetrievalPart) => ReactNode;
|
|
95
96
|
}
|
|
96
97
|
interface AiChatOverrides {
|
|
97
98
|
AI_CHAT_MESSAGES_ERROR: (error: Error) => ReactNode;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ComponentType, ComponentPropsWithoutRef, ElementType, ReactNode, FormEvent, ComponentProps, RefAttributes, MouseEvent, PropsWithChildren } from 'react';
|
|
3
|
-
import { CommentAttachment, Relax, CopilotId, AiKnowledgeSource, AiOpaqueToolDefinition, AiToolTypePack, JsonObject, AiToolExecuteCallback, NoInfr, CommentBody, MentionData, BaseMetadata, DM, CommentData, HistoryVersion, InboxNotificationData, InboxNotificationThreadData, InboxNotificationTextMentionData, InboxNotificationCustomData, KDAD, ThreadData } from '@liveblocks/core';
|
|
3
|
+
import { CommentAttachment, AiReasoningPart, AiRetrievalPart, Relax, CopilotId, AiKnowledgeSource, AiOpaqueToolDefinition, AiToolTypePack, JsonObject, AiToolExecuteCallback, NoInfr, CommentBody, MentionData, BaseMetadata, DM, CommentData, HistoryVersion, InboxNotificationData, InboxNotificationThreadData, InboxNotificationTextMentionData, InboxNotificationCustomData, KDAD, ThreadData } from '@liveblocks/core';
|
|
4
4
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
5
5
|
|
|
6
6
|
interface GlobalComponents {
|
|
@@ -90,8 +90,9 @@ interface AiComposerOverrides {
|
|
|
90
90
|
}
|
|
91
91
|
interface AiChatMessageOverrides {
|
|
92
92
|
AI_CHAT_MESSAGE_DELETED: string;
|
|
93
|
-
AI_CHAT_MESSAGE_THINKING:
|
|
94
|
-
AI_CHAT_MESSAGE_REASONING: (isStreaming: boolean) =>
|
|
93
|
+
AI_CHAT_MESSAGE_THINKING: ReactNode;
|
|
94
|
+
AI_CHAT_MESSAGE_REASONING: (isStreaming: boolean, part: AiReasoningPart) => ReactNode;
|
|
95
|
+
AI_CHAT_MESSAGE_RETRIEVAL: (isStreaming: boolean, part: AiRetrievalPart) => ReactNode;
|
|
95
96
|
}
|
|
96
97
|
interface AiChatOverrides {
|
|
97
98
|
AI_CHAT_MESSAGES_ERROR: (error: Error) => ReactNode;
|
package/dist/overrides.cjs
CHANGED
|
@@ -5,6 +5,7 @@ var jsxRuntime = require('react/jsx-runtime');
|
|
|
5
5
|
var core = require('@liveblocks/core');
|
|
6
6
|
var react = require('react');
|
|
7
7
|
var Emoji = require('./components/internal/Emoji.cjs');
|
|
8
|
+
var Duration = require('./primitives/Duration.cjs');
|
|
8
9
|
var pluralize = require('./utils/pluralize.cjs');
|
|
9
10
|
|
|
10
11
|
|
|
@@ -127,7 +128,47 @@ const defaultOverrides = {
|
|
|
127
128
|
AI_COMPOSER_ABORT: "Abort response",
|
|
128
129
|
AI_CHAT_MESSAGE_DELETED: "This message has been deleted.",
|
|
129
130
|
AI_CHAT_MESSAGE_THINKING: "Thinking\u2026",
|
|
130
|
-
AI_CHAT_MESSAGE_REASONING: (isStreaming) => isStreaming ?
|
|
131
|
+
AI_CHAT_MESSAGE_REASONING: (isStreaming, part) => isStreaming ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {
|
|
132
|
+
children: "Reasoning\u2026"
|
|
133
|
+
}) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, {
|
|
134
|
+
children: [
|
|
135
|
+
"Reasoned for",
|
|
136
|
+
" ",
|
|
137
|
+
/* @__PURE__ */ jsxRuntime.jsx(Duration.Duration, {
|
|
138
|
+
className: "lb-duration lb-ai-chat-message-reasoning-duration",
|
|
139
|
+
from: part.startedAt,
|
|
140
|
+
to: part.endedAt
|
|
141
|
+
})
|
|
142
|
+
]
|
|
143
|
+
}),
|
|
144
|
+
AI_CHAT_MESSAGE_RETRIEVAL: (isStreaming, part) => isStreaming ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, {
|
|
145
|
+
children: [
|
|
146
|
+
"Searching",
|
|
147
|
+
" ",
|
|
148
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", {
|
|
149
|
+
className: "lb-ai-chat-message-retrieval-query",
|
|
150
|
+
children: part.query
|
|
151
|
+
}),
|
|
152
|
+
"\u2026"
|
|
153
|
+
]
|
|
154
|
+
}) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, {
|
|
155
|
+
children: [
|
|
156
|
+
"Searched",
|
|
157
|
+
" ",
|
|
158
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", {
|
|
159
|
+
className: "lb-ai-chat-message-retrieval-query",
|
|
160
|
+
children: part.query
|
|
161
|
+
}),
|
|
162
|
+
" ",
|
|
163
|
+
"for",
|
|
164
|
+
" ",
|
|
165
|
+
/* @__PURE__ */ jsxRuntime.jsx(Duration.Duration, {
|
|
166
|
+
className: "lb-duration lb-ai-chat-message-retrieval-duration",
|
|
167
|
+
from: part.startedAt,
|
|
168
|
+
to: part.endedAt
|
|
169
|
+
})
|
|
170
|
+
]
|
|
171
|
+
}),
|
|
131
172
|
AI_CHAT_MESSAGES_ERROR: () => "There was an error while getting the messages.",
|
|
132
173
|
AI_TOOL_CONFIRMATION_CONFIRM: "Confirm",
|
|
133
174
|
AI_TOOL_CONFIRMATION_CANCEL: "Cancel"
|