mirror of
https://github.com/Yidadaa/ChatGPT-Next-Web.git
synced 2025-09-04 06:16:54 +08:00
Compare commits
35 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
7d7f3716be | ||
|
4e644cfca7 | ||
|
7572c99f4d | ||
|
bb30fdfa17 | ||
|
a4743034a9 | ||
|
c2a79e8a11 | ||
|
ec88785b6d | ||
|
d923869dc3 | ||
|
ae8ceb8dca | ||
|
8db26bbd5f | ||
|
efd3310711 | ||
|
a6890c0f58 | ||
|
b5a69b66b2 | ||
|
54447ba677 | ||
|
13a86aead7 | ||
|
73f4ea38c6 | ||
|
82c025a34a | ||
|
fb127f7857 | ||
|
6f82140ba5 | ||
|
6a9751efce | ||
|
5c75b6c784 | ||
|
34ea20b109 | ||
|
6e6d2a3310 | ||
|
56ba8a65e0 | ||
|
0e784c50ad | ||
|
73865651a0 | ||
|
f863a562a0 | ||
|
0d3bd42780 | ||
|
62f8675199 | ||
|
8a1c2f89be | ||
|
502d22bd20 | ||
|
f9f29afba9 | ||
|
c98517fc48 | ||
|
f606a61e1c | ||
|
4dbc984351 |
38
.github/workflows/sync.yml
vendored
38
.github/workflows/sync.yml
vendored
@@ -1,29 +1,33 @@
|
|||||||
name: Upstream Sync
|
name: Upstream Sync
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 */12 * * *' # every 12 hours
|
- cron: "0 */6 * * *" # every 6 hours
|
||||||
workflow_dispatch: # on button click
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
sync_latest_from_upstream:
|
sync_latest_from_upstream:
|
||||||
name: Sync latest commits from upstream repo
|
name: Sync latest commits from upstream repo
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.event.repository.fork }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
# Step 1: run a standard checkout action, provided by github
|
# Step 1: run a standard checkout action
|
||||||
- name: Checkout target repo
|
- name: Checkout target repo
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
# Step 2: run the sync action
|
# Step 2: run the sync action
|
||||||
- name: Sync upstream changes
|
- name: Sync upstream changes
|
||||||
id: sync
|
id: sync
|
||||||
uses: aormsby/Fork-Sync-With-Upstream-action@v3.4
|
uses: aormsby/Fork-Sync-With-Upstream-action@v3.4
|
||||||
with:
|
with:
|
||||||
upstream_sync_repo: Yidadaa/ChatGPT-Next-Web
|
upstream_sync_repo: Yidadaa/ChatGPT-Next-Web
|
||||||
upstream_sync_branch: main
|
upstream_sync_branch: main
|
||||||
target_sync_branch: main
|
target_sync_branch: main
|
||||||
target_repo_token: ${{ secrets.GITHUB_TOKEN }} # automatically generated, no need to set
|
target_repo_token: ${{ secrets.GITHUB_TOKEN }} # automatically generated, no need to set
|
||||||
|
|
||||||
# Set test_mode true to run tests instead of the true action!!
|
# Set test_mode true to run tests instead of the true action!!
|
||||||
test_mode: false
|
test_mode: false
|
||||||
|
13
README.md
13
README.md
@@ -21,7 +21,7 @@ One-Click to deploy your own ChatGPT web UI.
|
|||||||
|
|
||||||
- 在 1 分钟内使用 Vercel **免费一键部署**
|
- 在 1 分钟内使用 Vercel **免费一键部署**
|
||||||
- 精心设计的 UI,响应式设计,支持深色模式
|
- 精心设计的 UI,响应式设计,支持深色模式
|
||||||
- 极快的首屏加载速度(~85kb)
|
- 极快的首屏加载速度(~100kb)
|
||||||
- 海量的内置 prompt 列表,来自[中文](https://github.com/PlexPt/awesome-chatgpt-prompts-zh)和[英文](https://github.com/f/awesome-chatgpt-prompts)
|
- 海量的内置 prompt 列表,来自[中文](https://github.com/PlexPt/awesome-chatgpt-prompts-zh)和[英文](https://github.com/f/awesome-chatgpt-prompts)
|
||||||
- 自动压缩上下文聊天记录,在节省 Token 的同时支持超长对话
|
- 自动压缩上下文聊天记录,在节省 Token 的同时支持超长对话
|
||||||
- 一键导出聊天记录,完整的 Markdown 支持
|
- 一键导出聊天记录,完整的 Markdown 支持
|
||||||
@@ -31,7 +31,7 @@ One-Click to deploy your own ChatGPT web UI.
|
|||||||
|
|
||||||
- **Deploy for free with one-click** on Vercel in under 1 minute
|
- **Deploy for free with one-click** on Vercel in under 1 minute
|
||||||
- Responsive design, and dark mode
|
- Responsive design, and dark mode
|
||||||
- Fast first screen loading speed (~85kb)
|
- Fast first screen loading speed (~100kb)
|
||||||
- Awesome prompts powered by [awesome-chatgpt-prompts-zh](https://github.com/PlexPt/awesome-chatgpt-prompts-zh) and [awesome-chatgpt-prompts](https://github.com/f/awesome-chatgpt-prompts)
|
- Awesome prompts powered by [awesome-chatgpt-prompts-zh](https://github.com/PlexPt/awesome-chatgpt-prompts-zh) and [awesome-chatgpt-prompts](https://github.com/f/awesome-chatgpt-prompts)
|
||||||
- Automatically compresses chat history to support long conversations while also saving your tokens
|
- Automatically compresses chat history to support long conversations while also saving your tokens
|
||||||
- One-click export all chat history with full Markdown support
|
- One-click export all chat history with full Markdown support
|
||||||
@@ -74,7 +74,9 @@ One-Click to deploy your own ChatGPT web UI.
|
|||||||
- 前往 vercel 控制台,删除掉原先的 project,然后新建 project,选择你刚刚 fork 出来的项目重新进行部署即可;
|
- 前往 vercel 控制台,删除掉原先的 project,然后新建 project,选择你刚刚 fork 出来的项目重新进行部署即可;
|
||||||
- 在重新部署的过程中,请手动添加名为 `OPENAI_API_KEY` 的环境变量,并填入你的 api key 作为值。
|
- 在重新部署的过程中,请手动添加名为 `OPENAI_API_KEY` 的环境变量,并填入你的 api key 作为值。
|
||||||
|
|
||||||
本项目会持续更新,如果你想让代码库总是保持更新,可以查看 [Github 的文档](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork) 了解如何让 fork 的项目与上游代码同步,建议定期进行同步操作以获得新功能。
|
本项目会持续更新,当你 Fork 项目之后,默认会每天自动同步上游代码,无需额外操作。
|
||||||
|
|
||||||
|
如果你想让手动立即更新,可以查看 [Github 的文档](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork) 了解如何让 fork 的项目与上游代码同步。
|
||||||
|
|
||||||
你可以 star/watch 本项目或者 follow 作者来及时获得新功能更新通知。
|
你可以 star/watch 本项目或者 follow 作者来及时获得新功能更新通知。
|
||||||
|
|
||||||
@@ -87,7 +89,9 @@ We recommend that you follow the steps below to re-deploy:
|
|||||||
- Go to the Vercel dashboard, delete the original project, then create a new project and select the project you just forked to redeploy;
|
- Go to the Vercel dashboard, delete the original project, then create a new project and select the project you just forked to redeploy;
|
||||||
- Please manually add an environment variable named `OPENAI_API_KEY` and enter your API key as the value during the redeploy process.
|
- Please manually add an environment variable named `OPENAI_API_KEY` and enter your API key as the value during the redeploy process.
|
||||||
|
|
||||||
This project will be continuously maintained. If you want to keep the code repository up to date, you can check out the [Github documentation](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork) to learn how to synchronize a forked project with upstream code. It is recommended to perform synchronization operations regularly.
|
This project will be continuously updated, and after forking the project, the upstream code will be automatically synchronized every day without additional operations.
|
||||||
|
|
||||||
|
If you want to update instantly, you can check out the [Github documentation](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork) to learn how to synchronize a forked project with upstream code.
|
||||||
|
|
||||||
You can star or watch this project or follow author to get release notifictions in time.
|
You can star or watch this project or follow author to get release notifictions in time.
|
||||||
|
|
||||||
@@ -184,7 +188,6 @@ docker run -d -p 3000:3000 -e OPENAI_API_KEY="" -e CODE="" yidadaa/chatgpt-next-
|
|||||||
|
|
||||||
|
|
||||||
## 鸣谢 Special Thanks
|
## 鸣谢 Special Thanks
|
||||||
|
|
||||||
### 捐赠者 Sponsor
|
### 捐赠者 Sponsor
|
||||||
|
|
||||||
[@mushan0x0](https://github.com/mushan0x0)
|
[@mushan0x0](https://github.com/mushan0x0)
|
||||||
|
@@ -12,14 +12,7 @@ import BotIcon from "../icons/bot.svg";
|
|||||||
import AddIcon from "../icons/add.svg";
|
import AddIcon from "../icons/add.svg";
|
||||||
import DeleteIcon from "../icons/delete.svg";
|
import DeleteIcon from "../icons/delete.svg";
|
||||||
|
|
||||||
import {
|
import { Message, SubmitKey, useChatStore, BOT_HELLO, ROLES } from "../store";
|
||||||
Message,
|
|
||||||
SubmitKey,
|
|
||||||
useChatStore,
|
|
||||||
ChatSession,
|
|
||||||
BOT_HELLO,
|
|
||||||
ROLES,
|
|
||||||
} from "../store";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
copyToClipboard,
|
copyToClipboard,
|
||||||
@@ -377,7 +370,8 @@ export function Chat(props: {
|
|||||||
chatStore.onUserInput(userInput).then(() => setIsLoading(false));
|
chatStore.onUserInput(userInput).then(() => setIsLoading(false));
|
||||||
setUserInput("");
|
setUserInput("");
|
||||||
setPromptHints([]);
|
setPromptHints([]);
|
||||||
inputRef.current?.focus();
|
if (!isMobileScreen()) inputRef.current?.focus();
|
||||||
|
setAutoScroll(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
// stop response
|
// stop response
|
||||||
@@ -461,6 +455,7 @@ export function Chat(props: {
|
|||||||
|
|
||||||
// Auto focus
|
// Auto focus
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (props.sideBarShowing && isMobileScreen()) return;
|
||||||
inputRef.current?.focus();
|
inputRef.current?.focus();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -513,7 +508,10 @@ export function Chat(props: {
|
|||||||
bordered
|
bordered
|
||||||
title={Locale.Chat.Actions.Export}
|
title={Locale.Chat.Actions.Export}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
exportMessages(session.messages, session.topic);
|
exportMessages(
|
||||||
|
session.messages.filter((msg) => !msg.isError),
|
||||||
|
session.topic,
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -530,8 +528,11 @@ export function Chat(props: {
|
|||||||
className={styles["chat-body"]}
|
className={styles["chat-body"]}
|
||||||
ref={scrollRef}
|
ref={scrollRef}
|
||||||
onScroll={(e) => onChatBodyScroll(e.currentTarget)}
|
onScroll={(e) => onChatBodyScroll(e.currentTarget)}
|
||||||
onMouseOver={() => inputRef.current?.blur()}
|
onWheel={(e) => setAutoScroll(hitBottom && e.deltaY > 0)}
|
||||||
onTouchStart={() => inputRef.current?.blur()}
|
onTouchStart={() => {
|
||||||
|
inputRef.current?.blur();
|
||||||
|
setAutoScroll(false);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{messages.map((message, i) => {
|
{messages.map((message, i) => {
|
||||||
const isUser = message.role === "user";
|
const isUser = message.role === "user";
|
||||||
|
47
app/components/error.tsx
Normal file
47
app/components/error.tsx
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { IconButton } from "./button";
|
||||||
|
import GithubIcon from "../icons/github.svg";
|
||||||
|
import { ISSUE_URL } from "../constant";
|
||||||
|
|
||||||
|
interface IErrorBoundaryState {
|
||||||
|
hasError: boolean;
|
||||||
|
error: Error | null;
|
||||||
|
info: React.ErrorInfo | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ErrorBoundary extends React.Component<any, IErrorBoundaryState> {
|
||||||
|
constructor(props: any) {
|
||||||
|
super(props);
|
||||||
|
this.state = { hasError: false, error: null, info: null };
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidCatch(error: Error, info: React.ErrorInfo) {
|
||||||
|
// Update state with error details
|
||||||
|
this.setState({ hasError: true, error, info });
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.state.hasError) {
|
||||||
|
// Render error message
|
||||||
|
return (
|
||||||
|
<div className="error">
|
||||||
|
<h2>Oops, something went wrong!</h2>
|
||||||
|
<pre>
|
||||||
|
<code>{this.state.error?.toString()}</code>
|
||||||
|
<code>{this.state.info?.componentStack}</code>
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<a href={ISSUE_URL} className="report">
|
||||||
|
<IconButton
|
||||||
|
text="Report This Error"
|
||||||
|
icon={<GithubIcon />}
|
||||||
|
bordered
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// if no error occurred, render children
|
||||||
|
return this.props.children;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useRef, useEffect, useLayoutEffect } from "react";
|
require("../polyfill");
|
||||||
|
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
|
||||||
import { IconButton } from "./button";
|
import { IconButton } from "./button";
|
||||||
import styles from "./home.module.scss";
|
import styles from "./home.module.scss";
|
||||||
@@ -14,25 +16,15 @@ import AddIcon from "../icons/add.svg";
|
|||||||
import LoadingIcon from "../icons/three-dots.svg";
|
import LoadingIcon from "../icons/three-dots.svg";
|
||||||
import CloseIcon from "../icons/close.svg";
|
import CloseIcon from "../icons/close.svg";
|
||||||
|
|
||||||
import {
|
import { useChatStore } from "../store";
|
||||||
Message,
|
import { isMobileScreen } from "../utils";
|
||||||
SubmitKey,
|
|
||||||
useChatStore,
|
|
||||||
ChatSession,
|
|
||||||
BOT_HELLO,
|
|
||||||
} from "../store";
|
|
||||||
import {
|
|
||||||
copyToClipboard,
|
|
||||||
downloadAs,
|
|
||||||
isMobileScreen,
|
|
||||||
selectOrCopy,
|
|
||||||
} from "../utils";
|
|
||||||
import Locale from "../locales";
|
import Locale from "../locales";
|
||||||
import { ChatList } from "./chat-list";
|
import { ChatList } from "./chat-list";
|
||||||
import { Chat } from "./chat";
|
import { Chat } from "./chat";
|
||||||
|
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import { REPO_URL } from "../constant";
|
import { REPO_URL } from "../constant";
|
||||||
|
import { ErrorBoundary } from "./error";
|
||||||
|
|
||||||
export function Loading(props: { noLogo?: boolean }) {
|
export function Loading(props: { noLogo?: boolean }) {
|
||||||
return (
|
return (
|
||||||
@@ -78,7 +70,7 @@ const useHasHydrated = () => {
|
|||||||
return hasHydrated;
|
return hasHydrated;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function Home() {
|
function _Home() {
|
||||||
const [createNewSession, currentIndex, removeSession] = useChatStore(
|
const [createNewSession, currentIndex, removeSession] = useChatStore(
|
||||||
(state) => [
|
(state) => [
|
||||||
state.newSession,
|
state.newSession,
|
||||||
@@ -191,3 +183,11 @@ export function Home() {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function Home() {
|
||||||
|
return (
|
||||||
|
<ErrorBoundary>
|
||||||
|
<_Home></_Home>
|
||||||
|
</ErrorBoundary>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@@ -18,3 +18,12 @@
|
|||||||
.avatar {
|
.avatar {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.password-input {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
.password-eye {
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { useState, useEffect, useRef, useMemo } from "react";
|
import { useState, useEffect, useMemo, HTMLProps } from "react";
|
||||||
|
|
||||||
import EmojiPicker, { Theme as EmojiTheme } from "emoji-picker-react";
|
import EmojiPicker, { Theme as EmojiTheme } from "emoji-picker-react";
|
||||||
|
|
||||||
@@ -8,6 +8,8 @@ import ResetIcon from "../icons/reload.svg";
|
|||||||
import CloseIcon from "../icons/close.svg";
|
import CloseIcon from "../icons/close.svg";
|
||||||
import ClearIcon from "../icons/clear.svg";
|
import ClearIcon from "../icons/clear.svg";
|
||||||
import EditIcon from "../icons/edit.svg";
|
import EditIcon from "../icons/edit.svg";
|
||||||
|
import EyeIcon from "../icons/eye.svg";
|
||||||
|
import EyeOffIcon from "../icons/eye-off.svg";
|
||||||
|
|
||||||
import { List, ListItem, Popover, showToast } from "./ui-lib";
|
import { List, ListItem, Popover, showToast } from "./ui-lib";
|
||||||
|
|
||||||
@@ -19,6 +21,7 @@ import {
|
|||||||
ALL_MODELS,
|
ALL_MODELS,
|
||||||
useUpdateStore,
|
useUpdateStore,
|
||||||
useAccessStore,
|
useAccessStore,
|
||||||
|
ModalConfigValidator,
|
||||||
} from "../store";
|
} from "../store";
|
||||||
import { Avatar } from "./chat";
|
import { Avatar } from "./chat";
|
||||||
|
|
||||||
@@ -28,6 +31,7 @@ import Link from "next/link";
|
|||||||
import { UPDATE_URL } from "../constant";
|
import { UPDATE_URL } from "../constant";
|
||||||
import { SearchService, usePromptStore } from "../store/prompt";
|
import { SearchService, usePromptStore } from "../store/prompt";
|
||||||
import { requestUsage } from "../requests";
|
import { requestUsage } from "../requests";
|
||||||
|
import { ErrorBoundary } from "./error";
|
||||||
|
|
||||||
function SettingItem(props: {
|
function SettingItem(props: {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -47,6 +51,25 @@ function SettingItem(props: {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function PasswordInput(props: HTMLProps<HTMLInputElement>) {
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
|
||||||
|
function changeVisibility() {
|
||||||
|
setVisible(!visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles["password-input"]}>
|
||||||
|
<IconButton
|
||||||
|
icon={visible ? <EyeIcon /> : <EyeOffIcon />}
|
||||||
|
onClick={changeVisibility}
|
||||||
|
className={styles["password-eye"]}
|
||||||
|
/>
|
||||||
|
<input {...props} type={visible ? "text" : "password"} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function Settings(props: { closeSettings: () => void }) {
|
export function Settings(props: { closeSettings: () => void }) {
|
||||||
const [showEmojiPicker, setShowEmojiPicker] = useState(false);
|
const [showEmojiPicker, setShowEmojiPicker] = useState(false);
|
||||||
const [config, updateConfig, resetConfig, clearAllData, clearSessions] =
|
const [config, updateConfig, resetConfig, clearAllData, clearSessions] =
|
||||||
@@ -91,11 +114,13 @@ export function Settings(props: { closeSettings: () => void }) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
checkUpdate();
|
checkUpdate();
|
||||||
checkUsage();
|
checkUsage();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const accessStore = useAccessStore();
|
const accessStore = useAccessStore();
|
||||||
const enabledAccessControl = useMemo(
|
const enabledAccessControl = useMemo(
|
||||||
() => accessStore.enabledAccessControl(),
|
() => accessStore.enabledAccessControl(),
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -103,8 +128,15 @@ export function Settings(props: { closeSettings: () => void }) {
|
|||||||
const builtinCount = SearchService.count.builtin;
|
const builtinCount = SearchService.count.builtin;
|
||||||
const customCount = promptStore.prompts.size ?? 0;
|
const customCount = promptStore.prompts.size ?? 0;
|
||||||
|
|
||||||
|
const showUsage = accessStore.token !== "";
|
||||||
|
useEffect(() => {
|
||||||
|
if (showUsage) {
|
||||||
|
checkUsage();
|
||||||
|
}
|
||||||
|
}, [showUsage]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<ErrorBoundary>
|
||||||
<div className={styles["window-header"]}>
|
<div className={styles["window-header"]}>
|
||||||
<div className={styles["window-header-title"]}>
|
<div className={styles["window-header-title"]}>
|
||||||
<div className={styles["window-header-main-title"]}>
|
<div className={styles["window-header-main-title"]}>
|
||||||
@@ -327,14 +359,14 @@ export function Settings(props: { closeSettings: () => void }) {
|
|||||||
title={Locale.Settings.AccessCode.Title}
|
title={Locale.Settings.AccessCode.Title}
|
||||||
subTitle={Locale.Settings.AccessCode.SubTitle}
|
subTitle={Locale.Settings.AccessCode.SubTitle}
|
||||||
>
|
>
|
||||||
<input
|
<PasswordInput
|
||||||
value={accessStore.accessCode}
|
value={accessStore.accessCode}
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={Locale.Settings.AccessCode.Placeholder}
|
placeholder={Locale.Settings.AccessCode.Placeholder}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
accessStore.updateCode(e.currentTarget.value);
|
accessStore.updateCode(e.currentTarget.value);
|
||||||
}}
|
}}
|
||||||
></input>
|
/>
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
) : (
|
) : (
|
||||||
<></>
|
<></>
|
||||||
@@ -344,25 +376,27 @@ export function Settings(props: { closeSettings: () => void }) {
|
|||||||
title={Locale.Settings.Token.Title}
|
title={Locale.Settings.Token.Title}
|
||||||
subTitle={Locale.Settings.Token.SubTitle}
|
subTitle={Locale.Settings.Token.SubTitle}
|
||||||
>
|
>
|
||||||
<input
|
<PasswordInput
|
||||||
value={accessStore.token}
|
value={accessStore.token}
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={Locale.Settings.Token.Placeholder}
|
placeholder={Locale.Settings.Token.Placeholder}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
accessStore.updateToken(e.currentTarget.value);
|
accessStore.updateToken(e.currentTarget.value);
|
||||||
}}
|
}}
|
||||||
></input>
|
/>
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
|
|
||||||
<SettingItem
|
<SettingItem
|
||||||
title={Locale.Settings.Usage.Title}
|
title={Locale.Settings.Usage.Title}
|
||||||
subTitle={
|
subTitle={
|
||||||
loadingUsage
|
showUsage
|
||||||
? Locale.Settings.Usage.IsChecking
|
? loadingUsage
|
||||||
: Locale.Settings.Usage.SubTitle(usage?.used ?? "[?]")
|
? Locale.Settings.Usage.IsChecking
|
||||||
|
: Locale.Settings.Usage.SubTitle(usage?.used ?? "[?]")
|
||||||
|
: Locale.Settings.Usage.NoAccess
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{loadingUsage ? (
|
{!showUsage || loadingUsage ? (
|
||||||
<div />
|
<div />
|
||||||
) : (
|
) : (
|
||||||
<IconButton
|
<IconButton
|
||||||
@@ -420,7 +454,9 @@ export function Settings(props: { closeSettings: () => void }) {
|
|||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
updateConfig(
|
updateConfig(
|
||||||
(config) =>
|
(config) =>
|
||||||
(config.modelConfig.model = e.currentTarget.value),
|
(config.modelConfig.model = ModalConfigValidator.model(
|
||||||
|
e.currentTarget.value,
|
||||||
|
)),
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -437,7 +473,7 @@ export function Settings(props: { closeSettings: () => void }) {
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="range"
|
type="range"
|
||||||
value={config.modelConfig.temperature.toFixed(1)}
|
value={config.modelConfig.temperature?.toFixed(1)}
|
||||||
min="0"
|
min="0"
|
||||||
max="2"
|
max="2"
|
||||||
step="0.1"
|
step="0.1"
|
||||||
@@ -445,7 +481,9 @@ export function Settings(props: { closeSettings: () => void }) {
|
|||||||
updateConfig(
|
updateConfig(
|
||||||
(config) =>
|
(config) =>
|
||||||
(config.modelConfig.temperature =
|
(config.modelConfig.temperature =
|
||||||
e.currentTarget.valueAsNumber),
|
ModalConfigValidator.temperature(
|
||||||
|
e.currentTarget.valueAsNumber,
|
||||||
|
)),
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
></input>
|
></input>
|
||||||
@@ -457,13 +495,15 @@ export function Settings(props: { closeSettings: () => void }) {
|
|||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
min={100}
|
min={100}
|
||||||
max={4096}
|
max={32000}
|
||||||
value={config.modelConfig.max_tokens}
|
value={config.modelConfig.max_tokens}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
updateConfig(
|
updateConfig(
|
||||||
(config) =>
|
(config) =>
|
||||||
(config.modelConfig.max_tokens =
|
(config.modelConfig.max_tokens =
|
||||||
e.currentTarget.valueAsNumber),
|
ModalConfigValidator.max_tokens(
|
||||||
|
e.currentTarget.valueAsNumber,
|
||||||
|
)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
></input>
|
></input>
|
||||||
@@ -474,7 +514,7 @@ export function Settings(props: { closeSettings: () => void }) {
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="range"
|
type="range"
|
||||||
value={config.modelConfig.presence_penalty.toFixed(1)}
|
value={config.modelConfig.presence_penalty?.toFixed(1)}
|
||||||
min="-2"
|
min="-2"
|
||||||
max="2"
|
max="2"
|
||||||
step="0.5"
|
step="0.5"
|
||||||
@@ -482,13 +522,15 @@ export function Settings(props: { closeSettings: () => void }) {
|
|||||||
updateConfig(
|
updateConfig(
|
||||||
(config) =>
|
(config) =>
|
||||||
(config.modelConfig.presence_penalty =
|
(config.modelConfig.presence_penalty =
|
||||||
e.currentTarget.valueAsNumber),
|
ModalConfigValidator.presence_penalty(
|
||||||
|
e.currentTarget.valueAsNumber,
|
||||||
|
)),
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
></input>
|
></input>
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
</List>
|
</List>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</ErrorBoundary>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
export const OWNER = "Yidadaa";
|
export const OWNER = "Yidadaa";
|
||||||
export const REPO = "ChatGPT-Next-Web";
|
export const REPO = "ChatGPT-Next-Web";
|
||||||
export const REPO_URL = `https://github.com/${OWNER}/${REPO}`;
|
export const REPO_URL = `https://github.com/${OWNER}/${REPO}`;
|
||||||
|
export const ISSUE_URL = `https://github.com/${OWNER}/${REPO}/issues`;
|
||||||
export const UPDATE_URL = `${REPO_URL}#%E4%BF%9D%E6%8C%81%E6%9B%B4%E6%96%B0-keep-updated`;
|
export const UPDATE_URL = `${REPO_URL}#%E4%BF%9D%E6%8C%81%E6%9B%B4%E6%96%B0-keep-updated`;
|
||||||
export const FETCH_COMMIT_URL = `https://api.github.com/repos/${OWNER}/${REPO}/commits?per_page=1`;
|
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 FETCH_TAG_URL = `https://api.github.com/repos/${OWNER}/${REPO}/tags?per_page=1`;
|
||||||
|
4
app/icons/eye-off.svg
Normal file
4
app/icons/eye-off.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.7071 5.70711C20.0976 5.31658 20.0976 4.68342 19.7071 4.29289C19.3166 3.90237 18.6834 3.90237 18.2929 4.29289L14.032 8.55382C13.4365 8.20193 12.7418 8 12 8C9.79086 8 8 9.79086 8 12C8 12.7418 8.20193 13.4365 8.55382 14.032L4.29289 18.2929C3.90237 18.6834 3.90237 19.3166 4.29289 19.7071C4.68342 20.0976 5.31658 20.0976 5.70711 19.7071L9.96803 15.4462C10.5635 15.7981 11.2582 16 12 16C14.2091 16 16 14.2091 16 12C16 11.2582 15.7981 10.5635 15.4462 9.96803L19.7071 5.70711ZM12.518 10.0677C12.3528 10.0236 12.1792 10 12 10C10.8954 10 10 10.8954 10 12C10 12.1792 10.0236 12.3528 10.0677 12.518L12.518 10.0677ZM11.482 13.9323L13.9323 11.482C13.9764 11.6472 14 11.8208 14 12C14 13.1046 13.1046 14 12 14C11.8208 14 11.6472 13.9764 11.482 13.9323ZM15.7651 4.8207C14.6287 4.32049 13.3675 4 12 4C9.14754 4 6.75717 5.39462 4.99812 6.90595C3.23268 8.42276 2.00757 10.1376 1.46387 10.9698C1.05306 11.5985 1.05306 12.4015 1.46387 13.0302C1.92276 13.7326 2.86706 15.0637 4.21194 16.3739L5.62626 14.9596C4.4555 13.8229 3.61144 12.6531 3.18002 12C3.6904 11.2274 4.77832 9.73158 6.30147 8.42294C7.87402 7.07185 9.81574 6 12 6C12.7719 6 13.5135 6.13385 14.2193 6.36658L15.7651 4.8207ZM12 18C11.2282 18 10.4866 17.8661 9.78083 17.6334L8.23496 19.1793C9.37136 19.6795 10.6326 20 12 20C14.8525 20 17.2429 18.6054 19.002 17.0941C20.7674 15.5772 21.9925 13.8624 22.5362 13.0302C22.947 12.4015 22.947 11.5985 22.5362 10.9698C22.0773 10.2674 21.133 8.93627 19.7881 7.62611L18.3738 9.04043C19.5446 10.1771 20.3887 11.3469 20.8201 12C20.3097 12.7726 19.2218 14.2684 17.6986 15.5771C16.1261 16.9282 14.1843 18 12 18Z" fill="#000000"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
4
app/icons/eye.svg
Normal file
4
app/icons/eye.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.30147 15.5771C4.77832 14.2684 3.6904 12.7726 3.18002 12C3.6904 11.2274 4.77832 9.73158 6.30147 8.42294C7.87402 7.07185 9.81574 6 12 6C14.1843 6 16.1261 7.07185 17.6986 8.42294C19.2218 9.73158 20.3097 11.2274 20.8201 12C20.3097 12.7726 19.2218 14.2684 17.6986 15.5771C16.1261 16.9282 14.1843 18 12 18C9.81574 18 7.87402 16.9282 6.30147 15.5771ZM12 4C9.14754 4 6.75717 5.39462 4.99812 6.90595C3.23268 8.42276 2.00757 10.1376 1.46387 10.9698C1.05306 11.5985 1.05306 12.4015 1.46387 13.0302C2.00757 13.8624 3.23268 15.5772 4.99812 17.0941C6.75717 18.6054 9.14754 20 12 20C14.8525 20 17.2429 18.6054 19.002 17.0941C20.7674 15.5772 21.9925 13.8624 22.5362 13.0302C22.947 12.4015 22.947 11.5985 22.5362 10.9698C21.9925 10.1376 20.7674 8.42276 19.002 6.90595C17.2429 5.39462 14.8525 4 12 4ZM10 12C10 10.8954 10.8955 10 12 10C13.1046 10 14 10.8954 14 12C14 13.1046 13.1046 14 12 14C10.8955 14 10 13.1046 10 12ZM12 8C9.7909 8 8.00004 9.79086 8.00004 12C8.00004 14.2091 9.7909 16 12 16C14.2092 16 16 14.2091 16 12C16 9.79086 14.2092 8 12 8Z" fill="#000000"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
@@ -58,6 +58,7 @@ const cn = {
|
|||||||
en: "English",
|
en: "English",
|
||||||
tw: "繁體中文",
|
tw: "繁體中文",
|
||||||
es: "Español",
|
es: "Español",
|
||||||
|
it: "Italiano",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Avatar: "头像",
|
Avatar: "头像",
|
||||||
@@ -108,6 +109,7 @@ const cn = {
|
|||||||
},
|
},
|
||||||
IsChecking: "正在检查…",
|
IsChecking: "正在检查…",
|
||||||
Check: "重新检查",
|
Check: "重新检查",
|
||||||
|
NoAccess: "输入API Key查看余额",
|
||||||
},
|
},
|
||||||
AccessCode: {
|
AccessCode: {
|
||||||
Title: "访问码",
|
Title: "访问码",
|
||||||
|
@@ -60,6 +60,7 @@ const en: LocaleType = {
|
|||||||
en: "English",
|
en: "English",
|
||||||
tw: "繁體中文",
|
tw: "繁體中文",
|
||||||
es: "Español",
|
es: "Español",
|
||||||
|
it: "Italiano",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Avatar: "Avatar",
|
Avatar: "Avatar",
|
||||||
@@ -110,6 +111,7 @@ const en: LocaleType = {
|
|||||||
},
|
},
|
||||||
IsChecking: "Checking...",
|
IsChecking: "Checking...",
|
||||||
Check: "Check Again",
|
Check: "Check Again",
|
||||||
|
NoAccess: "Enter API Key to check balance",
|
||||||
},
|
},
|
||||||
AccessCode: {
|
AccessCode: {
|
||||||
Title: "Access Code",
|
Title: "Access Code",
|
||||||
|
@@ -60,6 +60,7 @@ const es: LocaleType = {
|
|||||||
en: "Inglés",
|
en: "Inglés",
|
||||||
tw: "繁體中文",
|
tw: "繁體中文",
|
||||||
es: "Español",
|
es: "Español",
|
||||||
|
it: "Italiano",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Avatar: "Avatar",
|
Avatar: "Avatar",
|
||||||
@@ -110,6 +111,7 @@ const es: LocaleType = {
|
|||||||
},
|
},
|
||||||
IsChecking: "Comprobando...",
|
IsChecking: "Comprobando...",
|
||||||
Check: "Comprobar de nuevo",
|
Check: "Comprobar de nuevo",
|
||||||
|
NoAccess: "Introduzca la clave API para comprobar el saldo",
|
||||||
},
|
},
|
||||||
AccessCode: {
|
AccessCode: {
|
||||||
Title: "Código de acceso",
|
Title: "Código de acceso",
|
||||||
|
@@ -2,10 +2,11 @@ import CN from "./cn";
|
|||||||
import EN from "./en";
|
import EN from "./en";
|
||||||
import TW from "./tw";
|
import TW from "./tw";
|
||||||
import ES from "./es";
|
import ES from "./es";
|
||||||
|
import IT from "./it";
|
||||||
|
|
||||||
export type { LocaleType } from "./cn";
|
export type { LocaleType } from "./cn";
|
||||||
|
|
||||||
export const AllLangs = ["cn", "tw", "en", "es"] as const;
|
export const AllLangs = ["en", "cn", "tw", "es", "it"] as const;
|
||||||
type Lang = (typeof AllLangs)[number];
|
type Lang = (typeof AllLangs)[number];
|
||||||
|
|
||||||
const LANG_KEY = "lang";
|
const LANG_KEY = "lang";
|
||||||
@@ -47,6 +48,8 @@ export function getLang(): Lang {
|
|||||||
return "tw";
|
return "tw";
|
||||||
} else if (lang.includes("es")) {
|
} else if (lang.includes("es")) {
|
||||||
return "es";
|
return "es";
|
||||||
|
} else if (lang.includes("it")) {
|
||||||
|
return "it";
|
||||||
} else {
|
} else {
|
||||||
return "en";
|
return "en";
|
||||||
}
|
}
|
||||||
@@ -57,4 +60,4 @@ export function changeLang(lang: Lang) {
|
|||||||
location.reload();
|
location.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
export default { en: EN, cn: CN, tw: TW, es: ES }[getLang()];
|
export default { en: EN, cn: CN, tw: TW, es: ES, it: IT }[getLang()];
|
||||||
|
165
app/locales/it.ts
Normal file
165
app/locales/it.ts
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
import { SubmitKey } from "../store/app";
|
||||||
|
import type { LocaleType } from "./index";
|
||||||
|
|
||||||
|
const it: LocaleType = {
|
||||||
|
WIP: "Work in progress...",
|
||||||
|
Error: {
|
||||||
|
Unauthorized:
|
||||||
|
"Accesso non autorizzato, inserire il codice di accesso nella pagina delle impostazioni.",
|
||||||
|
},
|
||||||
|
ChatItem: {
|
||||||
|
ChatItemCount: (count: number) => `${count} messaggi`,
|
||||||
|
},
|
||||||
|
Chat: {
|
||||||
|
SubTitle: (count: number) => `${count} messaggi con ChatGPT`,
|
||||||
|
Actions: {
|
||||||
|
ChatList: "Vai alla Chat List",
|
||||||
|
CompressedHistory: "Prompt di memoria della cronologia compressa",
|
||||||
|
Export: "Esportazione di tutti i messaggi come Markdown",
|
||||||
|
Copy: "Copia",
|
||||||
|
Stop: "Stop",
|
||||||
|
Retry: "Riprova",
|
||||||
|
},
|
||||||
|
Rename: "Rinomina Chat",
|
||||||
|
Typing: "Typing…",
|
||||||
|
Input: (submitKey: string) => {
|
||||||
|
var inputHints = `Scrivi qualcosa e premi ${submitKey} per inviare`;
|
||||||
|
if (submitKey === String(SubmitKey.Enter)) {
|
||||||
|
inputHints += ", premi Shift + Enter per andare a capo";
|
||||||
|
}
|
||||||
|
return inputHints;
|
||||||
|
},
|
||||||
|
Send: "Invia",
|
||||||
|
},
|
||||||
|
Export: {
|
||||||
|
Title: "Tutti i messaggi",
|
||||||
|
Copy: "Copia tutto",
|
||||||
|
Download: "Scarica",
|
||||||
|
},
|
||||||
|
Memory: {
|
||||||
|
Title: "Prompt di memoria",
|
||||||
|
EmptyContent: "Vuoto.",
|
||||||
|
Copy: "Copia tutto",
|
||||||
|
},
|
||||||
|
Home: {
|
||||||
|
NewChat: "Nuova Chat",
|
||||||
|
DeleteChat: "Confermare la cancellazione della conversazione selezionata?",
|
||||||
|
},
|
||||||
|
Settings: {
|
||||||
|
Title: "Impostazioni",
|
||||||
|
SubTitle: "Tutte le impostazioni",
|
||||||
|
Actions: {
|
||||||
|
ClearAll: "Cancella tutti i dati",
|
||||||
|
ResetAll: "Resetta tutte le impostazioni",
|
||||||
|
Close: "Chiudi",
|
||||||
|
},
|
||||||
|
Lang: {
|
||||||
|
Name: "Lingue",
|
||||||
|
Options: {
|
||||||
|
cn: "简体中文",
|
||||||
|
en: "English",
|
||||||
|
tw: "繁體中文",
|
||||||
|
es: "Español",
|
||||||
|
it: "Italiano",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Avatar: "Avatar",
|
||||||
|
FontSize: {
|
||||||
|
Title: "Dimensione carattere",
|
||||||
|
SubTitle: "Regolare la dimensione dei caratteri del contenuto della chat",
|
||||||
|
},
|
||||||
|
Update: {
|
||||||
|
Version: (x: string) => `Versione: ${x}`,
|
||||||
|
IsLatest: "Ultima versione",
|
||||||
|
CheckUpdate: "Controlla aggiornamenti",
|
||||||
|
IsChecking: "Sto controllando gli aggiornamenti...",
|
||||||
|
FoundUpdate: (x: string) => `Trovata nuova versione: ${x}`,
|
||||||
|
GoToUpdate: "Aggiorna",
|
||||||
|
},
|
||||||
|
SendKey: "Tasto invia",
|
||||||
|
Theme: "tema",
|
||||||
|
TightBorder: "Bordi stretti",
|
||||||
|
SendPreviewBubble: "Invia l'anteprima della bolla",
|
||||||
|
Prompt: {
|
||||||
|
Disable: {
|
||||||
|
Title: "Disabilita l'auto completamento",
|
||||||
|
SubTitle: "Input / per attivare il completamento automatico",
|
||||||
|
},
|
||||||
|
List: "Elenco dei suggerimenti",
|
||||||
|
ListCount: (builtin: number, custom: number) =>
|
||||||
|
`${builtin} built-in, ${custom} user-defined`,
|
||||||
|
Edit: "Modifica",
|
||||||
|
},
|
||||||
|
HistoryCount: {
|
||||||
|
Title: "Conteggio dei messaggi allegati",
|
||||||
|
SubTitle: "Numero di messaggi inviati allegati per richiesta",
|
||||||
|
},
|
||||||
|
CompressThreshold: {
|
||||||
|
Title: "Soglia di compressione della cronologia",
|
||||||
|
SubTitle:
|
||||||
|
"Comprimerà se la lunghezza dei messaggi non compressi supera il valore",
|
||||||
|
},
|
||||||
|
Token: {
|
||||||
|
Title: "Chiave API",
|
||||||
|
SubTitle:
|
||||||
|
"Utilizzare la chiave per ignorare il limite del codice di accesso",
|
||||||
|
Placeholder: "OpenAI API Key",
|
||||||
|
},
|
||||||
|
Usage: {
|
||||||
|
Title: "Bilancio Account",
|
||||||
|
SubTitle(used: any) {
|
||||||
|
return `Usato in questo mese $${used}`;
|
||||||
|
},
|
||||||
|
IsChecking: "Controllando...",
|
||||||
|
Check: "Controlla ancora",
|
||||||
|
NoAccess: "Inserire la chiave API per controllare il saldo",
|
||||||
|
},
|
||||||
|
AccessCode: {
|
||||||
|
Title: "Codice d'accesso",
|
||||||
|
SubTitle: "Controllo d'accesso abilitato",
|
||||||
|
Placeholder: "Inserisci il codice d'accesso",
|
||||||
|
},
|
||||||
|
Model: "Modello GPT",
|
||||||
|
Temperature: {
|
||||||
|
Title: "Temperature",
|
||||||
|
SubTitle: "Un valore maggiore rende l'output più casuale",
|
||||||
|
},
|
||||||
|
MaxTokens: {
|
||||||
|
Title: "Token massimi",
|
||||||
|
SubTitle: "Lunghezza massima dei token in ingresso e dei token generati",
|
||||||
|
},
|
||||||
|
PresencePenlty: {
|
||||||
|
Title: "Penalità di presenza",
|
||||||
|
SubTitle:
|
||||||
|
"Un valore maggiore aumenta la probabilità di parlare di nuovi argomenti",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Store: {
|
||||||
|
DefaultTopic: "Nuova conversazione",
|
||||||
|
BotHello: "Ciao, come posso aiutarti oggi?",
|
||||||
|
Error: "Qualcosa è andato storto, riprova più tardi.",
|
||||||
|
Prompt: {
|
||||||
|
History: (content: string) =>
|
||||||
|
"Questo è un riassunto della cronologia delle chat tra l'IA e l'utente:" +
|
||||||
|
content,
|
||||||
|
Topic:
|
||||||
|
"Si prega di generare un titolo di quattro o cinque parole che riassuma la nostra conversazione senza alcuna traccia, punteggiatura, virgolette, punti, simboli o testo aggiuntivo. Rimuovere le virgolette",
|
||||||
|
Summarize:
|
||||||
|
"Riassumi brevemente la nostra discussione in 200 caratteri o meno per usarla come spunto per una futura conversazione.",
|
||||||
|
},
|
||||||
|
ConfirmClearAll:
|
||||||
|
"Confermi la cancellazione di tutti i dati della chat e delle impostazioni?",
|
||||||
|
},
|
||||||
|
Copy: {
|
||||||
|
Success: "Copiato sugli appunti",
|
||||||
|
Failed:
|
||||||
|
"Copia fallita, concedere l'autorizzazione all'accesso agli appunti",
|
||||||
|
},
|
||||||
|
Context: {
|
||||||
|
Toast: (x: any) => `Con ${x} prompts contestuali`,
|
||||||
|
Edit: "Prompt contestuali e di memoria",
|
||||||
|
Add: "Aggiungi altro",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default it;
|
@@ -59,6 +59,7 @@ const tw: LocaleType = {
|
|||||||
en: "English",
|
en: "English",
|
||||||
tw: "繁體中文",
|
tw: "繁體中文",
|
||||||
es: "Español",
|
es: "Español",
|
||||||
|
it: "Italiano",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Avatar: "大頭貼",
|
Avatar: "大頭貼",
|
||||||
@@ -108,6 +109,7 @@ const tw: LocaleType = {
|
|||||||
},
|
},
|
||||||
IsChecking: "正在檢查…",
|
IsChecking: "正在檢查…",
|
||||||
Check: "重新檢查",
|
Check: "重新檢查",
|
||||||
|
NoAccess: "輸入API Key查看餘額",
|
||||||
},
|
},
|
||||||
AccessCode: {
|
AccessCode: {
|
||||||
Title: "訪問碼",
|
Title: "訪問碼",
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
import { Analytics } from "@vercel/analytics/react";
|
import { Analytics } from "@vercel/analytics/react";
|
||||||
|
|
||||||
import "array.prototype.at";
|
|
||||||
|
|
||||||
import { Home } from "./components/home";
|
import { Home } from "./components/home";
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
|
27
app/polyfill.ts
Normal file
27
app/polyfill.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
declare global {
|
||||||
|
interface Array<T> {
|
||||||
|
at(index: number): T | undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Array.prototype.at) {
|
||||||
|
Array.prototype.at = function (index: number) {
|
||||||
|
// Get the length of the array
|
||||||
|
const length = this.length;
|
||||||
|
|
||||||
|
// Convert negative index to a positive index
|
||||||
|
if (index < 0) {
|
||||||
|
index = length + index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return undefined if the index is out of range
|
||||||
|
if (index < 0 || index >= length) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use Array.prototype.slice method to get value at the specified index
|
||||||
|
return Array.prototype.slice.call(this, index, index + 1)[0];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export {};
|
@@ -1,5 +1,5 @@
|
|||||||
import type { ChatRequest, ChatReponse } from "./api/openai/typing";
|
import type { ChatRequest, ChatReponse } from "./api/openai/typing";
|
||||||
import { filterConfig, Message, ModelConfig, useAccessStore } from "./store";
|
import { Message, ModelConfig, useAccessStore } from "./store";
|
||||||
import Locale from "./locales";
|
import Locale from "./locales";
|
||||||
import { showToast } from "./components/ui-lib";
|
import { showToast } from "./components/ui-lib";
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ export async function requestChatStream(
|
|||||||
filterBot?: boolean;
|
filterBot?: boolean;
|
||||||
modelConfig?: ModelConfig;
|
modelConfig?: ModelConfig;
|
||||||
onMessage: (message: string, done: boolean) => void;
|
onMessage: (message: string, done: boolean) => void;
|
||||||
onError: (error: Error) => void;
|
onError: (error: Error, statusCode?: number) => void;
|
||||||
onController?: (controller: AbortController) => void;
|
onController?: (controller: AbortController) => void;
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
@@ -123,11 +123,6 @@ export async function requestChatStream(
|
|||||||
filterBot: options?.filterBot,
|
filterBot: options?.filterBot,
|
||||||
});
|
});
|
||||||
|
|
||||||
// valid and assign model config
|
|
||||||
if (options?.modelConfig) {
|
|
||||||
Object.assign(req, filterConfig(options.modelConfig));
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("[Request] ", req);
|
console.log("[Request] ", req);
|
||||||
|
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
@@ -178,11 +173,10 @@ export async function requestChatStream(
|
|||||||
finish();
|
finish();
|
||||||
} else if (res.status === 401) {
|
} else if (res.status === 401) {
|
||||||
console.error("Anauthorized");
|
console.error("Anauthorized");
|
||||||
responseText = Locale.Error.Unauthorized;
|
options?.onError(new Error("Anauthorized"), res.status);
|
||||||
finish();
|
|
||||||
} else {
|
} else {
|
||||||
console.error("Stream Error", res.body);
|
console.error("Stream Error", res.body);
|
||||||
options?.onError(new Error("Stream Error"));
|
options?.onError(new Error("Stream Error"), res.status);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("NetWork Error", err);
|
console.error("NetWork Error", err);
|
||||||
|
@@ -14,6 +14,7 @@ import Locale from "../locales";
|
|||||||
export type Message = ChatCompletionResponseMessage & {
|
export type Message = ChatCompletionResponseMessage & {
|
||||||
date: string;
|
date: string;
|
||||||
streaming?: boolean;
|
streaming?: boolean;
|
||||||
|
isError?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum SubmitKey {
|
export enum SubmitKey {
|
||||||
@@ -84,43 +85,39 @@ export const ALL_MODELS = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export function isValidModel(name: string) {
|
export function limitNumber(
|
||||||
return ALL_MODELS.some((m) => m.name === name && m.available);
|
x: number,
|
||||||
|
min: number,
|
||||||
|
max: number,
|
||||||
|
defaultValue: number,
|
||||||
|
) {
|
||||||
|
if (typeof x !== "number" || isNaN(x)) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.min(max, Math.max(min, x));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isValidNumber(x: number, min: number, max: number) {
|
export function limitModel(name: string) {
|
||||||
return typeof x === "number" && x <= max && x >= min;
|
return ALL_MODELS.some((m) => m.name === name && m.available)
|
||||||
|
? name
|
||||||
|
: ALL_MODELS[4].name;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function filterConfig(oldConfig: ModelConfig): Partial<ModelConfig> {
|
export const ModalConfigValidator = {
|
||||||
const config = Object.assign({}, oldConfig);
|
model(x: string) {
|
||||||
|
return limitModel(x);
|
||||||
const validator: {
|
},
|
||||||
[k in keyof ModelConfig]: (x: ModelConfig[keyof ModelConfig]) => boolean;
|
max_tokens(x: number) {
|
||||||
} = {
|
return limitNumber(x, 0, 32000, 2000);
|
||||||
model(x) {
|
},
|
||||||
return isValidModel(x as string);
|
presence_penalty(x: number) {
|
||||||
},
|
return limitNumber(x, -2, 2, 0);
|
||||||
max_tokens(x) {
|
},
|
||||||
return isValidNumber(x as number, 100, 4000);
|
temperature(x: number) {
|
||||||
},
|
return limitNumber(x, 0, 2, 1);
|
||||||
presence_penalty(x) {
|
},
|
||||||
return isValidNumber(x as number, -2, 2);
|
};
|
||||||
},
|
|
||||||
temperature(x) {
|
|
||||||
return isValidNumber(x as number, 0, 2);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Object.keys(validator).forEach((k) => {
|
|
||||||
const key = k as keyof ModelConfig;
|
|
||||||
if (!validator[key](config[key])) {
|
|
||||||
delete config[key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
const DEFAULT_CONFIG: ChatConfig = {
|
const DEFAULT_CONFIG: ChatConfig = {
|
||||||
historyMessageCount: 4,
|
historyMessageCount: 4,
|
||||||
@@ -351,9 +348,15 @@ export const useChatStore = create<ChatStore>()(
|
|||||||
set(() => ({}));
|
set(() => ({}));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError(error) {
|
onError(error, statusCode) {
|
||||||
botMessage.content += "\n\n" + Locale.Store.Error;
|
if (statusCode === 401) {
|
||||||
|
botMessage.content = Locale.Error.Unauthorized;
|
||||||
|
} else {
|
||||||
|
botMessage.content += "\n\n" + Locale.Store.Error;
|
||||||
|
}
|
||||||
botMessage.streaming = false;
|
botMessage.streaming = false;
|
||||||
|
userMessage.isError = true;
|
||||||
|
botMessage.isError = true;
|
||||||
set(() => ({}));
|
set(() => ({}));
|
||||||
ControllerPool.remove(sessionIndex, messageIndex);
|
ControllerPool.remove(sessionIndex, messageIndex);
|
||||||
},
|
},
|
||||||
@@ -383,7 +386,8 @@ export const useChatStore = create<ChatStore>()(
|
|||||||
getMessagesWithMemory() {
|
getMessagesWithMemory() {
|
||||||
const session = get().currentSession();
|
const session = get().currentSession();
|
||||||
const config = get().config;
|
const config = get().config;
|
||||||
const n = session.messages.length;
|
const messages = session.messages.filter((msg) => !msg.isError);
|
||||||
|
const n = messages.length;
|
||||||
|
|
||||||
const context = session.context.slice();
|
const context = session.context.slice();
|
||||||
|
|
||||||
@@ -393,7 +397,7 @@ export const useChatStore = create<ChatStore>()(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const recentMessages = context.concat(
|
const recentMessages = context.concat(
|
||||||
session.messages.slice(Math.max(0, n - config.historyMessageCount)),
|
messages.slice(Math.max(0, n - config.historyMessageCount)),
|
||||||
);
|
);
|
||||||
|
|
||||||
return recentMessages;
|
return recentMessages;
|
||||||
|
@@ -184,7 +184,8 @@ input[type="range"]::-webkit-slider-thumb:hover {
|
|||||||
}
|
}
|
||||||
|
|
||||||
input[type="number"],
|
input[type="number"],
|
||||||
input[type="text"] {
|
input[type="text"],
|
||||||
|
input[type="password"] {
|
||||||
appearance: none;
|
appearance: none;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
border: var(--border-in-light);
|
border: var(--border-in-light);
|
||||||
@@ -268,3 +269,18 @@ pre {
|
|||||||
filter: brightness(0.9);
|
filter: brightness(0.9);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
width: 80%;
|
||||||
|
border-radius: 20px;
|
||||||
|
border: var(--border-in-light);
|
||||||
|
box-shadow: var(--card-shadow);
|
||||||
|
padding: 20px;
|
||||||
|
overflow: auto;
|
||||||
|
background-color: var(--white);
|
||||||
|
color: var(--black);
|
||||||
|
|
||||||
|
pre {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -8,16 +8,22 @@
|
|||||||
font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
|
font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre code.hljs {
|
pre code {
|
||||||
display: block;
|
display: block;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
code.hljs {
|
code {
|
||||||
padding: 3px 5px;
|
padding: 3px 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hljs,
|
||||||
|
pre {
|
||||||
|
background: #1a1b26;
|
||||||
|
color: #cbd2ea;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Theme: Tokyo-night-Dark
|
Theme: Tokyo-night-Dark
|
||||||
origin: https://github.com/enkia/tokyo-night-vscode-theme
|
origin: https://github.com/enkia/tokyo-night-vscode-theme
|
||||||
@@ -99,11 +105,6 @@
|
|||||||
color: #c0caf5;
|
color: #c0caf5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hljs {
|
|
||||||
background: #1a1b26;
|
|
||||||
color: #9aa5ce;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-emphasis {
|
.hljs-emphasis {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
22
app/utils.ts
22
app/utils.ts
@@ -5,15 +5,19 @@ export function trimTopic(topic: string) {
|
|||||||
return topic.replace(/[,。!?、,.!?]*$/, "");
|
return topic.replace(/[,。!?、,.!?]*$/, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function copyToClipboard(text: string) {
|
export async function copyToClipboard(text: string) {
|
||||||
navigator.clipboard
|
try {
|
||||||
.writeText(text)
|
await navigator.clipboard.writeText(text);
|
||||||
.then((res) => {
|
} catch (error) {
|
||||||
showToast(Locale.Copy.Success);
|
const textarea = document.createElement("textarea");
|
||||||
})
|
textarea.value = text;
|
||||||
.catch((err) => {
|
document.body.appendChild(textarea);
|
||||||
showToast(Locale.Copy.Failed);
|
textarea.select();
|
||||||
});
|
document.execCommand("copy");
|
||||||
|
document.body.removeChild(textarea);
|
||||||
|
} finally {
|
||||||
|
showToast(Locale.Copy.Success);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function downloadAs(text: string, filename: string) {
|
export function downloadAs(text: string, filename: string) {
|
||||||
|
@@ -14,7 +14,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@svgr/webpack": "^6.5.1",
|
"@svgr/webpack": "^6.5.1",
|
||||||
"@vercel/analytics": "^0.1.11",
|
"@vercel/analytics": "^0.1.11",
|
||||||
"array.prototype.at": "^1.1.1",
|
|
||||||
"emoji-picker-react": "^4.4.7",
|
"emoji-picker-react": "^4.4.7",
|
||||||
"eventsource-parser": "^0.1.0",
|
"eventsource-parser": "^0.1.0",
|
||||||
"fuse.js": "^6.6.2",
|
"fuse.js": "^6.6.2",
|
||||||
|
107
yarn.lock
107
yarn.lock
@@ -1365,11 +1365,6 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
|
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
|
||||||
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
|
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
|
||||||
|
|
||||||
"@types/prismjs@^1.0.0":
|
|
||||||
version "1.26.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/prismjs/-/prismjs-1.26.0.tgz#a1c3809b0ad61c62cac6d4e0c56d610c910b7654"
|
|
||||||
integrity sha512-ZTaqn/qSqUuAq1YwvOFQfVW1AR/oQJlLSZVustdjwI+GZ8kr0MSHBj0tsXPW1EqHubx50gtBEjbPGsdZwQwCjQ==
|
|
||||||
|
|
||||||
"@types/prop-types@*", "@types/prop-types@^15.0.0":
|
"@types/prop-types@*", "@types/prop-types@^15.0.0":
|
||||||
version "15.7.5"
|
version "15.7.5"
|
||||||
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
|
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
|
||||||
@@ -1570,16 +1565,6 @@ array-union@^2.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
|
resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
|
||||||
integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
|
integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
|
||||||
|
|
||||||
array.prototype.at@^1.1.1:
|
|
||||||
version "1.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/array.prototype.at/-/array.prototype.at-1.1.1.tgz#6deda3cd3c704afa16361387ea344e0b8d8831b5"
|
|
||||||
integrity sha512-n/wYNLJy/fVEU9EGPt2ww920hy1XX3XB2yTREFy1QsxctBgQV/tZIwg1G8jVxELna4pLCzg/xvvS/DDXtI4NNg==
|
|
||||||
dependencies:
|
|
||||||
call-bind "^1.0.2"
|
|
||||||
define-properties "^1.1.4"
|
|
||||||
es-abstract "^1.20.4"
|
|
||||||
es-shim-unscopables "^1.0.0"
|
|
||||||
|
|
||||||
array.prototype.flat@^1.3.1:
|
array.prototype.flat@^1.3.1:
|
||||||
version "1.3.1"
|
version "1.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2"
|
resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2"
|
||||||
@@ -1769,21 +1754,11 @@ chalk@^4.0.0:
|
|||||||
ansi-styles "^4.1.0"
|
ansi-styles "^4.1.0"
|
||||||
supports-color "^7.1.0"
|
supports-color "^7.1.0"
|
||||||
|
|
||||||
character-entities-legacy@^3.0.0:
|
|
||||||
version "3.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz#76bc83a90738901d7bc223a9e93759fdd560125b"
|
|
||||||
integrity sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==
|
|
||||||
|
|
||||||
character-entities@^2.0.0:
|
character-entities@^2.0.0:
|
||||||
version "2.0.2"
|
version "2.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-2.0.2.tgz#2d09c2e72cd9523076ccb21157dff66ad43fcc22"
|
resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-2.0.2.tgz#2d09c2e72cd9523076ccb21157dff66ad43fcc22"
|
||||||
integrity sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==
|
integrity sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==
|
||||||
|
|
||||||
character-reference-invalid@^2.0.0:
|
|
||||||
version "2.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz#85c66b041e43b47210faf401278abf808ac45cb9"
|
|
||||||
integrity sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==
|
|
||||||
|
|
||||||
"chokidar@>=3.0.0 <4.0.0":
|
"chokidar@>=3.0.0 <4.0.0":
|
||||||
version "3.5.3"
|
version "3.5.3"
|
||||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
|
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
|
||||||
@@ -2879,13 +2854,6 @@ hast-util-parse-selector@^3.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/hast" "^2.0.0"
|
"@types/hast" "^2.0.0"
|
||||||
|
|
||||||
hast-util-to-string@^2.0.0:
|
|
||||||
version "2.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/hast-util-to-string/-/hast-util-to-string-2.0.0.tgz#b008b0a4ea472bf34dd390b7eea1018726ae152a"
|
|
||||||
integrity sha512-02AQ3vLhuH3FisaMM+i/9sm4OXGSq1UhOOCpTLLQtHdL3tZt7qil69r8M8iDkZYyC0HCFylcYoP+8IO7ddta1A==
|
|
||||||
dependencies:
|
|
||||||
"@types/hast" "^2.0.0"
|
|
||||||
|
|
||||||
hast-util-to-text@^3.0.0, hast-util-to-text@^3.1.0:
|
hast-util-to-text@^3.0.0, hast-util-to-text@^3.1.0:
|
||||||
version "3.1.2"
|
version "3.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/hast-util-to-text/-/hast-util-to-text-3.1.2.tgz#ecf30c47141f41e91a5d32d0b1e1859fd2ac04f2"
|
resolved "https://registry.yarnpkg.com/hast-util-to-text/-/hast-util-to-text-3.1.2.tgz#ecf30c47141f41e91a5d32d0b1e1859fd2ac04f2"
|
||||||
@@ -2982,19 +2950,6 @@ internal-slot@^1.0.3, internal-slot@^1.0.4, internal-slot@^1.0.5:
|
|||||||
has "^1.0.3"
|
has "^1.0.3"
|
||||||
side-channel "^1.0.4"
|
side-channel "^1.0.4"
|
||||||
|
|
||||||
is-alphabetical@^2.0.0:
|
|
||||||
version "2.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-2.0.1.tgz#01072053ea7c1036df3c7d19a6daaec7f19e789b"
|
|
||||||
integrity sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==
|
|
||||||
|
|
||||||
is-alphanumerical@^2.0.0:
|
|
||||||
version "2.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz#7c03fbe96e3e931113e57f964b0a368cc2dfd875"
|
|
||||||
integrity sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==
|
|
||||||
dependencies:
|
|
||||||
is-alphabetical "^2.0.0"
|
|
||||||
is-decimal "^2.0.0"
|
|
||||||
|
|
||||||
is-arguments@^1.1.1:
|
is-arguments@^1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b"
|
resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b"
|
||||||
@@ -3063,11 +3018,6 @@ is-date-object@^1.0.1, is-date-object@^1.0.5:
|
|||||||
dependencies:
|
dependencies:
|
||||||
has-tostringtag "^1.0.0"
|
has-tostringtag "^1.0.0"
|
||||||
|
|
||||||
is-decimal@^2.0.0:
|
|
||||||
version "2.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-2.0.1.tgz#9469d2dc190d0214fd87d78b78caecc0cc14eef7"
|
|
||||||
integrity sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==
|
|
||||||
|
|
||||||
is-docker@^2.0.0, is-docker@^2.1.1:
|
is-docker@^2.0.0, is-docker@^2.1.1:
|
||||||
version "2.2.1"
|
version "2.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa"
|
resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa"
|
||||||
@@ -3095,11 +3045,6 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-extglob "^2.1.1"
|
is-extglob "^2.1.1"
|
||||||
|
|
||||||
is-hexadecimal@^2.0.0:
|
|
||||||
version "2.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz#86b5bf668fca307498d319dfc03289d781a90027"
|
|
||||||
integrity sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==
|
|
||||||
|
|
||||||
is-map@^2.0.1, is-map@^2.0.2:
|
is-map@^2.0.1, is-map@^2.0.2:
|
||||||
version "2.0.2"
|
version "2.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127"
|
resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127"
|
||||||
@@ -4156,20 +4101,6 @@ parent-module@^1.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
callsites "^3.0.0"
|
callsites "^3.0.0"
|
||||||
|
|
||||||
parse-entities@^4.0.0:
|
|
||||||
version "4.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-4.0.1.tgz#4e2a01111fb1c986549b944af39eeda258fc9e4e"
|
|
||||||
integrity sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==
|
|
||||||
dependencies:
|
|
||||||
"@types/unist" "^2.0.0"
|
|
||||||
character-entities "^2.0.0"
|
|
||||||
character-entities-legacy "^3.0.0"
|
|
||||||
character-reference-invalid "^2.0.0"
|
|
||||||
decode-named-character-reference "^1.0.0"
|
|
||||||
is-alphanumerical "^2.0.0"
|
|
||||||
is-decimal "^2.0.0"
|
|
||||||
is-hexadecimal "^2.0.0"
|
|
||||||
|
|
||||||
parse-json@^5.0.0:
|
parse-json@^5.0.0:
|
||||||
version "5.2.0"
|
version "5.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
|
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
|
||||||
@@ -4180,11 +4111,6 @@ parse-json@^5.0.0:
|
|||||||
json-parse-even-better-errors "^2.3.0"
|
json-parse-even-better-errors "^2.3.0"
|
||||||
lines-and-columns "^1.1.6"
|
lines-and-columns "^1.1.6"
|
||||||
|
|
||||||
parse-numeric-range@^1.3.0:
|
|
||||||
version "1.3.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz#7c63b61190d61e4d53a1197f0c83c47bb670ffa3"
|
|
||||||
integrity sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==
|
|
||||||
|
|
||||||
parse5@^6.0.0:
|
parse5@^6.0.0:
|
||||||
version "6.0.1"
|
version "6.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
|
resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
|
||||||
@@ -4338,16 +4264,6 @@ readdirp@~3.6.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
picomatch "^2.2.1"
|
picomatch "^2.2.1"
|
||||||
|
|
||||||
refractor@^4.7.0:
|
|
||||||
version "4.8.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/refractor/-/refractor-4.8.1.tgz#fbdd889333a3d86c9c864479622855c9b38e9d42"
|
|
||||||
integrity sha512-/fk5sI0iTgFYlmVGYVew90AoYnNMP6pooClx/XKqyeeCQXrL0Kvgn8V0VEht5ccdljbzzF1i3Q213gcntkRExg==
|
|
||||||
dependencies:
|
|
||||||
"@types/hast" "^2.0.0"
|
|
||||||
"@types/prismjs" "^1.0.0"
|
|
||||||
hastscript "^7.0.0"
|
|
||||||
parse-entities "^4.0.0"
|
|
||||||
|
|
||||||
regenerate-unicode-properties@^10.1.0:
|
regenerate-unicode-properties@^10.1.0:
|
||||||
version "10.1.0"
|
version "10.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c"
|
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c"
|
||||||
@@ -4425,7 +4341,7 @@ rehype-katex@^6.0.2:
|
|||||||
unist-util-remove-position "^4.0.0"
|
unist-util-remove-position "^4.0.0"
|
||||||
unist-util-visit "^4.0.0"
|
unist-util-visit "^4.0.0"
|
||||||
|
|
||||||
rehype-parse@^8.0.0, rehype-parse@^8.0.2:
|
rehype-parse@^8.0.0:
|
||||||
version "8.0.4"
|
version "8.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/rehype-parse/-/rehype-parse-8.0.4.tgz#3d17c9ff16ddfef6bbcc8e6a25a99467b482d688"
|
resolved "https://registry.yarnpkg.com/rehype-parse/-/rehype-parse-8.0.4.tgz#3d17c9ff16ddfef6bbcc8e6a25a99467b482d688"
|
||||||
integrity sha512-MJJKONunHjoTh4kc3dsM1v3C9kGrrxvA3U8PxZlP2SjH8RNUSrb+lF7Y0KVaUDnGH2QZ5vAn7ulkiajM9ifuqg==
|
integrity sha512-MJJKONunHjoTh4kc3dsM1v3C9kGrrxvA3U8PxZlP2SjH8RNUSrb+lF7Y0KVaUDnGH2QZ5vAn7ulkiajM9ifuqg==
|
||||||
@@ -4435,18 +4351,6 @@ rehype-parse@^8.0.0, rehype-parse@^8.0.2:
|
|||||||
parse5 "^6.0.0"
|
parse5 "^6.0.0"
|
||||||
unified "^10.0.0"
|
unified "^10.0.0"
|
||||||
|
|
||||||
rehype-prism-plus@^1.5.1:
|
|
||||||
version "1.5.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/rehype-prism-plus/-/rehype-prism-plus-1.5.1.tgz#b5f4eb3c789a13ffe874c81039665e144bcb1cae"
|
|
||||||
integrity sha512-mowYefSfrIkMMxkb0fwuEXlvc5nA9b1vQ6mzujM81Qx28RI0mo7jCHsBZ2tJ4eIJKXdFn+EdPkZZBGB10K02vg==
|
|
||||||
dependencies:
|
|
||||||
hast-util-to-string "^2.0.0"
|
|
||||||
parse-numeric-range "^1.3.0"
|
|
||||||
refractor "^4.7.0"
|
|
||||||
rehype-parse "^8.0.2"
|
|
||||||
unist-util-filter "^4.0.0"
|
|
||||||
unist-util-visit "^4.0.0"
|
|
||||||
|
|
||||||
remark-breaks@^3.0.2:
|
remark-breaks@^3.0.2:
|
||||||
version "3.0.2"
|
version "3.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/remark-breaks/-/remark-breaks-3.0.2.tgz#f466b9d3474d7323146c0149fc1496dabadd908e"
|
resolved "https://registry.yarnpkg.com/remark-breaks/-/remark-breaks-3.0.2.tgz#f466b9d3474d7323146c0149fc1496dabadd908e"
|
||||||
@@ -4996,15 +4900,6 @@ unified@^10.0.0:
|
|||||||
trough "^2.0.0"
|
trough "^2.0.0"
|
||||||
vfile "^5.0.0"
|
vfile "^5.0.0"
|
||||||
|
|
||||||
unist-util-filter@^4.0.0:
|
|
||||||
version "4.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/unist-util-filter/-/unist-util-filter-4.0.1.tgz#fd885dd48adaad345de5f5dc706ec4ff44a8d074"
|
|
||||||
integrity sha512-RynicUM/vbOSTSiUK+BnaK9XMfmQUh6gyi7L6taNgc7FIf84GukXVV3ucGzEN/PhUUkdP5hb1MmXc+3cvPUm5Q==
|
|
||||||
dependencies:
|
|
||||||
"@types/unist" "^2.0.0"
|
|
||||||
unist-util-is "^5.0.0"
|
|
||||||
unist-util-visit-parents "^5.0.0"
|
|
||||||
|
|
||||||
unist-util-find-after@^4.0.0:
|
unist-util-find-after@^4.0.0:
|
||||||
version "4.0.1"
|
version "4.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/unist-util-find-after/-/unist-util-find-after-4.0.1.tgz#80c69c92b0504033638ce11973f4135f2c822e2d"
|
resolved "https://registry.yarnpkg.com/unist-util-find-after/-/unist-util-find-after-4.0.1.tgz#80c69c92b0504033638ce11973f4135f2c822e2d"
|
||||||
|
Reference in New Issue
Block a user