mirror of
https://github.com/jamiepine/voicebox.git
synced 2026-09-16 13:20:39 -07:00
- Bumped version for @voicebox/app, @voicebox/landing, @voicebox/tauri, and @voicebox/web to 0.1.4. - Added @tanstack/react-router dependency for enhanced routing capabilities. - Refactored App component to utilize RouterProvider for routing management. - Created a new router configuration in router.tsx to define application routes and layout. - Updated Sidebar component to use Link from @tanstack/react-router for navigation.
165 lines
5.4 KiB
TypeScript
165 lines
5.4 KiB
TypeScript
import { useEffect, 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';
|
|
|
|
// Track if server is starting to prevent duplicate starts
|
|
let serverStarting = false;
|
|
|
|
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);
|
|
|
|
// 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 (serverStarting) {
|
|
return;
|
|
}
|
|
|
|
serverStarting = 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)
|
|
// @ts-expect-error - adding property to window
|
|
window.__voiceboxServerStartedByApp = true;
|
|
})
|
|
.catch((error) => {
|
|
console.error('Failed to auto-start server:', error);
|
|
serverStarting = false;
|
|
// @ts-expect-error - adding property to window
|
|
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
|
|
serverStarting = 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;
|