diff --git a/apps/desktop/package.json b/apps/desktop/package.json
index e151b9f6fcda..8dd575b5a6bc 100644
--- a/apps/desktop/package.json
+++ b/apps/desktop/package.json
@@ -1,6 +1,6 @@
{
"name": "@t3tools/desktop",
- "version": "0.0.2",
+ "version": "0.0.3",
"private": true,
"main": "dist-electron/main.js",
"scripts": {
diff --git a/apps/desktop/src/main.ts b/apps/desktop/src/main.ts
index 46068492950c..7cf36bc1c730 100644
--- a/apps/desktop/src/main.ts
+++ b/apps/desktop/src/main.ts
@@ -56,6 +56,7 @@ const UPDATE_STATE_CHANNEL = "desktop:update-state";
const UPDATE_GET_STATE_CHANNEL = "desktop:update-get-state";
const UPDATE_DOWNLOAD_CHANNEL = "desktop:update-download";
const UPDATE_INSTALL_CHANNEL = "desktop:update-install";
+const UPDATE_CHECK_CHANNEL = "desktop:update-check";
const STATE_DIR =
process.env.T3CODE_STATE_DIR?.trim() || Path.join(OS.homedir(), ".t3", "userdata");
const DESKTOP_SCHEME = "t3";
@@ -1185,6 +1186,12 @@ function registerIpcHandlers(): void {
ipcMain.removeHandler(UPDATE_GET_STATE_CHANNEL);
ipcMain.handle(UPDATE_GET_STATE_CHANNEL, async () => updateState);
+ ipcMain.removeHandler(UPDATE_CHECK_CHANNEL);
+ ipcMain.handle(UPDATE_CHECK_CHANNEL, async () => {
+ await checkForUpdates("manual");
+ return updateState;
+ });
+
ipcMain.removeHandler(UPDATE_DOWNLOAD_CHANNEL);
ipcMain.handle(UPDATE_DOWNLOAD_CHANNEL, async () => {
const result = await downloadAvailableUpdate();
diff --git a/apps/desktop/src/preload.ts b/apps/desktop/src/preload.ts
index 1e1bb3bd8ebe..f71a173d8b7e 100644
--- a/apps/desktop/src/preload.ts
+++ b/apps/desktop/src/preload.ts
@@ -11,6 +11,7 @@ const UPDATE_STATE_CHANNEL = "desktop:update-state";
const UPDATE_GET_STATE_CHANNEL = "desktop:update-get-state";
const UPDATE_DOWNLOAD_CHANNEL = "desktop:update-download";
const UPDATE_INSTALL_CHANNEL = "desktop:update-install";
+const UPDATE_CHECK_CHANNEL = "desktop:update-check";
const wsUrl = process.env.T3CODE_DESKTOP_WS_URL ?? null;
contextBridge.exposeInMainWorld("desktopBridge", {
@@ -32,6 +33,7 @@ contextBridge.exposeInMainWorld("desktopBridge", {
};
},
getUpdateState: () => ipcRenderer.invoke(UPDATE_GET_STATE_CHANNEL),
+ checkForUpdate: () => ipcRenderer.invoke(UPDATE_CHECK_CHANNEL),
downloadUpdate: () => ipcRenderer.invoke(UPDATE_DOWNLOAD_CHANNEL),
installUpdate: () => ipcRenderer.invoke(UPDATE_INSTALL_CHANNEL),
onUpdateState: (listener) => {
diff --git a/apps/server/package.json b/apps/server/package.json
index bb8e480657a7..cc739c9bafc8 100644
--- a/apps/server/package.json
+++ b/apps/server/package.json
@@ -1,6 +1,6 @@
{
"name": "t3",
- "version": "0.0.2",
+ "version": "0.0.3",
"repository": {
"type": "git",
"url": "https://github.com/pingdotgg/t3code",
diff --git a/apps/web/package.json b/apps/web/package.json
index 1162b97fc0ee..7d4458031c33 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -1,6 +1,6 @@
{
"name": "@t3tools/web",
- "version": "0.0.2",
+ "version": "0.0.3",
"private": true,
"type": "module",
"scripts": {
diff --git a/apps/web/src/routes/_chat.settings.tsx b/apps/web/src/routes/_chat.settings.tsx
index ecc6c533456a..733a06d7e704 100644
--- a/apps/web/src/routes/_chat.settings.tsx
+++ b/apps/web/src/routes/_chat.settings.tsx
@@ -1,7 +1,7 @@
import { createFileRoute } from "@tanstack/react-router";
import { useQuery } from "@tanstack/react-query";
-import { useCallback, useState } from "react";
-import { type ProviderKind } from "@t3tools/contracts";
+import { useCallback, useEffect, useState } from "react";
+import { type DesktopUpdateState, type ProviderKind } from "@t3tools/contracts";
import { getModelOptions, normalizeModelSlug } from "@t3tools/shared/model";
import {
@@ -211,6 +211,82 @@ function SettingsRouteView() {
Partial
Version
-- Current version of the application. -
+Version
++ {updateState?.status === "up-to-date" + ? "You're on the latest version." + : updateState?.status === "checking" + ? "Checking for updates..." + : updateState?.status === "available" + ? `Version ${updateState.availableVersion ?? "unknown"} is available.` + : updateState?.status === "downloading" + ? `Downloading update${typeof updateState.downloadPercent === "number" ? ` (${Math.floor(updateState.downloadPercent)}%)` : ""}...` + : updateState?.status === "downloaded" + ? `Version ${updateState.downloadedVersion ?? updateState.availableVersion ?? "unknown"} is ready to install.` + : updateState?.status === "error" + ? (updateState.message ?? "Update check failed.") + : "Current version of the application."} +
+ {updateState?.checkedAt ? ( ++ Last checked: {new Date(updateState.checkedAt).toLocaleString()} +
+ ) : null} +{APP_VERSION}
+ {hasDesktopBridge ? (
+ <>
+ {updateState?.status === "available" ? (
+
+ ) : null}
+ {updateState?.status === "downloaded" ? (
+
+ ) : null}
+ {updateState?.status === "error" &&
+ updateState.errorContext === "download" &&
+ updateState.availableVersion ? (
+
+ ) : null}
+ {updateState?.status === "error" &&
+ updateState.errorContext === "install" &&
+ updateState.downloadedVersion ? (
+
+ ) : null}
+
+ >
+ ) : null}
+ {APP_VERSION}
+ {updateState?.status === "downloading" &&
+ typeof updateState.downloadPercent === "number" ? (
+