From a624f83821fd228cf7144004bf8f0ecf62f031f1 Mon Sep 17 00:00:00 2001 From: "[witbox2018]" <103164430@qq.com> Date: Mon, 10 Apr 2023 09:55:33 +0800 Subject: [PATCH] 22 --- .github/workflows/sync.yml | 2 +- Dockerfile | 24 ++++++++++- README.md | 33 +++++++++++++-- README_CN.md | 29 +++++++++++-- app/api/openai/route.ts | 2 +- app/components/button.module.scss | 3 ++ app/components/chat-list.tsx | 2 +- app/components/home.module.scss | 33 +++++++++++++-- app/components/home.tsx | 64 ++++++++++++++++++++++++++++- app/components/markdown.tsx | 2 +- app/components/settings.module.scss | 7 +++- app/components/ui-lib.module.scss | 3 ++ app/icons/max.svg | 41 ++++++++++++++++++ app/icons/min.svg | 45 ++++++++++++++++++++ app/icons/share.svg | 17 ++++++++ app/requests.ts | 14 ++++--- app/store/app.ts | 38 ++++++++++------- app/utils.ts | 54 ++++++++++++++++++++++++ scripts/fetch-prompts.mjs | 15 ++++--- tsconfig.json | 2 +- vercel.json | 5 +++ 21 files changed, 386 insertions(+), 49 deletions(-) create mode 100644 app/icons/max.svg create mode 100644 app/icons/min.svg create mode 100644 app/icons/share.svg create mode 100644 vercel.json diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml index 38c272e85..52feae50e 100644 --- a/.github/workflows/sync.yml +++ b/.github/workflows/sync.yml @@ -5,7 +5,7 @@ permissions: on: schedule: - - cron: "0 */6 * * *" # every 6 hours + - cron: "0 * * * *" # every hour workflow_dispatch: jobs: diff --git a/Dockerfile b/Dockerfile index 2afcd7945..151265b5e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,6 +8,7 @@ WORKDIR /app COPY package.json yarn.lock ./ +RUN yarn config set registry 'https://registry.npm.taobao.org' RUN yarn install FROM base AS builder @@ -27,7 +28,10 @@ RUN yarn build FROM base AS runner WORKDIR /app -ENV OPENAI_API_KEY="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" +RUN apk add proxychains-ng + +ENV PROXY_URL="" +ENV OPENAI_API_KEY="" ENV CODE="" COPY --from=builder /app/public ./public @@ -37,4 +41,20 @@ COPY --from=builder /app/.next/server ./.next/server EXPOSE 3000 -CMD ["node","server.js"] +CMD if [ -n "$PROXY_URL" ]; then \ + protocol=$(echo $PROXY_URL | cut -d: -f1); \ + host=$(echo $PROXY_URL | cut -d/ -f3 | cut -d: -f1); \ + port=$(echo $PROXY_URL | cut -d: -f3); \ + conf=/etc/proxychains.conf; \ + echo "strict_chain" >> $conf; \ + echo "proxy_dns" >> $conf; \ + echo "remote_dns_subnet 224" >> $conf; \ + echo "tcp_read_time_out 15000" >> $conf; \ + echo "tcp_connect_time_out 8000" >> $conf; \ + echo "[ProxyList]" >> $conf; \ + echo "$protocol $host $port" >> $conf; \ + cat /etc/proxychains.conf; \ + proxychains -f $conf node server.js; \ + else \ + node server.js; \ + fi diff --git a/README.md b/README.md index 55a6da404..ba4a6dc49 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,8 @@ One-Click to deploy well-designed ChatGPT web UI on Vercel. - 一键导出聊天记录,完整的 Markdown 支持 - 拥有自己的域名?好上加好,绑定后即可在任何地方**无障碍**快速访问 -## 开发计划 +## 开发计划 + - [x] 为每个对话设置系统 Prompt [#138](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/138) - [ ] 允许用户自行编辑内置 Prompt 列表 - [ ] 使用 tauri 打包桌面应用 @@ -58,11 +59,12 @@ One-Click to deploy well-designed ChatGPT web UI on Vercel. - [ ] 插件机制,支持联网搜索、计算器、调用其他平台 api [#165](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/165) ### 不会开发的功能 + - 界面文字自定义 - 用户登录、账号管理、消息云同步 - ## Get Started + > [简体中文 > 如何开始使用](./README_CN.md#开始使用) 1. Get [OpenAI API Key](https://platform.openai.com/account/api-keys); @@ -71,9 +73,11 @@ One-Click to deploy well-designed ChatGPT web UI on Vercel. 3. Enjoy :) ## FAQ + [简体中文 > 常见问题](./docs/faq-cn.md) | [English > FAQ](./docs/faq.en.md) ## Keep Updated + > [简体中文 > 如何保持代码更新](./README_CN.md#保持更新) If you have deployed your own project with just one click following the steps above, you may encounter the issue of "Updates Available" constantly showing up. This is because Vercel will create a new project for you by default instead of forking this project, resulting in the inability to detect updates correctly. @@ -87,11 +91,12 @@ We recommend that you follow the steps below to re-deploy: This project will be continuously updated, and after forking the project, the upstream code will be automatically synchronized every day without additional operations. -If you want to update instantly, you can check out the [Github documentation](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork) to learn how to synchronize a forked project with upstream code. +If you want to update instantly, you can check out the [Github documentation](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork) to learn how to synchronize a forked project with upstream code. You can star or watch this project or follow author to get release notifictions in time. ## Access Password + > [简体中文 > 如何增加访问密码](./README_CN.md#配置页面访问密码) This project provides limited access control. Please add an environment variable named `CODE` on the vercel environment variables page. The value should be passwords separated by comma like this: @@ -103,6 +108,7 @@ code1,code2,code3 After adding or modifying this environment variable, please redeploy the project for the changes to take effect. ## Environment Variables + > [简体中文 > 如何配置 api key、访问密码、接口代理](./README_CN.md#环境变量) ### `OPENAI_API_KEY` (required) @@ -128,6 +134,7 @@ Override openai api request base url. Override openai api request protocol. ## Development + > [简体中文 > 如何进行二次开发](./README_CN.md#开发) [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) @@ -149,13 +156,28 @@ yarn dev ``` ## Deployment + > [简体中文 > 如何部署到私人服务器](./README_CN.md#部署) + ### Docker (Recommended) ```shell docker pull yidadaa/chatgpt-next-web -docker run -d -p 3000:3000 -e OPENAI_API_KEY="" -e CODE="" yidadaa/chatgpt-next-web +docker run -d -p 3000:3000 \ + -e OPENAI_API_KEY="sk-xxxx" \ + -e CODE="your-password" \ + yidadaa/chatgpt-next-web +``` + +You can start service behind a proxy: + +```shell +docker run -d -p 3000:3000 \ + -e OPENAI_API_KEY="sk-xxxx" \ + -e CODE="your-password" \ + -e PROXY_URL="http://localhost:7890" \ + yidadaa/chatgpt-next-web ``` ### Shell @@ -163,6 +185,7 @@ docker run -d -p 3000:3000 -e OPENAI_API_KEY="" -e CODE="" yidadaa/chatgpt-next- ```shell bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/scripts/setup.sh) ``` + ## Screenshots ![Settings](./docs/images/settings.png) @@ -170,9 +193,11 @@ bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/s ![More](./docs/images/more.png) ## Donation + [Buy Me a Coffee](https://www.buymeacoffee.com/yidadaa) ## Special Thanks + ### Sponsor [@mushan0x0](https://github.com/mushan0x0) diff --git a/README_CN.md b/README_CN.md index cba9df9c5..0833ae1ac 100644 --- a/README_CN.md +++ b/README_CN.md @@ -11,7 +11,7 @@ [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) -![主界面](./static/cover.png) +![主界面](./docs/images/cover.png) @@ -29,7 +29,7 @@ 1. 准备好你的 [OpenAI API Key](https://platform.openai.com/account/api-keys); 2. 点击右侧按钮开始部署: - [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web),直接使用 Github 账号登录即可,记得在环境变量页填入 API Key; + [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web),直接使用 Github 账号登录即可,记得在环境变量页填入 API Key 和[页面访问密码](#配置页面访问密码) CODE; 3. 部署完毕后,即可开始使用; 4. (可选)[绑定自定义域名](https://vercel.com/docs/concepts/projects/domains/add-a-domain):Vercel 分配的域名 DNS 在某些区域被污染了,绑定自定义域名即可直连。 @@ -53,6 +53,8 @@ > 配置密码后,用户需要在设置页手动填写访问码才可以正常聊天,否则会通过消息提示未授权状态。 +> **警告**:请务必将密码的位数设置得足够长,最好 7 位以上,否则[会被爆破](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/518)。 + 本项目提供有限的权限控制功能,请在 Vercel 项目控制面板的环境变量页增加名为 `CODE` 的环境变量,值为用英文逗号分隔的自定义密码: ``` @@ -62,6 +64,7 @@ code1,code2,code3 增加或修改该环境变量后,请**重新部署**项目使改动生效。 ## 环境变量 + > 本项目大多数配置项都通过环境变量来设置。 ### `OPENAI_API_KEY` (必填项) @@ -108,25 +111,42 @@ OPENAI_API_KEY= 2. 执行 `yarn install && yarn dev` 即可。 ## 部署 + ### 容器部署 (推荐) + > 注意:docker 版本在大多数时间都会落后最新的版本 1 到 2 天,所以部署后会持续出现“存在更新”的提示,属于正常现象。 ```shell docker pull yidadaa/chatgpt-next-web -docker run -d -p 3000:3000 -e OPENAI_API_KEY="" -e CODE="" yidadaa/chatgpt-next-web +docker run -d -p 3000:3000 \ + -e OPENAI_API_KEY="sk-xxxx" \ + -e CODE="页面访问密码" \ + yidadaa/chatgpt-next-web +``` + +你也可以指定 proxy: + +```shell +docker run -d -p 3000:3000 \ + -e OPENAI_API_KEY="sk-xxxx" \ + -e CODE="页面访问密码" \ + -e PROXY_URL="http://localhost:7890" \ + yidadaa/chatgpt-next-web ``` ### 本地部署 + 在控制台运行下方命令: ```shell bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/scripts/setup.sh) ``` - ## 鸣谢 + ### 捐赠者 + > 仅列出了部分大额打赏,小额打赏(< 100RMB)人数太多,在此不再列出,敬请谅解。 [@mushan0x0](https://github.com/mushan0x0) @@ -139,6 +159,7 @@ bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/s [见项目贡献者列表](https://github.com/Yidadaa/ChatGPT-Next-Web/graphs/contributors) ## 开源协议 + > 反对 996,从我开始。 [Anti 996 License](https://github.com/kattgu7/Anti-996-License/blob/master/LICENSE_CN_EN) diff --git a/app/api/openai/route.ts b/app/api/openai/route.ts index 3477fc270..261c20a85 100644 --- a/app/api/openai/route.ts +++ b/app/api/openai/route.ts @@ -17,7 +17,7 @@ async function makeRequest(req: NextRequest) { }, { status: 500, - } + }, ); } } diff --git a/app/components/button.module.scss b/app/components/button.module.scss index 1594c366c..133017d97 100644 --- a/app/components/button.module.scss +++ b/app/components/button.module.scss @@ -50,4 +50,7 @@ .icon-button-text { margin-left: 5px; font-size: 12px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } diff --git a/app/components/chat-list.tsx b/app/components/chat-list.tsx index ab5d849f1..cab8812c3 100644 --- a/app/components/chat-list.tsx +++ b/app/components/chat-list.tsx @@ -96,7 +96,7 @@ export function ChatList() { index={i} selected={i === selectedIndex} onClick={() => selectSession(i)} - onDelete={chatStore.deleteSession} + onDelete={() => chatStore.deleteSession(i)} /> ))} {provided.placeholder} diff --git a/app/components/home.module.scss b/app/components/home.module.scss index 396a439a0..282461579 100644 --- a/app/components/home.module.scss +++ b/app/components/home.module.scss @@ -20,7 +20,7 @@ background-color: var(--white); min-width: 600px; min-height: 480px; - max-width: 900px; + max-width: 1200px; display: flex; overflow: hidden; @@ -61,6 +61,27 @@ display: flex; flex-direction: column; box-shadow: inset -2px 0px 2px 0px rgb(0, 0, 0, 0.05); + position: relative; + transition: width ease 0.1s; +} + +.sidebar-drag { + $width: 10px; + + position: absolute; + top: 0; + right: 0; + height: 100%; + width: $width; + background-color: var(--black); + cursor: ew-resize; + opacity: 0; + transition: all ease 0.3s; + + &:hover, + &:active { + opacity: 0.2; + } } .window-content { @@ -228,9 +249,12 @@ margin-top: 14px; } -.chat-item-count {} - -.chat-item-date {} +.chat-item-count, +.chat-item-date { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} .sidebar-tail { display: flex; @@ -552,6 +576,7 @@ .export-content { white-space: break-spaces; + padding: 10px !important; } .loading-content { diff --git a/app/components/home.tsx b/app/components/home.tsx index 65f017fd8..76cce1ef3 100644 --- a/app/components/home.tsx +++ b/app/components/home.tsx @@ -3,7 +3,13 @@ require("../polyfill"); -import { useState, useEffect } from "react"; +import { + useState, + useEffect, + useRef, + useCallback, + MouseEventHandler, +} from "react"; import { IconButton } from "./button"; import styles from "./home.module.scss"; @@ -26,6 +32,7 @@ import { Chat } from "./chat"; import dynamic from "next/dynamic"; import { REPO_URL } from "../constant"; import { ErrorBoundary } from "./error"; +import { useDebounce } from "use-debounce"; export function Loading(props: { noLogo?: boolean }) { return ( @@ -78,6 +85,53 @@ function useSwitchTheme() { }, [config.theme]); } +function useDragSideBar() { + const limit = (x: number) => Math.min(500, Math.max(220, x)); + + const chatStore = useChatStore(); + const startX = useRef(0); + const startDragWidth = useRef(chatStore.config.sidebarWidth ?? 300); + const lastUpdateTime = useRef(Date.now()); + + const handleMouseMove = useRef((e: MouseEvent) => { + if (Date.now() < lastUpdateTime.current + 100) { + return; + } + lastUpdateTime.current = Date.now(); + const d = e.clientX - startX.current; + const nextWidth = limit(startDragWidth.current + d); + chatStore.updateConfig((config) => (config.sidebarWidth = nextWidth)); + }); + + const handleMouseUp = useRef(() => { + startDragWidth.current = chatStore.config.sidebarWidth ?? 300; + window.removeEventListener("mousemove", handleMouseMove.current); + window.removeEventListener("mouseup", handleMouseUp.current); + }); + + const onDragMouseDown = (e: MouseEvent) => { + startX.current = e.clientX; + + window.addEventListener("mousemove", handleMouseMove.current); + window.addEventListener("mouseup", handleMouseUp.current); + }; + + useEffect(() => { + if (isMobileScreen()) { + return; + } + + document.documentElement.style.setProperty( + "--sidebar-width", + `${limit(chatStore.config.sidebarWidth ?? 300)}px`, + ); + }, [chatStore.config.sidebarWidth]); + + return { + onDragMouseDown, + }; +} + const useHasHydrated = () => { const [hasHydrated, setHasHydrated] = useState(false); @@ -104,6 +158,9 @@ function _Home() { const [openSettings, setOpenSettings] = useState(false); const config = useChatStore((state) => state.config); + // drag side bar + const { onDragMouseDown } = useDragSideBar(); + useSwitchTheme(); if (loading) { @@ -190,6 +247,11 @@ function _Home() { /> */} + +
onDragMouseDown(e as any)} + >
diff --git a/app/components/markdown.tsx b/app/components/markdown.tsx index 25991d742..c8076640c 100644 --- a/app/components/markdown.tsx +++ b/app/components/markdown.tsx @@ -67,7 +67,7 @@ export function Markdown(props: { content: string }) { components={{ pre: PreCode, }} - linkTarget={'_blank'} + linkTarget={"_blank"} > {props.content} diff --git a/app/components/settings.module.scss b/app/components/settings.module.scss index 7d40d83b8..830e1baeb 100644 --- a/app/components/settings.module.scss +++ b/app/components/settings.module.scss @@ -19,11 +19,16 @@ cursor: pointer; } -.password-input { +.password-input-container { + max-width: 50%; display: flex; justify-content: flex-end; .password-eye { margin-right: 4px; } + + .password-input { + min-width: 80%; + } } diff --git a/app/components/ui-lib.module.scss b/app/components/ui-lib.module.scss index 83eb614f7..457c55049 100644 --- a/app/components/ui-lib.module.scss +++ b/app/components/ui-lib.module.scss @@ -9,6 +9,7 @@ .popover { position: relative; + z-index: 2; } .popover-content { @@ -126,6 +127,7 @@ width: 100vw; display: flex; justify-content: center; + pointer-events: none; .toast-content { max-width: 80vw; @@ -140,6 +142,7 @@ margin-bottom: 20px; display: flex; align-items: center; + pointer-events: all; .toast-action { padding-left: 20px; diff --git a/app/icons/max.svg b/app/icons/max.svg new file mode 100644 index 000000000..7dab09ed2 --- /dev/null +++ b/app/icons/max.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/icons/min.svg b/app/icons/min.svg new file mode 100644 index 000000000..3be5cd3f2 --- /dev/null +++ b/app/icons/min.svg @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/icons/share.svg b/app/icons/share.svg new file mode 100644 index 000000000..735b8196f --- /dev/null +++ b/app/icons/share.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/requests.ts b/app/requests.ts index 987434ed4..ee3498c11 100644 --- a/app/requests.ts +++ b/app/requests.ts @@ -9,7 +9,7 @@ const makeRequestParam = ( options?: { filterBot?: boolean; stream?: boolean; - } + }, ): ChatRequest => { let sendMessages = messages.map((v) => ({ role: v.role, @@ -20,7 +20,11 @@ const makeRequestParam = ( sendMessages = sendMessages.filter((m) => m.role !== "assistant"); } - const modelConfig = useChatStore.getState().config.modelConfig; + const modelConfig = { ...useChatStore.getState().config.modelConfig }; + + // @yidadaa: wont send max_tokens, because it is nonsense for Muggles + // @ts-expect-error + delete modelConfig.max_tokens; return { messages: sendMessages, @@ -84,7 +88,7 @@ export async function requestUsage() { const [used, subs] = await Promise.all([ requestOpenaiClient( - `dashboard/billing/usage?start_date=${startDate}&end_date=${endDate}` + `dashboard/billing/usage?start_date=${startDate}&end_date=${endDate}`, )(null, "GET"), requestOpenaiClient("dashboard/billing/subscription")(null, "GET"), ]); @@ -124,7 +128,7 @@ export async function requestChatStream( onMessage: (message: string, done: boolean) => void; onError: (error: Error, statusCode?: number) => void; onController?: (controller: AbortController) => void; - } + }, ) { const req = makeRequestParam(messages, { stream: true, @@ -213,7 +217,7 @@ export const ControllerPool = { addController( sessionIndex: number, messageId: number, - controller: AbortController + controller: AbortController, ) { const key = this.key(sessionIndex, messageId); this.controllers[key] = controller; diff --git a/app/store/app.ts b/app/store/app.ts index 8586d8f12..c590955b4 100644 --- a/app/store/app.ts +++ b/app/store/app.ts @@ -53,6 +53,7 @@ export interface ChatConfig { theme: Theme; tightBorder: boolean; sendPreviewBubble: boolean; + sidebarWidth: number; disablePromptHint: boolean; @@ -140,7 +141,7 @@ const DEFAULT_CONFIG: ChatConfig = { fontSize: 15, theme: Theme.Dark as Theme, tightBorder: false, - sendPreviewBubble: false, + sendPreviewBubble: true, disablePromptHint: false, @@ -205,7 +206,7 @@ interface ChatStore { moveSession: (from: number, to: number) => void; selectSession: (index: number) => void; newSession: () => void; - deleteSession: () => void; + deleteSession: (index?: number) => void; currentSession: () => ChatSession; onNewMessage: (message: Message) => void; onUserInput: (content: string) => Promise; @@ -326,24 +327,31 @@ export const useChatStore = create()( })); }, - deleteSession() { + deleteSession(i?: number) { const deletedSession = get().currentSession(); - const index = get().currentSessionIndex; + const index = i ?? get().currentSessionIndex; const isLastSession = get().sessions.length === 1; if (!isMobileScreen() || confirm(Locale.Home.DeleteChat)) { get().removeSession(index); + + showToast( + Locale.Home.DeleteToast, + { + text: Locale.Home.Revert, + onClick() { + set((state) => ({ + sessions: state.sessions + .slice(0, index) + .concat([deletedSession]) + .concat( + state.sessions.slice(index + Number(isLastSession)), + ), + })); + }, + }, + 5000, + ); } - showToast(Locale.Home.DeleteToast, { - text: Locale.Home.Revert, - onClick() { - set((state) => ({ - sessions: state.sessions - .slice(0, index) - .concat([deletedSession]) - .concat(state.sessions.slice(index + Number(isLastSession))), - })); - }, - }); }, currentSession() { diff --git a/app/utils.ts b/app/utils.ts index 333866c7b..9a792fd52 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -51,6 +51,12 @@ export function isMobileScreen() { return window.innerWidth <= 600; } +export function isFirefox() { + return ( + typeof navigator !== "undefined" && /firefox/i.test(navigator.userAgent) + ); +} + export function selectOrCopy(el: HTMLElement, content: string) { const currentSelection = window.getSelection(); @@ -91,3 +97,51 @@ export function getCurrentVersion() { export function getEmojiUrl(unified: string, style: EmojiStyle) { return `https://cdn.staticfile.org/emoji-datasource-apple/14.0.0/img/${style}/64/${unified}.png`; } + +function getDomContentWidth(dom: HTMLElement) { + const style = window.getComputedStyle(dom); + const paddingWidth = + parseFloat(style.paddingLeft) + parseFloat(style.paddingRight); + const width = dom.clientWidth - paddingWidth; + return width; +} + +function getOrCreateMeasureDom(id: string, init?: (dom: HTMLElement) => void) { + let dom = document.getElementById(id); + + if (!dom) { + dom = document.createElement("span"); + dom.style.position = "absolute"; + dom.style.wordBreak = "break-word"; + dom.style.fontSize = "14px"; + dom.style.transform = "translateY(-200vh)"; + dom.style.pointerEvents = "none"; + dom.style.opacity = "0"; + dom.id = id; + document.body.appendChild(dom); + init?.(dom); + } + + return dom!; +} + +export function autoGrowTextArea(dom: HTMLTextAreaElement) { + const measureDom = getOrCreateMeasureDom("__measure"); + const singleLineDom = getOrCreateMeasureDom("__single_measure", (dom) => { + dom.innerText = "TEXT_FOR_MEASURE"; + }); + + const width = getDomContentWidth(dom); + measureDom.style.width = width + "px"; + measureDom.innerHTML = dom.value.trim().length > 0 ? dom.value : "1"; + + const lineWrapCount = Math.max(0, dom.value.split("\n").length - 1); + const height = parseFloat(window.getComputedStyle(measureDom).height); + const singleLineHeight = parseFloat( + window.getComputedStyle(singleLineDom).height, + ); + + const rows = Math.round(height / singleLineHeight) + lineWrapCount; + + return rows; +} diff --git a/scripts/fetch-prompts.mjs b/scripts/fetch-prompts.mjs index 3c4801443..7f6818d3b 100644 --- a/scripts/fetch-prompts.mjs +++ b/scripts/fetch-prompts.mjs @@ -1,14 +1,13 @@ import fetch from "node-fetch"; import fs from "fs/promises"; -const RAW_CN_URL = - "https://raw.githubusercontent.com/PlexPt/awesome-chatgpt-prompts-zh/main/prompts-zh.json"; -const CN_URL = - "https://cdn.jsdelivr.net/gh/PlexPt/awesome-chatgpt-prompts-zh@main/prompts-zh.json"; -const RAW_EN_URL = - "https://raw.githubusercontent.com/f/awesome-chatgpt-prompts/main/prompts.csv"; -const EN_URL = - "https://cdn.jsdelivr.net/gh/f/awesome-chatgpt-prompts@main/prompts.csv"; +const RAW_FILE_URL = "https://raw.githubusercontent.com/"; +const MIRRORF_FILE_URL = "https://raw.fgit.ml/"; + +const RAW_CN_URL = "PlexPt/awesome-chatgpt-prompts-zh/main/prompts-zh.json"; +const CN_URL = MIRRORF_FILE_URL + RAW_CN_URL; +const RAW_EN_URL = "f/awesome-chatgpt-prompts/main/prompts.csv"; +const EN_URL = MIRRORF_FILE_URL + RAW_EN_URL; const FILE = "./public/prompts.json"; async function fetchCN() { diff --git a/tsconfig.json b/tsconfig.json index 14d189328..c73eef3e8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -23,6 +23,6 @@ "@/*": ["./*"] } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "app/calcTextareaHeight.ts"], "exclude": ["node_modules"] } diff --git a/vercel.json b/vercel.json new file mode 100644 index 000000000..0cae358a1 --- /dev/null +++ b/vercel.json @@ -0,0 +1,5 @@ +{ + "github": { + "silent": true + } +}