Compare commits

...

35 Commits

Author SHA1 Message Date
Yifei Zhang
7d7f3716be Update README.md 2023-04-04 01:06:00 +08:00
yidadaa
4e644cfca7 fix: #418 valid model config 2023-04-04 01:05:33 +08:00
Yifei Zhang
7572c99f4d Merge pull request #420 from ZyXianzi/main
feat: optimize usage display
2023-04-03 22:52:08 +08:00
Feifan Zheng
bb30fdfa17 feat: optimize usage display 2023-04-03 12:18:04 +00:00
Yifei Zhang
a4743034a9 Merge pull request #415 from RugerMcCarthy/opt/back_to_autoscroll
opt: back to autoscroll when hit bottom
2023-04-03 17:57:40 +08:00
RugerMc
c2a79e8a11 opt: back to auto scroll 2023-04-03 17:51:35 +08:00
Ruger
ec88785b6d Merge branch 'Yidadaa:main' into main 2023-04-03 16:37:44 +08:00
Yifei Zhang
d923869dc3 Merge pull request #404 from cyhhao/pr2
optimize: scrolling experience
2023-04-03 15:33:01 +08:00
cyhhao
ae8ceb8dca rm input mouseover 2023-04-03 15:21:03 +08:00
cyhhao
8db26bbd5f rm msg body mouseover 2023-04-03 15:20:16 +08:00
cyhhao
efd3310711 fix conflict 2023-04-03 14:58:34 +08:00
cyhhao
a6890c0f58 optimize: scrolling experience 2023-04-03 14:56:13 +08:00
Yifei Zhang
b5a69b66b2 Merge pull request #401 from cyhhao/pr2
fix: context message should exclude all error tips
2023-04-03 14:26:32 +08:00
Yifei Zhang
54447ba677 Merge pull request #400 from yorunning/main
ci(sync): add push permission
2023-04-03 14:24:08 +08:00
GH Action - Upstream Sync
13a86aead7 Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-04-03 06:23:04 +00:00
cyhhao
73f4ea38c6 session message should exclude all error tips 2023-04-03 14:13:57 +08:00
Yorun
82c025a34a ci(sync): add push permission 2023-04-03 06:03:36 +00:00
Yifei Zhang
fb127f7857 Merge pull request #377 from ilario92/main
Update: locales in Italian
2023-04-03 13:44:40 +08:00
Yifei Zhang
6f82140ba5 Update README.md 2023-04-03 13:37:55 +08:00
github-actions[bot]
6a9751efce Merge pull request #3 from Yidadaa/main
Fork Sync
2023-04-03 05:29:44 +00:00
Yidadaa
5c75b6c784 fix: #397 #373 Array.prototype.at polyfill errors 2023-04-03 13:29:37 +08:00
Ruger
34ea20b109 Merge pull request #2 from Yidadaa/main
Fork Sync
2023-04-03 13:28:42 +08:00
Yifei Zhang
6e6d2a3310 Update README.md 2023-04-03 11:43:38 +08:00
Yifei Zhang
56ba8a65e0 fix: improve scroll 2023-04-03 03:35:24 +00:00
Yifei Zhang
0e784c50ad fix: #384 improve scroll 2023-04-03 03:27:42 +00:00
Yifei Zhang
73865651a0 fix: #366 use fallback copy 2023-04-03 03:27:42 +00:00
Yifei Zhang
f863a562a0 Merge pull request #392 from yorunning/main
ci(sync): skip main repo execution
2023-04-03 10:53:01 +08:00
Yifei Zhang
0d3bd42780 Update app.ts 2023-04-03 10:50:29 +08:00
Yorun
62f8675199 ci(sync): skip main repo execution 2023-04-03 02:11:40 +00:00
Yorun
8a1c2f89be ci(sync): shorten the timer interval 2023-04-03 02:10:48 +00:00
Ilario Scandurra
502d22bd20 Merge branch 'Yidadaa:main' into main 2023-04-02 21:14:04 +02:00
ilarioscandurra
f9f29afba9 Update: locales in Italian 2023-04-02 20:32:43 +02:00
Ilario Scandurra
c98517fc48 Merge branch 'main' into main 2023-04-02 20:13:58 +02:00
ilarioscandurra
f606a61e1c Update: locales in Italian 2023-04-02 19:23:50 +02:00
github-actions[bot]
4dbc984351 Merge pull request #1 from Yidadaa/main
Fork Sync
2023-03-31 08:52:24 +00:00
25 changed files with 471 additions and 242 deletions

View File

@@ -1,29 +1,33 @@
name: Upstream Sync
permissions:
contents: write
on:
schedule:
- cron: '0 */12 * * *' # every 12 hours
workflow_dispatch: # on button click
- cron: "0 */6 * * *" # every 6 hours
workflow_dispatch:
jobs:
sync_latest_from_upstream:
name: Sync latest commits from upstream repo
runs-on: ubuntu-latest
if: ${{ github.event.repository.fork }}
steps:
# Step 1: run a standard checkout action, provided by github
- name: Checkout target repo
uses: actions/checkout@v3
# Step 1: run a standard checkout action
- name: Checkout target repo
uses: actions/checkout@v3
# Step 2: run the sync action
- name: Sync upstream changes
id: sync
uses: aormsby/Fork-Sync-With-Upstream-action@v3.4
with:
upstream_sync_repo: Yidadaa/ChatGPT-Next-Web
upstream_sync_branch: main
target_sync_branch: main
target_repo_token: ${{ secrets.GITHUB_TOKEN }} # automatically generated, no need to set
# Set test_mode true to run tests instead of the true action!!
test_mode: false
# Step 2: run the sync action
- name: Sync upstream changes
id: sync
uses: aormsby/Fork-Sync-With-Upstream-action@v3.4
with:
upstream_sync_repo: Yidadaa/ChatGPT-Next-Web
upstream_sync_branch: main
target_sync_branch: main
target_repo_token: ${{ secrets.GITHUB_TOKEN }} # automatically generated, no need to set
# Set test_mode true to run tests instead of the true action!!
test_mode: false

View File

@@ -21,7 +21,7 @@ One-Click to deploy your own ChatGPT web UI.
- 在 1 分钟内使用 Vercel **免费一键部署**
- 精心设计的 UI响应式设计支持深色模式
- 极快的首屏加载速度(~85kb
- 极快的首屏加载速度(~100kb
- 海量的内置 prompt 列表,来自[中文](https://github.com/PlexPt/awesome-chatgpt-prompts-zh)和[英文](https://github.com/f/awesome-chatgpt-prompts)
- 自动压缩上下文聊天记录,在节省 Token 的同时支持超长对话
- 一键导出聊天记录,完整的 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
- 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)
- Automatically compresses chat history to support long conversations while also saving your tokens
- 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 出来的项目重新进行部署即可;
- 在重新部署的过程中,请手动添加名为 `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 作者来及时获得新功能更新通知。
@@ -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;
- 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.
@@ -184,7 +188,6 @@ docker run -d -p 3000:3000 -e OPENAI_API_KEY="" -e CODE="" yidadaa/chatgpt-next-
## 鸣谢 Special Thanks
### 捐赠者 Sponsor
[@mushan0x0](https://github.com/mushan0x0)

View File

@@ -12,14 +12,7 @@ import BotIcon from "../icons/bot.svg";
import AddIcon from "../icons/add.svg";
import DeleteIcon from "../icons/delete.svg";
import {
Message,
SubmitKey,
useChatStore,
ChatSession,
BOT_HELLO,
ROLES,
} from "../store";
import { Message, SubmitKey, useChatStore, BOT_HELLO, ROLES } from "../store";
import {
copyToClipboard,
@@ -377,7 +370,8 @@ export function Chat(props: {
chatStore.onUserInput(userInput).then(() => setIsLoading(false));
setUserInput("");
setPromptHints([]);
inputRef.current?.focus();
if (!isMobileScreen()) inputRef.current?.focus();
setAutoScroll(true);
};
// stop response
@@ -461,6 +455,7 @@ export function Chat(props: {
// Auto focus
useEffect(() => {
if (props.sideBarShowing && isMobileScreen()) return;
inputRef.current?.focus();
}, []);
@@ -513,7 +508,10 @@ export function Chat(props: {
bordered
title={Locale.Chat.Actions.Export}
onClick={() => {
exportMessages(session.messages, session.topic);
exportMessages(
session.messages.filter((msg) => !msg.isError),
session.topic,
);
}}
/>
</div>
@@ -530,8 +528,11 @@ export function Chat(props: {
className={styles["chat-body"]}
ref={scrollRef}
onScroll={(e) => onChatBodyScroll(e.currentTarget)}
onMouseOver={() => inputRef.current?.blur()}
onTouchStart={() => inputRef.current?.blur()}
onWheel={(e) => setAutoScroll(hitBottom && e.deltaY > 0)}
onTouchStart={() => {
inputRef.current?.blur();
setAutoScroll(false);
}}
>
{messages.map((message, i) => {
const isUser = message.role === "user";

47
app/components/error.tsx Normal file
View 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;
}
}

View File

@@ -1,6 +1,8 @@
"use client";
import { useState, useRef, useEffect, useLayoutEffect } from "react";
require("../polyfill");
import { useState, useEffect } from "react";
import { IconButton } from "./button";
import styles from "./home.module.scss";
@@ -14,25 +16,15 @@ import AddIcon from "../icons/add.svg";
import LoadingIcon from "../icons/three-dots.svg";
import CloseIcon from "../icons/close.svg";
import {
Message,
SubmitKey,
useChatStore,
ChatSession,
BOT_HELLO,
} from "../store";
import {
copyToClipboard,
downloadAs,
isMobileScreen,
selectOrCopy,
} from "../utils";
import { useChatStore } from "../store";
import { isMobileScreen } from "../utils";
import Locale from "../locales";
import { ChatList } from "./chat-list";
import { Chat } from "./chat";
import dynamic from "next/dynamic";
import { REPO_URL } from "../constant";
import { ErrorBoundary } from "./error";
export function Loading(props: { noLogo?: boolean }) {
return (
@@ -78,7 +70,7 @@ const useHasHydrated = () => {
return hasHydrated;
};
export function Home() {
function _Home() {
const [createNewSession, currentIndex, removeSession] = useChatStore(
(state) => [
state.newSession,
@@ -191,3 +183,11 @@ export function Home() {
</div>
);
}
export function Home() {
return (
<ErrorBoundary>
<_Home></_Home>
</ErrorBoundary>
);
}

View File

@@ -18,3 +18,12 @@
.avatar {
cursor: pointer;
}
.password-input {
display: flex;
justify-content: flex-end;
.password-eye {
margin-right: 4px;
}
}

View File

@@ -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";
@@ -8,6 +8,8 @@ import ResetIcon from "../icons/reload.svg";
import CloseIcon from "../icons/close.svg";
import ClearIcon from "../icons/clear.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";
@@ -19,6 +21,7 @@ import {
ALL_MODELS,
useUpdateStore,
useAccessStore,
ModalConfigValidator,
} from "../store";
import { Avatar } from "./chat";
@@ -28,6 +31,7 @@ import Link from "next/link";
import { UPDATE_URL } from "../constant";
import { SearchService, usePromptStore } from "../store/prompt";
import { requestUsage } from "../requests";
import { ErrorBoundary } from "./error";
function SettingItem(props: {
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 }) {
const [showEmojiPicker, setShowEmojiPicker] = useState(false);
const [config, updateConfig, resetConfig, clearAllData, clearSessions] =
@@ -91,11 +114,13 @@ export function Settings(props: { closeSettings: () => void }) {
useEffect(() => {
checkUpdate();
checkUsage();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const accessStore = useAccessStore();
const enabledAccessControl = useMemo(
() => 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 customCount = promptStore.prompts.size ?? 0;
const showUsage = accessStore.token !== "";
useEffect(() => {
if (showUsage) {
checkUsage();
}
}, [showUsage]);
return (
<>
<ErrorBoundary>
<div className={styles["window-header"]}>
<div className={styles["window-header-title"]}>
<div className={styles["window-header-main-title"]}>
@@ -327,14 +359,14 @@ export function Settings(props: { closeSettings: () => void }) {
title={Locale.Settings.AccessCode.Title}
subTitle={Locale.Settings.AccessCode.SubTitle}
>
<input
<PasswordInput
value={accessStore.accessCode}
type="text"
placeholder={Locale.Settings.AccessCode.Placeholder}
onChange={(e) => {
accessStore.updateCode(e.currentTarget.value);
}}
></input>
/>
</SettingItem>
) : (
<></>
@@ -344,25 +376,27 @@ export function Settings(props: { closeSettings: () => void }) {
title={Locale.Settings.Token.Title}
subTitle={Locale.Settings.Token.SubTitle}
>
<input
<PasswordInput
value={accessStore.token}
type="text"
placeholder={Locale.Settings.Token.Placeholder}
onChange={(e) => {
accessStore.updateToken(e.currentTarget.value);
}}
></input>
/>
</SettingItem>
<SettingItem
title={Locale.Settings.Usage.Title}
subTitle={
loadingUsage
? Locale.Settings.Usage.IsChecking
: Locale.Settings.Usage.SubTitle(usage?.used ?? "[?]")
showUsage
? loadingUsage
? Locale.Settings.Usage.IsChecking
: Locale.Settings.Usage.SubTitle(usage?.used ?? "[?]")
: Locale.Settings.Usage.NoAccess
}
>
{loadingUsage ? (
{!showUsage || loadingUsage ? (
<div />
) : (
<IconButton
@@ -420,7 +454,9 @@ export function Settings(props: { closeSettings: () => void }) {
onChange={(e) => {
updateConfig(
(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
type="range"
value={config.modelConfig.temperature.toFixed(1)}
value={config.modelConfig.temperature?.toFixed(1)}
min="0"
max="2"
step="0.1"
@@ -445,7 +481,9 @@ export function Settings(props: { closeSettings: () => void }) {
updateConfig(
(config) =>
(config.modelConfig.temperature =
e.currentTarget.valueAsNumber),
ModalConfigValidator.temperature(
e.currentTarget.valueAsNumber,
)),
);
}}
></input>
@@ -457,13 +495,15 @@ export function Settings(props: { closeSettings: () => void }) {
<input
type="number"
min={100}
max={4096}
max={32000}
value={config.modelConfig.max_tokens}
onChange={(e) =>
updateConfig(
(config) =>
(config.modelConfig.max_tokens =
e.currentTarget.valueAsNumber),
ModalConfigValidator.max_tokens(
e.currentTarget.valueAsNumber,
)),
)
}
></input>
@@ -474,7 +514,7 @@ export function Settings(props: { closeSettings: () => void }) {
>
<input
type="range"
value={config.modelConfig.presence_penalty.toFixed(1)}
value={config.modelConfig.presence_penalty?.toFixed(1)}
min="-2"
max="2"
step="0.5"
@@ -482,13 +522,15 @@ export function Settings(props: { closeSettings: () => void }) {
updateConfig(
(config) =>
(config.modelConfig.presence_penalty =
e.currentTarget.valueAsNumber),
ModalConfigValidator.presence_penalty(
e.currentTarget.valueAsNumber,
)),
);
}}
></input>
</SettingItem>
</List>
</div>
</>
</ErrorBoundary>
);
}

View File

@@ -1,6 +1,7 @@
export const OWNER = "Yidadaa";
export const REPO = "ChatGPT-Next-Web";
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 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`;

4
app/icons/eye-off.svg Normal file
View 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
View 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

View File

@@ -58,6 +58,7 @@ const cn = {
en: "English",
tw: "繁體中文",
es: "Español",
it: "Italiano",
},
},
Avatar: "头像",
@@ -108,6 +109,7 @@ const cn = {
},
IsChecking: "正在检查…",
Check: "重新检查",
NoAccess: "输入API Key查看余额",
},
AccessCode: {
Title: "访问码",

View File

@@ -60,6 +60,7 @@ const en: LocaleType = {
en: "English",
tw: "繁體中文",
es: "Español",
it: "Italiano",
},
},
Avatar: "Avatar",
@@ -110,6 +111,7 @@ const en: LocaleType = {
},
IsChecking: "Checking...",
Check: "Check Again",
NoAccess: "Enter API Key to check balance",
},
AccessCode: {
Title: "Access Code",

View File

@@ -60,6 +60,7 @@ const es: LocaleType = {
en: "Inglés",
tw: "繁體中文",
es: "Español",
it: "Italiano",
},
},
Avatar: "Avatar",
@@ -110,6 +111,7 @@ const es: LocaleType = {
},
IsChecking: "Comprobando...",
Check: "Comprobar de nuevo",
NoAccess: "Introduzca la clave API para comprobar el saldo",
},
AccessCode: {
Title: "Código de acceso",

View File

@@ -2,10 +2,11 @@ import CN from "./cn";
import EN from "./en";
import TW from "./tw";
import ES from "./es";
import IT from "./it";
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];
const LANG_KEY = "lang";
@@ -47,6 +48,8 @@ export function getLang(): Lang {
return "tw";
} else if (lang.includes("es")) {
return "es";
} else if (lang.includes("it")) {
return "it";
} else {
return "en";
}
@@ -57,4 +60,4 @@ export function changeLang(lang: Lang) {
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
View 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;

View File

@@ -59,6 +59,7 @@ const tw: LocaleType = {
en: "English",
tw: "繁體中文",
es: "Español",
it: "Italiano",
},
},
Avatar: "大頭貼",
@@ -108,6 +109,7 @@ const tw: LocaleType = {
},
IsChecking: "正在檢查…",
Check: "重新檢查",
NoAccess: "輸入API Key查看餘額",
},
AccessCode: {
Title: "訪問碼",

View File

@@ -1,7 +1,5 @@
import { Analytics } from "@vercel/analytics/react";
import "array.prototype.at";
import { Home } from "./components/home";
export default function App() {

27
app/polyfill.ts Normal file
View 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 {};

View File

@@ -1,5 +1,5 @@
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 { showToast } from "./components/ui-lib";
@@ -114,7 +114,7 @@ export async function requestChatStream(
filterBot?: boolean;
modelConfig?: ModelConfig;
onMessage: (message: string, done: boolean) => void;
onError: (error: Error) => void;
onError: (error: Error, statusCode?: number) => void;
onController?: (controller: AbortController) => void;
},
) {
@@ -123,11 +123,6 @@ export async function requestChatStream(
filterBot: options?.filterBot,
});
// valid and assign model config
if (options?.modelConfig) {
Object.assign(req, filterConfig(options.modelConfig));
}
console.log("[Request] ", req);
const controller = new AbortController();
@@ -178,11 +173,10 @@ export async function requestChatStream(
finish();
} else if (res.status === 401) {
console.error("Anauthorized");
responseText = Locale.Error.Unauthorized;
finish();
options?.onError(new Error("Anauthorized"), res.status);
} else {
console.error("Stream Error", res.body);
options?.onError(new Error("Stream Error"));
options?.onError(new Error("Stream Error"), res.status);
}
} catch (err) {
console.error("NetWork Error", err);

View File

@@ -14,6 +14,7 @@ import Locale from "../locales";
export type Message = ChatCompletionResponseMessage & {
date: string;
streaming?: boolean;
isError?: boolean;
};
export enum SubmitKey {
@@ -84,43 +85,39 @@ export const ALL_MODELS = [
},
];
export function isValidModel(name: string) {
return ALL_MODELS.some((m) => m.name === name && m.available);
export function limitNumber(
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) {
return typeof x === "number" && x <= max && x >= min;
export function limitModel(name: string) {
return ALL_MODELS.some((m) => m.name === name && m.available)
? name
: ALL_MODELS[4].name;
}
export function filterConfig(oldConfig: ModelConfig): Partial<ModelConfig> {
const config = Object.assign({}, oldConfig);
const validator: {
[k in keyof ModelConfig]: (x: ModelConfig[keyof ModelConfig]) => boolean;
} = {
model(x) {
return isValidModel(x as string);
},
max_tokens(x) {
return isValidNumber(x as number, 100, 4000);
},
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;
}
export const ModalConfigValidator = {
model(x: string) {
return limitModel(x);
},
max_tokens(x: number) {
return limitNumber(x, 0, 32000, 2000);
},
presence_penalty(x: number) {
return limitNumber(x, -2, 2, 0);
},
temperature(x: number) {
return limitNumber(x, 0, 2, 1);
},
};
const DEFAULT_CONFIG: ChatConfig = {
historyMessageCount: 4,
@@ -351,9 +348,15 @@ export const useChatStore = create<ChatStore>()(
set(() => ({}));
}
},
onError(error) {
botMessage.content += "\n\n" + Locale.Store.Error;
onError(error, statusCode) {
if (statusCode === 401) {
botMessage.content = Locale.Error.Unauthorized;
} else {
botMessage.content += "\n\n" + Locale.Store.Error;
}
botMessage.streaming = false;
userMessage.isError = true;
botMessage.isError = true;
set(() => ({}));
ControllerPool.remove(sessionIndex, messageIndex);
},
@@ -383,7 +386,8 @@ export const useChatStore = create<ChatStore>()(
getMessagesWithMemory() {
const session = get().currentSession();
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();
@@ -393,7 +397,7 @@ export const useChatStore = create<ChatStore>()(
}
const recentMessages = context.concat(
session.messages.slice(Math.max(0, n - config.historyMessageCount)),
messages.slice(Math.max(0, n - config.historyMessageCount)),
);
return recentMessages;

View File

@@ -184,7 +184,8 @@ input[type="range"]::-webkit-slider-thumb:hover {
}
input[type="number"],
input[type="text"] {
input[type="text"],
input[type="password"] {
appearance: none;
border-radius: 10px;
border: var(--border-in-light);
@@ -268,3 +269,18 @@ pre {
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;
}
}

View File

@@ -8,16 +8,22 @@
font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
}
pre code.hljs {
pre code {
display: block;
overflow-x: auto;
padding: 1em;
}
code.hljs {
code {
padding: 3px 5px;
}
.hljs,
pre {
background: #1a1b26;
color: #cbd2ea;
}
/*!
Theme: Tokyo-night-Dark
origin: https://github.com/enkia/tokyo-night-vscode-theme
@@ -99,11 +105,6 @@
color: #c0caf5;
}
.hljs {
background: #1a1b26;
color: #9aa5ce;
}
.hljs-emphasis {
font-style: italic;
}

View File

@@ -5,15 +5,19 @@ export function trimTopic(topic: string) {
return topic.replace(/[,。!?、,.!?]*$/, "");
}
export function copyToClipboard(text: string) {
navigator.clipboard
.writeText(text)
.then((res) => {
showToast(Locale.Copy.Success);
})
.catch((err) => {
showToast(Locale.Copy.Failed);
});
export async function copyToClipboard(text: string) {
try {
await navigator.clipboard.writeText(text);
} catch (error) {
const textarea = document.createElement("textarea");
textarea.value = text;
document.body.appendChild(textarea);
textarea.select();
document.execCommand("copy");
document.body.removeChild(textarea);
} finally {
showToast(Locale.Copy.Success);
}
}
export function downloadAs(text: string, filename: string) {

View File

@@ -14,7 +14,6 @@
"dependencies": {
"@svgr/webpack": "^6.5.1",
"@vercel/analytics": "^0.1.11",
"array.prototype.at": "^1.1.1",
"emoji-picker-react": "^4.4.7",
"eventsource-parser": "^0.1.0",
"fuse.js": "^6.6.2",

107
yarn.lock
View File

@@ -1365,11 +1365,6 @@
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
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":
version "15.7.5"
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"
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:
version "1.3.1"
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"
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:
version "2.0.2"
resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-2.0.2.tgz#2d09c2e72cd9523076ccb21157dff66ad43fcc22"
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":
version "3.5.3"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
@@ -2879,13 +2854,6 @@ hast-util-parse-selector@^3.0.0:
dependencies:
"@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:
version "3.1.2"
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"
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:
version "1.1.1"
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:
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:
version "2.2.1"
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:
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:
version "2.0.2"
resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127"
@@ -4156,20 +4101,6 @@ parent-module@^1.0.0:
dependencies:
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:
version "5.2.0"
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"
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:
version "6.0.1"
resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
@@ -4338,16 +4264,6 @@ readdirp@~3.6.0:
dependencies:
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:
version "10.1.0"
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-visit "^4.0.0"
rehype-parse@^8.0.0, rehype-parse@^8.0.2:
rehype-parse@^8.0.0:
version "8.0.4"
resolved "https://registry.yarnpkg.com/rehype-parse/-/rehype-parse-8.0.4.tgz#3d17c9ff16ddfef6bbcc8e6a25a99467b482d688"
integrity sha512-MJJKONunHjoTh4kc3dsM1v3C9kGrrxvA3U8PxZlP2SjH8RNUSrb+lF7Y0KVaUDnGH2QZ5vAn7ulkiajM9ifuqg==
@@ -4435,18 +4351,6 @@ rehype-parse@^8.0.0, rehype-parse@^8.0.2:
parse5 "^6.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:
version "3.0.2"
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"
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:
version "4.0.1"
resolved "https://registry.yarnpkg.com/unist-util-find-after/-/unist-util-find-after-4.0.1.tgz#80c69c92b0504033638ce11973f4135f2c822e2d"