From 2b921093e141d8ceb502b8861c0a0da840629982 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Wed, 12 Apr 2023 17:03:42 +0800 Subject: [PATCH 1/9] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 047175129..0607950c2 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ One-Click to deploy well-designed ChatGPT web UI on Vercel. - **Deploy for free with one-click** on Vercel in under 1 minute - Privacy first, all data stored locally in the browser - Responsive design, dark mode and PWA -- Fast first screen loading speed (~100kb) +- Fast first screen loading speed (~100kb), support streaming response - 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 - One-click export all chat history with full Markdown support @@ -49,7 +49,7 @@ One-Click to deploy well-designed ChatGPT web UI on Vercel. - 在 1 分钟内使用 Vercel **免费一键部署** - 精心设计的 UI,响应式设计,支持深色模式,支持 PWA -- 极快的首屏加载速度(~100kb) +- 极快的首屏加载速度(~100kb),支持流式响应 - 隐私安全,所有数据保存在用户浏览器本地 - 海量的内置 prompt 列表,来自[中文](https://github.com/PlexPt/awesome-chatgpt-prompts-zh)和[英文](https://github.com/f/awesome-chatgpt-prompts) - 自动压缩上下文聊天记录,在节省 Token 的同时支持超长对话 From c1bb53c4e3d03549aa1ec47c838bee4434cc12e4 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Wed, 12 Apr 2023 19:48:12 +0800 Subject: [PATCH 2/9] Revert "fix: autoscroll conflict" --- app/components/chat.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/app/components/chat.tsx b/app/components/chat.tsx index 03f905ecf..33ac3ac57 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -354,7 +354,6 @@ export function Chat(props: { const [hitBottom, setHitBottom] = useState(false); const onChatBodyScroll = (e: HTMLElement) => { - setAutoScroll(false); const isTouchBottom = e.scrollTop + e.clientHeight >= e.scrollHeight - 20; setHitBottom(isTouchBottom); }; From 9146b98285800c09666f7a439ac9fbbfa041e741 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Wed, 12 Apr 2023 23:04:45 +0800 Subject: [PATCH 3/9] fix: *.scss *.svg types --- app/global.d.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 app/global.d.ts diff --git a/app/global.d.ts b/app/global.d.ts new file mode 100644 index 000000000..bbe834b3d --- /dev/null +++ b/app/global.d.ts @@ -0,0 +1,14 @@ +declare module "*.jpg"; +declare module "*.png"; +declare module "*.woff2"; +declare module "*.woff"; +declare module "*.ttf"; +declare module "*.scss"; + +declare module "*.svg" { + import React = require("react"); + + export const ReactComponent: React.SFC>; + const src: string; + export default src; +} From d790b0b372c0ff2236b24a57f83f9e59a8b76914 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Wed, 12 Apr 2023 23:27:28 +0800 Subject: [PATCH 4/9] feat: close #680 lazy load markdown dom --- app/components/chat.tsx | 28 ++++++++--------- app/components/markdown.tsx | 61 +++++++++++++++++++++++++------------ app/global.d.ts | 13 +++----- 3 files changed, 59 insertions(+), 43 deletions(-) diff --git a/app/components/chat.tsx b/app/components/chat.tsx index 33ac3ac57..71c8de430 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -672,22 +672,18 @@ export function Chat(props: { )} - {(message.preview || message.content.length === 0) && - !isUser ? ( - - ) : ( -
onRightClick(e, message)} - onDoubleClickCapture={() => { - if (!isMobileScreen()) return; - setUserInput(message.content); - }} - > - -
- )} + onRightClick(e, message)} + onDoubleClickCapture={() => { + if (!isMobileScreen()) return; + setUserInput(message.content); + }} + /> {!isUser && !message.preview && (
diff --git a/app/components/markdown.tsx b/app/components/markdown.tsx index c8076640c..886956f06 100644 --- a/app/components/markdown.tsx +++ b/app/components/markdown.tsx @@ -8,6 +8,8 @@ import RehypeHighlight from "rehype-highlight"; import { useRef, useState, RefObject, useEffect } from "react"; import { copyToClipboard } from "../utils"; +import LoadingIcon from "../icons/three-dots.svg"; + export function PreCode(props: { children: any }) { const ref = useRef(null); @@ -50,26 +52,47 @@ const useLazyLoad = (ref: RefObject): boolean => { return isIntersecting; }; -export function Markdown(props: { content: string }) { +export function Markdown( + props: { + content: string; + loading?: boolean; + fontSize?: number; + } & React.DOMAttributes, +) { + const mdRef = useRef(null); + const shouldRender = useLazyLoad(mdRef); + const shouldLoading = props.loading || !shouldRender; + return ( - - {props.content} - + {shouldLoading ? ( + + ) : ( + + {props.content} + + )} +
); } diff --git a/app/global.d.ts b/app/global.d.ts index bbe834b3d..bd1c062de 100644 --- a/app/global.d.ts +++ b/app/global.d.ts @@ -3,12 +3,9 @@ declare module "*.png"; declare module "*.woff2"; declare module "*.woff"; declare module "*.ttf"; -declare module "*.scss"; - -declare module "*.svg" { - import React = require("react"); - - export const ReactComponent: React.SFC>; - const src: string; - export default src; +declare module "*.scss" { + const content: Record; + export default content; } + +declare module "*.svg"; From e12ea69b6a0c01b80379e032f49bf327204a96fe Mon Sep 17 00:00:00 2001 From: Yorun <547747006@qq.com> Date: Wed, 12 Apr 2023 15:31:40 +0000 Subject: [PATCH 5/9] ci(sync): add an error prompt --- .github/workflows/sync.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml index 52feae50e..15d324074 100644 --- a/.github/workflows/sync.yml +++ b/.github/workflows/sync.yml @@ -31,3 +31,10 @@ jobs: # Set test_mode true to run tests instead of the true action!! test_mode: false + + - name: Sync check + if: failure() + run: | + echo "::error::由于权限不足,导致同步失败(这是预期的行为),请前往仓库首页手动执行[Sync fork]。" + echo "::error::Due to insufficient permissions, synchronization failed (as expected). Please go to the repository homepage and manually perform [Sync fork]." + exit 1 From 8363cdd9faee5ad56e90586e51f582061d506404 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Thu, 13 Apr 2023 02:07:24 +0800 Subject: [PATCH 6/9] feat: close #680 lazy rendering markdown --- app/components/chat.module.scss | 24 +++++++++ app/components/chat.tsx | 87 ++++++++++++++++++++++++++++++--- app/components/home.module.scss | 9 +++- app/components/home.tsx | 6 +-- app/components/markdown.tsx | 42 ++++++++++++++-- app/icons/auto.svg | 1 + app/icons/bottom.svg | 1 + app/icons/dark.svg | 1 + app/icons/light.svg | 1 + app/styles/globals.scss | 12 +++-- app/utils.ts | 4 ++ 11 files changed, 168 insertions(+), 20 deletions(-) create mode 100644 app/icons/auto.svg create mode 100644 app/icons/bottom.svg create mode 100644 app/icons/dark.svg create mode 100644 app/icons/light.svg diff --git a/app/components/chat.module.scss b/app/components/chat.module.scss index f57e6c100..7cd2889f7 100644 --- a/app/components/chat.module.scss +++ b/app/components/chat.module.scss @@ -1,5 +1,29 @@ @import "../styles/animation.scss"; +.chat-input-actions { + display: flex; + flex-wrap: wrap; + + .chat-input-action { + display: inline-flex; + border-radius: 20px; + font-size: 12px; + background-color: var(--white); + color: var(--black); + border: var(--border-in-light); + padding: 4px 10px; + animation: slide-in ease 0.3s; + box-shadow: var(--card-shadow); + transition: all ease 0.3s; + margin-bottom: 10px; + align-items: center; + + &:not(:last-child) { + margin-right: 5px; + } + } +} + .prompt-toast { position: absolute; bottom: -50px; diff --git a/app/components/chat.tsx b/app/components/chat.tsx index 71c8de430..b9ae13926 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -14,6 +14,11 @@ import DeleteIcon from "../icons/delete.svg"; import MaxIcon from "../icons/max.svg"; import MinIcon from "../icons/min.svg"; +import LightIcon from "../icons/light.svg"; +import DarkIcon from "../icons/dark.svg"; +import AutoIcon from "../icons/auto.svg"; +import BottomIcon from "../icons/bottom.svg"; + import { Message, SubmitKey, @@ -22,6 +27,7 @@ import { ROLES, createMessage, useAccessStore, + Theme, } from "../store"; import { @@ -31,6 +37,7 @@ import { isMobileScreen, selectOrCopy, autoGrowTextArea, + getCSSVar, } from "../utils"; import dynamic from "next/dynamic"; @@ -60,7 +67,11 @@ export function Avatar(props: { role: Message["role"] }) { const config = useChatStore((state) => state.config); if (props.role !== "user") { - return ; + return ( +
+ +
+ ); } return ( @@ -316,22 +327,78 @@ function useScrollToBottom() { // for auto-scroll const scrollRef = useRef(null); const [autoScroll, setAutoScroll] = useState(true); + const scrollToBottom = () => { + const dom = scrollRef.current; + if (dom) { + setTimeout(() => (dom.scrollTop = dom.scrollHeight), 1); + } + }; // auto scroll useLayoutEffect(() => { - const dom = scrollRef.current; - if (dom && autoScroll) { - setTimeout(() => (dom.scrollTop = dom.scrollHeight), 1); - } + autoScroll && scrollToBottom(); }); return { scrollRef, autoScroll, setAutoScroll, + scrollToBottom, }; } +export function ChatActions(props: { + showPromptModal: () => void; + scrollToBottom: () => void; + hitBottom: boolean; +}) { + const chatStore = useChatStore(); + + const theme = chatStore.config.theme; + + function nextTheme() { + const themes = [Theme.Auto, Theme.Light, Theme.Dark]; + const themeIndex = themes.indexOf(theme); + const nextIndex = (themeIndex + 1) % themes.length; + const nextTheme = themes[nextIndex]; + chatStore.updateConfig((config) => (config.theme = nextTheme)); + } + + return ( +
+ {!props.hitBottom && ( +
+ +
+ )} + {props.hitBottom && ( +
+ +
+ )} + +
+ {theme === Theme.Auto ? ( + + ) : theme === Theme.Light ? ( + + ) : theme === Theme.Dark ? ( + + ) : null} +
+
+ ); +} + export function Chat(props: { showSideBar?: () => void; sideBarShowing?: boolean; @@ -350,7 +417,7 @@ export function Chat(props: { const [beforeInput, setBeforeInput] = useState(""); const [isLoading, setIsLoading] = useState(false); const { submitKey, shouldSubmit } = useSubmitHandler(); - const { scrollRef, setAutoScroll } = useScrollToBottom(); + const { scrollRef, setAutoScroll, scrollToBottom } = useScrollToBottom(); const [hitBottom, setHitBottom] = useState(false); const onChatBodyScroll = (e: HTMLElement) => { @@ -683,6 +750,8 @@ export function Chat(props: { if (!isMobileScreen()) return; setUserInput(message.content); }} + fontSize={fontSize} + parentRef={scrollRef} /> {!isUser && !message.preview && ( @@ -700,6 +769,12 @@ export function Chat(props: {
+ + setShowPromptModal(true)} + scrollToBottom={scrollToBottom} + hitBottom={hitBottom} + />