Compare commits

..

12 Commits

Author SHA1 Message Date
DDMeaqua
61245e3d7e fix: dark theme css 2024-09-09 19:29:10 +08:00
DDMeaqua
7804182d0d fix: type error 2024-09-09 19:18:12 +08:00
DDMeaqua
f2195154f6 feat: add shortcut key 2024-09-09 18:55:37 +08:00
mayfwl
d51d7b6797 Merge pull request #5376 from MrrDrr/add_chatgpt_4o_latest
add chatgpt-4o-latest
2024-09-08 10:15:41 +08:00
lloydzhou
23ac2efd89 hotfix and update version 2024-09-07 22:12:42 +08:00
Lloyd Zhou
daeffb2dc6 Merge pull request #5383 from SukkaW/fix-5378
fix(#5378): default plugin ids to empty array
2024-09-07 22:09:35 +08:00
SukkaW
db58ca6c1d fix(#5378): default plugin ids to empty array 2024-09-07 21:32:18 +08:00
Lloyd Zhou
2ff292cbfa Merge pull request #5381 from reggiezhang/patch-1
Add crossOrigin="use-credentials" for site.webmanifest
2024-09-07 16:58:07 +08:00
Reggie Zhang
5a81393863 Add crossOrigin="use-credentials" for site.webmanifest
Add `crossOrigin="use-credentials"` to the `<link>` element for `site.webmanifest` when the site is behind a proxy with authentication.
2024-09-07 16:24:52 +08:00
Lloyd Zhou
116a73d398 Merge pull request #5377 from ConnectAI-E/hotfix/mermaid
hotfix Mermaid can not render. close #5374
2024-09-07 13:01:36 +08:00
lloydzhou
cf0c057164 hotfix Mermaid can not render. close #5374 2024-09-07 13:00:55 +08:00
l.tingting
c1b74201e4 add chatgpt-4o-latest 2024-09-07 01:42:56 +08:00
17 changed files with 222 additions and 14 deletions

View File

@@ -13,7 +13,9 @@ function getModels(remoteModelRes: OpenAIListModelResponse) {
if (config.disableGPT4) { if (config.disableGPT4) {
remoteModelRes.data = remoteModelRes.data.filter( remoteModelRes.data = remoteModelRes.data.filter(
(m) => !m.id.startsWith("gpt-4") || m.id.startsWith("gpt-4o-mini"), (m) =>
!(m.id.startsWith("gpt-4") || m.id.startsWith("chatgpt-4o")) ||
m.id.startsWith("gpt-4o-mini"),
); );
} }

View File

@@ -203,7 +203,7 @@ export class ClaudeApi implements LLMApi {
const [tools, funcs] = usePluginStore const [tools, funcs] = usePluginStore
.getState() .getState()
.getAsTools( .getAsTools(
useChatStore.getState().currentSession().mask?.plugin as string[], useChatStore.getState().currentSession().mask?.plugin || [],
); );
return stream( return stream(
path, path,

View File

@@ -125,7 +125,7 @@ export class MoonshotApi implements LLMApi {
const [tools, funcs] = usePluginStore const [tools, funcs] = usePluginStore
.getState() .getState()
.getAsTools( .getAsTools(
useChatStore.getState().currentSession().mask?.plugin as string[], useChatStore.getState().currentSession().mask?.plugin || [],
); );
return stream( return stream(
chatPath, chatPath,

View File

@@ -244,7 +244,7 @@ export class ChatGPTApi implements LLMApi {
const [tools, funcs] = usePluginStore const [tools, funcs] = usePluginStore
.getState() .getState()
.getAsTools( .getAsTools(
useChatStore.getState().currentSession().mask?.plugin as string[], useChatStore.getState().currentSession().mask?.plugin || [],
); );
// console.log("getAsTools", tools, funcs); // console.log("getAsTools", tools, funcs);
stream( stream(
@@ -407,7 +407,9 @@ export class ChatGPTApi implements LLMApi {
}); });
const resJson = (await res.json()) as OpenAIListModelResponse; const resJson = (await res.json()) as OpenAIListModelResponse;
const chatModels = resJson.data?.filter((m) => m.id.startsWith("gpt-")); const chatModels = resJson.data?.filter(
(m) => m.id.startsWith("gpt-") || m.id.startsWith("chatgpt-"),
);
console.log("[Models]", chatModels); console.log("[Models]", chatModels);
if (!chatModels) { if (!chatModels) {

View File

@@ -646,3 +646,55 @@
bottom: 30px; bottom: 30px;
} }
} }
.shortcut-key-container {
padding: 10px;
overflow-y: auto;
display: flex;
flex-direction: column;
}
.shortcut-key-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 16px;
}
.shortcut-key-item {
display: flex;
flex-direction: column;
overflow: hidden;
border: var(--border-in-light);
border-radius: 10px;
padding: 10px;
background-color: var(--white);
box-shadow: var(--card-shadow);
transition: background-color 0.3s ease, box-shadow 0.3s ease;
}
.shortcut-key-title {
font-size: 14px;
color: var(--black);
margin-bottom: 8px;
}
.shortcut-key-keys {
display: flex;
gap: 8px;
}
.shortcut-key {
display: flex;
align-items: center;
justify-content: center;
border: var(--border-in-light);
border-radius: 8px;
padding: 4px;
background-color: var(--gray);
min-width: 32px;
}
.shortcut-key span {
font-size: 12px;
color: var(--black);
}

View File

@@ -829,6 +829,57 @@ export function DeleteImageButton(props: { deleteImage: () => void }) {
); );
} }
export function ShortcutKeyModal(props: { onClose: () => void }) {
const shortcuts = [
{ title: Locale.Chat.ShortcutKey.newChat, keys: ["⌘", "Shift", "o"] },
{ title: Locale.Chat.ShortcutKey.focusInput, keys: ["Shift", "Esc"] },
{ title: Locale.Chat.ShortcutKey.copyLastCode, keys: ["⌘", "Shift", ";"] },
{
title: Locale.Chat.ShortcutKey.copyLastMessage,
keys: ["⌘", "Shift", "c"],
},
{ title: Locale.Chat.ShortcutKey.showShortcutKey, keys: ["⌘", "/"] },
];
return (
<div className="modal-mask">
<Modal
title={Locale.Chat.ShortcutKey.Title}
onClose={props.onClose}
actions={[
<IconButton
type="primary"
text={Locale.UI.Confirm}
icon={<ConfirmIcon />}
key="ok"
onClick={() => {
props.onClose();
}}
/>,
]}
>
<div className={styles["shortcut-key-container"]}>
<div className={styles["shortcut-key-grid"]}>
{shortcuts.map((shortcut, index) => (
<div key={index} className={styles["shortcut-key-item"]}>
<div className={styles["shortcut-key-title"]}>
{shortcut.title}
</div>
<div className={styles["shortcut-key-keys"]}>
{shortcut.keys.map((key, i) => (
<div key={i} className={styles["shortcut-key"]}>
<span>{key}</span>
</div>
))}
</div>
</div>
))}
</div>
</div>
</Modal>
</div>
);
}
function _Chat() { function _Chat() {
type RenderMessage = ChatMessage & { preview?: boolean }; type RenderMessage = ChatMessage & { preview?: boolean };
@@ -1373,6 +1424,70 @@ function _Chat() {
setAttachImages(images); setAttachImages(images);
} }
// 快捷键
const [showShortcutKeyModal, setShowShortcutKeyModal] = useState(false);
useEffect(() => {
const handleKeyDown = (event: any) => {
// 打开新聊天 command + shift + o
if (
(event.metaKey || event.ctrlKey) &&
event.shiftKey &&
event.key.toLowerCase() === "o"
) {
event.preventDefault();
setTimeout(() => {
chatStore.newSession();
navigate(Path.Chat);
}, 10);
}
// 聚焦聊天输入 shift + esc
else if (event.shiftKey && event.key.toLowerCase() === "escape") {
event.preventDefault();
inputRef.current?.focus();
}
// 复制最后一个代码块 command + shift + ;
else if (
(event.metaKey || event.ctrlKey) &&
event.shiftKey &&
event.code === "Semicolon"
) {
event.preventDefault();
const copyCodeButton =
document.querySelectorAll<HTMLElement>(".copy-code-button");
if (copyCodeButton.length > 0) {
copyCodeButton[copyCodeButton.length - 1].click();
}
}
// 复制最后一个回复 command + shift + c
else if (
(event.metaKey || event.ctrlKey) &&
event.shiftKey &&
event.key.toLowerCase() === "c"
) {
event.preventDefault();
const lastNonUserMessage = messages
.filter((message) => message.role !== "user")
.pop();
if (lastNonUserMessage) {
const lastMessageContent = getMessageTextContent(lastNonUserMessage);
copyToClipboard(lastMessageContent);
}
}
// 展示快捷键 command + /
else if ((event.metaKey || event.ctrlKey) && event.key === "/") {
event.preventDefault();
setShowShortcutKeyModal(true);
}
};
window.addEventListener("keydown", handleKeyDown);
return () => {
window.removeEventListener("keydown", handleKeyDown);
};
}, [messages, chatStore, navigate]);
return ( return (
<div className={styles.chat} key={session.id}> <div className={styles.chat} key={session.id}>
<div className="window-header" data-tauri-drag-region> <div className="window-header" data-tauri-drag-region>
@@ -1760,6 +1875,10 @@ function _Chat() {
}} }}
/> />
)} )}
{showShortcutKeyModal && (
<ShortcutKeyModal onClose={() => setShowShortcutKeyModal(false)} />
)}
</div> </div>
); );
} }

View File

@@ -36,7 +36,8 @@ export function Avatar(props: { model?: ModelType; avatar?: string }) {
if (props.model) { if (props.model) {
return ( return (
<div className="no-dark"> <div className="no-dark">
{props.model?.startsWith("gpt-4") ? ( {props.model?.startsWith("gpt-4") ||
props.model?.startsWith("chatgpt-4o") ? (
<BlackBotIcon className="user-avatar" /> <BlackBotIcon className="user-avatar" />
) : ( ) : (
<BotIcon className="user-avatar" /> <BotIcon className="user-avatar" />

View File

@@ -163,7 +163,7 @@ export function PreCode(props: { children: any }) {
); );
} }
function CustomCode(props: { children: any }) { function CustomCode(props: { children: any; className?: string }) {
const ref = useRef<HTMLPreElement>(null); const ref = useRef<HTMLPreElement>(null);
const [collapsed, setCollapsed] = useState(true); const [collapsed, setCollapsed] = useState(true);
const [showToggle, setShowToggle] = useState(false); const [showToggle, setShowToggle] = useState(false);
@@ -182,6 +182,7 @@ function CustomCode(props: { children: any }) {
return ( return (
<> <>
<code <code
className={props?.className}
ref={ref} ref={ref}
style={{ style={{
maxHeight: collapsed ? "400px" : "none", maxHeight: collapsed ? "400px" : "none",

View File

@@ -120,12 +120,15 @@ export const getServerSideConfig = () => {
if (disableGPT4) { if (disableGPT4) {
if (customModels) customModels += ","; if (customModels) customModels += ",";
customModels += DEFAULT_MODELS.filter( customModels += DEFAULT_MODELS.filter(
(m) => m.name.startsWith("gpt-4") && !m.name.startsWith("gpt-4o-mini"), (m) =>
(m.name.startsWith("gpt-4") || m.name.startsWith("chatgpt-4o")) &&
!m.name.startsWith("gpt-4o-mini"),
) )
.map((m) => "-" + m.name) .map((m) => "-" + m.name)
.join(","); .join(",");
if ( if (
defaultModel.startsWith("gpt-4") && (defaultModel.startsWith("gpt-4") ||
defaultModel.startsWith("chatgpt-4o")) &&
!defaultModel.startsWith("gpt-4o-mini") !defaultModel.startsWith("gpt-4o-mini")
) )
defaultModel = ""; defaultModel = "";

View File

@@ -246,6 +246,7 @@ export const KnowledgeCutOffDate: Record<string, string> = {
"gpt-4o": "2023-10", "gpt-4o": "2023-10",
"gpt-4o-2024-05-13": "2023-10", "gpt-4o-2024-05-13": "2023-10",
"gpt-4o-2024-08-06": "2023-10", "gpt-4o-2024-08-06": "2023-10",
"chatgpt-4o-latest": "2023-10",
"gpt-4o-mini": "2023-10", "gpt-4o-mini": "2023-10",
"gpt-4o-mini-2024-07-18": "2023-10", "gpt-4o-mini-2024-07-18": "2023-10",
"gpt-4-vision-preview": "2023-04", "gpt-4-vision-preview": "2023-04",
@@ -268,6 +269,7 @@ const openaiModels = [
"gpt-4o", "gpt-4o",
"gpt-4o-2024-05-13", "gpt-4o-2024-05-13",
"gpt-4o-2024-08-06", "gpt-4o-2024-08-06",
"chatgpt-4o-latest",
"gpt-4o-mini", "gpt-4o-mini",
"gpt-4o-mini-2024-07-18", "gpt-4o-mini-2024-07-18",
"gpt-4-vision-preview", "gpt-4-vision-preview",

View File

@@ -41,7 +41,7 @@ export default function RootLayout({
name="viewport" name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
/> />
<link rel="manifest" href="/site.webmanifest"></link> <link rel="manifest" href="/site.webmanifest" crossOrigin="use-credentials"></link>
<script src="/serviceWorkerRegister.js" defer></script> <script src="/serviceWorkerRegister.js" defer></script>
</head> </head>
<body> <body>

View File

@@ -1,3 +1,4 @@
import { ShortcutKeyModal } from "../components/chat";
import { getClientConfig } from "../config/client"; import { getClientConfig } from "../config/client";
import { SubmitKey } from "../store/config"; import { SubmitKey } from "../store/config";
@@ -81,6 +82,14 @@ const cn = {
SaveAs: "存为面具", SaveAs: "存为面具",
}, },
IsContext: "预设提示词", IsContext: "预设提示词",
ShortcutKey: {
Title: "键盘快捷方式",
newChat: "打开新聊天",
focusInput: "聚焦输入框",
copyLastMessage: "复制最后一个回复",
copyLastCode: "复制最后一个代码块",
showShortcutKey: "显示快捷方式",
},
}, },
Export: { Export: {
Title: "分享聊天记录", Title: "分享聊天记录",

View File

@@ -83,6 +83,14 @@ const en: LocaleType = {
SaveAs: "Save as Mask", SaveAs: "Save as Mask",
}, },
IsContext: "Contextual Prompt", IsContext: "Contextual Prompt",
ShortcutKey: {
Title: "Keyboard Shortcuts",
newChat: "Open New Chat",
focusInput: "Focus Input Field",
copyLastMessage: "Copy Last Reply",
copyLastCode: "Copy Last Code Block",
showShortcutKey: "Show Shortcuts",
},
}, },
Export: { Export: {
Title: "Export Messages", Title: "Export Messages",

View File

@@ -81,6 +81,14 @@ const tw = {
SaveAs: "另存新檔", SaveAs: "另存新檔",
}, },
IsContext: "預設提示詞", IsContext: "預設提示詞",
ShortcutKey: {
Title: "鍵盤快捷方式",
newChat: "打開新聊天",
focusInput: "聚焦輸入框",
copyLastMessage: "複製最後一個回覆",
copyLastCode: "複製最後一個代碼塊",
showShortcutKey: "顯示快捷方式",
},
}, },
Export: { Export: {
Title: "將聊天記錄匯出為 Markdown", Title: "將聊天記錄匯出為 Markdown",

View File

@@ -106,7 +106,7 @@ function createEmptySession(): ChatSession {
function getSummarizeModel(currentModel: string) { function getSummarizeModel(currentModel: string) {
// if it is using gpt-* models, force to use 4o-mini to summarize // if it is using gpt-* models, force to use 4o-mini to summarize
if (currentModel.startsWith("gpt")) { if (currentModel.startsWith("gpt") || currentModel.startsWith("chatgpt")) {
const configStore = useAppConfig.getState(); const configStore = useAppConfig.getState();
const accessStore = useAccessStore.getState(); const accessStore = useAccessStore.getState();
const allModel = collectModelsWithDefaultModel( const allModel = collectModelsWithDefaultModel(
@@ -476,7 +476,8 @@ export const useChatStore = createPersistStore(
// system prompts, to get close to OpenAI Web ChatGPT // system prompts, to get close to OpenAI Web ChatGPT
const shouldInjectSystemPrompts = const shouldInjectSystemPrompts =
modelConfig.enableInjectSystemPrompts && modelConfig.enableInjectSystemPrompts &&
session.mask.modelConfig.model.startsWith("gpt-"); (session.mask.modelConfig.model.startsWith("gpt-") ||
session.mask.modelConfig.model.startsWith("chatgpt-"));
var systemPrompts: ChatMessage[] = []; var systemPrompts: ChatMessage[] = [];
systemPrompts = shouldInjectSystemPrompts systemPrompts = shouldInjectSystemPrompts

View File

@@ -199,7 +199,7 @@ export const usePluginStore = createPersistStore(
getAsTools(ids: string[]) { getAsTools(ids: string[]) {
const plugins = get().plugins; const plugins = get().plugins;
const selected = ids const selected = (ids || [])
.map((id) => plugins[id]) .map((id) => plugins[id])
.filter((i) => i) .filter((i) => i)
.map((p) => FunctionToolService.add(p)); .map((p) => FunctionToolService.add(p));

View File

@@ -9,7 +9,7 @@
}, },
"package": { "package": {
"productName": "NextChat", "productName": "NextChat",
"version": "2.15.0" "version": "2.15.1"
}, },
"tauri": { "tauri": {
"allowlist": { "allowlist": {