feat: support other type file upload

This commit is contained in:
Hk-Gosuto
2024-03-31 23:07:17 +08:00
parent aea5bedb68
commit 80a077a3db
9 changed files with 223 additions and 33 deletions

View File

@@ -1,5 +1,66 @@
@import "../styles/animation.scss";
.attach-files {
position: absolute;
left: 30px;
bottom: 32px;
display: flex;
}
.attach-file {
cursor: default;
width: 64px;
height: 64px;
border: rgba($color: #888, $alpha: 0.2) 1px solid;
border-radius: 5px;
margin-right: 10px;
background-size: cover;
background-position: center;
background-color: var(--second);
.attach-file-mask {
width: 100%;
height: 100%;
opacity: 0;
transition: all ease 0.2s;
}
.attach-file-mask:hover {
opacity: 1;
}
.delete-file {
width: 24px;
height: 24px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
border-radius: 5px;
float: right;
background-color: var(--white);
}
.attach-file-name {
font-size: 12px;
display: flex;
flex-direction: column;
justify-content: center;
width: 100%;
height: 100%;
padding: 4px;
box-sizing: border-box;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
// line-height: 1.2;
// max-height: 2.4em;
position: absolute;
top: 0;
left: 0;
}
}
.attach-images {
position: absolute;
left: 30px;
@@ -232,10 +293,12 @@
animation: slide-in ease 0.3s;
$linear: linear-gradient(to right,
rgba(0, 0, 0, 0),
rgba(0, 0, 0, 1),
rgba(0, 0, 0, 0));
$linear: linear-gradient(
to right,
rgba(0, 0, 0, 0),
rgba(0, 0, 0, 1),
rgba(0, 0, 0, 0)
);
mask-image: $linear;
@mixin show {
@@ -368,7 +431,7 @@
}
}
.chat-message-user>.chat-message-container {
.chat-message-user > .chat-message-container {
align-items: flex-end;
}
@@ -482,23 +545,27 @@
border: rgba($color: #888, $alpha: 0.2) 1px solid;
}
@media only screen and (max-width: 600px) {
$calc-image-width: calc(100vw/3*2/var(--image-count));
$calc-image-width: calc(100vw / 3 * 2 / var(--image-count));
.chat-message-item-image-multi {
width: $calc-image-width;
height: $calc-image-width;
}
.chat-message-item-image {
max-width: calc(100vw/3*2);
max-width: calc(100vw / 3 * 2);
}
}
@media screen and (min-width: 600px) {
$max-image-width: calc(calc(1200px - var(--sidebar-width))/3*2/var(--image-count));
$image-width: calc(calc(var(--window-width) - var(--sidebar-width))/3*2/var(--image-count));
$max-image-width: calc(
calc(1200px - var(--sidebar-width)) / 3 * 2 / var(--image-count)
);
$image-width: calc(
calc(var(--window-width) - var(--sidebar-width)) / 3 * 2 /
var(--image-count)
);
.chat-message-item-image-multi {
width: $image-width;
@@ -508,7 +575,7 @@
}
.chat-message-item-image {
max-width: calc(calc(1200px - var(--sidebar-width))/3*2);
max-width: calc(calc(1200px - var(--sidebar-width)) / 3 * 2);
}
}
@@ -526,7 +593,7 @@
z-index: 1;
}
.chat-message-user>.chat-message-container>.chat-message-item {
.chat-message-user > .chat-message-container > .chat-message-item {
background-color: var(--second);
&:hover {
@@ -637,7 +704,8 @@
min-height: 68px;
}
.chat-input:focus {}
.chat-input:focus {
}
.chat-input-send {
background-color: var(--primary);
@@ -656,4 +724,4 @@
.chat-input-send {
bottom: 30px;
}
}
}

View File

@@ -69,6 +69,7 @@ import {
isVisionModel,
compressImage,
isFirefox,
isSupportRAGModel,
} from "../utils";
import dynamic from "next/dynamic";
@@ -116,6 +117,7 @@ import {
SpeechApi,
WebTranscriptionApi,
} from "../utils/speech";
import { getServerSideConfig } from "../config/server";
const ttsPlayer = createTTSPlayer();
@@ -460,6 +462,8 @@ function useScrollToBottom(
export function ChatActions(props: {
uploadImage: () => void;
setAttachImages: (images: string[]) => void;
uploadFile: () => void;
setAttachFiles: (files: string[]) => void;
setUploading: (uploading: boolean) => void;
showPromptModal: () => void;
scrollToBottom: () => void;
@@ -503,9 +507,15 @@ export function ChatActions(props: {
const [showModelSelector, setShowModelSelector] = useState(false);
const [showUploadImage, setShowUploadImage] = useState(false);
const [showUploadFile, setShowUploadFile] = useState(false);
useEffect(() => {
const show = isVisionModel(currentModel);
setShowUploadImage(show);
const serverConfig = getServerSideConfig();
setShowUploadFile(
serverConfig.isEnableRAG && !show && isSupportRAGModel(currentModel),
);
if (!show) {
props.setAttachImages([]);
props.setUploading(false);
@@ -555,6 +565,14 @@ export function ChatActions(props: {
icon={props.uploading ? <LoadingButtonIcon /> : <ImageIcon />}
/>
)}
{showUploadFile && (
<ChatAction
onClick={props.uploadFile}
text={Locale.Chat.InputActions.UploadFle}
icon={props.uploading ? <LoadingButtonIcon /> : <UploadIcon />}
/>
)}
<ChatAction
onClick={nextTheme}
text={Locale.Chat.InputActions.Theme[theme]}
@@ -713,6 +731,14 @@ export function DeleteImageButton(props: { deleteImage: () => void }) {
);
}
export function DeleteFileButton(props: { deleteFile: () => void }) {
return (
<div className={styles["delete-file"]} onClick={props.deleteFile}>
<DeleteIcon />
</div>
);
}
function _Chat() {
type RenderMessage = ChatMessage & { preview?: boolean };
@@ -743,6 +769,7 @@ function _Chat() {
const navigate = useNavigate();
const [attachImages, setAttachImages] = useState<string[]>([]);
const [uploading, setUploading] = useState(false);
const [attachFiles, setAttachFiles] = useState<string[]>([]);
// prompt hints
const promptStore = usePromptStore();
@@ -851,6 +878,7 @@ function _Chat() {
.onUserInput(userInput, attachImages)
.then(() => setIsLoading(false));
setAttachImages([]);
setAttachFiles([]);
localStorage.setItem(LAST_INPUT_KEY, userInput);
setUserInput("");
setPromptHints([]);
@@ -1324,6 +1352,53 @@ function _Chat() {
setAttachImages(images);
}
async function uploadFile() {
const uploadFiles: string[] = [];
uploadFiles.push(...attachFiles);
uploadFiles.push(
...(await new Promise<string[]>((res, rej) => {
const fileInput = document.createElement("input");
fileInput.type = "file";
fileInput.accept = ".pdf,.txt,.json,.csv,.md";
fileInput.multiple = true;
fileInput.onchange = (event: any) => {
setUploading(true);
const files = event.target.files;
const api = new ClientApi();
const fileDatas: string[] = [];
for (let i = 0; i < files.length; i++) {
const file = event.target.files[i];
api.file
.upload(file)
.then((fileInfo) => {
console.log(fileInfo);
fileDatas.push(fileInfo);
if (
fileDatas.length === 5 ||
fileDatas.length === files.length
) {
setUploading(false);
res(fileDatas);
}
})
.catch((e) => {
setUploading(false);
rej(e);
});
}
};
fileInput.click();
})),
);
const filesLength = uploadFiles.length;
if (filesLength > 5) {
uploadFiles.splice(5, filesLength - 5);
}
setAttachFiles(uploadFiles);
}
return (
<div className={styles.chat} key={session.id}>
<div className="window-header" data-tauri-drag-region>
@@ -1632,6 +1707,8 @@ function _Chat() {
<ChatActions
uploadImage={uploadImage}
setAttachImages={setAttachImages}
uploadFile={uploadFile}
setAttachFiles={setAttachFiles}
setUploading={setUploading}
showPromptModal={() => setShowPromptModal(true)}
scrollToBottom={scrollToBottom}
@@ -1651,7 +1728,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"]
: ""
}`}
@@ -1697,7 +1774,26 @@ function _Chat() {
})}
</div>
)}
{attachFiles.length != 0 && (
<div className={styles["attach-files"]}>
{attachFiles.map((file, index) => {
return (
<div key={index} className={styles["attach-file"]}>
<div className={styles["attach-file-mask"]}>
<DeleteFileButton
deleteFile={() => {
setAttachFiles(
attachFiles.filter((_, i) => i !== index),
);
}}
/>
</div>
<div className={styles["attach-file-name"]}>${file}</div>
</div>
);
})}
</div>
)}
{config.sttConfig.enable ? (
<IconButton
icon={<VoiceWhiteIcon />}