Merge branch 'Yidadaa:main' into main

This commit is contained in:
Ilario Scandurra 2023-04-02 21:14:04 +02:00 committed by GitHub
commit 502d22bd20
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 83 additions and 46 deletions

View File

@ -35,14 +35,17 @@ One-Click to deploy your own ChatGPT web UI.
- Awesome prompts powered by [awesome-chatgpt-prompts-zh](https://github.com/PlexPt/awesome-chatgpt-prompts-zh) and [awesome-chatgpt-prompts](https://github.com/f/awesome-chatgpt-prompts) - Awesome prompts powered by [awesome-chatgpt-prompts-zh](https://github.com/PlexPt/awesome-chatgpt-prompts-zh) and [awesome-chatgpt-prompts](https://github.com/f/awesome-chatgpt-prompts)
- Automatically compresses chat history to support long conversations while also saving your tokens - Automatically compresses chat history to support long conversations while also saving your tokens
- One-click export all chat history with full Markdown support - One-click export all chat history with full Markdown support
- I18n supported
## 开发计划 Roadmap ## 开发计划 Roadmap
- System Prompt: pin a user defined prompt as system prompt 为每个对话设置系统 Prompt [#138](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/138) - System Prompt: pin a user defined prompt as system prompt 为每个对话设置系统 Prompt [#138](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/138)
- User Prompt: user can edit and save custom prompts to prompt list 允许用户自行编辑内置 Prompt 列表 - User Prompt: user can edit and save custom prompts to prompt list 允许用户自行编辑内置 Prompt 列表
- Self-host Model: support llama, alpaca, ChatGLM, BELLE etc. 支持自部署的大语言模型 - Self-host Model: support llama, alpaca, ChatGLM, BELLE etc. 支持自部署的大语言模型
- Plugins: support network search, caculator, any other apis etc. 插件机制,支持联网搜索、计算器、调用其他平台 api [#165](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/165) - Plugins: support network search, caculator, any other apis etc. 插件机制,支持联网搜索、计算器、调用其他平台 api [#165](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/165)
### 不会开发的功能 Not in Plan ### 不会开发的功能 Not in Plan
- User login, accounts, cloud sync 用户登录、账号管理、消息云同步 - User login, accounts, cloud sync 用户登录、账号管理、消息云同步
- UI text customize 界面文字自定义 - UI text customize 界面文字自定义
@ -179,9 +182,10 @@ docker run -d -p 3000:3000 -e OPENAI_API_KEY="" -e CODE="" yidadaa/chatgpt-next-
![更多展示 More](./static/more.png) ![更多展示 More](./static/more.png)
## 捐赠 Donate USDT ## 捐赠 Donate USDT
> BNB Smart Chain (BEP 20) > BNB Smart Chain (BEP 20)
``` ```
0x67cD02c7EB62641De576a1fA3EdB32eA0c3ffD89 0x67cD02c7EB62641De576a1fA3EdB32eA0c3ffD89
``` ```

View File

@ -8,6 +8,7 @@ export function IconButton(props: {
text?: string; text?: string;
bordered?: boolean; bordered?: boolean;
shadow?: boolean; shadow?: boolean;
noDark?: boolean;
className?: string; className?: string;
title?: string; title?: string;
}) { }) {
@ -23,7 +24,11 @@ export function IconButton(props: {
title={props.title} title={props.title}
role="button" role="button"
> >
<div className={styles["icon-button-icon"]}>{props.icon}</div> <div
className={styles["icon-button-icon"] + ` ${props.noDark && "no-dark"}`}
>
{props.icon}
</div>
{props.text && ( {props.text && (
<div className={styles["icon-button-text"]}>{props.text}</div> <div className={styles["icon-button-text"]}>{props.text}</div>
)} )}

View File

@ -1,3 +1,5 @@
@import "../styles/animation.scss";
.prompt-toast { .prompt-toast {
position: absolute; position: absolute;
bottom: -50px; bottom: -50px;
@ -19,6 +21,8 @@
padding: 10px 20px; padding: 10px 20px;
border-radius: 100px; border-radius: 100px;
animation: slide-in-from-top ease 0.3s;
.prompt-toast-content { .prompt-toast-content {
margin-left: 10px; margin-left: 10px;
} }

View File

@ -51,7 +51,7 @@ const Emoji = dynamic(async () => (await import("emoji-picker-react")).Emoji, {
export function Avatar(props: { role: Message["role"] }) { export function Avatar(props: { role: Message["role"] }) {
const config = useChatStore((state) => state.config); const config = useChatStore((state) => state.config);
if (props.role === "assistant") { if (props.role !== "user") {
return <BotIcon className={styles["user-avtar"]} />; return <BotIcon className={styles["user-avtar"]} />;
} }
@ -99,7 +99,8 @@ function exportMessages(messages: Message[], topic: string) {
} }
function PromptToast(props: { function PromptToast(props: {
showModal: boolean; showToast?: boolean;
showModal?: boolean;
setShowModal: (_: boolean) => void; setShowModal: (_: boolean) => void;
}) { }) {
const chatStore = useChatStore(); const chatStore = useChatStore();
@ -126,6 +127,7 @@ function PromptToast(props: {
return ( return (
<div className={chatStyle["prompt-toast"]} key="prompt-toast"> <div className={chatStyle["prompt-toast"]} key="prompt-toast">
{props.showToast && (
<div <div
className={chatStyle["prompt-toast-inner"] + " clickable"} className={chatStyle["prompt-toast-inner"] + " clickable"}
role="button" role="button"
@ -136,6 +138,7 @@ function PromptToast(props: {
{Locale.Context.Toast(context.length)} {Locale.Context.Toast(context.length)}
</span> </span>
</div> </div>
)}
{props.showModal && ( {props.showModal && (
<div className="modal-mask"> <div className="modal-mask">
<Modal <Modal
@ -187,6 +190,7 @@ function PromptToast(props: {
icon={<DeleteIcon />} icon={<DeleteIcon />}
className={chatStyle["context-delete-button"]} className={chatStyle["context-delete-button"]}
onClick={() => removeContextPrompt(i)} onClick={() => removeContextPrompt(i)}
bordered
/> />
</div> </div>
))} ))}
@ -281,7 +285,7 @@ function useScrollToBottom() {
useLayoutEffect(() => { useLayoutEffect(() => {
const dom = scrollRef.current; const dom = scrollRef.current;
if (dom && autoScroll) { if (dom && autoScroll) {
setTimeout(() => (dom.scrollTop = dom.scrollHeight), 500); setTimeout(() => (dom.scrollTop = dom.scrollHeight), 1);
} }
}); });
@ -310,6 +314,12 @@ export function Chat(props: {
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { submitKey, shouldSubmit } = useSubmitHandler(); const { submitKey, shouldSubmit } = useSubmitHandler();
const { scrollRef, setAutoScroll } = useScrollToBottom(); const { scrollRef, setAutoScroll } = useScrollToBottom();
const [hitBottom, setHitBottom] = useState(false);
const onChatBodyScroll = (e: HTMLElement) => {
const isTouchBottom = e.scrollTop + e.clientHeight >= e.scrollHeight - 20;
setHitBottom(isTouchBottom);
};
// prompt hints // prompt hints
const promptStore = usePromptStore(); const promptStore = usePromptStore();
@ -441,7 +451,7 @@ export function Chat(props: {
role: "user", role: "user",
content: userInput, content: userInput,
date: new Date().toLocaleString(), date: new Date().toLocaleString(),
preview: false, preview: true,
}, },
] ]
: [], : [],
@ -505,12 +515,17 @@ export function Chat(props: {
</div> </div>
<PromptToast <PromptToast
showToast={!hitBottom}
showModal={showPromptModal} showModal={showPromptModal}
setShowModal={setShowPromptModal} setShowModal={setShowPromptModal}
/> />
</div> </div>
<div className={styles["chat-body"]} ref={scrollRef}> <div
className={styles["chat-body"]}
ref={scrollRef}
onScroll={(e) => onChatBodyScroll(e.currentTarget)}
>
{messages.map((message, i) => { {messages.map((message, i) => {
const isUser = message.role === "user"; const isUser = message.role === "user";
@ -533,6 +548,7 @@ export function Chat(props: {
<div <div
className={styles["chat-message-item"]} className={styles["chat-message-item"]}
onMouseOver={() => inputRef.current?.blur()} onMouseOver={() => inputRef.current?.blur()}
onTouchStart={() => inputRef.current?.blur()}
> >
{!isUser && {!isUser &&
!(message.preview || message.content.length === 0) && ( !(message.preview || message.content.length === 0) && (
@ -612,7 +628,8 @@ export function Chat(props: {
<IconButton <IconButton
icon={<SendWhiteIcon />} icon={<SendWhiteIcon />}
text={Locale.Chat.Send} text={Locale.Chat.Send}
className={styles["chat-input-send"] + " no-dark"} className={styles["chat-input-send"]}
noDark
onClick={onUserSubmit} onClick={onUserSubmit}
/> />
</div> </div>

View File

@ -1,4 +1,5 @@
@import "./window.scss"; @import "./window.scss";
@import "../styles/animation.scss";
@mixin container { @mixin container {
background-color: var(--white); background-color: var(--white);
@ -73,7 +74,7 @@
.sidebar { .sidebar {
position: absolute; position: absolute;
left: -100%; left: -100%;
z-index: 999; z-index: 1000;
height: var(--full-height); height: var(--full-height);
transition: all ease 0.3s; transition: all ease 0.3s;
box-shadow: none; box-shadow: none;
@ -132,18 +133,6 @@
overflow: hidden; overflow: hidden;
} }
@keyframes slide-in {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0px);
}
}
.chat-item:hover { .chat-item:hover {
background-color: var(--hover-color); background-color: var(--hover-color);
} }
@ -344,6 +333,7 @@
.chat-input-panel { .chat-input-panel {
width: 100%; width: 100%;
padding: 20px; padding: 20px;
padding-top: 5px;
box-sizing: border-box; box-sizing: border-box;
flex-direction: column; flex-direction: column;
} }

View File

@ -1,3 +1,5 @@
@import "../styles/animation.scss";
.card { .card {
background-color: var(--white); background-color: var(--white);
border-radius: 10px; border-radius: 10px;
@ -24,18 +26,6 @@
height: 100vh; height: 100vh;
} }
@keyframes slide-in {
from {
transform: translateY(10px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
.list-item { .list-item {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;

View File

@ -35,7 +35,7 @@ const cn = {
Download: "下载文件", Download: "下载文件",
}, },
Memory: { Memory: {
Title: "上下文记忆 Prompt", Title: "历史记忆",
EmptyContent: "尚未记忆", EmptyContent: "尚未记忆",
Copy: "全部复制", Copy: "全部复制",
}, },

View File

@ -88,7 +88,11 @@ export async function requestUsage() {
const response = (await res.json()) as { const response = (await res.json()) as {
total_usage: number; total_usage: number;
}; };
return Math.round(response.total_usage) / 100;
if (response.total_usage) {
response.total_usage = Math.round(response.total_usage) / 100;
}
return response.total_usage;
} catch (error) { } catch (error) {
console.error("[Request usage] ", error, res.body); console.error("[Request usage] ", error, res.body);
} }

23
app/styles/animation.scss Normal file
View File

@ -0,0 +1,23 @@
@keyframes slide-in {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0px);
}
}
@keyframes slide-in-from-top {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0px);
}
}

View File

@ -188,7 +188,7 @@ input[type="text"] {
appearance: none; appearance: none;
border-radius: 10px; border-radius: 10px;
border: var(--border-in-light); border: var(--border-in-light);
height: 36px; min-height: 36px;
box-sizing: border-box; box-sizing: border-box;
background: var(--white); background: var(--white);
color: var(--black); color: var(--black);