Compare commits

..

7 Commits
v1.0 ... v1.1

Author SHA1 Message Date
Yifei Zhang
806e7b09c1 feat: #2 trying to add stop response button 2023-03-23 17:42:38 +00:00
Yifei Zhang
99b88f36fd refactor: #6 check update over one hour and debound scroll 2023-03-23 17:00:33 +00:00
Yifei Zhang
29de957395 feat: add check update 2023-03-23 16:01:00 +00:00
Yifei Zhang
e55520e93c fix: #5 crash if code block cannot be highlighted 2023-03-23 11:28:07 +00:00
Yifei Zhang
fa8667ec19 chore: update readme about dev config 2023-03-23 03:59:50 +00:00
Yifei Zhang
547ef5565e fix: #2 use shift+enter to wrap lines when submit key is enter 2023-03-23 03:38:40 +00:00
Yifei Zhang
eb531d4524 fix: code highlight styles 2023-03-23 03:17:18 +00:00
21 changed files with 446 additions and 5365 deletions

View File

@@ -49,12 +49,30 @@ One-Click to deploy your own ChatGPT web UI.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web);
3. Enjoy :)
## 保持更新 Keep Updated
本项目会持续更新,如果你想让代码库总是保持更新,可以查看 [Github 的文档](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork) 了解如何让 fork 的项目与上游代码同步,建议定期进行同步操作以获得新功能。
你可以 star/watch 本项目或者 follow 作者来及时获得新功能更新通知。
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.
You can star or watch this project or follow author to get release notifictions in time.
## 开发 Development
点击下方按钮,开始二次开发:
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web)
在开始写代码之前,需要在项目根目录新建一个 `.env.local` 文件,里面填入环境变量:
Before development, you must create a new `.env.local` file at project root, and place your api key into it:
```
OPENAI_API_KEY=<your api key here>
```
## 截图 Screenshots
![设置 Settings](./static/settings.png)

View File

@@ -237,6 +237,14 @@
flex-direction: column;
align-items: flex-start;
animation: slide-in ease 0.3s;
&:hover {
.chat-message-top-actions {
opacity: 1;
right: 10px;
pointer-events: all;
}
}
}
.chat-message-user > .chat-message-container {
@@ -276,6 +284,34 @@
user-select: text;
word-break: break-word;
border: var(--border-in-light);
position: relative;
}
.chat-message-top-actions {
font-size: 12px;
position: absolute;
right: 20px;
top: -26px;
transition: all ease 0.3s;
opacity: 0;
pointer-events: none;
display: flex;
flex-direction: row-reverse;
.chat-message-top-action {
opacity: 0.5;
color: var(--black);
cursor: pointer;
&:hover {
opacity: 1;
}
&:not(:first-child) {
margin-right: 10px;
}
}
}
.chat-message-user > .chat-message-container > .chat-message-item {
@@ -288,10 +324,10 @@
width: 100%;
padding-top: 5px;
box-sizing: border-box;
font-size: 12px;
}
.chat-message-action-date {
font-size: 12px;
color: #aaa;
}

View File

@@ -1,6 +1,6 @@
"use client";
import { useState, useRef, useEffect } from "react";
import { useState, useRef, useEffect, useLayoutEffect } from "react";
import { IconButton } from "./button";
import styles from "./home.module.scss";
@@ -21,11 +21,12 @@ import CopyIcon from "../icons/copy.svg";
import DownloadIcon from "../icons/download.svg";
import { Message, SubmitKey, useChatStore, ChatSession } from "../store";
import { showModal } from "./ui-lib";
import { showModal, showToast } from "./ui-lib";
import { copyToClipboard, downloadAs, isIOS, selectOrCopy } from "../utils";
import Locale from "../locales";
import dynamic from "next/dynamic";
import { REPO_URL } from "../constant";
export function Loading(props: { noLogo?: boolean }) {
return (
@@ -129,7 +130,10 @@ function useSubmitHandler() {
(config.submitKey === SubmitKey.AltEnter && e.altKey) ||
(config.submitKey === SubmitKey.CtrlEnter && e.ctrlKey) ||
(config.submitKey === SubmitKey.ShiftEnter && e.shiftKey) ||
config.submitKey === SubmitKey.Enter
(config.submitKey === SubmitKey.Enter &&
!e.altKey &&
!e.ctrlKey &&
!e.shiftKey)
);
};
@@ -162,6 +166,8 @@ export function Chat(props: { showSideBar?: () => void }) {
};
const latestMessageRef = useRef<HTMLDivElement>(null);
const [hoveringMessage, setHoveringMessage] = useState(false);
const messages = (session.messages as RenderMessage[])
.concat(
isLoading
@@ -188,14 +194,16 @@ export function Chat(props: { showSideBar?: () => void }) {
: []
);
useEffect(() => {
const dom = latestMessageRef.current;
if (dom && !isIOS()) {
dom.scrollIntoView({
behavior: "smooth",
block: "end",
});
}
useLayoutEffect(() => {
setTimeout(() => {
const dom = latestMessageRef.current;
if (dom && !isIOS() && !hoveringMessage) {
dom.scrollIntoView({
behavior: "smooth",
block: "end",
});
}
}, 500);
});
return (
@@ -244,7 +252,15 @@ export function Chat(props: { showSideBar?: () => void }) {
</div>
</div>
<div className={styles["chat-body"]}>
<div
className={styles["chat-body"]}
onMouseOver={() => {
setHoveringMessage(true);
}}
onMouseOut={() => {
setHoveringMessage(false);
}}
>
{messages.map((message, i) => {
const isUser = message.role === "user";
@@ -265,6 +281,25 @@ export function Chat(props: { showSideBar?: () => void }) {
</div>
)}
<div className={styles["chat-message-item"]}>
{!isUser && (
<div className={styles["chat-message-top-actions"]}>
{message.streaming && (
<div
className={styles["chat-message-top-action"]}
onClick={() => showToast(Locale.WIP)}
>
{Locale.Chat.Actions.Stop}
</div>
)}
<div
className={styles["chat-message-top-action"]}
onClick={() => copyToClipboard(message.content)}
>
{Locale.Chat.Actions.Copy}
</div>
</div>
)}
{(message.preview || message.content.length === 0) &&
!isUser ? (
<LoadingIcon />
@@ -292,9 +327,9 @@ export function Chat(props: { showSideBar?: () => void }) {
</div>
);
})}
<span ref={latestMessageRef} style={{ opacity: 0 }}>
<div ref={latestMessageRef} style={{ opacity: 0, height: "2em" }}>
-
</span>
</div>
</div>
<div className={styles["chat-input-panel"]}>
@@ -463,10 +498,7 @@ export function Home() {
/>
</div>
<div className={styles["sidebar-action"]}>
<a
href="https://github.com/Yidadaa/ChatGPT-Next-Web"
target="_blank"
>
<a href={REPO_URL} target="_blank">
<IconButton icon={<GithubIcon />} />
</a>
</div>

View File

@@ -2,13 +2,19 @@ import ReactMarkdown from "react-markdown";
import "katex/dist/katex.min.css";
import RemarkMath from "remark-math";
import RehypeKatex from "rehype-katex";
import RemarkGfm from 'remark-gfm'
import RehypePrsim from 'rehype-prism-plus'
import RemarkGfm from "remark-gfm";
import RehypePrsim from "rehype-prism-plus";
export function Markdown(props: { content: string }) {
return (
<ReactMarkdown remarkPlugins={[RemarkMath, RemarkGfm]} rehypePlugins={[RehypeKatex, RehypePrsim]}>
{props.content}
</ReactMarkdown>
);
}
return (
<ReactMarkdown
remarkPlugins={[RemarkMath, RemarkGfm]}
rehypePlugins={[
RehypeKatex,
[RehypePrsim, { ignoreMissing: true }],
]}
>
{props.content}
</ReactMarkdown>
);
}

View File

@@ -1,4 +1,4 @@
import { useState } from "react";
import { useState, useEffect } from "react";
import EmojiPicker, { Theme as EmojiTheme } from "emoji-picker-react";
@@ -11,10 +11,19 @@ import ClearIcon from "../icons/clear.svg";
import { List, ListItem, Popover } from "./ui-lib";
import { IconButton } from "./button";
import { SubmitKey, useChatStore, Theme, ALL_MODELS } from "../store";
import {
SubmitKey,
useChatStore,
Theme,
ALL_MODELS,
useUpdateStore,
} from "../store";
import { Avatar } from "./home";
import Locale, { changeLang, getLang } from "../locales";
import { getCurrentCommitId } from "../utils";
import Link from "next/link";
import { UPDATE_URL } from "../constant";
function SettingItem(props: {
title: string;
@@ -45,6 +54,23 @@ export function Settings(props: { closeSettings: () => void }) {
]
);
const updateStore = useUpdateStore();
const [checkingUpdate, setCheckingUpdate] = useState(false);
const currentId = getCurrentCommitId();
const remoteId = updateStore.remoteId;
const hasNewVersion = currentId !== remoteId;
function checkUpdate(force = false) {
setCheckingUpdate(true);
updateStore.getLatestCommitId(force).then(() => {
setCheckingUpdate(false);
});
}
useEffect(() => {
checkUpdate();
}, []);
return (
<>
<div className={styles["window-header"]}>
@@ -109,6 +135,31 @@ export function Settings(props: { closeSettings: () => void }) {
</Popover>
</SettingItem>
<SettingItem
title={Locale.Settings.Update.Version(currentId)}
subTitle={
checkingUpdate
? Locale.Settings.Update.IsChecking
: hasNewVersion
? Locale.Settings.Update.FoundUpdate(remoteId ?? "ERROR")
: Locale.Settings.Update.IsLatest
}
>
{checkingUpdate ? (
<div />
) : hasNewVersion ? (
<Link href={UPDATE_URL} target="_blank" className="link">
{Locale.Settings.Update.GoToUpdate}
</Link>
) : (
<IconButton
icon={<ResetIcon></ResetIcon>}
text={Locale.Settings.Update.CheckUpdate}
onClick={() => checkUpdate(true)}
/>
)}
</SettingItem>
<SettingItem title={Locale.Settings.SendKey}>
<select
value={config.submitKey}

5
app/constant.ts Normal file
View File

@@ -0,0 +1,5 @@
export const OWNER = "Yidadaa";
export const REPO = "ChatGPT-Next-Web";
export const REPO_URL = `https://github.com/${OWNER}/${REPO}`;
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`;

View File

@@ -4,9 +4,11 @@ import "./styles/prism.scss";
export const metadata = {
title: "ChatGPT Next Web",
description: "Your personal ChatGPT Chat Bot.",
description: "Your personal ChatGPT Chat Bot."
};
const COMMIT_ID = process.env.COMMIT_ID
export default function RootLayout({
children,
}: {
@@ -19,7 +21,14 @@ export default function RootLayout({
name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
/>
<meta name="version" content={COMMIT_ID} />
<link rel="manifest" href="/site.webmanifest"></link>
<link rel="preconnect" href="https://fonts.googleapis.com"></link>
<link rel="preconnect" href="https://fonts.gstatic.com"></link>
<link
href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;700;900&display=swap"
rel="stylesheet"
></link>
</head>
<body>{children}</body>
</html>

View File

@@ -1,4 +1,5 @@
const cn = {
WIP: "该功能仍在开发中……",
ChatItem: {
ChatItemCount: (count: number) => `${count} 条对话`,
},
@@ -8,6 +9,8 @@ const cn = {
ChatList: "查看消息列表",
CompressedHistory: "查看压缩后的历史 Prompt",
Export: "导出聊天记录",
Copy: "复制",
Stop: "停止",
},
Typing: "正在输入…",
Input: (submitKey: string) => `输入消息,${submitKey} 发送`,
@@ -43,6 +46,14 @@ const cn = {
},
},
Avatar: "头像",
Update: {
Version: (x: string) => `当前版本:${x}`,
IsLatest: "已是最新版本",
CheckUpdate: "检查更新",
IsChecking: "正在检查更新...",
FoundUpdate: (x: string) => `发现新版本:${x}`,
GoToUpdate: "前往更新",
},
SendKey: "发送键",
Theme: "主题",
TightBorder: "紧凑边框",

View File

@@ -1,6 +1,7 @@
import type { LocaleType } from "./index";
const en: LocaleType = {
WIP: "WIP...",
ChatItem: {
ChatItemCount: (count: number) => `${count} messages`,
},
@@ -10,6 +11,8 @@ const en: LocaleType = {
ChatList: "Go To Chat List",
CompressedHistory: "Compressed History Memory Prompt",
Export: "Export All Messages as Markdown",
Copy: "Copy",
Stop: "Stop",
},
Typing: "Typing…",
Input: (submitKey: string) =>
@@ -46,6 +49,14 @@ const en: LocaleType = {
},
},
Avatar: "Avatar",
Update: {
Version: (x: string) => `Version: ${x}`,
IsLatest: "Latest version",
CheckUpdate: "Check Update",
IsChecking: "Checking update...",
FoundUpdate: (x: string) => `Found new version: ${x}`,
GoToUpdate: "Update",
},
SendKey: "Send Key",
Theme: "Theme",
TightBorder: "Tight Border",

View File

@@ -1,5 +1,5 @@
import type { ChatRequest, ChatReponse } from "./api/chat/typing";
import { filterConfig, isValidModel, Message, ModelConfig } from "./store";
import { filterConfig, Message, ModelConfig } from "./store";
const TIME_OUT_MS = 30000;

View File

@@ -2,10 +2,10 @@ import { create } from "zustand";
import { persist } from "zustand/middleware";
import { type ChatCompletionResponseMessage } from "openai";
import { requestChatStream, requestWithPrompt } from "./requests";
import { trimTopic } from "./utils";
import { requestChatStream, requestWithPrompt } from "../requests";
import { trimTopic } from "../utils";
import Locale from "./locales";
import Locale from "../locales";
export type Message = ChatCompletionResponseMessage & {
date: string;

2
app/store/index.ts Normal file
View File

@@ -0,0 +1,2 @@
export * from "./app";
export * from "./update";

49
app/store/update.ts Normal file
View File

@@ -0,0 +1,49 @@
import { create } from "zustand";
import { persist } from "zustand/middleware";
import { FETCH_COMMIT_URL } from "../constant";
import { getCurrentCommitId } from "../utils";
export interface UpdateStore {
lastUpdate: number;
remoteId: string;
getLatestCommitId: (force: boolean) => Promise<string>;
}
export const UPDATE_KEY = "chat-update";
export const useUpdateStore = create<UpdateStore>()(
persist(
(set, get) => ({
lastUpdate: 0,
remoteId: "",
async getLatestCommitId(force = false) {
const overOneHour = Date.now() - get().lastUpdate > 3600 * 1000;
const shouldFetch = force || overOneHour;
if (!shouldFetch) {
return getCurrentCommitId();
}
try {
const data = await (await fetch(FETCH_COMMIT_URL)).json();
const sha = data[0].sha as string;
const remoteId = sha.substring(0, 7);
set(() => ({
lastUpdate: Date.now(),
remoteId,
}));
console.log("[Got Upstream] ", remoteId);
return remoteId;
} catch (error) {
console.error("[Fetch Upstream Commit Id]", error);
return getCurrentCommitId();
}
},
}),
{
name: UPDATE_KEY,
version: 1,
}
)
);

View File

@@ -83,6 +83,8 @@ body {
justify-content: center;
align-items: center;
user-select: none;
font-family: "Noto Sans SC", "SF Pro SC", "SF Pro Text", "SF Pro Icons",
"PingFang SC", "Helvetica Neue", "Helvetica", "Arial", sans-serif;
}
::-webkit-scrollbar {
@@ -192,3 +194,13 @@ div.math {
align-items: center;
justify-content: center;
}
.link {
font-size: 12px;
color: var(--primary);
text-decoration: none;
&:hover {
text-decoration: underline;
}
}

View File

@@ -96,8 +96,6 @@
margin: 0;
color: var(--color-fg-default);
background-color: var(--color-canvas-default);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans",
Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
font-size: 14px;
line-height: 1.5;
word-wrap: break-word;
@@ -319,7 +317,7 @@
cursor: pointer;
}
.markdown-body details:not([open])>*:not(summary) {
.markdown-body details:not([open]) > *:not(summary) {
display: none !important;
}
@@ -489,11 +487,11 @@
content: "";
}
.markdown-body>*:first-child {
.markdown-body > *:first-child {
margin-top: 0 !important;
}
.markdown-body>*:last-child {
.markdown-body > *:last-child {
margin-bottom: 0 !important;
}
@@ -529,11 +527,11 @@
margin-bottom: 16px;
}
.markdown-body blockquote> :first-child {
.markdown-body blockquote > :first-child {
margin-top: 0;
}
.markdown-body blockquote> :last-child {
.markdown-body blockquote > :last-child {
margin-bottom: 0;
}
@@ -632,7 +630,7 @@
list-style-type: decimal;
}
.markdown-body div>ol:not([type]) {
.markdown-body div > ol:not([type]) {
list-style-type: decimal;
}
@@ -644,11 +642,11 @@
margin-bottom: 0;
}
.markdown-body li>p {
.markdown-body li > p {
margin-top: 16px;
}
.markdown-body li+li {
.markdown-body li + li {
margin-top: 0.25em;
}
@@ -711,7 +709,7 @@
overflow: hidden;
}
.markdown-body span.frame>span {
.markdown-body span.frame > span {
display: block;
float: left;
width: auto;
@@ -739,7 +737,7 @@
clear: both;
}
.markdown-body span.align-center>span {
.markdown-body span.align-center > span {
display: block;
margin: 13px auto 0;
overflow: hidden;
@@ -757,7 +755,7 @@
clear: both;
}
.markdown-body span.align-right>span {
.markdown-body span.align-right > span {
display: block;
margin: 13px 0 0;
overflow: hidden;
@@ -787,7 +785,7 @@
overflow: hidden;
}
.markdown-body span.float-right>span {
.markdown-body span.float-right > span {
display: block;
margin: 13px auto 0;
overflow: hidden;
@@ -821,7 +819,7 @@
font-size: 100%;
}
.markdown-body pre>code {
.markdown-body pre > code {
padding: 0;
margin: 0;
word-break: normal;
@@ -1085,7 +1083,7 @@
cursor: pointer;
}
.markdown-body .task-list-item+.task-list-item {
.markdown-body .task-list-item + .task-list-item {
margin-top: 4px;
}
@@ -1107,7 +1105,9 @@
}
.markdown-body .contains-task-list:hover .task-list-item-convert-container,
.markdown-body .contains-task-list:focus-within .task-list-item-convert-container {
.markdown-body
.contains-task-list:focus-within
.task-list-item-convert-container {
display: block;
width: auto;
height: 24px;
@@ -1117,4 +1117,4 @@
.markdown-body ::-webkit-calendar-picker-indicator {
filter: invert(50%);
}
}

View File

@@ -1,9 +1,10 @@
code[class*="language-"],
pre[class*="language-"] {
.markdown-body {
code[class*="language-"],
pre[class*="language-"] {
color: #f8f8f2;
background: none;
text-shadow: 0 1px rgba(0, 0, 0, 0.3);
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
text-align: left;
white-space: pre;
word-spacing: normal;
@@ -17,131 +18,130 @@ pre[class*="language-"] {
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
}
/* Code blocks */
pre[class*="language-"] {
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
margin: 0.5em 0;
overflow: auto;
border-radius: 0.3em;
}
}
:not(pre)>code[class*="language-"],
pre[class*="language-"] {
:not(pre) > code[class*="language-"],
pre[class*="language-"] {
background: #282a36;
}
}
/* Inline code */
:not(pre)>code[class*="language-"] {
padding: .1em;
border-radius: .3em;
/* Inline code */
:not(pre) > code[class*="language-"] {
padding: 0.1em;
border-radius: 0.3em;
white-space: normal;
}
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: #6272a4;
}
}
.token.punctuation {
.token.punctuation {
color: #f8f8f2;
}
}
.namespace {
opacity: .7;
}
.namespace {
opacity: 0.7;
}
.token.property,
.token.tag,
.token.constant,
.token.symbol,
.token.deleted {
.token.property,
.token.tag,
.token.constant,
.token.symbol,
.token.deleted {
color: #ff79c6;
}
}
.token.boolean,
.token.number {
.token.boolean,
.token.number {
color: #bd93f9;
}
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #50fa7b;
}
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string,
.token.variable {
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string,
.token.variable {
color: #f8f8f2;
}
}
.token.atrule,
.token.attr-value,
.token.function,
.token.class-name {
.token.atrule,
.token.attr-value,
.token.function,
.token.class-name {
color: #f1fa8c;
}
}
.token.keyword {
.token.keyword {
color: #8be9fd;
}
}
.token.regex,
.token.important {
.token.regex,
.token.important {
color: #ffb86c;
}
}
.token.important,
.token.bold {
.token.important,
.token.bold {
font-weight: bold;
}
}
.token.italic {
.token.italic {
font-style: italic;
}
}
.token.entity {
.token.entity {
cursor: help;
}
}
@mixin light {
.markdown-body pre {
filter: invert(1) hue-rotate(50deg) brightness(1.3);
}
.markdown-body pre[class*="language-"] {
filter: invert(1) hue-rotate(50deg) brightness(1.3);
}
}
@mixin dark {
.markdown-body pre {
filter: none;
}
.markdown-body pre[class*="language-"] {
filter: none;
}
}
:root {
@include light();
@include light();
}
.light {
@include light();
@include light();
}
.dark {
@include dark();
@include dark();
}
@media (prefers-color-scheme: dark) {
:root {
@include dark();
}
}
:root {
@include dark();
}
}

View File

@@ -56,3 +56,21 @@ export function selectOrCopy(el: HTMLElement, content: string) {
return true;
}
let currentId: string;
export function getCurrentCommitId() {
if (currentId) {
return currentId;
}
if (document) {
const meta = document.head.querySelector(
"meta[name='version']"
) as HTMLMetaElement;
currentId = meta?.content ?? "";
} else {
currentId = process.env.COMMIT_ID ?? "";
}
return currentId;
}

View File

@@ -11,7 +11,7 @@ const nextConfig = {
}); // 针对 SVG 的处理规则
return config;
},
}
};
module.exports = nextConfig;

5221
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,11 @@
{
"name": "chatgpt-next-web",
"version": "0.1.0",
"private": true,
"version": "1.1",
"private": false,
"scripts": {
"dev": "next dev",
"local:dev": "./dev/proxychains.exe -f ./scripts/proxychains.conf yarn dev",
"local:start": "./dev/proxychains.exe -f ./scripts/proxychains.conf yarn start",
"build": "next build",
"start": "next start",
"dev": "cross-env COMMIT_ID=$(git rev-parse --short HEAD) next dev",
"build": "cross-env COMMIT_ID=$(git rev-parse --short HEAD) next build",
"start": "cross-env COMMIT_ID=$(git rev-parse --short HEAD) next start",
"lint": "next lint"
},
"dependencies": {
@@ -17,10 +15,12 @@
"@types/react-dom": "^18.0.11",
"@types/react-katex": "^3.0.0",
"@vercel/analytics": "^0.1.11",
"cross-env": "^7.0.3",
"emoji-picker-react": "^4.4.7",
"eslint": "8.35.0",
"eslint-config-next": "13.2.3",
"eventsource-parser": "^0.1.0",
"git-rev-sync": "^3.0.2",
"next": "^13.2.3",
"openai": "^3.2.1",
"react": "^18.2.0",

View File

@@ -1817,7 +1817,14 @@ cosmiconfig@^7.0.1:
path-type "^4.0.0"
yaml "^1.10.0"
cross-spawn@^7.0.2, cross-spawn@^7.0.3:
cross-env@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf"
integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==
dependencies:
cross-spawn "^7.0.1"
cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
version "7.0.3"
resolved "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
@@ -2126,7 +2133,7 @@ escalade@^3.1.1:
resolved "https://registry.npmmirror.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
escape-string-regexp@^1.0.5:
escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5:
version "1.0.5"
resolved "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
@@ -2514,6 +2521,15 @@ get-tsconfig@^4.2.0:
resolved "https://registry.npmmirror.com/get-tsconfig/-/get-tsconfig-4.4.0.tgz#64eee64596668a81b8fce18403f94f245ee0d4e5"
integrity sha512-0Gdjo/9+FzsYhXCEFueo2aY1z1tpXrxWZzP7k8ul9qt1U5o8rYJwTJYmaeHdrVosYIVYkOy2iwCJ9FdpocJhPQ==
git-rev-sync@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/git-rev-sync/-/git-rev-sync-3.0.2.tgz#9763c730981187c3419b75dd270088cc5f0e161b"
integrity sha512-Nd5RiYpyncjLv0j6IONy0lGzAqdRXUaBctuGBbrEA2m6Bn4iDrN/9MeQTXuiquw8AEKL9D2BW0nw5m/lQvxqnQ==
dependencies:
escape-string-regexp "1.0.5"
graceful-fs "4.1.15"
shelljs "0.8.5"
glob-parent@^5.1.2, glob-parent@~5.1.2:
version "5.1.2"
resolved "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
@@ -2540,7 +2556,7 @@ glob@7.1.7:
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@^7.1.3:
glob@^7.0.0, glob@^7.1.3:
version "7.2.3"
resolved "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
@@ -2611,6 +2627,11 @@ gopd@^1.0.1:
dependencies:
get-intrinsic "^1.1.3"
graceful-fs@4.1.15:
version "4.1.15"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==
graceful-fs@^4.2.4:
version "4.2.10"
resolved "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
@@ -2778,6 +2799,11 @@ internal-slot@^1.0.3, internal-slot@^1.0.4:
has "^1.0.3"
side-channel "^1.0.4"
interpret@^1.0.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e"
integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==
is-alphabetical@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-2.0.1.tgz#01072053ea7c1036df3c7d19a6daaec7f19e789b"
@@ -3983,6 +4009,13 @@ readdirp@~3.6.0:
dependencies:
picomatch "^2.2.1"
rechoir@^0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384"
integrity sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==
dependencies:
resolve "^1.1.6"
refractor@^4.7.0:
version "4.8.1"
resolved "https://registry.yarnpkg.com/refractor/-/refractor-4.8.1.tgz#fbdd889333a3d86c9c864479622855c9b38e9d42"
@@ -4130,7 +4163,7 @@ resolve-from@^4.0.0:
resolved "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
resolve@^1.14.2, resolve@^1.22.1:
resolve@^1.1.6, resolve@^1.14.2, resolve@^1.22.1:
version "1.22.1"
resolved "https://registry.npmmirror.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
@@ -4223,6 +4256,15 @@ shebang-regex@^3.0.0:
resolved "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
shelljs@0.8.5:
version "0.8.5"
resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c"
integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==
dependencies:
glob "^7.0.0"
interpret "^1.0.0"
rechoir "^0.6.2"
side-channel@^1.0.4:
version "1.0.4"
resolved "https://registry.npmmirror.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"