Update dependencies and enhance UI components for better functionality

- Added new Tauri plugins: @tauri-apps/plugin-process and @tauri-apps/plugin-updater to improve application capabilities.
- Introduced UpdateStatus component to display update information in the UI.
- Enhanced GenerationForm to include an optional instruct field for additional input.
- Refactored various components for improved styling and responsiveness, including Sidebar, AudioPlayer, and ProfileCard.
- Updated API models and schemas to accommodate new instruct parameter in generation requests and responses.
- Improved documentation for autoupdater setup and usage.
This commit is contained in:
Jamie Pine
2026-01-25 16:38:25 -08:00
parent 45c4c2f488
commit e7e3a0a440
87 changed files with 2421 additions and 1628 deletions
@@ -77,9 +77,7 @@ export function ConnectionForm() {
)}
/>
{isDirty && (
<Button type="submit">Update Connection</Button>
)}
{isDirty && <Button type="submit">Update Connection</Button>}
</form>
</Form>
@@ -106,8 +104,8 @@ export function ConnectionForm() {
Keep server running when app closes
</label>
<p className="text-sm text-muted-foreground">
When enabled, the server will continue running in the background after closing the app.
Disabled by default.
When enabled, the server will continue running in the background after closing the
app. Disabled by default.
</p>
</div>
</div>
@@ -73,7 +73,9 @@ export function ModelManagement() {
<div className="space-y-4">
{/* TTS Models */}
<div>
<h3 className="text-sm font-semibold mb-3 text-muted-foreground">Voice Generation Models</h3>
<h3 className="text-sm font-semibold mb-3 text-muted-foreground">
Voice Generation Models
</h3>
<div className="space-y-2">
{modelStatus.models
.filter((m) => m.model_name.startsWith('qwen-tts'))
@@ -91,7 +93,9 @@ export function ModelManagement() {
{/* Whisper Models */}
<div>
<h3 className="text-sm font-semibold mb-3 text-muted-foreground">Transcription Models</h3>
<h3 className="text-sm font-semibold mb-3 text-muted-foreground">
Transcription Models
</h3>
<div className="space-y-2">
{modelStatus.models
.filter((m) => m.model_name.startsWith('whisper'))
@@ -109,7 +113,9 @@ export function ModelManagement() {
{/* Progress indicators */}
<div className="pt-4 border-t">
<h3 className="text-sm font-semibold mb-3 text-muted-foreground">Download Progress</h3>
<h3 className="text-sm font-semibold mb-3 text-muted-foreground">
Download Progress
</h3>
<div className="space-y-2">
{modelStatus.models.map((model) => (
<ModelProgress
@@ -147,10 +153,14 @@ function ModelItem({ model, onDownload, isDownloading, formatSize }: ModelItemPr
<div className="flex items-center gap-2">
<span className="font-medium text-sm">{model.display_name}</span>
{model.loaded && (
<Badge variant="default" className="text-xs">Loaded</Badge>
<Badge variant="default" className="text-xs">
Loaded
</Badge>
)}
{model.downloaded && !model.loaded && (
<Badge variant="secondary" className="text-xs">Downloaded</Badge>
<Badge variant="secondary" className="text-xs">
Downloaded
</Badge>
)}
</div>
{model.downloaded && model.size_mb && (
@@ -166,12 +176,7 @@ function ModelItem({ model, onDownload, isDownloading, formatSize }: ModelItemPr
<span>Ready</span>
</div>
) : (
<Button
size="sm"
onClick={onDownload}
disabled={isDownloading}
variant="outline"
>
<Button size="sm" onClick={onDownload} disabled={isDownloading} variant="outline">
{isDownloading ? (
<>
<Loader2 className="h-4 w-4 mr-2 animate-spin" />
@@ -25,7 +25,7 @@ export function ModelProgress({ modelName, displayName }: ModelProgressProps) {
try {
const data = JSON.parse(event.data) as ModelProgressType;
setProgress(data);
// Close connection if complete or error
if (data.status === 'complete' || data.status === 'error') {
eventSource.close();
@@ -51,7 +51,10 @@ export function ModelProgress({ modelName, displayName }: ModelProgressProps) {
}, [serverUrl, modelName, isSubscribed]);
// Don't render if no progress or if complete/error and some time has passed
if (!progress || (progress.status === 'complete' && Date.now() - new Date(progress.timestamp).getTime() > 5000)) {
if (
!progress ||
(progress.status === 'complete' && Date.now() - new Date(progress.timestamp).getTime() > 5000)
) {
return null;
}
@@ -111,9 +114,7 @@ export function ModelProgress({ modelName, displayName }: ModelProgressProps) {
</span>
)}
</div>
{progress.total > 0 && (
<Progress value={progress.progress} className="h-2" />
)}
{progress.total > 0 && <Progress value={progress.progress} className="h-2" />}
</div>
</CardContent>
</Card>
@@ -47,7 +47,9 @@ export function ServerStatus() {
<span className="text-sm">Connected</span>
</div>
<div className="flex flex-wrap gap-2">
<Badge variant={health.model_loaded || health.model_downloaded ? 'default' : 'secondary'}>
<Badge
variant={health.model_loaded || health.model_downloaded ? 'default' : 'secondary'}
>
{health.model_loaded || health.model_downloaded ? 'Model Ready' : 'No Model'}
</Badge>
<Badge variant={health.gpu_available ? 'default' : 'secondary'}>
@@ -0,0 +1,101 @@
import { useState, useEffect } from 'react';
import { RefreshCw, Download, CheckCircle2, AlertCircle } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Progress } from '@/components/ui/progress';
import { useAutoUpdater } from '@/hooks/useAutoUpdater';
import { getVersion } from '@tauri-apps/api/app';
export function UpdateStatus() {
const { status, checkForUpdates, downloadAndInstall } = useAutoUpdater(false);
const [currentVersion, setCurrentVersion] = useState<string>('');
useEffect(() => {
getVersion()
.then(setCurrentVersion)
.catch(() => setCurrentVersion('0.1.0'));
}, []);
return (
<Card>
<CardHeader>
<CardTitle>App Updates</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex items-center justify-between">
<div className="space-y-1">
<div className="text-sm font-medium">Current Version</div>
<div className="text-sm text-muted-foreground">v{currentVersion}</div>
</div>
<Button
onClick={checkForUpdates}
disabled={status.checking || status.downloading || status.installing}
variant="outline"
size="sm"
>
<RefreshCw className={`h-4 w-4 mr-2 ${status.checking ? 'animate-spin' : ''}`} />
Check for Updates
</Button>
</div>
{status.checking && (
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<RefreshCw className="h-4 w-4 animate-spin" />
Checking for updates...
</div>
)}
{status.error && (
<div className="flex items-center gap-2 text-sm text-destructive">
<AlertCircle className="h-4 w-4" />
{status.error}
</div>
)}
{status.available && !status.downloading && !status.installing && (
<div className="space-y-3 p-4 border rounded-lg bg-primary/5">
<div className="flex items-center justify-between">
<div>
<div className="font-semibold">Update Available</div>
<div className="text-sm text-muted-foreground">Version {status.version}</div>
</div>
<Badge>New</Badge>
</div>
<Button onClick={downloadAndInstall} className="w-full" size="sm">
<Download className="h-4 w-4 mr-2" />
Install Update
</Button>
</div>
)}
{status.downloading && (
<div className="space-y-2">
<div className="flex items-center gap-2 text-sm">
<Download className="h-4 w-4" />
Downloading update...
</div>
<Progress />
</div>
)}
{status.installing && (
<div className="space-y-2">
<div className="flex items-center gap-2 text-sm">
<RefreshCw className="h-4 w-4 animate-spin" />
Installing update...
</div>
<div className="text-xs text-muted-foreground">App will restart automatically</div>
</div>
)}
{!status.available && !status.checking && !status.error && status.checking === false && (
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<CheckCircle2 className="h-4 w-4 text-green-500" />
You're up to date
</div>
)}
</CardContent>
</Card>
);
}