diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..3a3cce576 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "npm" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" diff --git a/.github/workflows/app.yml b/.github/workflows/app.yml new file mode 100644 index 000000000..234338dd4 --- /dev/null +++ b/.github/workflows/app.yml @@ -0,0 +1,88 @@ +name: Release App + +on: + workflow_dispatch: + release: + types: [published] + +jobs: + create-release: + permissions: + contents: write + runs-on: ubuntu-20.04 + outputs: + release_id: ${{ steps.create-release.outputs.result }} + + steps: + - uses: actions/checkout@v3 + - name: setup node + uses: actions/setup-node@v3 + with: + node-version: 16 + - name: get version + run: echo "PACKAGE_VERSION=$(node -p "require('./src-tauri/tauri.conf.json').package.version")" >> $GITHUB_ENV + - name: create release + id: create-release + uses: actions/github-script@v6 + with: + script: | + const { data } = await github.rest.repos.getLatestRelease({ + owner: context.repo.owner, + repo: context.repo.repo, + }) + return data.id + + build-tauri: + needs: create-release + permissions: + contents: write + strategy: + fail-fast: false + matrix: + platform: [macos-latest, ubuntu-20.04, windows-latest] + + runs-on: ${{ matrix.platform }} + steps: + - uses: actions/checkout@v3 + - name: setup node + uses: actions/setup-node@v3 + with: + node-version: 16 + - name: install Rust stable + uses: dtolnay/rust-toolchain@stable + - name: install dependencies (ubuntu only) + if: matrix.platform == 'ubuntu-20.04' + run: | + sudo apt-get update + sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf + - name: install frontend dependencies + run: yarn install # change this to npm or pnpm depending on which one you use + - uses: tauri-apps/tauri-action@v0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} + TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} + with: + releaseId: ${{ needs.create-release.outputs.release_id }} + + publish-release: + permissions: + contents: write + runs-on: ubuntu-20.04 + needs: [create-release, build-tauri] + + steps: + - name: publish release + id: publish-release + uses: actions/github-script@v6 + env: + release_id: ${{ needs.create-release.outputs.release_id }} + with: + script: | + github.rest.repos.updateRelease({ + owner: context.repo.owner, + repo: context.repo.repo, + release_id: process.env.release_id, + draft: false, + prerelease: false + }) diff --git a/.github/workflows/issue-translator.yml b/.github/workflows/issue-translator.yml new file mode 100644 index 000000000..560f66d34 --- /dev/null +++ b/.github/workflows/issue-translator.yml @@ -0,0 +1,15 @@ +name: Issue Translator +on: + issue_comment: + types: [created] + issues: + types: [opened] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: usthe/issues-translate-action@v2.7 + with: + IS_MODIFY_TITLE: false + CUSTOM_BOT_NOTE: Bot detected the issue body's language is not English, translate it automatically. diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml index a4c14c843..ebf5587d0 100644 --- a/.github/workflows/sync.yml +++ b/.github/workflows/sync.yml @@ -35,6 +35,6 @@ jobs: - 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]." + echo "[Error] 由于上游仓库的 workflow 文件变更,导致 GitHub 自动暂停了本次自动更新,你需要手动 Sync Fork 一次,详细教程请查看:https://github.com/Yidadaa/ChatGPT-Next-Web/blob/main/README_CN.md#%E6%89%93%E5%BC%80%E8%87%AA%E5%8A%A8%E6%9B%B4%E6%96%B0" + echo "[Error] Due to a change in the workflow file of the upstream repository, GitHub has automatically suspended the scheduled automatic update. You need to manually sync your fork. Please refer to the detailed tutorial for instructions: https://github.com/Yidadaa/ChatGPT-Next-Web#enable-automatic-updates" exit 1 diff --git a/.gitignore b/.gitignore index 446a21433..b00b0e325 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,6 @@ dev # docker-compose env files .env + +*.key +*.key.pub \ No newline at end of file diff --git a/README.md b/README.md index 9607a21ef..148c137f8 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,25 @@ English / [简体中文](./README_CN.md) -One-Click to deploy well-designed ChatGPT web UI on Vercel. +One-Click to get well-designed cross-platform ChatGPT web UI. -一键免费部署你的私人 ChatGPT 网页应用。 +一键免费部署你的跨平台私人 ChatGPT 应用。 -[Demo](https://chatgpt.nextweb.fun/) / [Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Buy Me a Coffee](https://www.buymeacoffee.com/yidadaa) +[![Web][Web-image]][web-url] +[![Windows][Windows-image]][download-url] +[![MacOS][MacOS-image]][download-url] +[![Linux][Linux-image]][download-url] -[演示](https://chatgpt.nextweb.fun/) / [反馈](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [QQ 群](https://github.com/Yidadaa/ChatGPT-Next-Web/discussions/1724) / [打赏开发者](https://user-images.githubusercontent.com/16968934/227772541-5bcd52d8-61b7-488c-a203-0330d8006e2b.jpg) +[Web App](https://chatgpt.nextweb.fun/) / [Desktop App](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Buy Me a Coffee](https://www.buymeacoffee.com/yidadaa) + +[网页版](https://chatgpt.nextweb.fun/) / [客户端](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [反馈](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [QQ 群](https://github.com/Yidadaa/ChatGPT-Next-Web/discussions/1724) / [打赏开发者](https://user-images.githubusercontent.com/16968934/227772541-5bcd52d8-61b7-488c-a203-0330d8006e2b.jpg) + +[web-url]: https://chatgpt.nextweb.fun +[download-url]: https://github.com/Yidadaa/ChatGPT-Next-Web/releases +[Web-image]: https://img.shields.io/badge/Web-PWA-orange?logo=microsoftedge +[Windows-image]: https://img.shields.io/badge/-Windows-blue?logo=windows +[MacOS-image]: https://img.shields.io/badge/-MacOS-black?logo=apple +[Linux-image]: https://img.shields.io/badge/-Linux-333?logo=ubuntu [![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) @@ -24,6 +36,8 @@ One-Click to deploy well-designed ChatGPT web UI on Vercel. ## Features - **Deploy for free with one-click** on Vercel in under 1 minute +- Compact client (~5MB) on Linux/Windows/MacOS, [download it now](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) +- Fully compatible with self-deployed llms, recommended for use with [RWKV-Runner](https://github.com/josStorer/RWKV-Runner) or [LocalAI](https://github.com/go-skynet/LocalAI) - Privacy first, all data stored locally in the browser - Markdown support: LaTex, mermaid, code highlight, etc. - Responsive design, dark mode and PWA @@ -39,23 +53,20 @@ One-Click to deploy well-designed ChatGPT web UI on Vercel. - [x] User Prompt: user can edit and save custom prompts to prompt list - [x] Prompt Template: create a new chat with pre-defined in-context prompts [#993](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/993) - [x] Share as image, share to ShareGPT [#1741](https://github.com/Yidadaa/ChatGPT-Next-Web/pull/1741) -- [ ] Desktop App with tauri -- [ ] Self-host Model: support llama, alpaca, ChatGLM, BELLE etc. +- [x] Desktop App with tauri +- [x] Self-host Model: Fully compatible with [RWKV-Runner](https://github.com/josStorer/RWKV-Runner), as well as server deployment of [LocalAI](https://github.com/go-skynet/LocalAI): llama/gpt4all/rwkv/vicuna/koala/gpt4all-j/cerebras/falcon/dolly etc. - [ ] Plugins: support network search, calculator, any other apis etc. [#165](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/165) -### Not in Plan - -- User login, accounts, cloud sync -- UI text customize - ## What's New - 🚀 v2.0 is released, now you can create prompt templates, turn your ideas into reality! Read this: [ChatGPT Prompt Engineering Tips: Zero, One and Few Shot Prompting](https://www.allabtai.com/prompt-engineering-tips-zero-one-and-few-shot-prompting/). - 🚀 v2.7 let's share conversations as image, or share to ShareGPT! +- 🚀 v2.8 now we have a client that runs across all platforms! ## 主要功能 - 在 1 分钟内使用 Vercel **免费一键部署** +- 提供体积极小(~5MB)的跨平台客户端(Linux/Windows/MacOS), [下载地址](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) - 完整的 Markdown 支持:LaTex 公式、Mermaid 流程图、代码高亮等等 - 精心设计的 UI,响应式设计,支持深色模式,支持 PWA - 极快的首屏加载速度(~100kb),支持流式响应 @@ -72,20 +83,16 @@ One-Click to deploy well-designed ChatGPT web UI on Vercel. - [x] 允许用户自行编辑内置 Prompt 列表 - [x] 预制角色:使用预制角色快速定制新对话 [#993](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/993) - [x] 分享为图片,分享到 ShareGPT 链接 [#1741](https://github.com/Yidadaa/ChatGPT-Next-Web/pull/1741) -- [ ] 使用 tauri 打包桌面应用 -- [ ] 支持自部署的大语言模型 +- [x] 使用 tauri 打包桌面应用 +- [x] 支持自部署的大语言模型:开箱即用 [RWKV-Runner](https://github.com/josStorer/RWKV-Runner) ,服务端部署 [LocalAI 项目](https://github.com/go-skynet/LocalAI) llama / gpt4all / rwkv / vicuna / koala / gpt4all-j / cerebras / falcon / dolly 等等 - [ ] 插件机制,支持联网搜索、计算器、调用其他平台 api [#165](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/165) -### 不会开发的功能 - -- 界面文字自定义 -- 用户登录、账号管理、消息云同步 - ## 最新动态 - 🚀 v2.0 已经发布,现在你可以使用面具功能快速创建预制对话了! 了解更多: [ChatGPT 提示词高阶技能:零次、一次和少样本提示](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/138)。 - 💡 想要更方便地随时随地使用本项目?可以试下这款桌面插件:https://github.com/mushan0x0/AI0x0.com - 🚀 v2.7 现在可以将会话分享为图片了,也可以分享到 ShareGPT 的在线链接。 +- 🚀 v2.8 发布了横跨 Linux/Windows/MacOS 的体积极小的客户端。 ## Get Started @@ -178,6 +185,10 @@ If you do not want users to input their own API key, set this value to 1. If you do not want users to use GPT-4, set this value to 1. +## Requirements + +NodeJS >= 18, Docker >= 20 + ## Development > [简体中文 > 如何进行二次开发](./README_CN.md#开发) @@ -188,6 +199,9 @@ Before starting development, you must create a new `.env.local` file at project ``` OPENAI_API_KEY= + +# if you are not able to access openai service, use this BASE_URL +BASE_URL=https://chatgpt1.nextweb.fun/api/proxy ``` ### Local Development @@ -225,6 +239,12 @@ docker run -d -p 3000:3000 \ yidadaa/chatgpt-next-web ``` +If your proxy needs password, use: + +```shell +-e PROXY_URL="http://127.0.0.1:7890 user pass" +``` + ### Shell ```shell diff --git a/README_CN.md b/README_CN.md index 1e987542d..5fda7fc2e 100644 --- a/README_CN.md +++ b/README_CN.md @@ -100,8 +100,6 @@ OpenAI 接口代理 URL,如果你手动配置了 openai 接口代理,请填 ## 开发 -> 强烈不建议在本地进行开发或者部署,由于一些技术原因,很难在本地配置好 OpenAI API 代理,除非你能保证可以直连 OpenAI 服务器。 - 点击下方按钮,开始二次开发: [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) @@ -110,6 +108,9 @@ OpenAI 接口代理 URL,如果你手动配置了 openai 接口代理,请填 ``` OPENAI_API_KEY= + +# 中国大陆用户,可以使用本项目自带的代理进行开发,你也可以自由选择其他代理地址 +BASE_URL=https://chatgpt1.nextweb.fun/api/proxy ``` ### 本地开发 @@ -146,6 +147,12 @@ docker run -d -p 3000:3000 \ yidadaa/chatgpt-next-web ``` +如果你的本地代理需要账号密码,可以使用: + +```shell +-e PROXY_URL="http://127.0.0.1:7890 user password" +``` + 如果你需要指定其他环境变量,请自行在上述命令中增加 `-e 环境变量=环境变量值` 来指定。 ### 本地部署 diff --git a/app/api/auth.ts b/app/api/auth.ts index fffb63c1f..e0453b2b4 100644 --- a/app/api/auth.ts +++ b/app/api/auth.ts @@ -2,7 +2,6 @@ import { NextRequest } from "next/server"; import { getServerSideConfig } from "../config/server"; import md5 from "spark-md5"; import { ACCESS_CODE_PREFIX } from "../constant"; -import { OPENAI_URL } from "./common"; function getIP(req: NextRequest) { let ip = req.ip ?? req.headers.get("x-real-ip"); diff --git a/app/api/common.ts b/app/api/common.ts index ccfb99e26..22bd5d4a4 100644 --- a/app/api/common.ts +++ b/app/api/common.ts @@ -43,6 +43,8 @@ export async function requestOpenai(req: NextRequest) { cache: "no-store", method: req.method, body: req.body, + // @ts-ignore + duplex: "half", signal: controller.signal, }; diff --git a/app/api/openai/[...path]/route.ts b/app/api/openai/[...path]/route.ts index 981749e7e..36f92d0ff 100644 --- a/app/api/openai/[...path]/route.ts +++ b/app/api/openai/[...path]/route.ts @@ -1,14 +1,36 @@ +import { OpenaiPath } from "@/app/constant"; import { prettyObject } from "@/app/utils/format"; import { NextRequest, NextResponse } from "next/server"; import { auth } from "../../auth"; import { requestOpenai } from "../../common"; +const ALLOWD_PATH = new Set(Object.values(OpenaiPath)); + async function handle( req: NextRequest, { params }: { params: { path: string[] } }, ) { console.log("[OpenAI Route] params ", params); + if (req.method === "OPTIONS") { + return NextResponse.json({ body: "OK" }, { status: 200 }); + } + + const subpath = params.path.join("/"); + + if (!ALLOWD_PATH.has(subpath)) { + console.log("[OpenAI Route] forbidden path ", subpath); + return NextResponse.json( + { + error: true, + msg: "you are not allowed to request " + subpath, + }, + { + status: 403, + }, + ); + } + const authResult = auth(req); if (authResult.error) { return NextResponse.json(authResult, { diff --git a/app/client/api.ts b/app/client/api.ts index fb829f97a..a8960ff51 100644 --- a/app/client/api.ts +++ b/app/client/api.ts @@ -1,5 +1,6 @@ +import { getClientConfig } from "../config/client"; import { ACCESS_CODE_PREFIX } from "../constant"; -import { ChatMessage, ModelConfig, ModelType, useAccessStore } from "../store"; +import { ChatMessage, ModelType, useAccessStore } from "../store"; import { ChatGPTApi } from "./platforms/openai"; export const ROLES = ["system", "user", "assistant"] as const; @@ -42,6 +43,27 @@ export abstract class LLMApi { abstract usage(): Promise; } +type ProviderName = "openai" | "azure" | "claude" | "palm"; + +interface Model { + name: string; + provider: ProviderName; + ctxlen: number; +} + +interface ChatProvider { + name: ProviderName; + apiConfig: { + baseUrl: string; + apiKey: string; + summaryModel: Model; + }; + models: Model[]; + + chat: () => void; + usage: () => void; +} + export class ClientApi { public llm: LLMApi; @@ -72,7 +94,11 @@ export class ClientApi { // Please do not modify this message console.log("[Share]", msgs); - const res = await fetch("/sharegpt", { + const clientConfig = getClientConfig(); + const proxyUrl = "/sharegpt"; + const rawUrl = "https://sharegpt.com/api/conversations"; + const shareUrl = clientConfig?.isApp ? rawUrl : proxyUrl; + const res = await fetch(shareUrl, { body: JSON.stringify({ avatarUrl, items: msgs, diff --git a/app/client/platforms/openai.ts b/app/client/platforms/openai.ts index 84c4a2df0..fce7eee4e 100644 --- a/app/client/platforms/openai.ts +++ b/app/client/platforms/openai.ts @@ -1,4 +1,4 @@ -import { REQUEST_TIMEOUT_MS } from "@/app/constant"; +import { OpenaiPath, REQUEST_TIMEOUT_MS } from "@/app/constant"; import { useAccessStore, useAppConfig, useChatStore } from "@/app/store"; import { ChatOptions, getHeaders, LLMApi, LLMUsage } from "../api"; @@ -10,10 +10,6 @@ import { import { prettyObject } from "@/app/utils/format"; export class ChatGPTApi implements LLMApi { - public ChatPath = "v1/chat/completions"; - public UsagePath = "dashboard/billing/usage"; - public SubsPath = "dashboard/billing/subscription"; - path(path: string): string { let openaiUrl = useAccessStore.getState().openaiUrl; if (openaiUrl.endsWith("/")) { @@ -46,6 +42,7 @@ export class ChatGPTApi implements LLMApi { model: modelConfig.model, temperature: modelConfig.temperature, presence_penalty: modelConfig.presence_penalty, + frequency_penalty: modelConfig.frequency_penalty, }; console.log("[Request] openai payload: ", requestPayload); @@ -55,7 +52,7 @@ export class ChatGPTApi implements LLMApi { options.onController?.(controller); try { - const chatPath = this.path(this.ChatPath); + const chatPath = this.path(OpenaiPath.ChatPath); const chatPayload = { method: "POST", body: JSON.stringify(requestPayload), @@ -177,14 +174,14 @@ export class ChatGPTApi implements LLMApi { const [used, subs] = await Promise.all([ fetch( this.path( - `${this.UsagePath}?start_date=${startDate}&end_date=${endDate}`, + `${OpenaiPath.UsagePath}?start_date=${startDate}&end_date=${endDate}`, ), { method: "GET", headers: getHeaders(), }, ), - fetch(this.path(this.SubsPath), { + fetch(this.path(OpenaiPath.SubsPath), { method: "GET", headers: getHeaders(), }), @@ -228,3 +225,4 @@ export class ChatGPTApi implements LLMApi { } as LLMUsage; } } +export { OpenaiPath }; diff --git a/app/components/auth.tsx b/app/components/auth.tsx index 93df35b90..de0df4542 100644 --- a/app/components/auth.tsx +++ b/app/components/auth.tsx @@ -25,7 +25,7 @@ export function AuthPage() { { diff --git a/app/components/chat-list.tsx b/app/components/chat-list.tsx index c1365182c..fc4e53788 100644 --- a/app/components/chat-list.tsx +++ b/app/components/chat-list.tsx @@ -72,9 +72,7 @@ export function ChatItem(props: {
{Locale.ChatItem.ChatItemCount(props.count)}
-
- {new Date(props.time).toLocaleString()} -
+
{props.time}
)} diff --git a/app/components/chat.module.scss b/app/components/chat.module.scss index 0e2741e70..644c917a1 100644 --- a/app/components/chat.module.scss +++ b/app/components/chat.module.scss @@ -17,10 +17,38 @@ transition: all ease 0.3s; margin-bottom: 10px; align-items: center; + height: 16px; + width: var(--icon-width); &:not(:last-child) { margin-right: 5px; } + + .text { + white-space: nowrap; + padding-left: 5px; + opacity: 0; + transform: translateX(-5px); + transition: all ease 0.3s; + transition-delay: 0.1s; + pointer-events: none; + } + + &:hover { + width: var(--full-width); + + .text { + opacity: 1; + transform: translate(0); + } + } + + .text, + .icon { + display: flex; + align-items: center; + justify-content: center; + } } } diff --git a/app/components/chat.tsx b/app/components/chat.tsx index 70fd462d9..047607d4e 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -1,5 +1,11 @@ import { useDebouncedCallback } from "use-debounce"; -import { useState, useRef, useEffect, useLayoutEffect } from "react"; +import React, { + useState, + useRef, + useEffect, + useLayoutEffect, + useMemo, +} from "react"; import SendWhiteIcon from "../icons/send-white.svg"; import BrainIcon from "../icons/brain.svg"; @@ -61,6 +67,7 @@ import { useMaskStore } from "../store/mask"; import { useCommand } from "../command"; import { prettyObject } from "../utils/format"; import { ExportMessageModal } from "./exporter"; +import { getClientConfig } from "../config/client"; const Markdown = dynamic(async () => (await import("./markdown")).Markdown, { loading: () => , @@ -279,6 +286,57 @@ function ClearContextDivider() { ); } +function ChatAction(props: { + text: string; + icon: JSX.Element; + onClick: () => void; +}) { + const iconRef = useRef(null); + const textRef = useRef(null); + const [width, setWidth] = useState({ + full: 20, + icon: 20, + }); + + function updateWidth() { + if (!iconRef.current || !textRef.current) return; + const getWidth = (dom: HTMLDivElement) => dom.getBoundingClientRect().width; + const textWidth = getWidth(textRef.current); + const iconWidth = getWidth(iconRef.current); + setWidth({ + full: textWidth + iconWidth, + icon: iconWidth, + }); + } + + useEffect(() => { + updateWidth(); + }, []); + + return ( +
{ + props.onClick(); + setTimeout(updateWidth, 1); + }} + style={ + { + "--icon-width": `${width.icon}px`, + "--full-width": `${width.full}px`, + } as React.CSSProperties + } + > +
+ {props.icon} +
+
+ {props.text} +
+
+ ); +} + function useScrollToBottom() { // for auto-scroll const scrollRef = useRef(null); @@ -330,61 +388,60 @@ export function ChatActions(props: { return (
{couldStop && ( -
- -
+ text={Locale.Chat.InputActions.Stop} + icon={} + /> )} {!props.hitBottom && ( -
- -
+ text={Locale.Chat.InputActions.ToBottom} + icon={} + /> )} {props.hitBottom && ( -
- -
+ text={Locale.Chat.InputActions.Settings} + icon={} + /> )} -
- {theme === Theme.Auto ? ( - - ) : theme === Theme.Light ? ( - - ) : theme === Theme.Dark ? ( - - ) : null} -
+ text={Locale.Chat.InputActions.Theme[theme]} + icon={ + <> + {theme === Theme.Auto ? ( + + ) : theme === Theme.Light ? ( + + ) : theme === Theme.Dark ? ( + + ) : null} + + } + /> -
- -
+ text={Locale.Chat.InputActions.Prompt} + icon={} + /> -
{ navigate(Path.Masks); }} - > - -
+ text={Locale.Chat.InputActions.Masks} + icon={} + /> -
} onClick={() => { chatStore.updateCurrentSession((session) => { if (session.clearContextIndex === session.messages.length) { @@ -395,9 +452,7 @@ export function ChatActions(props: { } }); }} - > - -
+ />
); } @@ -425,7 +480,7 @@ export function Chat() { const navigate = useNavigate(); const onChatBodyScroll = (e: HTMLElement) => { - const isTouchBottom = e.scrollTop + e.clientHeight >= e.scrollHeight - 100; + const isTouchBottom = e.scrollTop + e.clientHeight >= e.scrollHeight - 10; setHitBottom(isTouchBottom); }; @@ -656,9 +711,13 @@ export function Chat() { } }; + const clientConfig = useMemo(() => getClientConfig(), []); + const location = useLocation(); const isChat = location.pathname === Path.Chat; + const autoFocus = !isMobileScreen || isChat; // only focus in chat page + const showMaxIcon = !isMobileScreen && !clientConfig?.isApp; useCommand({ fill: setUserInput, @@ -669,7 +728,7 @@ export function Chat() { return (
-
+
- {!isMobileScreen && ( + {showMaxIcon && (
: } @@ -868,6 +927,9 @@ export function Chat() { onBlur={() => setAutoScroll(false)} rows={inputRows} autoFocus={autoFocus} + style={{ + fontSize: config.fontSize, + }} /> } diff --git a/app/components/exporter.tsx b/app/components/exporter.tsx index 10d5af994..7765b77aa 100644 --- a/app/components/exporter.tsx +++ b/app/components/exporter.tsx @@ -449,16 +449,16 @@ export function ImagePreviewer(props: {
- Model: {mask.modelConfig.model} + {Locale.Exporter.Model}: {mask.modelConfig.model}
- Messages: {props.messages.length} + {Locale.Exporter.Messages}: {props.messages.length}
- Topic: {session.topic} + {Locale.Exporter.Topic}: {session.topic}
- Time:{" "} + {Locale.Exporter.Time}:{" "} {new Date( props.messages.at(-1)?.date ?? Date.now(), ).toLocaleString()} diff --git a/app/components/home.tsx b/app/components/home.tsx index 96bcd2882..46fd78e81 100644 --- a/app/components/home.tsx +++ b/app/components/home.tsx @@ -24,6 +24,7 @@ import { import { SideBar } from "./sidebar"; import { useAppConfig } from "../store/config"; import { AuthPage } from "./auth"; +import { getClientConfig } from "../config/client"; export function Loading(props: { noLogo?: boolean }) { return ( @@ -93,9 +94,14 @@ const useHasHydrated = () => { const loadAsyncGoogleFont = () => { const linkEl = document.createElement("link"); + const proxyFontUrl = "/google-fonts"; + const remoteFontUrl = "https://fonts.googleapis.com"; + const googleFontUrl = + getClientConfig()?.buildMode === "export" ? remoteFontUrl : proxyFontUrl; linkEl.rel = "stylesheet"; linkEl.href = - "/google-fonts/css2?family=Noto+Sans+SC:wght@300;400;700;900&display=swap"; + googleFontUrl + + "/css2?family=Noto+Sans+SC:wght@300;400;700;900&display=swap"; document.head.appendChild(linkEl); }; @@ -147,6 +153,10 @@ function Screen() { export function Home() { useSwitchTheme(); + useEffect(() => { + console.log("[Config] got config from build time", getClientConfig()); + }, []); + if (!useHasHydrated()) { return ; } diff --git a/app/components/markdown.tsx b/app/components/markdown.tsx index 108b0570d..fbde64530 100644 --- a/app/components/markdown.tsx +++ b/app/components/markdown.tsx @@ -11,18 +11,21 @@ import mermaid from "mermaid"; import LoadingIcon from "../icons/three-dots.svg"; import React from "react"; +import { useDebouncedCallback, useThrottledCallback } from "use-debounce"; -export function Mermaid(props: { code: string; onError: () => void }) { +export function Mermaid(props: { code: string }) { const ref = useRef(null); + const [hasError, setHasError] = useState(false); useEffect(() => { if (props.code && ref.current) { mermaid .run({ nodes: [ref.current], + suppressErrors: true, }) .catch((e) => { - props.onError(); + setHasError(true); console.error("[Mermaid] ", e.message); }); } @@ -41,10 +44,17 @@ export function Mermaid(props: { code: string; onError: () => void }) { } } + if (hasError) { + return null; + } + return (
viewSvgInNewWindow()} > @@ -55,33 +65,40 @@ export function Mermaid(props: { code: string; onError: () => void }) { export function PreCode(props: { children: any }) { const ref = useRef(null); + const refText = ref.current?.innerText; const [mermaidCode, setMermaidCode] = useState(""); - useEffect(() => { + const renderMermaid = useDebouncedCallback(() => { if (!ref.current) return; const mermaidDom = ref.current.querySelector("code.language-mermaid"); if (mermaidDom) { setMermaidCode((mermaidDom as HTMLElement).innerText); } - }, [props.children]); + }, 600); - if (mermaidCode) { - return setMermaidCode("")} />; - } + useEffect(() => { + setTimeout(renderMermaid, 1); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [refText]); return ( -
-       {
-          if (ref.current) {
-            const code = ref.current.innerText;
-            copyToClipboard(code);
-          }
-        }}
-      >
-      {props.children}
-    
+ <> + {mermaidCode.length > 0 && ( + + )} +
+         {
+            if (ref.current) {
+              const code = ref.current.innerText;
+              copyToClipboard(code);
+            }
+          }}
+        >
+        {props.children}
+      
+ ); } @@ -127,43 +144,57 @@ export function Markdown( ) { const mdRef = useRef(null); const renderedHeight = useRef(0); + const renderedWidth = useRef(0); const inView = useRef(!!props.defaultShow); + const [_, triggerRender] = useState(0); + const checkInView = useThrottledCallback( + () => { + const parent = props.parentRef?.current; + const md = mdRef.current; + if (parent && md && !props.defaultShow) { + const parentBounds = parent.getBoundingClientRect(); + const twoScreenHeight = Math.max(500, parentBounds.height * 2); + const mdBounds = md.getBoundingClientRect(); + const parentTop = parentBounds.top - twoScreenHeight; + const parentBottom = parentBounds.bottom + twoScreenHeight; + const isOverlap = + Math.max(parentTop, mdBounds.top) <= + Math.min(parentBottom, mdBounds.bottom); + inView.current = isOverlap; + triggerRender(Date.now()); + } - const parent = props.parentRef?.current; - const md = mdRef.current; + if (inView.current && md) { + const rect = md.getBoundingClientRect(); + renderedHeight.current = Math.max(renderedHeight.current, rect.height); + renderedWidth.current = Math.max(renderedWidth.current, rect.width); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, + 300, + { + leading: true, + trailing: true, + }, + ); - const checkInView = () => { - if (parent && md) { - const parentBounds = parent.getBoundingClientRect(); - const twoScreenHeight = Math.max(500, parentBounds.height * 2); - const mdBounds = md.getBoundingClientRect(); - const parentTop = parentBounds.top - twoScreenHeight; - const parentBottom = parentBounds.bottom + twoScreenHeight; - const isOverlap = - Math.max(parentTop, mdBounds.top) <= - Math.min(parentBottom, mdBounds.bottom); - inView.current = isOverlap; - } + useEffect(() => { + props.parentRef?.current?.addEventListener("scroll", checkInView); + checkInView(); + return () => + props.parentRef?.current?.removeEventListener("scroll", checkInView); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); - if (inView.current && md) { - renderedHeight.current = Math.max( - renderedHeight.current, - md.getBoundingClientRect().height, - ); - } - }; - - setTimeout(() => checkInView(), 1); + const getSize = (x: number) => (!inView.current && x > 0 ? x : "auto"); return (
0 - ? renderedHeight.current - : "auto", + height: getSize(renderedHeight.current), + width: getSize(renderedWidth.current), }} ref={mdRef} onContextMenu={props.onContextMenu} diff --git a/app/components/mask.tsx b/app/components/mask.tsx index de724e26d..d48ed7c29 100644 --- a/app/components/mask.tsx +++ b/app/components/mask.tsx @@ -185,7 +185,12 @@ function ContextPromptItem(props: { className={chatStyle["context-content"]} rows={focusingInput ? 5 : 1} onFocus={() => setFocusingInput(true)} - onBlur={() => setFocusingInput(false)} + onBlur={() => { + setFocusingInput(false); + // If the selection is not removed when the user loses focus, some + // extensions like "Translate" will always display a floating bar + window?.getSelection()?.removeAllRanges(); + }} onInput={(e) => props.update({ ...props.prompt, diff --git a/app/components/model-config.tsx b/app/components/model-config.tsx index 0392621d9..f79e0e8f6 100644 --- a/app/components/model-config.tsx +++ b/app/components/model-config.tsx @@ -2,7 +2,7 @@ import { ALL_MODELS, ModalConfigValidator, ModelConfig } from "../store"; import Locale from "../locales"; import { InputRange } from "./input-range"; -import { List, ListItem, Select } from "./ui-lib"; +import { ListItem, Select } from "./ui-lib"; export function ModelConfigList(props: { modelConfig: ModelConfig; @@ -88,6 +88,42 @@ export function ModelConfigList(props: { > + + { + props.updateConfig( + (config) => + (config.frequency_penalty = + ModalConfigValidator.frequency_penalty( + e.currentTarget.valueAsNumber, + )), + ); + }} + > + + + + + props.updateConfig( + (config) => (config.template = e.currentTarget.value), + ) + } + > + + void }) { const promptStore = usePromptStore(); @@ -285,9 +286,12 @@ export function Settings() { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + const clientConfig = useMemo(() => getClientConfig(), []); + const showAccessCode = enabledAccessControl && !clientConfig?.isApp; + return ( -
+
{Locale.Settings.Title} @@ -484,7 +488,7 @@ export function Settings() { - {enabledAccessControl ? ( + {showAccessCode ? ( )} + + {!accessStore.hideUserApiKey ? ( + + + accessStore.updateOpenAiUrl(e.currentTarget.value) + } + > + + ) : null} diff --git a/app/components/sidebar.tsx b/app/components/sidebar.tsx index 70e21e32d..33e7e3c1c 100644 --- a/app/components/sidebar.tsx +++ b/app/components/sidebar.tsx @@ -118,8 +118,10 @@ export function SideBar(props: { className?: string }) { shouldNarrow && styles["narrow-sidebar"] }`} > -
-
SoulShellGPT
+
+
+ SoulShellGPT +
有温度的AI助理
diff --git a/app/config/build.ts b/app/config/build.ts index 79ed5d3e8..2009b5f33 100644 --- a/app/config/build.ts +++ b/app/config/build.ts @@ -1,16 +1,3 @@ -const COMMIT_ID: string = (() => { - try { - const childProcess = require("child_process"); - return childProcess - .execSync('git log -1 --format="%at000" --date=unix') - .toString() - .trim(); - } catch (e) { - console.error("[Build Config] No git or not from git repo."); - return "unknown"; - } -})(); - export const getBuildConfig = () => { if (typeof process === "undefined") { throw Error( @@ -18,7 +5,24 @@ export const getBuildConfig = () => { ); } + const COMMIT_ID: string = (() => { + try { + const childProcess = require("child_process"); + return childProcess + .execSync('git log -1 --format="%at000" --date=unix') + .toString() + .trim(); + } catch (e) { + console.error("[Build Config] No git or not from git repo."); + return "unknown"; + } + })(); + return { commitId: COMMIT_ID, + buildMode: process.env.BUILD_MODE ?? "standalone", + isApp: !!process.env.BUILD_APP, }; }; + +export type BuildConfig = ReturnType; diff --git a/app/config/client.ts b/app/config/client.ts new file mode 100644 index 000000000..da582a3e8 --- /dev/null +++ b/app/config/client.ts @@ -0,0 +1,27 @@ +import { BuildConfig, getBuildConfig } from "./build"; + +export function getClientConfig() { + if (typeof document !== "undefined") { + // client side + return JSON.parse(queryMeta("config")) as BuildConfig; + } + + if (typeof process !== "undefined") { + // server side + return getBuildConfig(); + } +} + +function queryMeta(key: string, defaultValue?: string): string { + let ret: string; + if (document) { + const meta = document.head.querySelector( + `meta[name='${key}']`, + ) as HTMLMetaElement; + ret = meta?.content ?? ""; + } else { + ret = defaultValue ?? ""; + } + + return ret; +} diff --git a/app/config/server.ts b/app/config/server.ts index b978e726e..0f6e6fb86 100644 --- a/app/config/server.ts +++ b/app/config/server.ts @@ -10,6 +10,8 @@ declare global { VERCEL?: string; HIDE_USER_API_KEY?: string; // disable user's api key input DISABLE_GPT4?: string; // allow user to use gpt-4 or not + BUILD_MODE?: "standalone" | "export"; + BUILD_APP?: string; // is building desktop app } } } diff --git a/app/constant.ts b/app/constant.ts index b640919e5..6630b7043 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -6,6 +6,7 @@ export const UPDATE_URL = `${REPO_URL}#keep-updated`; export const FETCH_COMMIT_URL = `https://api.github.com/repos/${OWNER}/${REPO}/commits?per_page=1`; export const FETCH_TAG_URL = `https://api.github.com/repos/${OWNER}/${REPO}/tags?per_page=1`; export const RUNTIME_CONFIG_DOM = "danger-runtime-config"; +export const DEFAULT_API_HOST = "https://chatgpt1.nextweb.fun/api/proxy"; export enum Path { Home = "/", @@ -45,3 +46,16 @@ export const LAST_INPUT_KEY = "last-input"; export const REQUEST_TIMEOUT_MS = 60000; export const EXPORT_MESSAGE_CLASS_NAME = "export-markdown"; + +export const OpenaiPath = { + ChatPath: "v1/chat/completions", + UsagePath: "dashboard/billing/usage", + SubsPath: "dashboard/billing/subscription", +}; + +export const DEFAULT_INPUT_TEMPLATE = ` +Act as a virtual assistant powered by model: '{{model}}', my input is: +''' +{{input}} +''' +`; diff --git a/app/layout.tsx b/app/layout.tsx index a42672000..85f883168 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -2,9 +2,7 @@ import "./styles/globals.scss"; import "./styles/markdown.scss"; import "./styles/highlight.scss"; -import { getBuildConfig } from "./config/build"; - -const buildConfig = getBuildConfig(); +import { getClientConfig } from "./config/client"; export const metadata = { title: "SoulShellGPT", @@ -23,7 +21,7 @@ export default function RootLayout({ return ( - + diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 1fdd2d33b..beaef6d85 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -27,6 +27,19 @@ const cn = { Retry: "重试", Delete: "删除", }, + InputActions: { + Stop: "停止响应", + ToBottom: "滚到最新", + Theme: { + auto: "自动主题", + light: "亮色模式", + dark: "深色模式", + }, + Prompt: "快捷指令", + Masks: "所有面具", + Clear: "清除聊天", + Settings: "对话设置", + }, Rename: "重命名对话", Typing: "正在输入…", Input: (submitKey: string) => { @@ -102,6 +115,11 @@ const cn = { SubTitle: "聊天内容的字体大小", }, + InputTemplate: { + Title: "用户输入预处理", + SubTitle: "用户最新的一条消息会填充到此模板", + }, + Update: { Version: (x: string) => `当前版本:${x}`, IsLatest: "已是最新版本", @@ -167,6 +185,10 @@ const cn = { SubTitle: "管理员已开启加密访问", Placeholder: "请输入访问密码", }, + Endpoint: { + Title: "接口地址", + SubTitle: "除默认地址外,必须包含 http(s)://", + }, Model: "模型 (model)", Temperature: { Title: "随机性 (temperature)", @@ -180,6 +202,10 @@ const cn = { Title: "话题新鲜度 (presence_penalty)", SubTitle: "值越大,越有可能扩展到新话题", }, + FrequencyPenalty: { + Title: "频率惩罚度 (frequency_penalty)", + SubTitle: "值越大,越有可能降低重复字词", + }, }, Store: { DefaultTopic: "新的聊天", @@ -260,6 +286,12 @@ const cn = { Create: "新建", Edit: "编辑", }, + Exporter: { + Model: "模型", + Messages: "消息", + Topic: "主题", + Time: "时间", + }, }; type DeepPartial = T extends object @@ -267,7 +299,8 @@ type DeepPartial = T extends object [P in keyof T]?: DeepPartial; } : T; -export type LocaleType = DeepPartial; -export type RequiredLocaleType = typeof cn; + +export type LocaleType = typeof cn; +export type PartialLocaleType = DeepPartial; export default cn; diff --git a/app/locales/cs.ts b/app/locales/cs.ts index 734db37f5..e1706b726 100644 --- a/app/locales/cs.ts +++ b/app/locales/cs.ts @@ -1,7 +1,7 @@ import { SubmitKey } from "../store/config"; -import type { LocaleType } from "./index"; +import type { PartialLocaleType } from "./index"; -const cs: LocaleType = { +const cs: PartialLocaleType = { WIP: "V přípravě...", Error: { Unauthorized: @@ -155,6 +155,11 @@ const cs: LocaleType = { Title: "Přítomnostní korekce", SubTitle: "Větší hodnota zvyšuje pravděpodobnost nových témat.", }, + FrequencyPenalty: { + Title: "Frekvenční penalizace", + SubTitle: + "Větší hodnota snižující pravděpodobnost opakování stejného řádku", + }, }, Store: { DefaultTopic: "Nová konverzace", @@ -226,6 +231,12 @@ const cs: LocaleType = { Create: "Vytvořit", Edit: "Upravit", }, + Exporter: { + Model: "Model", + Messages: "Zprávy", + Topic: "Téma", + Time: "Čas", + }, }; export default cs; diff --git a/app/locales/de.ts b/app/locales/de.ts index 7b0ca5cca..30eb2b0db 100644 --- a/app/locales/de.ts +++ b/app/locales/de.ts @@ -1,7 +1,7 @@ import { SubmitKey } from "../store/config"; -import type { LocaleType } from "./index"; +import type { PartialLocaleType } from "./index"; -const de: LocaleType = { +const de: PartialLocaleType = { WIP: "In Bearbeitung...", Error: { Unauthorized: @@ -158,6 +158,11 @@ const de: LocaleType = { SubTitle: "Ein größerer Wert erhöht die Wahrscheinlichkeit, dass über neue Themen gesprochen wird", }, + FrequencyPenalty: { + Title: "Frequency Penalty", // HäufigkeitStrafe + SubTitle: + "Ein größerer Wert, der die Wahrscheinlichkeit verringert, dass dieselbe Zeile wiederholt wird", + }, }, Store: { DefaultTopic: "Neues Gespräch", @@ -231,6 +236,12 @@ const de: LocaleType = { Create: "Create", Edit: "Edit", }, + Exporter: { + Model: "Modell", + Messages: "Nachrichten", + Topic: "Thema", + Time: "Zeit", + }, }; export default de; diff --git a/app/locales/en.ts b/app/locales/en.ts index 954e1da7c..a7cf32bad 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -1,7 +1,7 @@ import { SubmitKey } from "../store/config"; -import { RequiredLocaleType } from "./index"; +import { LocaleType } from "./index"; -const en: RequiredLocaleType = { +const en: LocaleType = { WIP: "Coming Soon...", Error: { Unauthorized: @@ -28,6 +28,19 @@ const en: RequiredLocaleType = { Retry: "Retry", Delete: "Delete", }, + InputActions: { + Stop: "Stop", + ToBottom: "To Latest", + Theme: { + auto: "Auto", + light: "Light Theme", + dark: "Dark Theme", + }, + Prompt: "Prompts", + Masks: "Masks", + Clear: "Clear Context", + Settings: "Settings", + }, Rename: "Rename Chat", Typing: "Typing…", Input: (submitKey: string) => { @@ -103,6 +116,12 @@ const en: RequiredLocaleType = { Title: "Font Size", SubTitle: "Adjust font size of chat content", }, + + InputTemplate: { + Title: "Input Template", + SubTitle: "Newest message will be filled to this template", + }, + Update: { Version: (x: string) => `Version: ${x}`, IsLatest: "Latest version", @@ -168,6 +187,10 @@ const en: RequiredLocaleType = { SubTitle: "Access control enabled", Placeholder: "Need Access Code", }, + Endpoint: { + Title: "Endpoint", + SubTitle: "Custom endpoint must start with http(s)://", + }, Model: "Model", Temperature: { Title: "Temperature", @@ -182,6 +205,11 @@ const en: RequiredLocaleType = { SubTitle: "A larger value increases the likelihood to talk about new topics", }, + FrequencyPenalty: { + Title: "Frequency Penalty", + SubTitle: + "A larger value decreasing the likelihood to repeat the same line", + }, }, Store: { DefaultTopic: "New Conversation", @@ -263,6 +291,12 @@ const en: RequiredLocaleType = { Create: "Create", Edit: "Edit", }, + Exporter: { + Model: "Model", + Messages: "Messages", + Topic: "Topic", + Time: "Time", + }, }; export default en; diff --git a/app/locales/es.ts b/app/locales/es.ts index 15f731cbb..4f89208b5 100644 --- a/app/locales/es.ts +++ b/app/locales/es.ts @@ -1,7 +1,7 @@ import { SubmitKey } from "../store/config"; -import type { LocaleType } from "./index"; +import type { PartialLocaleType } from "./index"; -const es: LocaleType = { +const es: PartialLocaleType = { WIP: "En construcción...", Error: { Unauthorized: @@ -156,6 +156,11 @@ const es: LocaleType = { SubTitle: "Un valor mayor aumenta la probabilidad de hablar sobre nuevos temas", }, + FrequencyPenalty: { + Title: "Penalización de frecuencia", + SubTitle: + "Un valor mayor que disminuye la probabilidad de repetir la misma línea", + }, }, Store: { DefaultTopic: "Nueva conversación", @@ -228,6 +233,12 @@ const es: LocaleType = { Create: "Create", Edit: "Edit", }, + Exporter: { + Model: "Modelo", + Messages: "Mensajes", + Topic: "Tema", + Time: "Time", + }, }; export default es; diff --git a/app/locales/fr.ts b/app/locales/fr.ts index 046cebe64..db5e35448 100644 --- a/app/locales/fr.ts +++ b/app/locales/fr.ts @@ -1,7 +1,7 @@ import { SubmitKey } from "../store/config"; -import type { LocaleType } from "./index"; +import type { PartialLocaleType } from "./index"; -const fr: LocaleType = { +const fr: PartialLocaleType = { WIP: "Prochainement...", Error: { Unauthorized: @@ -159,6 +159,11 @@ const fr: LocaleType = { SubTitle: "Une valeur plus élevée augmentera la probabilité d'introduire de nouveaux sujets", }, + FrequencyPenalty: { + Title: "Pénalité de fréquence", + SubTitle: + "Une valeur plus élevée diminuant la probabilité de répéter la même ligne", + }, }, Store: { DefaultTopic: "Nouvelle conversation", @@ -232,6 +237,12 @@ const fr: LocaleType = { Create: "Créer", Edit: "Éditer", }, + Exporter: { + Model: "Modèle", + Messages: "Messages", + Topic: "Sujet", + Time: "Temps", + }, }; export default fr; diff --git a/app/locales/index.ts b/app/locales/index.ts index 22c417da5..38d54770e 100644 --- a/app/locales/index.ts +++ b/app/locales/index.ts @@ -1,56 +1,71 @@ -import CN from "./cn"; -import EN from "./en"; -import TW from "./tw"; -import FR from "./fr"; -import ES from "./es"; -import IT from "./it"; -import TR from "./tr"; -import JP from "./jp"; -import DE from "./de"; -import VI from "./vi"; -import RU from "./ru"; -import CS from "./cs"; -import KO from "./ko"; +import cn from "./cn"; +import en from "./en"; +import tw from "./tw"; +import fr from "./fr"; +import es from "./es"; +import it from "./it"; +import tr from "./tr"; +import jp from "./jp"; +import de from "./de"; +import vi from "./vi"; +import ru from "./ru"; +import no from "./no"; +import cs from "./cs"; +import ko from "./ko"; import { merge } from "../utils/merge"; -export type { LocaleType, RequiredLocaleType } from "./cn"; +import type { LocaleType } from "./cn"; +export type { LocaleType, PartialLocaleType } from "./cn"; -export const AllLangs = [ - "en", - "cn", - "tw", - "fr", - "es", - "it", - "tr", - "jp", - "de", - "vi", - "ru", - "cs", - "ko", -] as const; -export type Lang = (typeof AllLangs)[number]; +const ALL_LANGS = { + cn, + en, + tw, + jp, + ko, + fr, + es, + it, + tr, + de, + vi, + ru, + cs, + no, +}; + +export type Lang = keyof typeof ALL_LANGS; + +export const AllLangs = Object.keys(ALL_LANGS) as Lang[]; export const ALL_LANG_OPTIONS: Record = { cn: "简体中文", en: "English", tw: "繁體中文", + jp: "日本語", + ko: "한국어", fr: "Français", es: "Español", it: "Italiano", tr: "Türkçe", - jp: "日本語", de: "Deutsch", vi: "Tiếng Việt", ru: "Русский", cs: "Čeština", - ko: "한국어", + no: "Nynorsk", }; const LANG_KEY = "lang"; const DEFAULT_LANG = "en"; +const fallbackLang = en; +const targetLang = ALL_LANGS[getLang()] as LocaleType; + +// if target lang missing some fields, it will use fallback lang string +merge(fallbackLang, targetLang); + +export default fallbackLang as LocaleType; + function getItem(key: string) { try { return localStorage.getItem(key); @@ -69,7 +84,6 @@ function getLanguage() { try { return navigator.language.toLowerCase(); } catch { - console.log("[Lang] failed to detect user lang."); return DEFAULT_LANG; } } @@ -96,25 +110,3 @@ export function changeLang(lang: Lang) { setItem(LANG_KEY, lang); location.reload(); } - -const fallbackLang = EN; -const targetLang = { - en: EN, - cn: CN, - tw: TW, - fr: FR, - es: ES, - it: IT, - tr: TR, - jp: JP, - de: DE, - vi: VI, - ru: RU, - cs: CS, - ko: KO, -}[getLang()] as typeof CN; - -// if target lang missing some fields, it will use fallback lang string -merge(fallbackLang, targetLang); - -export default fallbackLang as typeof CN; diff --git a/app/locales/it.ts b/app/locales/it.ts index 0d87588fd..72206754b 100644 --- a/app/locales/it.ts +++ b/app/locales/it.ts @@ -1,7 +1,7 @@ import { SubmitKey } from "../store/config"; -import type { LocaleType } from "./index"; +import type { PartialLocaleType } from "./index"; -const it: LocaleType = { +const it: PartialLocaleType = { WIP: "Work in progress...", Error: { Unauthorized: @@ -157,6 +157,11 @@ const it: LocaleType = { SubTitle: "Un valore maggiore aumenta la probabilità di parlare di nuovi argomenti", }, + FrequencyPenalty: { + Title: "Penalità di frequenza", + SubTitle: + "Un valore maggiore che diminuisce la probabilità di ripetere la stessa riga", + }, }, Store: { DefaultTopic: "Nuova conversazione", @@ -229,6 +234,12 @@ const it: LocaleType = { Create: "Create", Edit: "Edit", }, + Exporter: { + Model: "Modello", + Messages: "Messaggi", + Topic: "Argomento", + Time: "Tempo", + }, }; export default it; diff --git a/app/locales/jp.ts b/app/locales/jp.ts index 8002d0450..02a0bf864 100644 --- a/app/locales/jp.ts +++ b/app/locales/jp.ts @@ -1,11 +1,11 @@ import { SubmitKey } from "../store/config"; -import type { LocaleType } from "./index"; +import type { PartialLocaleType } from "./index"; -const jp: LocaleType = { - WIP: "この機能は開発中です……", +const jp: PartialLocaleType = { + WIP: "この機能は開発中です", Error: { Unauthorized: - "現在は未承認状態です。左下の設定ボタンをクリックし、アクセスパスワードを入力してください。", + "現在は未承認状態です。左下の設定ボタンをクリックし、アクセスパスワードかOpenAIのAPIキーを入力してください。", }, ChatItem: { ChatItemCount: (count: number) => `${count} 通のチャット`, @@ -19,7 +19,7 @@ const jp: LocaleType = { Copy: "コピー", Stop: "停止", Retry: "リトライ", - Delete: "Delete", + Delete: "削除", }, Rename: "チャットの名前を変更", Typing: "入力中…", @@ -32,7 +32,7 @@ const jp: LocaleType = { }, Send: "送信", Config: { - Reset: "重置默认", + Reset: "リセット", SaveAs: "另存为面具", }, }, @@ -70,7 +70,7 @@ const jp: LocaleType = { }, Lang: { Name: "Language", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language` - All: "所有语言", + All: "全ての言語", }, Avatar: "アバター", FontSize: { @@ -91,11 +91,11 @@ const jp: LocaleType = { TightBorder: "ボーダーレスモード", SendPreviewBubble: { Title: "プレビューバブルの送信", - SubTitle: "在预览气泡中预览 Markdown 内容", + SubTitle: "プレビューバブルでマークダウンコンテンツをプレビュー", }, Mask: { - Title: "面具启动页", - SubTitle: "新建聊天时,展示面具启动页", + Title: "キャラクターページ", + SubTitle: "新規チャット作成時にキャラクターページを表示する", }, Prompt: { Disable: { @@ -113,7 +113,7 @@ const jp: LocaleType = { Search: "プロンプトワード検索", }, EditModal: { - Title: "编辑提示词", + Title: "編集", }, }, HistoryCount: { @@ -158,6 +158,10 @@ const jp: LocaleType = { Title: "トピックの新鮮度 (presence_penalty)", SubTitle: "値が大きいほど、新しいトピックへの展開が可能になります。", }, + FrequencyPenalty: { + Title: "話題の頻度 (frequency_penalty)", + SubTitle: "値が大きいほど、重複語を低減する可能性が高くなります", + }, }, Store: { DefaultTopic: "新しいチャット", @@ -178,54 +182,70 @@ const jp: LocaleType = { Failed: "コピーに失敗しました。クリップボード許可を与えてください。", }, Context: { - Toast: (x: any) => `前置コンテキストが ${x} 件設定されました`, - Edit: "前置コンテキストと履歴メモリ", - Add: "新規追加", + Toast: (x: any) => `キャラクターが ${x} 件設定されました`, + Edit: "キャラクタープリセットとモデル設定", + Add: "追加", }, - Plugin: { Name: "插件" }, + Plugin: { Name: "プラグイン" }, Mask: { - Name: "面具", + Name: "キャラクタープリセット", Page: { - Title: "预设角色面具", - SubTitle: (count: number) => `${count} 个预设角色定义`, - Search: "搜索角色面具", - Create: "新建", + Title: "キャラクタープリセット", + SubTitle: (count: number) => `${count} 件見つかりました。`, + Search: "検索", + Create: "新規", }, Item: { Info: (count: number) => `包含 ${count} 条预设对话`, - Chat: "对话", - View: "查看", - Edit: "编辑", - Delete: "删除", - DeleteConfirm: "确认删除?", + Chat: "会話", + View: "詳細", + Edit: "編集", + Delete: "削除", + DeleteConfirm: "本当に削除しますか?", }, EditModal: { Title: (readonly: boolean) => - `编辑预设面具 ${readonly ? "(只读)" : ""}`, - Download: "下载预设", - Clone: "克隆预设", + `キャラクタープリセットを編集 ${readonly ? "(読み取り専用)" : ""}`, + Download: "ダウンロード", + Clone: "複製", }, Config: { - Avatar: "角色头像", - Name: "角色名称", + Avatar: "キャラクターのアイコン", + Name: "キャラクターの名前", + Sync: { + Title: "グローバル設定を利用する", + SubTitle: "このチャットでグローバル設定を利用します。", + Confirm: + "カスタム設定を上書きしてグローバル設定を使用します、よろしいですか?", + }, + HideContext: { + Title: "キャラクター設定を表示しない", + SubTitle: "チャット画面でのキャラクター設定を非表示にします。", + }, }, }, NewChat: { - Return: "返回", - Skip: "跳过", - Title: "挑选一个面具", - SubTitle: "现在开始,与面具背后的灵魂思维碰撞", - More: "搜索更多", - NotShow: "不再展示", - ConfirmNoShow: "确认禁用?禁用后可以随时在设置中重新启用。", + Return: "戻る", + Skip: "スキップ", + Title: "キャラクター", + SubTitle: "さあ、AIにキャラクターを設定して会話を始めてみましょう", + More: "もっと探す", + NotShow: "今後は表示しない", + ConfirmNoShow: "いつでも設定から有効化できます。", }, UI: { - Confirm: "确认", - Cancel: "取消", - Close: "关闭", - Create: "新建", - Edit: "编辑", + Confirm: "確認", + Cancel: "キャンセル", + Close: "閉じる", + Create: "新規", + Edit: "編集", + }, + Exporter: { + Model: "モデル", + Messages: "メッセージ", + Topic: "トピック", + Time: "時間", }, }; diff --git a/app/locales/ko.ts b/app/locales/ko.ts index 12b8db281..8985fcfb5 100644 --- a/app/locales/ko.ts +++ b/app/locales/ko.ts @@ -1,8 +1,8 @@ import { SubmitKey } from "../store/config"; -import type { LocaleType } from "./index"; +import type { PartialLocaleType } from "./index"; -const ko: LocaleType = { +const ko: PartialLocaleType = { WIP: "곧 출시 예정...", Error: { Unauthorized: "권한이 없습니다. 설정 페이지에서 액세스 코드를 입력하세요.", @@ -154,6 +154,10 @@ const ko: LocaleType = { Title: "존재 페널티 (presence_penalty)", SubTitle: "값이 클수록 새로운 주제에 대해 대화할 가능성이 높아집니다.", }, + FrequencyPenalty: { + Title: "빈도 페널티(frequency penalty)", + SubTitle: "값이 클수록 같은 줄이 반복될 가능성이 줄어듭니다.", + }, }, Store: { DefaultTopic: "새 대화", @@ -225,6 +229,12 @@ const ko: LocaleType = { Create: "생성", Edit: "편집", }, + Exporter: { + Model: "모델", + Messages: "메시지", + Topic: "주제", + Time: "시간", + }, }; export default ko; diff --git a/app/locales/no.ts b/app/locales/no.ts new file mode 100644 index 000000000..f46a454fb --- /dev/null +++ b/app/locales/no.ts @@ -0,0 +1,168 @@ +import { SubmitKey } from "../store/config"; +import type { PartialLocaleType } from "./index"; + +const no: PartialLocaleType = { + WIP: "Arbeid pågår ...", + Error: { + Unauthorized: "Du har ikke tilgang. Vennlig oppgi tildelt adgangskode.", + }, + ChatItem: { + ChatItemCount: (count: number) => `${count} meldinger`, + }, + Chat: { + SubTitle: (count: number) => `${count} meldinger med ChatGPT`, + Actions: { + ChatList: "Gå til chatlisten", + CompressedHistory: "Komprimert historikk for instrukser", + Export: "Eksporter alle meldinger i markdown-format", + Copy: "Kopier", + Stop: "Stopp", + Retry: "Prøv igjen", + Delete: "Slett", + }, + Rename: "Gi nytt navn", + Typing: "Skriver …", + Input: (submitKey: string) => { + var inputHints = `${submitKey} for å sende`; + if (submitKey === String(SubmitKey.Enter)) { + inputHints += ", Shift + Enter for å omgi"; + } + return inputHints + ", / for å søke instrukser"; + }, + Send: "Send", + }, + Export: { + Title: "Alle meldinger", + Copy: "Kopiere alle", + Download: "Last ned", + MessageFromYou: "Melding fra deg", + MessageFromChatGPT: "Melding fra ChatGPT", + }, + Memory: { + Title: "Minneinstruks", + EmptyContent: "Ingen sålant.", + Send: "Send minne", + Copy: "Kopiere minne", + Reset: "Nulstill sesjon", + ResetConfirm: + "Om du nillstiller vil du slette hele historikken. Er du sikker på at du vil nullstille?", + }, + Home: { + NewChat: "Ny chat", + DeleteChat: "Bekreft for å slette det valgte dialogen", + DeleteToast: "Samtale slettet", + Revert: "Tilbakestill", + }, + Settings: { + Title: "Innstillinger", + SubTitle: "Alle innstillinger", + Actions: { + ClearAll: "Fjern alle data", + ResetAll: "Nullstill innstillinger", + Close: "Lukk", + }, + Lang: { + Name: "Language", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language` + }, + Avatar: "Avatar", + FontSize: { + Title: "Fontstørrelsen", + SubTitle: "Juster fontstørrelsen for samtaleinnholdet.", + }, + Update: { + Version: (x: string) => `Versjon: ${x}`, + IsLatest: "Siste versjon", + CheckUpdate: "Se etter oppdatering", + IsChecking: "Ser etter oppdatering ...", + FoundUpdate: (x: string) => `Fant ny versjon: ${x}`, + GoToUpdate: "Oppdater", + }, + SendKey: "Send nøkkel", + Theme: "Tema", + TightBorder: "Stram innramming", + Prompt: { + Disable: { + Title: "Skru av autofullfør", + SubTitle: "Skriv / for å trigge autofullfør", + }, + List: "Instruksliste", + ListCount: (builtin: number, custom: number) => + `${builtin} innebygde, ${custom} brukerdefinerte`, + Edit: "Endre", + Modal: { + Title: "Instruksliste", + Add: "Legg til", + Search: "Søk instrukser", + }, + }, + HistoryCount: { + Title: "Tall på tilhørende meldinger", + SubTitle: "Antall sendte meldinger tilknyttet hver spørring", + }, + CompressThreshold: { + Title: "Terskeverdi for komprimering av historikk", + SubTitle: + "Komprimer dersom ikke-komprimert lengde på meldinger overskrider denne verdien", + }, + Token: { + Title: "API Key", + SubTitle: + "Bruk din egen API-nøkkel for å ignorere tilgangskoden begrensning", + Placeholder: "OpenAI API-nøkkel", + }, + Usage: { + Title: "Saldo for konto", + SubTitle(used: any, total: any) { + return `Brukt denne måneden $${used}, abonnement $${total}`; + }, + IsChecking: "Sjekker ...", + Check: "Sjekk", + NoAccess: "Skriv inn API-nøkkelen for å sjekke saldo", + }, + AccessCode: { + Title: "Tilgangskode", + SubTitle: "Tilgangskontroll på", + Placeholder: "Trenger tilgangskode", + }, + Model: "Model", + Temperature: { + Title: "Temperatur", + SubTitle: "Høyere verdi gir mer kreative svar", + }, + MaxTokens: { + Title: "Maks tokens", + SubTitle: "Maksimum lengde på tokens for instrukser og svar", + }, + }, + Store: { + DefaultTopic: "Ny samtale", + BotHello: "Hei! Hva kan jeg hjelpe deg med i dag?", + Error: "Noe gikk galt, vennligst prøv igjen senere.", + Prompt: { + History: (content: string) => + "Dette er et sammendrag av chatthistorikken mellom AI-en og brukeren som en oppsummering: " + + content, + Topic: + "Vennligst lag en fire til fem ords tittel som oppsummerer samtalen vår uten innledning, punktsetting, anførselstegn, punktum, symboler eller tillegg tekst. Fjern innrammende anførselstegn.", + Summarize: + "Oppsummer diskusjonen vår kort i 200 ord eller mindre for å bruke som en oppfordring til fremtidig sammenheng.", + }, + }, + Copy: { + Success: "Kopiert til utklippstavle", + Failed: "Kopiering feilet. Vennligst gi tilgang til utklippstavlen.", + }, + Context: { + Toast: (x: any) => `Med ${x} kontekstuelle instrukser`, + Edit: "Kontekstuelle -og minneinstrukser", + Add: "Legg til", + }, + Exporter: { + Model: "Model", + Messages: "Meldingar", + Topic: "Emne", + Time: "Tid", + }, +}; + +export default no; diff --git a/app/locales/ru.ts b/app/locales/ru.ts index 3993ea43f..fcc494c00 100644 --- a/app/locales/ru.ts +++ b/app/locales/ru.ts @@ -1,7 +1,7 @@ import { SubmitKey } from "../store/config"; -import type { LocaleType } from "./index"; +import type { PartialLocaleType } from "./index"; -const ru: LocaleType = { +const ru: PartialLocaleType = { WIP: "Скоро...", Error: { Unauthorized: @@ -157,6 +157,11 @@ const ru: LocaleType = { SubTitle: "Чем выше значение, тем больше вероятность общения на новые темы", }, + FrequencyPenalty: { + Title: "Штраф за частоту", + SubTitle: + "Большее значение снижает вероятность повторения одной и той же строки", + }, }, Store: { DefaultTopic: "Новый разговор", @@ -232,6 +237,12 @@ const ru: LocaleType = { Create: "Создать", Edit: "Редактировать", }, + Exporter: { + Model: "Модель", + Messages: "Сообщения", + Topic: "Тема", + Time: "Время", + }, }; export default ru; diff --git a/app/locales/tr.ts b/app/locales/tr.ts index e26091fef..fb49b64c8 100644 --- a/app/locales/tr.ts +++ b/app/locales/tr.ts @@ -1,7 +1,7 @@ import { SubmitKey } from "../store/config"; -import type { LocaleType } from "./index"; +import type { PartialLocaleType } from "./index"; -const tr: LocaleType = { +const tr: PartialLocaleType = { WIP: "Çalışma devam ediyor...", Error: { Unauthorized: @@ -158,6 +158,11 @@ const tr: LocaleType = { SubTitle: "Daha büyük bir değer, yeni konular hakkında konuşma olasılığını artırır", }, + FrequencyPenalty: { + Title: "Frekans Cezası", + SubTitle: + "Aynı satırı tekrar etme olasılığını azaltan daha büyük bir değer", + }, }, Store: { DefaultTopic: "Yeni Konuşma", @@ -229,6 +234,12 @@ const tr: LocaleType = { Create: "Create", Edit: "Edit", }, + Exporter: { + Model: "Model", + Messages: "Mesajlar", + Topic: "Konu", + Time: "Zaman", + }, }; export default tr; diff --git a/app/locales/tw.ts b/app/locales/tw.ts index ecabe14c2..3fc77f8f7 100644 --- a/app/locales/tw.ts +++ b/app/locales/tw.ts @@ -1,7 +1,7 @@ import { SubmitKey } from "../store/config"; -import type { LocaleType } from "./index"; +import type { PartialLocaleType } from "./index"; -const tw: LocaleType = { +const tw: PartialLocaleType = { WIP: "該功能仍在開發中……", Error: { Unauthorized: "目前您的狀態是未授權,請前往設定頁面輸入授權碼。", @@ -152,6 +152,10 @@ const tw: LocaleType = { Title: "話題新穎度 (presence_penalty)", SubTitle: "值越大,越有可能擴展到新話題", }, + FrequencyPenalty: { + Title: "頻率懲罰度 (frequency_penalty)", + SubTitle: "值越大,越有可能降低重複字詞", + }, }, Store: { DefaultTopic: "新的對話", @@ -219,6 +223,12 @@ const tw: LocaleType = { Create: "新建", Edit: "编辑", }, + Exporter: { + Model: "模型", + Messages: "消息", + Topic: "主題", + Time: "時間", + }, }; export default tw; diff --git a/app/locales/vi.ts b/app/locales/vi.ts index 78eeaf408..fea4545d2 100644 --- a/app/locales/vi.ts +++ b/app/locales/vi.ts @@ -1,7 +1,7 @@ import { SubmitKey } from "../store/config"; -import type { LocaleType } from "./index"; +import type { PartialLocaleType } from "./index"; -const vi: LocaleType = { +const vi: PartialLocaleType = { WIP: "Sắp ra mắt...", Error: { Unauthorized: @@ -154,6 +154,10 @@ const vi: LocaleType = { 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", }, + FrequencyPenalty: { + Title: "Hình phạt tần suất", + SubTitle: "Giá trị lớn hơn làm giảm khả năng lặp lại cùng một dòng", + }, }, Store: { DefaultTopic: "Cuộc trò chuyện mới", @@ -225,6 +229,12 @@ const vi: LocaleType = { Create: "Tạo", Edit: "Chỉnh sửa", }, + Exporter: { + Model: "Mô hình", + Messages: "Thông điệp", + Topic: "Chủ đề", + Time: "Thời gian", + }, }; export default vi; diff --git a/app/masks/cn.ts b/app/masks/cn.ts index 55859afe9..c0feda535 100644 --- a/app/masks/cn.ts +++ b/app/masks/cn.ts @@ -29,6 +29,7 @@ export const CN_MASKS: BuiltinMask[] = [ temperature: 1, max_tokens: 2000, presence_penalty: 0, + frequency_penalty: 0, sendMemory: true, historyMessageCount: 32, compressMessageLengthThreshold: 1000, @@ -52,6 +53,7 @@ export const CN_MASKS: BuiltinMask[] = [ temperature: 1, max_tokens: 2000, presence_penalty: 0, + frequency_penalty: 0, sendMemory: true, historyMessageCount: 4, compressMessageLengthThreshold: 1000, @@ -75,6 +77,7 @@ export const CN_MASKS: BuiltinMask[] = [ temperature: 1, max_tokens: 2000, presence_penalty: 0, + frequency_penalty: 0, sendMemory: true, historyMessageCount: 4, compressMessageLengthThreshold: 1000, @@ -98,6 +101,7 @@ export const CN_MASKS: BuiltinMask[] = [ temperature: 1, max_tokens: 2000, presence_penalty: 0, + frequency_penalty: 0, sendMemory: true, historyMessageCount: 4, compressMessageLengthThreshold: 1000, @@ -121,6 +125,7 @@ export const CN_MASKS: BuiltinMask[] = [ temperature: 1, max_tokens: 2000, presence_penalty: 0, + frequency_penalty: 0, sendMemory: true, historyMessageCount: 4, compressMessageLengthThreshold: 1000, @@ -144,6 +149,7 @@ export const CN_MASKS: BuiltinMask[] = [ temperature: 1, max_tokens: 2000, presence_penalty: 0, + frequency_penalty: 0, sendMemory: false, historyMessageCount: 4, compressMessageLengthThreshold: 1000, @@ -167,6 +173,7 @@ export const CN_MASKS: BuiltinMask[] = [ temperature: 1, max_tokens: 2000, presence_penalty: 0, + frequency_penalty: 0, sendMemory: false, historyMessageCount: 4, compressMessageLengthThreshold: 1000, @@ -190,6 +197,7 @@ export const CN_MASKS: BuiltinMask[] = [ temperature: 1, max_tokens: 2000, presence_penalty: 0, + frequency_penalty: 0, sendMemory: false, historyMessageCount: 0, compressMessageLengthThreshold: 1000, @@ -218,6 +226,7 @@ export const CN_MASKS: BuiltinMask[] = [ temperature: 0.5, max_tokens: 2000, presence_penalty: 0, + frequency_penalty: 0, sendMemory: true, historyMessageCount: 4, compressMessageLengthThreshold: 1000, @@ -241,6 +250,7 @@ export const CN_MASKS: BuiltinMask[] = [ temperature: 1, max_tokens: 2000, presence_penalty: 0, + frequency_penalty: 0, sendMemory: true, historyMessageCount: 4, compressMessageLengthThreshold: 1000, @@ -270,6 +280,7 @@ export const CN_MASKS: BuiltinMask[] = [ temperature: 1, max_tokens: 2000, presence_penalty: 0, + frequency_penalty: 0, sendMemory: false, historyMessageCount: 4, compressMessageLengthThreshold: 1000, @@ -299,6 +310,7 @@ export const CN_MASKS: BuiltinMask[] = [ temperature: 1, max_tokens: 2000, presence_penalty: 0, + frequency_penalty: 0, sendMemory: false, historyMessageCount: 4, compressMessageLengthThreshold: 1000, @@ -349,6 +361,7 @@ export const CN_MASKS: BuiltinMask[] = [ temperature: 1, max_tokens: 2000, presence_penalty: 0, + frequency_penalty: 0, sendMemory: false, historyMessageCount: 4, compressMessageLengthThreshold: 1000, @@ -377,6 +390,7 @@ export const CN_MASKS: BuiltinMask[] = [ temperature: 0.5, max_tokens: 2000, presence_penalty: 0, + frequency_penalty: 0, sendMemory: true, historyMessageCount: 4, compressMessageLengthThreshold: 1000, diff --git a/app/masks/en.ts b/app/masks/en.ts index 01c1d31f6..31223cea2 100644 --- a/app/masks/en.ts +++ b/app/masks/en.ts @@ -17,6 +17,7 @@ export const EN_MASKS: BuiltinMask[] = [ temperature: 0.3, max_tokens: 2000, presence_penalty: 0, + frequency_penalty: 0, sendMemory: true, historyMessageCount: 4, compressMessageLengthThreshold: 1000, @@ -57,6 +58,7 @@ export const EN_MASKS: BuiltinMask[] = [ temperature: 1, max_tokens: 2000, presence_penalty: 0, + frequency_penalty: 0, sendMemory: true, historyMessageCount: 4, compressMessageLengthThreshold: 1000, @@ -80,6 +82,7 @@ export const EN_MASKS: BuiltinMask[] = [ temperature: 0.5, max_tokens: 2000, presence_penalty: 0, + frequency_penalty: 0, sendMemory: true, historyMessageCount: 4, compressMessageLengthThreshold: 1000, @@ -108,6 +111,7 @@ export const EN_MASKS: BuiltinMask[] = [ temperature: 0.5, max_tokens: 2000, presence_penalty: 0, + frequency_penalty: 0, sendMemory: true, historyMessageCount: 4, compressMessageLengthThreshold: 2000, diff --git a/app/masks/index.ts b/app/masks/index.ts index 07c6a3e8c..b9cb23f20 100644 --- a/app/masks/index.ts +++ b/app/masks/index.ts @@ -9,7 +9,7 @@ export const BUILTIN_MASK_ID = 100000; export const BUILTIN_MASK_STORE = { buildinId: BUILTIN_MASK_ID, - masks: {} as Record, + masks: {} as Record, get(id?: number) { if (!id) return undefined; return this.masks[id] as Mask | undefined; @@ -21,6 +21,6 @@ export const BUILTIN_MASK_STORE = { }, }; -export const BUILTIN_MASKS: Mask[] = [...CN_MASKS, ...EN_MASKS].map((m) => - BUILTIN_MASK_STORE.add(m), +export const BUILTIN_MASKS: BuiltinMask[] = [...CN_MASKS, ...EN_MASKS].map( + (m) => BUILTIN_MASK_STORE.add(m), ); diff --git a/app/masks/typing.ts b/app/masks/typing.ts index 510d94a2c..1ded6a902 100644 --- a/app/masks/typing.ts +++ b/app/masks/typing.ts @@ -1,5 +1,7 @@ +import { ModelConfig } from "../store"; import { type Mask } from "../store/mask"; -export type BuiltinMask = Omit & { - builtin: true; +export type BuiltinMask = Omit & { + builtin: Boolean; + modelConfig: Partial; }; diff --git a/app/store/access.ts b/app/store/access.ts index 91049846b..0601903d3 100644 --- a/app/store/access.ts +++ b/app/store/access.ts @@ -1,9 +1,10 @@ import { create } from "zustand"; import { persist } from "zustand/middleware"; -import { StoreKey } from "../constant"; +import { DEFAULT_API_HOST, StoreKey } from "../constant"; import { getHeaders } from "../client/api"; import { BOT_HELLO } from "./chat"; import { ALL_MODELS } from "./config"; +import { getClientConfig } from "../config/client"; export interface AccessControlStore { accessCode: string; @@ -15,6 +16,7 @@ export interface AccessControlStore { updateToken: (_: string) => void; updateCode: (_: string) => void; + updateOpenAiUrl: (_: string) => void; enabledAccessControl: () => boolean; isAuthorized: () => boolean; fetch: () => void; @@ -22,6 +24,10 @@ export interface AccessControlStore { let fetchState = 0; // 0 not fetch, 1 fetching, 2 done +const DEFAULT_OPENAI_URL = + getClientConfig()?.buildMode === "export" ? DEFAULT_API_HOST : "/api/openai/"; +console.log("[API] default openai url", DEFAULT_OPENAI_URL); + export const useAccessStore = create()( persist( (set, get) => ({ @@ -29,7 +35,7 @@ export const useAccessStore = create()( accessCode: "", needCode: true, hideUserApiKey: false, - openaiUrl: "/api/openai/", + openaiUrl: DEFAULT_OPENAI_URL, enabledAccessControl() { get().fetch(); @@ -42,6 +48,9 @@ export const useAccessStore = create()( updateToken(token: string) { set(() => ({ token })); }, + updateOpenAiUrl(url: string) { + set(() => ({ openaiUrl: url })); + }, isAuthorized() { get().fetch(); @@ -51,7 +60,7 @@ export const useAccessStore = create()( ); }, fetch() { - if (fetchState > 0) return; + if (fetchState > 0 || getClientConfig()?.buildMode === "export") return; fetchState = 1; fetch("/api/config", { method: "post", diff --git a/app/store/chat.ts b/app/store/chat.ts index 3c9953e58..d311c88ff 100644 --- a/app/store/chat.ts +++ b/app/store/chat.ts @@ -3,14 +3,15 @@ import { persist } from "zustand/middleware"; import { trimTopic } from "../utils"; -import Locale from "../locales"; +import Locale, { getLang } from "../locales"; import { showToast } from "../components/ui-lib"; -import { ModelType } from "./config"; +import { ModelConfig, ModelType, useAppConfig } from "./config"; import { createEmptyMask, Mask } from "./mask"; -import { StoreKey } from "../constant"; +import { DEFAULT_INPUT_TEMPLATE, StoreKey } from "../constant"; import { api, RequestMessage } from "../client/api"; import { ChatControllerPool } from "../client/controller"; import { prettyObject } from "../utils/format"; +import { estimateTokenLength } from "../utils/token"; export type ChatMessage = RequestMessage & { date: string; @@ -102,7 +103,30 @@ interface ChatStore { } function countMessages(msgs: ChatMessage[]) { - return msgs.reduce((pre, cur) => pre + cur.content.length, 0); + return msgs.reduce((pre, cur) => pre + estimateTokenLength(cur.content), 0); +} + +function fillTemplateWith(input: string, modelConfig: ModelConfig) { + const vars = { + model: modelConfig.model, + time: new Date().toLocaleString(), + lang: getLang(), + input: input, + }; + + let output = modelConfig.template ?? DEFAULT_INPUT_TEMPLATE; + + // must contains {{input}} + const inputVar = "{{input}}"; + if (!output.includes(inputVar)) { + output += "\n" + inputVar; + } + + Object.entries(vars).forEach(([name, value]) => { + output = output.replaceAll(`{{${name}}}`, value); + }); + + return output; } export const useChatStore = create()( @@ -157,7 +181,16 @@ export const useChatStore = create()( session.id = get().globalId; if (mask) { - session.mask = { ...mask }; + const config = useAppConfig.getState(); + const globalModelConfig = config.modelConfig; + + session.mask = { + ...mask, + modelConfig: { + ...globalModelConfig, + ...mask.modelConfig, + }, + }; session.topic = mask.name; } @@ -226,6 +259,7 @@ export const useChatStore = create()( onNewMessage(message) { get().updateCurrentSession((session) => { + session.messages = session.messages.concat(); session.lastUpdate = Date.now(); }); get().updateStat(message); @@ -236,9 +270,12 @@ export const useChatStore = create()( const session = get().currentSession(); const modelConfig = session.mask.modelConfig; + const userContent = fillTemplateWith(content, modelConfig); + console.log("[User Input] fill with template: ", userContent); + const userMessage: ChatMessage = createMessage({ role: "user", - content, + content: userContent, }); const botMessage: ChatMessage = createMessage({ @@ -248,32 +285,22 @@ export const useChatStore = create()( model: modelConfig.model, }); - const systemInfo = createMessage({ - role: "system", - content: `IMPORTANT: 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 = []; - // if user define a mask with context prompts, wont send system info - if (session.mask.context.length === 0) { - systemMessages.push(systemInfo); - } - const recentMessages = get().getMessagesWithMemory(); - const sendMessages = systemMessages.concat( - recentMessages.concat(userMessage), - ); + const sendMessages = recentMessages.concat(userMessage); const sessionIndex = get().currentSessionIndex; const messageIndex = get().currentSession().messages.length + 1; // save user's and bot's message get().updateCurrentSession((session) => { - session.messages.push(userMessage); - session.messages.push(botMessage); + const savedUserMessage = { + ...userMessage, + content, + }; + session.messages = session.messages.concat([ + savedUserMessage, + botMessage, + ]); }); // make request @@ -286,7 +313,9 @@ export const useChatStore = create()( if (message) { botMessage.content = message; } - set(() => ({})); + get().updateCurrentSession((session) => { + session.messages = session.messages.concat(); + }); }, onFinish(message) { botMessage.streaming = false; @@ -298,7 +327,6 @@ export const useChatStore = create()( sessionIndex, botMessage.id ?? messageIndex, ); - set(() => ({})); }, onError(error) { const isAborted = error.message.includes("aborted"); @@ -311,8 +339,9 @@ export const useChatStore = create()( botMessage.streaming = false; userMessage.isError = !isAborted; botMessage.isError = !isAborted; - - set(() => ({})); + get().updateCurrentSession((session) => { + session.messages = session.messages.concat(); + }); ChatControllerPool.remove( sessionIndex, botMessage.id ?? messageIndex, @@ -347,53 +376,62 @@ export const useChatStore = create()( getMessagesWithMemory() { const session = get().currentSession(); const modelConfig = session.mask.modelConfig; + const clearContextIndex = session.clearContextIndex ?? 0; + const messages = session.messages.slice(); + const totalMessageCount = session.messages.length; - // wont send cleared context messages - const clearedContextMessages = session.messages.slice( - session.clearContextIndex ?? 0, - ); - const messages = clearedContextMessages.filter((msg) => !msg.isError); - const n = messages.length; - - const context = session.mask.context.slice(); + // in-context prompts + const contextPrompts = session.mask.context.slice(); // long term memory - if ( + const shouldSendLongTermMemory = modelConfig.sendMemory && session.memoryPrompt && - session.memoryPrompt.length > 0 - ) { - const memoryPrompt = get().getMemoryPrompt(); - context.push(memoryPrompt); - } + session.memoryPrompt.length > 0 && + session.lastSummarizeIndex <= clearContextIndex; + const longTermMemoryPrompts = shouldSendLongTermMemory + ? [get().getMemoryPrompt()] + : []; + const longTermMemoryStartIndex = session.lastSummarizeIndex; - // get short term and unmemoried long term memory - const shortTermMemoryMessageIndex = Math.max( + // short term memory + const shortTermMemoryStartIndex = Math.max( 0, - n - modelConfig.historyMessageCount, + totalMessageCount - modelConfig.historyMessageCount, ); - const longTermMemoryMessageIndex = session.lastSummarizeIndex; - const mostRecentIndex = Math.max( - shortTermMemoryMessageIndex, - longTermMemoryMessageIndex, - ); - const threshold = modelConfig.compressMessageLengthThreshold * 2; - // get recent messages as many as possible + // lets concat send messages, including 4 parts: + // 1. long term memory: summarized memory messages + // 2. pre-defined in-context prompts + // 3. short term memory: latest n messages + // 4. newest input message + const memoryStartIndex = Math.min( + longTermMemoryStartIndex, + shortTermMemoryStartIndex, + ); + // and if user has cleared history messages, we should exclude the memory too. + const contextStartIndex = Math.max(clearContextIndex, memoryStartIndex); + const maxTokenThreshold = modelConfig.max_tokens; + + // get recent messages as much as possible const reversedRecentMessages = []; for ( - let i = n - 1, count = 0; - i >= mostRecentIndex && count < threshold; + let i = totalMessageCount - 1, tokenCount = 0; + i >= contextStartIndex && tokenCount < maxTokenThreshold; i -= 1 ) { const msg = messages[i]; if (!msg || msg.isError) continue; - count += msg.content.length; + tokenCount += estimateTokenLength(msg.content); reversedRecentMessages.push(msg); } - // concat - const recentMessages = context.concat(reversedRecentMessages.reverse()); + // concat all messages + const recentMessages = [ + ...longTermMemoryPrompts, + ...contextPrompts, + ...reversedRecentMessages.reverse(), + ]; return recentMessages; }, diff --git a/app/store/config.ts b/app/store/config.ts index e7e6555ed..c545f0392 100644 --- a/app/store/config.ts +++ b/app/store/config.ts @@ -1,6 +1,7 @@ import { create } from "zustand"; import { persist } from "zustand/middleware"; -import { StoreKey } from "../constant"; +import { getClientConfig } from "../config/client"; +import { DEFAULT_INPUT_TEMPLATE, StoreKey } from "../constant"; export enum SubmitKey { Enter = "Enter", @@ -21,7 +22,7 @@ export const DEFAULT_CONFIG = { avatar: "1f437", fontSize: 14, theme: Theme.Auto as Theme, - tightBorder: false, + tightBorder: !!getClientConfig()?.isApp, sendPreviewBubble: false, sidebarWidth: 300, @@ -34,9 +35,11 @@ export const DEFAULT_CONFIG = { temperature: 0.8, max_tokens: 2000, presence_penalty: 0, + frequency_penalty: 0, sendMemory: true, historyMessageCount: 4, compressMessageLengthThreshold: 1000, + template: DEFAULT_INPUT_TEMPLATE, }, }; @@ -60,6 +63,10 @@ export const ALL_MODELS = [ name: "gpt-4-0314", available: ENABLE_GPT4, }, + { + name: "gpt-4-0613", + available: ENABLE_GPT4, + }, { name: "gpt-4-32k", available: ENABLE_GPT4, @@ -68,6 +75,10 @@ export const ALL_MODELS = [ name: "gpt-4-32k-0314", available: ENABLE_GPT4, }, + { + name: "gpt-4-32k-0613", + available: ENABLE_GPT4, + }, { name: "gpt-3.5-turbo", available: true, @@ -76,6 +87,18 @@ export const ALL_MODELS = [ name: "gpt-3.5-turbo-0301", available: true, }, + { + name: "gpt-3.5-turbo-0613", + available: true, + }, + { + name: "gpt-3.5-turbo-16k", + available: true, + }, + { + name: "gpt-3.5-turbo-16k-0613", + available: true, + }, { name: "qwen-v1", // 通义千问 available: false, @@ -116,7 +139,7 @@ export function limitNumber( export function limitModel(name: string) { return ALL_MODELS.some((m) => m.name === name && m.available) ? name - : ALL_MODELS[4].name; + : "gpt-3.5-turbo"; } export const ModalConfigValidator = { @@ -129,6 +152,9 @@ export const ModalConfigValidator = { presence_penalty(x: number) { return limitNumber(x, -2, 2, 0); }, + frequency_penalty(x: number) { + return limitNumber(x, -2, 2, 0); + }, temperature(x: number) { return limitNumber(x, 0, 1, 1); }, @@ -151,14 +177,16 @@ export const useAppConfig = create()( }), { name: StoreKey.Config, - version: 2, + version: 3.1, migrate(persistedState, version) { - if (version === 2) return persistedState as any; + if (version === 3.1) return persistedState as any; const state = persistedState as ChatConfig; state.modelConfig.sendMemory = true; state.modelConfig.historyMessageCount = 4; state.modelConfig.compressMessageLengthThreshold = 1000; + state.modelConfig.frequency_penalty = 0; + state.modelConfig.template = DEFAULT_INPUT_TEMPLATE; state.dontShowMaskSplashScreen = false; return state; diff --git a/app/store/mask.ts b/app/store/mask.ts index ed45241f8..6d6377c37 100644 --- a/app/store/mask.ts +++ b/app/store/mask.ts @@ -3,7 +3,7 @@ import { persist } from "zustand/middleware"; import { BUILTIN_MASKS } from "../masks"; import { getLang, Lang } from "../locales"; import { DEFAULT_TOPIC, ChatMessage } from "./chat"; -import { ModelConfig, ModelType, useAppConfig } from "./config"; +import { ModelConfig, useAppConfig } from "./config"; import { StoreKey } from "../constant"; export type Mask = { @@ -89,7 +89,18 @@ export const useMaskStore = create()( const userMasks = Object.values(get().masks).sort( (a, b) => b.id - a.id, ); - return userMasks.concat(BUILTIN_MASKS); + const config = useAppConfig.getState(); + const buildinMasks = BUILTIN_MASKS.map( + (m) => + ({ + ...m, + modelConfig: { + ...config.modelConfig, + ...m.modelConfig, + }, + } as Mask), + ); + return userMasks.concat(buildinMasks); }, search(text) { return Object.values(get().masks); diff --git a/app/store/update.ts b/app/store/update.ts index 5a9bec9d7..ca2ae70ad 100644 --- a/app/store/update.ts +++ b/app/store/update.ts @@ -2,7 +2,7 @@ import { create } from "zustand"; import { persist } from "zustand/middleware"; import { FETCH_COMMIT_URL, StoreKey } from "../constant"; import { api } from "../client/api"; -import { showToast } from "../components/ui-lib"; +import { getClientConfig } from "../config/client"; export interface UpdateStore { lastUpdate: number; @@ -17,20 +17,6 @@ export interface UpdateStore { updateUsage: (force?: boolean) => Promise; } -function queryMeta(key: string, defaultValue?: string): string { - let ret: string; - if (document) { - const meta = document.head.querySelector( - `meta[name='${key}']`, - ) as HTMLMetaElement; - ret = meta?.content ?? ""; - } else { - ret = defaultValue ?? ""; - } - - return ret; -} - const ONE_MINUTE = 60 * 1000; export const useUpdateStore = create()( @@ -44,7 +30,7 @@ export const useUpdateStore = create()( version: "unknown", async getLatestVersion(force = false) { - set(() => ({ version: queryMeta("version") ?? "unknown" })); + set(() => ({ version: getClientConfig()?.commitId ?? "unknown" })); const overTenMins = Date.now() - get().lastUpdate > 10 * ONE_MINUTE; if (!force && !overTenMins) return; diff --git a/app/styles/markdown.scss b/app/styles/markdown.scss index 107c1b80d..31d10c4c4 100644 --- a/app/styles/markdown.scss +++ b/app/styles/markdown.scss @@ -1116,4 +1116,16 @@ .markdown-body ::-webkit-calendar-picker-indicator { filter: invert(50%); -} \ No newline at end of file +} + +.markdown-body .mermaid { + border: var(--border-in-light); + margin-bottom: 10px; + border-radius: 4px; + padding: 10px; + background-color: var(--white); +} + +#dmermaid { + display: none; +} diff --git a/app/utils.ts b/app/utils.ts index 120b1e158..c083c34fe 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -152,6 +152,7 @@ export function autoGrowTextArea(dom: HTMLTextAreaElement) { const width = getDomContentWidth(dom); measureDom.style.width = width + "px"; measureDom.innerText = dom.value !== "" ? dom.value : "1"; + measureDom.style.fontSize = dom.style.fontSize; const endWithEmptyLine = dom.value.endsWith("\n"); const height = parseFloat(window.getComputedStyle(measureDom).height); const singleLineHeight = parseFloat( diff --git a/app/utils/token.ts b/app/utils/token.ts new file mode 100644 index 000000000..ec8139b20 --- /dev/null +++ b/app/utils/token.ts @@ -0,0 +1,22 @@ +export function estimateTokenLength(input: string): number { + let tokenLength = 0; + + for (let i = 0; i < input.length; i++) { + const charCode = input.charCodeAt(i); + + if (charCode < 128) { + // ASCII character + if (charCode <= 122 && charCode >= 65) { + // a-Z + tokenLength += 0.25; + } else { + tokenLength += 0.5; + } + } else { + // Unicode character + tokenLength += 1.5; + } + } + + return tokenLength; +} diff --git a/docs/vercel-cn.md b/docs/vercel-cn.md index c49229694..51018d5da 100644 --- a/docs/vercel-cn.md +++ b/docs/vercel-cn.md @@ -14,7 +14,7 @@ ![vercel-create-3](./images/vercel/vercel-create-3.jpg) 1. 在项目配置页,点开 Environmane Variables 开始配置环境变量; -2. 依次新增名为 OPENAI_API_KEY 和 CODE 的环境变量; +2. 依次新增名为 OPENAI_API_KEY 和 CODE ([访问密码](https://github.com/Yidadaa/ChatGPT-Next-Web/blob/357296986609c14de10bf210871d30e2f67a8784/docs/faq-cn.md#%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F-code-%E6%98%AF%E4%BB%80%E4%B9%88%E5%BF%85%E9%A1%BB%E8%AE%BE%E7%BD%AE%E5%90%97)) 的环境变量; 3. 填入环境变量对应的值; 4. 点击 Add 确认增加环境变量; 5. 请确保你添加了 OPENAI_API_KEY,否则无法使用; @@ -36,4 +36,4 @@ ![vercel-redeploy](./images/vercel/vercel-redeploy.jpg) 1. 进入 Vercel 项目内部控制台,点击顶部的 Deployments 按钮; 2. 选择列表最顶部一条的右侧按钮; -3. 点击 Redeploy 即可重新部署。 \ No newline at end of file +3. 点击 Redeploy 即可重新部署。 diff --git a/next.config.mjs b/next.config.mjs index 7bb1436bf..a0cc4fafc 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -12,9 +12,36 @@ const nextConfig = { return config; }, output: mode, + images: { + unoptimized: mode === "export", + }, }; if (mode !== "export") { + nextConfig.headers = async () => { + return [ + { + source: "/api/:path*", + headers: [ + { key: "Access-Control-Allow-Credentials", value: "true" }, + { key: "Access-Control-Allow-Origin", value: "*" }, + { + key: "Access-Control-Allow-Methods", + value: "*", + }, + { + key: "Access-Control-Allow-Headers", + value: "*", + }, + { + key: "Access-Control-Max-Age", + value: "86400", + }, + ], + }, + ]; + }; + nextConfig.rewrites = async () => { const ret = [ { diff --git a/package.json b/package.json index c95f80876..0938c5b94 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,16 @@ { "name": "chatgpt-next-web", - "version": "1.9.3", "private": false, - "license": "Anti 996", + "license": "mit", "scripts": { "dev": "next dev", "build": "next build", "start": "next start", "lint": "next lint", + "export": "cross-env BUILD_MODE=export BUILD_APP=1 yarn build", + "export:dev": "cross-env BUILD_MODE=export BUILD_APP=1 yarn dev", + "app:dev": "yarn tauri dev", + "app:build": "yarn tauri build", "prompts": "node ./scripts/fetch-prompts.mjs", "prepare": "husky install", "proxy-dev": "sh ./scripts/init-proxy.sh && proxychains -f ./scripts/proxychains.conf yarn dev" @@ -20,12 +23,12 @@ "emoji-picker-react": "^4.4.7", "fuse.js": "^6.6.2", "html-to-image": "^1.11.11", - "mermaid": "^10.1.0", - "next": "^13.4.3", + "mermaid": "^10.2.3", + "next": "^13.4.6", "node-fetch": "^3.3.1", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-markdown": "^8.0.5", + "react-markdown": "^8.0.7", "react-router-dom": "^6.10.0", "rehype-highlight": "^6.0.0", "rehype-katex": "^6.0.2", @@ -34,12 +37,13 @@ "remark-math": "^5.1.1", "sass": "^1.59.2", "spark-md5": "^3.0.2", - "use-debounce": "^9.0.3", - "zustand": "^4.3.6" + "use-debounce": "^9.0.4", + "zustand": "^4.3.8" }, "devDependencies": { - "@types/node": "^18.14.6", - "@types/react": "^18.0.28", + "@tauri-apps/cli": "^1.3.1", + "@types/node": "^20.3.1", + "@types/react": "^18.2.12", "@types/react-dom": "^18.0.11", "@types/react-katex": "^3.0.0", "@types/spark-md5": "^3.0.2", @@ -49,9 +53,11 @@ "eslint-config-prettier": "^8.8.0", "eslint-plugin-prettier": "^4.2.1", "husky": "^8.0.0", - "lint-staged": "^13.2.0", + "lint-staged": "^13.2.2", "prettier": "^2.8.7", - "typescript": "4.9.5", - "@tauri-apps/cli": "^1.3.1" + "typescript": "4.9.5" + }, + "resolutions": { + "lint-staged/yaml": "^2.2.2" } } diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 7583bf590..bb72a88e7 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -63,7 +63,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c3d816ce6f0e2909a96830d6911c2aff044370b1ef92d7f267b43bae5addedd" dependencies = [ "atk-sys", - "bitflags", + "bitflags 1.3.2", "glib", "libc", ] @@ -80,6 +80,22 @@ dependencies = [ "system-deps 6.1.0", ] +[[package]] +name = "attohttpc" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fcf00bc6d5abb29b5f97e3c61a90b6d3caa12f3faf897d4a3e3607c050a35a7" +dependencies = [ + "flate2", + "http", + "log", + "native-tls", + "serde", + "serde_json", + "serde_urlencoded", + "url", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -98,12 +114,27 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbe3c979c178231552ecba20214a8272df4e09f232a87aef4320cf06539aded" + [[package]] name = "block" version = "0.1.6" @@ -180,7 +211,7 @@ version = "0.15.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c76ee391b03d35510d9fa917357c7f1855bd9a6659c95a1b392e33f49b3369bc" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cairo-sys-rs", "glib", "libc", @@ -264,6 +295,7 @@ dependencies = [ "serde_json", "tauri", "tauri-build", + "tauri-plugin-window-state", ] [[package]] @@ -285,7 +317,7 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" dependencies = [ - "bitflags", + "bitflags 1.3.2", "block", "cocoa-foundation", "core-foundation", @@ -301,7 +333,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "931d3837c286f56e3c58423ce4eba12d08db2374461a785c86f672b08b5650d6" dependencies = [ - "bitflags", + "bitflags 1.3.2", "block", "core-foundation", "core-graphics-types", @@ -354,7 +386,7 @@ version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-graphics-types", "foreign-types", @@ -367,7 +399,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "foreign-types", "libc", @@ -786,7 +818,7 @@ version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6e05c1f572ab0e1f15be94217f0dc29088c248b14f792a5ff0af0d84bcda9e8" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cairo-rs", "gdk-pixbuf", "gdk-sys", @@ -802,7 +834,7 @@ version = "0.15.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad38dd9cc8b099cceecdf41375bb6d481b1b5a7cd5cd603e10a69a9383f8619a" dependencies = [ - "bitflags", + "bitflags 1.3.2", "gdk-pixbuf-sys", "gio", "glib", @@ -917,7 +949,7 @@ version = "0.15.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68fdbc90312d462781a395f7a16d96a2b379bb6ef8cd6310a2df272771c4283b" dependencies = [ - "bitflags", + "bitflags 1.3.2", "futures-channel", "futures-core", "futures-io", @@ -947,7 +979,7 @@ version = "0.15.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edb0306fbad0ab5428b0ca674a23893db909a98582969c9b537be4ced78c505d" dependencies = [ - "bitflags", + "bitflags 1.3.2", "futures-channel", "futures-core", "futures-executor", @@ -1023,7 +1055,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92e3004a2d5d6d8b5057d2b57b3712c9529b62e82c77f25c1fecde1fd5c23bd0" dependencies = [ "atk", - "bitflags", + "bitflags 1.3.2", "cairo-rs", "field-offset", "futures-channel", @@ -1282,7 +1314,7 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf053e7843f2812ff03ef5afe34bb9c06ffee120385caad4f6b9967fcd37d41c" dependencies = [ - "bitflags", + "bitflags 1.3.2", "glib", "javascriptcore-rs-sys", ] @@ -1472,6 +1504,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "minisign-verify" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "933dca44d65cdd53b355d0b73d380a2ff5da71f87f036053188bf1eab6a19881" + [[package]] name = "miniz_oxide" version = "0.7.1" @@ -1482,13 +1520,31 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "ndk" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4" dependencies = [ - "bitflags", + "bitflags 1.3.2", "jni-sys", "ndk-sys", "num_enum", @@ -1612,6 +1668,17 @@ dependencies = [ "objc_exception", ] +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + [[package]] name = "objc_exception" version = "0.1.2" @@ -1636,6 +1703,60 @@ version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +[[package]] +name = "open" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2078c0039e6a54a0c42c28faa984e115fb4c2d5bf2208f77d1961002df8576f8" +dependencies = [ + "pathdiff", + "windows-sys 0.42.0", +] + +[[package]] +name = "openssl" +version = "0.10.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.16", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "overload" version = "0.1.1" @@ -1648,7 +1769,7 @@ version = "0.15.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22e4045548659aee5313bde6c582b0d83a627b7904dd20dc2d9ef0895d414e4f" dependencies = [ - "bitflags", + "bitflags 1.3.2", "glib", "libc", "once_cell", @@ -1690,6 +1811,12 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + [[package]] name = "percent-encoding" version = "2.2.0" @@ -1832,7 +1959,7 @@ version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aaeebc51f9e7d2c150d3f3bfeb667f2aa985db5ef1e3d212847bdedb488beeaa" dependencies = [ - "bitflags", + "bitflags 1.3.2", "crc32fast", "fdeflate", "flate2", @@ -2014,7 +2141,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -2023,7 +2150,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -2069,6 +2196,30 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" +[[package]] +name = "rfd" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0149778bd99b6959285b0933288206090c50e2327f47a9c463bfdbf45c8823ea" +dependencies = [ + "block", + "dispatch", + "glib-sys", + "gobject-sys", + "gtk-sys", + "js-sys", + "lazy_static", + "log", + "objc", + "objc-foundation", + "objc_id", + "raw-window-handle", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows 0.37.0", +] + [[package]] name = "rustc_version" version = "0.4.0" @@ -2084,7 +2235,7 @@ version = "0.37.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", @@ -2119,6 +2270,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +dependencies = [ + "windows-sys 0.42.0", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -2131,13 +2291,36 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "security-framework" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "selectors" version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cssparser", "derive_more", "fxhash", @@ -2211,6 +2394,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa 1.0.6", + "ryu", + "serde", +] + [[package]] name = "serde_with" version = "2.3.3" @@ -2324,7 +2519,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2b4d76501d8ba387cf0fefbe055c3e0a59891d09f0f995ae4e4b16f6b60f3c0" dependencies = [ - "bitflags", + "bitflags 1.3.2", "gio", "glib", "libc", @@ -2338,7 +2533,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "009ef427103fcb17f802871647a7fa6c60cbb654b4c4e4c0ac60a31c5f6dc9cf" dependencies = [ - "bitflags", + "bitflags 1.3.2", "gio-sys", "glib-sys", "gobject-sys", @@ -2447,7 +2642,7 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd3cde9c0cd2b872616bba26b818e0d6469330196869d7e5000dba96ce9431df" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cairo-rs", "cc", "cocoa", @@ -2523,6 +2718,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d42ba3a2e8556722f31336a0750c10dbb6a81396a1c452977f515da83f69f842" dependencies = [ "anyhow", + "attohttpc", + "base64 0.21.0", "cocoa", "dirs-next", "embed_plist", @@ -2535,11 +2732,15 @@ dependencies = [ "heck 0.4.1", "http", "ignore", + "minisign-verify", "objc", "once_cell", + "open", "percent-encoding", "rand 0.8.5", "raw-window-handle", + "regex", + "rfd", "semver", "serde", "serde_json", @@ -2553,12 +2754,14 @@ dependencies = [ "tauri-utils", "tempfile", "thiserror", + "time", "tokio", "url", "uuid", "webkit2gtk", "webview2-com", "windows 0.39.0", + "zip", ] [[package]] @@ -2593,6 +2796,7 @@ dependencies = [ "png", "proc-macro2", "quote", + "regex", "semver", "serde", "serde_json", @@ -2618,6 +2822,20 @@ dependencies = [ "tauri-utils", ] +[[package]] +name = "tauri-plugin-window-state" +version = "0.1.0" +source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#7b9d7a1d8896c213998949a423d5835e8e2734c6" +dependencies = [ + "bincode", + "bitflags 2.3.2", + "log", + "serde", + "serde_json", + "tauri", + "thiserror", +] + [[package]] name = "tauri-runtime" version = "0.13.0" @@ -2977,6 +3195,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version-compare" version = "0.0.11" @@ -3062,6 +3286,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d1985d03709c53167ce907ff394f5316aa22cb4e12761295c5dc57dacb6297e" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.86" @@ -3091,13 +3327,23 @@ version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" +[[package]] +name = "web-sys" +version = "0.3.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webkit2gtk" version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8f859735e4a452aeb28c6c56a852967a8a76c8eb1cc32dbf931ad28a13d6370" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cairo-rs", "gdk", "gdk-sys", @@ -3122,7 +3368,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d76ca6ecc47aeba01ec61e480139dda143796abcae6f83bcddf50d6b5b1dcf3" dependencies = [ "atk-sys", - "bitflags", + "bitflags 1.3.2", "cairo-sys-rs", "gdk-pixbuf-sys", "gdk-sys", @@ -3207,6 +3453,19 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57b543186b344cc61c85b5aab0d2e3adf4e0f99bc076eff9aa5927bcc0b8a647" +dependencies = [ + "windows_aarch64_msvc 0.37.0", + "windows_i686_gnu 0.37.0", + "windows_i686_msvc 0.37.0", + "windows_x86_64_gnu 0.37.0", + "windows_x86_64_msvc 0.37.0", +] + [[package]] name = "windows" version = "0.39.0" @@ -3256,6 +3515,21 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ee5e275231f07c6e240d14f34e1b635bf1faa1c76c57cfd59a5cdb9848e4278" +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-sys" version = "0.45.0" @@ -3322,6 +3596,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a" + [[package]] name = "windows_aarch64_msvc" version = "0.39.0" @@ -3340,6 +3620,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +[[package]] +name = "windows_i686_gnu" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1" + [[package]] name = "windows_i686_gnu" version = "0.39.0" @@ -3358,6 +3644,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +[[package]] +name = "windows_i686_msvc" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c" + [[package]] name = "windows_i686_msvc" version = "0.39.0" @@ -3376,6 +3668,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +[[package]] +name = "windows_x86_64_gnu" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d" + [[package]] name = "windows_x86_64_gnu" version = "0.39.0" @@ -3406,6 +3704,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +[[package]] +name = "windows_x86_64_msvc" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d" + [[package]] name = "windows_x86_64_msvc" version = "0.39.0" @@ -3510,3 +3814,14 @@ checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" dependencies = [ "libc", ] + +[[package]] +name = "zip" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +dependencies = [ + "byteorder", + "crc32fast", + "crossbeam-utils", +] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 504ee6a23..694f62cb6 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "chatgpt-next-web" version = "0.1.0" -description = "A Tauri App" +description = "A cross platform app for LLM ChatBot." authors = ["Yidadaa"] -license = "anti-996" +license = "mit" repository = "" default-run = "chatgpt-next-web" edition = "2021" @@ -17,7 +17,8 @@ tauri-build = { version = "1.3.0", features = [] } [dependencies] serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } -tauri = { version = "1.3.0", features = [] } +tauri = { version = "1.3.0", features = ["clipboard-all", "shell-open", "updater", "window-close", "window-hide", "window-maximize", "window-minimize", "window-set-icon", "window-set-ignore-cursor-events", "window-set-resizable", "window-show", "window-start-dragging", "window-unmaximize", "window-unminimize"] } +tauri-plugin-window-state = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" } [features] # this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled. diff --git a/src-tauri/icons/128x128.png b/src-tauri/icons/128x128.png index 77e7d2338..7fee8db6e 100644 Binary files a/src-tauri/icons/128x128.png and b/src-tauri/icons/128x128.png differ diff --git a/src-tauri/icons/128x128@2x.png b/src-tauri/icons/128x128@2x.png index 0f7976f1a..178761b6a 100644 Binary files a/src-tauri/icons/128x128@2x.png and b/src-tauri/icons/128x128@2x.png differ diff --git a/src-tauri/icons/32x32.png b/src-tauri/icons/32x32.png index 98fda06fc..471cdbb65 100644 Binary files a/src-tauri/icons/32x32.png and b/src-tauri/icons/32x32.png differ diff --git a/src-tauri/icons/Square107x107Logo.png b/src-tauri/icons/Square107x107Logo.png index f35d84ff1..241e101ba 100644 Binary files a/src-tauri/icons/Square107x107Logo.png and b/src-tauri/icons/Square107x107Logo.png differ diff --git a/src-tauri/icons/Square142x142Logo.png b/src-tauri/icons/Square142x142Logo.png index 1823bb269..b27ce753d 100644 Binary files a/src-tauri/icons/Square142x142Logo.png and b/src-tauri/icons/Square142x142Logo.png differ diff --git a/src-tauri/icons/Square150x150Logo.png b/src-tauri/icons/Square150x150Logo.png index dc2b22cea..d9d58df2d 100644 Binary files a/src-tauri/icons/Square150x150Logo.png and b/src-tauri/icons/Square150x150Logo.png differ diff --git a/src-tauri/icons/Square284x284Logo.png b/src-tauri/icons/Square284x284Logo.png index 0ed3984c5..64dd15d81 100644 Binary files a/src-tauri/icons/Square284x284Logo.png and b/src-tauri/icons/Square284x284Logo.png differ diff --git a/src-tauri/icons/Square30x30Logo.png b/src-tauri/icons/Square30x30Logo.png index 60bf0eadf..c585069db 100644 Binary files a/src-tauri/icons/Square30x30Logo.png and b/src-tauri/icons/Square30x30Logo.png differ diff --git a/src-tauri/icons/Square310x310Logo.png b/src-tauri/icons/Square310x310Logo.png index c8ca0ad13..70b0b5ddb 100644 Binary files a/src-tauri/icons/Square310x310Logo.png and b/src-tauri/icons/Square310x310Logo.png differ diff --git a/src-tauri/icons/Square44x44Logo.png b/src-tauri/icons/Square44x44Logo.png index 8756459b6..6657a9617 100644 Binary files a/src-tauri/icons/Square44x44Logo.png and b/src-tauri/icons/Square44x44Logo.png differ diff --git a/src-tauri/icons/Square71x71Logo.png b/src-tauri/icons/Square71x71Logo.png index 2c8023cc8..865a99ed8 100644 Binary files a/src-tauri/icons/Square71x71Logo.png and b/src-tauri/icons/Square71x71Logo.png differ diff --git a/src-tauri/icons/Square89x89Logo.png b/src-tauri/icons/Square89x89Logo.png index 2c5e6034f..4be716430 100644 Binary files a/src-tauri/icons/Square89x89Logo.png and b/src-tauri/icons/Square89x89Logo.png differ diff --git a/src-tauri/icons/StoreLogo.png b/src-tauri/icons/StoreLogo.png index 17d142c0a..9791b7adb 100644 Binary files a/src-tauri/icons/StoreLogo.png and b/src-tauri/icons/StoreLogo.png differ diff --git a/src-tauri/icons/icon.icns b/src-tauri/icons/icon.icns index a2993adc8..deca5bc69 100644 Binary files a/src-tauri/icons/icon.icns and b/src-tauri/icons/icon.icns differ diff --git a/src-tauri/icons/icon.ico b/src-tauri/icons/icon.ico index 06c23c82f..59f1568ee 100644 Binary files a/src-tauri/icons/icon.ico and b/src-tauri/icons/icon.ico differ diff --git a/src-tauri/icons/icon.png b/src-tauri/icons/icon.png index d1756ce45..3ae7ae9bf 100644 Binary files a/src-tauri/icons/icon.png and b/src-tauri/icons/icon.png differ diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index f5c5be236..ed3ec32f3 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -3,6 +3,7 @@ fn main() { tauri::Builder::default() + .plugin(tauri_plugin_window_state::Builder::default().build()) .run(tauri::generate_context!()) .expect("error while running tauri application"); } diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 728d92d86..967ad2cd1 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,23 +1,44 @@ { "$schema": "../node_modules/@tauri-apps/cli/schema.json", "build": { - "beforeBuildCommand": "yarn build", - "beforeDevCommand": "yarn dev", + "beforeBuildCommand": "yarn export", + "beforeDevCommand": "yarn export:dev", "devPath": "http://localhost:3000", "distDir": "../out" }, "package": { "productName": "chatgpt-next-web", - "version": "2.8" + "version": "2.8.3" }, "tauri": { "allowlist": { - "all": false + "all": false, + "shell": { + "all": false, + "open": true + }, + "clipboard": { + "all": true + }, + "window": { + "all": false, + "close": true, + "hide": true, + "maximize": true, + "minimize": true, + "setIcon": true, + "setIgnoreCursorEvents": true, + "setResizable": true, + "show": true, + "startDragging": true, + "unmaximize": true, + "unminimize": true + } }, "bundle": { "active": true, "category": "DeveloperTool", - "copyright": "", + "copyright": "2023, Zhang Yifei All Rights Reserved.", "deb": { "depends": [] }, @@ -29,8 +50,8 @@ "icons/icon.icns", "icons/icon.ico" ], - "identifier": "com.yida.chatgpt.nextweb", - "longDescription": "", + "identifier": "com.yida.chatgpt.next.web", + "longDescription": "ChatGPT Next Web is a cross-platform ChatGPT client, including Web/Win/Linux/OSX/PWA.", "macOS": { "entitlements": null, "exceptionDomain": "", @@ -39,7 +60,7 @@ "signingIdentity": null }, "resources": [], - "shortDescription": "", + "shortDescription": "ChatGPT Next Web App", "targets": "all", "windows": { "certificateThumbprint": null, @@ -51,15 +72,25 @@ "csp": null }, "updater": { - "active": false + "active": true, + "endpoints": [ + "https://github.com/Yidadaa/ChatGPT-Next-Web/releases/download/{{current_version}}/latest.json" + ], + "dialog": false, + "windows": { + "installMode": "passive" + }, + "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IERFNDE4MENFM0Y1RTZBOTQKUldTVWFsNC96b0JCM3RqM2NmMnlFTmxIaStRaEJrTHNOU2VqRVlIV1hwVURoWUdVdEc1eDcxVEYK" }, "windows": [ { "fullscreen": false, "height": 600, "resizable": true, - "title": "tauri-next", - "width": 800 + "title": "ChatGPT Next Web", + "width": 960, + "hiddenTitle": true, + "titleBarStyle": "Overlay" } ] } diff --git a/yarn.lock b/yarn.lock index ed3401b39..79dec543b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -995,9 +995,9 @@ "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" -"@braintree/sanitize-url@^6.0.0": +"@braintree/sanitize-url@^6.0.2": version "6.0.2" - resolved "https://registry.npmmirror.com/@braintree/sanitize-url/-/sanitize-url-6.0.2.tgz#6110f918d273fe2af8ea1c4398a88774bb9fc12f" + resolved "https://registry.yarnpkg.com/@braintree/sanitize-url/-/sanitize-url-6.0.2.tgz#6110f918d273fe2af8ea1c4398a88774bb9fc12f" integrity sha512-Tbsj02wXCbqGmzdnXNk0SOF19ChhRU70BsroIi4Pm6Ehp56in6vch94mfbdQ17DozxkL3BAVjbZ4Qc1a0HFRAg== "@eslint-community/eslint-utils@^4.2.0": @@ -1109,17 +1109,10 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@khanacademy/simple-markdown@^0.8.6": - version "0.8.6" - resolved "https://registry.npmmirror.com/@khanacademy/simple-markdown/-/simple-markdown-0.8.6.tgz#9c9aef1f5ce2ce60292d13849165965a57c26f25" - integrity sha512-mAUlR9lchzfqunR89pFvNI51jQKsMpJeWYsYWw0DQcUXczn/T/V6510utgvm7X0N3zN87j1SvuKk8cMbl9IAFw== - dependencies: - "@types/react" ">=16.0.0" - -"@next/env@13.4.3": - version "13.4.3" - resolved "https://registry.npmmirror.com/@next/env/-/env-13.4.3.tgz#cb00bdd43a0619a79a52c9336df8a0aa84f8f4bf" - integrity sha512-pa1ErjyFensznttAk3EIv77vFbfSYT6cLzVRK5jx4uiRuCQo+m2wCFAREaHKIy63dlgvOyMlzh6R8Inu8H3KrQ== +"@next/env@13.4.6": + version "13.4.6" + resolved "https://registry.yarnpkg.com/@next/env/-/env-13.4.6.tgz#3f2041c7758660d7255707ae4cb9166519113dea" + integrity sha512-nqUxEtvDqFhmV1/awSg0K2XHNwkftNaiUqCYO9e6+MYmqNObpKVl7OgMkGaQ2SZnFx5YqF0t60ZJTlyJIDAijg== "@next/eslint-plugin-next@13.2.3": version "13.2.3" @@ -1128,50 +1121,50 @@ dependencies: glob "7.1.7" -"@next/swc-darwin-arm64@13.4.3": - version "13.4.3" - resolved "https://registry.npmmirror.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.3.tgz#2d6c99dd5afbcce37e4ba0f64196317a1259034d" - integrity sha512-yx18udH/ZmR4Bw4M6lIIPE3JxsAZwo04iaucEfA2GMt1unXr2iodHUX/LAKNyi6xoLP2ghi0E+Xi1f4Qb8f1LQ== +"@next/swc-darwin-arm64@13.4.6": + version "13.4.6" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.6.tgz#47485f3deaee6681b4a4036c74bb9c4b728d5ddd" + integrity sha512-ahi6VP98o4HV19rkOXPSUu+ovfHfUxbJQ7VVJ7gL2FnZRr7onEFC1oGQ6NQHpm8CxpIzSSBW79kumlFMOmZVjg== -"@next/swc-darwin-x64@13.4.3": - version "13.4.3" - resolved "https://registry.npmmirror.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.3.tgz#162b15fb8a54d9f64e69c898ebeb55b7dac9bddd" - integrity sha512-Mi8xJWh2IOjryAM1mx18vwmal9eokJ2njY4nDh04scy37F0LEGJ/diL6JL6kTXi0UfUCGbMsOItf7vpReNiD2A== +"@next/swc-darwin-x64@13.4.6": + version "13.4.6" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.6.tgz#a6a5b232ec0f2079224fb8ed6bf11dc479af1acf" + integrity sha512-13cXxKFsPJIJKzUqrU5XB1mc0xbUgYsRcdH6/rB8c4NMEbWGdtD4QoK9ShN31TZdePpD4k416Ur7p+deMIxnnA== -"@next/swc-linux-arm64-gnu@13.4.3": - version "13.4.3" - resolved "https://registry.npmmirror.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.3.tgz#aee57422f11183d6a2e4a2e8aa23b9285873e18f" - integrity sha512-aBvtry4bxJ1xwKZ/LVPeBGBwWVwxa4bTnNkRRw6YffJnn/f4Tv4EGDPaVeYHZGQVA56wsGbtA6nZMuWs/EIk4Q== +"@next/swc-linux-arm64-gnu@13.4.6": + version "13.4.6" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.6.tgz#2a67144e863d9c45fdbd13c7827370e7f2a28405" + integrity sha512-Ti+NMHEjTNktCVxNjeWbYgmZvA2AqMMI2AMlzkXsU7W4pXCMhrryAmAIoo+7YdJbsx01JQWYVxGe62G6DoCLaA== -"@next/swc-linux-arm64-musl@13.4.3": - version "13.4.3" - resolved "https://registry.npmmirror.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.3.tgz#c10b6aaaa47b341c6c9ea15f8b0ddb37e255d035" - integrity sha512-krT+2G3kEsEUvZoYte3/2IscscDraYPc2B+fDJFipPktJmrv088Pei/RjrhWm5TMIy5URYjZUoDZdh5k940Dyw== +"@next/swc-linux-arm64-musl@13.4.6": + version "13.4.6" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.6.tgz#5a191ac3575a70598e9e9c6e7264fc0b8a90b2db" + integrity sha512-OHoC6gO7XfjstgwR+z6UHKlvhqJfyMtNaJidjx3sEcfaDwS7R2lqR5AABi8PuilGgi0BO0O0sCXqLlpp3a0emQ== -"@next/swc-linux-x64-gnu@13.4.3": - version "13.4.3" - resolved "https://registry.npmmirror.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.3.tgz#3f85bc5591c6a0d4908404f7e88e3c04f4462039" - integrity sha512-AMdFX6EKJjC0G/CM6hJvkY8wUjCcbdj3Qg7uAQJ7PVejRWaVt0sDTMavbRfgMchx8h8KsAudUCtdFkG9hlEClw== +"@next/swc-linux-x64-gnu@13.4.6": + version "13.4.6" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.6.tgz#d38adf842a8b8f9de492454328fd32a2c53350f3" + integrity sha512-zHZxPGkUlpfNJCboUrFqwlwEX5vI9LSN70b8XEb0DYzzlrZyCyOi7hwDp/+3Urm9AB7YCAJkgR5Sp1XBVjHdfQ== -"@next/swc-linux-x64-musl@13.4.3": - version "13.4.3" - resolved "https://registry.npmmirror.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.3.tgz#f4535adc2374a86bc8e43af149b551567df065de" - integrity sha512-jySgSXE48shaLtcQbiFO9ajE9mqz7pcAVLnVLvRIlUHyQYR/WyZdK8ehLs65Mz6j9cLrJM+YdmdJPyV4WDaz2g== +"@next/swc-linux-x64-musl@13.4.6": + version "13.4.6" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.6.tgz#74c745774358b78be7f958e7a8b7d93936cd6ebc" + integrity sha512-K/Y8lYGTwTpv5ME8PSJxwxLolaDRdVy+lOd9yMRMiQE0BLUhtxtCWC9ypV42uh9WpLjoaD0joOsB9Q6mbrSGJg== -"@next/swc-win32-arm64-msvc@13.4.3": - version "13.4.3" - resolved "https://registry.npmmirror.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.3.tgz#e76106d85391c308c5ed70cda2bca2c582d65536" - integrity sha512-5DxHo8uYcaADiE9pHrg8o28VMt/1kR8voDehmfs9AqS0qSClxAAl+CchjdboUvbCjdNWL1MISCvEfKY2InJ3JA== +"@next/swc-win32-arm64-msvc@13.4.6": + version "13.4.6" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.6.tgz#1e1e02c175573e64808fc1a7e8650e3e217f1edc" + integrity sha512-U6LtxEUrjBL2tpW+Kr1nHCSJWNeIed7U7l5o7FiKGGwGgIlFi4UHDiLI6TQ2lxi20fAU33CsruV3U0GuzMlXIw== -"@next/swc-win32-ia32-msvc@13.4.3": - version "13.4.3" - resolved "https://registry.npmmirror.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.3.tgz#8eb5d9dd71ed7a971671291605ad64ad522fb3bc" - integrity sha512-LaqkF3d+GXRA5X6zrUjQUrXm2MN/3E2arXBtn5C7avBCNYfm9G3Xc646AmmmpN3DJZVaMYliMyCIQCMDEzk80w== +"@next/swc-win32-ia32-msvc@13.4.6": + version "13.4.6" + resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.6.tgz#2b528ae3ec7f6e727f4f0d81a1015f63da55c7a6" + integrity sha512-eEBeAqpCfhdPSlCZCayjCiyIllVqy4tcqvm1xmg3BgJG0G5ITiMM4Cw2WVeRSgWDJqQGRyyb+q8Y2ltzhXOWsQ== -"@next/swc-win32-x64-msvc@13.4.3": - version "13.4.3" - resolved "https://registry.npmmirror.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.3.tgz#c7b2b1b9e158fd7749f8209e68ee8e43a997eb4c" - integrity sha512-jglUk/x7ZWeOJWlVoKyIAkHLTI+qEkOriOOV+3hr1GyiywzcqfI7TpFSiwC7kk1scOiH7NTFKp8mA3XPNO9bDw== +"@next/swc-win32-x64-msvc@13.4.6": + version "13.4.6" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.6.tgz#38620bd68267ff13e50ecd432f1822eac51382a8" + integrity sha512-OrZs94AuO3ZS5tnqlyPRNgfWvboXaDQCi5aXGve3o3C+Sj0ctMUV9+Do+0zMvvLRumR8E0PTWKvtz9n5vzIsWw== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -1438,10 +1431,10 @@ resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197" integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== -"@types/node@^18.14.6": - version "18.15.11" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.11.tgz#b3b790f09cb1696cffcec605de025b088fa4225f" - integrity sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q== +"@types/node@^20.3.1": + version "20.3.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.3.1.tgz#e8a83f1aa8b649377bb1fb5d7bac5cb90e784dfe" + integrity sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg== "@types/parse-json@^4.0.0": version "4.0.0" @@ -1467,19 +1460,10 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^18.0.28": - version "18.0.31" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.31.tgz#a69ef8dd7bfa849734d258c793a8fe343a338205" - integrity sha512-EEG67of7DsvRDU6BLLI0p+k1GojDLz9+lZsnCpCRTa/lOokvyPBvp8S5x+A24hME3yyQuIipcP70KJ6H7Qupww== - dependencies: - "@types/prop-types" "*" - "@types/scheduler" "*" - csstype "^3.0.2" - -"@types/react@>=16.0.0": - version "18.2.5" - resolved "https://registry.npmmirror.com/@types/react/-/react-18.2.5.tgz#f9403e1113b12b53f7edcdd9a900c10dd4b49a59" - integrity sha512-RuoMedzJ5AOh23Dvws13LU9jpZHIc/k90AgmK7CecAYeWmSr3553L4u5rk4sWAPBuQosfT7HmTfG4Rg5o4nGEA== +"@types/react@*", "@types/react@^18.2.12": + version "18.2.12" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.12.tgz#95d584338610b78bb9ba0415e3180fb03debdf97" + integrity sha512-ndmBMLCgn38v3SntMeoJaIrO6tGHYKMEBohCUmw8HoLLQdRMOIGXfeYaBTLe2lsFaSB3MOK1VXscYFnmLtTSmw== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -2469,10 +2453,10 @@ domhandler@^4.2.0, domhandler@^4.3.1: dependencies: domelementtype "^2.2.0" -dompurify@2.4.5: - version "2.4.5" - resolved "https://registry.npmmirror.com/dompurify/-/dompurify-2.4.5.tgz#0e89a27601f0bad978f9a924e7a05d5d2cccdd87" - integrity sha512-jggCCd+8Iqp4Tsz0nIvpcb22InKEBrGz5dw3EQJMs8HPJDsKbFIO3STYtAvCfDx26Muevn1MHVI0XxjgFfmiSA== +dompurify@3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.0.3.tgz#4b115d15a091ddc96f232bcef668550a2f6f1430" + integrity sha512-axQ9zieHLnAnHh0sfAamKYiqXMJAVwu+LM/alQ7WDagoWessyWvMSFyW65CqF3owufNu8HBcE4cM2Vflu7YWcQ== domutils@^2.8.0: version "2.8.0" @@ -3065,6 +3049,11 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + glob@7.1.7: version "7.1.7" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" @@ -3148,7 +3137,7 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" -graceful-fs@^4.2.4: +graceful-fs@^4.1.2, graceful-fs@^4.2.4: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -3697,10 +3686,10 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== -lint-staged@^13.2.0: - version "13.2.0" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-13.2.0.tgz#b7abaf79c91cd36d824f17b23a4ce5209206126a" - integrity sha512-GbyK5iWinax5Dfw5obm2g2ccUiZXNGtAS4mCbJ0Lv4rq6iEtfBSjOYdcbOtAIFtM114t0vdpViDDetjVTSd8Vw== +lint-staged@^13.2.2: + version "13.2.2" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-13.2.2.tgz#5e711d3139c234f73402177be2f8dd312e6508ca" + integrity sha512-71gSwXKy649VrSU09s10uAT0rWCcY3aewhMaHyl2N84oBk4Xs9HgxvUp3AYu+bNsK4NrOYYxvSgg7FyGJ+jGcA== dependencies: chalk "5.2.0" cli-truncate "^3.1.0" @@ -3714,7 +3703,7 @@ lint-staged@^13.2.0: object-inspect "^1.12.3" pidtree "^0.6.0" string-argv "^0.3.1" - yaml "^2.2.1" + yaml "^2.2.2" listr2@^5.0.7: version "5.0.8" @@ -3826,10 +3815,10 @@ mdast-util-find-and-replace@^2.0.0: unist-util-is "^5.0.0" unist-util-visit-parents "^5.0.0" -mdast-util-from-markdown@^1.0.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.0.tgz#0214124154f26154a2b3f9d401155509be45e894" - integrity sha512-HN3W1gRIuN/ZW295c7zi7g9lVBllMgZE40RxCX37wrTPWXCWtpvOZdfnuK+1WNpvZje6XuJeI3Wnb4TJEUem+g== +mdast-util-from-markdown@^1.0.0, mdast-util-from-markdown@^1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz#9421a5a247f10d31d2faed2a30df5ec89ceafcf0" + integrity sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww== dependencies: "@types/mdast" "^3.0.0" "@types/unist" "^2.0.0" @@ -3974,25 +3963,25 @@ merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -mermaid@^10.1.0: - version "10.1.0" - resolved "https://registry.npmmirror.com/mermaid/-/mermaid-10.1.0.tgz#6e40d5250174f4750ca6548e4ee00f6ae210855a" - integrity sha512-LYekSMNJygI1VnMizAPUddY95hZxOjwZxr7pODczILInO0dhQKuhXeu4sargtnuTwCilSuLS7Uiq/Qn7HTVrmA== +mermaid@^10.2.3: + version "10.2.3" + resolved "https://registry.yarnpkg.com/mermaid/-/mermaid-10.2.3.tgz#789d3b582c5da8c69aa4a7c0e2b826562c8c8b12" + integrity sha512-cMVE5s9PlQvOwfORkyVpr5beMsLdInrycAosdr+tpZ0WFjG4RJ/bUHST7aTgHNJbujHkdBRAm+N50P3puQOfPw== dependencies: - "@braintree/sanitize-url" "^6.0.0" - "@khanacademy/simple-markdown" "^0.8.6" + "@braintree/sanitize-url" "^6.0.2" cytoscape "^3.23.0" cytoscape-cose-bilkent "^4.1.0" cytoscape-fcose "^2.1.0" d3 "^7.4.0" dagre-d3-es "7.0.10" dayjs "^1.11.7" - dompurify "2.4.5" + dompurify "3.0.3" elkjs "^0.8.2" khroma "^2.0.0" lodash-es "^4.17.21" + mdast-util-from-markdown "^1.3.0" non-layered-tidy-tree-layout "^2.0.2" - stylis "^4.1.2" + stylis "^4.1.3" ts-dedent "^2.2.0" uuid "^9.0.0" web-worker "^1.2.0" @@ -4340,28 +4329,29 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -next@^13.4.3: - version "13.4.3" - resolved "https://registry.npmmirror.com/next/-/next-13.4.3.tgz#7f417dec9fa2731d8c1d1819a1c7d0919ad6fc75" - integrity sha512-FV3pBrAAnAIfOclTvncw9dDohyeuEEXPe5KNcva91anT/rdycWbgtu3IjUj4n5yHnWK8YEPo0vrUecHmnmUNbA== +next@^13.4.6: + version "13.4.6" + resolved "https://registry.yarnpkg.com/next/-/next-13.4.6.tgz#ebe52f5c74d60176d45b45e73f25a51103713ea4" + integrity sha512-sjVqjxU+U2aXZnYt4Ud6CTLNNwWjdSfMgemGpIQJcN3Z7Jni9xRWbR0ie5fQzCg87aLqQVhKA2ud2gPoqJ9lGw== dependencies: - "@next/env" "13.4.3" + "@next/env" "13.4.6" "@swc/helpers" "0.5.1" busboy "1.6.0" caniuse-lite "^1.0.30001406" postcss "8.4.14" styled-jsx "5.1.1" + watchpack "2.4.0" zod "3.21.4" optionalDependencies: - "@next/swc-darwin-arm64" "13.4.3" - "@next/swc-darwin-x64" "13.4.3" - "@next/swc-linux-arm64-gnu" "13.4.3" - "@next/swc-linux-arm64-musl" "13.4.3" - "@next/swc-linux-x64-gnu" "13.4.3" - "@next/swc-linux-x64-musl" "13.4.3" - "@next/swc-win32-arm64-msvc" "13.4.3" - "@next/swc-win32-ia32-msvc" "13.4.3" - "@next/swc-win32-x64-msvc" "13.4.3" + "@next/swc-darwin-arm64" "13.4.6" + "@next/swc-darwin-x64" "13.4.6" + "@next/swc-linux-arm64-gnu" "13.4.6" + "@next/swc-linux-arm64-musl" "13.4.6" + "@next/swc-linux-x64-gnu" "13.4.6" + "@next/swc-linux-x64-musl" "13.4.6" + "@next/swc-win32-arm64-msvc" "13.4.6" + "@next/swc-win32-ia32-msvc" "13.4.6" + "@next/swc-win32-x64-msvc" "13.4.6" node-domexception@^1.0.0: version "1.0.0" @@ -4677,10 +4667,10 @@ react-is@^18.0.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== -react-markdown@^8.0.5: - version "8.0.6" - resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-8.0.6.tgz#3e939018f8bfce800ffdf22cf50aba3cdded7ad1" - integrity sha512-KgPWsYgHuftdx510wwIzpwf+5js/iHqBR+fzxefv8Khk3mFbnioF1bmL2idHN3ler0LMQmICKeDrWnZrX9mtbQ== +react-markdown@^8.0.7: + version "8.0.7" + resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-8.0.7.tgz#c8dbd1b9ba5f1c5e7e5f2a44de465a3caafdf89b" + integrity sha512-bvWbzG4MtOU62XqBx3Xx+zB2raaFFsq4mYiAzfjXJMEz2sixgeAfraA3tvzULF02ZdOMUOKTBFFaZJDDrq+BJQ== dependencies: "@types/hast" "^2.0.0" "@types/prop-types" "^15.0.0" @@ -5208,10 +5198,10 @@ styled-jsx@5.1.1: dependencies: client-only "0.0.1" -stylis@^4.1.2: - version "4.1.4" - resolved "https://registry.npmmirror.com/stylis/-/stylis-4.1.4.tgz#9cb60e7153d8ac6d02d773552bf51c7a0344535b" - integrity sha512-USf5pszRYwuE6hg9by0OkKChkQYEXfkeTtm0xKw+jqQhwyjCVLdYyMBK7R+n7dhzsblAWJnGxju4vxq5eH20GQ== +stylis@^4.1.3: + version "4.2.0" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51" + integrity sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw== supports-color@^5.3.0: version "5.5.0" @@ -5491,10 +5481,10 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -use-debounce@^9.0.3: - version "9.0.3" - resolved "https://registry.yarnpkg.com/use-debounce/-/use-debounce-9.0.3.tgz#bac660c19ab7b38662e08608fee23c7ad303f532" - integrity sha512-FhtlbDtDXILJV7Lix5OZj5yX/fW1tzq+VrvK1fnT2bUrPOGruU9Rw8NCEn+UI9wopfERBEZAOQ8lfeCJPllgnw== +use-debounce@^9.0.4: + version "9.0.4" + resolved "https://registry.yarnpkg.com/use-debounce/-/use-debounce-9.0.4.tgz#51d25d856fbdfeb537553972ce3943b897f1ac85" + integrity sha512-6X8H/mikbrt0XE8e+JXRtZ8yYVvKkdYRfmIhWZYsP8rcNs9hk3APV8Ua2mFkKRLcJKVdnX2/Vwrmg2GWKUQEaQ== use-memo-one@^1.1.3: version "1.1.3" @@ -5547,6 +5537,14 @@ vfile@^5.0.0: unist-util-stringify-position "^3.0.0" vfile-message "^3.0.0" +watchpack@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" + integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + web-namespaces@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-2.0.1.tgz#1010ff7c650eccb2592cebeeaf9a1b253fd40692" @@ -5645,10 +5643,10 @@ yaml@^1.10.0: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -yaml@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.2.1.tgz#3014bf0482dcd15147aa8e56109ce8632cd60ce4" - integrity sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw== +yaml@^2.2.2: + version "2.3.1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.1.tgz#02fe0975d23cd441242aa7204e09fc28ac2ac33b" + integrity sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ== yocto-queue@^0.1.0: version "0.1.0" @@ -5660,10 +5658,10 @@ zod@3.21.4: resolved "https://registry.npmmirror.com/zod/-/zod-3.21.4.tgz#10882231d992519f0a10b5dd58a38c9dabbb64db" integrity sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw== -zustand@^4.3.6: - version "4.3.6" - resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.3.6.tgz#ce7804eb75361af0461a2d0536b65461ec5de86f" - integrity sha512-6J5zDxjxLE+yukC2XZWf/IyWVKnXT9b9HUv09VJ/bwGCpKNcaTqp7Ws28Xr8jnbvnZcdRaidztAPsXFBIqufiw== +zustand@^4.3.8: + version "4.3.8" + resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.3.8.tgz#37113df8e9e1421b0be1b2dca02b49b76210e7c4" + integrity sha512-4h28KCkHg5ii/wcFFJ5Fp+k1J3gJoasaIbppdgZFO4BPJnsNxL0mQXBSFgOgAdCdBj35aDTPvdAJReTMntFPGg== dependencies: use-sync-external-store "1.2.0"