@liveblocks/react-ui 2.25.0-aiprivatebeta7 → 2.25.0-aiprivatebeta9
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.cjs +14 -10
- package/dist/_private/index.cjs.map +1 -1
- package/dist/_private/index.d.cts +200 -105
- package/dist/_private/index.d.ts +200 -105
- package/dist/_private/index.js +10 -5
- package/dist/_private/index.js.map +1 -1
- package/dist/components/AiChat.cjs +85 -120
- package/dist/components/AiChat.cjs.map +1 -1
- package/dist/components/AiChat.js +87 -122
- package/dist/components/AiChat.js.map +1 -1
- package/dist/components/AiTool.cjs +164 -0
- package/dist/components/AiTool.cjs.map +1 -0
- package/dist/components/AiTool.js +162 -0
- package/dist/components/AiTool.js.map +1 -0
- package/dist/components/Comment.cjs +5 -3
- package/dist/components/Comment.cjs.map +1 -1
- package/dist/components/Comment.js +6 -4
- package/dist/components/Comment.js.map +1 -1
- package/dist/components/InboxNotificationList.cjs +11 -3
- package/dist/components/InboxNotificationList.cjs.map +1 -1
- package/dist/components/InboxNotificationList.js +12 -4
- package/dist/components/InboxNotificationList.js.map +1 -1
- package/dist/components/Thread.cjs +3 -3
- package/dist/components/Thread.cjs.map +1 -1
- package/dist/components/Thread.js +3 -3
- package/dist/components/Thread.js.map +1 -1
- package/dist/components/internal/AiChatAssistantMessage.cjs +53 -229
- package/dist/components/internal/AiChatAssistantMessage.cjs.map +1 -1
- package/dist/components/internal/AiChatAssistantMessage.js +55 -231
- package/dist/components/internal/AiChatAssistantMessage.js.map +1 -1
- package/dist/components/internal/AiChatComposer.cjs +29 -17
- package/dist/components/internal/AiChatComposer.cjs.map +1 -1
- package/dist/components/internal/AiChatComposer.js +29 -17
- package/dist/components/internal/AiChatComposer.js.map +1 -1
- package/dist/components/internal/AiChatUserMessage.cjs +17 -10
- package/dist/components/internal/AiChatUserMessage.cjs.map +1 -1
- package/dist/components/internal/AiChatUserMessage.js +17 -10
- package/dist/components/internal/AiChatUserMessage.js.map +1 -1
- package/dist/components/internal/Button.cjs.map +1 -1
- package/dist/components/internal/Button.js.map +1 -1
- package/dist/components/internal/CodeBlock.cjs +72 -0
- package/dist/components/internal/CodeBlock.cjs.map +1 -0
- package/dist/components/internal/CodeBlock.js +70 -0
- package/dist/components/internal/CodeBlock.js.map +1 -0
- package/dist/components/internal/Emoji.cjs +12 -4
- package/dist/components/internal/Emoji.cjs.map +1 -1
- package/dist/components/internal/Emoji.js +12 -4
- package/dist/components/internal/Emoji.js.map +1 -1
- package/dist/components/internal/Prose.cjs +37 -0
- package/dist/components/internal/Prose.cjs.map +1 -0
- package/dist/components/internal/Prose.js +35 -0
- package/dist/components/internal/Prose.js.map +1 -0
- package/dist/icon.cjs +2 -0
- package/dist/icon.cjs.map +1 -1
- package/dist/icon.js +1 -0
- package/dist/icon.js.map +1 -1
- package/dist/icons/{Resolve.cjs → CheckCircle.cjs} +3 -3
- package/dist/icons/CheckCircle.cjs.map +1 -0
- package/dist/icons/{Resolve.js → CheckCircle.js} +3 -3
- package/dist/icons/CheckCircle.js.map +1 -0
- package/dist/icons/{Resolved.cjs → CheckCircleFill.cjs} +3 -3
- package/dist/icons/CheckCircleFill.cjs.map +1 -0
- package/dist/icons/{Resolved.js → CheckCircleFill.js} +3 -3
- package/dist/icons/CheckCircleFill.js.map +1 -0
- package/dist/icons/index.cjs +4 -4
- package/dist/icons/index.js +2 -2
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +68 -14
- package/dist/index.d.ts +68 -14
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/overrides.cjs +2 -8
- package/dist/overrides.cjs.map +1 -1
- package/dist/overrides.js +2 -8
- package/dist/overrides.js.map +1 -1
- package/dist/primitives/AiChatComposer/index.cjs +1 -2
- package/dist/primitives/AiChatComposer/index.cjs.map +1 -1
- package/dist/primitives/AiChatComposer/index.js +1 -2
- package/dist/primitives/AiChatComposer/index.js.map +1 -1
- package/dist/primitives/AiMessage/contexts.cjs +18 -0
- package/dist/primitives/AiMessage/contexts.cjs.map +1 -0
- package/dist/primitives/AiMessage/contexts.js +15 -0
- package/dist/primitives/AiMessage/contexts.js.map +1 -0
- package/dist/primitives/AiMessage/index.cjs +133 -0
- package/dist/primitives/AiMessage/index.cjs.map +1 -0
- package/dist/primitives/AiMessage/index.js +131 -0
- package/dist/primitives/AiMessage/index.js.map +1 -0
- package/dist/primitives/{internal/Collapsible → Collapsible}/index.cjs +39 -17
- package/dist/primitives/Collapsible/index.cjs.map +1 -0
- package/dist/primitives/{internal/Collapsible → Collapsible}/index.js +37 -15
- package/dist/primitives/Collapsible/index.js.map +1 -0
- package/dist/primitives/{internal/Markdown.cjs → Markdown.cjs} +150 -83
- package/dist/primitives/Markdown.cjs.map +1 -0
- package/dist/primitives/{internal/Markdown.js → Markdown.js} +151 -83
- package/dist/primitives/Markdown.js.map +1 -0
- package/dist/primitives/index.cjs +4 -6
- package/dist/primitives/index.cjs.map +1 -1
- package/dist/primitives/index.d.cts +2 -79
- package/dist/primitives/index.d.ts +2 -79
- package/dist/primitives/index.js +4 -6
- package/dist/primitives/index.js.map +1 -1
- package/dist/utils/ErrorBoundary.cjs +48 -0
- package/dist/utils/ErrorBoundary.cjs.map +1 -0
- package/dist/utils/ErrorBoundary.js +45 -0
- package/dist/utils/ErrorBoundary.js.map +1 -0
- package/dist/utils/use-visible.cjs +63 -45
- package/dist/utils/use-visible.cjs.map +1 -1
- package/dist/utils/use-visible.js +64 -46
- package/dist/utils/use-visible.js.map +1 -1
- package/dist/version.cjs +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -5
- package/src/styles/constants.css +1 -1
- package/src/styles/dark/index.css +7 -3
- package/src/styles/index.css +584 -238
- package/src/styles/utils.css +1 -1
- package/styles/dark/attributes.css +1 -1
- package/styles/dark/attributes.css.map +1 -1
- package/styles/dark/media-query.css +1 -1
- package/styles/dark/media-query.css.map +1 -1
- package/styles.css +1 -1
- package/styles.css.map +1 -1
- package/dist/icons/Resolve.cjs.map +0 -1
- package/dist/icons/Resolve.js.map +0 -1
- package/dist/icons/Resolved.cjs.map +0 -1
- package/dist/icons/Resolved.js.map +0 -1
- package/dist/primitives/internal/Collapsible/index.cjs.map +0 -1
- package/dist/primitives/internal/Collapsible/index.js.map +0 -1
- package/dist/primitives/internal/Emoji.cjs +0 -32
- package/dist/primitives/internal/Emoji.cjs.map +0 -1
- package/dist/primitives/internal/Emoji.js +0 -30
- package/dist/primitives/internal/Emoji.js.map +0 -1
- package/dist/primitives/internal/Markdown.cjs.map +0 -1
- package/dist/primitives/internal/Markdown.js.map +0 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
3
3
|
import { useRef, forwardRef, Children } from 'react';
|
|
4
4
|
import { classNames } from '../utils/class-names.js';
|
|
5
|
-
import {
|
|
5
|
+
import { useIntersectionCallback } from '../utils/use-visible.js';
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
function ReachEndMarker({
|
|
@@ -10,9 +10,17 @@ function ReachEndMarker({
|
|
|
10
10
|
onReachEnd
|
|
11
11
|
}) {
|
|
12
12
|
const markerRef = useRef(null);
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
useIntersectionCallback(
|
|
14
|
+
markerRef,
|
|
15
|
+
(isIntersecting) => {
|
|
16
|
+
if (isIntersecting) {
|
|
17
|
+
onReachEnd();
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
enabled
|
|
22
|
+
}
|
|
23
|
+
);
|
|
16
24
|
return /* @__PURE__ */ jsx("div", {
|
|
17
25
|
ref: markerRef,
|
|
18
26
|
style: { height: 0 }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InboxNotificationList.js","sources":["../../src/components/InboxNotificationList.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ComponentPropsWithoutRef } from \"react\";\nimport { Children, forwardRef, useRef } from \"react\";\n\nimport { classNames } from \"../utils/class-names\";\nimport {
|
|
1
|
+
{"version":3,"file":"InboxNotificationList.js","sources":["../../src/components/InboxNotificationList.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ComponentPropsWithoutRef } from \"react\";\nimport { Children, forwardRef, useRef } from \"react\";\n\nimport { classNames } from \"../utils/class-names\";\nimport { useIntersectionCallback } from \"../utils/use-visible\";\n\nexport interface InboxNotificationListProps\n extends ComponentPropsWithoutRef<\"ol\"> {\n /**\n * This API is *EXPERIMENTAL* and likely not going to be the final API. Do\n * not rely on it.\n *\n * @private\n */\n onReachEnd?: () => void;\n}\n\nfunction ReachEndMarker({\n enabled,\n onReachEnd,\n}: {\n enabled: boolean;\n onReachEnd: () => void;\n}) {\n const markerRef = useRef<HTMLDivElement>(null);\n\n useIntersectionCallback(\n markerRef,\n (isIntersecting) => {\n if (isIntersecting) {\n onReachEnd();\n }\n },\n {\n enabled,\n }\n );\n\n return <div ref={markerRef} style={{ height: 0 }} />;\n}\n\n/**\n * Displays inbox notifications as a list.\n *\n * @example\n * <InboxNotificationList>\n * {inboxNotifications.map((inboxNotification) => (\n * <InboxNotification key={inboxNotification.id} inboxNotification={inboxNotification} />\n * ))}\n * </InboxNotificationList>\n */\nexport const InboxNotificationList = forwardRef<\n HTMLOListElement,\n InboxNotificationListProps\n>(({ onReachEnd, children, className, ...props }, forwardedRef) => {\n return (\n <ol\n className={classNames(\"lb-root lb-inbox-notification-list\", className)}\n {...props}\n ref={forwardedRef}\n >\n {Children.map(children, (child, index) => (\n <li key={index} className=\"lb-inbox-notification-list-item\">\n {child}\n </li>\n ))}\n {/* Render an invisible marker at the end which is tied to an IntersectionObserver to be alerted when the end of the list has been reached */}\n {onReachEnd && (\n <ReachEndMarker\n onReachEnd={onReachEnd}\n enabled={Children.count(children) > 0}\n />\n )}\n </ol>\n );\n});\n"],"names":[],"mappings":";;;;;;;AAmBA;AAAwB;AACtB;AAEF;AAIE;AAEA;AAAA;AACE;AAEE;AACE;AAAW;AACb;AACF;AACA;AACE;AACF;AAGF;AAAQ;AAAS;AAA8B;AACjD;AAYa;AAIX;AACG;AACsE;AACjE;AACC;AAEJ;AACE;AAAyB;AACvB;AAEJ;AAGE;AACC;AACoC;AACtC;AAAA;AAIR;;"}
|
|
@@ -8,8 +8,8 @@ var react = require('react');
|
|
|
8
8
|
var ArrowDown = require('../icons/ArrowDown.cjs');
|
|
9
9
|
var Bell = require('../icons/Bell.cjs');
|
|
10
10
|
var BellCrossed = require('../icons/BellCrossed.cjs');
|
|
11
|
-
var
|
|
12
|
-
var
|
|
11
|
+
var CheckCircle = require('../icons/CheckCircle.cjs');
|
|
12
|
+
var CheckCircleFill = require('../icons/CheckCircleFill.cjs');
|
|
13
13
|
var overrides = require('../overrides.cjs');
|
|
14
14
|
var classNames = require('../utils/class-names.cjs');
|
|
15
15
|
var findLastIndex = require('../utils/find-last-index.cjs');
|
|
@@ -189,7 +189,7 @@ const Thread = react.forwardRef(
|
|
|
189
189
|
className: "lb-comment-action",
|
|
190
190
|
onClick: stopPropagation,
|
|
191
191
|
"aria-label": thread.resolved ? $.THREAD_UNRESOLVE : $.THREAD_RESOLVE,
|
|
192
|
-
icon: thread.resolved ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
192
|
+
icon: thread.resolved ? /* @__PURE__ */ jsxRuntime.jsx(CheckCircleFill.CheckCircleFillIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(CheckCircle.CheckCircleIcon, {})
|
|
193
193
|
})
|
|
194
194
|
})
|
|
195
195
|
}) : null,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Thread.cjs","sources":["../../src/components/Thread.tsx"],"sourcesContent":["\"use client\";\n\nimport type {\n BaseMetadata,\n CommentData,\n DM,\n ThreadData,\n} from \"@liveblocks/core\";\nimport {\n useMarkRoomThreadAsResolved,\n useMarkRoomThreadAsUnresolved,\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 { ArrowDownIcon } from \"../icons/ArrowDown\";\nimport { BellIcon } from \"../icons/Bell\";\nimport { BellCrossedIcon } from \"../icons/BellCrossed\";\nimport { ResolveIcon } from \"../icons/Resolve\";\nimport { ResolvedIcon } from \"../icons/Resolved\";\nimport type {\n CommentOverrides,\n ComposerOverrides,\n GlobalOverrides,\n ThreadOverrides,\n} from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport { classNames } from \"../utils/class-names\";\nimport { findLastIndex } from \"../utils/find-last-index\";\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 * 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/**\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 onResolvedChange,\n onCommentEdit,\n onCommentDelete,\n onThreadDelete,\n onAuthorClick,\n onMentionClick,\n onAttachmentClick,\n onComposerSubmit,\n overrides,\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 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) => comment.body);\n }, [showDeletedComments, thread.comments]);\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 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={classNames(\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\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 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 <ResolvedIcon />\n ) : (\n <ResolveIcon />\n )\n }\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 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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8JO;AAAe;AAElB;AACE;AACuB;AACT;AACd;AACoB;AACJ;AACD;AACG;AACe;AACjC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AACE;AAEuD;AAEzD;AACE;AAE4D;AAE9D;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;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;AAGA;AACG;AAEC;AACU;AACmB;AAC7B;AACe;AACF;AACb;AACA;AACA;AACA;AAGA;AACiB;AACjB;AACA;AACA;AAIM;AAGmC;AAIpC;AAIS;AAGP;AACiB;AACC;AACV;AAEN;AACW;AACD;AAID;AAMS;AAGnB;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;AACW;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 type {\n BaseMetadata,\n CommentData,\n DM,\n ThreadData,\n} from \"@liveblocks/core\";\nimport {\n useMarkRoomThreadAsResolved,\n useMarkRoomThreadAsUnresolved,\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 { 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 { classNames } from \"../utils/class-names\";\nimport { findLastIndex } from \"../utils/find-last-index\";\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 * 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/**\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 onResolvedChange,\n onCommentEdit,\n onCommentDelete,\n onThreadDelete,\n onAuthorClick,\n onMentionClick,\n onAttachmentClick,\n onComposerSubmit,\n overrides,\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 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) => comment.body);\n }, [showDeletedComments, thread.comments]);\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 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={classNames(\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\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 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 />\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 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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8JO;AAAe;AAElB;AACE;AACuB;AACT;AACd;AACoB;AACJ;AACD;AACG;AACe;AACjC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AACE;AAEuD;AAEzD;AACE;AAE4D;AAE9D;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;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;AAGA;AACG;AAEC;AACU;AACmB;AAC7B;AACe;AACF;AACb;AACA;AACA;AACA;AAGA;AACiB;AACjB;AACA;AACA;AAIM;AAGmC;AAIpC;AAIS;AAGP;AACiB;AACC;AACV;AAEN;AACW;AACD;AAID;AAMa;AAGvB;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;AACW;AACe;AACP;AACd;AACL;AACe;AACjB;AAAA;AAEJ;AACF;AAGN;;"}
|
|
@@ -6,8 +6,8 @@ import { forwardRef, useMemo, useState, useEffect, useCallback, Fragment } from
|
|
|
6
6
|
import { ArrowDownIcon } from '../icons/ArrowDown.js';
|
|
7
7
|
import { BellIcon } from '../icons/Bell.js';
|
|
8
8
|
import { BellCrossedIcon } from '../icons/BellCrossed.js';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import { CheckCircleIcon } from '../icons/CheckCircle.js';
|
|
10
|
+
import { CheckCircleFillIcon } from '../icons/CheckCircleFill.js';
|
|
11
11
|
import { useOverrides } from '../overrides.js';
|
|
12
12
|
import { classNames } from '../utils/class-names.js';
|
|
13
13
|
import { findLastIndex } from '../utils/find-last-index.js';
|
|
@@ -168,7 +168,7 @@ const Thread = forwardRef(
|
|
|
168
168
|
className: "lb-comment-action",
|
|
169
169
|
onClick: stopPropagation,
|
|
170
170
|
"aria-label": thread.resolved ? $.THREAD_UNRESOLVE : $.THREAD_RESOLVE,
|
|
171
|
-
icon: thread.resolved ? /* @__PURE__ */ jsx(
|
|
171
|
+
icon: thread.resolved ? /* @__PURE__ */ jsx(CheckCircleFillIcon, {}) : /* @__PURE__ */ jsx(CheckCircleIcon, {})
|
|
172
172
|
})
|
|
173
173
|
})
|
|
174
174
|
}) : null,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Thread.js","sources":["../../src/components/Thread.tsx"],"sourcesContent":["\"use client\";\n\nimport type {\n BaseMetadata,\n CommentData,\n DM,\n ThreadData,\n} from \"@liveblocks/core\";\nimport {\n useMarkRoomThreadAsResolved,\n useMarkRoomThreadAsUnresolved,\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 { ArrowDownIcon } from \"../icons/ArrowDown\";\nimport { BellIcon } from \"../icons/Bell\";\nimport { BellCrossedIcon } from \"../icons/BellCrossed\";\nimport { ResolveIcon } from \"../icons/Resolve\";\nimport { ResolvedIcon } from \"../icons/Resolved\";\nimport type {\n CommentOverrides,\n ComposerOverrides,\n GlobalOverrides,\n ThreadOverrides,\n} from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport { classNames } from \"../utils/class-names\";\nimport { findLastIndex } from \"../utils/find-last-index\";\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 * 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/**\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 onResolvedChange,\n onCommentEdit,\n onCommentDelete,\n onThreadDelete,\n onAuthorClick,\n onMentionClick,\n onAttachmentClick,\n onComposerSubmit,\n overrides,\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 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) => comment.body);\n }, [showDeletedComments, thread.comments]);\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 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={classNames(\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\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 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 <ResolvedIcon />\n ) : (\n <ResolveIcon />\n )\n }\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 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":";;;;;;;;;;;;;;;;;;;;;AA8JO;AAAe;AAElB;AACE;AACuB;AACT;AACd;AACoB;AACJ;AACD;AACG;AACe;AACjC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AACE;AAEuD;AAEzD;AACE;AAE4D;AAE9D;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;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;AAGA;AACG;AAEC;AACU;AACmB;AAC7B;AACe;AACF;AACb;AACA;AACA;AACA;AAGA;AACiB;AACjB;AACA;AACA;AAIM;AAGmC;AAIpC;AAIS;AAGP;AACiB;AACC;AACV;AAEN;AACW;AACD;AAID;AAMS;AAGnB;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;AACW;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 type {\n BaseMetadata,\n CommentData,\n DM,\n ThreadData,\n} from \"@liveblocks/core\";\nimport {\n useMarkRoomThreadAsResolved,\n useMarkRoomThreadAsUnresolved,\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 { 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 { classNames } from \"../utils/class-names\";\nimport { findLastIndex } from \"../utils/find-last-index\";\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 * 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/**\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 onResolvedChange,\n onCommentEdit,\n onCommentDelete,\n onThreadDelete,\n onAuthorClick,\n onMentionClick,\n onAttachmentClick,\n onComposerSubmit,\n overrides,\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 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) => comment.body);\n }, [showDeletedComments, thread.comments]);\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 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={classNames(\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\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 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 />\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 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":";;;;;;;;;;;;;;;;;;;;;AA8JO;AAAe;AAElB;AACE;AACuB;AACT;AACd;AACoB;AACJ;AACD;AACG;AACe;AACjC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AACE;AAEuD;AAEzD;AACE;AAE4D;AAE9D;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;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;AAGA;AACG;AAEC;AACU;AACmB;AAC7B;AACe;AACF;AACb;AACA;AACA;AACA;AAGA;AACiB;AACjB;AACA;AACA;AAIM;AAGmC;AAIpC;AAIS;AAGP;AACiB;AACC;AACV;AAEN;AACW;AACD;AAID;AAMa;AAGvB;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;AACW;AACe;AACP;AACd;AACL;AACe;AACjB;AAAA;AAEJ;AACF;AAGN;;"}
|
|
@@ -1,69 +1,27 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
-
var core = require('@liveblocks/core');
|
|
5
|
-
var react$1 = require('@liveblocks/react');
|
|
6
|
-
var _private = require('@liveblocks/react/_private');
|
|
7
|
-
var marked = require('marked');
|
|
8
4
|
var react = require('react');
|
|
9
|
-
var
|
|
10
|
-
var Tooltip = require('./Tooltip.cjs');
|
|
11
|
-
var Check = require('../../icons/Check.cjs');
|
|
12
|
-
var ChevronDown = require('../../icons/ChevronDown.cjs');
|
|
5
|
+
var components = require('../../components.cjs');
|
|
13
6
|
var ChevronRight = require('../../icons/ChevronRight.cjs');
|
|
14
|
-
var Copy = require('../../icons/Copy.cjs');
|
|
15
|
-
var Retry = require('../../icons/Retry.cjs');
|
|
16
7
|
var Warning = require('../../icons/Warning.cjs');
|
|
17
8
|
var overrides = require('../../overrides.cjs');
|
|
18
|
-
var index = require('../../primitives/
|
|
19
|
-
var
|
|
9
|
+
var index = require('../../primitives/AiMessage/index.cjs');
|
|
10
|
+
var index$1 = require('../../primitives/Collapsible/index.cjs');
|
|
20
11
|
var classNames = require('../../utils/class-names.cjs');
|
|
21
|
-
var
|
|
12
|
+
var Prose = require('./Prose.cjs');
|
|
22
13
|
|
|
23
14
|
const AiChatAssistantMessage = react.memo(
|
|
24
15
|
react.forwardRef(
|
|
25
|
-
({
|
|
26
|
-
message,
|
|
27
|
-
showActions = false,
|
|
28
|
-
showRegenerate = false,
|
|
29
|
-
copilotId,
|
|
30
|
-
className,
|
|
31
|
-
overrides: overrides$1,
|
|
32
|
-
...props
|
|
33
|
-
}, forwardedRef) => {
|
|
16
|
+
({ message, className, overrides: overrides$1, components: components$1, ...props }, forwardedRef) => {
|
|
34
17
|
const $ = overrides.useOverrides(overrides$1);
|
|
35
18
|
let children = null;
|
|
36
|
-
function MessageActions({ text }) {
|
|
37
|
-
if (!showActions)
|
|
38
|
-
return null;
|
|
39
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
40
|
-
className: "lb-ai-chat-message-actions",
|
|
41
|
-
children: [
|
|
42
|
-
/* @__PURE__ */ jsxRuntime.jsx(Tooltip.Tooltip, {
|
|
43
|
-
content: $.AI_CHAT_MESSAGE_COPY,
|
|
44
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(CopyTextButton, {
|
|
45
|
-
text,
|
|
46
|
-
label: $.AI_CHAT_MESSAGE_COPY
|
|
47
|
-
})
|
|
48
|
-
}),
|
|
49
|
-
showRegenerate && /* @__PURE__ */ jsxRuntime.jsx(Tooltip.Tooltip, {
|
|
50
|
-
content: $.AI_CHAT_MESSAGE_TRY_AGAIN,
|
|
51
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(RegenerateMessageButton, {
|
|
52
|
-
chatId: message.chatId,
|
|
53
|
-
messageId: message.id,
|
|
54
|
-
copilotId,
|
|
55
|
-
label: $.AI_CHAT_MESSAGE_TRY_AGAIN
|
|
56
|
-
})
|
|
57
|
-
})
|
|
58
|
-
]
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
19
|
if (message.deletedAt !== void 0) {
|
|
62
20
|
children = /* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
63
21
|
className: "lb-ai-chat-message-deleted",
|
|
64
22
|
children: $.AI_CHAT_MESSAGE_DELETED
|
|
65
23
|
});
|
|
66
|
-
} else if (message.status === "
|
|
24
|
+
} else if (message.status === "generating" || message.status === "awaiting-tool") {
|
|
67
25
|
if (message.contentSoFar.length === 0) {
|
|
68
26
|
children = /* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
69
27
|
className: "lb-ai-chat-message-thinking lb-ai-chat-pending",
|
|
@@ -71,53 +29,23 @@ const AiChatAssistantMessage = react.memo(
|
|
|
71
29
|
});
|
|
72
30
|
} else {
|
|
73
31
|
children = /* @__PURE__ */ jsxRuntime.jsx(AssistantMessageContent, {
|
|
74
|
-
|
|
75
|
-
chatId: message.chatId
|
|
32
|
+
message
|
|
76
33
|
});
|
|
77
34
|
}
|
|
78
35
|
} else if (message.status === "completed") {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
return acc + part.text;
|
|
82
|
-
}
|
|
83
|
-
return acc;
|
|
84
|
-
}, "");
|
|
85
|
-
children = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, {
|
|
86
|
-
children: [
|
|
87
|
-
/* @__PURE__ */ jsxRuntime.jsx(AssistantMessageContent, {
|
|
88
|
-
content: message.content,
|
|
89
|
-
chatId: message.chatId
|
|
90
|
-
}),
|
|
91
|
-
/* @__PURE__ */ jsxRuntime.jsx(MessageActions, {
|
|
92
|
-
text
|
|
93
|
-
})
|
|
94
|
-
]
|
|
36
|
+
children = /* @__PURE__ */ jsxRuntime.jsx(AssistantMessageContent, {
|
|
37
|
+
message
|
|
95
38
|
});
|
|
96
39
|
} else if (message.status === "failed") {
|
|
97
|
-
const text = message.contentSoFar.reduce((acc, part) => {
|
|
98
|
-
if (part.type === "text") {
|
|
99
|
-
return acc + part.text;
|
|
100
|
-
}
|
|
101
|
-
return acc;
|
|
102
|
-
}, "");
|
|
103
40
|
if (message.errorReason === "Aborted by user") {
|
|
104
|
-
children = /* @__PURE__ */ jsxRuntime.
|
|
105
|
-
|
|
106
|
-
/* @__PURE__ */ jsxRuntime.jsx(AssistantMessageContent, {
|
|
107
|
-
content: message.contentSoFar,
|
|
108
|
-
chatId: message.chatId
|
|
109
|
-
}),
|
|
110
|
-
/* @__PURE__ */ jsxRuntime.jsx(MessageActions, {
|
|
111
|
-
text
|
|
112
|
-
})
|
|
113
|
-
]
|
|
41
|
+
children = /* @__PURE__ */ jsxRuntime.jsx(AssistantMessageContent, {
|
|
42
|
+
message
|
|
114
43
|
});
|
|
115
44
|
} else {
|
|
116
45
|
children = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, {
|
|
117
46
|
children: [
|
|
118
47
|
/* @__PURE__ */ jsxRuntime.jsx(AssistantMessageContent, {
|
|
119
|
-
|
|
120
|
-
chatId: message.chatId
|
|
48
|
+
message
|
|
121
49
|
}),
|
|
122
50
|
/* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
123
51
|
className: "lb-ai-chat-message-error",
|
|
@@ -128,185 +56,81 @@ const AiChatAssistantMessage = react.memo(
|
|
|
128
56
|
}),
|
|
129
57
|
message.errorReason
|
|
130
58
|
]
|
|
131
|
-
}),
|
|
132
|
-
/* @__PURE__ */ jsxRuntime.jsx(MessageActions, {
|
|
133
|
-
text
|
|
134
59
|
})
|
|
135
60
|
]
|
|
136
61
|
});
|
|
137
62
|
}
|
|
138
63
|
}
|
|
139
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
64
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
65
|
+
className: classNames.classNames(
|
|
66
|
+
"lb-ai-chat-message lb-ai-chat-assistant-message",
|
|
67
|
+
className
|
|
68
|
+
),
|
|
69
|
+
...props,
|
|
70
|
+
ref: forwardedRef,
|
|
71
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(components.ComponentsProvider, {
|
|
72
|
+
components: components$1,
|
|
148
73
|
children
|
|
149
74
|
})
|
|
150
75
|
});
|
|
151
76
|
}
|
|
152
77
|
)
|
|
153
78
|
);
|
|
154
|
-
function
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
clearTimeout(timeoutId);
|
|
162
|
-
};
|
|
163
|
-
}, [isCopied]);
|
|
164
|
-
return /* @__PURE__ */ jsxRuntime.jsx(Button.Button, {
|
|
165
|
-
onClick: function() {
|
|
166
|
-
navigator.clipboard.writeText(text);
|
|
167
|
-
setIsCopied(true);
|
|
79
|
+
function AssistantMessageContent({ message }) {
|
|
80
|
+
return /* @__PURE__ */ jsxRuntime.jsx(index.Content, {
|
|
81
|
+
message,
|
|
82
|
+
components: {
|
|
83
|
+
TextPart,
|
|
84
|
+
ReasoningPart,
|
|
85
|
+
ToolInvocationPart
|
|
168
86
|
},
|
|
169
|
-
className: "lb-ai-chat-message-
|
|
170
|
-
"aria-label": label,
|
|
171
|
-
icon: isCopied ? /* @__PURE__ */ jsxRuntime.jsx(Check.CheckIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(Copy.CopyIcon, {})
|
|
87
|
+
className: "lb-ai-chat-message-content"
|
|
172
88
|
});
|
|
173
89
|
}
|
|
174
|
-
function
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
label
|
|
179
|
-
}) {
|
|
180
|
-
const client = react$1.useClient();
|
|
181
|
-
return /* @__PURE__ */ jsxRuntime.jsx(Button.Button, {
|
|
182
|
-
onClick: function() {
|
|
183
|
-
client[core.kInternal].ai.regenerateMessage(chatId, messageId, {
|
|
184
|
-
copilotId,
|
|
185
|
-
stream: true
|
|
186
|
-
});
|
|
187
|
-
},
|
|
188
|
-
className: "lb-ai-chat-message-action",
|
|
189
|
-
"aria-label": label,
|
|
190
|
-
icon: /* @__PURE__ */ jsxRuntime.jsx(Retry.RetryIcon, {})
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
function AssistantMessageContent({
|
|
194
|
-
content,
|
|
195
|
-
chatId
|
|
196
|
-
}) {
|
|
197
|
-
const isReasoning = content.some((part) => part.type === "reasoning") && content.every((part) => part.type === "reasoning");
|
|
198
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
199
|
-
className: "lb-ai-chat-message-content",
|
|
200
|
-
children: content.map((part, index) => {
|
|
201
|
-
switch (part.type) {
|
|
202
|
-
case "text": {
|
|
203
|
-
return /* @__PURE__ */ jsxRuntime.jsx(TextPart, {
|
|
204
|
-
text: part.text,
|
|
205
|
-
className: "lb-ai-chat-message-text"
|
|
206
|
-
}, index);
|
|
207
|
-
}
|
|
208
|
-
case "tool-call": {
|
|
209
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ToolCallPart, {
|
|
210
|
-
chatId,
|
|
211
|
-
name: part.toolName,
|
|
212
|
-
args: part.args
|
|
213
|
-
}, index);
|
|
214
|
-
}
|
|
215
|
-
case "reasoning": {
|
|
216
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ReasoningPart, {
|
|
217
|
-
text: part.text,
|
|
218
|
-
isPending: isReasoning
|
|
219
|
-
}, index);
|
|
220
|
-
}
|
|
221
|
-
default: {
|
|
222
|
-
return null;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
})
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
const TextPart = react.forwardRef(
|
|
229
|
-
({ text, ...props }, forwardedRef) => {
|
|
230
|
-
const tokens = react.useMemo(() => {
|
|
231
|
-
return new marked.Lexer().lex(text);
|
|
232
|
-
}, [text]);
|
|
233
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
234
|
-
ref: forwardedRef,
|
|
235
|
-
...props,
|
|
236
|
-
children: tokens.map((token, index) => {
|
|
237
|
-
return /* @__PURE__ */ jsxRuntime.jsx(MemoizedBlockTokenComp, {
|
|
238
|
-
token
|
|
239
|
-
}, index);
|
|
240
|
-
})
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
);
|
|
244
|
-
const MemoizedBlockTokenComp = react.memo(
|
|
245
|
-
function BlockTokenComp({ token }) {
|
|
246
|
-
return /* @__PURE__ */ jsxRuntime.jsx(Markdown.BlockTokenComp, {
|
|
247
|
-
token
|
|
248
|
-
});
|
|
249
|
-
},
|
|
250
|
-
(prevProps, nextProps) => {
|
|
251
|
-
const prevToken = prevProps.token;
|
|
252
|
-
const nextToken = nextProps.token;
|
|
253
|
-
if (prevToken.raw.length !== nextToken.raw.length) {
|
|
254
|
-
return false;
|
|
255
|
-
}
|
|
256
|
-
if (prevToken.type !== nextToken.type) {
|
|
257
|
-
return false;
|
|
258
|
-
}
|
|
259
|
-
return prevToken.raw === nextToken.raw;
|
|
260
|
-
}
|
|
261
|
-
);
|
|
262
|
-
function ToolCallPart({
|
|
263
|
-
chatId,
|
|
264
|
-
name,
|
|
265
|
-
args
|
|
266
|
-
}) {
|
|
267
|
-
const client = react$1.useClient();
|
|
268
|
-
const tool = _private.useSignal(
|
|
269
|
-
client[core.kInternal].ai.signals.getToolDefinition\u03A3(chatId, name)
|
|
270
|
-
);
|
|
271
|
-
if (tool === void 0 || tool.render === void 0)
|
|
272
|
-
return null;
|
|
273
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
274
|
-
className: "lb-ai-chat-message-tool",
|
|
275
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(tool.render, {
|
|
276
|
-
args
|
|
277
|
-
})
|
|
90
|
+
function TextPart({ part }) {
|
|
91
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Prose.Prose, {
|
|
92
|
+
content: part.text,
|
|
93
|
+
className: "lb-ai-chat-message-text"
|
|
278
94
|
});
|
|
279
95
|
}
|
|
280
96
|
function ReasoningPart({
|
|
281
|
-
|
|
282
|
-
|
|
97
|
+
part,
|
|
98
|
+
isStreaming
|
|
283
99
|
}) {
|
|
284
100
|
const [isOpen, setIsOpen] = react.useState(false);
|
|
285
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(index.Root, {
|
|
286
|
-
className: "lb-
|
|
101
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(index$1.Root, {
|
|
102
|
+
className: "lb-collapsible lb-ai-chat-message-reasoning",
|
|
287
103
|
open: isOpen,
|
|
288
104
|
onOpenChange: setIsOpen,
|
|
289
105
|
children: [
|
|
290
|
-
/* @__PURE__ */ jsxRuntime.jsxs(index.Trigger, {
|
|
106
|
+
/* @__PURE__ */ jsxRuntime.jsxs(index$1.Trigger, {
|
|
291
107
|
className: classNames.classNames(
|
|
292
|
-
"lb-
|
|
293
|
-
|
|
108
|
+
"lb-collapsible-trigger",
|
|
109
|
+
isStreaming && "lb-ai-chat-pending"
|
|
294
110
|
),
|
|
295
111
|
children: [
|
|
296
112
|
"Reasoning",
|
|
297
113
|
/* @__PURE__ */ jsxRuntime.jsx("span", {
|
|
298
|
-
className: "lb-icon-container",
|
|
299
|
-
children:
|
|
114
|
+
className: "lb-collapsible-chevron lb-icon-container",
|
|
115
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(ChevronRight.ChevronRightIcon, {})
|
|
300
116
|
})
|
|
301
117
|
]
|
|
302
118
|
}),
|
|
303
|
-
/* @__PURE__ */ jsxRuntime.jsx(index.Content, {
|
|
304
|
-
className: "lb-
|
|
305
|
-
children:
|
|
119
|
+
/* @__PURE__ */ jsxRuntime.jsx(index$1.Content, {
|
|
120
|
+
className: "lb-collapsible-content",
|
|
121
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Prose.Prose, {
|
|
122
|
+
content: part.text
|
|
123
|
+
})
|
|
306
124
|
})
|
|
307
125
|
]
|
|
308
126
|
});
|
|
309
127
|
}
|
|
128
|
+
function ToolInvocationPart({ children }) {
|
|
129
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
130
|
+
className: "lb-ai-chat-message-tool-invocation",
|
|
131
|
+
children
|
|
132
|
+
});
|
|
133
|
+
}
|
|
310
134
|
|
|
311
135
|
exports.AiChatAssistantMessage = AiChatAssistantMessage;
|
|
312
136
|
//# sourceMappingURL=AiChatAssistantMessage.cjs.map
|