@liveblocks/react-ui 2.25.0-aiprivatebeta8 → 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 +10 -12
- package/dist/_private/index.cjs.map +1 -1
- package/dist/_private/index.d.cts +141 -112
- package/dist/_private/index.d.ts +141 -112
- package/dist/_private/index.js +8 -5
- package/dist/_private/index.js.map +1 -1
- package/dist/components/AiChat.cjs +74 -106
- package/dist/components/AiChat.cjs.map +1 -1
- package/dist/components/AiChat.js +75 -107
- 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/internal/AiChatAssistantMessage.cjs +43 -199
- package/dist/components/internal/AiChatAssistantMessage.cjs.map +1 -1
- package/dist/components/internal/AiChatAssistantMessage.js +44 -200
- package/dist/components/internal/AiChatAssistantMessage.js.map +1 -1
- package/dist/components/internal/AiChatComposer.cjs +1 -1
- package/dist/components/internal/AiChatComposer.cjs.map +1 -1
- package/dist/components/internal/AiChatComposer.js +1 -1
- 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/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +50 -9
- package/dist/index.d.ts +50 -9
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/overrides.cjs +2 -4
- package/dist/overrides.cjs.map +1 -1
- package/dist/overrides.js +2 -4
- package/dist/overrides.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} +99 -63
- package/dist/primitives/Markdown.cjs.map +1 -0
- package/dist/primitives/{internal/Markdown.js → Markdown.js} +100 -63
- 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 -75
- package/dist/primitives/index.d.ts +2 -75
- 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 +4 -4
- package/src/styles/constants.css +1 -1
- package/src/styles/dark/index.css +7 -3
- package/src/styles/index.css +555 -253
- 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/components/AiToolDebugger.cjs +0 -74
- package/dist/components/AiToolDebugger.cjs.map +0 -1
- package/dist/components/AiToolDebugger.js +0 -72
- package/dist/components/AiToolDebugger.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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -9,11 +9,19 @@ var ArrowDown = require('../icons/ArrowDown.cjs');
|
|
|
9
9
|
var Spinner = require('../icons/Spinner.cjs');
|
|
10
10
|
var overrides = require('../overrides.cjs');
|
|
11
11
|
var classNames = require('../utils/class-names.cjs');
|
|
12
|
+
var useVisible = require('../utils/use-visible.cjs');
|
|
12
13
|
var AiChatAssistantMessage = require('./internal/AiChatAssistantMessage.cjs');
|
|
13
14
|
var AiChatComposer = require('./internal/AiChatComposer.cjs');
|
|
14
15
|
var AiChatUserMessage = require('./internal/AiChatUserMessage.cjs');
|
|
15
16
|
|
|
16
|
-
const
|
|
17
|
+
const MIN_DISTANCE_BOTTOM_SCROLL_INDICATOR = 50;
|
|
18
|
+
const defaultComponents = {
|
|
19
|
+
Empty: () => null,
|
|
20
|
+
Loading: () => /* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
21
|
+
className: "lb-loading lb-ai-chat-loading",
|
|
22
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Spinner.SpinnerIcon, {})
|
|
23
|
+
})
|
|
24
|
+
};
|
|
17
25
|
const AiChat = react.forwardRef(
|
|
18
26
|
({
|
|
19
27
|
chatId,
|
|
@@ -22,19 +30,26 @@ const AiChat = react.forwardRef(
|
|
|
22
30
|
overrides: overrides$1,
|
|
23
31
|
knowledge,
|
|
24
32
|
tools = {},
|
|
33
|
+
layout = "inset",
|
|
25
34
|
components,
|
|
26
35
|
className,
|
|
27
36
|
...props
|
|
28
37
|
}, forwardedRef) => {
|
|
29
38
|
const { messages, isLoading, error } = react$1.useAiChatMessages(chatId);
|
|
30
39
|
const $ = overrides.useOverrides(overrides$1);
|
|
40
|
+
const Empty = components?.Empty ?? defaultComponents.Empty;
|
|
41
|
+
const Loading = components?.Loading ?? defaultComponents.Loading;
|
|
31
42
|
const containerRef = react.useRef(null);
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
43
|
+
const containerBottomRef = react.useRef(null);
|
|
44
|
+
const isScrollAtBottom = useVisible.useVisible(containerBottomRef, {
|
|
45
|
+
enabled: !isLoading && !error,
|
|
46
|
+
root: containerRef,
|
|
47
|
+
rootMargin: MIN_DISTANCE_BOTTOM_SCROLL_INDICATOR
|
|
48
|
+
});
|
|
49
|
+
const isScrollIndicatorVisible = isLoading || error ? false : !isScrollAtBottom;
|
|
35
50
|
const client = react$1.useClient();
|
|
36
51
|
const ai = client[core.kInternal].ai;
|
|
37
|
-
const [
|
|
52
|
+
const [lastSentMessageId, setLastSentMessageId] = react.useState(null);
|
|
38
53
|
react.useImperativeHandle(
|
|
39
54
|
forwardedRef,
|
|
40
55
|
() => containerRef.current,
|
|
@@ -50,74 +65,27 @@ const AiChat = react.forwardRef(
|
|
|
50
65
|
);
|
|
51
66
|
};
|
|
52
67
|
}, [ai, chatId, tools]);
|
|
53
|
-
react.useEffect(() => {
|
|
54
|
-
const container = containerRef.current;
|
|
55
|
-
if (container === null)
|
|
56
|
-
return;
|
|
57
|
-
function handleScrollChange() {
|
|
58
|
-
const container2 = containerRef.current;
|
|
59
|
-
if (container2 === null)
|
|
60
|
-
return;
|
|
61
|
-
setDistanceToBottom(
|
|
62
|
-
container2.scrollHeight - container2.clientHeight - container2.scrollTop
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
container.addEventListener("scroll", handleScrollChange);
|
|
66
|
-
return () => {
|
|
67
|
-
container.removeEventListener("scroll", handleScrollChange);
|
|
68
|
-
};
|
|
69
|
-
}, []);
|
|
70
|
-
react.useEffect(() => {
|
|
71
|
-
const container = containerRef.current;
|
|
72
|
-
if (container === null)
|
|
73
|
-
return;
|
|
74
|
-
setDistanceToBottom(
|
|
75
|
-
container.scrollHeight - container.clientHeight - container.scrollTop
|
|
76
|
-
);
|
|
77
|
-
}, [messages]);
|
|
78
|
-
react.useEffect(() => {
|
|
79
|
-
const container = containerRef.current;
|
|
80
|
-
if (container === null)
|
|
81
|
-
return;
|
|
82
|
-
container.scrollTo({
|
|
83
|
-
top: container.scrollHeight,
|
|
84
|
-
behavior: "smooth"
|
|
85
|
-
});
|
|
86
|
-
}, [lastSendMessageId]);
|
|
87
|
-
react.useEffect(() => {
|
|
88
|
-
const container = containerRef.current;
|
|
89
|
-
if (container === null)
|
|
90
|
-
return;
|
|
91
|
-
const observer = new ResizeObserver(() => {
|
|
92
|
-
const container2 = containerRef.current;
|
|
93
|
-
if (container2 === null)
|
|
94
|
-
return;
|
|
95
|
-
setDistanceToBottom(
|
|
96
|
-
container2.scrollHeight - container2.clientHeight - container2.scrollTop
|
|
97
|
-
);
|
|
98
|
-
});
|
|
99
|
-
observer.observe(container);
|
|
100
|
-
return () => {
|
|
101
|
-
observer.disconnect();
|
|
102
|
-
};
|
|
103
|
-
}, []);
|
|
104
68
|
const scrollToBottomCallbackRef = react.useRef(void 0);
|
|
105
69
|
if (scrollToBottomCallbackRef.current === void 0) {
|
|
106
|
-
scrollToBottomCallbackRef.current = function() {
|
|
70
|
+
scrollToBottomCallbackRef.current = function(behavior) {
|
|
107
71
|
const container = containerRef.current;
|
|
108
72
|
if (container === null)
|
|
109
73
|
return;
|
|
110
74
|
container.scrollTo({
|
|
111
75
|
top: container.scrollHeight,
|
|
112
|
-
behavior
|
|
76
|
+
behavior
|
|
113
77
|
});
|
|
114
78
|
};
|
|
115
79
|
}
|
|
116
|
-
const
|
|
80
|
+
const scrollToBottom = scrollToBottomCallbackRef.current;
|
|
117
81
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
118
82
|
ref: containerRef,
|
|
119
83
|
...props,
|
|
120
|
-
className: classNames.classNames(
|
|
84
|
+
className: classNames.classNames(
|
|
85
|
+
"lb-root lb-ai-chat",
|
|
86
|
+
layout === "compact" ? "lb-ai-chat:layout-compact" : "lb-ai-chat:layout-inset",
|
|
87
|
+
className
|
|
88
|
+
),
|
|
121
89
|
children: [
|
|
122
90
|
knowledge ? knowledge.map((source, index) => /* @__PURE__ */ jsxRuntime.jsx(react$1.RegisterAiKnowledge, {
|
|
123
91
|
description: source.description,
|
|
@@ -125,20 +93,35 @@ const AiChat = react.forwardRef(
|
|
|
125
93
|
}, index)) : null,
|
|
126
94
|
/* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
127
95
|
className: "lb-ai-chat-content",
|
|
128
|
-
children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
129
|
-
className: "lb-loading lb-ai-chat-loading",
|
|
130
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(Spinner.SpinnerIcon, {})
|
|
131
|
-
}) : error !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
96
|
+
children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(Loading, {}) : error !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
132
97
|
className: "lb-error lb-ai-chat-error",
|
|
133
98
|
children: $.AI_CHAT_MESSAGES_ERROR(error)
|
|
134
|
-
}) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
99
|
+
}) : messages.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(Empty, {}) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, {
|
|
100
|
+
children: [
|
|
101
|
+
/* @__PURE__ */ jsxRuntime.jsx(AutoScrollHandler, {
|
|
102
|
+
lastSentMessageId,
|
|
103
|
+
scrollToBottom
|
|
104
|
+
}),
|
|
105
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
106
|
+
className: "lb-ai-chat-messages",
|
|
107
|
+
children: messages.map((message) => {
|
|
108
|
+
if (message.role === "user") {
|
|
109
|
+
return /* @__PURE__ */ jsxRuntime.jsx(AiChatUserMessage.AiChatUserMessage, {
|
|
110
|
+
message,
|
|
111
|
+
overrides: overrides$1
|
|
112
|
+
}, message.id);
|
|
113
|
+
} else if (message.role === "assistant") {
|
|
114
|
+
return /* @__PURE__ */ jsxRuntime.jsx(AiChatAssistantMessage.AiChatAssistantMessage, {
|
|
115
|
+
message,
|
|
116
|
+
overrides: overrides$1,
|
|
117
|
+
components
|
|
118
|
+
}, message.id);
|
|
119
|
+
} else {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
})
|
|
124
|
+
]
|
|
142
125
|
})
|
|
143
126
|
}),
|
|
144
127
|
/* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
@@ -147,22 +130,14 @@ const AiChat = react.forwardRef(
|
|
|
147
130
|
/* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
148
131
|
className: "lb-ai-chat-footer-actions",
|
|
149
132
|
children: /* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
150
|
-
className: "lb-elevation lb-ai-chat-scroll-indicator",
|
|
133
|
+
className: "lb-root lb-elevation lb-elevation-moderate lb-ai-chat-scroll-indicator",
|
|
151
134
|
"data-visible": isScrollIndicatorVisible ? "" : void 0,
|
|
152
135
|
children: /* @__PURE__ */ jsxRuntime.jsx("button", {
|
|
153
136
|
className: "lb-ai-chat-scroll-indicator-button",
|
|
154
137
|
tabIndex: isScrollIndicatorVisible ? 0 : -1,
|
|
155
138
|
"aria-hidden": !isScrollIndicatorVisible,
|
|
156
139
|
disabled: !isScrollIndicatorVisible,
|
|
157
|
-
onClick: () =>
|
|
158
|
-
const container = containerRef.current;
|
|
159
|
-
if (container === null)
|
|
160
|
-
return;
|
|
161
|
-
container.scrollTo({
|
|
162
|
-
top: container.scrollHeight,
|
|
163
|
-
behavior: "smooth"
|
|
164
|
-
});
|
|
165
|
-
},
|
|
140
|
+
onClick: () => scrollToBottom("smooth"),
|
|
166
141
|
children: /* @__PURE__ */ jsxRuntime.jsx("span", {
|
|
167
142
|
className: "lb-icon-container",
|
|
168
143
|
children: /* @__PURE__ */ jsxRuntime.jsx(ArrowDown.ArrowDownIcon, {})
|
|
@@ -175,39 +150,32 @@ const AiChat = react.forwardRef(
|
|
|
175
150
|
copilotId,
|
|
176
151
|
overrides: $,
|
|
177
152
|
autoFocus,
|
|
178
|
-
onUserMessageCreate: ({ id }) =>
|
|
153
|
+
onUserMessageCreate: ({ id }) => setLastSentMessageId(id),
|
|
154
|
+
className: layout === "inset" ? "lb-elevation lb-elevation-moderate" : void 0
|
|
179
155
|
}, chatId)
|
|
180
156
|
]
|
|
157
|
+
}),
|
|
158
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
159
|
+
ref: containerBottomRef,
|
|
160
|
+
style: { position: "sticky", height: 0 },
|
|
161
|
+
"aria-hidden": true,
|
|
162
|
+
"data-scroll-at-bottom": isScrollAtBottom ? "" : void 0
|
|
181
163
|
})
|
|
182
164
|
]
|
|
183
165
|
});
|
|
184
166
|
}
|
|
185
167
|
);
|
|
186
|
-
function
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
components,
|
|
190
|
-
onDistanceToBottomChange
|
|
168
|
+
function AutoScrollHandler({
|
|
169
|
+
lastSentMessageId,
|
|
170
|
+
scrollToBottom
|
|
191
171
|
}) {
|
|
192
172
|
_private.useLayoutEffect(() => {
|
|
193
|
-
|
|
194
|
-
}, [
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
overrides
|
|
200
|
-
}, message.id);
|
|
201
|
-
} else if (message.role === "assistant") {
|
|
202
|
-
return /* @__PURE__ */ jsxRuntime.jsx(AiChatAssistantMessage.AiChatAssistantMessage, {
|
|
203
|
-
message,
|
|
204
|
-
overrides,
|
|
205
|
-
components
|
|
206
|
-
}, message.id);
|
|
207
|
-
} else {
|
|
208
|
-
return null;
|
|
209
|
-
}
|
|
210
|
-
});
|
|
173
|
+
scrollToBottom("instant");
|
|
174
|
+
}, [scrollToBottom]);
|
|
175
|
+
react.useEffect(() => {
|
|
176
|
+
scrollToBottom("smooth");
|
|
177
|
+
}, [lastSentMessageId, scrollToBottom]);
|
|
178
|
+
return null;
|
|
211
179
|
}
|
|
212
180
|
|
|
213
181
|
exports.AiChat = AiChat;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AiChat.cjs","sources":["../../src/components/AiChat.tsx"],"sourcesContent":["import type {\n AiKnowledgeSource,\n AiToolDefinition,\n CopilotId,\n MessageId,\n UiChatMessage,\n} from \"@liveblocks/core\";\nimport { kInternal } from \"@liveblocks/core\";\nimport {\n RegisterAiKnowledge,\n useAiChatMessages,\n useClient,\n} from \"@liveblocks/react\";\nimport { useLayoutEffect } from \"@liveblocks/react/_private\";\nimport {\n type ComponentProps,\n forwardRef,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from \"react\";\n\nimport type { GlobalComponents } from \"../components\";\nimport { ArrowDownIcon } from \"../icons/ArrowDown\";\nimport { SpinnerIcon } from \"../icons/Spinner\";\nimport {\n type AiChatComposerOverrides,\n type AiChatMessageOverrides,\n type AiChatOverrides,\n type GlobalOverrides,\n useOverrides,\n} from \"../overrides\";\nimport { classNames } from \"../utils/class-names\";\nimport { AiChatAssistantMessage } from \"./internal/AiChatAssistantMessage\";\nimport { AiChatComposer } from \"./internal/AiChatComposer\";\nimport { AiChatUserMessage } from \"./internal/AiChatUserMessage\";\n\n/**\n * The number of pixels from the bottom of the messages list to trigger the scroll to bottom.\n */\nconst MIN_DISTANCE_TO_BOTTOM = 50;\n\nexport interface AiChatProps extends ComponentProps<\"div\"> {\n /**\n * The id of the chat the composer belongs to.\n */\n chatId: string;\n /**\n * Whether to focus the chat composer on mount.\n */\n autoFocus?: boolean;\n /**\n * The id of the copilot to use to send the message.\n */\n copilotId?: string;\n /**\n * The contextual knowledge to include in the chat. May be used by the assistant when generating responses.\n * Any knowledge you provide via this prop will be added to any already globally registered knowledge via <RegisterAiKnowledge />.\n */\n knowledge?: AiKnowledgeSource[];\n /**\n * Tool definitions to make available within this chat. May be used by the assistant when generating responses.\n */\n tools?: Record<string, AiToolDefinition>;\n /**\n * Override the component's strings.\n */\n overrides?: Partial<\n GlobalOverrides &\n AiChatMessageOverrides &\n AiChatComposerOverrides &\n AiChatOverrides\n >;\n /**\n * Override the component's components.\n */\n components?: Partial<GlobalComponents>; // TODO: Add more slots than the global ones over time (Markdown tags, the empty state, etc)\n}\n\nexport const AiChat = forwardRef<HTMLDivElement, AiChatProps>(\n (\n {\n chatId,\n copilotId,\n autoFocus,\n overrides,\n knowledge,\n tools = {},\n components,\n className,\n ...props\n },\n forwardedRef\n ) => {\n const { messages, isLoading, error } = useAiChatMessages(chatId);\n const $ = useOverrides(overrides);\n\n const containerRef = useRef<HTMLDivElement | null>(null);\n const [distanceToBottom, setDistanceToBottom] = useState<number | null>(\n null\n );\n const client = useClient();\n const ai = client[kInternal].ai;\n\n const [lastSendMessageId, setLastSendMessageId] =\n useState<MessageId | null>(null);\n\n useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(\n forwardedRef,\n () => containerRef.current,\n []\n );\n\n // Register the provided tools to the chat on mount and unregister them on unmount\n useEffect(() => {\n Object.entries(tools).map(([key, value]) =>\n ai.registerChatTool(chatId, key, value)\n );\n return () => {\n Object.entries(tools).map(([key]) =>\n ai.unregisterChatTool(chatId, key)\n );\n };\n }, [ai, chatId, tools]);\n\n useEffect(() => {\n const container = containerRef.current;\n if (container === null) return;\n function handleScrollChange() {\n const container = containerRef.current;\n if (container === null) return;\n\n setDistanceToBottom(\n container.scrollHeight - container.clientHeight - container.scrollTop\n );\n }\n container.addEventListener(\"scroll\", handleScrollChange);\n return () => {\n container.removeEventListener(\"scroll\", handleScrollChange);\n };\n }, []);\n\n useEffect(() => {\n const container = containerRef.current;\n if (container === null) return;\n\n setDistanceToBottom(\n container.scrollHeight - container.clientHeight - container.scrollTop\n );\n }, [messages]);\n\n useEffect(() => {\n const container = containerRef.current;\n if (container === null) return;\n\n container.scrollTo({\n top: container.scrollHeight,\n behavior: \"smooth\",\n });\n }, [lastSendMessageId]);\n\n useEffect(() => {\n const container = containerRef.current;\n if (container === null) return;\n\n const observer = new ResizeObserver(() => {\n const container = containerRef.current;\n if (container === null) return;\n setDistanceToBottom(\n container.scrollHeight - container.clientHeight - container.scrollTop\n );\n });\n observer.observe(container);\n return () => {\n observer.disconnect();\n };\n }, []);\n\n const scrollToBottomCallbackRef = useRef<() => void>(undefined);\n if (scrollToBottomCallbackRef.current === undefined) {\n scrollToBottomCallbackRef.current = function () {\n const container = containerRef.current;\n if (container === null) return;\n\n container.scrollTo({\n top: container.scrollHeight,\n behavior: \"instant\",\n });\n };\n }\n const isScrollIndicatorVisible =\n distanceToBottom !== null && distanceToBottom > MIN_DISTANCE_TO_BOTTOM;\n\n return (\n <div\n ref={containerRef}\n {...props}\n className={classNames(\"lb-root lb-ai-chat\", className)}\n >\n {knowledge\n ? knowledge.map((source, index) => (\n <RegisterAiKnowledge\n key={index}\n description={source.description}\n value={source.value}\n // knowledgeKey={source.knowledgeKey}\n />\n ))\n : null}\n <div className=\"lb-ai-chat-content\">\n {isLoading ? (\n <div className=\"lb-loading lb-ai-chat-loading\">\n <SpinnerIcon />\n </div>\n ) : error !== undefined ? (\n <div className=\"lb-error lb-ai-chat-error\">\n {$.AI_CHAT_MESSAGES_ERROR(error)}\n </div>\n ) : (\n <div className=\"lb-ai-chat-messages\">\n <Messages\n messages={messages}\n overrides={$}\n components={components}\n onDistanceToBottomChange={scrollToBottomCallbackRef.current}\n />\n </div>\n )}\n </div>\n\n <div className=\"lb-ai-chat-footer\">\n <div className=\"lb-ai-chat-footer-actions\">\n <div\n className=\"lb-elevation lb-ai-chat-scroll-indicator\"\n data-visible={isScrollIndicatorVisible ? \"\" : undefined}\n >\n <button\n className=\"lb-ai-chat-scroll-indicator-button\"\n tabIndex={isScrollIndicatorVisible ? 0 : -1}\n aria-hidden={!isScrollIndicatorVisible}\n disabled={!isScrollIndicatorVisible}\n onClick={() => {\n const container = containerRef.current;\n if (container === null) return;\n\n container.scrollTo({\n top: container.scrollHeight,\n behavior: \"smooth\",\n });\n }}\n >\n <span className=\"lb-icon-container\">\n <ArrowDownIcon />\n </span>\n </button>\n </div>\n </div>\n <AiChatComposer\n key={chatId}\n chatId={chatId}\n copilotId={copilotId as CopilotId}\n overrides={$}\n autoFocus={autoFocus}\n onUserMessageCreate={({ id }) => setLastSendMessageId(id)}\n />\n </div>\n </div>\n );\n }\n);\n\nfunction Messages({\n messages,\n overrides,\n components,\n onDistanceToBottomChange,\n}: {\n messages: readonly UiChatMessage[];\n overrides?: Partial<GlobalOverrides & AiChatMessageOverrides>;\n components?: Partial<GlobalComponents>;\n onDistanceToBottomChange: () => void;\n}) {\n useLayoutEffect(() => {\n onDistanceToBottomChange();\n }, [onDistanceToBottomChange]);\n\n return messages.map((message) => {\n if (message.role === \"user\") {\n return (\n <AiChatUserMessage\n key={message.id}\n message={message}\n overrides={overrides}\n />\n );\n } else if (message.role === \"assistant\") {\n return (\n <AiChatAssistantMessage\n key={message.id}\n message={message}\n overrides={overrides}\n components={components}\n />\n );\n } else {\n return null;\n }\n });\n}\n"],"names":["forwardRef","overrides","useAiChatMessages","useOverrides","useRef","useState","useClient","kInternal","useImperativeHandle","useEffect","container","jsxs","classNames","jsx","RegisterAiKnowledge","SpinnerIcon","ArrowDownIcon","AiChatComposer","useLayoutEffect","AiChatUserMessage","AiChatAssistantMessage"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AiChat.cjs","sources":["../../src/components/AiChat.tsx"],"sourcesContent":["import type {\n AiKnowledgeSource,\n AiOpaqueToolDefinition,\n CopilotId,\n MessageId,\n} from \"@liveblocks/core\";\nimport { kInternal } from \"@liveblocks/core\";\nimport {\n RegisterAiKnowledge,\n useAiChatMessages,\n useClient,\n} from \"@liveblocks/react\";\nimport { useLayoutEffect } from \"@liveblocks/react/_private\";\nimport {\n type ComponentProps,\n type ComponentType,\n forwardRef,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from \"react\";\n\nimport type { GlobalComponents } from \"../components\";\nimport { ArrowDownIcon } from \"../icons/ArrowDown\";\nimport { SpinnerIcon } from \"../icons/Spinner\";\nimport {\n type AiChatComposerOverrides,\n type AiChatMessageOverrides,\n type AiChatOverrides,\n type GlobalOverrides,\n useOverrides,\n} from \"../overrides\";\nimport { classNames } from \"../utils/class-names\";\nimport { useVisible } from \"../utils/use-visible\";\nimport { AiChatAssistantMessage } from \"./internal/AiChatAssistantMessage\";\nimport { AiChatComposer } from \"./internal/AiChatComposer\";\nimport { AiChatUserMessage } from \"./internal/AiChatUserMessage\";\n\n// No props for now\ntype AiChatComponentsEmptyProps = Record<string, never>;\n\n// No props for now\ntype AiChatComponentsLoadingProps = Record<string, never>;\n\n// TODO: Add Markdown components\ntype AiChatComponents = {\n Empty: ComponentType<AiChatComponentsEmptyProps>;\n Loading: ComponentType<AiChatComponentsLoadingProps>;\n};\n\n/**\n * The minimum number of pixels from the bottom of the scrollable area\n * before showing the scroll to bottom indicator.\n */\nconst MIN_DISTANCE_BOTTOM_SCROLL_INDICATOR = 50;\n\nexport interface AiChatProps extends ComponentProps<\"div\"> {\n /**\n * The id of the chat the composer belongs to.\n */\n chatId: string;\n /**\n * Whether to focus the chat composer on mount.\n */\n autoFocus?: boolean;\n /**\n * The id of the copilot to use to send the message.\n */\n copilotId?: string;\n /**\n * The contextual knowledge to include in the chat. May be used by the assistant when generating responses.\n * Any knowledge you provide via this prop will be added to any already globally registered knowledge via <RegisterAiKnowledge />.\n */\n knowledge?: AiKnowledgeSource[];\n /**\n * Tool definitions to make available within this chat. May be used by the assistant when generating responses.\n */\n tools?: Record<string, AiOpaqueToolDefinition>;\n /**\n * The layout of the chat and its composer.\n */\n layout?: \"inset\" | \"compact\";\n /**\n * Override the component's strings.\n */\n overrides?: Partial<\n GlobalOverrides &\n AiChatMessageOverrides &\n AiChatComposerOverrides &\n AiChatOverrides\n >;\n /**\n * Override the component's components.\n */\n components?: Partial<GlobalComponents & AiChatComponents>;\n}\n\nconst defaultComponents: AiChatComponents = {\n Empty: () => null,\n Loading: () => (\n <div className=\"lb-loading lb-ai-chat-loading\">\n <SpinnerIcon />\n </div>\n ),\n};\n\nexport const AiChat = forwardRef<HTMLDivElement, AiChatProps>(\n (\n {\n chatId,\n copilotId,\n autoFocus,\n overrides,\n knowledge,\n tools = {},\n layout = \"inset\",\n components,\n className,\n ...props\n },\n forwardedRef\n ) => {\n const { messages, isLoading, error } = useAiChatMessages(chatId);\n const $ = useOverrides(overrides);\n const Empty = components?.Empty ?? defaultComponents.Empty;\n const Loading = components?.Loading ?? defaultComponents.Loading;\n\n const containerRef = useRef<HTMLDivElement | null>(null);\n const containerBottomRef = useRef<HTMLDivElement | null>(null);\n const isScrollAtBottom = useVisible(containerBottomRef, {\n enabled: !isLoading && !error,\n root: containerRef,\n rootMargin: MIN_DISTANCE_BOTTOM_SCROLL_INDICATOR,\n });\n const isScrollIndicatorVisible =\n isLoading || error ? false : !isScrollAtBottom;\n\n const client = useClient();\n const ai = client[kInternal].ai;\n\n const [lastSentMessageId, setLastSentMessageId] =\n useState<MessageId | null>(null);\n\n useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(\n forwardedRef,\n () => containerRef.current,\n []\n );\n\n // Register the provided tools to the chat on mount and unregister them on unmount\n useEffect(() => {\n Object.entries(tools).map(([key, value]) =>\n ai.registerChatTool(chatId, key, value)\n );\n return () => {\n Object.entries(tools).map(([key]) =>\n ai.unregisterChatTool(chatId, key)\n );\n };\n }, [ai, chatId, tools]);\n\n const scrollToBottomCallbackRef =\n useRef<(behavior: \"instant\" | \"smooth\") => void>(undefined);\n if (scrollToBottomCallbackRef.current === undefined) {\n scrollToBottomCallbackRef.current = function (\n behavior: \"instant\" | \"smooth\"\n ) {\n const container = containerRef.current;\n if (container === null) return;\n\n container.scrollTo({\n top: container.scrollHeight,\n behavior,\n });\n };\n }\n const scrollToBottom = scrollToBottomCallbackRef.current;\n\n return (\n <div\n ref={containerRef}\n {...props}\n className={classNames(\n \"lb-root lb-ai-chat\",\n layout === \"compact\"\n ? \"lb-ai-chat:layout-compact\"\n : \"lb-ai-chat:layout-inset\",\n className\n )}\n >\n {knowledge\n ? knowledge.map((source, index) => (\n <RegisterAiKnowledge\n key={index}\n description={source.description}\n value={source.value}\n // knowledgeKey={source.knowledgeKey}\n />\n ))\n : null}\n <div className=\"lb-ai-chat-content\">\n {isLoading ? (\n <Loading />\n ) : error !== undefined ? (\n <div className=\"lb-error lb-ai-chat-error\">\n {$.AI_CHAT_MESSAGES_ERROR(error)}\n </div>\n ) : messages.length === 0 ? (\n <Empty />\n ) : (\n <>\n <AutoScrollHandler\n lastSentMessageId={lastSentMessageId}\n scrollToBottom={scrollToBottom}\n />\n <div className=\"lb-ai-chat-messages\">\n {messages.map((message) => {\n if (message.role === \"user\") {\n return (\n <AiChatUserMessage\n key={message.id}\n message={message}\n overrides={overrides}\n />\n );\n } else if (message.role === \"assistant\") {\n return (\n <AiChatAssistantMessage\n key={message.id}\n message={message}\n overrides={overrides}\n components={components}\n />\n );\n } else {\n return null;\n }\n })}\n </div>\n </>\n )}\n </div>\n\n <div className=\"lb-ai-chat-footer\">\n <div className=\"lb-ai-chat-footer-actions\">\n <div\n className=\"lb-root lb-elevation lb-elevation-moderate lb-ai-chat-scroll-indicator\"\n data-visible={isScrollIndicatorVisible ? \"\" : undefined}\n >\n <button\n className=\"lb-ai-chat-scroll-indicator-button\"\n tabIndex={isScrollIndicatorVisible ? 0 : -1}\n aria-hidden={!isScrollIndicatorVisible}\n disabled={!isScrollIndicatorVisible}\n onClick={() => scrollToBottom(\"smooth\")}\n >\n <span className=\"lb-icon-container\">\n <ArrowDownIcon />\n </span>\n </button>\n </div>\n </div>\n <AiChatComposer\n key={chatId}\n chatId={chatId}\n copilotId={copilotId as CopilotId}\n overrides={$}\n autoFocus={autoFocus}\n onUserMessageCreate={({ id }) => setLastSentMessageId(id)}\n className={\n layout === \"inset\"\n ? \"lb-elevation lb-elevation-moderate\"\n : undefined\n }\n />\n </div>\n {/* This invisible element is a trick which allows us to use IntersectionObserver to detect when the\n * scrollable area is fully scrolled to the bottom instead of manually tracking the scroll position\n * and having to deal with resizes, etc.\n *\n * It's positioned at the bottom of the scrollable area and reliably only becomes \"visible\" to the\n * IntersectionObserver when the scrollable area is fully scrolled.\n */}\n <div\n ref={containerBottomRef}\n style={{ position: \"sticky\", height: 0 }}\n aria-hidden\n data-scroll-at-bottom={isScrollAtBottom ? \"\" : undefined}\n />\n </div>\n );\n }\n);\n\nfunction AutoScrollHandler({\n lastSentMessageId,\n scrollToBottom,\n}: {\n lastSentMessageId: MessageId | null;\n scrollToBottom: (behavior: \"instant\" | \"smooth\") => void;\n}) {\n // Scroll to bottom when the component first mounts\n useLayoutEffect(() => {\n scrollToBottom(\"instant\");\n }, [scrollToBottom]);\n\n // Scroll to bottom when sending a new message\n useEffect(() => {\n scrollToBottom(\"smooth\");\n }, [lastSentMessageId, scrollToBottom]);\n\n return null;\n}\n"],"names":["jsx","SpinnerIcon","forwardRef","overrides","useAiChatMessages","useOverrides","useRef","useVisible","useClient","kInternal","useState","useImperativeHandle","useEffect","jsxs","classNames","RegisterAiKnowledge","Fragment","AiChatUserMessage","AiChatAssistantMessage","ArrowDownIcon","AiChatComposer","useLayoutEffect"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsxs,
|
|
1
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import { kInternal } from '@liveblocks/core';
|
|
3
3
|
import { useAiChatMessages, useClient, RegisterAiKnowledge } from '@liveblocks/react';
|
|
4
4
|
import { useLayoutEffect } from '@liveblocks/react/_private';
|
|
@@ -7,11 +7,19 @@ import { ArrowDownIcon } from '../icons/ArrowDown.js';
|
|
|
7
7
|
import { SpinnerIcon } from '../icons/Spinner.js';
|
|
8
8
|
import { useOverrides } from '../overrides.js';
|
|
9
9
|
import { classNames } from '../utils/class-names.js';
|
|
10
|
+
import { useVisible } from '../utils/use-visible.js';
|
|
10
11
|
import { AiChatAssistantMessage } from './internal/AiChatAssistantMessage.js';
|
|
11
12
|
import { AiChatComposer } from './internal/AiChatComposer.js';
|
|
12
13
|
import { AiChatUserMessage } from './internal/AiChatUserMessage.js';
|
|
13
14
|
|
|
14
|
-
const
|
|
15
|
+
const MIN_DISTANCE_BOTTOM_SCROLL_INDICATOR = 50;
|
|
16
|
+
const defaultComponents = {
|
|
17
|
+
Empty: () => null,
|
|
18
|
+
Loading: () => /* @__PURE__ */ jsx("div", {
|
|
19
|
+
className: "lb-loading lb-ai-chat-loading",
|
|
20
|
+
children: /* @__PURE__ */ jsx(SpinnerIcon, {})
|
|
21
|
+
})
|
|
22
|
+
};
|
|
15
23
|
const AiChat = forwardRef(
|
|
16
24
|
({
|
|
17
25
|
chatId,
|
|
@@ -20,19 +28,26 @@ const AiChat = forwardRef(
|
|
|
20
28
|
overrides,
|
|
21
29
|
knowledge,
|
|
22
30
|
tools = {},
|
|
31
|
+
layout = "inset",
|
|
23
32
|
components,
|
|
24
33
|
className,
|
|
25
34
|
...props
|
|
26
35
|
}, forwardedRef) => {
|
|
27
36
|
const { messages, isLoading, error } = useAiChatMessages(chatId);
|
|
28
37
|
const $ = useOverrides(overrides);
|
|
38
|
+
const Empty = components?.Empty ?? defaultComponents.Empty;
|
|
39
|
+
const Loading = components?.Loading ?? defaultComponents.Loading;
|
|
29
40
|
const containerRef = useRef(null);
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
41
|
+
const containerBottomRef = useRef(null);
|
|
42
|
+
const isScrollAtBottom = useVisible(containerBottomRef, {
|
|
43
|
+
enabled: !isLoading && !error,
|
|
44
|
+
root: containerRef,
|
|
45
|
+
rootMargin: MIN_DISTANCE_BOTTOM_SCROLL_INDICATOR
|
|
46
|
+
});
|
|
47
|
+
const isScrollIndicatorVisible = isLoading || error ? false : !isScrollAtBottom;
|
|
33
48
|
const client = useClient();
|
|
34
49
|
const ai = client[kInternal].ai;
|
|
35
|
-
const [
|
|
50
|
+
const [lastSentMessageId, setLastSentMessageId] = useState(null);
|
|
36
51
|
useImperativeHandle(
|
|
37
52
|
forwardedRef,
|
|
38
53
|
() => containerRef.current,
|
|
@@ -48,74 +63,27 @@ const AiChat = forwardRef(
|
|
|
48
63
|
);
|
|
49
64
|
};
|
|
50
65
|
}, [ai, chatId, tools]);
|
|
51
|
-
useEffect(() => {
|
|
52
|
-
const container = containerRef.current;
|
|
53
|
-
if (container === null)
|
|
54
|
-
return;
|
|
55
|
-
function handleScrollChange() {
|
|
56
|
-
const container2 = containerRef.current;
|
|
57
|
-
if (container2 === null)
|
|
58
|
-
return;
|
|
59
|
-
setDistanceToBottom(
|
|
60
|
-
container2.scrollHeight - container2.clientHeight - container2.scrollTop
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
container.addEventListener("scroll", handleScrollChange);
|
|
64
|
-
return () => {
|
|
65
|
-
container.removeEventListener("scroll", handleScrollChange);
|
|
66
|
-
};
|
|
67
|
-
}, []);
|
|
68
|
-
useEffect(() => {
|
|
69
|
-
const container = containerRef.current;
|
|
70
|
-
if (container === null)
|
|
71
|
-
return;
|
|
72
|
-
setDistanceToBottom(
|
|
73
|
-
container.scrollHeight - container.clientHeight - container.scrollTop
|
|
74
|
-
);
|
|
75
|
-
}, [messages]);
|
|
76
|
-
useEffect(() => {
|
|
77
|
-
const container = containerRef.current;
|
|
78
|
-
if (container === null)
|
|
79
|
-
return;
|
|
80
|
-
container.scrollTo({
|
|
81
|
-
top: container.scrollHeight,
|
|
82
|
-
behavior: "smooth"
|
|
83
|
-
});
|
|
84
|
-
}, [lastSendMessageId]);
|
|
85
|
-
useEffect(() => {
|
|
86
|
-
const container = containerRef.current;
|
|
87
|
-
if (container === null)
|
|
88
|
-
return;
|
|
89
|
-
const observer = new ResizeObserver(() => {
|
|
90
|
-
const container2 = containerRef.current;
|
|
91
|
-
if (container2 === null)
|
|
92
|
-
return;
|
|
93
|
-
setDistanceToBottom(
|
|
94
|
-
container2.scrollHeight - container2.clientHeight - container2.scrollTop
|
|
95
|
-
);
|
|
96
|
-
});
|
|
97
|
-
observer.observe(container);
|
|
98
|
-
return () => {
|
|
99
|
-
observer.disconnect();
|
|
100
|
-
};
|
|
101
|
-
}, []);
|
|
102
66
|
const scrollToBottomCallbackRef = useRef(void 0);
|
|
103
67
|
if (scrollToBottomCallbackRef.current === void 0) {
|
|
104
|
-
scrollToBottomCallbackRef.current = function() {
|
|
68
|
+
scrollToBottomCallbackRef.current = function(behavior) {
|
|
105
69
|
const container = containerRef.current;
|
|
106
70
|
if (container === null)
|
|
107
71
|
return;
|
|
108
72
|
container.scrollTo({
|
|
109
73
|
top: container.scrollHeight,
|
|
110
|
-
behavior
|
|
74
|
+
behavior
|
|
111
75
|
});
|
|
112
76
|
};
|
|
113
77
|
}
|
|
114
|
-
const
|
|
78
|
+
const scrollToBottom = scrollToBottomCallbackRef.current;
|
|
115
79
|
return /* @__PURE__ */ jsxs("div", {
|
|
116
80
|
ref: containerRef,
|
|
117
81
|
...props,
|
|
118
|
-
className: classNames(
|
|
82
|
+
className: classNames(
|
|
83
|
+
"lb-root lb-ai-chat",
|
|
84
|
+
layout === "compact" ? "lb-ai-chat:layout-compact" : "lb-ai-chat:layout-inset",
|
|
85
|
+
className
|
|
86
|
+
),
|
|
119
87
|
children: [
|
|
120
88
|
knowledge ? knowledge.map((source, index) => /* @__PURE__ */ jsx(RegisterAiKnowledge, {
|
|
121
89
|
description: source.description,
|
|
@@ -123,20 +91,35 @@ const AiChat = forwardRef(
|
|
|
123
91
|
}, index)) : null,
|
|
124
92
|
/* @__PURE__ */ jsx("div", {
|
|
125
93
|
className: "lb-ai-chat-content",
|
|
126
|
-
children: isLoading ? /* @__PURE__ */ jsx("div", {
|
|
127
|
-
className: "lb-loading lb-ai-chat-loading",
|
|
128
|
-
children: /* @__PURE__ */ jsx(SpinnerIcon, {})
|
|
129
|
-
}) : error !== void 0 ? /* @__PURE__ */ jsx("div", {
|
|
94
|
+
children: isLoading ? /* @__PURE__ */ jsx(Loading, {}) : error !== void 0 ? /* @__PURE__ */ jsx("div", {
|
|
130
95
|
className: "lb-error lb-ai-chat-error",
|
|
131
96
|
children: $.AI_CHAT_MESSAGES_ERROR(error)
|
|
132
|
-
}) : /* @__PURE__ */ jsx(
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
97
|
+
}) : messages.length === 0 ? /* @__PURE__ */ jsx(Empty, {}) : /* @__PURE__ */ jsxs(Fragment, {
|
|
98
|
+
children: [
|
|
99
|
+
/* @__PURE__ */ jsx(AutoScrollHandler, {
|
|
100
|
+
lastSentMessageId,
|
|
101
|
+
scrollToBottom
|
|
102
|
+
}),
|
|
103
|
+
/* @__PURE__ */ jsx("div", {
|
|
104
|
+
className: "lb-ai-chat-messages",
|
|
105
|
+
children: messages.map((message) => {
|
|
106
|
+
if (message.role === "user") {
|
|
107
|
+
return /* @__PURE__ */ jsx(AiChatUserMessage, {
|
|
108
|
+
message,
|
|
109
|
+
overrides
|
|
110
|
+
}, message.id);
|
|
111
|
+
} else if (message.role === "assistant") {
|
|
112
|
+
return /* @__PURE__ */ jsx(AiChatAssistantMessage, {
|
|
113
|
+
message,
|
|
114
|
+
overrides,
|
|
115
|
+
components
|
|
116
|
+
}, message.id);
|
|
117
|
+
} else {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
})
|
|
121
|
+
})
|
|
122
|
+
]
|
|
140
123
|
})
|
|
141
124
|
}),
|
|
142
125
|
/* @__PURE__ */ jsxs("div", {
|
|
@@ -145,22 +128,14 @@ const AiChat = forwardRef(
|
|
|
145
128
|
/* @__PURE__ */ jsx("div", {
|
|
146
129
|
className: "lb-ai-chat-footer-actions",
|
|
147
130
|
children: /* @__PURE__ */ jsx("div", {
|
|
148
|
-
className: "lb-elevation lb-ai-chat-scroll-indicator",
|
|
131
|
+
className: "lb-root lb-elevation lb-elevation-moderate lb-ai-chat-scroll-indicator",
|
|
149
132
|
"data-visible": isScrollIndicatorVisible ? "" : void 0,
|
|
150
133
|
children: /* @__PURE__ */ jsx("button", {
|
|
151
134
|
className: "lb-ai-chat-scroll-indicator-button",
|
|
152
135
|
tabIndex: isScrollIndicatorVisible ? 0 : -1,
|
|
153
136
|
"aria-hidden": !isScrollIndicatorVisible,
|
|
154
137
|
disabled: !isScrollIndicatorVisible,
|
|
155
|
-
onClick: () =>
|
|
156
|
-
const container = containerRef.current;
|
|
157
|
-
if (container === null)
|
|
158
|
-
return;
|
|
159
|
-
container.scrollTo({
|
|
160
|
-
top: container.scrollHeight,
|
|
161
|
-
behavior: "smooth"
|
|
162
|
-
});
|
|
163
|
-
},
|
|
138
|
+
onClick: () => scrollToBottom("smooth"),
|
|
164
139
|
children: /* @__PURE__ */ jsx("span", {
|
|
165
140
|
className: "lb-icon-container",
|
|
166
141
|
children: /* @__PURE__ */ jsx(ArrowDownIcon, {})
|
|
@@ -173,39 +148,32 @@ const AiChat = forwardRef(
|
|
|
173
148
|
copilotId,
|
|
174
149
|
overrides: $,
|
|
175
150
|
autoFocus,
|
|
176
|
-
onUserMessageCreate: ({ id }) =>
|
|
151
|
+
onUserMessageCreate: ({ id }) => setLastSentMessageId(id),
|
|
152
|
+
className: layout === "inset" ? "lb-elevation lb-elevation-moderate" : void 0
|
|
177
153
|
}, chatId)
|
|
178
154
|
]
|
|
155
|
+
}),
|
|
156
|
+
/* @__PURE__ */ jsx("div", {
|
|
157
|
+
ref: containerBottomRef,
|
|
158
|
+
style: { position: "sticky", height: 0 },
|
|
159
|
+
"aria-hidden": true,
|
|
160
|
+
"data-scroll-at-bottom": isScrollAtBottom ? "" : void 0
|
|
179
161
|
})
|
|
180
162
|
]
|
|
181
163
|
});
|
|
182
164
|
}
|
|
183
165
|
);
|
|
184
|
-
function
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
components,
|
|
188
|
-
onDistanceToBottomChange
|
|
166
|
+
function AutoScrollHandler({
|
|
167
|
+
lastSentMessageId,
|
|
168
|
+
scrollToBottom
|
|
189
169
|
}) {
|
|
190
170
|
useLayoutEffect(() => {
|
|
191
|
-
|
|
192
|
-
}, [
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
overrides
|
|
198
|
-
}, message.id);
|
|
199
|
-
} else if (message.role === "assistant") {
|
|
200
|
-
return /* @__PURE__ */ jsx(AiChatAssistantMessage, {
|
|
201
|
-
message,
|
|
202
|
-
overrides,
|
|
203
|
-
components
|
|
204
|
-
}, message.id);
|
|
205
|
-
} else {
|
|
206
|
-
return null;
|
|
207
|
-
}
|
|
208
|
-
});
|
|
171
|
+
scrollToBottom("instant");
|
|
172
|
+
}, [scrollToBottom]);
|
|
173
|
+
useEffect(() => {
|
|
174
|
+
scrollToBottom("smooth");
|
|
175
|
+
}, [lastSentMessageId, scrollToBottom]);
|
|
176
|
+
return null;
|
|
209
177
|
}
|
|
210
178
|
|
|
211
179
|
export { AiChat };
|