mirror of
https://github.com/jamiepine/voicebox.git
synced 2026-09-16 13:20:39 -07:00
- Introduced useGenerationForm hook to streamline audio generation form handling, including validation and model download management. - Updated FloatingGenerateBox and GenerationForm components to utilize the new hook, enhancing code organization and reducing duplication. - Replaced console logging with a debug utility for better logging control during audio playback and generation processes. - Improved error handling in HistoryTable and MainEditor components by integrating toast notifications for user feedback. - Adjusted audio recording duration limits across various components for consistency.
161 lines
5.3 KiB
TypeScript
161 lines
5.3 KiB
TypeScript
import { useEffect, useRef, useState } from 'react';
|
|
import { RouterProvider } from '@tanstack/react-router';
|
|
import voiceboxLogo from '@/assets/voicebox-logo.png';
|
|
import ShinyText from '@/components/ShinyText';
|
|
import { TitleBarDragRegion } from '@/components/TitleBarDragRegion';
|
|
import { TOP_SAFE_AREA_PADDING } from '@/lib/constants/ui';
|
|
import {
|
|
isTauri,
|
|
setKeepServerRunning,
|
|
setupWindowCloseHandler,
|
|
startServer,
|
|
} from '@/lib/tauri';
|
|
import { cn } from '@/lib/utils/cn';
|
|
import { router } from '@/router';
|
|
import { useServerStore } from '@/stores/serverStore';
|
|
|
|
const LOADING_MESSAGES = [
|
|
'Warming up tensors...',
|
|
'Calibrating synthesizer engine...',
|
|
'Initializing voice models...',
|
|
'Loading neural networks...',
|
|
'Preparing audio pipelines...',
|
|
'Optimizing waveform generators...',
|
|
'Tuning frequency analyzers...',
|
|
'Building voice embeddings...',
|
|
'Configuring text-to-speech cores...',
|
|
'Syncing audio buffers...',
|
|
'Establishing model connections...',
|
|
'Preprocessing training data...',
|
|
'Validating voice samples...',
|
|
'Compiling inference engines...',
|
|
'Mapping phoneme sequences...',
|
|
'Aligning prosody parameters...',
|
|
'Activating speech synthesis...',
|
|
'Fine-tuning acoustic models...',
|
|
'Preparing voice cloning matrices...',
|
|
'Initializing Qwen TTS framework...',
|
|
];
|
|
|
|
function App() {
|
|
const [serverReady, setServerReady] = useState(false);
|
|
const [loadingMessageIndex, setLoadingMessageIndex] = useState(0);
|
|
const serverStartingRef = useRef(false);
|
|
|
|
// Sync stored setting to Rust on startup
|
|
useEffect(() => {
|
|
if (isTauri()) {
|
|
const keepRunning = useServerStore.getState().keepServerRunningOnClose;
|
|
setKeepServerRunning(keepRunning).catch((error) => {
|
|
console.error('Failed to sync initial setting to Rust:', error);
|
|
});
|
|
}
|
|
}, []);
|
|
|
|
// Setup window close handler and auto-start server when running in Tauri (production only)
|
|
useEffect(() => {
|
|
if (!isTauri()) {
|
|
return;
|
|
}
|
|
|
|
// Setup window close handler to check setting and stop server if needed
|
|
// This works in both dev and prod, but will only stop server if it was started by the app
|
|
setupWindowCloseHandler().catch((error) => {
|
|
console.error('Failed to setup window close handler:', error);
|
|
});
|
|
|
|
// Only auto-start server in production mode
|
|
// In dev mode, user runs server separately
|
|
if (!import.meta.env?.PROD) {
|
|
console.log('Dev mode: Skipping auto-start of server (run it separately)');
|
|
setServerReady(true); // Mark as ready so UI doesn't show loading screen
|
|
// Mark that server was not started by app (so we don't try to stop it on close)
|
|
// @ts-expect-error - adding property to window
|
|
window.__voiceboxServerStartedByApp = false;
|
|
return;
|
|
}
|
|
|
|
// Auto-start server in production
|
|
if (serverStartingRef.current) {
|
|
return;
|
|
}
|
|
|
|
serverStartingRef.current = true;
|
|
console.log('Production mode: Starting bundled server...');
|
|
|
|
startServer(false)
|
|
.then((serverUrl) => {
|
|
console.log('Server is ready at:', serverUrl);
|
|
// Update the server URL in the store with the dynamically assigned port
|
|
useServerStore.getState().setServerUrl(serverUrl);
|
|
setServerReady(true);
|
|
// Mark that we started the server (so we know to stop it on close)
|
|
window.__voiceboxServerStartedByApp = true;
|
|
})
|
|
.catch((error) => {
|
|
console.error('Failed to auto-start server:', error);
|
|
serverStartingRef.current = false;
|
|
window.__voiceboxServerStartedByApp = false;
|
|
});
|
|
|
|
// Cleanup: stop server on actual unmount (not StrictMode remount)
|
|
// Note: Window close is handled separately in Tauri Rust code
|
|
return () => {
|
|
// Window close event handles server shutdown based on setting
|
|
serverStartingRef.current = false;
|
|
};
|
|
}, []);
|
|
|
|
// Cycle through loading messages every 3 seconds
|
|
useEffect(() => {
|
|
if (!isTauri() || serverReady) {
|
|
return;
|
|
}
|
|
|
|
const interval = setInterval(() => {
|
|
setLoadingMessageIndex((prev) => (prev + 1) % LOADING_MESSAGES.length);
|
|
}, 3000);
|
|
|
|
return () => clearInterval(interval);
|
|
}, [serverReady]);
|
|
|
|
// Show loading screen while server is starting in Tauri
|
|
if (isTauri() && !serverReady) {
|
|
return (
|
|
<div
|
|
className={cn(
|
|
'min-h-screen bg-background flex items-center justify-center',
|
|
TOP_SAFE_AREA_PADDING,
|
|
)}
|
|
>
|
|
<TitleBarDragRegion />
|
|
<div className="text-center space-y-6">
|
|
<div className="flex justify-center relative">
|
|
<div className="absolute inset-0 flex items-center justify-center">
|
|
<div className="w-48 h-48 rounded-full bg-accent/20 blur-3xl" />
|
|
</div>
|
|
<img
|
|
src={voiceboxLogo}
|
|
alt="Voicebox"
|
|
className="w-48 h-48 object-contain animate-fade-in-scale relative z-10"
|
|
/>
|
|
</div>
|
|
<div className="animate-fade-in-delayed">
|
|
<ShinyText
|
|
text={LOADING_MESSAGES[loadingMessageIndex]}
|
|
className="text-lg font-medium text-muted-foreground"
|
|
speed={2}
|
|
color="hsl(var(--muted-foreground))"
|
|
shineColor="hsl(var(--foreground))"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return <RouterProvider router={router} />;
|
|
}
|
|
|
|
export default App;
|