Merge remote-tracking branch 'origin/main' into codex/webui-segmented-transcript-store
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
} from "react";
|
||||
import {
|
||||
Activity,
|
||||
ArrowUpCircle,
|
||||
Bot,
|
||||
Brain,
|
||||
Check,
|
||||
@@ -22,6 +23,7 @@ import {
|
||||
Database,
|
||||
Eye,
|
||||
EyeOff,
|
||||
ExternalLink,
|
||||
Gem,
|
||||
Globe2,
|
||||
Grid3X3,
|
||||
@@ -75,6 +77,7 @@ import {
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import {
|
||||
checkVersion,
|
||||
createModelConfiguration,
|
||||
fetchSettings,
|
||||
fetchSettingsUsage,
|
||||
@@ -1852,6 +1855,104 @@ function OverviewSettings({
|
||||
/>
|
||||
</SettingsGroup>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<SettingsSectionTitle>{tx("settings.sections.about", "About")}</SettingsSectionTitle>
|
||||
<SettingsGroup>
|
||||
<VersionCheckRow currentVersion={settings.version?.current} />
|
||||
</SettingsGroup>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function VersionCheckRow({ currentVersion }: { currentVersion?: string }) {
|
||||
const { t } = useTranslation();
|
||||
const tx = (key: string, fallback: string) => t(key, { defaultValue: fallback });
|
||||
const { token } = useClient();
|
||||
const [checking, setChecking] = useState(false);
|
||||
const [result, setResult] = useState<
|
||||
| { type: "up-to-date" }
|
||||
| { type: "update"; latestVersion: string; pypiUrl?: string }
|
||||
| { type: "error"; message: string }
|
||||
| null
|
||||
>(null);
|
||||
|
||||
const handleCheck = async () => {
|
||||
setChecking(true);
|
||||
setResult(null);
|
||||
try {
|
||||
const res = await checkVersion(token);
|
||||
if (res.updateAvailable) {
|
||||
setResult({
|
||||
type: "update",
|
||||
latestVersion: res.updateAvailable.latestVersion,
|
||||
pypiUrl: res.updateAvailable.pypiUrl,
|
||||
});
|
||||
} else {
|
||||
setResult({ type: "up-to-date" });
|
||||
}
|
||||
} catch (err) {
|
||||
setResult({ type: "error", message: (err as Error).message });
|
||||
} finally {
|
||||
setChecking(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex min-h-[62px] flex-col gap-3 px-4 py-3.5 sm:flex-row sm:items-center sm:justify-between sm:px-5">
|
||||
<div className="min-w-0">
|
||||
<div className="text-[14px] font-medium leading-5 text-foreground">
|
||||
{tx("settings.about.version", "Version")}
|
||||
</div>
|
||||
<div className="mt-0.5 text-[12px] leading-5 text-muted-foreground">
|
||||
{currentVersion ? `v${currentVersion}` : "nanobot"}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex shrink-0 flex-col items-end gap-2">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => void handleCheck()}
|
||||
disabled={checking}
|
||||
className="rounded-full"
|
||||
>
|
||||
{checking ? (
|
||||
<Loader2 className="mr-1.5 h-3.5 w-3.5 animate-spin" aria-hidden />
|
||||
) : (
|
||||
<ArrowUpCircle className="mr-1.5 h-3.5 w-3.5" aria-hidden />
|
||||
)}
|
||||
{checking
|
||||
? tx("settings.about.checking", "Checking...")
|
||||
: tx("settings.about.checkForUpdates", "Check for updates")}
|
||||
</Button>
|
||||
{result?.type === "up-to-date" ? (
|
||||
<span className="inline-flex items-center gap-1.5 text-[12px] text-emerald-600 dark:text-emerald-300">
|
||||
<Check className="h-3 w-3" aria-hidden />
|
||||
{tx("settings.about.upToDate", "You're up to date")}
|
||||
</span>
|
||||
) : null}
|
||||
{result?.type === "update" ? (
|
||||
<span className="inline-flex items-center gap-1.5 text-[12px] text-blue-600 dark:text-blue-300">
|
||||
<ArrowUpCircle className="h-3 w-3" aria-hidden />
|
||||
{tx("settings.about.updateAvailable", "Update available")}{result.latestVersion && ` v${result.latestVersion}`}
|
||||
{result.pypiUrl ? (
|
||||
<a
|
||||
href={result.pypiUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-0.5 underline-offset-2 hover:underline"
|
||||
>
|
||||
PyPI
|
||||
<ExternalLink className="h-2.5 w-2.5" aria-hidden />
|
||||
</a>
|
||||
) : null}
|
||||
</span>
|
||||
) : null}
|
||||
{result?.type === "error" ? (
|
||||
<span className="text-[12px] text-destructive">{result.message}</span>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -244,6 +244,26 @@ export async function fetchSettingsUsage(
|
||||
);
|
||||
}
|
||||
|
||||
export interface VersionCheckResult {
|
||||
updateAvailable: {
|
||||
currentVersion: string;
|
||||
latestVersion: string;
|
||||
pypiUrl?: string;
|
||||
} | null;
|
||||
}
|
||||
|
||||
export async function checkVersion(
|
||||
token: string,
|
||||
base: string = "",
|
||||
): Promise<VersionCheckResult> {
|
||||
return request<VersionCheckResult>(
|
||||
`${base}/api/settings/version-check`,
|
||||
token,
|
||||
undefined,
|
||||
10_000,
|
||||
);
|
||||
}
|
||||
|
||||
export async function fetchWorkspaces(
|
||||
token: string,
|
||||
base: string = "",
|
||||
|
||||
@@ -480,10 +480,14 @@ export interface SettingsPayload {
|
||||
mcp_server_count: number;
|
||||
exec_enabled: boolean;
|
||||
exec_sandbox?: string | null;
|
||||
exec_path_prepend_set: boolean;
|
||||
exec_path_append_set: boolean;
|
||||
};
|
||||
requires_restart: boolean;
|
||||
restart_required_sections?: Array<"runtime" | "browser" | "image">;
|
||||
version?: {
|
||||
current: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface AppPackageRef {
|
||||
|
||||
@@ -125,6 +125,7 @@ function baseSettingsPayload() {
|
||||
mcp_server_count: 0,
|
||||
exec_enabled: true,
|
||||
exec_sandbox: null,
|
||||
exec_path_prepend_set: false,
|
||||
exec_path_append_set: false,
|
||||
},
|
||||
requires_restart: false,
|
||||
@@ -1023,6 +1024,7 @@ describe("App layout", () => {
|
||||
mcp_server_count: 0,
|
||||
exec_enabled: true,
|
||||
exec_sandbox: null,
|
||||
exec_path_prepend_set: false,
|
||||
exec_path_append_set: false,
|
||||
},
|
||||
requires_restart: false,
|
||||
@@ -1349,6 +1351,7 @@ describe("App layout", () => {
|
||||
mcp_server_count: 0,
|
||||
exec_enabled: true,
|
||||
exec_sandbox: null,
|
||||
exec_path_prepend_set: false,
|
||||
exec_path_append_set: false,
|
||||
},
|
||||
requires_restart: false,
|
||||
|
||||
@@ -93,6 +93,7 @@ function settingsPayload(): SettingsPayload {
|
||||
mcp_server_count: 0,
|
||||
exec_enabled: true,
|
||||
exec_sandbox: null,
|
||||
exec_path_prepend_set: false,
|
||||
exec_path_append_set: false,
|
||||
},
|
||||
requires_restart: false,
|
||||
|
||||
@@ -212,6 +212,7 @@ function modelSettings(model: string, provider: string): SettingsPayload {
|
||||
mcp_server_count: 0,
|
||||
exec_enabled: true,
|
||||
exec_sandbox: null,
|
||||
exec_path_prepend_set: false,
|
||||
exec_path_append_set: false,
|
||||
},
|
||||
requires_restart: false,
|
||||
|
||||
Reference in New Issue
Block a user