diff --git a/.github/workflows/deploy_preview.yml b/.github/workflows/deploy_preview.yml
index 30d9b85b4..b98845243 100644
--- a/.github/workflows/deploy_preview.yml
+++ b/.github/workflows/deploy_preview.yml
@@ -3,9 +3,7 @@ name: VercelPreviewDeployment
on:
pull_request_target:
types:
- - opened
- - synchronize
- - reopened
+ - review_requested
env:
VERCEL_TEAM: ${{ secrets.VERCEL_TEAM }}
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index fc885b293..faf7205d9 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -7,6 +7,8 @@ on:
tags:
- "!*"
pull_request:
+ types:
+ - review_requested
jobs:
test:
diff --git a/app/client/platforms/openai.ts b/app/client/platforms/openai.ts
index a22633611..76bac59e8 100644
--- a/app/client/platforms/openai.ts
+++ b/app/client/platforms/openai.ts
@@ -352,7 +352,7 @@ export class ChatGPTApi implements LLMApi {
// make a fetch request
const requestTimeoutId = setTimeout(
() => controller.abort(),
- isDalle3 || isO1 ? REQUEST_TIMEOUT_MS * 2 : REQUEST_TIMEOUT_MS, // dalle3 using b64_json is slow.
+ isDalle3 || isO1 ? REQUEST_TIMEOUT_MS * 4 : REQUEST_TIMEOUT_MS, // dalle3 using b64_json is slow.
);
const res = await fetch(chatPath, chatPayload);
diff --git a/app/components/chat.tsx b/app/components/chat.tsx
index b45d36f95..3d5b6a4f2 100644
--- a/app/components/chat.tsx
+++ b/app/components/chat.tsx
@@ -115,11 +115,14 @@ import { getClientConfig } from "../config/client";
import { useAllModels } from "../utils/hooks";
import { MultimodalContent } from "../client/api";
-const localStorage = safeLocalStorage();
import { ClientApi } from "../client/api";
import { createTTSPlayer } from "../utils/audio";
import { MsEdgeTTS, OUTPUT_FORMAT } from "../utils/ms_edge_tts";
+import { isEmpty } from "lodash-es";
+
+const localStorage = safeLocalStorage();
+
const ttsPlayer = createTTSPlayer();
const Markdown = dynamic(async () => (await import("./markdown")).Markdown, {
@@ -1015,7 +1018,7 @@ function _Chat() {
};
const doSubmit = (userInput: string) => {
- if (userInput.trim() === "") return;
+ if (userInput.trim() === "" && isEmpty(attachImages)) return;
const matchCommand = chatCommands.match(userInput);
if (matchCommand.matched) {
setUserInput("");
diff --git a/app/components/settings.tsx b/app/components/settings.tsx
index e24644813..82ce70e5a 100644
--- a/app/components/settings.tsx
+++ b/app/components/settings.tsx
@@ -49,7 +49,7 @@ import Locale, {
changeLang,
getLang,
} from "../locales";
-import { copyToClipboard } from "../utils";
+import { copyToClipboard, clientUpdate, semverCompare } from "../utils";
import Link from "next/link";
import {
Anthropic,
@@ -585,7 +585,7 @@ export function Settings() {
const [checkingUpdate, setCheckingUpdate] = useState(false);
const currentVersion = updateStore.formatVersion(updateStore.version);
const remoteId = updateStore.formatVersion(updateStore.remoteVersion);
- const hasNewVersion = currentVersion !== remoteId;
+ const hasNewVersion = semverCompare(currentVersion, remoteId) === -1;
const updateUrl = getClientConfig()?.isApp ? RELEASE_URL : UPDATE_URL;
function checkUpdate(force = false) {
@@ -1357,9 +1357,17 @@ export function Settings() {
{checkingUpdate ? (
) : hasNewVersion ? (
-
- {Locale.Settings.Update.GoToUpdate}
-
+ clientConfig?.isApp ? (
+ }
+ text={Locale.Settings.Update.GoToUpdate}
+ onClick={() => clientUpdate()}
+ />
+ ) : (
+
+ {Locale.Settings.Update.GoToUpdate}
+
+ )
) : (
}
diff --git a/app/global.d.ts b/app/global.d.ts
index 8ee636bcd..897871fec 100644
--- a/app/global.d.ts
+++ b/app/global.d.ts
@@ -26,6 +26,13 @@ declare interface Window {
isPermissionGranted(): Promise;
sendNotification(options: string | Options): void;
};
+ updater: {
+ checkUpdate(): Promise;
+ installUpdate(): Promise;
+ onUpdaterEvent(
+ handler: (status: UpdateStatusResult) => void,
+ ): Promise;
+ };
http: {
fetch(
url: string,
diff --git a/app/locales/cn.ts b/app/locales/cn.ts
index b7debe805..e514eb4fe 100644
--- a/app/locales/cn.ts
+++ b/app/locales/cn.ts
@@ -205,6 +205,8 @@ const cn = {
IsChecking: "正在检查更新...",
FoundUpdate: (x: string) => `发现新版本:${x}`,
GoToUpdate: "前往更新",
+ Success: "更新成功!",
+ Failed: "更新失败",
},
SendKey: "发送键",
Theme: "主题",
diff --git a/app/locales/en.ts b/app/locales/en.ts
index 5cc296f1e..c86cc08f0 100644
--- a/app/locales/en.ts
+++ b/app/locales/en.ts
@@ -207,6 +207,8 @@ const en: LocaleType = {
IsChecking: "Checking update...",
FoundUpdate: (x: string) => `Found new version: ${x}`,
GoToUpdate: "Update",
+ Success: "Update Successful.",
+ Failed: "Update Failed.",
},
SendKey: "Send Key",
Theme: "Theme",
diff --git a/app/store/chat.ts b/app/store/chat.ts
index 02adb26b3..6900899e1 100644
--- a/app/store/chat.ts
+++ b/app/store/chat.ts
@@ -372,22 +372,16 @@ export const useChatStore = createPersistStore(
if (attachImages && attachImages.length > 0) {
mContent = [
- {
- type: "text",
- text: userContent,
- },
+ ...(userContent
+ ? [{ type: "text" as const, text: userContent }]
+ : []),
+ ...attachImages.map((url) => ({
+ type: "image_url" as const,
+ image_url: { url },
+ })),
];
- mContent = mContent.concat(
- attachImages.map((url) => {
- return {
- type: "image_url",
- image_url: {
- url: url,
- },
- };
- }),
- );
}
+
let userMessage: ChatMessage = createMessage({
role: "user",
content: mContent,
diff --git a/app/store/update.ts b/app/store/update.ts
index e68fde369..327dc5e88 100644
--- a/app/store/update.ts
+++ b/app/store/update.ts
@@ -6,6 +6,7 @@ import {
} from "../constant";
import { getClientConfig } from "../config/client";
import { createPersistStore } from "../utils/store";
+import { clientUpdate } from "../utils";
import ChatGptIcon from "../icons/chatgpt.png";
import Locale from "../locales";
import { ClientApi } from "../client/api";
@@ -119,6 +120,7 @@ export const useUpdateStore = createPersistStore(
icon: `${ChatGptIcon.src}`,
sound: "Default",
});
+ clientUpdate();
}
}
});
diff --git a/app/utils.ts b/app/utils.ts
index 5d4501710..d8fc46330 100644
--- a/app/utils.ts
+++ b/app/utils.ts
@@ -386,3 +386,37 @@ export function getOperationId(operation: {
`${operation.method.toUpperCase()}${operation.path.replaceAll("/", "_")}`
);
}
+
+export function clientUpdate() {
+ // this a wild for updating client app
+ return window.__TAURI__?.updater
+ .checkUpdate()
+ .then((updateResult) => {
+ if (updateResult.shouldUpdate) {
+ window.__TAURI__?.updater
+ .installUpdate()
+ .then((result) => {
+ showToast(Locale.Settings.Update.Success);
+ })
+ .catch((e) => {
+ console.error("[Install Update Error]", e);
+ showToast(Locale.Settings.Update.Failed);
+ });
+ }
+ })
+ .catch((e) => {
+ console.error("[Check Update Error]", e);
+ showToast(Locale.Settings.Update.Failed);
+ });
+}
+
+// https://gist.github.com/iwill/a83038623ba4fef6abb9efca87ae9ccb
+export function semverCompare(a: string, b: string) {
+ if (a.startsWith(b + "-")) return -1;
+ if (b.startsWith(a + "-")) return 1;
+ return a.localeCompare(b, undefined, {
+ numeric: true,
+ sensitivity: "case",
+ caseFirst: "upper",
+ });
+}
diff --git a/package.json b/package.json
index e43344534..803c0d1a4 100644
--- a/package.json
+++ b/package.json
@@ -58,7 +58,7 @@
"@tauri-apps/cli": "1.5.11",
"@testing-library/jest-dom": "^6.4.8",
"@testing-library/react": "^16.0.0",
- "@types/jest": "^29.5.12",
+ "@types/jest": "^29.5.13",
"@types/js-yaml": "4.0.9",
"@types/lodash-es": "^4.17.12",
"@types/node": "^20.11.30",
diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json
index cc137ee8a..b2c3e04b0 100644
--- a/src-tauri/tauri.conf.json
+++ b/src-tauri/tauri.conf.json
@@ -9,7 +9,7 @@
},
"package": {
"productName": "NextChat",
- "version": "2.15.4"
+ "version": "2.15.5"
},
"tauri": {
"allowlist": {
@@ -99,7 +99,7 @@
"endpoints": [
"https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/releases/latest/download/latest.json"
],
- "dialog": false,
+ "dialog": true,
"windows": {
"installMode": "passive"
},
diff --git a/yarn.lock b/yarn.lock
index 0f6a29d6d..ef296924e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2263,10 +2263,10 @@
dependencies:
"@types/istanbul-lib-report" "*"
-"@types/jest@^29.5.12":
- version "29.5.12"
- resolved "https://registry.npmmirror.com/@types/jest/-/jest-29.5.12.tgz#7f7dc6eb4cf246d2474ed78744b05d06ce025544"
- integrity sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==
+"@types/jest@^29.5.13":
+ version "29.5.13"
+ resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.13.tgz#8bc571659f401e6a719a7bf0dbcb8b78c71a8adc"
+ integrity sha512-wd+MVEZCHt23V0/L642O5APvspWply/rGY5BcW4SUETo2UzPU3Z26qr8jC2qxpimI2jjx9h7+2cj2FwIr01bXg==
dependencies:
expect "^29.0.0"
pretty-format "^29.0.0"