mirror of
https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web.git
synced 2025-09-30 23:26:39 +08:00
Merge branch 'main' of https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web
This commit is contained in:
commit
6554f04aab
@ -21,6 +21,7 @@ import {
|
|||||||
} from "./artifacts";
|
} from "./artifacts";
|
||||||
import { useChatStore } from "../store";
|
import { useChatStore } from "../store";
|
||||||
import { IconButton } from "./button";
|
import { IconButton } from "./button";
|
||||||
|
import { useAppConfig } from "../store/config";
|
||||||
|
|
||||||
export function Mermaid(props: { code: string }) {
|
export function Mermaid(props: { code: string }) {
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
@ -92,7 +93,9 @@ export function PreCode(props: { children: any }) {
|
|||||||
}
|
}
|
||||||
}, 600);
|
}, 600);
|
||||||
|
|
||||||
const enableArtifacts = session.mask?.enableArtifacts !== false;
|
const config = useAppConfig();
|
||||||
|
const enableArtifacts =
|
||||||
|
session.mask?.enableArtifacts !== false && config.enableArtifacts;
|
||||||
|
|
||||||
//Wrap the paragraph for plain-text
|
//Wrap the paragraph for plain-text
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -279,6 +282,20 @@ function _MarkDownContent(props: { content: string }) {
|
|||||||
p: (pProps) => <p {...pProps} dir="auto" />,
|
p: (pProps) => <p {...pProps} dir="auto" />,
|
||||||
a: (aProps) => {
|
a: (aProps) => {
|
||||||
const href = aProps.href || "";
|
const href = aProps.href || "";
|
||||||
|
if (/\.(aac|mp3|opus|wav)$/.test(href)) {
|
||||||
|
return (
|
||||||
|
<figure>
|
||||||
|
<audio controls src={href}></audio>
|
||||||
|
</figure>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (/\.(3gp|3g2|webm|ogv|mpeg|mp4|avi)$/.test(href)) {
|
||||||
|
return (
|
||||||
|
<video controls width="99.9%">
|
||||||
|
<source src={href} />
|
||||||
|
</video>
|
||||||
|
);
|
||||||
|
}
|
||||||
const isInternal = /^\/#/i.test(href);
|
const isInternal = /^\/#/i.test(href);
|
||||||
const target = isInternal ? "_self" : aProps.target ?? "_blank";
|
const target = isInternal ? "_self" : aProps.target ?? "_blank";
|
||||||
return <a {...aProps} target={target} />;
|
return <a {...aProps} target={target} />;
|
||||||
|
@ -166,21 +166,23 @@ export function MaskConfig(props: {
|
|||||||
></input>
|
></input>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|
||||||
<ListItem
|
{globalConfig.enableArtifacts && (
|
||||||
title={Locale.Mask.Config.Artifacts.Title}
|
<ListItem
|
||||||
subTitle={Locale.Mask.Config.Artifacts.SubTitle}
|
title={Locale.Mask.Config.Artifacts.Title}
|
||||||
>
|
subTitle={Locale.Mask.Config.Artifacts.SubTitle}
|
||||||
<input
|
>
|
||||||
aria-label={Locale.Mask.Config.Artifacts.Title}
|
<input
|
||||||
type="checkbox"
|
aria-label={Locale.Mask.Config.Artifacts.Title}
|
||||||
checked={props.mask.enableArtifacts !== false}
|
type="checkbox"
|
||||||
onChange={(e) => {
|
checked={props.mask.enableArtifacts !== false}
|
||||||
props.updateMask((mask) => {
|
onChange={(e) => {
|
||||||
mask.enableArtifacts = e.currentTarget.checked;
|
props.updateMask((mask) => {
|
||||||
});
|
mask.enableArtifacts = e.currentTarget.checked;
|
||||||
}}
|
});
|
||||||
></input>
|
}}
|
||||||
</ListItem>
|
></input>
|
||||||
|
</ListItem>
|
||||||
|
)}
|
||||||
|
|
||||||
{!props.shouldSyncFromGlobal ? (
|
{!props.shouldSyncFromGlobal ? (
|
||||||
<ListItem
|
<ListItem
|
||||||
|
@ -10,7 +10,29 @@
|
|||||||
max-height: 240px;
|
max-height: 240px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
min-width: 300px;
|
min-width: 280px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.plugin-schema {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
input {
|
||||||
|
margin-right: 20px;
|
||||||
|
|
||||||
|
@media screen and (max-width: 600px) {
|
||||||
|
margin-right: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 600px) {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -12,7 +12,6 @@ import EditIcon from "../icons/edit.svg";
|
|||||||
import AddIcon from "../icons/add.svg";
|
import AddIcon from "../icons/add.svg";
|
||||||
import CloseIcon from "../icons/close.svg";
|
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 ConfirmIcon from "../icons/confirm.svg";
|
import ConfirmIcon from "../icons/confirm.svg";
|
||||||
import ReloadIcon from "../icons/reload.svg";
|
import ReloadIcon from "../icons/reload.svg";
|
||||||
import GithubIcon from "../icons/github.svg";
|
import GithubIcon from "../icons/github.svg";
|
||||||
@ -29,7 +28,6 @@ import {
|
|||||||
import Locale from "../locales";
|
import Locale from "../locales";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { getClientConfig } from "../config/client";
|
|
||||||
|
|
||||||
export function PluginPage() {
|
export function PluginPage() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -209,19 +207,11 @@ export function PluginPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles["mask-actions"]}>
|
<div className={styles["mask-actions"]}>
|
||||||
{m.builtin ? (
|
<IconButton
|
||||||
<IconButton
|
icon={<EditIcon />}
|
||||||
icon={<EyeIcon />}
|
text={Locale.Plugin.Item.Edit}
|
||||||
text={Locale.Plugin.Item.View}
|
onClick={() => setEditingPluginId(m.id)}
|
||||||
onClick={() => setEditingPluginId(m.id)}
|
/>
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<IconButton
|
|
||||||
icon={<EditIcon />}
|
|
||||||
text={Locale.Plugin.Item.Edit}
|
|
||||||
onClick={() => setEditingPluginId(m.id)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{!m.builtin && (
|
{!m.builtin && (
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={<DeleteIcon />}
|
icon={<DeleteIcon />}
|
||||||
@ -325,30 +315,13 @@ export function PluginPage() {
|
|||||||
></PasswordInput>
|
></PasswordInput>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
)}
|
)}
|
||||||
{!getClientConfig()?.isApp && (
|
|
||||||
<ListItem
|
|
||||||
title={Locale.Plugin.Auth.Proxy}
|
|
||||||
subTitle={Locale.Plugin.Auth.ProxyDescription}
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={editingPlugin?.usingProxy}
|
|
||||||
style={{ minWidth: 16 }}
|
|
||||||
onChange={(e) => {
|
|
||||||
pluginStore.updatePlugin(editingPlugin.id, (plugin) => {
|
|
||||||
plugin.usingProxy = e.currentTarget.checked;
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
></input>
|
|
||||||
</ListItem>
|
|
||||||
)}
|
|
||||||
</List>
|
</List>
|
||||||
<List>
|
<List>
|
||||||
<ListItem title={Locale.Plugin.EditModal.Content}>
|
<ListItem title={Locale.Plugin.EditModal.Content}>
|
||||||
<div style={{ display: "flex", justifyContent: "flex-end" }}>
|
<div className={pluginStyles["plugin-schema"]}>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
style={{ minWidth: 200, marginRight: 20 }}
|
style={{ minWidth: 200 }}
|
||||||
onInput={(e) => setLoadUrl(e.currentTarget.value)}
|
onInput={(e) => setLoadUrl(e.currentTarget.value)}
|
||||||
></input>
|
></input>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
@ -1465,6 +1465,23 @@ export function Settings() {
|
|||||||
}
|
}
|
||||||
></input>
|
></input>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|
||||||
|
<ListItem
|
||||||
|
title={Locale.Mask.Config.Artifacts.Title}
|
||||||
|
subTitle={Locale.Mask.Config.Artifacts.SubTitle}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
aria-label={Locale.Mask.Config.Artifacts.Title}
|
||||||
|
type="checkbox"
|
||||||
|
checked={config.enableArtifacts}
|
||||||
|
onChange={(e) =>
|
||||||
|
updateConfig(
|
||||||
|
(config) =>
|
||||||
|
(config.enableArtifacts = e.currentTarget.checked),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
></input>
|
||||||
|
</ListItem>
|
||||||
</List>
|
</List>
|
||||||
|
|
||||||
<SyncItems />
|
<SyncItems />
|
||||||
|
@ -50,6 +50,8 @@ export const DEFAULT_CONFIG = {
|
|||||||
enableAutoGenerateTitle: true,
|
enableAutoGenerateTitle: true,
|
||||||
sidebarWidth: DEFAULT_SIDEBAR_WIDTH,
|
sidebarWidth: DEFAULT_SIDEBAR_WIDTH,
|
||||||
|
|
||||||
|
enableArtifacts: true, // show artifacts config
|
||||||
|
|
||||||
disablePromptHint: false,
|
disablePromptHint: false,
|
||||||
|
|
||||||
dontShowMaskSplashScreen: false, // dont show splash screen when create chat
|
dontShowMaskSplashScreen: false, // dont show splash screen when create chat
|
||||||
|
@ -2,8 +2,12 @@ import OpenAPIClientAxios from "openapi-client-axios";
|
|||||||
import { StoreKey } from "../constant";
|
import { StoreKey } from "../constant";
|
||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
import { createPersistStore } from "../utils/store";
|
import { createPersistStore } from "../utils/store";
|
||||||
|
import { getClientConfig } from "../config/client";
|
||||||
import yaml from "js-yaml";
|
import yaml from "js-yaml";
|
||||||
import { adapter } from "../utils";
|
import { adapter } from "../utils";
|
||||||
|
import { useAccessStore } from "./access";
|
||||||
|
|
||||||
|
const isApp = getClientConfig()?.isApp;
|
||||||
|
|
||||||
export type Plugin = {
|
export type Plugin = {
|
||||||
id: string;
|
id: string;
|
||||||
@ -16,7 +20,6 @@ export type Plugin = {
|
|||||||
authLocation?: string;
|
authLocation?: string;
|
||||||
authHeader?: string;
|
authHeader?: string;
|
||||||
authToken?: string;
|
authToken?: string;
|
||||||
usingProxy?: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FunctionToolItem = {
|
export type FunctionToolItem = {
|
||||||
@ -46,18 +49,25 @@ export const FunctionToolService = {
|
|||||||
plugin?.authType == "basic"
|
plugin?.authType == "basic"
|
||||||
? `Basic ${plugin?.authToken}`
|
? `Basic ${plugin?.authToken}`
|
||||||
: plugin?.authType == "bearer"
|
: plugin?.authType == "bearer"
|
||||||
? ` Bearer ${plugin?.authToken}`
|
? `Bearer ${plugin?.authToken}`
|
||||||
: plugin?.authToken;
|
: plugin?.authToken;
|
||||||
const authLocation = plugin?.authLocation || "header";
|
const authLocation = plugin?.authLocation || "header";
|
||||||
const definition = yaml.load(plugin.content) as any;
|
const definition = yaml.load(plugin.content) as any;
|
||||||
const serverURL = definition?.servers?.[0]?.url;
|
const serverURL = definition?.servers?.[0]?.url;
|
||||||
const baseURL = !!plugin?.usingProxy ? "/api/proxy" : serverURL;
|
const baseURL = !isApp ? "/api/proxy" : serverURL;
|
||||||
const headers: Record<string, string | undefined> = {
|
const headers: Record<string, string | undefined> = {
|
||||||
"X-Base-URL": !!plugin?.usingProxy ? serverURL : undefined,
|
"X-Base-URL": !isApp ? serverURL : undefined,
|
||||||
};
|
};
|
||||||
if (authLocation == "header") {
|
if (authLocation == "header") {
|
||||||
headers[headerName] = tokenValue;
|
headers[headerName] = tokenValue;
|
||||||
}
|
}
|
||||||
|
// try using openaiApiKey for Dalle3 Plugin.
|
||||||
|
if (!tokenValue && plugin.id === "dalle3") {
|
||||||
|
const openaiApiKey = useAccessStore.getState().openaiApiKey;
|
||||||
|
if (openaiApiKey) {
|
||||||
|
headers[headerName] = `Bearer ${openaiApiKey}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
const api = new OpenAPIClientAxios({
|
const api = new OpenAPIClientAxios({
|
||||||
definition: yaml.load(plugin.content) as any,
|
definition: yaml.load(plugin.content) as any,
|
||||||
axiosConfigDefaults: {
|
axiosConfigDefaults: {
|
||||||
@ -165,7 +175,7 @@ export const usePluginStore = createPersistStore(
|
|||||||
(set, get) => ({
|
(set, get) => ({
|
||||||
create(plugin?: Partial<Plugin>) {
|
create(plugin?: Partial<Plugin>) {
|
||||||
const plugins = get().plugins;
|
const plugins = get().plugins;
|
||||||
const id = nanoid();
|
const id = plugin?.id || nanoid();
|
||||||
plugins[id] = {
|
plugins[id] = {
|
||||||
...createEmptyPlugin(),
|
...createEmptyPlugin(),
|
||||||
...plugin,
|
...plugin,
|
||||||
@ -220,5 +230,42 @@ export const usePluginStore = createPersistStore(
|
|||||||
{
|
{
|
||||||
name: StoreKey.Plugin,
|
name: StoreKey.Plugin,
|
||||||
version: 1,
|
version: 1,
|
||||||
|
onRehydrateStorage(state) {
|
||||||
|
// Skip store rehydration on server side
|
||||||
|
if (typeof window === "undefined") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch("./plugins.json")
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((res) => {
|
||||||
|
Promise.all(
|
||||||
|
res.map((item: any) =>
|
||||||
|
// skip get schema
|
||||||
|
state.get(item.id)
|
||||||
|
? item
|
||||||
|
: fetch(item.schema)
|
||||||
|
.then((res) => res.text())
|
||||||
|
.then((content) => ({
|
||||||
|
...item,
|
||||||
|
content,
|
||||||
|
}))
|
||||||
|
.catch((e) => item),
|
||||||
|
),
|
||||||
|
).then((builtinPlugins: any) => {
|
||||||
|
builtinPlugins
|
||||||
|
.filter((item: any) => item?.content)
|
||||||
|
.forEach((item: any) => {
|
||||||
|
const plugin = state.create(item);
|
||||||
|
state.updatePlugin(plugin.id, (plugin) => {
|
||||||
|
const tool = FunctionToolService.add(plugin, true);
|
||||||
|
plugin.title = tool.api.definition.info.title;
|
||||||
|
plugin.version = tool.api.definition.info.version;
|
||||||
|
plugin.builtin = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
17
public/plugins.json
Normal file
17
public/plugins.json
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "dalle3",
|
||||||
|
"name": "Dalle3",
|
||||||
|
"schema": "https://ghp.ci/https://raw.githubusercontent.com/ChatGPTNextWeb/NextChat-Awesome-Plugins/main/plugins/dalle/openapi.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arxivsearch",
|
||||||
|
"name": "ArxivSearch",
|
||||||
|
"schema": "https://ghp.ci/https://raw.githubusercontent.com/ChatGPTNextWeb/NextChat-Awesome-Plugins/main/plugins/arxivsearch/openapi.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "duckduckgolite",
|
||||||
|
"name": "DuckDuckGoLiteSearch",
|
||||||
|
"schema": "https://ghp.ci/https://raw.githubusercontent.com/ChatGPTNextWeb/NextChat-Awesome-Plugins/main/plugins/duckduckgolite/openapi.json"
|
||||||
|
}
|
||||||
|
]
|
Loading…
Reference in New Issue
Block a user