From f6ff32f339c974866a529f2612739cb141daa0ce Mon Sep 17 00:00:00 2001 From: Binh Le Date: Sun, 7 May 2023 14:54:09 +0700 Subject: [PATCH 01/21] Add Vietnamese translation --- app/locales/cn.ts | 1 + app/locales/de.ts | 1 + app/locales/en.ts | 1 + app/locales/es.ts | 1 + app/locales/index.ts | 3 + app/locales/it.ts | 1 + app/locales/jp.ts | 1 + app/locales/tr.ts | 1 + app/locales/tw.ts | 1 + app/locales/vi.ts | 241 +++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 252 insertions(+) create mode 100644 app/locales/vi.ts diff --git a/app/locales/cn.ts b/app/locales/cn.ts index f973dfbc9..8bec7c40e 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -78,6 +78,7 @@ const cn = { tr: "Türkçe", jp: "日本語", de: "Deutsch", + vi: "Vietnamese", }, }, Avatar: "头像", diff --git a/app/locales/de.ts b/app/locales/de.ts index 048e575c5..56202722d 100644 --- a/app/locales/de.ts +++ b/app/locales/de.ts @@ -81,6 +81,7 @@ const de: LocaleType = { tr: "Türkçe", jp: "日本語", de: "Deutsch", + vi: "Vietnamese", }, }, Avatar: "Avatar", diff --git a/app/locales/en.ts b/app/locales/en.ts index e424d9b47..cb97c51ce 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -80,6 +80,7 @@ const en: LocaleType = { tr: "Türkçe", jp: "日本語", de: "Deutsch", + vi: "Vietnamese", }, }, Avatar: "Avatar", diff --git a/app/locales/es.ts b/app/locales/es.ts index 46d18a547..df28075eb 100644 --- a/app/locales/es.ts +++ b/app/locales/es.ts @@ -80,6 +80,7 @@ const es: LocaleType = { tr: "Türkçe", jp: "日本語", de: "Deutsch", + vi: "Vietnamese", }, }, Avatar: "Avatar", diff --git a/app/locales/index.ts b/app/locales/index.ts index 40f0a1ade..dee6f795b 100644 --- a/app/locales/index.ts +++ b/app/locales/index.ts @@ -6,6 +6,7 @@ import IT from "./it"; import TR from "./tr"; import JP from "./jp"; import DE from "./de"; +import VI from "./vi"; export type { LocaleType } from "./cn"; @@ -18,6 +19,7 @@ export const AllLangs = [ "tr", "jp", "de", + "vi", ] as const; export type Lang = (typeof AllLangs)[number]; @@ -79,4 +81,5 @@ export default { tr: TR, jp: JP, de: DE, + vi: VI, }[getLang()] as typeof CN; diff --git a/app/locales/it.ts b/app/locales/it.ts index ee9a2c2bc..abf655f0d 100644 --- a/app/locales/it.ts +++ b/app/locales/it.ts @@ -80,6 +80,7 @@ const it: LocaleType = { tr: "Türkçe", jp: "日本語", de: "Deutsch", + vi: "Vietnamese", }, }, Avatar: "Avatar", diff --git a/app/locales/jp.ts b/app/locales/jp.ts index fb693cf5b..de03f9fdc 100644 --- a/app/locales/jp.ts +++ b/app/locales/jp.ts @@ -80,6 +80,7 @@ const jp: LocaleType = { tr: "Türkçe", jp: "日本語", de: "Deutsch", + vi: "Vietnamese", }, }, Avatar: "アバター", diff --git a/app/locales/tr.ts b/app/locales/tr.ts index 5eb4fe3e4..6793beb95 100644 --- a/app/locales/tr.ts +++ b/app/locales/tr.ts @@ -80,6 +80,7 @@ const tr: LocaleType = { tr: "Türkçe", jp: "日本語", de: "Deutsch", + vi: "Vietnamese", }, }, Avatar: "Avatar", diff --git a/app/locales/tw.ts b/app/locales/tw.ts index de964fc3f..c541e9724 100644 --- a/app/locales/tw.ts +++ b/app/locales/tw.ts @@ -78,6 +78,7 @@ const tw: LocaleType = { tr: "Türkçe", jp: "日本語", de: "Deutsch", + vi: "Vietnamese", }, }, Avatar: "大頭貼", diff --git a/app/locales/vi.ts b/app/locales/vi.ts new file mode 100644 index 000000000..66d4a4d58 --- /dev/null +++ b/app/locales/vi.ts @@ -0,0 +1,241 @@ +import { SubmitKey } from "../store/config"; +import type { LocaleType } from "./index"; + +const vi: LocaleType = { + WIP: "Coming Soon...", + Error: { + Unauthorized: + "Truy cập chưa xác thực, vui lòng nhập mã truy cập trong trang cài đặt.", + }, + ChatItem: { + ChatItemCount: (count: number) => `${count} tin nhắn`, + }, + Chat: { + SubTitle: (count: number) => `${count} tin nhắn với ChatGPT`, + Actions: { + ChatList: "Xem danh sách chat", + CompressedHistory: "Nén tin nhắn trong quá khứ", + Export: "Xuất tất cả tin nhắn dưới dạng Markdown", + Copy: "Sao chép", + Stop: "Dừng", + Retry: "Thử lại", + Delete: "Xóa", + }, + Rename: "Đổi tên", + Typing: "Đang nhập…", + Input: (submitKey: string) => { + var inputHints = `${submitKey} để gửi`; + if (submitKey === String(SubmitKey.Enter)) { + inputHints += ", Shift + Enter để xuống dòng"; + } + return inputHints + ", / để tìm kiếm mẫu gợi ý"; + }, + Send: "Gửi", + Config: { + Reset: "Khôi phục cài đặt gốc", + SaveAs: "Lưu dưới dạng Mẫu", + }, + }, + Export: { + Title: "Tất cả tin nhắn", + Copy: "Sao chép tất cả", + Download: "Tải xuống", + MessageFromYou: "Tin nhắn của bạn", + MessageFromChatGPT: "Tin nhắn từ ChatGPT", + }, + Memory: { + Title: "Lịch sử tin nhắn", + EmptyContent: "Chưa có tin nhắn", + Send: "Gửi tin nhắn trong quá khứ", + Copy: "Sao chép tin nhắn trong quá khứ", + Reset: "Đặt lại phiên", + ResetConfirm: + "Đặt lại sẽ xóa toàn bộ lịch sử trò chuyện hiện tại và bộ nhớ. Bạn có chắc chắn muốn đặt lại không?", + }, + Home: { + NewChat: "Cuộc trò chuyện mới", + DeleteChat: "Xác nhận xóa các cuộc trò chuyện đã chọn?", + DeleteToast: "Đã xóa cuộc trò chuyện", + Revert: "Khôi phục", + }, + Settings: { + Title: "Cài đặt", + SubTitle: "Tất cả cài đặt", + Actions: { + ClearAll: "Xóa toàn bộ dữ liệu", + ResetAll: "Khôi phục cài đặt gốc", + Close: "Đóng", + ConfirmResetAll: "Bạn chắc chắn muốn thiết lập lại tất cả cài đặt?", + ConfirmClearAll: "Bạn chắc chắn muốn thiết lập lại tất cả dữ liệu?", + }, + Lang: { + Name: "Language", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language` + All: "Tất cả ngôn ngữ", + Options: { + cn: "简体中文", + en: "English", + tw: "繁體中文", + es: "Español", + it: "Italiano", + tr: "Türkçe", + jp: "日本語", + de: "Deutsch", + vi: "Vietnamese", + }, + }, + Avatar: "Ảnh đại diện", + FontSize: { + Title: "Font chữ", + SubTitle: "Thay đổi font chữ của nội dung trò chuyện", + }, + Update: { + Version: (x: string) => `Phiên bản: ${x}`, + IsLatest: "Phiên bản mới nhất", + CheckUpdate: "Kiểm tra bản cập nhật", + IsChecking: "Kiểm tra bản cập nhật...", + FoundUpdate: (x: string) => `Phát hiện phiên bản mới: ${x}`, + GoToUpdate: "Cập nhật", + }, + SendKey: "Phím gửi", + Theme: "Theme", + TightBorder: "Chế độ không viền", + SendPreviewBubble: { + Title: "Gửi bong bóng xem trước", + SubTitle: "Xem trước nội dung markdown bằng bong bóng", + }, + Mask: { + Title: "Mask Splash Screen", + SubTitle: "Chớp màn hình khi bắt đầu cuộc trò chuyện mới", + }, + Prompt: { + Disable: { + Title: "Vô hiệu hóa chức năng tự động hoàn thành", + SubTitle: "Nhập / để kích hoạt chức năng tự động hoàn thành", + }, + List: "Danh sách mẫu gợi ý", + ListCount: (builtin: number, custom: number) => + `${builtin} có sẵn, ${custom} do người dùng xác định`, + Edit: "Chỉnh sửa", + Modal: { + Title: "Danh sách mẫu gợi ý", + Add: "Thêm", + Search: "Tìm kiếm mẫu", + }, + EditModal: { + Title: "Chỉnh sửa mẫu", + }, + }, + HistoryCount: { + Title: "Số lượng tin nhắn đính kèm", + SubTitle: "Số lượng tin nhắn trong quá khứ được gửi kèm theo mỗi yêu cầu", + }, + CompressThreshold: { + Title: "Ngưỡng nén lịch sử tin nhắn", + SubTitle: "Thực hiện nén nếu số lượng tin nhắn chưa nén vượt quá ngưỡng", + }, + Token: { + Title: "API Key", + SubTitle: "Sử dụng khóa của bạn để bỏ qua giới hạn mã truy cập", + Placeholder: "OpenAI API Key", + }, + Usage: { + Title: "Hạn mức tài khoản", + SubTitle(used: any, total: any) { + return `Đã sử dụng $${used} trong tháng này, hạn mức $${total}`; + }, + IsChecking: "Đang kiểm tra...", + Check: "Kiểm tra", + NoAccess: "Nhập API Key để kiểm tra hạn mức", + }, + AccessCode: { + Title: "Mã truy cập", + SubTitle: "Đã bật kiểm soát truy cập", + Placeholder: "Nhập mã truy cập", + }, + Model: "Mô hình", + Temperature: { + Title: "Tính ngẫu nhiên (temperature)", + SubTitle: "Giá trị càng lớn, câu trả lời càng ngẫu nhiên", + }, + MaxTokens: { + Title: "Giới hạn số lượng token (max_tokens)", + SubTitle: "Số lượng token tối đa được sử dụng trong mỗi lần tương tác", + }, + PresencePenlty: { + Title: "Chủ đề mới (presence_penalty)", + SubTitle: "Giá trị càng lớn tăng khả năng mở rộng sang các chủ đề mới", + }, + }, + Store: { + DefaultTopic: "Cuộc trò chuyện mới", + BotHello: "Xin chào! Mình có thể giúp gì cho bạn?", + Error: "Có lỗi xảy ra, vui lòng thử lại sau.", + Prompt: { + History: (content: string) => + "Tóm tắt ngắn gọn cuộc trò chuyện giữa người dùng và AI: " + content, + Topic: + "Sử dụng 4 đến 5 từ tóm tắt cuộc trò chuyện này mà không có phần mở đầu, dấu chấm câu, dấu ngoặc kép, dấu chấm, ký hiệu hoặc văn bản bổ sung nào. Loại bỏ các dấu ngoặc kép kèm theo.", + Summarize: + "Tóm tắt cuộc trò chuyện này một cách ngắn gọn trong 200 từ hoặc ít hơn để sử dụng làm gợi ý cho ngữ cảnh tiếp theo.", + }, + }, + Copy: { + Success: "Sao chép vào bộ nhớ tạm", + Failed: + "Sao chép không thành công, vui lòng cấp quyền truy cập vào bộ nhớ tạm", + }, + Context: { + Toast: (x: any) => `Sử dụng ${x} tin nhắn chứa ngữ cảnh`, + Edit: "Thiết lập ngữ cảnh và bộ nhớ", + Add: "Thêm tin nhắn", + }, + Plugin: { + Name: "Plugin", + }, + Mask: { + Name: "Mẫu", + Page: { + Title: "Mẫu trò chuyện", + SubTitle: (count: number) => `${count} mẫu`, + Search: "Tìm kiếm mẫu", + Create: "Tạo", + }, + Item: { + Info: (count: number) => `${count} tin nhắn`, + Chat: "Chat", + View: "Xem trước", + Edit: "Chỉnh sửa", + Delete: "Xóa", + DeleteConfirm: "Xác nhận xóa?", + }, + EditModal: { + Title: (readonly: boolean) => + `Chỉnh sửa mẫu ${readonly ? "(chỉ xem)" : ""}`, + Download: "Tải xuống", + Clone: "Tạo bản sao", + }, + Config: { + Avatar: "Ảnh đại diện bot", + Name: "Tên bot", + }, + }, + NewChat: { + Return: "Quay lại", + Skip: "Bỏ qua", + Title: "Chọn 1 biểu tượng", + SubTitle: "Bắt đầu trò chuyện ẩn sau lớp mặt nạ", + More: "Tìm thêm", + NotShow: "Không hiển thị lại", + ConfirmNoShow: "Xác nhận tắt? Bạn có thể bật lại trong phần cài đặt.", + }, + + UI: { + Confirm: "Xác nhận", + Cancel: "Hủy", + Close: "Đóng", + Create: "Tạo", + Edit: "Chỉnh sửa", + }, +}; + +export default vi; From 4a82a91f2d3370785cbeeae76c6b0ddded1f1147 Mon Sep 17 00:00:00 2001 From: "ShengYan, Zhang" Date: Mon, 8 May 2023 08:27:10 +0800 Subject: [PATCH 02/21] fix: styles on .user-prompt-buttons --- app/components/settings.module.scss | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app/components/settings.module.scss b/app/components/settings.module.scss index f257a3ca4..863147b81 100644 --- a/app/components/settings.module.scss +++ b/app/components/settings.module.scss @@ -60,13 +60,11 @@ .user-prompt-buttons { display: flex; align-items: center; + column-gap: 2px; .user-prompt-button { - height: 100%; - - &:not(:last-child) { - margin-right: 5px; - } + //height: 100%; + padding: 7px; } } } From 9bcfe6461db9fd8aa7e1dc40b3817629b212e72b Mon Sep 17 00:00:00 2001 From: "ShengYan, Zhang" Date: Mon, 8 May 2023 08:48:47 +0800 Subject: [PATCH 03/21] chore: remove an unused css name --- app/components/settings.module.scss | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/components/settings.module.scss b/app/components/settings.module.scss index 863147b81..1eac17c16 100644 --- a/app/components/settings.module.scss +++ b/app/components/settings.module.scss @@ -69,7 +69,4 @@ } } } - - .user-prompt-actions { - } } From c394b214231508c25843fa37534ced3b9232a2b8 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Mon, 8 May 2023 22:18:19 +0800 Subject: [PATCH 04/21] fix: #1294 fallback while mermaid render fails --- app/components/markdown.tsx | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/app/components/markdown.tsx b/app/components/markdown.tsx index 9e71bb048..cbe730cd3 100644 --- a/app/components/markdown.tsx +++ b/app/components/markdown.tsx @@ -12,15 +12,21 @@ import mermaid from "mermaid"; import LoadingIcon from "../icons/three-dots.svg"; import React from "react"; -export function Mermaid(props: { code: string }) { +export function Mermaid(props: { code: string; onError: () => void }) { const ref = useRef(null); useEffect(() => { if (props.code && ref.current) { - mermaid.run({ - nodes: [ref.current], - }); + mermaid + .run({ + nodes: [ref.current], + }) + .catch((e) => { + props.onError(); + console.error("[Mermaid] ", e.message); + }); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [props.code]); function viewSvgInNewWindow() { @@ -38,7 +44,7 @@ export function Mermaid(props: { code: string }) { return (
viewSvgInNewWindow()} > @@ -60,7 +66,7 @@ export function PreCode(props: { children: any }) { }, [props.children]); if (mermaidCode) { - return ; + return setMermaidCode("")} />; } return ( From 1f2ef1cdb714500b500c1ff207d580c73fe53ba3 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Mon, 8 May 2023 22:21:06 +0800 Subject: [PATCH 05/21] fix: #1307 empty messages --- app/components/markdown.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/markdown.tsx b/app/components/markdown.tsx index cbe730cd3..fb37fdc43 100644 --- a/app/components/markdown.tsx +++ b/app/components/markdown.tsx @@ -153,7 +153,7 @@ export function Markdown( } }; - checkInView(); + setTimeout(() => checkInView(), 1); return (
Date: Mon, 8 May 2023 22:49:51 +0800 Subject: [PATCH 06/21] feat: #1303 improve long text input ux and mobile modal --- app/components/mask.tsx | 95 +++++++++++++++++++------------ app/components/model-config.tsx | 1 - app/components/ui-lib.module.scss | 22 +++---- app/store/chat.ts | 2 +- app/styles/globals.scss | 4 ++ 5 files changed, 76 insertions(+), 48 deletions(-) diff --git a/app/components/mask.tsx b/app/components/mask.tsx index 106418e82..964a3cc35 100644 --- a/app/components/mask.tsx +++ b/app/components/mask.tsx @@ -106,6 +106,59 @@ export function MaskConfig(props: { ); } +function ContextPromptItem(props: { + prompt: Message; + update: (prompt: Message) => void; + remove: () => void; +}) { + const [focusingInput, setFocusingInput] = useState(false); + + return ( +
+ {!focusingInput && ( + + )} + setFocusingInput(true)} + onBlur={() => setFocusingInput(false)} + onInput={(e) => + props.update({ + ...props.prompt, + content: e.currentTarget.value as any, + }) + } + /> + {!focusingInput && ( + } + className={chatStyle["context-delete-button"]} + onClick={() => props.remove()} + bordered + /> + )} +
+ ); +} + export function ContextPrompts(props: { context: Message[]; updateContext: (updater: (context: Message[]) => void) => void; @@ -128,42 +181,12 @@ export function ContextPrompts(props: { <>
{context.map((c, i) => ( -
- - - updateContextPrompt(i, { - ...c, - content: e.currentTarget.value as any, - }) - } - /> - } - className={chatStyle["context-delete-button"]} - onClick={() => removeContextPrompt(i)} - bordered - /> -
+ updateContextPrompt(i, prompt)} + remove={() => removeContextPrompt(i)} + /> ))}
diff --git a/app/components/model-config.tsx b/app/components/model-config.tsx index 32c2f5c0d..fe9319e0f 100644 --- a/app/components/model-config.tsx +++ b/app/components/model-config.tsx @@ -1,4 +1,3 @@ -import styles from "./settings.module.scss"; import { ALL_MODELS, ModalConfigValidator, ModelConfig } from "../store"; import Locale from "../locales"; diff --git a/app/components/ui-lib.module.scss b/app/components/ui-lib.module.scss index 465fc0de4..ce512dab4 100644 --- a/app/components/ui-lib.module.scss +++ b/app/components/ui-lib.module.scss @@ -124,6 +124,18 @@ } } +@media screen and (max-width: 600px) { + .modal-container { + width: 100vw; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + + .modal-content { + max-height: 50vh; + } + } +} + .show { opacity: 1; transition: all ease 0.3s; @@ -191,13 +203,3 @@ resize: none; min-width: 50px; } - -@media only screen and (max-width: 600px) { - .modal-container { - width: 90vw; - - .modal-content { - max-height: 50vh; - } - } -} diff --git a/app/store/chat.ts b/app/store/chat.ts index 5abd81298..0d66580dc 100644 --- a/app/store/chat.ts +++ b/app/store/chat.ts @@ -33,7 +33,7 @@ export function createMessage(override: Partial): Message { }; } -export const ROLES: Message["role"][] = ["system", "user", "assistant"]; +export const ROLES: Message["role"][] = ["user", "system", "assistant"]; export interface ChatStat { tokenCount: number; diff --git a/app/styles/globals.scss b/app/styles/globals.scss index f849516a0..1ae908be5 100644 --- a/app/styles/globals.scss +++ b/app/styles/globals.scss @@ -248,6 +248,10 @@ div.math { display: flex; align-items: center; justify-content: center; + + @media screen and (max-width: 600px) { + align-items: flex-end; + } } .link { From 222301307fd13ec2ed9828cc0dc4f8b2e309c0d6 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Tue, 9 May 2023 00:39:00 +0800 Subject: [PATCH 07/21] feat: close #1301 support message actions --- app/command.ts | 28 ++++++++++++++++++++++++++++ app/components/chat.tsx | 23 ++++++++++++----------- app/components/home.tsx | 1 + app/components/mask.tsx | 4 ++-- app/components/new-chat.tsx | 14 +++++++++++++- app/locales/cn.ts | 4 ++-- app/store/chat.ts | 6 +++--- app/store/config.ts | 2 +- next.config.mjs | 7 ++++++- 9 files changed, 68 insertions(+), 21 deletions(-) create mode 100644 app/command.ts diff --git a/app/command.ts b/app/command.ts new file mode 100644 index 000000000..40bad92b3 --- /dev/null +++ b/app/command.ts @@ -0,0 +1,28 @@ +import { useSearchParams } from "react-router-dom"; + +type Command = (param: string) => void; +interface Commands { + fill?: Command; + submit?: Command; + mask?: Command; +} + +export function useCommand(commands: Commands = {}) { + const [searchParams, setSearchParams] = useSearchParams(); + + if (commands === undefined) return; + + let shouldUpdate = false; + searchParams.forEach((param, name) => { + const commandName = name as keyof Commands; + if (typeof commands[commandName] === "function") { + commands[commandName]!(param); + searchParams.delete(name); + shouldUpdate = true; + } + }); + + if (shouldUpdate) { + setSearchParams(searchParams); + } +} diff --git a/app/components/chat.tsx b/app/components/chat.tsx index ca51a06af..8786877ba 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -26,12 +26,10 @@ import { SubmitKey, useChatStore, BOT_HELLO, - ROLES, createMessage, useAccessStore, Theme, useAppConfig, - ModelConfig, DEFAULT_TOPIC, } from "../store"; @@ -58,11 +56,8 @@ import { useLocation, useNavigate } from "react-router-dom"; import { Path } from "../constant"; import { Avatar } from "./emoji"; import { MaskAvatar, MaskConfig } from "./mask"; -import { - DEFAULT_MASK_AVATAR, - DEFAULT_MASK_ID, - useMaskStore, -} from "../store/mask"; +import { useMaskStore } from "../store/mask"; +import { useCommand } from "../command"; const Markdown = dynamic(async () => (await import("./markdown")).Markdown, { loading: () => , @@ -478,8 +473,7 @@ export function Chat() { } }; - // submit user input - const onUserSubmit = () => { + const doSubmit = (userInput: string) => { if (userInput.trim() === "") return; setIsLoading(true); chatStore.onUserInput(userInput).then(() => setIsLoading(false)); @@ -504,7 +498,7 @@ export function Chat() { return; } if (shouldSubmit(e)) { - onUserSubmit(); + doSubmit(userInput); e.preventDefault(); } }; @@ -618,6 +612,13 @@ export function Chat() { const isChat = location.pathname === Path.Chat; const autoFocus = !isMobileScreen || isChat; // only focus in chat page + useCommand({ + fill: setUserInput, + submit: (text) => { + doSubmit(text); + }, + }); + return (
@@ -816,7 +817,7 @@ export function Chat() { text={Locale.Chat.Send} className={styles["chat-input-send"]} type="primary" - onClick={onUserSubmit} + onClick={() => doSubmit(userInput)} />
diff --git a/app/components/home.tsx b/app/components/home.tsx index a83a77982..4c3d0a646 100644 --- a/app/components/home.tsx +++ b/app/components/home.tsx @@ -23,6 +23,7 @@ import { } from "react-router-dom"; import { SideBar } from "./sidebar"; import { useAppConfig } from "../store/config"; +import { useMaskStore } from "../store/mask"; export function Loading(props: { noLogo?: boolean }) { return ( diff --git a/app/components/mask.tsx b/app/components/mask.tsx index 964a3cc35..9794c9745 100644 --- a/app/components/mask.tsx +++ b/app/components/mask.tsx @@ -20,7 +20,7 @@ import Locale, { AllLangs, Lang } from "../locales"; import { useNavigate } from "react-router-dom"; import chatStyle from "./chat.module.scss"; -import { useEffect, useState } from "react"; +import { useState } from "react"; import { downloadAs, readFromFile } from "../utils"; import { Updater } from "../api/openai/typing"; import { ModelConfigList } from "./model-config"; @@ -197,7 +197,7 @@ export function ContextPrompts(props: { className={chatStyle["context-prompt-button"]} onClick={() => addContextPrompt({ - role: "system", + role: "user", content: "", date: "", }) diff --git a/app/components/new-chat.tsx b/app/components/new-chat.tsx index 42612e0ad..81858fb02 100644 --- a/app/components/new-chat.tsx +++ b/app/components/new-chat.tsx @@ -13,6 +13,7 @@ import { Mask, useMaskStore } from "../store/mask"; import Locale from "../locales"; import { useAppConfig, useChatStore } from "../store"; import { MaskAvatar } from "./mask"; +import { useCommand } from "../command"; function getIntersectionArea(aRect: DOMRect, bRect: DOMRect) { const xmin = Math.max(aRect.x, bRect.x); @@ -108,9 +109,20 @@ export function NewChat() { const startChat = (mask?: Mask) => { chatStore.newSession(mask); - navigate(Path.Chat); + setTimeout(() => navigate(Path.Chat), 1); }; + useCommand({ + mask: (id) => { + try { + const mask = maskStore.get(parseInt(id)); + startChat(mask ?? undefined); + } catch { + console.error("[New Chat] failed to create chat from mask id=", id); + } + }, + }); + return (
diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 8bec7c40e..112b3b5cf 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -4,7 +4,7 @@ const cn = { WIP: "该功能仍在开发中……", Error: { Unauthorized: - "现在是未授权状态,请点击左下角[设置](/#/settings)按钮输入访问密码。", + "访问密码不正确或为空,请前往[设置](/#/settings)页输入正确的访问密码,或者填入你自己的 OpenAI API Key。", }, ChatItem: { ChatItemCount: (count: number) => `${count} 条对话`, @@ -149,7 +149,7 @@ const cn = { }, AccessCode: { Title: "访问密码", - SubTitle: "已开启加密访问", + SubTitle: "管理员已开启加密访问", Placeholder: "请输入访问密码", }, Model: "模型 (model)", diff --git a/app/store/chat.ts b/app/store/chat.ts index 0d66580dc..c938d7879 100644 --- a/app/store/chat.ts +++ b/app/store/chat.ts @@ -7,11 +7,11 @@ import { requestChatStream, requestWithPrompt, } from "../requests"; -import { isMobileScreen, trimTopic } from "../utils"; +import { trimTopic } from "../utils"; import Locale from "../locales"; import { showToast } from "../components/ui-lib"; -import { DEFAULT_CONFIG, ModelConfig, ModelType, useAppConfig } from "./config"; +import { ModelType } from "./config"; import { createEmptyMask, Mask } from "./mask"; import { StoreKey } from "../constant"; @@ -33,7 +33,7 @@ export function createMessage(override: Partial): Message { }; } -export const ROLES: Message["role"][] = ["user", "system", "assistant"]; +export const ROLES: Message["role"][] = ["system", "user", "assistant"]; export interface ChatStat { tokenCount: number; diff --git a/app/store/config.ts b/app/store/config.ts index da77c7b3b..926c296f4 100644 --- a/app/store/config.ts +++ b/app/store/config.ts @@ -31,7 +31,7 @@ export const DEFAULT_CONFIG = { modelConfig: { model: "gpt-3.5-turbo" as ModelType, - temperature: 1, + temperature: 0.5, max_tokens: 2000, presence_penalty: 0, sendMemory: true, diff --git a/next.config.mjs b/next.config.mjs index 3f7c2fb6b..c62f88409 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -5,7 +5,12 @@ const nextConfig = { appDir: true, }, async rewrites() { - const ret = []; + const ret = [ + { + source: "/api/proxy/:path*", + destination: "https://api.openai.com/:path*", + }, + ]; const apiUrl = process.env.API_URL; if (apiUrl) { From a1418fe33c0bd664f5cc88cb4df130026b0e7218 Mon Sep 17 00:00:00 2001 From: Crazyang Date: Tue, 9 May 2023 01:03:51 +0800 Subject: [PATCH 08/21] Update sync.yml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 因为 Vercel 频繁更新会失败,遂建议改为一天同步一次。 --- .github/workflows/sync.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml index 15d324074..a4c14c843 100644 --- a/.github/workflows/sync.yml +++ b/.github/workflows/sync.yml @@ -5,7 +5,7 @@ permissions: on: schedule: - - cron: "0 * * * *" # every hour + - cron: "0 0 * * *" # every day workflow_dispatch: jobs: From 15e046b3ceee5e6a529cf03d6b52de03841cb237 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Tue, 9 May 2023 11:27:57 +0800 Subject: [PATCH 09/21] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 9f54194ab..3fb13a84f 100644 --- a/README.md +++ b/README.md @@ -255,6 +255,9 @@ bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/s [@WingCH](https://github.com/WingCH) [@jtung4](https://github.com/jtung4) [@micozhu](https://github.com/micozhu) +[@jhansion](https://github.com/jhansion) +[@Sha1rholder](https://github.com/Sha1rholder) +[@AnsonHyq](https://github.com/AnsonHyq) ### Contributor From f4caa0029ef496b665e501c13202d2ef286c2801 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Tue, 9 May 2023 16:51:27 +0800 Subject: [PATCH 10/21] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3fb13a84f..7f6695bba 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,7 @@ One-Click to deploy well-designed ChatGPT web UI on Vercel. ## 最新动态 - 🚀 v2.0 已经发布,现在你可以使用面具功能快速创建预制对话了! 了解更多: [ChatGPT 提示词高阶技能:零次、一次和少样本提示](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/138)。 +- 💡 想要更方便地随时随地使用本项目?可以试下这款桌面插件:https://github.com/mushan0x0/AI0x0.com ## Get Started From 9b1f25140e861b72a3b783d52ca7f42e6bd966b2 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Tue, 9 May 2023 22:46:06 +0800 Subject: [PATCH 11/21] fix: #1359 empty line wrap count --- app/utils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/utils.ts b/app/utils.ts index 43ea796e5..1e34a6b3f 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -160,13 +160,13 @@ export function autoGrowTextArea(dom: HTMLTextAreaElement) { measureDom.style.width = width + "px"; measureDom.innerText = dom.value.trim().length > 0 ? dom.value : "1"; - const lineWrapCount = Math.max(0, dom.value.split("\n").length - 1); + const emptyLineWrap = Math.max(0, dom.value.split("\n\n").length - 1); const height = parseFloat(window.getComputedStyle(measureDom).height); const singleLineHeight = parseFloat( window.getComputedStyle(singleLineDom).height, ); - const rows = Math.round(height / singleLineHeight) + lineWrapCount; + const rows = Math.round(height / singleLineHeight) + emptyLineWrap; return rows; } From 2b7f72deec7dec5ccbe5583c10e81af7cf136808 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Tue, 9 May 2023 23:01:17 +0800 Subject: [PATCH 12/21] feat: close #1055 cmd/alt/ctrl + arrow up/down to switch window --- app/components/chat.tsx | 14 ++++---------- app/components/sidebar.tsx | 25 ++++++++++++++++++++++++- app/constant.ts | 2 ++ 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/app/components/chat.tsx b/app/components/chat.tsx index 8786877ba..54def01cf 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -53,7 +53,7 @@ import chatStyle from "./chat.module.scss"; import { ListItem, Modal, showModal } from "./ui-lib"; import { useLocation, useNavigate } from "react-router-dom"; -import { Path } from "../constant"; +import { LAST_INPUT_KEY, Path } from "../constant"; import { Avatar } from "./emoji"; import { MaskAvatar, MaskConfig } from "./mask"; import { useMaskStore } from "../store/mask"; @@ -404,7 +404,6 @@ export function Chat() { const inputRef = useRef(null); const [userInput, setUserInput] = useState(""); - const [beforeInput, setBeforeInput] = useState(""); const [isLoading, setIsLoading] = useState(false); const { submitKey, shouldSubmit } = useSubmitHandler(); const { scrollRef, setAutoScroll, scrollToBottom } = useScrollToBottom(); @@ -477,7 +476,7 @@ export function Chat() { if (userInput.trim() === "") return; setIsLoading(true); chatStore.onUserInput(userInput).then(() => setIsLoading(false)); - setBeforeInput(userInput); + localStorage.setItem(LAST_INPUT_KEY, userInput); setUserInput(""); setPromptHints([]); if (!isMobileScreen) inputRef.current?.focus(); @@ -491,9 +490,9 @@ export function Chat() { // check if should send message const onInputKeyDown = (e: React.KeyboardEvent) => { - // if ArrowUp and no userInput + // if ArrowUp and no userInput, fill with last input if (e.key === "ArrowUp" && userInput.length <= 0) { - setUserInput(beforeInput); + setUserInput(localStorage.getItem(LAST_INPUT_KEY) ?? ""); e.preventDefault(); return; } @@ -503,11 +502,6 @@ export function Chat() { } }; const onRightClick = (e: any, message: Message) => { - // auto fill user input - if (message.role === "user") { - setUserInput(message.content); - } - // copy to clipboard if (selectOrCopy(e.currentTarget, message.content)) { e.preventDefault(); diff --git a/app/components/sidebar.tsx b/app/components/sidebar.tsx index e32739259..63614ffae 100644 --- a/app/components/sidebar.tsx +++ b/app/components/sidebar.tsx @@ -32,6 +32,28 @@ const ChatList = dynamic(async () => (await import("./chat-list")).ChatList, { loading: () => null, }); +function useHotKey() { + const chatStore = useChatStore(); + + useEffect(() => { + const onKeyDown = (e: KeyboardEvent) => { + if (e.metaKey || e.altKey || e.ctrlKey) { + const n = chatStore.sessions.length; + const limit = (x: number) => (x + n) % n; + const i = chatStore.currentSessionIndex; + if (e.key === "ArrowUp") { + chatStore.selectSession(limit(i - 1)); + } else if (e.key === "ArrowDown") { + chatStore.selectSession(limit(i + 1)); + } + } + }; + + window.addEventListener("keydown", onKeyDown); + return () => window.removeEventListener("keydown", onKeyDown); + }); +} + function useDragSideBar() { const limit = (x: number) => Math.min(MAX_SIDEBAR_WIDTH, x); @@ -86,9 +108,10 @@ export function SideBar(props: { className?: string }) { // drag side bar const { onDragMouseDown, shouldNarrow } = useDragSideBar(); const navigate = useNavigate(); - const config = useAppConfig(); + useHotKey(); + return (
Date: Tue, 9 May 2023 23:20:03 +0800 Subject: [PATCH 13/21] feat: close #444 use env var to disable gpt-4 --- README.md | 8 +++++++- README_CN.md | 6 +++++- app/api/config/route.ts | 3 ++- app/config/server.ts | 2 ++ app/store/access.ts | 9 +++++++++ app/store/config.ts | 20 ++++++++++++++++++++ 6 files changed, 45 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7f6695bba..90ed7d42f 100644 --- a/README.md +++ b/README.md @@ -168,7 +168,13 @@ Specify OpenAI organization ID. > Default: Empty -If you do not want users to input their own API key, set this environment variable to 1. +If you do not want users to input their own API key, set this value to 1. + +### `DISABLE_GPT4` (optional) + +> Default: Empty + +If you do not want users to use GPT-4, set this value to 1. ## Development diff --git a/README_CN.md b/README_CN.md index 1da68f655..9601e5fda 100644 --- a/README_CN.md +++ b/README_CN.md @@ -64,7 +64,7 @@ code1,code2,code3 ## 环境变量 -> 本项目大多数配置项都通过环境变量来设置。 +> 本项目大多数配置项都通过环境变量来设置,教程:[如何修改 Vercel 环境变量](./docs/vercel-cn.md)。 ### `OPENAI_API_KEY` (必填项) @@ -94,6 +94,10 @@ OpenAI 接口代理 URL,如果你手动配置了 openai 接口代理,请填 如果你不想让用户自行填入 API Key,将此环境变量设置为 1 即可。 +### `DISABLE_GPT4` (可选) + +如果你不想让用户使用 GPT-4,将此环境变量设置为 1 即可。 + ## 开发 > 强烈不建议在本地进行开发或者部署,由于一些技术原因,很难在本地配置好 OpenAI API 代理,除非你能保证可以直连 OpenAI 服务器。 diff --git a/app/api/config/route.ts b/app/api/config/route.ts index 62c8d60fb..2b3bcbf20 100644 --- a/app/api/config/route.ts +++ b/app/api/config/route.ts @@ -1,4 +1,4 @@ -import { NextRequest, NextResponse } from "next/server"; +import { NextResponse } from "next/server"; import { getServerSideConfig } from "../../config/server"; @@ -9,6 +9,7 @@ const serverConfig = getServerSideConfig(); const DANGER_CONFIG = { needCode: serverConfig.needCode, hideUserApiKey: serverConfig.hideUserApiKey, + enableGPT4: serverConfig.enableGPT4, }; declare global { diff --git a/app/config/server.ts b/app/config/server.ts index c1cf439b7..23fec8682 100644 --- a/app/config/server.ts +++ b/app/config/server.ts @@ -8,6 +8,7 @@ declare global { PROXY_URL?: string; VERCEL?: string; HIDE_USER_API_KEY?: string; // disable user's api key input + DISABLE_GPT4?: string; // allow user to use gpt-4 or not } } } @@ -40,5 +41,6 @@ export const getServerSideConfig = () => { proxyUrl: process.env.PROXY_URL, isVercel: !!process.env.VERCEL, hideUserApiKey: !!process.env.HIDE_USER_API_KEY, + enableGPT4: !process.env.DISABLE_GPT4, }; }; diff --git a/app/store/access.ts b/app/store/access.ts index 51290d0a7..79b7b9900 100644 --- a/app/store/access.ts +++ b/app/store/access.ts @@ -2,6 +2,7 @@ import { create } from "zustand"; import { persist } from "zustand/middleware"; import { StoreKey } from "../constant"; import { BOT_HELLO } from "./chat"; +import { ALL_MODELS } from "./config"; export interface AccessControlStore { accessCode: string; @@ -60,6 +61,14 @@ export const useAccessStore = create()( console.log("[Config] got config from server", res); set(() => ({ ...res })); + if (!res.enableGPT4) { + ALL_MODELS.forEach((model) => { + if (model.name.startsWith("gpt-4")) { + (model as any).available = false; + } + }); + } + if ((res as any).botHello) { BOT_HELLO.content = (res as any).botHello; } diff --git a/app/store/config.ts b/app/store/config.ts index 926c296f4..1e960456f 100644 --- a/app/store/config.ts +++ b/app/store/config.ts @@ -76,6 +76,26 @@ export const ALL_MODELS = [ name: "gpt-3.5-turbo-0301", available: true, }, + { + name: "qwen-v1", // 通义千问 + available: false, + }, + { + name: "ernie", // 文心一言 + available: false, + }, + { + name: "spark", // 讯飞星火 + available: false, + }, + { + name: "llama", // llama + available: false, + }, + { + name: "chatglm", // chatglm-6b + available: false, + }, ] as const; export type ModelType = (typeof ALL_MODELS)[number]["name"]; From 6d9abf11b8a3c92a946c55af660332b06cbba822 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Tue, 9 May 2023 23:36:30 +0800 Subject: [PATCH 14/21] fix: #1363 session index after deleting --- app/components/mask.tsx | 11 ++++++++++- app/masks/en.ts | 2 +- app/store/chat.ts | 3 ++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/app/components/mask.tsx b/app/components/mask.tsx index 9794c9745..b42e7efb9 100644 --- a/app/components/mask.tsx +++ b/app/components/mask.tsx @@ -11,10 +11,11 @@ import CloseIcon from "../icons/close.svg"; import DeleteIcon from "../icons/delete.svg"; import EyeIcon from "../icons/eye.svg"; import CopyIcon from "../icons/copy.svg"; +import ShareIcon from "../icons/share.svg"; import { DEFAULT_MASK_AVATAR, Mask, useMaskStore } from "../store/mask"; import { Message, ModelConfig, ROLES, useChatStore } from "../store"; -import { Input, List, ListItem, Modal, Popover, showToast } from "./ui-lib"; +import { Input, List, ListItem, Modal, Popover } from "./ui-lib"; import { Avatar, AvatarPicker } from "./emoji"; import Locale, { AllLangs, Lang } from "../locales"; import { useNavigate } from "react-router-dom"; @@ -358,6 +359,14 @@ export function MaskPage() {
+ } + text={Locale.Mask.Item.Chat} + onClick={() => { + chatStore.newSession(m); + navigate(Path.Chat); + }} + /> } text={Locale.Mask.Item.Chat} diff --git a/app/masks/en.ts b/app/masks/en.ts index 93e9bd6aa..af4f215c4 100644 --- a/app/masks/en.ts +++ b/app/masks/en.ts @@ -31,7 +31,7 @@ export const EN_MASKS: BuiltinMask[] = [ ], modelConfig: { model: "gpt-4", - temperature: 1, + temperature: 0.5, max_tokens: 2000, presence_penalty: 0, sendMemory: true, diff --git a/app/store/chat.ts b/app/store/chat.ts index c938d7879..c18aa2bc6 100644 --- a/app/store/chat.ts +++ b/app/store/chat.ts @@ -180,8 +180,9 @@ export const useChatStore = create()( const sessions = get().sessions.slice(); sessions.splice(index, 1); + const currentIndex = get().currentSessionIndex; let nextIndex = Math.min( - get().currentSessionIndex, + currentIndex - Number(index < currentIndex), sessions.length - 1, ); From e4630e6a0f5f8aeb8d2ba325bb3e2a670e4081fa Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Tue, 9 May 2023 23:38:34 +0800 Subject: [PATCH 15/21] fixup --- app/components/mask.tsx | 9 --------- 1 file changed, 9 deletions(-) diff --git a/app/components/mask.tsx b/app/components/mask.tsx index b42e7efb9..a37cfba71 100644 --- a/app/components/mask.tsx +++ b/app/components/mask.tsx @@ -11,7 +11,6 @@ import CloseIcon from "../icons/close.svg"; import DeleteIcon from "../icons/delete.svg"; import EyeIcon from "../icons/eye.svg"; import CopyIcon from "../icons/copy.svg"; -import ShareIcon from "../icons/share.svg"; import { DEFAULT_MASK_AVATAR, Mask, useMaskStore } from "../store/mask"; import { Message, ModelConfig, ROLES, useChatStore } from "../store"; @@ -359,14 +358,6 @@ export function MaskPage() {
- } - text={Locale.Mask.Item.Chat} - onClick={() => { - chatStore.newSession(m); - navigate(Path.Chat); - }} - /> } text={Locale.Mask.Item.Chat} From cb55ce084ce86cc71db12aa4e4e1f82f696e27af Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Wed, 10 May 2023 00:51:43 +0800 Subject: [PATCH 16/21] feat: add model and time info to prompts --- app/store/chat.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/app/store/chat.ts b/app/store/chat.ts index c18aa2bc6..cb11087d4 100644 --- a/app/store/chat.ts +++ b/app/store/chat.ts @@ -252,9 +252,20 @@ export const useChatStore = create()( model: modelConfig.model, }); + const systemInfo = createMessage({ + role: "system", + content: `IMPRTANT: You are a virtual assistant powered by the ${ + modelConfig.model + } model, now time is ${new Date().toLocaleString()}}`, + id: botMessage.id! + 1, + }); + // get recent messages + const systemMessages = [systemInfo]; const recentMessages = get().getMessagesWithMemory(); - const sendMessages = recentMessages.concat(userMessage); + const sendMessages = systemMessages.concat( + recentMessages.concat(userMessage), + ); const sessionIndex = get().currentSessionIndex; const messageIndex = get().currentSession().messages.length + 1; From 26889141259c3cf735ad017ec07e7f77096fbce0 Mon Sep 17 00:00:00 2001 From: Darth Pika <103236915+darth-pika-hu@users.noreply.github.com> Date: Tue, 9 May 2023 16:47:41 -0700 Subject: [PATCH 17/21] Create cloudflare-pages-cn.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加Cloudflare Pages的部署说明。这里临时说明,Next-on-Pages有一个bug将很快修复,修复后,Build命令会修改。 --- docs/cloudflare-pages-cn.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 docs/cloudflare-pages-cn.md diff --git a/docs/cloudflare-pages-cn.md b/docs/cloudflare-pages-cn.md new file mode 100644 index 000000000..faaeeedaa --- /dev/null +++ b/docs/cloudflare-pages-cn.md @@ -0,0 +1,31 @@ +# Cloudflare Pages 的部署说明 + +## 如何新建项目 +从Github fork 本项目,然后登录dash.cloudflare.com,进入pages。 + + +1. 点击 Create a project; +2. 选择 Connect to Git; +3. 关联Cloudlfare Pages和你的GitHub账号; +4. 选中你fork的此项目; +5. 点击 Begin setup; +6. Project name, Production branch,默认即可,有需要按实际改动; +7. Build Settings下的Framework prsets要选Next.js; +8. Build command 暂时不要用默认的命令,因为有node:buffer的bug,写我给的这个: +``` +npx https://prerelease-registry.devprod.cloudflare.dev/next-on-pages/runs/4930842298/npm-package-next-on-pages-230 --experimental-minify +``` +9. Build output directory 用系统默认,不要改动; +10. Root Directory 不要改动; +11. Environment variables ,点击>,然后Add variable,按如下填写: + NODE_VERSION=20.1 + NEXT_TELEMETRY_DISABLE=1 + OPENAI_API_KEY=你自己的API Key + YARN_VERSION=1.22.19 + PHP_VERSION=7.4 + 下面的根据实际需要选填: + CODE= 可选填,访问密码,可以使用逗号隔开多个密码。 + OPENAI_ORG_ID= 可选填,指定 OpenAI 中的组织 ID + HIDE_USER_API_KEY=1 可选,不让用户自行填入 API Key + DISABLE_GPT4=1 可选,不让用户使用 GPT-4 +12. Save and Deploy From 652d803739277f40c79bdacc05388faea2ac57bf Mon Sep 17 00:00:00 2001 From: Darth Pika <103236915+darth-pika-hu@users.noreply.github.com> Date: Tue, 9 May 2023 17:25:00 -0700 Subject: [PATCH 18/21] Update cloudflare-pages-cn.md --- docs/cloudflare-pages-cn.md | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/docs/cloudflare-pages-cn.md b/docs/cloudflare-pages-cn.md index faaeeedaa..de663cc8b 100644 --- a/docs/cloudflare-pages-cn.md +++ b/docs/cloudflare-pages-cn.md @@ -18,14 +18,20 @@ npx https://prerelease-registry.devprod.cloudflare.dev/next-on-pages/runs/493084 9. Build output directory 用系统默认,不要改动; 10. Root Directory 不要改动; 11. Environment variables ,点击>,然后Add variable,按如下填写: - NODE_VERSION=20.1 - NEXT_TELEMETRY_DISABLE=1 - OPENAI_API_KEY=你自己的API Key - YARN_VERSION=1.22.19 - PHP_VERSION=7.4 + -NODE_VERSION=20.1 + -NEXT_TELEMETRY_DISABLE=1 + -OPENAI_API_KEY=你自己的API Key + -YARN_VERSION=1.22.19 + -PHP_VERSION=7.4 下面的根据实际需要选填: - CODE= 可选填,访问密码,可以使用逗号隔开多个密码。 - OPENAI_ORG_ID= 可选填,指定 OpenAI 中的组织 ID - HIDE_USER_API_KEY=1 可选,不让用户自行填入 API Key - DISABLE_GPT4=1 可选,不让用户使用 GPT-4 -12. Save and Deploy + -CODE= 可选填,访问密码,可以使用逗号隔开多个密码 + -OPENAI_ORG_ID= 可选填,指定 OpenAI 中的组织 ID + -HIDE_USER_API_KEY=1 可选,不让用户自行填入 API Key + -DISABLE_GPT4=1 可选,不让用户使用 GPT-4 +12. Save and Deploy; +13. 点Cancel deployment,因为要填 Compatibility flags; +14. 去Build settings, Functions, 找到Compatibility flags; +15. Configure Production compatibility flag 填 nodejs_compat; +16. Configure Preview compatibility flag 填 nodejs_compat; +17. 去 Deployments,点Retry depolyment. +18. Enjoy. From be58d3afb644a3a0202f1d11213a5e37295cfa9f Mon Sep 17 00:00:00 2001 From: Darth Pika <103236915+darth-pika-hu@users.noreply.github.com> Date: Tue, 9 May 2023 17:31:19 -0700 Subject: [PATCH 19/21] Update cloudflare-pages-cn.md --- docs/cloudflare-pages-cn.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/docs/cloudflare-pages-cn.md b/docs/cloudflare-pages-cn.md index de663cc8b..ff4a3d600 100644 --- a/docs/cloudflare-pages-cn.md +++ b/docs/cloudflare-pages-cn.md @@ -18,16 +18,19 @@ npx https://prerelease-registry.devprod.cloudflare.dev/next-on-pages/runs/493084 9. Build output directory 用系统默认,不要改动; 10. Root Directory 不要改动; 11. Environment variables ,点击>,然后Add variable,按如下填写: - -NODE_VERSION=20.1 - -NEXT_TELEMETRY_DISABLE=1 - -OPENAI_API_KEY=你自己的API Key - -YARN_VERSION=1.22.19 - -PHP_VERSION=7.4 + + - NODE_VERSION=20.1 + - NEXT_TELEMETRY_DISABLE=1 + - OPENAI_API_KEY=你自己的API Key + - YARN_VERSION=1.22.19 + - PHP_VERSION=7.4 + 下面的根据实际需要选填: - -CODE= 可选填,访问密码,可以使用逗号隔开多个密码 - -OPENAI_ORG_ID= 可选填,指定 OpenAI 中的组织 ID - -HIDE_USER_API_KEY=1 可选,不让用户自行填入 API Key - -DISABLE_GPT4=1 可选,不让用户使用 GPT-4 + + - CODE= 可选填,访问密码,可以使用逗号隔开多个密码 + - OPENAI_ORG_ID= 可选填,指定 OpenAI 中的组织 ID + - HIDE_USER_API_KEY=1 可选,不让用户自行填入 API Key + - DISABLE_GPT4=1 可选,不让用户使用 GPT-4 12. Save and Deploy; 13. 点Cancel deployment,因为要填 Compatibility flags; 14. 去Build settings, Functions, 找到Compatibility flags; From 89551774db3b42166648ac1645b7391a4b2e4668 Mon Sep 17 00:00:00 2001 From: Darth Pika <103236915+darth-pika-hu@users.noreply.github.com> Date: Tue, 9 May 2023 17:45:01 -0700 Subject: [PATCH 20/21] Re-format the Chinese version and added an English version Finally found a computer. --- docs/cloudflare-pages-cn.md | 67 ++++++++++++++++++------------------- docs/cloudflare-pages-en.md | 38 +++++++++++++++++++++ 2 files changed, 71 insertions(+), 34 deletions(-) create mode 100644 docs/cloudflare-pages-en.md diff --git a/docs/cloudflare-pages-cn.md b/docs/cloudflare-pages-cn.md index ff4a3d600..2f9a99f2d 100644 --- a/docs/cloudflare-pages-cn.md +++ b/docs/cloudflare-pages-cn.md @@ -1,40 +1,39 @@ -# Cloudflare Pages 的部署说明 +# Cloudflare Pages 部署指南 ## 如何新建项目 -从Github fork 本项目,然后登录dash.cloudflare.com,进入pages。 +在 Github 上 fork 本项目,然后登录到 dash.cloudflare.com 并进入 Pages。 +1. 点击 "Create a project"。 +2. 选择 "Connect to Git"。 +3. 关联 Cloudflare Pages 和你的 GitHub 账号。 +4. 选中你 fork 的此项目。 +5. 点击 "Begin setup"。 +6. 对于 "Project name" 和 "Production branch",可以使用默认值,也可以根据需要进行更改。 +7. 在 "Build Settings" 中,选择 "Framework presets" 选项并选择 "Next.js"。 +8. 由于 node:buffer 的 bug,暂时不要使用默认的 "Build command"。请使用以下命令: + ``` + npx https://prerelease-registry.devprod.cloudflare.dev/next-on-pages/runs/4930842298/npm-package-next-on-pages-230 --experimental-minify + ``` +9. 对于 "Build output directory",使用默认值并且不要修改。 +10. 不要修改 "Root Directory"。 +11. 对于 "Environment variables",点击 ">" 然后点击 "Add variable"。按照以下信息填写: -1. 点击 Create a project; -2. 选择 Connect to Git; -3. 关联Cloudlfare Pages和你的GitHub账号; -4. 选中你fork的此项目; -5. 点击 Begin setup; -6. Project name, Production branch,默认即可,有需要按实际改动; -7. Build Settings下的Framework prsets要选Next.js; -8. Build command 暂时不要用默认的命令,因为有node:buffer的bug,写我给的这个: -``` -npx https://prerelease-registry.devprod.cloudflare.dev/next-on-pages/runs/4930842298/npm-package-next-on-pages-230 --experimental-minify -``` -9. Build output directory 用系统默认,不要改动; -10. Root Directory 不要改动; -11. Environment variables ,点击>,然后Add variable,按如下填写: + - `NODE_VERSION=20.1` + - `NEXT_TELEMETRY_DISABLE=1` + - `OPENAI_API_KEY=你自己的API Key` + - `YARN_VERSION=1.22.19` + - `PHP_VERSION=7.4` - - NODE_VERSION=20.1 - - NEXT_TELEMETRY_DISABLE=1 - - OPENAI_API_KEY=你自己的API Key - - YARN_VERSION=1.22.19 - - PHP_VERSION=7.4 + 根据实际需要,可以选择填写以下选项: - 下面的根据实际需要选填: - - - CODE= 可选填,访问密码,可以使用逗号隔开多个密码 - - OPENAI_ORG_ID= 可选填,指定 OpenAI 中的组织 ID - - HIDE_USER_API_KEY=1 可选,不让用户自行填入 API Key - - DISABLE_GPT4=1 可选,不让用户使用 GPT-4 -12. Save and Deploy; -13. 点Cancel deployment,因为要填 Compatibility flags; -14. 去Build settings, Functions, 找到Compatibility flags; -15. Configure Production compatibility flag 填 nodejs_compat; -16. Configure Preview compatibility flag 填 nodejs_compat; -17. 去 Deployments,点Retry depolyment. -18. Enjoy. + - `CODE= 可选填,访问密码,可以使用逗号隔开多个密码` + - `OPENAI_ORG_ID= 可选填,指定 OpenAI 中的组织 ID` + - `HIDE_USER_API_KEY=1 可选,不让用户自行填入 API Key` + - `DISABLE_GPT4=1 可选,不让用户使用 GPT-4` + +12. 点击 "Save and Deploy"。 +13. 点击 "Cancel deployment",因为需要填写 Compatibility flags。 +14. 前往 "Build settings"、"Functions",找到 "Compatibility flags"。 +15. 在 "Configure Production compatibility flag" 和 "Configure Preview compatibility flag" 中填写 "nodejs_compat"。 +16. 前往 "Deployments",点击 "Retry deployment"。 +17. Enjoy. \ No newline at end of file diff --git a/docs/cloudflare-pages-en.md b/docs/cloudflare-pages-en.md new file mode 100644 index 000000000..ee8ff6a6b --- /dev/null +++ b/docs/cloudflare-pages-en.md @@ -0,0 +1,38 @@ +# Cloudflare Pages Deployment Guide + +## How to create a new project +Fork this project on GitHub, then log in to dash.cloudflare.com and go to Pages. + +1. Click "Create a project". +2. Choose "Connect to Git". +3. Connect Cloudflare Pages to your GitHub account. +4. Select the forked project. +5. Click "Begin setup". +6. For "Project name" and "Production branch", use the default values or change them as needed. +7. In "Build Settings", choose the "Framework presets" option and select "Next.js". +8. Do not use the default "Build command" due to a node:buffer bug. Instead, use the following command: + ``` + npx https://prerelease-registry.devprod.cloudflare.dev/next-on-pages/runs/4930842298/npm-package-next-on-pages-230 --experimental-minify + ``` +9. For "Build output directory", use the default value and do not modify it. +10. Do not modify "Root Directory". +11. For "Environment variables", click ">" and then "Add variable". Fill in the following information: + - `NODE_VERSION=20.1` + - `NEXT_TELEMETRY_DISABLE=1` + - `OPENAI_API_KEY=your_own_API_key` + - `YARN_VERSION=1.22.19` + - `PHP_VERSION=7.4` + + Optionally fill in the following based on your needs: + + - `CODE= Optional, access passwords, multiple passwords can be separated by commas` + - `OPENAI_ORG_ID= Optional, specify the organization ID in OpenAI` + - `HIDE_USER_API_KEY=1 Optional, do not allow users to enter their own API key` + - `DISABLE_GPT4=1 Optional, do not allow users to use GPT-4` + +12. Click "Save and Deploy". +13. Click "Cancel deployment" because you need to fill in Compatibility flags. +14. Go to "Build settings", "Functions", and find "Compatibility flags". +15. Fill in "nodejs_compat" for both "Configure Production compatibility flag" and "Configure Preview compatibility flag". +16. Go to "Deployments" and click "Retry deployment". +17. Enjoy. \ No newline at end of file From a80dcaa1c37db621bac15dad198e3054cbae6af1 Mon Sep 17 00:00:00 2001 From: "ShengYan, Zhang" Date: Wed, 10 May 2023 09:27:33 +0800 Subject: [PATCH 21/21] fix: row count logic --- app/utils.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/utils.ts b/app/utils.ts index 1e34a6b3f..a272d5684 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -158,15 +158,15 @@ export function autoGrowTextArea(dom: HTMLTextAreaElement) { const width = getDomContentWidth(dom); measureDom.style.width = width + "px"; - measureDom.innerText = dom.value.trim().length > 0 ? dom.value : "1"; - - const emptyLineWrap = Math.max(0, dom.value.split("\n\n").length - 1); + measureDom.innerText = dom.value !== "" ? dom.value : "1"; + const endWithEmptyLine = dom.value.endsWith("\n"); const height = parseFloat(window.getComputedStyle(measureDom).height); const singleLineHeight = parseFloat( window.getComputedStyle(singleLineDom).height, ); - const rows = Math.round(height / singleLineHeight) + emptyLineWrap; + const rows = + Math.round(height / singleLineHeight) + (endWithEmptyLine ? 1 : 0); return rows; }