Compare commits

..

31 Commits
v1.7 ... v1.8

Author SHA1 Message Date
Yifei Zhang
83862eae44 Update docker.yml 2023-03-31 01:56:42 +08:00
Yifei Zhang
6e6faec398 Merge pull request #255 from Yidadaa/docker-fix
fix: docker build
2023-03-31 01:42:37 +08:00
Yifei Zhang
e7e39ba56e fix: docker build 2023-03-30 17:41:43 +00:00
Yifei Zhang
fe9dd88c3f Merge pull request #254 from Yidadaa/bugfix-0330
Bugfix 0330
2023-03-31 01:10:36 +08:00
Yifei Zhang
b3fdf3efec fix: #182 prompt cannot be selected 2023-03-30 17:08:50 +00:00
Yifei Zhang
802ea20ec4 fix: auto scroll on enter 2023-03-30 17:04:32 +00:00
Yifei Zhang
52a217883d Merge pull request #234 from GOWxx/feat_set_history_to_zero
feat: support history message count to zero
2023-03-31 00:56:57 +08:00
Yifei Zhang
7783545bff feat: use tag as version number 2023-03-30 16:46:17 +00:00
Yifei Zhang
1b140a1ed3 fix: tight border on mobile style 2023-03-30 16:20:47 +00:00
Yifei Zhang
bf50ebac94 fix: #229 disable light code theme 2023-03-30 15:50:08 +00:00
Yifei Zhang
7599ae385b fix: #244 better scroll ux 2023-03-30 15:42:54 +00:00
Yifei Zhang
7827b40f17 fix: #185 input and select align center 2023-03-30 15:25:38 +00:00
Yifei Zhang
dea3d26335 fix: crash caused by filter config 2023-03-30 15:20:19 +00:00
Yifei Zhang
9eb77207fb Merge pull request #226 from jack0pan/fix/deps
fix: update yarn.lock file
2023-03-30 19:01:04 +08:00
Yifei Zhang
164d3fb4fe Merge pull request #228 from yanglyu902/fix-title
fix: remove English punctuation in generated title
2023-03-30 19:00:05 +08:00
GOWxx
0c9add7988 feat: support history message count to zero 2023-03-30 18:50:58 +08:00
Jack
166329abee fix: update yarn.lock file 2023-03-30 09:55:41 +00:00
Leotrinos
7d7abca2c4 fix trimTopic 2023-03-30 02:55:19 -07:00
Yifei Zhang
dab69c7507 Merge pull request #215 from hibobmaster/multi-arch-docker-build
CI: Multi Arch docker builds
2023-03-30 16:06:22 +08:00
hibobmaster
852f8b8aa5 Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web into multi-arch-docker-build 2023-03-30 15:53:44 +08:00
Yifei Zhang
fe858621f2 Merge pull request #213 from leedom92/copy-content-in-mobile
feat: setUserInput with onDoubleClickCapture in mobile phone
2023-03-30 15:38:54 +08:00
Leedom
6e4e804af8 Update home.tsx 2023-03-30 15:32:03 +08:00
leedom
e68aaf24f1 feat: setUserInput with onDoubleClickCapture in mobile phone 2023-03-30 15:22:10 +08:00
Yifei Zhang
29c20a3d5c Merge pull request #202 from RugerMcCarthy/feat/display_line_break
feat: import remark-breaks plugin
2023-03-30 14:56:21 +08:00
hibobmaster
6ed61f533a Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web into multi-arch-docker-build 2023-03-30 13:48:06 +08:00
hibobmaster
ad7a365f32 Support multi-arch docker build 2023-03-30 13:19:32 +08:00
RugerMc
2c5420ab9e feat: import ramarkBreaks plugin 2023-03-30 12:48:38 +08:00
Yifei Zhang
8d6d6bbf5d Update README.md 2023-03-30 11:22:27 +08:00
Yifei Zhang
d5235c81d0 Update README.md 2023-03-30 11:21:44 +08:00
Yifei Zhang
9e5b119e92 Merge pull request #197 from angular-moon/patch-1
onUserSubmit hide promptHints
2023-03-30 10:53:54 +08:00
angular-moon
d9fc9cd198 onUserSubmit hide promptHints 2023-03-30 10:16:00 +08:00
17 changed files with 5317 additions and 7753 deletions

View File

@@ -1,6 +1,7 @@
name: Publish Docker image name: Publish Docker image
on: on:
workflow_dispatch:
release: release:
types: [published] types: [published]
@@ -9,25 +10,43 @@ jobs:
name: Push Docker image to Docker Hub name: Push Docker image to Docker Hub
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Check out the repo -
name: Check out the repo
uses: actions/checkout@v3 uses: actions/checkout@v3
-
- name: Log in to Docker Hub name: Log in to Docker Hub
uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 uses: docker/login-action@v2
with: with:
username: ${{ secrets.DOCKER_USERNAME }} username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }} password: ${{ secrets.DOCKER_PASSWORD }}
- name: Extract metadata (tags, labels) for Docker -
name: Extract metadata (tags, labels) for Docker
id: meta id: meta
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 uses: docker/metadata-action@v4
with: with:
images: yidadaa/chatgpt-next-web images: yidadaa/chatgpt-next-web
tags: |
type=raw,value=latest
type=semver,pattern={{version}}
- name: Build and push Docker image -
uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc name: Set up QEMU
uses: docker/setup-qemu-action@v2
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
-
name: Build and push Docker image
uses: docker/build-push-action@v4
with: with:
context: . context: .
platforms: linux/amd64
push: true push: true
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

View File

@@ -6,13 +6,9 @@ RUN apk add --no-cache libc6-compat
WORKDIR /app WORKDIR /app
COPY package.json yarn.lock* package-lock.json* ./ COPY package.json yarn.lock ./
RUN \ RUN yarn install
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
else echo "Lockfile not found." && exit 1; \
fi
FROM base AS builder FROM base AS builder

View File

@@ -7,7 +7,7 @@
One-Click to deploy your own ChatGPT web UI. One-Click to deploy your own ChatGPT web UI.
[演示 Demo](https://chat-gpt-next-web.vercel.app/) / [反馈 Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [加入 Discord](https://discord.gg/zrhvHCr79N) / [QQ 群](https://user-images.githubusercontent.com/16968934/228190818-7dd00845-e9b9-4363-97e5-44c507ac76da.jpeg) / [打赏开发者](https://user-images.githubusercontent.com/16968934/227772541-5bcd52d8-61b7-488c-a203-0330d8006e2b.jpg) [演示 Demo](https://chat-gpt-next-web.vercel.app/) / [反馈 Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [加入 Discord](https://discord.gg/zrhvHCr79N) / [QQ 群](https://user-images.githubusercontent.com/16968934/228190818-7dd00845-e9b9-4363-97e5-44c507ac76da.jpeg) / [打赏开发者](https://user-images.githubusercontent.com/16968934/227772541-5bcd52d8-61b7-488c-a203-0330d8006e2b.jpg) / [Donate](#捐赠-donate-usdt)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web) [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web)
@@ -169,15 +169,12 @@ docker run -d -p 3000:3000 -e OPENAI_API_KEY="" -e CODE="" yidadaa/chatgpt-next-
![更多展示 More](./static/more.png) ![更多展示 More](./static/more.png)
## 说明 Attention
本项目的演示地址所用的 OpenAI 账户的免费额度将于 2023-04-01 过期,届时将无法通过演示地址在线体验。 ## 捐赠 Donate USDT
> BNB Smart Chain (BEP 20)
如果你想贡献出自己的 API Key可以通过作者主页的邮箱发送给作者并标注过期时间。 ```
0x67cD02c7EB62641De576a1fA3EdB32eA0c3ffD89
The free trial of the OpenAI account used by the demo will expire on April 1, 2023, and the demo will not be available at that time. ```
If you would like to contribute your API key, you can email it to the author and indicate the expiration date of the API key.
## 鸣谢 Special Thanks ## 鸣谢 Special Thanks

View File

@@ -26,13 +26,13 @@
@media only screen and (min-width: 600px) { @media only screen and (min-width: 600px) {
.tight-container { .tight-container {
--window-width: 100vw; --window-width: 100vw;
--window-height: 100vh; --window-height: var(--full-height);
--window-content-width: calc(100% - var(--sidebar-width)); --window-content-width: calc(100% - var(--sidebar-width));
@include container(); @include container();
max-width: 100vw; max-width: 100vw;
max-height: 100vh; max-height: var(--full-height);
border-radius: 0; border-radius: 0;
} }
@@ -74,7 +74,7 @@
position: absolute; position: absolute;
left: -100%; left: -100%;
z-index: 999; z-index: 999;
height: 100vh; height: var(--full-height);
transition: all ease 0.3s; transition: all ease 0.3s;
box-shadow: none; box-shadow: none;
} }

View File

@@ -23,7 +23,13 @@ import DownloadIcon from "../icons/download.svg";
import { Message, SubmitKey, useChatStore, ChatSession } from "../store"; import { Message, SubmitKey, useChatStore, ChatSession } from "../store";
import { showModal, showToast } from "./ui-lib"; import { showModal, showToast } from "./ui-lib";
import { copyToClipboard, downloadAs, isIOS, selectOrCopy } from "../utils"; import {
copyToClipboard,
downloadAs,
isIOS,
isMobileScreen,
selectOrCopy,
} from "../utils";
import Locale from "../locales"; import Locale from "../locales";
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
@@ -239,6 +245,7 @@ export function Chat(props: {
setIsLoading(true); setIsLoading(true);
chatStore.onUserInput(userInput).then(() => setIsLoading(false)); chatStore.onUserInput(userInput).then(() => setIsLoading(false));
setUserInput(""); setUserInput("");
setPromptHints([]);
inputRef.current?.focus(); inputRef.current?.focus();
}; };
@@ -283,9 +290,7 @@ export function Chat(props: {
// for auto-scroll // for auto-scroll
const latestMessageRef = useRef<HTMLDivElement>(null); const latestMessageRef = useRef<HTMLDivElement>(null);
const [autoScroll, setAutoScroll] = useState(true);
// wont scroll while hovering messages
const [autoScroll, setAutoScroll] = useState(false);
// preview messages // preview messages
const messages = (session.messages as RenderMessage[]) const messages = (session.messages as RenderMessage[])
@@ -318,7 +323,17 @@ export function Chat(props: {
useLayoutEffect(() => { useLayoutEffect(() => {
setTimeout(() => { setTimeout(() => {
const dom = latestMessageRef.current; const dom = latestMessageRef.current;
if (dom && !isIOS() && autoScroll) { const inputDom = inputRef.current;
// only scroll when input overlaped message body
let shouldScroll = true;
if (dom && inputDom) {
const domRect = dom.getBoundingClientRect();
const inputRect = inputDom.getBoundingClientRect();
shouldScroll = domRect.top > inputRect.top;
}
if (dom && autoScroll && shouldScroll) {
dom.scrollIntoView({ dom.scrollIntoView({
block: "end", block: "end",
}); });
@@ -438,6 +453,7 @@ export function Chat(props: {
className="markdown-body" className="markdown-body"
style={{ fontSize: `${fontSize}px` }} style={{ fontSize: `${fontSize}px` }}
onContextMenu={(e) => onRightClick(e, message)} onContextMenu={(e) => onRightClick(e, message)}
onDoubleClickCapture={() => setUserInput(message.content)}
> >
<Markdown content={message.content} /> <Markdown content={message.content} />
</div> </div>
@@ -473,7 +489,7 @@ export function Chat(props: {
onFocus={() => setAutoScroll(true)} onFocus={() => setAutoScroll(true)}
onBlur={() => { onBlur={() => {
setAutoScroll(false); setAutoScroll(false);
setTimeout(() => setPromptHints([]), 100); setTimeout(() => setPromptHints([]), 500);
}} }}
autoFocus={!props?.sideBarShowing} autoFocus={!props?.sideBarShowing}
/> />
@@ -602,7 +618,9 @@ export function Home() {
return ( return (
<div <div
className={`${ className={`${
config.tightBorder ? styles["tight-container"] : styles.container config.tightBorder && !isMobileScreen()
? styles["tight-container"]
: styles.container
}`} }`}
> >
<div <div

View File

@@ -1,6 +1,7 @@
import ReactMarkdown from "react-markdown"; import ReactMarkdown from "react-markdown";
import "katex/dist/katex.min.css"; import "katex/dist/katex.min.css";
import RemarkMath from "remark-math"; import RemarkMath from "remark-math";
import RemarkBreaks from "remark-breaks";
import RehypeKatex from "rehype-katex"; import RehypeKatex from "rehype-katex";
import RemarkGfm from "remark-gfm"; import RemarkGfm from "remark-gfm";
import RehypePrsim from "rehype-prism-plus"; import RehypePrsim from "rehype-prism-plus";
@@ -29,7 +30,7 @@ export function PreCode(props: { children: any }) {
export function Markdown(props: { content: string }) { export function Markdown(props: { content: string }) {
return ( return (
<ReactMarkdown <ReactMarkdown
remarkPlugins={[RemarkMath, RemarkGfm]} remarkPlugins={[RemarkMath, RemarkGfm, RemarkBreaks]}
rehypePlugins={[RehypeKatex, [RehypePrsim, { ignoreMissing: true }]]} rehypePlugins={[RehypeKatex, [RehypePrsim, { ignoreMissing: true }]]}
components={{ components={{
pre: PreCode, pre: PreCode,

View File

@@ -23,7 +23,7 @@ import {
import { Avatar, PromptHints } from "./home"; import { Avatar, PromptHints } from "./home";
import Locale, { AllLangs, changeLang, getLang } from "../locales"; import Locale, { AllLangs, changeLang, getLang } from "../locales";
import { getCurrentCommitId } from "../utils"; import { getCurrentVersion } from "../utils";
import Link from "next/link"; 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";
@@ -60,7 +60,7 @@ export function Settings(props: { closeSettings: () => void }) {
const updateStore = useUpdateStore(); const updateStore = useUpdateStore();
const [checkingUpdate, setCheckingUpdate] = useState(false); const [checkingUpdate, setCheckingUpdate] = useState(false);
const currentId = getCurrentCommitId(); const currentId = getCurrentVersion();
const remoteId = updateStore.remoteId; const remoteId = updateStore.remoteId;
const hasNewVersion = currentId !== remoteId; const hasNewVersion = currentId !== remoteId;
@@ -267,19 +267,17 @@ export function Settings(props: { closeSettings: () => void }) {
></input> ></input>
</SettingItem> </SettingItem>
<div className="no-mobile"> <SettingItem title={Locale.Settings.TightBorder}>
<SettingItem title={Locale.Settings.TightBorder}> <input
<input type="checkbox"
type="checkbox" checked={config.tightBorder}
checked={config.tightBorder} onChange={(e) =>
onChange={(e) => updateConfig(
updateConfig( (config) => (config.tightBorder = e.currentTarget.checked),
(config) => (config.tightBorder = e.currentTarget.checked), )
) }
} ></input>
></input> </SettingItem>
</SettingItem>
</div>
</List> </List>
<List> <List>
<SettingItem <SettingItem
@@ -375,7 +373,7 @@ export function Settings(props: { closeSettings: () => void }) {
type="range" type="range"
title={config.historyMessageCount.toString()} title={config.historyMessageCount.toString()}
value={config.historyMessageCount} value={config.historyMessageCount}
min="2" min="0"
max="25" max="25"
step="2" step="2"
onChange={(e) => onChange={(e) =>

View File

@@ -3,3 +3,4 @@ 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 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`;

View File

@@ -8,11 +8,11 @@ import { ACCESS_CODES, IS_IN_DOCKER } from "./api/access";
let COMMIT_ID: string | undefined; let COMMIT_ID: string | undefined;
try { try {
COMMIT_ID = process COMMIT_ID = process
.execSync("git rev-parse --short HEAD") .execSync("git describe --tags --abbrev=0")
.toString() .toString()
.trim(); .trim();
} catch (e) { } catch (e) {
console.error("No git or not from git repo.") console.error("No git or not from git repo.");
} }
export const metadata = { export const metadata = {
@@ -22,13 +22,13 @@ export const metadata = {
title: "ChatGPT Next Web", title: "ChatGPT Next Web",
statusBarStyle: "black-translucent", statusBarStyle: "black-translucent",
}, },
themeColor: "#fafafa" themeColor: "#fafafa",
}; };
function Meta() { function Meta() {
const metas = { const metas = {
version: COMMIT_ID ?? "unknown", version: COMMIT_ID ?? "unknown",
access: (ACCESS_CODES.size > 0 || IS_IN_DOCKER) ? "enabled" : "disabled", access: ACCESS_CODES.size > 0 || IS_IN_DOCKER ? "enabled" : "disabled",
}; };
return ( return (

View File

@@ -89,7 +89,9 @@ export function isValidNumber(x: number, min: number, max: number) {
return typeof x === "number" && x <= max && x >= min; return typeof x === "number" && x <= max && x >= min;
} }
export function filterConfig(config: ModelConfig): Partial<ModelConfig> { export function filterConfig(oldConfig: ModelConfig): Partial<ModelConfig> {
const config = Object.assign({}, oldConfig);
const validator: { const validator: {
[k in keyof ModelConfig]: (x: ModelConfig[keyof ModelConfig]) => boolean; [k in keyof ModelConfig]: (x: ModelConfig[keyof ModelConfig]) => boolean;
} = { } = {
@@ -103,7 +105,7 @@ export function filterConfig(config: ModelConfig): Partial<ModelConfig> {
return isValidNumber(x as number, -2, 2); return isValidNumber(x as number, -2, 2);
}, },
temperature(x) { temperature(x) {
return isValidNumber(x as number, 0, 1); return isValidNumber(x as number, 0, 2);
}, },
}; };

View File

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

View File

@@ -53,12 +53,13 @@
--sidebar-width: 300px; --sidebar-width: 300px;
--window-content-width: calc(100% - var(--sidebar-width)); --window-content-width: calc(100% - var(--sidebar-width));
--message-max-width: 80%; --message-max-width: 80%;
--full-height: 100vh;
} }
@media only screen and (max-width: 600px) { @media only screen and (max-width: 600px) {
:root { :root {
--window-width: 100vw; --window-width: 100vw;
--window-height: 100vh; --window-height: var(--full-height);
--sidebar-width: 100vw; --sidebar-width: 100vw;
--window-content-width: var(--window-width); --window-content-width: var(--window-width);
--message-max-width: 100%; --message-max-width: 100%;
@@ -80,14 +81,14 @@ body {
color: var(--black); color: var(--black);
margin: 0; margin: 0;
padding: 0; padding: 0;
height: 100vh; height: var(--full-height);
width: 100vw; width: 100vw;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
user-select: none; user-select: none;
font-family: "Noto Sans SC", "SF Pro SC", "SF Pro Text", "SF Pro Icons", font-family: "Noto Sans SC", "SF Pro SC", "SF Pro Text", "SF Pro Icons",
"PingFang SC", "Helvetica Neue", "Helvetica", "Arial", sans-serif; "PingFang SC", "Helvetica Neue", "Helvetica", "Arial", sans-serif;
@media only screen and (max-width: 600px) { @media only screen and (max-width: 600px) {
background-color: var(--second); background-color: var(--second);
@@ -119,6 +120,11 @@ select {
cursor: pointer; cursor: pointer;
background-color: var(--white); background-color: var(--white);
color: var(--black); color: var(--black);
text-align: center;
}
input {
text-align: center;
} }
input[type="checkbox"] { input[type="checkbox"] {
@@ -196,7 +202,7 @@ div.math {
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
height: 100vh; height: var(--full-height);
width: 100vw; width: 100vw;
background-color: rgba($color: #000000, $alpha: 0.5); background-color: rgba($color: #000000, $alpha: 0.5);
display: flex; display: flex;

View File

@@ -120,33 +120,3 @@
cursor: help; cursor: help;
} }
} }
@mixin light {
.markdown-body pre {
filter: invert(1) hue-rotate(90deg) brightness(1.3);
}
}
@mixin dark {
.markdown-body pre {
filter: none;
}
}
:root {
@include light();
}
.light {
@include light();
}
.dark {
@include dark();
}
@media (prefers-color-scheme: dark) {
:root {
@include dark();
}
}

View File

@@ -4,7 +4,7 @@ import Locale from "./locales";
export function trimTopic(topic: string) { export function trimTopic(topic: string) {
const s = topic.split(""); const s = topic.split("");
let lastChar = s.at(-1); // 获取 s 的最后一个字符 let lastChar = s.at(-1); // 获取 s 的最后一个字符
let pattern = /[,。!?、]/; // 定义匹配中文标点符号的正则表达式 let pattern = /[,。!?、,.!?]/; // 定义匹配中文和英文标点符号的正则表达式
while (lastChar && pattern.test(lastChar!)) { while (lastChar && pattern.test(lastChar!)) {
s.pop(); s.pop();
lastChar = s.at(-1); lastChar = s.at(-1);
@@ -28,7 +28,7 @@ export function downloadAs(text: string, filename: string) {
const element = document.createElement("a"); const element = document.createElement("a");
element.setAttribute( element.setAttribute(
"href", "href",
"data:text/plain;charset=utf-8," + encodeURIComponent(text) "data:text/plain;charset=utf-8," + encodeURIComponent(text),
); );
element.setAttribute("download", filename); element.setAttribute("download", filename);
@@ -45,6 +45,10 @@ export function isIOS() {
return /iphone|ipad|ipod/.test(userAgent); return /iphone|ipad|ipod/.test(userAgent);
} }
export function isMobileScreen() {
return window.innerWidth <= 600;
}
export function selectOrCopy(el: HTMLElement, content: string) { export function selectOrCopy(el: HTMLElement, content: string) {
const currentSelection = window.getSelection(); const currentSelection = window.getSelection();
@@ -61,7 +65,7 @@ export function queryMeta(key: string, defaultValue?: string): string {
let ret: string; let ret: string;
if (document) { if (document) {
const meta = document.head.querySelector( const meta = document.head.querySelector(
`meta[name='${key}']` `meta[name='${key}']`,
) as HTMLMetaElement; ) as HTMLMetaElement;
ret = meta?.content ?? ""; ret = meta?.content ?? "";
} else { } else {
@@ -72,7 +76,7 @@ export function queryMeta(key: string, defaultValue?: string): string {
} }
let currentId: string; let currentId: string;
export function getCurrentCommitId() { export function getCurrentVersion() {
if (currentId) { if (currentId) {
return currentId; return currentId;
} }

View File

@@ -23,6 +23,7 @@
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-markdown": "^8.0.5", "react-markdown": "^8.0.5",
"remark-breaks": "^3.0.2",
"rehype-katex": "^6.0.2", "rehype-katex": "^6.0.2",
"rehype-prism-plus": "^1.5.1", "rehype-prism-plus": "^1.5.1",
"remark-gfm": "^3.0.1", "remark-gfm": "^3.0.1",

View File

@@ -1,10 +1,14 @@
import fetch from "node-fetch"; import fetch from "node-fetch";
import fs from "fs/promises"; import fs from "fs/promises";
const CN_URL = const RAW_CN_URL =
"https://raw.githubusercontent.com/PlexPt/awesome-chatgpt-prompts-zh/main/prompts-zh.json"; "https://raw.githubusercontent.com/PlexPt/awesome-chatgpt-prompts-zh/main/prompts-zh.json";
const EN_URL = const CN_URL =
"https://cdn.jsdelivr.net/gh/PlexPt/awesome-chatgpt-prompts-zh@main/prompts-zh.json";
const RAW_EN_URL =
"https://raw.githubusercontent.com/f/awesome-chatgpt-prompts/main/prompts.csv"; "https://raw.githubusercontent.com/f/awesome-chatgpt-prompts/main/prompts.csv";
const EN_URL =
"https://cdn.jsdelivr.net/gh/f/awesome-chatgpt-prompts@main/prompts.csv";
const FILE = "./public/prompts.json"; const FILE = "./public/prompts.json";
async function fetchCN() { async function fetchCN() {

12840
yarn.lock

File diff suppressed because it is too large Load Diff