Files
voicebox/app/src/stores/serverStore.ts
T
James Pine 60aac279ce fix: address PR #319 review feedback — health validation, error handling, queryClient decoupling
- Rust: Replace fragile body.contains("status") with proper JSON
  deserialization validating status=="healthy", model_loaded (bool),
  and gpu_available (bool) to prevent misidentifying non-Voicebox services

- Frontend: Validate health response has Voicebox-specific fields before
  marking server as ready during fallback polling

- Frontend: Discriminate port-in-use errors (poll for external server) from
  real startup failures (missing sidecar, signing issues) — surface errors
  immediately with a startupError state and Retry button in the UI

- Frontend: Set explicit startup-error state when 2-minute polling timeout
  expires so the loading screen shows actionable feedback instead of hanging

- Architecture: Extract QueryClient to standalone side-effect-free module
  (lib/queryClient.ts) to decouple serverStore from React bootstrap entrypoint
2026-03-21 10:24:15 -07:00

83 lines
2.2 KiB
TypeScript

import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import { queryClient } from '@/lib/queryClient';
interface ServerStore {
serverUrl: string;
setServerUrl: (url: string) => void;
isConnected: boolean;
setIsConnected: (connected: boolean) => void;
mode: 'local' | 'remote';
setMode: (mode: 'local' | 'remote') => void;
keepServerRunningOnClose: boolean;
setKeepServerRunningOnClose: (keepRunning: boolean) => void;
maxChunkChars: number;
setMaxChunkChars: (value: number) => void;
crossfadeMs: number;
setCrossfadeMs: (value: number) => void;
normalizeAudio: boolean;
setNormalizeAudio: (value: boolean) => void;
autoplayOnGenerate: boolean;
setAutoplayOnGenerate: (value: boolean) => void;
customModelsDir: string | null;
setCustomModelsDir: (dir: string | null) => void;
}
/**
* Invalidate all React Query caches so stale data from the previous
* server is not shown. Called when the server URL changes.
*/
function invalidateAllServerData() {
queryClient.invalidateQueries();
}
export const useServerStore = create<ServerStore>()(
persist(
(set, get) => ({
serverUrl: 'http://127.0.0.1:17493',
setServerUrl: (url) => {
const prev = get().serverUrl;
set({ serverUrl: url });
if (url !== prev) {
invalidateAllServerData();
}
},
isConnected: false,
setIsConnected: (connected) => set({ isConnected: connected }),
mode: 'local',
setMode: (mode) => set({ mode }),
keepServerRunningOnClose: false,
setKeepServerRunningOnClose: (keepRunning) => set({ keepServerRunningOnClose: keepRunning }),
maxChunkChars: 800,
setMaxChunkChars: (value) => set({ maxChunkChars: value }),
crossfadeMs: 50,
setCrossfadeMs: (value) => set({ crossfadeMs: value }),
normalizeAudio: true,
setNormalizeAudio: (value) => set({ normalizeAudio: value }),
autoplayOnGenerate: true,
setAutoplayOnGenerate: (value) => set({ autoplayOnGenerate: value }),
customModelsDir: null,
setCustomModelsDir: (dir) => set({ customModelsDir: dir }),
}),
{
name: 'voicebox-server',
},
),
);