@katechat/ui 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +11 -0
  2. package/dist/cjs/index.css +491 -0
  3. package/dist/cjs/index.css.map +7 -0
  4. package/dist/cjs/index.js +75302 -0
  5. package/dist/cjs/index.js.map +7 -0
  6. package/dist/esm/index.css +491 -0
  7. package/dist/esm/index.css.map +7 -0
  8. package/dist/esm/index.js +75301 -0
  9. package/dist/esm/index.js.map +7 -0
  10. package/dist/index.css +1 -0
  11. package/dist/index.js +539 -0
  12. package/dist/types/tsconfig.tsbuildinfo +1 -0
  13. package/package.json +27 -4
  14. package/.prettierrc +0 -9
  15. package/esbuild.js +0 -56
  16. package/jest.config.js +0 -24
  17. package/postcss.config.cjs +0 -14
  18. package/src/__mocks__/fileMock.js +0 -1
  19. package/src/__mocks__/styleMock.js +0 -1
  20. package/src/components/chat/ChatMessagesContainer.module.scss +0 -77
  21. package/src/components/chat/ChatMessagesContainer.tsx +0 -151
  22. package/src/components/chat/ChatMessagesList.tsx +0 -216
  23. package/src/components/chat/index.ts +0 -4
  24. package/src/components/chat/input/ChatInput.module.scss +0 -113
  25. package/src/components/chat/input/ChatInput.tsx +0 -259
  26. package/src/components/chat/input/index.ts +0 -1
  27. package/src/components/chat/message/ChatMessage.Carousel.module.scss +0 -7
  28. package/src/components/chat/message/ChatMessage.module.scss +0 -378
  29. package/src/components/chat/message/ChatMessage.tsx +0 -271
  30. package/src/components/chat/message/ChatMessagePreview.tsx +0 -22
  31. package/src/components/chat/message/LinkedChatMessage.tsx +0 -64
  32. package/src/components/chat/message/MessageStatus.tsx +0 -38
  33. package/src/components/chat/message/controls/CopyMessageButton.tsx +0 -32
  34. package/src/components/chat/message/index.ts +0 -4
  35. package/src/components/icons/ProviderIcon.tsx +0 -49
  36. package/src/components/icons/index.ts +0 -1
  37. package/src/components/index.ts +0 -3
  38. package/src/components/modal/ImagePopup.tsx +0 -97
  39. package/src/components/modal/index.ts +0 -1
  40. package/src/controls/FileDropzone/FileDropzone.module.scss +0 -15
  41. package/src/controls/FileDropzone/FileDropzone.tsx +0 -120
  42. package/src/controls/index.ts +0 -1
  43. package/src/core/ai.ts +0 -1
  44. package/src/core/index.ts +0 -4
  45. package/src/core/message.ts +0 -59
  46. package/src/core/model.ts +0 -23
  47. package/src/core/user.ts +0 -8
  48. package/src/hooks/index.ts +0 -2
  49. package/src/hooks/useIntersectionObserver.ts +0 -24
  50. package/src/hooks/useTheme.tsx +0 -66
  51. package/src/index.ts +0 -5
  52. package/src/lib/__tests__/markdown.parser.test.ts +0 -289
  53. package/src/lib/__tests__/markdown.parser.testUtils.ts +0 -31
  54. package/src/lib/__tests__/markdown.parser_sanitizeUrl.test.ts +0 -130
  55. package/src/lib/assert.ts +0 -14
  56. package/src/lib/markdown.parser.ts +0 -189
  57. package/src/setupTests.ts +0 -1
  58. package/src/types/scss.d.ts +0 -4
  59. package/tsconfig.json +0 -26
@@ -1,378 +0,0 @@
1
- .messageContainer {
2
- width: 100%;
3
- overflow-x: hidden;
4
- display: flex;
5
- flex-direction: row;
6
-
7
- .main,
8
- .linked {
9
- padding: var(--mantine-spacing-md);
10
- flex-grow: 1;
11
- width: 50%;
12
- display: flex;
13
- flex-direction: column;
14
- border-radius: var(--mantine-radius-md);
15
- }
16
-
17
- .linked {
18
- margin: 0 var(--mantine-spacing-md);
19
- }
20
-
21
- .linkedToggle {
22
- display: none;
23
- }
24
- }
25
-
26
- @media (max-width: 1280px) {
27
- .messageContainer {
28
- flex-direction: column;
29
- position: relative;
30
-
31
- .linkedToggle {
32
- position: absolute;
33
- right: 0.5rem;
34
- top: 0.5rem;
35
- display: flex;
36
- z-index: 5;
37
- }
38
-
39
- .main,
40
- .linked {
41
- width: 100%;
42
- margin: 0;
43
- border-radius: 0;
44
- }
45
-
46
- .hidden {
47
- display: none;
48
- }
49
- }
50
- }
51
-
52
- .linkedMessageContainer {
53
- width: 100%;
54
- overflow-x: hidden;
55
- display: flex;
56
- flex-direction: column;
57
- }
58
-
59
- .message {
60
- display: flex;
61
- flex-direction: column;
62
- align-items: stretch;
63
- padding: var(--mb-padding, var(--mantine-spacing-sm));
64
- border-radius: var(--mantine-radius-md);
65
-
66
- margin-top: var(--mantine-spacing-xs);
67
-
68
- :global p {
69
- margin-block-start: 0;
70
- margin-block-end: 0;
71
- }
72
-
73
- :global blockquote {
74
- padding: 0 var(--mb-padding, var(--mantine-spacing-sm));
75
- border-left: 0.5rem solid var(--mantine-color-default-border);
76
- margin: 0.25rem 0;
77
- }
78
-
79
- :global hr {
80
- width: 100%;
81
- margin: 0.5rem 0 0.25rem;
82
- border: none;
83
- border-top: 1px solid var(--mantine-color-default-border);
84
- }
85
-
86
- :global img {
87
- max-width: 256px;
88
- max-height: 256px;
89
- align-self: center;
90
- height: auto;
91
- margin: 0 auto;
92
- cursor: pointer;
93
- }
94
-
95
- &.preview {
96
- height: 200px;
97
- padding: var(--mb-padding, var(--mantine-spacing-sm)) 0 0 0;
98
- font-size: 80%;
99
-
100
- :global img {
101
- max-width: 50%;
102
- height: auto;
103
- }
104
- }
105
-
106
- .detailsBlock {
107
- margin-top: 1rem;
108
- font-size: 80%;
109
- display: flex;
110
- flex-direction: column;
111
- gap: 0;
112
-
113
- :global ol,
114
- :global ul {
115
- margin: 0.25rem 0;
116
- padding: 0 0 0 1rem;
117
- }
118
-
119
- :global .message-details-header {
120
- margin-bottom: var(--mantine-spacing-md);
121
- margin-left: 1rem;
122
-
123
- :global svg {
124
- color: var(--mantine-color-gray-7);
125
- }
126
- }
127
-
128
- :global .message-details-content {
129
- padding-left: var(--mantine-spacing-md);
130
- border-left: 1px solid var(--mantine-color-gray-7);
131
- margin-left: 1.5rem;
132
- }
133
- }
134
-
135
- :global table {
136
- margin-top: var(--mantine-spacing-md);
137
- margin-bottom: var(--mantine-spacing-md);
138
- border-spacing: 0;
139
-
140
- td,
141
- th {
142
- padding: var(--mb-padding, var(--mantine-spacing-xs));
143
- margin: 0;
144
- border-top: 1px solid var(--mantine-color-default-border);
145
- border-left: 1px solid var(--mantine-color-default-border);
146
- }
147
-
148
- tr td:last-child,
149
- tr th:last-child {
150
- border-right: 1px solid var(--mantine-color-default-border);
151
- }
152
- tr:last-child td {
153
- border-bottom: 1px solid var(--mantine-color-default-border);
154
- }
155
-
156
- tr:nth-child(2n) {
157
- background-color: var(--mantine-color-dark-5);
158
- }
159
-
160
- thead {
161
- tr {
162
- background-color: var(--mantine-color-dark-5);
163
- }
164
-
165
- tr:first-child th:first-child {
166
- border-top-left-radius: var(--mantine-radius-md);
167
- }
168
- tr:first-child th:last-child {
169
- border-top-right-radius: var(--mantine-radius-md);
170
- }
171
- }
172
-
173
- tr:last-child td:first-child {
174
- border-bottom-left-radius: var(--mantine-radius-md);
175
- }
176
- tr:last-child td:last-child {
177
- border-bottom-right-radius: var(--mantine-radius-md);
178
- }
179
- }
180
-
181
- :global pre {
182
- margin: 0.5rem 0;
183
- padding: 1em;
184
- clip-path: none;
185
- font-size: 90%;
186
- transition: all var(--theme-default-time) ease;
187
- font-family: var(--monospace-font-family);
188
- overflow-x: auto;
189
- }
190
-
191
- :global .code-header {
192
- font-size: 0.875rem;
193
- margin-top: 0.5rem;
194
- padding: 0.25rem 1rem 0.25rem 0.5rem;
195
- border-top-right-radius: 0.25em;
196
- border-top-left-radius: 0.25em;
197
-
198
- display: flex;
199
- justify-content: space-between;
200
- align-items: center;
201
- flex-direction: row;
202
- white-space: normal;
203
- gap: 0.5rem;
204
-
205
- :global .title {
206
- width: fit-content;
207
- font-weight: 700;
208
- display: flex;
209
- align-items: center;
210
- flex-direction: row;
211
- cursor: pointer;
212
- user-select: none;
213
- min-height: 2rem;
214
-
215
- :global .header-toggle {
216
- margin-right: 0.5rem;
217
- display: none;
218
- display: inline-block;
219
- transform: rotateZ(90deg);
220
- transition: transform var(--theme-default-time) ease;
221
- }
222
- }
223
-
224
- :global .code-header-actions {
225
- align-content: flex-end;
226
- }
227
-
228
- :global .action-btn {
229
- padding: 0.1rem;
230
- line-height: 1;
231
- display: flex;
232
- justify-content: center;
233
- align-items: center;
234
- width: fit-content;
235
- gap: 0.25rem;
236
- cursor: pointer;
237
- }
238
-
239
- &.collapsed {
240
- border-bottom-right-radius: 0.25em;
241
- border-bottom-left-radius: 0.25em;
242
- margin-bottom: 0;
243
- :global .title {
244
- :global .header-toggle {
245
- transform: translateY(0.2rem) rotateZ(0deg);
246
- }
247
- }
248
-
249
- .code-copy-btn {
250
- display: none;
251
- }
252
- }
253
- }
254
-
255
- :global .code-block {
256
- :global pre {
257
- margin: 0;
258
- white-space: pre;
259
- }
260
-
261
- flex-grow: 1;
262
- overflow-x: auto;
263
- margin-top: 0;
264
- margin-bottom: 1rem;
265
- border-bottom-right-radius: 0.25em;
266
- border-bottom-left-radius: 0.25em;
267
- transition:
268
- clip-path 0.5s ease,
269
- height 0.5s ease,
270
- opacity 0.5s ease;
271
-
272
- &.collapsed {
273
- clip-path: inset(0 0 100% 0);
274
- overflow-y: hidden;
275
- height: 0;
276
- opacity: 0;
277
- }
278
- }
279
-
280
- :global .code-data {
281
- display: none;
282
- }
283
-
284
- :global .code-footer {
285
- font-size: 90%;
286
- display: flex;
287
- align-items: flex-start;
288
- margin-bottom: 0.7rem;
289
-
290
- &.collapsed {
291
- display: none;
292
- }
293
- }
294
-
295
- :global .katex-html {
296
- display: inline-block;
297
- margin: 0.5rem 0.5rem 0 0;
298
- }
299
- }
300
-
301
- .messageFooter {
302
- display: flex;
303
- flex-direction: row;
304
- align-items: center;
305
- gap: 0.5rem;
306
- padding: 0.5rem 0 0.25rem;
307
- opacity: 0.5;
308
- transition: opacity var(--theme-default-time) ease;
309
-
310
- :global .check-icon {
311
- display: none;
312
- }
313
-
314
- &:hover {
315
- opacity: 1;
316
- }
317
- }
318
-
319
- [data-mantine-color-scheme="dark"] {
320
- .messageContainer {
321
- :global .code-block,
322
- :global pre {
323
- background-color: var(--mantine-color-dark-6);
324
- }
325
-
326
- :global .code-header {
327
- background-color: var(--mantine-color-dark-6);
328
- &:hover {
329
- background-color: var(--mantine-color-dark-5);
330
- }
331
- }
332
-
333
- .message.user {
334
- background-color: var(--mantine-color-dark-9);
335
- }
336
- .message.error {
337
- color: var(--mantine-color-yellow-9);
338
- }
339
-
340
- .linked {
341
- background-color: var(--mantine-color-dark-8);
342
- }
343
- }
344
- }
345
-
346
- [data-mantine-color-scheme="light"] {
347
- .messageContainer {
348
- :global .code-block,
349
- :global pre {
350
- background-color: var(--mantine-color-gray-3);
351
- }
352
-
353
- :global .code-header {
354
- background-color: var(--mantine-color-gray-3);
355
- &:hover {
356
- background-color: var(--mantine-color-gray-4);
357
- }
358
- }
359
-
360
- .message.user {
361
- background-color: var(--mantine-color-blue-0);
362
- }
363
- .message.error {
364
- color: var(--mantine-color-red-5);
365
- }
366
- .linked {
367
- background-color: var(--mantine-color-gray-3);
368
- }
369
-
370
- tr:nth-child(2n) {
371
- background-color: var(--mantine-color-blue-1);
372
- }
373
-
374
- thead tr {
375
- background-color: var(--mantine-color-blue-1);
376
- }
377
- }
378
- }
@@ -1,271 +0,0 @@
1
- import React, { useCallback, useEffect, useMemo, useRef } from "react";
2
- import { Text, Group, Avatar, Switch, Loader, Button, Collapse, Box } from "@mantine/core";
3
- import { Carousel } from "@mantine/carousel";
4
- import { IconRobot, IconUser } from "@tabler/icons-react";
5
- import { MessageRole, Message } from "@/core/message";
6
- import { LinkedChatMessage, MessageStatus } from "@/components/chat";
7
- import { debounce } from "lodash";
8
- import { ProviderIcon } from "@/components/icons/ProviderIcon";
9
- import { Model } from "@/core";
10
- import { CopyMessageButton } from "./controls/CopyMessageButton";
11
-
12
- import classes from "./ChatMessage.module.scss";
13
- import carouselClasses from "./ChatMessage.Carousel.module.scss";
14
-
15
- interface ChatMessageProps {
16
- message: Message;
17
- index: number;
18
- disabled?: boolean;
19
- pluginsLoader?: (message: Message) => React.ReactNode;
20
- messageDetailsLoader?: (message: Message) => React.ReactNode;
21
- models?: Model[];
22
- }
23
-
24
- export const ChatMessage = (props: ChatMessageProps) => {
25
- const { message, index, disabled = false, pluginsLoader, messageDetailsLoader, models } = props;
26
-
27
- const {
28
- role,
29
- id,
30
- modelName,
31
- modelId,
32
- content,
33
- html,
34
- updatedAt,
35
- user,
36
- streaming = false,
37
- linkedMessages,
38
- status,
39
- statusInfo,
40
- } = message;
41
-
42
- const componentRef = useRef<HTMLDivElement>(null);
43
- const disableActions = useMemo(() => disabled || streaming, [disabled, streaming]);
44
- const [showMainMessage, setShowMainMessage] = React.useState(true);
45
- const [showDetails, setShowDetails] = React.useState(false);
46
-
47
- const timestamp = new Date(updatedAt).toLocaleString();
48
- const isUserMessage = role === MessageRole.USER;
49
- const username = isUserMessage
50
- ? `${user?.firstName || ""} ${user?.lastName || ""}`.trim() || "You"
51
- : modelName || "AI";
52
-
53
- const codeHeaderTemplate = `
54
- <span class="title">
55
- <span class="header-toggle">
56
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
57
- stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
58
- class="icon icon-tabler icons-tabler-outline icon-tabler-chevron-right">
59
- <path stroke="none" d="M0 0h24v24H0z" fill="none" />
60
- <path d="M9 6l6 6l-6 6" />
61
- </svg>
62
- </span>
63
- <span class="language"><LANG></span>
64
- </span>
65
-
66
- <div class="code-header-actions">
67
- <div type="button" class="action-btn mantine-focus-auto mantine-active code-copy-btn">
68
- <div class="copy-icon">
69
- <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none"
70
- stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
71
- class="icon icon-tabler icons-tabler-outline icon-tabler-copy">
72
- <path stroke="none" d="M0 0h24v24H0z" fill="none" />
73
- <path
74
- d="M7 7m0 2.667a2.667 2.667 0 0 1 2.667 -2.667h8.666a2.667 2.667 0 0 1 2.667 2.667v8.666a2.667 2.667 0 0 1 -2.667 2.667h-8.666a2.667 2.667 0 0 1 -2.667 -2.667z" />
75
- <path
76
- d="M4.012 16.737a2.005 2.005 0 0 1 -1.012 -1.737v-10c0 -1.1 .9 -2 2 -2h10c.75 0 1.158 .385 1.5 1" />
77
- </svg>
78
- </div>
79
- <div class="check-icon" style="display: none;">
80
- <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none"
81
- stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
82
- class="icon icon-tabler icons-tabler-outline icon-tabler-copy-check">
83
- <path stroke="none" d="M0 0h24v24H0z" fill="none" />
84
- <path stroke="none" d="M0 0h24v24H0z" />
85
- <path
86
- d="M7 9.667a2.667 2.667 0 0 1 2.667 -2.667h8.666a2.667 2.667 0 0 1 2.667 2.667v8.666a2.667 2.667 0 0 1 -2.667 2.667h-8.666a2.667 2.667 0 0 1 -2.667 -2.667z" />
87
- <path d="M4.012 16.737a2 2 0 0 1 -1.012 -1.737v-10c0 -1.1 .9 -2 2 -2h10c.75 0 1.158 .385 1.5 1" />
88
- <path d="M11 14l2 2l4 -4" />
89
- </svg>
90
- </div>
91
- <span>Copy</span>
92
- </div>
93
- </div>
94
- `;
95
-
96
- const processCodeElements = useCallback(
97
- debounce(() => {
98
- if (!componentRef.current) return;
99
-
100
- componentRef.current.querySelectorAll("pre").forEach(pre => {
101
- if (pre.querySelector(".code-data") && !pre?.parentElement?.classList?.contains("code-block")) {
102
- const data = pre.querySelector(".code-data");
103
- const block = document.createElement("div");
104
- const header = document.createElement("div");
105
- block.className = "code-block";
106
- header.className = "code-header";
107
- header.innerHTML = codeHeaderTemplate.replaceAll("<LANG>", data?.getAttribute("data-lang") || "plaintext");
108
-
109
- pre.parentNode?.insertBefore(header, pre);
110
- pre.parentNode?.insertBefore(block, pre);
111
- block.appendChild(pre);
112
- }
113
- });
114
-
115
- componentRef.current.querySelectorAll("img").forEach(img => {
116
- if (!img?.classList?.contains("message-image")) {
117
- img.classList.add("message-image");
118
- const fileName = img.src.split("/").pop() || "";
119
- img.setAttribute("data-file-name", fileName);
120
- }
121
- });
122
- }, 250),
123
- []
124
- );
125
-
126
- useEffect(() => {
127
- if (streaming) return;
128
-
129
- if (componentRef.current) {
130
- const observer = new MutationObserver(processCodeElements);
131
- observer.observe(componentRef.current, { childList: true, subtree: true });
132
- processCodeElements(); // Initial call to inject code elements
133
- return () => observer.disconnect();
134
- }
135
- }, [role, streaming]);
136
-
137
- const toggleDetails = () => setShowDetails(s => !s);
138
-
139
- const details = useMemo(() => {
140
- return messageDetailsLoader ? messageDetailsLoader(message) : null;
141
- }, [messageDetailsLoader, message]);
142
-
143
- const mainMessage = useMemo(() => {
144
- const plugins = pluginsLoader ? pluginsLoader(message) : null;
145
- const model = models?.find(m => m.modelId === message?.modelId);
146
-
147
- return (
148
- <>
149
- <Group align="center">
150
- <Avatar color="gray" radius="xl" size="md" src={isUserMessage ? message?.user?.avatarUrl : undefined}>
151
- {isUserMessage ? (
152
- <IconUser />
153
- ) : model ? (
154
- <ProviderIcon apiProvider={model.apiProvider} provider={model.provider} />
155
- ) : (
156
- <IconRobot />
157
- )}
158
- </Avatar>
159
- <Group gap="xs">
160
- <Text size="sm" fw={500} c={isUserMessage ? "blue" : "teal"}>
161
- {username}
162
- </Text>
163
- <Text size="xs" c="dimmed">
164
- {timestamp}
165
- </Text>
166
- {status && <MessageStatus status={status} />}
167
- {statusInfo && (
168
- <Text size="xs" c="dimmed">
169
- {statusInfo}
170
- </Text>
171
- )}
172
- </Group>
173
- </Group>
174
- <div className={`${classes.message} ${classes[role] || ""} ${streaming ? classes.streaming : ""}`}>
175
- {streaming && !content && <Loader size="md" mb="md" />}
176
-
177
- {html ? (
178
- html.map((part, index) => <div key={index} dangerouslySetInnerHTML={{ __html: part }} />)
179
- ) : (
180
- <div>{content}</div>
181
- )}
182
-
183
- {details && (
184
- <Box mt="md">
185
- <Group mb={5}>
186
- <Button onClick={toggleDetails} p="xs" variant="light" size="xs">
187
- Details
188
- </Button>
189
- </Group>
190
-
191
- <Collapse in={showDetails}>
192
- <div className={classes.detailsBlock}>{details}</div>
193
- </Collapse>
194
- </Box>
195
- )}
196
-
197
- <div className={classes.messageFooter}>
198
- <CopyMessageButton messageId={id} messageIndex={index} />
199
-
200
- {plugins}
201
- </div>
202
- </div>
203
- </>
204
- );
205
- }, [
206
- role,
207
- username,
208
- timestamp,
209
- content,
210
- html,
211
- id,
212
- modelName,
213
- modelId,
214
- models,
215
- index,
216
- disableActions,
217
- details,
218
- showDetails,
219
- streaming,
220
- ]);
221
-
222
- const linkedMessagesCmp = useMemo(() => {
223
- if (!linkedMessages || linkedMessages.length === 0) return null;
224
-
225
- return (
226
- <Carousel
227
- withIndicators={linkedMessages.length > 1}
228
- emblaOptions={{ align: "center", loop: true }}
229
- slideGap="0"
230
- withControls={linkedMessages.length > 1}
231
- initialSlide={linkedMessages.findIndex(m => m.streaming)}
232
- classNames={carouselClasses}
233
- >
234
- {linkedMessages.map((linkedMsg, linkedIndex) => (
235
- <LinkedChatMessage
236
- key={linkedMsg.id}
237
- message={linkedMsg}
238
- parentIndex={index}
239
- index={linkedIndex}
240
- models={models}
241
- plugins={pluginsLoader?.(linkedMsg)}
242
- />
243
- ))}
244
- </Carousel>
245
- );
246
- }, [linkedMessages, models, pluginsLoader, index]);
247
-
248
- if (!linkedMessagesCmp) {
249
- return (
250
- <div className={classes.messageContainer} ref={componentRef}>
251
- <div className={classes.main}>{mainMessage}</div>
252
- </div>
253
- );
254
- }
255
-
256
- return (
257
- <div className={classes.messageContainer} ref={componentRef}>
258
- <div className={classes.linkedToggle}>
259
- <Switch
260
- checked={showMainMessage}
261
- onChange={event => setShowMainMessage(event.currentTarget.checked)}
262
- label={showMainMessage ? "Main" : "Others"}
263
- size="sm"
264
- />
265
- </div>
266
- <div className={[classes.main, showMainMessage ? "" : classes.hidden].join(" ")}>{mainMessage}</div>
267
- <div className={[classes.linked, showMainMessage ? classes.hidden : ""].join(" ")}>{linkedMessagesCmp}</div>
268
- </div>
269
- );
270
- };
271
- ChatMessage.displayName = "ChatMessage";
@@ -1,22 +0,0 @@
1
- import React from "react";
2
- import { ScrollArea } from "@mantine/core";
3
-
4
- import classes from "./ChatMessage.module.scss";
5
-
6
- export const ChatMessagePreview: React.FC<{ html?: string[]; text?: string }> = ({ html, text }) => {
7
- return (
8
- <ScrollArea type="hover" offsetScrollbars className={[classes.message, classes.preview].join(" ")}>
9
- {text ? (
10
- <>
11
- {html ? (
12
- html.map((part, index) => <div key={index} dangerouslySetInnerHTML={{ __html: part }} />)
13
- ) : (
14
- <div>{text}</div>
15
- )}
16
- </>
17
- ) : (
18
- "..."
19
- )}
20
- </ScrollArea>
21
- );
22
- };