diff --git a/app/src/components/ServerTab/ChangelogPage.tsx b/app/src/components/ServerTab/ChangelogPage.tsx index 9582d0c2..afb32b47 100644 --- a/app/src/components/ServerTab/ChangelogPage.tsx +++ b/app/src/components/ServerTab/ChangelogPage.tsx @@ -57,8 +57,8 @@ function renderMarkdown(md: string): React.ReactNode[] { } elements.push(
| {inlineMarkdown(h)} @@ -107,10 +107,10 @@ function renderTable(tableLines: string[], keyBase: number): React.ReactNode { |
|---|
| + {rows.map((row, rowIdx) => ( + |
| {inlineMarkdown(cell)} | ))} diff --git a/app/src/components/ServerTab/GeneralPage.tsx b/app/src/components/ServerTab/GeneralPage.tsx index 51b5ed74..ccf117b1 100644 --- a/app/src/components/ServerTab/GeneralPage.tsx +++ b/app/src/components/ServerTab/GeneralPage.tsx @@ -133,6 +133,13 @@ export function GeneralPage() { setKeepServerRunningOnClose(checked); platform.lifecycle.setKeepServerRunning(checked).catch((error) => { console.error('Failed to sync setting to Rust:', error); + setKeepServerRunningOnClose(!checked); + toast({ + title: 'Failed to update setting', + description: 'Could not sync setting to backend.', + variant: 'destructive', + }); + return; }); toast({ title: 'Setting updated', diff --git a/app/src/components/ServerTab/GpuPage.tsx b/app/src/components/ServerTab/GpuPage.tsx index f0f38681..0ebe1d37 100644 --- a/app/src/components/ServerTab/GpuPage.tsx +++ b/app/src/components/ServerTab/GpuPage.tsx @@ -171,17 +171,21 @@ export function GpuPage() { }; }, [cudaDownloading, serverUrl, refetchCudaStatus]); + const clearHealthPolling = useCallback(() => { + if (healthPollRef.current) { + clearInterval(healthPollRef.current); + healthPollRef.current = null; + } + }, []); + const startHealthPolling = useCallback(() => { - if (healthPollRef.current) return; + clearHealthPolling(); healthPollRef.current = setInterval(async () => { try { const result = await apiClient.getHealth(); if (result.status === 'healthy') { - if (healthPollRef.current) { - clearInterval(healthPollRef.current); - healthPollRef.current = null; - } + clearHealthPolling(); setRestartPhase('ready'); queryClient.invalidateQueries(); setTimeout(() => setRestartPhase('idle'), 2000); @@ -190,7 +194,23 @@ export function GpuPage() { // Server still down, keep polling } }, 1000); - }, [queryClient]); + }, [queryClient, clearHealthPolling]); + + const restartServerWithPolling = useCallback( + async (errorMessage: string) => { + setRestartPhase('stopping'); + try { + await platform.lifecycle.restartServer(); + setRestartPhase('waiting'); + startHealthPolling(); + } catch (e: unknown) { + clearHealthPolling(); + setRestartPhase('idle'); + throw new Error(e instanceof Error ? e.message : errorMessage); + } + }, + [platform, startHealthPolling, clearHealthPolling], + ); const handleDownload = async () => { setError(null); @@ -209,24 +229,9 @@ export function GpuPage() { const handleRestart = async () => { setError(null); - setRestartPhase('stopping'); try { - setRestartPhase('waiting'); - startHealthPolling(); - await platform.lifecycle.restartServer(); - if (healthPollRef.current) { - clearInterval(healthPollRef.current); - healthPollRef.current = null; - } - setRestartPhase('ready'); - queryClient.invalidateQueries(); - setTimeout(() => setRestartPhase('idle'), 2000); + await restartServerWithPolling('Restart failed'); } catch (e: unknown) { - setRestartPhase('idle'); - if (healthPollRef.current) { - clearInterval(healthPollRef.current); - healthPollRef.current = null; - } setError(e instanceof Error ? e.message : 'Restart failed'); } }; @@ -236,22 +241,8 @@ export function GpuPage() { setRestartPhase('stopping'); try { await apiClient.deleteCudaBackend(); - setRestartPhase('waiting'); - startHealthPolling(); - await platform.lifecycle.restartServer(); - if (healthPollRef.current) { - clearInterval(healthPollRef.current); - healthPollRef.current = null; - } - setRestartPhase('ready'); - queryClient.invalidateQueries(); - setTimeout(() => setRestartPhase('idle'), 2000); + await restartServerWithPolling('Failed to switch to CPU'); } catch (e: unknown) { - setRestartPhase('idle'); - if (healthPollRef.current) { - clearInterval(healthPollRef.current); - healthPollRef.current = null; - } setError(e instanceof Error ? e.message : 'Failed to switch to CPU'); refetchCudaStatus(); } diff --git a/app/src/components/ServerTab/LogsPage.tsx b/app/src/components/ServerTab/LogsPage.tsx index b7cf85ae..68b8f317 100644 --- a/app/src/components/ServerTab/LogsPage.tsx +++ b/app/src/components/ServerTab/LogsPage.tsx @@ -96,7 +96,7 @@ export function LogsPage() { )} ) : ( - entries.map((entry, i) =>