Merge branch 'Yidadaa:main' into main

This commit is contained in:
Hopelwj 2023-07-18 19:39:59 +08:00 committed by GitHub
commit b9895ecadd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 221 additions and 79 deletions

14
.babelrc Normal file
View File

@ -0,0 +1,14 @@
{
"presets": [
[
"next/babel",
{
"preset-env": {
"targets": {
"browsers": ["> 0.25%, not dead"]
}
}
}
]
]
}

View File

@ -14,7 +14,7 @@ One-Click to get well-designed cross-platform ChatGPT web UI.
[![MacOS][MacOS-image]][download-url] [![MacOS][MacOS-image]][download-url]
[![Linux][Linux-image]][download-url] [![Linux][Linux-image]][download-url]
[Web App](https://chatgpt.nextweb.fun/) / [Desktop App](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Buy Me a Coffee](https://www.buymeacoffee.com/yidadaa) [Web App](https://chatgpt.nextweb.fun/) / [Desktop App](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Discord](https://discord.gg/YCkeafCafC) / [Buy Me a Coffee](https://www.buymeacoffee.com/yidadaa)
[网页版](https://chatgpt.nextweb.fun/) / [客户端](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [反馈](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [QQ 群](https://github.com/Yidadaa/ChatGPT-Next-Web/discussions/1724) / [打赏开发者](https://user-images.githubusercontent.com/16968934/227772541-5bcd52d8-61b7-488c-a203-0330d8006e2b.jpg) [网页版](https://chatgpt.nextweb.fun/) / [客户端](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [反馈](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [QQ 群](https://github.com/Yidadaa/ChatGPT-Next-Web/discussions/1724) / [打赏开发者](https://user-images.githubusercontent.com/16968934/227772541-5bcd52d8-61b7-488c-a203-0330d8006e2b.jpg)

View File

@ -2,8 +2,8 @@ import { NextRequest, NextResponse } from "next/server";
export const OPENAI_URL = "api.openai.com"; export const OPENAI_URL = "api.openai.com";
const DEFAULT_PROTOCOL = "https"; const DEFAULT_PROTOCOL = "https";
const PROTOCOL = process.env.PROTOCOL ?? DEFAULT_PROTOCOL; const PROTOCOL = process.env.PROTOCOL || DEFAULT_PROTOCOL;
const BASE_URL = process.env.BASE_URL ?? OPENAI_URL; const BASE_URL = process.env.BASE_URL || OPENAI_URL;
const DISABLE_GPT4 = !!process.env.DISABLE_GPT4; const DISABLE_GPT4 = !!process.env.DISABLE_GPT4;
export async function requestOpenai(req: NextRequest) { export async function requestOpenai(req: NextRequest) {

View File

@ -99,7 +99,7 @@ export class ClientApi {
// 敬告二开开发者们,为了开源大模型的发展,请不要修改上述消息,此消息用于后续数据清洗使用 // 敬告二开开发者们,为了开源大模型的发展,请不要修改上述消息,此消息用于后续数据清洗使用
// Please do not modify this message // Please do not modify this message
console.log("[Share]", msgs); console.log("[Share]", messages, msgs);
const clientConfig = getClientConfig(); const clientConfig = getClientConfig();
const proxyUrl = "/sharegpt"; const proxyUrl = "/sharegpt";
const rawUrl = "https://sharegpt.com/api/conversations"; const rawUrl = "https://sharegpt.com/api/conversations";

View File

@ -101,6 +101,19 @@
width: 100%; width: 100%;
margin-bottom: 10px; margin-bottom: 10px;
&:hover {
.context-drag {
opacity: 1;
}
}
.context-drag {
display: flex;
align-items: center;
opacity: 0.5;
transition: all ease 0.3s;
}
.context-role { .context-role {
margin-right: 10px; margin-right: 10px;
} }

View File

@ -5,6 +5,7 @@ import React, {
useEffect, useEffect,
useMemo, useMemo,
useCallback, useCallback,
Fragment,
} from "react"; } from "react";
import SendWhiteIcon from "../icons/send-white.svg"; import SendWhiteIcon from "../icons/send-white.svg";
@ -975,9 +976,8 @@ export function Chat() {
const shouldShowClearContextDivider = i === clearContextIndex - 1; const shouldShowClearContextDivider = i === clearContextIndex - 1;
return ( return (
<> <Fragment key={i}>
<div <div
key={i}
className={ className={
isUser ? styles["chat-message-user"] : styles["chat-message"] isUser ? styles["chat-message-user"] : styles["chat-message"]
} }
@ -1082,7 +1082,7 @@ export function Chat() {
</div> </div>
</div> </div>
{shouldShowClearContextDivider && <ClearContextDivider />} {shouldShowClearContextDivider && <ClearContextDivider />}
</> </Fragment>
); );
})} })}
</div> </div>

View File

@ -8,6 +8,7 @@ import {
Modal, Modal,
Select, Select,
showImageModal, showImageModal,
showModal,
showToast, showToast,
} from "./ui-lib"; } from "./ui-lib";
import { IconButton } from "./button"; import { IconButton } from "./button";
@ -244,11 +245,11 @@ export function RenderExport(props: {
} }
const renderMsgs = messages.map((v, i) => { const renderMsgs = messages.map((v, i) => {
const [_, role] = v.id.split(":"); const [role, _] = v.id.split(":");
return { return {
id: i.toString(), id: i.toString(),
role: role as any, role: role as any,
content: v.innerHTML, content: role === "user" ? v.textContent ?? "" : v.innerHTML,
date: "", date: "",
}; };
}); });
@ -287,7 +288,30 @@ export function PreviewActions(props: {
.share(msgs) .share(msgs)
.then((res) => { .then((res) => {
if (!res) return; if (!res) return;
copyToClipboard(res); showModal({
title: Locale.Export.Share,
children: [
<input
type="text"
value={res}
key="input"
style={{
width: "100%",
maxWidth: "unset",
}}
readOnly
onClick={(e) => e.currentTarget.select()}
></input>,
],
actions: [
<IconButton
icon={<CopyIcon />}
text={Locale.Chat.Actions.Copy}
key="copy"
onClick={() => copyToClipboard(res)}
/>,
],
});
setTimeout(() => { setTimeout(() => {
window.open(res, "_blank"); window.open(res, "_blank");
}, 800); }, 800);

View File

@ -61,24 +61,36 @@
} }
} }
} }
&:hover,
&:active {
.sidebar-drag {
background-color: rgba($color: #000000, $alpha: 0.01);
svg {
opacity: 0.2;
}
}
}
} }
.sidebar-drag { .sidebar-drag {
$width: 10px; $width: 14px;
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; right: 0;
height: 100%; height: 100%;
width: $width; width: $width;
background-color: var(--black); background-color: rgba($color: #000000, $alpha: 0);
cursor: ew-resize; cursor: ew-resize;
opacity: 0;
transition: all ease 0.3s; transition: all ease 0.3s;
display: flex;
align-items: center;
&:hover, svg {
&:active { opacity: 0;
opacity: 0.2; margin-left: -2px;
} }
} }

View File

@ -11,6 +11,7 @@ import CloseIcon from "../icons/close.svg";
import DeleteIcon from "../icons/delete.svg"; import DeleteIcon from "../icons/delete.svg";
import EyeIcon from "../icons/eye.svg"; import EyeIcon from "../icons/eye.svg";
import CopyIcon from "../icons/copy.svg"; import CopyIcon from "../icons/copy.svg";
import DragIcon from "../icons/drag.svg";
import { DEFAULT_MASK_AVATAR, Mask, useMaskStore } from "../store/mask"; import { DEFAULT_MASK_AVATAR, Mask, useMaskStore } from "../store/mask";
import { import {
@ -42,6 +43,20 @@ import { ModelConfigList } from "./model-config";
import { FileName, Path } from "../constant"; import { FileName, Path } from "../constant";
import { BUILTIN_MASK_STORE } from "../masks"; import { BUILTIN_MASK_STORE } from "../masks";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
import {
DragDropContext,
Droppable,
Draggable,
OnDragEndResponder,
} from "@hello-pangea/dnd";
// drag and drop helper function
function reorder<T>(list: T[], startIndex: number, endIndex: number): T[] {
const result = [...list];
const [removed] = result.splice(startIndex, 1);
result.splice(endIndex, 0, removed);
return result;
}
export function MaskAvatar(props: { mask: Mask }) { export function MaskAvatar(props: { mask: Mask }) {
return props.mask.avatar !== DEFAULT_MASK_AVATAR ? ( return props.mask.avatar !== DEFAULT_MASK_AVATAR ? (
@ -192,6 +207,7 @@ export function MaskConfig(props: {
} }
function ContextPromptItem(props: { function ContextPromptItem(props: {
index: number;
prompt: ChatMessage; prompt: ChatMessage;
update: (prompt: ChatMessage) => void; update: (prompt: ChatMessage) => void;
remove: () => void; remove: () => void;
@ -199,53 +215,67 @@ function ContextPromptItem(props: {
const [focusingInput, setFocusingInput] = useState(false); const [focusingInput, setFocusingInput] = useState(false);
return ( return (
<div className={chatStyle["context-prompt-row"]}> <Draggable draggableId={props.prompt.id || props.index.toString()} index={props.index}>
{!focusingInput && ( {(provided) => (
<Select <div
value={props.prompt.role} className={chatStyle["context-prompt-row"]}
className={chatStyle["context-role"]} ref={provided.innerRef}
onChange={(e) => {...provided.draggableProps}
props.update({ {...provided.dragHandleProps}
...props.prompt,
role: e.target.value as any,
})
}
> >
{ROLES.map((r) => ( {!focusingInput && (
<option key={r} value={r}> <>
{r} <div className={chatStyle["context-drag"]}>
</option> <DragIcon />
))} </div>
</Select> <Select
value={props.prompt.role}
className={chatStyle["context-role"]}
onChange={(e) =>
props.update({
...props.prompt,
role: e.target.value as any,
})
}
>
{ROLES.map((r) => (
<option key={r} value={r}>
{r}
</option>
))}
</Select>
</>
)}
<Input
value={props.prompt.content}
type="text"
className={chatStyle["context-content"]}
rows={focusingInput ? 5 : 1}
onFocus={() => setFocusingInput(true)}
onBlur={() => {
setFocusingInput(false);
// If the selection is not removed when the user loses focus, some
// extensions like "Translate" will always display a floating bar
window?.getSelection()?.removeAllRanges();
}}
onInput={(e) =>
props.update({
...props.prompt,
content: e.currentTarget.value as any,
})
}
/>
{!focusingInput && (
<IconButton
icon={<DeleteIcon />}
className={chatStyle["context-delete-button"]}
onClick={() => props.remove()}
bordered
/>
)}
</div>
)} )}
<Input </Draggable>
value={props.prompt.content}
type="text"
className={chatStyle["context-content"]}
rows={focusingInput ? 5 : 1}
onFocus={() => setFocusingInput(true)}
onBlur={() => {
setFocusingInput(false);
// If the selection is not removed when the user loses focus, some
// extensions like "Translate" will always display a floating bar
window?.getSelection()?.removeAllRanges();
}}
onInput={(e) =>
props.update({
...props.prompt,
content: e.currentTarget.value as any,
})
}
/>
{!focusingInput && (
<IconButton
icon={<DeleteIcon />}
className={chatStyle["context-delete-button"]}
onClick={() => props.remove()}
bordered
/>
)}
</div>
); );
} }
@ -267,17 +297,41 @@ export function ContextPrompts(props: {
props.updateContext((context) => (context[i] = prompt)); props.updateContext((context) => (context[i] = prompt));
}; };
const onDragEnd: OnDragEndResponder = (result) => {
if (!result.destination) {
return;
}
const newContext = reorder(
context,
result.source.index,
result.destination.index,
);
props.updateContext((context) => {
context.splice(0, context.length, ...newContext);
});
};
return ( return (
<> <>
<div className={chatStyle["context-prompt"]} style={{ marginBottom: 20 }}> <div className={chatStyle["context-prompt"]} style={{ marginBottom: 20 }}>
{context.map((c, i) => ( <DragDropContext onDragEnd={onDragEnd}>
<ContextPromptItem <Droppable droppableId="context-prompt-list">
key={i} {(provided) => (
prompt={c} <div ref={provided.innerRef} {...provided.droppableProps}>
update={(prompt) => updateContextPrompt(i, prompt)} {context.map((c, i) => (
remove={() => removeContextPrompt(i)} <ContextPromptItem
/> index={i}
))} key={c.id}
prompt={c}
update={(prompt) => updateContextPrompt(i, prompt)}
remove={() => removeContextPrompt(i)}
/>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
<div className={chatStyle["context-prompt-row"]}> <div className={chatStyle["context-prompt-row"]}>
<IconButton <IconButton

View File

@ -10,6 +10,7 @@ import AddIcon from "../icons/add.svg";
import CloseIcon from "../icons/close.svg"; import CloseIcon from "../icons/close.svg";
import MaskIcon from "../icons/mask.svg"; import MaskIcon from "../icons/mask.svg";
import PluginIcon from "../icons/plugin.svg"; import PluginIcon from "../icons/plugin.svg";
import DragIcon from "../icons/drag.svg";
import Locale from "../locales"; import Locale from "../locales";
@ -198,7 +199,9 @@ export function SideBar(props: { className?: string }) {
<div <div
className={styles["sidebar-drag"]} className={styles["sidebar-drag"]}
onMouseDown={(e) => onDragMouseDown(e as any)} onMouseDown={(e) => onDragMouseDown(e as any)}
></div> >
<DragIcon />
</div>
</div> </div>
); );
} }

1
app/icons/drag.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16" fill="none"><g opacity="1" transform="translate(0 0) rotate(0)"><mask id="bg-mask-0" fill="white"><use transform="translate(0 0) rotate(0)" xlink:href="#path_0"></use></mask><g mask="url(#bg-mask-0)" ><path id="路径 1" fill-rule="evenodd" style="fill:#333333" opacity="1" d="M6.33663,3.33c0,0.74 -0.6,1.34 -1.34,1.34c-0.73,0 -1.33,-0.6 -1.33,-1.34c0,-0.73 0.6,-1.33 1.33,-1.33c0.74,0 1.34,0.6 1.34,1.33zM4.99663,9.33c-0.73,0 -1.33,-0.59 -1.33,-1.33c0,-0.74 0.6,-1.33 1.33,-1.33c0.74,0 1.34,0.59 1.34,1.33c0,0.74 -0.6,1.33 -1.34,1.33zM4.99663,14c-0.73,0 -1.33,-0.6 -1.33,-1.33c0,-0.74 0.6,-1.34 1.33,-1.34c0.74,0 1.34,0.6 1.34,1.34c0,0.73 -0.6,1.33 -1.34,1.33z"></path><path id="路径 2" fill-rule="evenodd" style="fill:#333333" opacity="1" d="M12.3366,3.33c0,0.74 -0.6,1.34 -1.34,1.34c-0.73,0 -1.32997,-0.6 -1.32997,-1.34c0,-0.73 0.59997,-1.33 1.32997,-1.33c0.74,0 1.34,0.6 1.34,1.33zM10.9966,9.33c-0.73,0 -1.32997,-0.59 -1.32997,-1.33c0,-0.74 0.59997,-1.33 1.32997,-1.33c0.74,0 1.34,0.59 1.34,1.33c0,0.74 -0.6,1.33 -1.34,1.33zM10.9966,14c-0.73,0 -1.32997,-0.6 -1.32997,-1.33c0,-0.74 0.59997,-1.34 1.32997,-1.34c0.74,0 1.34,0.6 1.34,1.34c0,0.73 -0.6,1.33 -1.34,1.33z"></path></g></g><defs><rect id="path_0" x="0" y="0" width="16" height="16" /></defs></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -3,8 +3,9 @@ import "./styles/globals.scss";
import "./styles/markdown.scss"; import "./styles/markdown.scss";
import "./styles/highlight.scss"; import "./styles/highlight.scss";
import { getClientConfig } from "./config/client"; import { getClientConfig } from "./config/client";
import { type Metadata } from 'next';
export const metadata = { export const metadata: Metadata = {
title: "ChatGPT Next Web", title: "ChatGPT Next Web",
description: "Your personal ChatGPT Chat Bot.", description: "Your personal ChatGPT Chat Bot.",
viewport: { viewport: {

View File

@ -589,7 +589,7 @@ export const useChatStore = create<ChatStore>()(
}), }),
{ {
name: StoreKey.Chat, name: StoreKey.Chat,
version: 3, version: 3.1,
migrate(persistedState, version) { migrate(persistedState, version) {
const state = persistedState as any; const state = persistedState as any;
const newState = JSON.parse(JSON.stringify(state)) as ChatStore; const newState = JSON.parse(JSON.stringify(state)) as ChatStore;
@ -617,6 +617,23 @@ export const useChatStore = create<ChatStore>()(
}); });
} }
// Enable `enableInjectSystemPrompts` attribute for old sessions.
// Resolve issue of old sessions not automatically enabling.
if (version < 3.1) {
newState.sessions.forEach((s) => {
if (
// Exclude those already set by user
!s.mask.modelConfig.hasOwnProperty("enableInjectSystemPrompts")
) {
// Because users may have changed this configuration,
// the user's current configuration is used instead of the default
const config = useAppConfig.getState();
s.mask.modelConfig.enableInjectSystemPrompts =
config.modelConfig.enableInjectSystemPrompts;
}
});
}
return newState; return newState;
}, },
}, },

View File

@ -30,6 +30,9 @@ const nextConfig = {
images: { images: {
unoptimized: mode === "export", unoptimized: mode === "export",
}, },
experimental: {
forceSwcTransforms: true,
},
}; };
if (mode !== "export") { if (mode !== "export") {

View File

@ -9,7 +9,7 @@
}, },
"package": { "package": {
"productName": "ChatGPT Next Web", "productName": "ChatGPT Next Web",
"version": "2.8.9" "version": "2.9.0"
}, },
"tauri": { "tauri": {
"allowlist": { "allowlist": {

View File

@ -5322,14 +5322,14 @@ schema-utils@^3.1.1, schema-utils@^3.2.0:
ajv-keywords "^3.5.2" ajv-keywords "^3.5.2"
semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: semver@^6.1.1, semver@^6.1.2, semver@^6.3.0:
version "6.3.0" version "6.3.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
semver@^7.3.7: semver@^7.3.7:
version "7.3.8" version "7.5.4"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
dependencies: dependencies:
lru-cache "^6.0.0" lru-cache "^6.0.0"