mirror of
https://github.com/Yidadaa/ChatGPT-Next-Web.git
synced 2025-09-10 12:55:53 +08:00
Merge branch 'main' into feat/voice-input
This commit is contained in:
@@ -219,6 +219,8 @@ function useSubmitHandler() {
|
||||
}, []);
|
||||
|
||||
const shouldSubmit = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||
// Fix Chinese input method "Enter" on Safari
|
||||
if (e.keyCode == 229) return false;
|
||||
if (e.key !== "Enter") return false;
|
||||
if (e.key === "Enter" && (e.nativeEvent.isComposing || isComposing.current))
|
||||
return false;
|
||||
@@ -1098,6 +1100,47 @@ function _Chat() {
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const handlePaste = useCallback(
|
||||
async (event: React.ClipboardEvent<HTMLTextAreaElement>) => {
|
||||
const currentModel = chatStore.currentSession().mask.modelConfig.model;
|
||||
if(!isVisionModel(currentModel)){return;}
|
||||
const items = (event.clipboardData || window.clipboardData).items;
|
||||
for (const item of items) {
|
||||
if (item.kind === "file" && item.type.startsWith("image/")) {
|
||||
event.preventDefault();
|
||||
const file = item.getAsFile();
|
||||
if (file) {
|
||||
const images: string[] = [];
|
||||
images.push(...attachImages);
|
||||
images.push(
|
||||
...(await new Promise<string[]>((res, rej) => {
|
||||
setUploading(true);
|
||||
const imagesData: string[] = [];
|
||||
compressImage(file, 256 * 1024)
|
||||
.then((dataUrl) => {
|
||||
imagesData.push(dataUrl);
|
||||
setUploading(false);
|
||||
res(imagesData);
|
||||
})
|
||||
.catch((e) => {
|
||||
setUploading(false);
|
||||
rej(e);
|
||||
});
|
||||
})),
|
||||
);
|
||||
const imagesLength = images.length;
|
||||
|
||||
if (imagesLength > 3) {
|
||||
images.splice(3, imagesLength - 3);
|
||||
}
|
||||
setAttachImages(images);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
[attachImages, chatStore],
|
||||
);
|
||||
|
||||
async function uploadImage() {
|
||||
const images: string[] = [];
|
||||
@@ -1448,6 +1491,7 @@ function _Chat() {
|
||||
onKeyDown={onInputKeyDown}
|
||||
onFocus={scrollToBottom}
|
||||
onClick={scrollToBottom}
|
||||
onPaste={handlePaste}
|
||||
rows={inputRows}
|
||||
autoFocus={autoFocus}
|
||||
style={{
|
||||
|
@@ -21,6 +21,7 @@ export function AvatarPicker(props: {
|
||||
}) {
|
||||
return (
|
||||
<EmojiPicker
|
||||
width={"100%"}
|
||||
lazyLoadEmojis
|
||||
theme={EmojiTheme.AUTO}
|
||||
getEmojiUrl={getEmojiUrl}
|
||||
|
@@ -5,6 +5,8 @@
|
||||
|
||||
.avatar {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.edit-prompt-modal {
|
||||
|
@@ -693,7 +693,9 @@ export function Settings() {
|
||||
>
|
||||
<div
|
||||
className={styles.avatar}
|
||||
onClick={() => setShowEmojiPicker(true)}
|
||||
onClick={() => {
|
||||
setShowEmojiPicker(!showEmojiPicker);
|
||||
}}
|
||||
>
|
||||
<Avatar avatar={config.avatar} />
|
||||
</div>
|
||||
|
@@ -14,17 +14,24 @@
|
||||
|
||||
.popover-content {
|
||||
position: absolute;
|
||||
width: 350px;
|
||||
animation: slide-in 0.3s ease;
|
||||
right: 0;
|
||||
top: calc(100% + 10px);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
.popover-content {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
.popover-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
.list-item {
|
||||
|
@@ -26,10 +26,10 @@ export function Popover(props: {
|
||||
<div className={styles.popover}>
|
||||
{props.children}
|
||||
{props.open && (
|
||||
<div className={styles["popover-content"]}>
|
||||
<div className={styles["popover-mask"]} onClick={props.onClose}></div>
|
||||
{props.content}
|
||||
</div>
|
||||
<div className={styles["popover-mask"]} onClick={props.onClose}></div>
|
||||
)}
|
||||
{props.open && (
|
||||
<div className={styles["popover-content"]}>{props.content}</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
Reference in New Issue
Block a user