From 5fa253c99e2772b2e69157d5771dd48018ff3f7b Mon Sep 17 00:00:00 2001 From: Lee Jackson <130007945+Imagineer99@users.noreply.github.com> Date: Mon, 15 Jun 2026 19:41:12 +0100 Subject: [PATCH] Studio: add Studio version info to General settings (#5675) * chore: add Studio version info to General settings * chore: cache Studio version fetch * fix: clear cached Studio version request on failure * fix: retry incomplete Studio version fetch * fix: remove cached Studio version fetch * chore: move connections below API * fix: localize studio version rows --- .../components/studio-version-section.tsx | 64 +++++++++++++------ .../src/features/settings/settings-dialog.tsx | 12 ++-- .../src/features/settings/tabs/about-tab.tsx | 1 + .../features/settings/tabs/general-tab.tsx | 2 + 4 files changed, 55 insertions(+), 24 deletions(-) diff --git a/studio/frontend/src/features/settings/components/studio-version-section.tsx b/studio/frontend/src/features/settings/components/studio-version-section.tsx index 9b0c513cbf..2c23e14f97 100644 --- a/studio/frontend/src/features/settings/components/studio-version-section.tsx +++ b/studio/frontend/src/features/settings/components/studio-version-section.tsx @@ -1,7 +1,7 @@ // SPDX-License-Identifier: AGPL-3.0-only // Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 -import { getAuthToken } from "@/features/auth"; +import { getAuthToken, refreshSession } from "@/features/auth"; import { useT } from "@/i18n"; import { apiUrl } from "@/lib/api-base"; import { useEffect, useState } from "react"; @@ -9,29 +9,57 @@ import { SettingsRow } from "./settings-row"; import { SettingsSection } from "./settings-section"; type ApiObject = Record; - -async function fetchStudioVersions(): Promise<{ +type StudioVersions = { packageVersion: string | null; studioVersion: string | null; -}> { +}; + +const EMPTY_VERSIONS: StudioVersions = { + packageVersion: null, + studioVersion: null, +}; + +function parseStudioVersions(data: ApiObject): StudioVersions { + const packageVersion = data["version"]; + const studioVersion = data["studio_version"]; + return { + packageVersion: + typeof packageVersion === "string" ? packageVersion : null, + studioVersion: typeof studioVersion === "string" ? studioVersion : null, + }; +} + +function hasAllVersions(versions: StudioVersions): boolean { + return Boolean(versions.packageVersion && versions.studioVersion); +} + +async function requestStudioVersions( + token: string | null, +): Promise { + const headers = new Headers(); + if (token) headers.set("Authorization", `Bearer ${token}`); + const res = await fetch(apiUrl("/api/health"), { headers }); + if (!res.ok) { + return EMPTY_VERSIONS; + } + const data = (await res.json()) as ApiObject; + return parseStudioVersions(data); +} + +async function fetchStudioVersions(): Promise { try { const token = getAuthToken(); - const headers = new Headers(); - if (token) headers.set("Authorization", `Bearer ${token}`); - const res = await fetch(apiUrl("/api/health"), { headers }); - if (!res.ok) { - return { packageVersion: null, studioVersion: null }; + const versions = await requestStudioVersions(token); + if (!token || hasAllVersions(versions)) { + return versions; } - const data = (await res.json()) as ApiObject; - const packageVersion = data.version; - const studioVersion = data.studio_version; - return { - packageVersion: - typeof packageVersion === "string" ? packageVersion : null, - studioVersion: typeof studioVersion === "string" ? studioVersion : null, - }; + + if (await refreshSession()) { + return requestStudioVersions(getAuthToken()); + } + return versions; } catch { - return { packageVersion: null, studioVersion: null }; + return EMPTY_VERSIONS; } } diff --git a/studio/frontend/src/features/settings/settings-dialog.tsx b/studio/frontend/src/features/settings/settings-dialog.tsx index 0ddba11b3a..a2da6c201b 100644 --- a/studio/frontend/src/features/settings/settings-dialog.tsx +++ b/studio/frontend/src/features/settings/settings-dialog.tsx @@ -50,18 +50,18 @@ const TABS: TabDef[] = [ icon: PaintBrush02Icon, }, { id: "chat", labelKey: "settings.tabs.chat", icon: Message01Icon }, - { - id: "connections", - labelKey: "settings.tabs.connections", - icon: CloudIcon, - badgeKey: "common.new", - }, { id: "api-keys", labelKey: "settings.tabs.apiKeys", icon: Globe02Icon, badgeKey: "common.new", }, + { + id: "connections", + labelKey: "settings.tabs.connections", + icon: CloudIcon, + badgeKey: "common.new", + }, { id: "about", labelKey: "settings.tabs.about", icon: HelpCircleIcon }, ]; diff --git a/studio/frontend/src/features/settings/tabs/about-tab.tsx b/studio/frontend/src/features/settings/tabs/about-tab.tsx index 2394e2ad92..fc4a0a953e 100644 --- a/studio/frontend/src/features/settings/tabs/about-tab.tsx +++ b/studio/frontend/src/features/settings/tabs/about-tab.tsx @@ -125,6 +125,7 @@ export function AboutTab() { {hw.gpus.map((gpu, i) => ( 1 diff --git a/studio/frontend/src/features/settings/tabs/general-tab.tsx b/studio/frontend/src/features/settings/tabs/general-tab.tsx index c8cc27649d..64408c9e90 100644 --- a/studio/frontend/src/features/settings/tabs/general-tab.tsx +++ b/studio/frontend/src/features/settings/tabs/general-tab.tsx @@ -296,6 +296,8 @@ export function GeneralTab() { + +