- {status.downloadedBytes !== undefined &&
- status.totalBytes !== undefined &&
- status.totalBytes > 0 && (
-
-
-
-
Update Ready to Install
+ {status.downloading && (
+
+
+
+
+ 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
+
+ )}
+
+ )}
+
+ {status.readyToInstall && (
+
+
+
+
Update Ready to Install
+
+ Version {status.version} has been downloaded
+
+
+
- Version {status.version} has been downloaded
+ The app needs to restart to complete the installation. You can do this now or
+ later at your convenience.
+
+
+ Restart Now
+
-
-
- The app needs to restart to complete the installation. You can do this now or later at
- your convenience.
-
-
-
- Restart Now
-
-
- )}
+ )}
- {!status.available && !status.checking && !status.error && status.checking === false && (
-
- You're up to date
-
+ {!status.available && !status.checking && !status.error && (
+
+ You're up to date
+
+ )}
+ >
)}
diff --git a/app/src/components/TitleBarDragRegion.tsx b/app/src/components/TitleBarDragRegion.tsx
index 5da86ec4..4287ba13 100644
--- a/app/src/components/TitleBarDragRegion.tsx
+++ b/app/src/components/TitleBarDragRegion.tsx
@@ -1,8 +1,7 @@
+const isWindows = navigator.userAgent.includes('Windows');
+
export function TitleBarDragRegion() {
- return (
-
- );
+ if (isWindows) return null;
+
+ return
;
}
diff --git a/app/src/lib/constants/ui.ts b/app/src/lib/constants/ui.ts
index 41a46483..af495e84 100644
--- a/app/src/lib/constants/ui.ts
+++ b/app/src/lib/constants/ui.ts
@@ -2,11 +2,14 @@
* UI layout constants for safe area padding
*/
+const isWindows = typeof navigator !== 'undefined' && navigator.userAgent.includes('Windows');
+
/**
* Top safe area padding - height of the drag region bar
- * Corresponds to Tailwind's pt-12 (3rem / 48px)
+ * On macOS this accounts for the overlay titlebar (48px).
+ * On Windows the native title bar is outside the webview, so no padding is needed.
*/
-export const TOP_SAFE_AREA_PADDING = 'pt-12';
+export const TOP_SAFE_AREA_PADDING = isWindows ? 'pt-8' : 'pt-12';
/**
* Bottom safe area padding - height of the audio player
diff --git a/backend/main.py b/backend/main.py
index 487d35ab..9f1bce4a 100644
--- a/backend/main.py
+++ b/backend/main.py
@@ -263,7 +263,7 @@ async def health():
gpu_type=gpu_type,
vram_used_mb=vram_used,
backend_type=backend_type,
- backend_variant=os.environ.get("VOICEBOX_BACKEND_VARIANT", "cpu"),
+ backend_variant=os.environ.get("VOICEBOX_BACKEND_VARIANT", "cuda" if torch.cuda.is_available() else "cpu"),
)
diff --git a/justfile b/justfile
index 95ae87b4..b1a9a1df 100644
--- a/justfile
+++ b/justfile
@@ -66,6 +66,11 @@ setup-python:
}
Write-Host "Installing Python dependencies..."
& "{{ python }}" -m pip install --upgrade pip -q
+ $hasNvidia = $null -ne (Get-WmiObject Win32_VideoController | Where-Object { $_.Name -match 'NVIDIA' })
+ if ($hasNvidia) { \
+ Write-Host "NVIDIA GPU detected — installing PyTorch with CUDA support..."; \
+ & "{{ pip }}" install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126; \
+ }
& "{{ pip }}" install -r {{ backend_dir }}/requirements.txt
& "{{ pip }}" install --no-deps chatterbox-tts
& "{{ pip }}" install git+https://github.com/QwenLM/Qwen3-TTS.git
@@ -77,30 +82,37 @@ setup-js:
# ─── Development ──────────────────────────────────────────────────────
-# Start backend + frontend for development (two processes, one terminal)
+# Start backend (if not already running) + frontend for development
[unix]
dev: _ensure-venv _ensure-sidecar
#!/usr/bin/env bash
set -euo pipefail
- trap 'kill 0' EXIT
- echo "Starting backend on http://localhost:17493 ..."
- {{ venv_bin }}/uvicorn backend.main:app --reload --port 17493 &
- sleep 2
+ backend_pid=""
+ if curl -sf http://127.0.0.1:17493/health > /dev/null 2>&1; then
+ echo "Backend already running on http://localhost:17493"
+ else
+ echo "Starting backend on http://localhost:17493 ..."
+ {{ venv_bin }}/uvicorn backend.main:app --reload --port 17493 &
+ backend_pid=$!
+ sleep 2
+ fi
+
+ trap '[ -n "$backend_pid" ] && kill "$backend_pid" 2>/dev/null; wait' EXIT
echo "Starting Tauri desktop app..."
- cd {{ tauri_dir }} && bun run tauri dev &
-
- wait
+ cd {{ tauri_dir }} && bun run tauri dev
[windows]
dev: _ensure-venv _ensure-sidecar
- Write-Host "Starting backend on http://localhost:17493 ..."
- $backend = Start-Process -NoNewWindow -PassThru -FilePath "{{ python }}" -ArgumentList "-m", "uvicorn", "backend.main:app", "--reload", "--port", "17493"
- Start-Sleep -Seconds 2
+ $backendJob = $null
+ try { $null = Invoke-WebRequest -Uri "http://127.0.0.1:17493/health" -UseBasicParsing -TimeoutSec 2 -ErrorAction Stop; Write-Host "Backend already running on http://localhost:17493" } catch { \
+ Write-Host "Starting backend on http://localhost:17493 ..."; \
+ $backendJob = Start-Job -ScriptBlock { & "{{ python }}" -m uvicorn backend.main:app --reload --port 17493 } -WorkingDirectory (Get-Location); \
+ Start-Sleep -Seconds 2; \
+ }
Write-Host "Starting Tauri desktop app..."
- $frontend = Start-Process -NoNewWindow -PassThru -WorkingDirectory "{{ tauri_dir }}" -FilePath "bun" -ArgumentList "run", "tauri", "dev"
- try { Wait-Process -Id $backend.Id, $frontend.Id } finally { Stop-Process -Id $backend.Id, $frontend.Id -ErrorAction SilentlyContinue }
+ try { Set-Location "{{ tauri_dir }}"; bun run tauri dev } finally { if ($backendJob) { Stop-Job $backendJob -ErrorAction SilentlyContinue; Remove-Job $backendJob -Force -ErrorAction SilentlyContinue } }
# Start backend only
[unix]
@@ -115,25 +127,36 @@ dev-backend: _ensure-venv
dev-frontend: _ensure-sidecar
cd {{ tauri_dir }} && bun run tauri dev
-# Start backend + web app (no Tauri)
+# Start backend (if not already running) + web app (no Tauri)
[unix]
dev-web: _ensure-venv
#!/usr/bin/env bash
set -euo pipefail
- trap 'kill 0' EXIT
- {{ venv_bin }}/uvicorn backend.main:app --reload --port 17493 &
- sleep 2
- cd {{ web_dir }} && bun run dev &
- wait
+
+ backend_pid=""
+ if curl -sf http://127.0.0.1:17493/health > /dev/null 2>&1; then
+ echo "Backend already running on http://localhost:17493"
+ else
+ echo "Starting backend on http://localhost:17493 ..."
+ {{ venv_bin }}/uvicorn backend.main:app --reload --port 17493 &
+ backend_pid=$!
+ sleep 2
+ fi
+
+ trap '[ -n "$backend_pid" ] && kill "$backend_pid" 2>/dev/null; wait' EXIT
+
+ cd {{ web_dir }} && bun run dev
[windows]
dev-web: _ensure-venv
- Write-Host "Starting backend on http://localhost:17493 ..."
- $backend = Start-Process -NoNewWindow -PassThru -FilePath "{{ python }}" -ArgumentList "-m", "uvicorn", "backend.main:app", "--reload", "--port", "17493"
- Start-Sleep -Seconds 2
+ $backendJob = $null
+ try { $null = Invoke-WebRequest -Uri "http://127.0.0.1:17493/health" -UseBasicParsing -TimeoutSec 2 -ErrorAction Stop; Write-Host "Backend already running on http://localhost:17493" } catch { \
+ Write-Host "Starting backend on http://localhost:17493 ..."; \
+ $backendJob = Start-Job -ScriptBlock { & "{{ python }}" -m uvicorn backend.main:app --reload --port 17493 } -WorkingDirectory (Get-Location); \
+ Start-Sleep -Seconds 2; \
+ }
Write-Host "Starting web app..."
- $frontend = Start-Process -NoNewWindow -PassThru -WorkingDirectory "{{ web_dir }}" -FilePath "bun" -ArgumentList "run", "dev"
- try { Wait-Process -Id $backend.Id, $frontend.Id } finally { Stop-Process -Id $backend.Id, $frontend.Id -ErrorAction SilentlyContinue }
+ try { Set-Location "{{ web_dir }}"; bun run dev } finally { if ($backendJob) { Stop-Job $backendJob -ErrorAction SilentlyContinue; Remove-Job $backendJob -Force -ErrorAction SilentlyContinue } }
# Kill all dev processes
[unix]