feat: add functionality to upload documents in chat
This commit is contained in:
parent
2474d5b6d2
commit
8878e238d2
|
@ -1,10 +1,18 @@
|
|||
@import "../styles/animation.scss";
|
||||
|
||||
.attach-images {
|
||||
.attachments {
|
||||
position: absolute;
|
||||
left: 30px;
|
||||
bottom: 32px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.attach-images {
|
||||
//position: absolute;
|
||||
//left: 30px;
|
||||
//bottom: 32px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.attach-image {
|
||||
|
@ -42,6 +50,53 @@
|
|||
}
|
||||
}
|
||||
|
||||
.attach-files {
|
||||
//position: absolute;
|
||||
//left: 30px;
|
||||
//bottom: 32px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 11px;
|
||||
}
|
||||
|
||||
.attach-file {
|
||||
cursor: default;
|
||||
//width: 64px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
//border: rgba($color: #888, $alpha: 0.2) 1px solid;
|
||||
border-radius: 5px;
|
||||
margin-right: 10px;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-color: var(--white);
|
||||
|
||||
.attach-image-mask {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
transition: all ease 0.2s;
|
||||
}
|
||||
|
||||
.attach-image-mask:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.delete-image {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 5px;
|
||||
float: right;
|
||||
background-color: var(--white);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.chat-input-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
@ -693,4 +748,4 @@
|
|||
.shortcut-key span {
|
||||
font-size: 12px;
|
||||
color: var(--black);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ import StyleIcon from "../icons/palette.svg";
|
|||
import PluginIcon from "../icons/plugin.svg";
|
||||
import ShortcutkeyIcon from "../icons/shortcutkey.svg";
|
||||
import ReloadIcon from "../icons/reload.svg";
|
||||
import UploadDocIcon from "../icons/upload-doc.svg";
|
||||
|
||||
import {
|
||||
ChatMessage,
|
||||
|
@ -96,6 +97,7 @@ import {
|
|||
showToast,
|
||||
} from "./ui-lib";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { FileIcon, defaultStyles } from "react-file-icon";
|
||||
import {
|
||||
CHAT_PAGE_SIZE,
|
||||
DEFAULT_TTS_ENGINE,
|
||||
|
@ -442,8 +444,10 @@ function useScrollToBottom(
|
|||
}
|
||||
|
||||
export function ChatActions(props: {
|
||||
uploadDocument: () => void;
|
||||
uploadImage: () => void;
|
||||
setAttachImages: (images: string[]) => void;
|
||||
setAttachFiles: (files: string[]) => void;
|
||||
setUploading: (uploading: boolean) => void;
|
||||
showPromptModal: () => void;
|
||||
scrollToBottom: () => void;
|
||||
|
@ -502,7 +506,8 @@ export function ChatActions(props: {
|
|||
}, [models, currentModel, currentProviderName]);
|
||||
const [showModelSelector, setShowModelSelector] = useState(false);
|
||||
const [showPluginSelector, setShowPluginSelector] = useState(false);
|
||||
const [showUploadImage, setShowUploadImage] = useState(false);
|
||||
// TODO: remember to make it false
|
||||
const [showUploadImage, setShowUploadImage] = useState(true);
|
||||
|
||||
const [showSizeSelector, setShowSizeSelector] = useState(false);
|
||||
const [showQualitySelector, setShowQualitySelector] = useState(false);
|
||||
|
@ -521,7 +526,8 @@ export function ChatActions(props: {
|
|||
|
||||
useEffect(() => {
|
||||
const show = isVisionModel(currentModel);
|
||||
setShowUploadImage(show);
|
||||
//NOTE: temporary disable upload image
|
||||
//setShowUploadImage(show);
|
||||
if (!show) {
|
||||
props.setAttachImages([]);
|
||||
props.setUploading(false);
|
||||
|
@ -577,6 +583,11 @@ export function ChatActions(props: {
|
|||
icon={props.uploading ? <LoadingButtonIcon /> : <ImageIcon />}
|
||||
/>
|
||||
)}
|
||||
<ChatAction
|
||||
onClick={props.uploadDocument}
|
||||
text={"Upload Document"}
|
||||
icon={props.uploading ? <LoadingButtonIcon /> : <UploadDocIcon />}
|
||||
/>
|
||||
<ChatAction
|
||||
onClick={nextTheme}
|
||||
text={Locale.Chat.InputActions.Theme[theme]}
|
||||
|
@ -945,6 +956,7 @@ function _Chat() {
|
|||
const isMobileScreen = useMobileScreen();
|
||||
const navigate = useNavigate();
|
||||
const [attachImages, setAttachImages] = useState<string[]>([]);
|
||||
const [attachFiles, setAttachFiles] = useState<string[]>([]);
|
||||
const [uploading, setUploading] = useState(false);
|
||||
|
||||
// prompt hints
|
||||
|
@ -1460,6 +1472,51 @@ function _Chat() {
|
|||
[attachImages, chatStore],
|
||||
);
|
||||
|
||||
async function uploadDocument() {
|
||||
const files: string[] = [];
|
||||
files.push(...attachFiles);
|
||||
|
||||
files.push(
|
||||
...(await new Promise<string[]>((res, rej) => {
|
||||
const fileInput = document.createElement("input");
|
||||
fileInput.type = "file";
|
||||
fileInput.accept = "text/*";
|
||||
fileInput.multiple = true;
|
||||
fileInput.onchange = (event: any) => {
|
||||
setUploading(true);
|
||||
const files = event.target.files;
|
||||
const imagesData: string[] = [];
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = event.target.files[i];
|
||||
uploadImageRemote(file)
|
||||
.then((dataUrl) => {
|
||||
imagesData.push(dataUrl);
|
||||
if (
|
||||
imagesData.length === 3 ||
|
||||
imagesData.length === files.length
|
||||
) {
|
||||
setUploading(false);
|
||||
res(imagesData);
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
setUploading(false);
|
||||
rej(e);
|
||||
});
|
||||
}
|
||||
};
|
||||
fileInput.click();
|
||||
})),
|
||||
);
|
||||
|
||||
const filesLength = files.length;
|
||||
if (filesLength > 3) {
|
||||
files.splice(3, filesLength - 3);
|
||||
}
|
||||
setAttachFiles(files);
|
||||
console.log("upload files: ", files);
|
||||
}
|
||||
|
||||
async function uploadImage() {
|
||||
const images: string[] = [];
|
||||
images.push(...attachImages);
|
||||
|
@ -1897,8 +1954,10 @@ function _Chat() {
|
|||
<PromptHints prompts={promptHints} onPromptSelect={onPromptSelect} />
|
||||
|
||||
<ChatActions
|
||||
uploadDocument={uploadDocument}
|
||||
uploadImage={uploadImage}
|
||||
setAttachImages={setAttachImages}
|
||||
setAttachFiles={setAttachFiles}
|
||||
setUploading={setUploading}
|
||||
showPromptModal={() => setShowPromptModal(true)}
|
||||
scrollToBottom={scrollToBottom}
|
||||
|
@ -1920,7 +1979,7 @@ function _Chat() {
|
|||
/>
|
||||
<label
|
||||
className={`${styles["chat-input-panel-inner"]} ${
|
||||
attachImages.length != 0
|
||||
attachImages.length != 0 || attachFiles.length != 0
|
||||
? styles["chat-input-panel-inner-attach"]
|
||||
: ""
|
||||
}`}
|
||||
|
@ -1944,29 +2003,55 @@ function _Chat() {
|
|||
fontFamily: config.fontFamily,
|
||||
}}
|
||||
/>
|
||||
{attachImages.length != 0 && (
|
||||
<div className={styles["attach-images"]}>
|
||||
{attachImages.map((image, index) => {
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className={styles["attach-image"]}
|
||||
style={{ backgroundImage: `url("${image}")` }}
|
||||
>
|
||||
<div className={styles["attach-image-mask"]}>
|
||||
<DeleteImageButton
|
||||
deleteImage={() => {
|
||||
setAttachImages(
|
||||
attachImages.filter((_, i) => i !== index),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<div className={styles["attachments"]}>
|
||||
{attachImages.length != 0 && (
|
||||
<div className={styles["attach-images"]}>
|
||||
{attachImages.map((image, index) => {
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className={styles["attach-image"]}
|
||||
style={{ backgroundImage: `url("${image}")` }}
|
||||
>
|
||||
<div className={styles["attach-image-mask"]}>
|
||||
<DeleteImageButton
|
||||
deleteImage={() => {
|
||||
setAttachImages(
|
||||
attachImages.filter((_, i) => i !== index),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
{attachFiles.length != 0 && (
|
||||
<div className={styles["attach-files"]}>
|
||||
{attachFiles.map((file, index) => {
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className={styles["attach-file"]}
|
||||
style={{ backgroundImage: `url("${file}")` }}
|
||||
>
|
||||
<FileIcon extension="csv" {...defaultStyles["csv"]} />
|
||||
<div className={styles["attach-image-mask"]}>
|
||||
<DeleteImageButton
|
||||
deleteImage={() => {
|
||||
setAttachFiles(
|
||||
attachFiles.filter((_, i) => i !== index),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<IconButton
|
||||
icon={<SendWhiteIcon />}
|
||||
text={Locale.Chat.Send}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><defs><image width="32" height="40" id="img1" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAoCAMAAACo9wirAAAAAXNSR0IB2cksfwAAAIpQTFRFAAAAVVVVUVFRUVFRUFBQUVFRUFBQUVFRf39/UFBQUVFRUFBQZmZmVVVVUVFRAAAAUVFRUFBQUVFRUVFRUFBQX19fUFBQUVFRUVFRU1NTT09PUVFRUVFRUVFRUlJSUFBQTU1NT09PUVFRUlJSUFBQUVFRUVFRUVFRUFBQUFBQUFBQUFBQUFBQUVFReUEd1wAAAC50Uk5TAAmB3Pz/+ZACxJx1BQadAZaed4CRCH7bMjEw7O81Iu0hdq8l4NXWhveF83jawwNJtaIAAAEdSURBVHicpdRrSwJBFAbg8+7WskarlIqhtBAS1f//LwaFIJkWiX3wgotbTrNIeubMBaL32+x5dnYunAWhCh2jR4UxTL9I5HTHBKJY1k2BOKIEK1YUAiegdEkCMAH9oCQT1DZMuMAZ1lptCy8oG5U43xReQEy4ARMecBQW+N12muz3YgF9cFnJhAWaCz7Sn5JAXB4sQG1gzq7fBmJLAlzNWtFbAHTxQR2MvaCqExcC7OtcmCDH9CBHLnDzSt14TNffU0rWLtAHVtsF1ZNsp4a+beqDrs9D5/B/cLmk7DME8gn1XkJA5E/gDkNW6eNRgEbv2Xj3diBneFDhGfxrkO3PkxWkcOgDRz1/0msXfSBz4fqJsdy/Q/YBi2oqNfsBQGqACm/REAgAAAAASUVORK5CYII="/></defs><style></style><use href="#img1" transform="matrix(.333,0,0,.333,2.667,1.333)"/></svg>
|
After Width: | Height: | Size: 1020 B |
|
@ -22,6 +22,7 @@
|
|||
"@hello-pangea/dnd": "^16.5.0",
|
||||
"@next/third-parties": "^14.1.0",
|
||||
"@svgr/webpack": "^6.5.1",
|
||||
"@types/react-file-icon": "^1.0.4",
|
||||
"@vercel/analytics": "^0.1.11",
|
||||
"@vercel/speed-insights": "^1.0.2",
|
||||
"axios": "^1.7.5",
|
||||
|
@ -31,14 +32,15 @@
|
|||
"html-to-image": "^1.11.11",
|
||||
"idb-keyval": "^6.2.1",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mermaid": "^10.6.1",
|
||||
"markdown-to-txt": "^2.0.1",
|
||||
"mermaid": "^10.6.1",
|
||||
"nanoid": "^5.0.3",
|
||||
"next": "^14.1.1",
|
||||
"node-fetch": "^3.3.1",
|
||||
"openapi-client-axios": "^7.5.5",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-file-icon": "^1.5.0",
|
||||
"react-markdown": "^8.0.7",
|
||||
"react-router-dom": "^6.15.0",
|
||||
"rehype-highlight": "^6.0.0",
|
||||
|
@ -80,4 +82,4 @@
|
|||
"lint-staged/yaml": "^2.2.2"
|
||||
},
|
||||
"packageManager": "yarn@1.22.19"
|
||||
}
|
||||
}
|
||||
|
|
22
yarn.lock
22
yarn.lock
|
@ -1762,6 +1762,13 @@
|
|||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-file-icon@^1.0.4":
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-file-icon/-/react-file-icon-1.0.4.tgz#6825b0e6b8ab639f7f25a6cd52499650d3afcd89"
|
||||
integrity sha512-c1mIklUDaxm9odxf8RTiy/EAxsblZliJ86EKIOAyuafP9eK3iudyn4ATv53DX6ZvgGymc7IttVNm97LTGnTiYA==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-katex@^3.0.0":
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-katex/-/react-katex-3.0.0.tgz#119a902bff10eb52f449fac744aaed8c4909391f"
|
||||
|
@ -2416,6 +2423,11 @@ color-name@~1.1.4:
|
|||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
|
||||
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
||||
|
||||
colord@^2.9.3:
|
||||
version "2.9.3"
|
||||
resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43"
|
||||
integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==
|
||||
|
||||
colorette@^2.0.19:
|
||||
version "2.0.19"
|
||||
resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798"
|
||||
|
@ -5404,7 +5416,7 @@ prettier@^3.0.2:
|
|||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.0.2.tgz#78fcecd6d870551aa5547437cdae39d4701dca5b"
|
||||
integrity sha512-o2YR9qtniXvwEZlOKbveKfDQVyqxbEIWn48Z8m3ZJjBjcCmUy3xZGIv+7AkaeuaTr6yPXJjwv07ZWlsWbEy1rQ==
|
||||
|
||||
prop-types@^15.0.0, prop-types@^15.8.1:
|
||||
prop-types@^15.0.0, prop-types@^15.7.2, prop-types@^15.8.1:
|
||||
version "15.8.1"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
|
||||
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
|
||||
|
@ -5453,6 +5465,14 @@ react-dom@^18.2.0:
|
|||
loose-envify "^1.1.0"
|
||||
scheduler "^0.23.0"
|
||||
|
||||
react-file-icon@^1.5.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/react-file-icon/-/react-file-icon-1.5.0.tgz#cccc8827d927291b8a52fab41afbe5b3625ddbf4"
|
||||
integrity sha512-6K2/nAI69CS838HOS+4S95MLXwf1neWywek1FgqcTFPTYjnM8XT7aBLz4gkjoqQKY9qPhu3A2tu+lvxhmZYY9w==
|
||||
dependencies:
|
||||
colord "^2.9.3"
|
||||
prop-types "^15.7.2"
|
||||
|
||||
react-is@^16.13.1, react-is@^16.7.0:
|
||||
version "16.13.1"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||
|
|
Loading…
Reference in New Issue