diff --git a/app/src/components/ServerSettings/UpdateStatus.tsx b/app/src/components/ServerSettings/UpdateStatus.tsx
index 7e1e160d..31e4640b 100644
--- a/app/src/components/ServerSettings/UpdateStatus.tsx
+++ b/app/src/components/ServerSettings/UpdateStatus.tsx
@@ -71,11 +71,23 @@ export function UpdateStatus() {
{status.downloading && (
-
-
- Downloading update...
+
+
+
+ Downloading update...
+
+ {status.downloadProgress !== undefined && (
+
+ {status.downloadProgress}%
+
+ )}
-
+
+ {status.downloadedBytes !== undefined && status.totalBytes !== undefined && status.totalBytes > 0 && (
+
+ {(status.downloadedBytes / 1024 / 1024).toFixed(1)} MB / {(status.totalBytes / 1024 / 1024).toFixed(1)} MB
+
+ )}
)}
diff --git a/app/src/components/VoiceProfiles/AudioSampleRecording.tsx b/app/src/components/VoiceProfiles/AudioSampleRecording.tsx
index 34c3c372..9c7c9b62 100644
--- a/app/src/components/VoiceProfiles/AudioSampleRecording.tsx
+++ b/app/src/components/VoiceProfiles/AudioSampleRecording.tsx
@@ -1,4 +1,4 @@
-import { Mic, Square, Play, Pause } from 'lucide-react';
+import { Mic, Pause, Play, Square } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { FormControl, FormItem, FormLabel, FormMessage } from '@/components/ui/form';
import { formatAudioDuration } from '@/lib/utils/audio';
@@ -35,12 +35,7 @@ export function AudioSampleRecording({
{!isRecording && !file && (
-
-
- File: {file.name}
-
+
File: {file.name}
-
+
{isPlaying ? : }
{!isRecording && !file && (
-
+
Start Capture
@@ -81,16 +76,9 @@ export function AudioSampleSystem({
Capture complete
-
- File: {file.name}
-
+ File: {file.name}
-
+
{isPlaying ? : }
{
@@ -25,7 +28,7 @@ export function useAutoUpdater(checkOnMount = false) {
const [update, setUpdate] = useState(null);
- const checkForUpdates = async () => {
+ const checkForUpdates = useCallback(async () => {
if (!isTauri()) {
return;
}
@@ -61,7 +64,7 @@ export function useAutoUpdater(checkOnMount = false) {
error: error instanceof Error ? error.message : 'Failed to check for updates',
});
}
- };
+ }, []);
const downloadAndInstall = async () => {
if (!update || !isTauri()) return;
@@ -69,19 +72,40 @@ export function useAutoUpdater(checkOnMount = false) {
try {
setStatus((prev) => ({ ...prev, downloading: true, error: undefined }));
+ let downloadedBytes = 0;
+ let totalBytes = 0;
+
await update.downloadAndInstall((event) => {
switch (event.event) {
case 'Started':
- setStatus((prev) => ({ ...prev, downloading: true }));
+ totalBytes = event.data.contentLength || 0;
+ downloadedBytes = 0;
+ setStatus((prev) => ({
+ ...prev,
+ downloading: true,
+ totalBytes,
+ downloadedBytes: 0,
+ downloadProgress: 0
+ }));
break;
- case 'Progress':
- console.log(`Downloaded ${event.data.chunkLength} bytes`);
+ case 'Progress': {
+ downloadedBytes += event.data.chunkLength;
+ const progress = totalBytes > 0
+ ? Math.round((downloadedBytes / totalBytes) * 100)
+ : undefined;
+ setStatus((prev) => ({
+ ...prev,
+ downloadedBytes,
+ downloadProgress: progress
+ }));
break;
+ }
case 'Finished':
setStatus((prev) => ({
...prev,
downloading: false,
installing: true,
+ downloadProgress: 100
}));
break;
}
@@ -93,6 +117,9 @@ export function useAutoUpdater(checkOnMount = false) {
...prev,
downloading: false,
installing: false,
+ downloadProgress: undefined,
+ downloadedBytes: undefined,
+ totalBytes: undefined,
error: error instanceof Error ? error.message : 'Failed to install update',
}));
}
@@ -102,7 +129,7 @@ export function useAutoUpdater(checkOnMount = false) {
if (checkOnMount && isTauri()) {
checkForUpdates();
}
- }, [checkOnMount]);
+ }, [checkOnMount, checkForUpdates]);
return {
status,