@katechat/ui 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.css +491 -0
- package/dist/cjs/index.css.map +7 -0
- package/dist/cjs/index.js +75305 -0
- package/dist/cjs/index.js.map +7 -0
- package/dist/esm/index.css +491 -0
- package/dist/esm/index.css.map +7 -0
- package/dist/esm/index.js +75304 -0
- package/dist/esm/index.js.map +7 -0
- package/dist/index.css +1 -0
- package/dist/index.js +539 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -0
- package/package.json +27 -4
- package/.prettierrc +0 -9
- package/esbuild.js +0 -56
- package/jest.config.js +0 -24
- package/postcss.config.cjs +0 -14
- package/src/__mocks__/fileMock.js +0 -1
- package/src/__mocks__/styleMock.js +0 -1
- package/src/components/chat/ChatMessagesContainer.module.scss +0 -77
- package/src/components/chat/ChatMessagesContainer.tsx +0 -151
- package/src/components/chat/ChatMessagesList.tsx +0 -216
- package/src/components/chat/index.ts +0 -4
- package/src/components/chat/input/ChatInput.module.scss +0 -113
- package/src/components/chat/input/ChatInput.tsx +0 -259
- package/src/components/chat/input/index.ts +0 -1
- package/src/components/chat/message/ChatMessage.Carousel.module.scss +0 -7
- package/src/components/chat/message/ChatMessage.module.scss +0 -378
- package/src/components/chat/message/ChatMessage.tsx +0 -271
- package/src/components/chat/message/ChatMessagePreview.tsx +0 -22
- package/src/components/chat/message/LinkedChatMessage.tsx +0 -64
- package/src/components/chat/message/MessageStatus.tsx +0 -38
- package/src/components/chat/message/controls/CopyMessageButton.tsx +0 -32
- package/src/components/chat/message/index.ts +0 -4
- package/src/components/icons/ProviderIcon.tsx +0 -49
- package/src/components/icons/index.ts +0 -1
- package/src/components/index.ts +0 -3
- package/src/components/modal/ImagePopup.tsx +0 -97
- package/src/components/modal/index.ts +0 -1
- package/src/controls/FileDropzone/FileDropzone.module.scss +0 -15
- package/src/controls/FileDropzone/FileDropzone.tsx +0 -120
- package/src/controls/index.ts +0 -1
- package/src/core/ai.ts +0 -1
- package/src/core/index.ts +0 -4
- package/src/core/message.ts +0 -59
- package/src/core/model.ts +0 -23
- package/src/core/user.ts +0 -8
- package/src/hooks/index.ts +0 -2
- package/src/hooks/useIntersectionObserver.ts +0 -24
- package/src/hooks/useTheme.tsx +0 -66
- package/src/index.ts +0 -5
- package/src/lib/__tests__/markdown.parser.test.ts +0 -289
- package/src/lib/__tests__/markdown.parser.testUtils.ts +0 -31
- package/src/lib/__tests__/markdown.parser_sanitizeUrl.test.ts +0 -130
- package/src/lib/assert.ts +0 -14
- package/src/lib/markdown.parser.ts +0 -189
- package/src/setupTests.ts +0 -1
- package/src/types/scss.d.ts +0 -4
- package/tsconfig.json +0 -26
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import React, { useMemo } from "react";
|
|
2
|
-
import { Text, Group, Avatar } from "@mantine/core";
|
|
3
|
-
import { Carousel } from "@mantine/carousel";
|
|
4
|
-
import { IconRobot } from "@tabler/icons-react";
|
|
5
|
-
|
|
6
|
-
import { Message, Model } from "@/core";
|
|
7
|
-
import { ProviderIcon } from "@/components/icons/ProviderIcon";
|
|
8
|
-
import { MessageStatus } from "./MessageStatus";
|
|
9
|
-
import { CopyMessageButton } from "./controls/CopyMessageButton";
|
|
10
|
-
|
|
11
|
-
import classes from "./ChatMessage.module.scss";
|
|
12
|
-
|
|
13
|
-
interface IProps {
|
|
14
|
-
message: Message;
|
|
15
|
-
parentIndex: number;
|
|
16
|
-
index: number;
|
|
17
|
-
models?: Model[];
|
|
18
|
-
plugins?: React.ReactNode;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export const LinkedChatMessage = ({ message, parentIndex, index, plugins, models }: IProps) => {
|
|
22
|
-
const model = useMemo(() => {
|
|
23
|
-
return models?.find(m => m.modelId === message.modelId);
|
|
24
|
-
}, [models, message.modelId]);
|
|
25
|
-
|
|
26
|
-
return (
|
|
27
|
-
<Carousel.Slide key={message.id} className={classes.linkedMessageContainer}>
|
|
28
|
-
<Group align="center">
|
|
29
|
-
<Avatar radius="xl" size="md">
|
|
30
|
-
{model ? <ProviderIcon apiProvider={model.apiProvider} provider={model.provider} /> : <IconRobot />}
|
|
31
|
-
</Avatar>
|
|
32
|
-
<Group gap="xs">
|
|
33
|
-
<Text size="sm" fw={500} c="teal">
|
|
34
|
-
{message.modelName}
|
|
35
|
-
</Text>
|
|
36
|
-
{message.status && <MessageStatus status={message.status} />}
|
|
37
|
-
{message.statusInfo && (
|
|
38
|
-
<Text size="xs" c="dimmed">
|
|
39
|
-
{message.statusInfo}
|
|
40
|
-
</Text>
|
|
41
|
-
)}
|
|
42
|
-
</Group>
|
|
43
|
-
</Group>
|
|
44
|
-
|
|
45
|
-
<div className={classes.message}>
|
|
46
|
-
{message.html ? (
|
|
47
|
-
message.html.map((part: string, index: number) => (
|
|
48
|
-
<div key={index} dangerouslySetInnerHTML={{ __html: part }} />
|
|
49
|
-
))
|
|
50
|
-
) : (
|
|
51
|
-
<div>{message.content}</div>
|
|
52
|
-
)}
|
|
53
|
-
|
|
54
|
-
<div className={classes.messageFooter}>
|
|
55
|
-
<CopyMessageButton messageId={message.id} messageIndex={parentIndex} linkedMessageIndex={index} />
|
|
56
|
-
|
|
57
|
-
{plugins}
|
|
58
|
-
</div>
|
|
59
|
-
</div>
|
|
60
|
-
</Carousel.Slide>
|
|
61
|
-
);
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
LinkedChatMessage.displayName = "LinkedChatMessage";
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { Badge, DefaultMantineColor } from "@mantine/core";
|
|
3
|
-
import { ResponseStatus } from "@/core/message";
|
|
4
|
-
|
|
5
|
-
const TITLE_MAP: Record<ResponseStatus, string> = {
|
|
6
|
-
[ResponseStatus.IN_PROGRESS]: "In Progress",
|
|
7
|
-
[ResponseStatus.COMPLETED]: "Completed",
|
|
8
|
-
[ResponseStatus.RAG_SEARCH]: "RAG Search",
|
|
9
|
-
[ResponseStatus.WEB_SEARCH]: "Web Search",
|
|
10
|
-
[ResponseStatus.CODE_INTERPRETER]: "Code Interpreter",
|
|
11
|
-
[ResponseStatus.TOOL_CALL]: "Tool Call",
|
|
12
|
-
[ResponseStatus.REASONING]: "Reasoning",
|
|
13
|
-
[ResponseStatus.ERROR]: "Error",
|
|
14
|
-
[ResponseStatus.TOOL_CALL_COMPLETED]: "Tool Call Completed",
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
const COLOR_MAP: Record<ResponseStatus, DefaultMantineColor> = {
|
|
18
|
-
[ResponseStatus.IN_PROGRESS]: "blue",
|
|
19
|
-
[ResponseStatus.COMPLETED]: "green",
|
|
20
|
-
[ResponseStatus.RAG_SEARCH]: "orange",
|
|
21
|
-
[ResponseStatus.WEB_SEARCH]: "teal",
|
|
22
|
-
[ResponseStatus.CODE_INTERPRETER]: "teal",
|
|
23
|
-
[ResponseStatus.TOOL_CALL]: "cyan",
|
|
24
|
-
[ResponseStatus.REASONING]: "yellow",
|
|
25
|
-
[ResponseStatus.ERROR]: "red",
|
|
26
|
-
[ResponseStatus.TOOL_CALL_COMPLETED]: "green",
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
export const MessageStatus = ({ status }: { status: ResponseStatus }) => {
|
|
30
|
-
const title = TITLE_MAP[status] || status;
|
|
31
|
-
const color = COLOR_MAP[status] || "indigo";
|
|
32
|
-
|
|
33
|
-
return (
|
|
34
|
-
<Badge color={color} variant="light">
|
|
35
|
-
{title}
|
|
36
|
-
</Badge>
|
|
37
|
-
);
|
|
38
|
-
};
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { ActionIcon, Tooltip } from "@mantine/core";
|
|
2
|
-
import { IconCopy, IconCopyCheck } from "@tabler/icons-react";
|
|
3
|
-
import React from "react";
|
|
4
|
-
|
|
5
|
-
export const CopyMessageButton = ({
|
|
6
|
-
messageId,
|
|
7
|
-
messageIndex,
|
|
8
|
-
linkedMessageIndex,
|
|
9
|
-
}: {
|
|
10
|
-
messageId: string;
|
|
11
|
-
messageIndex: number;
|
|
12
|
-
linkedMessageIndex?: number;
|
|
13
|
-
}) => (
|
|
14
|
-
<>
|
|
15
|
-
<Tooltip label="Copy message" position="top" withArrow>
|
|
16
|
-
<ActionIcon
|
|
17
|
-
className="copy-message-btn"
|
|
18
|
-
data-message-id={messageId}
|
|
19
|
-
data-message-index={messageIndex}
|
|
20
|
-
data-message-linked-index={linkedMessageIndex}
|
|
21
|
-
size="sm"
|
|
22
|
-
color="gray"
|
|
23
|
-
variant="transparent"
|
|
24
|
-
>
|
|
25
|
-
<IconCopy />
|
|
26
|
-
</ActionIcon>
|
|
27
|
-
</Tooltip>
|
|
28
|
-
<ActionIcon disabled size="sm" className="check-icon">
|
|
29
|
-
<IconCopyCheck />
|
|
30
|
-
</ActionIcon>
|
|
31
|
-
</>
|
|
32
|
-
);
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import {
|
|
3
|
-
IconBrandOpenai,
|
|
4
|
-
IconBrandAws,
|
|
5
|
-
IconServer,
|
|
6
|
-
IconBrandYandex,
|
|
7
|
-
IconMessageChatbot,
|
|
8
|
-
IconAi,
|
|
9
|
-
IconBrandMeta,
|
|
10
|
-
IconBrandMedium,
|
|
11
|
-
IconBrandGoogle,
|
|
12
|
-
} from "@tabler/icons-react";
|
|
13
|
-
import { ApiProvider } from "@/core/ai";
|
|
14
|
-
|
|
15
|
-
export const ProviderIcon = ({
|
|
16
|
-
apiProvider,
|
|
17
|
-
provider,
|
|
18
|
-
size = 24,
|
|
19
|
-
}: {
|
|
20
|
-
apiProvider: ApiProvider;
|
|
21
|
-
provider?: string;
|
|
22
|
-
size?: number;
|
|
23
|
-
}) => {
|
|
24
|
-
switch (apiProvider) {
|
|
25
|
-
case "open_ai":
|
|
26
|
-
return <IconBrandOpenai size={size} />;
|
|
27
|
-
case "aws_bedrock":
|
|
28
|
-
switch (provider?.toLowerCase()) {
|
|
29
|
-
case "amazon":
|
|
30
|
-
return <IconBrandAws size={size} />;
|
|
31
|
-
case "anthropic":
|
|
32
|
-
return <IconAi size={size} />;
|
|
33
|
-
case "mistral ai":
|
|
34
|
-
return <IconBrandMedium size={size} />;
|
|
35
|
-
case "meta":
|
|
36
|
-
return <IconBrandMeta size={size} />;
|
|
37
|
-
|
|
38
|
-
default:
|
|
39
|
-
return <IconBrandAws size={size} />;
|
|
40
|
-
}
|
|
41
|
-
case "yandex_fm":
|
|
42
|
-
return <IconBrandYandex size={size} />;
|
|
43
|
-
case "google_vertex_ai":
|
|
44
|
-
return <IconBrandGoogle size={size} />;
|
|
45
|
-
|
|
46
|
-
default:
|
|
47
|
-
return <IconMessageChatbot size={size} />;
|
|
48
|
-
}
|
|
49
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./ProviderIcon";
|
package/src/components/index.ts
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import React, { useEffect, useCallback } from "react";
|
|
2
|
-
import { Image, Text, Group, Stack, ActionIcon, Modal, Tooltip } from "@mantine/core";
|
|
3
|
-
import { useDisclosure } from "@mantine/hooks";
|
|
4
|
-
import { useNavigate } from "react-router-dom";
|
|
5
|
-
import { IconExternalLink } from "@tabler/icons-react";
|
|
6
|
-
import { ok } from "@/lib/assert";
|
|
7
|
-
|
|
8
|
-
interface IProps {
|
|
9
|
-
fileName: string;
|
|
10
|
-
fileUrl: string;
|
|
11
|
-
mimeType?: string;
|
|
12
|
-
createdAt?: string;
|
|
13
|
-
sourceUrl?: string;
|
|
14
|
-
sourceTitle?: string;
|
|
15
|
-
onClose: () => void;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export const ImagePopup: React.FC<IProps> = ({
|
|
19
|
-
fileName,
|
|
20
|
-
fileUrl,
|
|
21
|
-
mimeType,
|
|
22
|
-
createdAt,
|
|
23
|
-
sourceUrl,
|
|
24
|
-
sourceTitle,
|
|
25
|
-
onClose,
|
|
26
|
-
}) => {
|
|
27
|
-
const navigate = useNavigate();
|
|
28
|
-
const [opened, { open, close }] = useDisclosure(false);
|
|
29
|
-
|
|
30
|
-
useEffect(() => {
|
|
31
|
-
if (fileUrl) {
|
|
32
|
-
open();
|
|
33
|
-
}
|
|
34
|
-
}, [fileUrl, open]);
|
|
35
|
-
|
|
36
|
-
const handleClose = useCallback(() => {
|
|
37
|
-
onClose();
|
|
38
|
-
close();
|
|
39
|
-
}, [onClose, close]);
|
|
40
|
-
|
|
41
|
-
const navigateToChat = useCallback(() => {
|
|
42
|
-
ok(sourceUrl);
|
|
43
|
-
navigate(sourceUrl);
|
|
44
|
-
handleClose();
|
|
45
|
-
}, [navigate, sourceUrl, handleClose]);
|
|
46
|
-
|
|
47
|
-
const formatDate = (dateString: string) => {
|
|
48
|
-
return new Date(dateString).toLocaleDateString("en-US", {
|
|
49
|
-
year: "numeric",
|
|
50
|
-
month: "short",
|
|
51
|
-
day: "numeric",
|
|
52
|
-
hour: "2-digit",
|
|
53
|
-
minute: "2-digit",
|
|
54
|
-
});
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
// Image Preview Modal
|
|
58
|
-
return (
|
|
59
|
-
<Modal opened={opened} onClose={handleClose} size="xl" title="Image Preview" centered>
|
|
60
|
-
{fileUrl && (
|
|
61
|
-
<Stack gap="md">
|
|
62
|
-
<Image src={fileUrl} alt={fileName} fit="contain" mah="70vh" />
|
|
63
|
-
|
|
64
|
-
<Group justify="space-between">
|
|
65
|
-
<div>
|
|
66
|
-
<Text size="sm" fw={500}>
|
|
67
|
-
{fileName}
|
|
68
|
-
</Text>
|
|
69
|
-
<Text size="xs" c="dimmed">
|
|
70
|
-
{createdAt ? formatDate(createdAt) + " •" : ""} {mimeType}
|
|
71
|
-
</Text>
|
|
72
|
-
</div>
|
|
73
|
-
|
|
74
|
-
{sourceUrl && (
|
|
75
|
-
<Group gap="xs">
|
|
76
|
-
<Tooltip label="Open source">
|
|
77
|
-
<ActionIcon variant="light" onClick={navigateToChat}>
|
|
78
|
-
<IconExternalLink size={16} />
|
|
79
|
-
</ActionIcon>
|
|
80
|
-
</Tooltip>
|
|
81
|
-
</Group>
|
|
82
|
-
)}
|
|
83
|
-
</Group>
|
|
84
|
-
|
|
85
|
-
{sourceUrl && (
|
|
86
|
-
<Text size="sm" c="dimmed">
|
|
87
|
-
From:{" "}
|
|
88
|
-
<Text span c="blue" style={{ cursor: "pointer" }} onClick={navigateToChat}>
|
|
89
|
-
{sourceTitle || sourceUrl}
|
|
90
|
-
</Text>
|
|
91
|
-
</Text>
|
|
92
|
-
)}
|
|
93
|
-
</Stack>
|
|
94
|
-
)}
|
|
95
|
-
</Modal>
|
|
96
|
-
);
|
|
97
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./ImagePopup";
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import React, { useState, useCallback, useRef, useEffect } from "react";
|
|
2
|
-
import { Group, Tooltip, Box } from "@mantine/core";
|
|
3
|
-
import { IconFileUpload } from "@tabler/icons-react";
|
|
4
|
-
import { notEmpty } from "@/lib/assert";
|
|
5
|
-
import classes from "./FileDropzone.module.scss";
|
|
6
|
-
|
|
7
|
-
interface IProps {
|
|
8
|
-
disabled?: boolean;
|
|
9
|
-
uploadFormats?: string[];
|
|
10
|
-
onFilesAdd: (images: File[]) => void;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export const IMAGE_UPLOAD_FORMATS = ["image/jpeg", "image/png", "image/webp"];
|
|
14
|
-
|
|
15
|
-
export const FileDropzone: React.FC<IProps> = ({ onFilesAdd, disabled, uploadFormats = IMAGE_UPLOAD_FORMATS }) => {
|
|
16
|
-
const [isDragging, setIsDragging] = useState(false);
|
|
17
|
-
const dropzoneRef = useRef<HTMLDivElement>(null);
|
|
18
|
-
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
19
|
-
|
|
20
|
-
// Handle paste events for clipboard images
|
|
21
|
-
useEffect(() => {
|
|
22
|
-
const handlePaste = (e: ClipboardEvent) => {
|
|
23
|
-
if (disabled) return;
|
|
24
|
-
if (e.clipboardData && e.clipboardData.items) {
|
|
25
|
-
const files = Array.from(e.clipboardData.items)
|
|
26
|
-
.map(f => f.getAsFile())
|
|
27
|
-
.filter(notEmpty);
|
|
28
|
-
|
|
29
|
-
if (files.length > 0) {
|
|
30
|
-
e.preventDefault();
|
|
31
|
-
e.stopPropagation();
|
|
32
|
-
onFilesAdd(files);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
document.addEventListener("paste", handlePaste);
|
|
38
|
-
return () => {
|
|
39
|
-
document.removeEventListener("paste", handlePaste);
|
|
40
|
-
};
|
|
41
|
-
}, [onFilesAdd, disabled]);
|
|
42
|
-
|
|
43
|
-
const handleDragOver = useCallback((e: React.DragEvent<HTMLDivElement>) => {
|
|
44
|
-
e.preventDefault();
|
|
45
|
-
e.stopPropagation();
|
|
46
|
-
setIsDragging(true);
|
|
47
|
-
}, []);
|
|
48
|
-
|
|
49
|
-
const handleDragLeave = useCallback((e: React.DragEvent<HTMLDivElement>) => {
|
|
50
|
-
e.preventDefault();
|
|
51
|
-
e.stopPropagation();
|
|
52
|
-
setIsDragging(false);
|
|
53
|
-
}, []);
|
|
54
|
-
|
|
55
|
-
const handleDrop = useCallback(
|
|
56
|
-
(e: React.DragEvent<HTMLDivElement>) => {
|
|
57
|
-
e.preventDefault();
|
|
58
|
-
e.stopPropagation();
|
|
59
|
-
setIsDragging(false);
|
|
60
|
-
if (disabled) return;
|
|
61
|
-
|
|
62
|
-
const files =
|
|
63
|
-
e.dataTransfer.files && e.dataTransfer.files.length > 0
|
|
64
|
-
? Array.from(e.dataTransfer.files).filter(f => f.size > 0)
|
|
65
|
-
: [];
|
|
66
|
-
|
|
67
|
-
onFilesAdd(files);
|
|
68
|
-
},
|
|
69
|
-
[onFilesAdd]
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
const handleFileSelect = useCallback(
|
|
73
|
-
(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
74
|
-
if (e.target.files && e.target.files.length > 0) {
|
|
75
|
-
const files = Array.from(e.target.files).filter(f => f.size > 0);
|
|
76
|
-
if (files.length) {
|
|
77
|
-
onFilesAdd(files);
|
|
78
|
-
}
|
|
79
|
-
if (fileInputRef.current) {
|
|
80
|
-
fileInputRef.current.value = ""; // Reset file input value
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
},
|
|
84
|
-
[onFilesAdd]
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
const openFileDialog = () => {
|
|
88
|
-
if (disabled) return;
|
|
89
|
-
fileInputRef.current?.click();
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
return (
|
|
93
|
-
<>
|
|
94
|
-
<Box
|
|
95
|
-
ref={dropzoneRef}
|
|
96
|
-
className={`drop-zone ${classes.dropzone} ${isDragging ? classes.dragging : ""} ${disabled ? classes.disabled : ""}`}
|
|
97
|
-
onDragOver={handleDragOver}
|
|
98
|
-
onDragLeave={handleDragLeave}
|
|
99
|
-
onDrop={handleDrop}
|
|
100
|
-
onClick={openFileDialog}
|
|
101
|
-
>
|
|
102
|
-
<Group justify="center" gap="md" className="drop-zone-control">
|
|
103
|
-
<Tooltip label="Click or drop an image/document here" position="top">
|
|
104
|
-
<IconFileUpload size={32} stroke={1.5} />
|
|
105
|
-
</Tooltip>
|
|
106
|
-
<input
|
|
107
|
-
ref={fileInputRef}
|
|
108
|
-
type="file"
|
|
109
|
-
multiple
|
|
110
|
-
accept={uploadFormats?.join(",")}
|
|
111
|
-
onChange={handleFileSelect}
|
|
112
|
-
className={classes.fileInput}
|
|
113
|
-
style={{ display: "none" }}
|
|
114
|
-
disabled={disabled}
|
|
115
|
-
/>
|
|
116
|
-
</Group>
|
|
117
|
-
</Box>
|
|
118
|
-
</>
|
|
119
|
-
);
|
|
120
|
-
};
|
package/src/controls/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./FileDropzone/FileDropzone";
|
package/src/core/ai.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export type ApiProvider = "AWS_BEDROCK" | "OPEN_AI" | "YANDEX_FM" | "GOOGLE_VERTEX_AI" | "DEEPSEEK" | "ANTHROPIC";
|
package/src/core/index.ts
DELETED
package/src/core/message.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { User } from "./user";
|
|
2
|
-
|
|
3
|
-
export enum MessageType {
|
|
4
|
-
MESSAGE = "message",
|
|
5
|
-
SYSTEM = "system",
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export enum MessageRole {
|
|
9
|
-
USER = "user",
|
|
10
|
-
ASSISTANT = "assistant",
|
|
11
|
-
ERROR = "error",
|
|
12
|
-
SYSTEM = "system",
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export enum ResponseStatus {
|
|
16
|
-
IN_PROGRESS = "in_progress",
|
|
17
|
-
RAG_SEARCH = "rag_search",
|
|
18
|
-
WEB_SEARCH = "web_search",
|
|
19
|
-
CODE_INTERPRETER = "code_interpreter",
|
|
20
|
-
TOOL_CALL = "tool_call",
|
|
21
|
-
TOOL_CALL_COMPLETED = "tool_call_completed",
|
|
22
|
-
REASONING = "reasoning",
|
|
23
|
-
COMPLETED = "completed",
|
|
24
|
-
ERROR = "error",
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export interface Message<TUser = User, TMetadata = Record<string, unknown>> {
|
|
28
|
-
id: string;
|
|
29
|
-
chatId: string;
|
|
30
|
-
content: string;
|
|
31
|
-
html?: string[];
|
|
32
|
-
role: MessageRole;
|
|
33
|
-
modelId?: string;
|
|
34
|
-
modelName?: string;
|
|
35
|
-
user?: TUser;
|
|
36
|
-
createdAt: string;
|
|
37
|
-
updatedAt: string;
|
|
38
|
-
streaming?: boolean;
|
|
39
|
-
linkedToMessageId?: string;
|
|
40
|
-
linkedMessages?: Message<TUser, TMetadata>[];
|
|
41
|
-
metadata?: TMetadata;
|
|
42
|
-
status?: ResponseStatus;
|
|
43
|
-
statusInfo?: string;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export interface PluginProps<TMessage = Message> {
|
|
47
|
-
message: TMessage;
|
|
48
|
-
disabled?: boolean;
|
|
49
|
-
onAddMessage?: (message: TMessage) => void;
|
|
50
|
-
onAction?: (messageId: string) => void;
|
|
51
|
-
onActionEnd?: (messageId: string) => void;
|
|
52
|
-
onMessageDeleted?: (args: { messagesToDelete?: TMessage[]; deleteAfter?: TMessage }) => void;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export interface ImageInput {
|
|
56
|
-
fileName: string;
|
|
57
|
-
mimeType: string;
|
|
58
|
-
bytesBase64: string;
|
|
59
|
-
}
|
package/src/core/model.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { ApiProvider } from "./ai";
|
|
2
|
-
import { User } from "./user";
|
|
3
|
-
|
|
4
|
-
export enum ModelType {
|
|
5
|
-
CHAT = "CHAT",
|
|
6
|
-
EMBEDDING = "EMBEDDING",
|
|
7
|
-
IMAGE_GENERATION = "IMAGE_GENERATION",
|
|
8
|
-
AUDIO_GENERATION = "AUDIO_GENERATION",
|
|
9
|
-
OTHER = "OTHER",
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface Model<TUser = User> {
|
|
13
|
-
id: string;
|
|
14
|
-
name: string;
|
|
15
|
-
modelId: string;
|
|
16
|
-
apiProvider: ApiProvider;
|
|
17
|
-
type: ModelType;
|
|
18
|
-
provider: string;
|
|
19
|
-
isActive: boolean;
|
|
20
|
-
imageInput?: boolean;
|
|
21
|
-
maxInputTokens?: number;
|
|
22
|
-
user?: TUser;
|
|
23
|
-
}
|
package/src/core/user.ts
DELETED
package/src/hooks/index.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { DependencyList, useEffect, useRef } from "react";
|
|
2
|
-
|
|
3
|
-
export function useIntersectionObserver<T extends HTMLElement>(callback: () => void, deps: DependencyList, delay = 0) {
|
|
4
|
-
const observer = useRef<IntersectionObserver | null>(null);
|
|
5
|
-
const ref = useRef<T>(null);
|
|
6
|
-
|
|
7
|
-
useEffect(() => {
|
|
8
|
-
observer.current?.disconnect();
|
|
9
|
-
const timeoutId = setTimeout(() => {
|
|
10
|
-
observer.current = new IntersectionObserver(entries => {
|
|
11
|
-
if (entries[0].isIntersecting) callback();
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
if (ref.current) observer.current.observe(ref.current);
|
|
15
|
-
}, delay);
|
|
16
|
-
|
|
17
|
-
return () => {
|
|
18
|
-
clearTimeout(timeoutId);
|
|
19
|
-
observer.current?.disconnect();
|
|
20
|
-
};
|
|
21
|
-
}, [deps, callback]);
|
|
22
|
-
|
|
23
|
-
return ref;
|
|
24
|
-
}
|
package/src/hooks/useTheme.tsx
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import React, { createContext, useContext, useEffect } from "react";
|
|
2
|
-
import { useLocalStorage } from "@mantine/hooks";
|
|
3
|
-
|
|
4
|
-
type ColorScheme = "light" | "dark" | "auto";
|
|
5
|
-
|
|
6
|
-
// Define the ThemeContext type
|
|
7
|
-
interface ThemeContextType {
|
|
8
|
-
colorScheme: ColorScheme;
|
|
9
|
-
setColorScheme: (value: ColorScheme) => void;
|
|
10
|
-
toggleColorScheme: () => void;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// Create the context
|
|
14
|
-
export const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
|
|
15
|
-
|
|
16
|
-
// Theme provider component
|
|
17
|
-
export const ThemeProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|
18
|
-
// Use localStorage to store theme preference
|
|
19
|
-
const [colorScheme, setColorScheme] = useLocalStorage<ColorScheme>({
|
|
20
|
-
key: "ui-theme",
|
|
21
|
-
defaultValue: "light",
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
// Apply theme changes to document
|
|
25
|
-
useEffect(() => {
|
|
26
|
-
if (colorScheme === "auto") {
|
|
27
|
-
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
28
|
-
document.documentElement.dataset.mantine = prefersDark ? "dark" : "light";
|
|
29
|
-
} else {
|
|
30
|
-
document.documentElement.dataset.mantine = colorScheme;
|
|
31
|
-
}
|
|
32
|
-
}, [colorScheme]);
|
|
33
|
-
|
|
34
|
-
// Toggle between light and dark themes
|
|
35
|
-
const toggleColorScheme = () => {
|
|
36
|
-
const newColorScheme = colorScheme === "dark" ? "light" : "dark";
|
|
37
|
-
setColorScheme(newColorScheme);
|
|
38
|
-
document.documentElement.dataset.mantine = newColorScheme;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
// Listen for system color scheme changes if set to 'auto'
|
|
42
|
-
useEffect(() => {
|
|
43
|
-
if (colorScheme === "auto") {
|
|
44
|
-
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
45
|
-
const handleChange = (e: MediaQueryListEvent) => {
|
|
46
|
-
document.documentElement.dataset.mantine = e.matches ? "dark" : "light";
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
mediaQuery.addEventListener("change", handleChange);
|
|
50
|
-
return () => mediaQuery.removeEventListener("change", handleChange);
|
|
51
|
-
}
|
|
52
|
-
}, [colorScheme]);
|
|
53
|
-
|
|
54
|
-
return (
|
|
55
|
-
<ThemeContext.Provider value={{ colorScheme, setColorScheme, toggleColorScheme }}>{children}</ThemeContext.Provider>
|
|
56
|
-
);
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
// Custom hook to use the theme context
|
|
60
|
-
export const useTheme = (): ThemeContextType => {
|
|
61
|
-
const context = useContext(ThemeContext);
|
|
62
|
-
if (!context) {
|
|
63
|
-
throw new Error("useTheme must be used within a ThemeProvider");
|
|
64
|
-
}
|
|
65
|
-
return context;
|
|
66
|
-
};
|