From 3cae8534407060fa1b7765d175920eb453fac973 Mon Sep 17 00:00:00 2001
From: Palash Debnath <4178343+debpalash@users.noreply.github.com>
Date: Thu, 10 Sep 2026 06:46:09 -0700
Subject: [PATCH] =?UTF-8?q?feat(catalogue):=20one=20page,=20one=20axis=20?=
=?UTF-8?q?=E2=80=94=20setup=20summary=20over=20per-family=20engines=20and?=
=?UTF-8?q?=20weights?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The Model Catalogue put the same decision on two axes: an Engines pane with
TTS/ASR/LLM tabs and a Models pane with TTS/ASR/Dictation/Diarisation
sections, dictation shown in both, plus storage stats, the HF token and the
voice-preview toggle parked on the model list. Settings → Voice still carried
Engines and Models entries that only pointed back here.
Now the page reads top-down: a SetupSummary (speech, transcription,
dictation, language model — engine, device, one status word, Change), the
engine list for one family, and that family's downloadable weights under it
(TTS under TTS; offline ASR, streaming dictation and diarisation under ASR;
nothing for LLM, whose engines bring their own). One storage line points at
Settings → Storage.
- ModelStoreTab takes a `family` and scopes sections and the recommendation
preset to it (scopeReco); stats strip, HF-token toolbar and previews
panel removed from it.
- Settings: Engines/Models categories and CataloguePointer removed; models
directory → Storage, HF mirror → Network (both restart-flagged), voice
previews → Storage. "Manage models" in disk usage opens the catalogue.
- Store: openCatalogue takes a family (pane key tolerated, ignored);
pendingCatalogueTab gone.
- Engine matrix title is now the locale's plain "Engines".
- i18n: catalogue.* summary keys in all 21 locales; pane/pointer keys dropped.
- Docs: "Model Catalogue → Engines" is "Model Catalogue"; "→ Models" is
"→ Downloaded weights".
---
docs/DESKTOP_RELEASE.md | 2 +-
docs/downloading-models.md | 6 +-
docs/engines/README.md | 2 +-
docs/engines/audio-cpp.md | 6 +-
docs/engines/confucius4-tts.md | 2 +-
docs/engines/dots-tts.md | 6 +-
docs/engines/faster-whisper-isolated.md | 4 +-
docs/engines/faster-whisper.md | 2 +-
docs/engines/funasr.md | 2 +-
docs/engines/gpt-sovits.md | 2 +-
docs/engines/indextts.md | 4 +-
docs/engines/kittentts.md | 2 +-
docs/engines/mlx-audio.md | 4 +-
docs/engines/mlx-whisper.md | 2 +-
docs/engines/moonshine.md | 2 +-
docs/engines/moss-tts-nano.md | 2 +-
docs/engines/moss-tts-v15.md | 4 +-
docs/engines/nemo-parakeet.md | 2 +-
docs/engines/omnivoice-gguf.md | 4 +-
docs/engines/omnivoice-subprocess.md | 2 +-
docs/engines/omnivoice.md | 4 +-
docs/engines/openai-compatible-asr.md | 2 +-
docs/engines/parakeet-mlx.md | 4 +-
docs/engines/pockettts.md | 8 +-
docs/engines/pytorch-whisper.md | 2 +-
docs/engines/sherpa-onnx-asr.md | 4 +-
docs/engines/sherpa-onnx.md | 2 +-
docs/engines/supertonic3.md | 8 +-
docs/engines/voxcpm2.md | 2 +-
docs/engines/whisperx.md | 2 +-
docs/expressive-speech.md | 2 +-
docs/install/docker.md | 2 +-
docs/install/macos.md | 4 +-
docs/install/troubleshooting.md | 16 +-
docs/migration/real-time-voice-cloning.md | 2 +-
docs/performance.md | 6 +-
frontend/src/components/EngineQuickSwitch.jsx | 2 +-
frontend/src/components/Header.jsx | 4 +-
.../src/components/catalogue/SetupSummary.jsx | 236 +++++++++++++
.../catalogue/SetupSummary.test.jsx | 208 +++++++++++
.../components/settings/CataloguePointer.jsx | 49 ---
.../settings/CataloguePointer.test.jsx | 28 --
.../components/settings/LLMProvidersPanel.jsx | 2 +-
.../src/components/settings/ModelStoreTab.jsx | 330 ++++++------------
.../settings/SettingsSidebar.test.jsx | 8 +-
.../components/settings/StorageUsagePanel.jsx | 4 +-
.../components/settings/models/sections.js | 28 ++
.../settings/settingsCategories.jsx | 57 +--
.../settings/settingsCategories.test.jsx | 4 +-
frontend/src/i18n/locales/ar.json | 19 +-
frontend/src/i18n/locales/de.json | 19 +-
frontend/src/i18n/locales/en.json | 19 +-
frontend/src/i18n/locales/es.json | 19 +-
frontend/src/i18n/locales/fr.json | 19 +-
frontend/src/i18n/locales/hi.json | 19 +-
frontend/src/i18n/locales/id.json | 19 +-
frontend/src/i18n/locales/it.json | 19 +-
frontend/src/i18n/locales/ja.json | 19 +-
frontend/src/i18n/locales/ko.json | 19 +-
frontend/src/i18n/locales/nl.json | 19 +-
frontend/src/i18n/locales/pl.json | 19 +-
frontend/src/i18n/locales/pt.json | 19 +-
frontend/src/i18n/locales/ru.json | 19 +-
frontend/src/i18n/locales/sv.json | 19 +-
frontend/src/i18n/locales/th.json | 19 +-
frontend/src/i18n/locales/tr.json | 19 +-
frontend/src/i18n/locales/uk.json | 19 +-
frontend/src/i18n/locales/vi.json | 19 +-
frontend/src/i18n/locales/zh-CN.json | 19 +-
frontend/src/i18n/locales/zh-TW.json | 19 +-
frontend/src/pages/ModelCatalogue.jsx | 239 ++++++-------
frontend/src/pages/ModelCatalogue.test.jsx | 197 +++++------
frontend/src/pages/Settings.jsx | 30 +-
frontend/src/store/uiSlice.ts | 29 +-
.../test/EngineCompatibilityMatrix.test.jsx | 2 +-
frontend/src/test/modelStoreGrouping.test.jsx | 54 ++-
.../src/test/modelStoreTokenPath.test.jsx | 88 -----
77 files changed, 1184 insertions(+), 946 deletions(-)
create mode 100644 frontend/src/components/catalogue/SetupSummary.jsx
create mode 100644 frontend/src/components/catalogue/SetupSummary.test.jsx
delete mode 100644 frontend/src/components/settings/CataloguePointer.jsx
delete mode 100644 frontend/src/components/settings/CataloguePointer.test.jsx
delete mode 100644 frontend/src/test/modelStoreTokenPath.test.jsx
diff --git a/docs/DESKTOP_RELEASE.md b/docs/DESKTOP_RELEASE.md
index 9268145d..2ceaa4c6 100644
--- a/docs/DESKTOP_RELEASE.md
+++ b/docs/DESKTOP_RELEASE.md
@@ -69,7 +69,7 @@ We exclude every `nvidia.*` wheel from the Apple Silicon build (saves ~2 GB) and
For us:
- **Base DMG ships MPS + MLX path only.** Excludes `nvidia.*`, `triton`, `flash-attn`, anything CUDA-specific in the spec.
-- **Optional pack: VoxCPM2** (requires CUDA). Not installed by default. Model Catalogue → Engines → "Install VoxCPM2" triggers download from our `voxcpm2-cu128-v1.tar.gz` release asset.
+- **Optional pack: VoxCPM2** (requires CUDA). Not installed by default. Model Catalogue → "Install VoxCPM2" triggers download from our `voxcpm2-cu128-v1.tar.gz` release asset.
- **Optional pack: pyannote** (HF-token gated). Default off. Settings → Speaker diarisation → "Enable" prompts for HF token, downloads + installs.
- **Optional pack: MOSS-TTS-Nano.** Same pattern.
diff --git a/docs/downloading-models.md b/docs/downloading-models.md
index e641aa03..8aaac6fb 100644
--- a/docs/downloading-models.md
+++ b/docs/downloading-models.md
@@ -60,7 +60,7 @@ State is reported at **Settings → About** / `GET /system/info`:
- `fast_download.xet_installed` — `hf_xet` present (true)
- `fast_download.xet_active` — whether Xet actually drives downloads (false by
default, because of `HF_HUB_DISABLE_XET`)
-- the **⚡ fast download** badge in **Model Catalogue → Models** appears only when Xet
+- the **⚡ fast download** badge in **Model Catalogue → Downloaded weights** appears only when Xet
is *active*.
The backend logs one line at startup, e.g.
@@ -148,7 +148,7 @@ failed download at once. Caveats:
## Cancelling a download
-**Model Catalogue → Models** lets you cancel an in-flight install. Cancellation stops
+**Model Catalogue → Downloaded weights** lets you cancel an in-flight install. Cancellation stops
further retries and clears the failure cooldown so you can restart
immediately. A file that's already streaming finishes first — cancellation
takes effect at the next retry boundary.
@@ -162,6 +162,6 @@ takes effect at the next retry boundary.
High-performance mode only helps if RAM and bandwidth are plentiful.
- **"download finished but no model weights were found"** — the download was
interrupted and left a partial snapshot. Delete the model in
- **Model Catalogue → Models** and install it again.
+ **Model Catalogue → Downloaded weights** and install it again.
- **Out of disk** — model sizes are shown in the catalog; free space or change
the cache location with `HF_HOME` / `HF_HUB_CACHE`.
diff --git a/docs/engines/README.md b/docs/engines/README.md
index e73e7b8d..37e67f81 100644
--- a/docs/engines/README.md
+++ b/docs/engines/README.md
@@ -1,7 +1,7 @@
# Engine guides
One page per engine: what it's for, what it needs, how to enable it, and its
-quirks. Select engines in **Model Catalogue → Engines** (or quick-switch with
+quirks. Select engines in **Model Catalogue** (or quick-switch with
Ctrl/Cmd+E), or pin one with
`OMNIVOICE_TTS_BACKEND` / `OMNIVOICE_ASR_BACKEND`.
diff --git a/docs/engines/audio-cpp.md b/docs/engines/audio-cpp.md
index de1f5d8e..32934937 100644
--- a/docs/engines/audio-cpp.md
+++ b/docs/engines/audio-cpp.md
@@ -80,11 +80,11 @@ instead of that dedicated-VRAM floor.
```
Alternatively set `OMNIVOICE_AUDIOCPP_DIR` to the directory containing it.
-3. Restart VoiceStudio, open **Model Catalogue → Models**, find
+3. Restart VoiceStudio, open **Model Catalogue → Downloaded weights**, find
**Breeze-TTS-2 Q8_0 for audio.cpp**, review its research/non-commercial
license note, and click **Install**. Generation never starts this ~4.73 GiB
download automatically.
-4. Pick `audiocpp` in **Model Catalogue → Engines**. The server starts
+4. Pick `audiocpp` in **Model Catalogue**. The server starts
lazily on first generate (`server.json` + `server.log` live under the app
data `audiocpp/` directory).
@@ -131,7 +131,7 @@ The managed loopback port may be taken. Check `server.log` next to
### `Breeze-TTS-2 ... not installed` or `package ... not completely installed`
-Install the model from **Model Catalogue → Models**. If an interrupted install
+Install the model from **Model Catalogue → Downloaded weights**. If an interrupted install
left it incomplete, use **Reinstall** there. If the error persists after a
complete reinstall, file an issue with the package listing.
diff --git a/docs/engines/confucius4-tts.md b/docs/engines/confucius4-tts.md
index d478b4b1..9b20875f 100644
--- a/docs/engines/confucius4-tts.md
+++ b/docs/engines/confucius4-tts.md
@@ -56,7 +56,7 @@ Then point VoiceStudio at the clone and restart:
- **macOS/Linux:** `export OMNIVOICE_CONFUCIUS4_TTS_DIR=/path/to/Confucius4-TTS`
- **Windows (PowerShell):** `[Environment]::SetEnvironmentVariable("OMNIVOICE_CONFUCIUS4_TTS_DIR","C:\path\to\Confucius4-TTS","User")`
-Select **Confucius4-TTS** in Model Catalogue → Engines. The first synthesize triggers
+Select **Confucius4-TTS** in Model Catalogue. The first synthesize triggers
the weight downloads above, then generates.
### Optional overrides
diff --git a/docs/engines/dots-tts.md b/docs/engines/dots-tts.md
index ae57cb5b..8dc5d131 100644
--- a/docs/engines/dots-tts.md
+++ b/docs/engines/dots-tts.md
@@ -11,14 +11,14 @@ pins `transformers>=5.3` — the same isolation primitive used by
[IndexTTS-2](indextts.md) and [MOSS-TTS-v1.5](moss-tts-v15.md).
> **Opt-in, and never a default.** dots.tts is selected explicitly in
-> **Model Catalogue → Engines** (or `OMNIVOICE_TTS_BACKEND=dots-tts`). It is not
+> **Model Catalogue** (or `OMNIVOICE_TTS_BACKEND=dots-tts`). It is not
> part of the default install.
## Platform support
- **Linux / macOS only.** dots.tts's upstream package declares Linux and
macOS classifiers and has **no Windows install path**. On Windows the
- engine reports itself unavailable in **Model Catalogue → Engines** with a clear
+ engine reports itself unavailable in **Model Catalogue** with a clear
reason — run VoiceStudio under WSL2 or use a Linux/macOS host.
- **No MPS.** Upstream device selection is CUDA-or-CPU with no Metal branch,
so on Apple Silicon the official package runs on **CPU** (slow but
@@ -60,7 +60,7 @@ dots.tts is **not** bundled (large checkpoint + conflicting `transformers`).
source ~/.zshrc
```
-5. Restart VoiceStudio. dots.tts appears in **Model Catalogue → Engines** with
+5. Restart VoiceStudio. dots.tts appears in **Model Catalogue** with
`available: true` and `isolation_mode: subprocess`.
## Venv resolution order
diff --git a/docs/engines/faster-whisper-isolated.md b/docs/engines/faster-whisper-isolated.md
index e48d506d..44e9169c 100644
--- a/docs/engines/faster-whisper-isolated.md
+++ b/docs/engines/faster-whisper-isolated.md
@@ -13,7 +13,7 @@ only the process boundary is new.
## Selecting it
-- **Model Catalogue → Engines**, ASR tab → **Use** on the crash-isolated row, or
+- **Model Catalogue**, ASR tab → **Use** on the crash-isolated row, or
- pin it with `OMNIVOICE_ASR_BACKEND=faster-whisper-isolated`.
It is never picked by auto-detect — it's an explicit opt-in escape hatch.
@@ -56,6 +56,6 @@ Weights download on first load — see
"sidecar crashed" error and the backend stays up — retry to respawn.
- **cuDNN 8 is still required on CUDA** — same CTranslate2 requirement as the
in-process engine. It's checked up front so a missing cuDNN 8 shows as
- "unavailable" in Model Catalogue → Engines instead of a sidecar that
+ "unavailable" in Model Catalogue instead of a sidecar that
silently fails every transcribe
([#1371](https://github.com/debpalash/VoiceStudio/issues/1371)).
diff --git a/docs/engines/faster-whisper.md b/docs/engines/faster-whisper.md
index 35023d5f..e5cf30c8 100644
--- a/docs/engines/faster-whisper.md
+++ b/docs/engines/faster-whisper.md
@@ -7,7 +7,7 @@ fallback on non-Apple machines.
## Selecting it
-- **Model Catalogue → Engines**, ASR tab → **Use** on the Faster-Whisper row, or
+- **Model Catalogue**, ASR tab → **Use** on the Faster-Whisper row, or
- pin it with `OMNIVOICE_ASR_BACKEND=faster-whisper`.
Auto-detect only picks it when [whisperx](whisperx.md) is unavailable.
diff --git a/docs/engines/funasr.md b/docs/engines/funasr.md
index 89a60c10..13f8c4bb 100644
--- a/docs/engines/funasr.md
+++ b/docs/engines/funasr.md
@@ -10,7 +10,7 @@ WhisperX remains the cross-platform default.
## Selecting it
- Install it into the app venv: `uv pip install funasr`.
-- Then **Model Catalogue → Engines**, ASR tab → **Use** on the FunASR row, or
+- Then **Model Catalogue**, ASR tab → **Use** on the FunASR row, or
`OMNIVOICE_ASR_BACKEND=funasr`.
Auto-detect never picks it; it's an explicit opt-in.
diff --git a/docs/engines/gpt-sovits.md b/docs/engines/gpt-sovits.md
index 0aa1f36d..72df3139 100644
--- a/docs/engines/gpt-sovits.md
+++ b/docs/engines/gpt-sovits.md
@@ -24,7 +24,7 @@ HTTP.
python api_v2.py -a 127.0.0.1 -p 9880 -c GPT_SoVITS/configs/tts_infer.yaml
```
-2. Select the engine via **Model Catalogue → Engines** or
+2. Select the engine via **Model Catalogue** or
`OMNIVOICE_TTS_BACKEND=gpt-sovits`.
VoiceStudio marks the engine available only when the server responds
diff --git a/docs/engines/indextts.md b/docs/engines/indextts.md
index 34ca0fe3..07c2845c 100644
--- a/docs/engines/indextts.md
+++ b/docs/engines/indextts.md
@@ -14,7 +14,7 @@ runtime. Existing user-managed IndexTTS-2 environments remain supported.
IndexTTS 2.5 is not bundled because its source environment and model weights
require substantial disk space.
-1. Open **Model Catalogue → Engines**.
+1. Open **Model Catalogue**.
2. Expand **IndexTTS 2.5** and select **Install**.
3. Keep VoiceStudio open while source, dependencies, and weights download.
@@ -97,7 +97,7 @@ IndexTTS-2 installations continue receiving their `target_tokens` control.
### Engine unavailable
-Use **Model Catalogue → Engines → IndexTTS 2.5 → Install**. For a manual install,
+Use **Model Catalogue → IndexTTS 2.5 → Install**. For a manual install,
confirm that the configured directory contains:
```text
diff --git a/docs/engines/kittentts.md b/docs/engines/kittentts.md
index 943a30fe..1e5631a9 100644
--- a/docs/engines/kittentts.md
+++ b/docs/engines/kittentts.md
@@ -20,7 +20,7 @@ only — but a much faster and much smaller install.
pip install kittentts
```
-Then select the engine via **Model Catalogue → Engines** or
+Then select the engine via **Model Catalogue** or
`OMNIVOICE_TTS_BACKEND=kittentts`.
## Voices
diff --git a/docs/engines/mlx-audio.md b/docs/engines/mlx-audio.md
index 07a40e97..a5b4a455 100644
--- a/docs/engines/mlx-audio.md
+++ b/docs/engines/mlx-audio.md
@@ -19,7 +19,7 @@ platforms never reports as available
pip install mlx-audio
```
-Then select the engine via **Model Catalogue → Engines** or
+Then select the engine via **Model Catalogue** or
`OMNIVOICE_TTS_BACKEND=mlx-audio`.
## Model selection
@@ -36,7 +36,7 @@ One backend hosts many models. The curated set:
| `melotts` | `mlx-community/MeloTTS-English-v3-MLX` | lightweight VITS |
| `outetts` | `mlx-community/Llama-OuteTTS-1.0-1B-4bit` | LM-based |
-Pick a model in the **Model Catalogue → Engines** curated picker
+Pick a model in the **Model Catalogue** curated picker
([#981](https://github.com/debpalash/VoiceStudio/issues/981)) or set
`OMNIVOICE_MLX_AUDIO_MODEL` to either a curated key (`kokoro`) or any full
HF repo id. The env var overrides the persisted UI choice.
diff --git a/docs/engines/mlx-whisper.md b/docs/engines/mlx-whisper.md
index e4fddd92..eb8abcde 100644
--- a/docs/engines/mlx-whisper.md
+++ b/docs/engines/mlx-whisper.md
@@ -11,7 +11,7 @@ on every Apple Silicon machine
## Selecting it
- Nothing to do on Apple Silicon — auto-detect prefers it there.
-- Or explicitly: **Model Catalogue → Engines**, ASR tab → **Use**, or
+- Or explicitly: **Model Catalogue**, ASR tab → **Use**, or
`OMNIVOICE_ASR_BACKEND=mlx-whisper`.
## Best at
diff --git a/docs/engines/moonshine.md b/docs/engines/moonshine.md
index 02d85f59..0f02754f 100644
--- a/docs/engines/moonshine.md
+++ b/docs/engines/moonshine.md
@@ -11,7 +11,7 @@ where even int8 whisper-large is too slow.
- Install one of the runtimes into the app venv:
`uv pip install moonshine-onnx` (lighter, tried first) or
`moonshine-voice`.
-- Then **Model Catalogue → Engines**, ASR tab → **Use** on the Moonshine row,
+- Then **Model Catalogue**, ASR tab → **Use** on the Moonshine row,
or `OMNIVOICE_ASR_BACKEND=moonshine`.
Auto-detect never picks it; it's an explicit opt-in.
diff --git a/docs/engines/moss-tts-nano.md b/docs/engines/moss-tts-nano.md
index 286891a5..3c3f884b 100644
--- a/docs/engines/moss-tts-nano.md
+++ b/docs/engines/moss-tts-nano.md
@@ -25,7 +25,7 @@ cd MOSS-TTS-Nano
uv pip install -e .
```
-Then select the engine via **Model Catalogue → Engines** or
+Then select the engine via **Model Catalogue** or
`OMNIVOICE_TTS_BACKEND=moss-tts-nano`.
## Model selection
diff --git a/docs/engines/moss-tts-v15.md b/docs/engines/moss-tts-v15.md
index 63666db6..db566ffd 100644
--- a/docs/engines/moss-tts-v15.md
+++ b/docs/engines/moss-tts-v15.md
@@ -13,7 +13,7 @@ interpreter, so MOSS runs behind
`backend/services/subprocess_backend.py::SubprocessBackend`.
> **Opt-in, and never a default.** MOSS-TTS-v1.5 is selected explicitly in
-> **Model Catalogue → Engines** (or `OMNIVOICE_TTS_BACKEND=moss-tts-v15`). It is
+> **Model Catalogue** (or `OMNIVOICE_TTS_BACKEND=moss-tts-v15`). It is
> not part of the default install and does not change VoiceStudio's
> out-of-the-box behaviour on any platform.
@@ -75,7 +75,7 @@ into an isolated venv on demand.
[Environment]::SetEnvironmentVariable("OMNIVOICE_MOSS_TTS_V15_DIR","$env:USERPROFILE\code\MOSS-TTS","User")
```
-5. Restart VoiceStudio. MOSS-TTS-v1.5 appears in **Model Catalogue → Engines** with
+5. Restart VoiceStudio. MOSS-TTS-v1.5 appears in **Model Catalogue** with
`available: true` and `isolation_mode: subprocess`.
## Venv resolution order
diff --git a/docs/engines/nemo-parakeet.md b/docs/engines/nemo-parakeet.md
index 0277b18e..0d7cbe99 100644
--- a/docs/engines/nemo-parakeet.md
+++ b/docs/engines/nemo-parakeet.md
@@ -29,7 +29,7 @@ instead — same model family, no NeMo dependency:
Only meaningful if you've set up `nemo_toolkit[asr]` in a **separate,
dedicated Python environment** that runs the backend:
-- **Model Catalogue → Engines**, ASR tab → **Use** on the Parakeet TDT row, or
+- **Model Catalogue**, ASR tab → **Use** on the Parakeet TDT row, or
- `OMNIVOICE_ASR_BACKEND=nemo-parakeet`.
Auto-detect never picks it; it's an explicit opt-in.
diff --git a/docs/engines/omnivoice-gguf.md b/docs/engines/omnivoice-gguf.md
index dd551df3..bf5ce7b8 100644
--- a/docs/engines/omnivoice-gguf.md
+++ b/docs/engines/omnivoice-gguf.md
@@ -32,10 +32,10 @@ against the same table (an F32 reference quant, ~3.2 GB, is override-only).
## Setup
Nothing to install: installer and CI builds bundle the binary for your
-platform. Select the engine via **Model Catalogue → Engines** or
+platform. Select the engine via **Model Catalogue** or
`OMNIVOICE_TTS_BACKEND=omnivoice-gguf`. The quant weights download on first
use (see [downloading-models.md](../downloading-models.md)) — install them
-ahead of time from **Model Catalogue → Models** if you want the first
+ahead of time from **Model Catalogue → Downloaded weights** if you want the first
generation to be quick; a long first render is the download, not a hang.
**Source checkouts:** the repo ships zero-byte placeholders in `bin/` — real
diff --git a/docs/engines/omnivoice-subprocess.md b/docs/engines/omnivoice-subprocess.md
index e08b28a3..0fb4fcb0 100644
--- a/docs/engines/omnivoice-subprocess.md
+++ b/docs/engines/omnivoice-subprocess.md
@@ -32,7 +32,7 @@ lower call overhead.
## Selecting it
-- **Model Catalogue → Engines** on CUDA, ROCm, or CPU, or
+- **Model Catalogue** on CUDA, ROCm, or CPU, or
- `OMNIVOICE_TTS_BACKEND=omnivoice-subprocess`
The explicit engine is opt-in on CUDA, ROCm, and CPU. On Apple Silicon it is
diff --git a/docs/engines/omnivoice.md b/docs/engines/omnivoice.md
index 19546cfa..8e5bee58 100644
--- a/docs/engines/omnivoice.md
+++ b/docs/engines/omnivoice.md
@@ -39,7 +39,7 @@ quantized native binary with a much smaller memory footprint.
OmniVoice is the default, so normally there is nothing to do. If you switched
away and want it back:
-- **Model Catalogue → Engines**, or
+- **Model Catalogue**, or
- set `OMNIVOICE_TTS_BACKEND=omnivoice`.
The env var overrides the persisted UI choice.
@@ -103,7 +103,7 @@ The env var overrides the persisted UI choice.
above — switch to OmniVoice GGUF or close other GPU apps.
- First generation is slow: the first call downloads multi-GB weights. To
keep the first render quick, install the model ahead of time from
- **Model Catalogue → Models** — a long first generate is almost always the
+ **Model Catalogue → Downloaded weights** — a long first generate is almost always the
download, not a hang.
- General install issues: [install/troubleshooting.md](../install/troubleshooting.md).
diff --git a/docs/engines/openai-compatible-asr.md b/docs/engines/openai-compatible-asr.md
index decd3cbe..ee63190e 100644
--- a/docs/engines/openai-compatible-asr.md
+++ b/docs/engines/openai-compatible-asr.md
@@ -9,7 +9,7 @@ model installation and compute.
## Setup
-Everything lives on one screen — **Model Catalogue → Engines**, **ASR** tab:
+Everything lives on one screen — **Model Catalogue**, **ASR** tab:
1. The **OpenAI-compatible (remote server)** row shows as unavailable until
a server is configured. The config panel appears **below the engine
diff --git a/docs/engines/parakeet-mlx.md b/docs/engines/parakeet-mlx.md
index 22e43a71..fca24e92 100644
--- a/docs/engines/parakeet-mlx.md
+++ b/docs/engines/parakeet-mlx.md
@@ -12,10 +12,10 @@ Apple Silicon source installs since 0.3.22**.
## Selecting it
-- **Model Catalogue → Engines**, ASR tab → **Use** on the Parakeet TDT v3
+- **Model Catalogue**, ASR tab → **Use** on the Parakeet TDT v3
(MLX) row, or `OMNIVOICE_ASR_BACKEND=parakeet-mlx`.
- **Dictation prefers it automatically**: once the model weights are
- installed (Model Catalogue → Models — the auto-pick never triggers a
+ installed (Model Catalogue → Downloaded weights — the auto-pick never triggers a
download), live dictation/capture uses it whenever your system language is
one of the 25 covered European languages. Other languages keep the
multilingual Whisper engine, so dictation coverage never regresses.
diff --git a/docs/engines/pockettts.md b/docs/engines/pockettts.md
index 73f54e02..681b87e0 100644
--- a/docs/engines/pockettts.md
+++ b/docs/engines/pockettts.md
@@ -24,18 +24,18 @@ for this model.
uv sync --extra pockettts
```
- (Or enable it from **Model Catalogue → Engines**.)
+ (Or enable it from **Model Catalogue**.)
2. **Accept the license in-app**
([#1306](https://github.com/debpalash/VoiceStudio/issues/1306)). The code
is MIT and the weights are CC-BY-4.0, but the weights are **gated on
HuggingFace** behind an access agreement with an acceptable-use clause.
VoiceStudio surfaces this before first use: the engine stays unavailable
- until you review and accept in **Model Catalogue → Engines → PocketTTS**.
+ until you review and accept in **Model Catalogue → PocketTTS**.
You also need HuggingFace access to the gated repo (see
[downloading-models.md](../downloading-models.md) for token setup).
-3. Select the engine via **Model Catalogue → Engines** or
+3. Select the engine via **Model Catalogue** or
`OMNIVOICE_TTS_BACKEND=pockettts`.
## Platform notes
@@ -77,7 +77,7 @@ for this model.
## Troubleshooting
- "pocket_tts package not installed": run the `uv sync` above.
-- "license not accepted": open **Model Catalogue → Engines → PocketTTS**
+- "license not accepted": open **Model Catalogue → PocketTTS**
and review/accept.
- Timeouts on a slow connection: raise
`OMNIVOICE_POCKETTTS_RECV_TIMEOUT_S` for the first (download-heavy) run.
diff --git a/docs/engines/pytorch-whisper.md b/docs/engines/pytorch-whisper.md
index 8029f074..815a327e 100644
--- a/docs/engines/pytorch-whisper.md
+++ b/docs/engines/pytorch-whisper.md
@@ -9,7 +9,7 @@ genuinely uses **AMD ROCm** GPUs, so auto-detect picks it on ROCm hosts
## Selecting it
-- **Model Catalogue → Engines**, ASR tab → **Use** on the PyTorch Whisper
+- **Model Catalogue**, ASR tab → **Use** on the PyTorch Whisper
row, or `OMNIVOICE_ASR_BACKEND=pytorch-whisper`.
- `OMNIVOICE_ASR_BACKEND=omnivoice` is accepted as a compatibility alias and
selects this same PyTorch-native ASR path on ROCm hosts.
diff --git a/docs/engines/sherpa-onnx-asr.md b/docs/engines/sherpa-onnx-asr.md
index 95a131f6..fbc74dab 100644
--- a/docs/engines/sherpa-onnx-asr.md
+++ b/docs/engines/sherpa-onnx-asr.md
@@ -10,8 +10,8 @@ partials either way.
## Selecting it
- Ensure `sherpa-onnx` is installed (`uv add sherpa-onnx` on source installs).
-- Pick a dictation model in the app (Model Catalogue → Models lists the
- selectable set below), or **Model Catalogue → Engines**, ASR tab → **Use**, or
+- Pick a dictation model in the app (Model Catalogue → Downloaded weights lists the
+ selectable set below), or **Model Catalogue**, ASR tab → **Use**, or
pin `OMNIVOICE_ASR_BACKEND=sherpa-onnx-asr`.
- `OMNIVOICE_SHERPA_ASR_MODEL` selects the model — default
`sherpa-whisper-tiny`.
diff --git a/docs/engines/sherpa-onnx.md b/docs/engines/sherpa-onnx.md
index 6cd09aab..5313b1d4 100644
--- a/docs/engines/sherpa-onnx.md
+++ b/docs/engines/sherpa-onnx.md
@@ -30,7 +30,7 @@ TTS model directory.
export OMNIVOICE_SHERPA_MODEL=/path/to/model-dir
```
-4. Select the engine via **Model Catalogue → Engines** or
+4. Select the engine via **Model Catalogue** or
`OMNIVOICE_TTS_BACKEND=sherpa-onnx`.
The directory must contain `model.onnx` and `tokens.txt`. Sherpa-ONNX ships
diff --git a/docs/engines/supertonic3.md b/docs/engines/supertonic3.md
index f1db1744..27f0a3c9 100644
--- a/docs/engines/supertonic3.md
+++ b/docs/engines/supertonic3.md
@@ -19,16 +19,16 @@ crashes and cold init never block the rest of VoiceStudio.
uv sync --extra supertonic
```
- (Or enable it from **Model Catalogue → Engines**, which installs the
+ (Or enable it from **Model Catalogue**, which installs the
pinned `supertonic` wheel for you.)
2. **Accept the license in-app.** First use is gated behind an explicit
acceptance dialog: the inference SDK is MIT, but the model weights are
**OpenRAIL-M**, which carries use restrictions. The engine stays
- unavailable until you review and accept in **Model Catalogue → Engines →
+ unavailable until you review and accept in **Model Catalogue →
Supertonic-3**.
-3. Select the engine via **Model Catalogue → Engines** or
+3. Select the engine via **Model Catalogue** or
`OMNIVOICE_TTS_BACKEND=supertonic3`.
The first synthesis cold-downloads ~400 MB of model weights, pinned to an
@@ -66,7 +66,7 @@ log line.
- "supertonic package not installed": run the `uv sync` above or enable
from the Model Catalogue.
-- "license not accepted": open **Model Catalogue → Engines → Supertonic-3**
+- "license not accepted": open **Model Catalogue → Supertonic-3**
and accept.
- Other issues: [install/troubleshooting.md](../install/troubleshooting.md).
diff --git a/docs/engines/voxcpm2.md b/docs/engines/voxcpm2.md
index 556c9b5d..0bea7920 100644
--- a/docs/engines/voxcpm2.md
+++ b/docs/engines/voxcpm2.md
@@ -31,7 +31,7 @@ pip install "voxcpm>=2.0.3"
That is a version **floor**, not a pin — an older install still works, but
the engine logs an upgrade hint at load time. Then select the engine via
-**Model Catalogue → Engines** or `OMNIVOICE_TTS_BACKEND=voxcpm2`.
+**Model Catalogue** or `OMNIVOICE_TTS_BACKEND=voxcpm2`.
## Model selection
diff --git a/docs/engines/whisperx.md b/docs/engines/whisperx.md
index 95c7a628..19474540 100644
--- a/docs/engines/whisperx.md
+++ b/docs/engines/whisperx.md
@@ -8,7 +8,7 @@ prefers it wherever CTranslate2 can use the GPU.
## Selecting it
-- **Model Catalogue → Engines**, ASR tab → **Use** on the WhisperX row, or
+- **Model Catalogue**, ASR tab → **Use** on the WhisperX row, or
- pin it with `OMNIVOICE_ASR_BACKEND=whisperx` (the env var always wins over
the Settings pick; with neither set, auto-detect chooses per-hardware).
diff --git a/docs/expressive-speech.md b/docs/expressive-speech.md
index f2288d89..32f43657 100644
--- a/docs/expressive-speech.md
+++ b/docs/expressive-speech.md
@@ -129,7 +129,7 @@ the default engine's taxonomy, so free-text instruct currently needs the API
Setup: clone + install [CosyVoice](https://github.com/FunAudioLLM/CosyVoice)
(non-trivial: `git clone --recursive`, its requirements, SoX), then set
`OMNIVOICE_COSYVOICE_MODEL` to the model directory and select it in
-Model Catalogue → Engines. CUDA or CPU; MPS is unverified upstream.
+Model Catalogue. CUDA or CPU; MPS is unverified upstream.
### VoxCPM2 (opt-in)
diff --git a/docs/install/docker.md b/docs/install/docker.md
index 1f5fe554..2ee4e9d4 100644
--- a/docs/install/docker.md
+++ b/docs/install/docker.md
@@ -215,7 +215,7 @@ ROCm container `omnivoice-studio-rocm` (CPU: `omnivoice-studio`, NVIDIA:
name means torch can see the GPU.) That check alone isn't proof the app is
using it: **Settings → Performance & Device** shows the device VoiceStudio
actually resolved.
-**Model Catalogue → Engines** should report both `omnivoice` and
+**Model Catalogue** should report both `omnivoice` and
`omnivoice-subprocess` as accelerated on ROCm, rather than a CPU-fallback
warning.
If it reads `cpu` while the command above prints `True`, the backend log line
diff --git a/docs/install/macos.md b/docs/install/macos.md
index 5be288d0..fc00f950 100644
--- a/docs/install/macos.md
+++ b/docs/install/macos.md
@@ -157,7 +157,7 @@ without the quarantine step.
- **Apple Silicon (M-series):** VoiceStudio automatically picks the `mlx-whisper`
and `mlx-audio` backends where available — these use the Apple Neural Engine
and Metal Performance Shaders for ~2× the throughput of the CPU path.
- Installing the **Parakeet TDT v3 (MLX)** model from **Model Catalogue → Models**
+ Installing the **Parakeet TDT v3 (MLX)** model from **Model Catalogue → Downloaded weights**
additionally makes dictation/capture prefer the `parakeet-mlx` engine
(25 European languages, word timestamps, ~2 GB unified memory) — it is never
downloaded without that explicit install, and it is only auto-preferred when
@@ -170,7 +170,7 @@ without the quarantine step.
works only when pointed at a remote backend (**Settings → Sharing → Remote
backend**).
-The picker in **Model Catalogue → Engines** shows which backend is active.
+The picker in **Model Catalogue** shows which backend is active.
## Hugging Face token (optional but recommended)
diff --git a/docs/install/troubleshooting.md b/docs/install/troubleshooting.md
index c6aec3dd..29dab25d 100644
--- a/docs/install/troubleshooting.md
+++ b/docs/install/troubleshooting.md
@@ -89,7 +89,7 @@ uv pip install --reinstall transformers
```
Or, as a quick workaround, switch ASR to **faster-whisper** in
-**Model Catalogue → Models**. If it recurs, add the backend **`.venv`** to your
+**Model Catalogue → Downloaded weights**. If it recurs, add the backend **`.venv`** to your
antivirus exclusions (see §1). Newer builds classify this error and show the
reinstall hint directly instead of a bare path + "try restarting".
@@ -141,7 +141,7 @@ uv pip install --reinstall pytorch-lightning
If it recurs, add the backend **`.venv`** to your antivirus exclusions (see
§1). Since this fix landed the app also degrades gracefully: WhisperX is
-marked unavailable (Model Catalogue → Engines shows why, with this repair command)
+marked unavailable (Model Catalogue shows why, with this repair command)
and dubbing automatically falls through to **faster-whisper** instead of
failing outright.
@@ -205,7 +205,7 @@ itself does not grant access.
license and prohibited-use conditions, share the requested contact details,
and accept the conditions.
2. Open **Settings → API Keys** and save a read token from that same account.
-3. Open **Model Catalogue → Engines**, review and accept the PocketTTS terms locally,
+3. Open **Model Catalogue**, review and accept the PocketTTS terms locally,
then retry. VoiceStudio stores this acknowledgement only on your machine.
## 3. Gatekeeper quarantine on macOS
@@ -408,7 +408,7 @@ Intel-Mac wheels, so this entry only applies to historical installs (see
## 10. Windows: `Could not locate cudnn_ops_infer64_8.dll` during transcription
**Symptom:** on Windows + NVIDIA, transcription/dubbing fails and the backend
-log shows `Could not locate cudnn_ops_infer64_8.dll`. Model Catalogue → Models shows
+log shows `Could not locate cudnn_ops_infer64_8.dll`. Model Catalogue → Downloaded weights shows
WhisperX or faster-whisper selected.
On builds before this was fixed, the failure looked much worse than a failed
@@ -444,7 +444,7 @@ uv pip install --no-deps --python .venv\Scripts\python.exe --target .venv\Lib\si
(On Linux the target is `.venv/lib/pythonX.Y/site-packages/cudnn8_compat`.)
Or sidestep cuDNN 8 entirely: switch the ASR backend to **PyTorch Whisper** in
-**Model Catalogue → Models**. It runs on PyTorch's own stack (cuDNN 9, bundled with
+**Model Catalogue → Downloaded weights**. It runs on PyTorch's own stack (cuDNN 9, bundled with
torch) and needs no cuDNN-8 DLL — it loads its Whisper pipeline on demand (no
extra env var).
@@ -600,12 +600,12 @@ did was `generate:start (audio)`, a dub, or a dictation.
**Fix — reduce ASR load (any one of these):**
-1. **Pick a smaller ASR model / engine** in **Model Catalogue → Models** — e.g.
+1. **Pick a smaller ASR model / engine** in **Model Catalogue → Downloaded weights** — e.g.
faster-whisper **medium** or **small**, instead of large-v3. Biggest win on
low-VRAM GPUs.
2. **Free VRAM**: **Flush the TTS model** before dubbing so ASR isn't competing
for memory (top toolbar → Flush → "Unload all + flush", or per-model from
- Model Catalogue → Models — see [Flush caches / Unload resident model](../performance.md#flush-caches--unload-resident-model)
+ Model Catalogue → Downloaded weights — see [Flush caches / Unload resident model](../performance.md#flush-caches--unload-resident-model)
for exactly what it frees and the API equivalents for scripts), or
3. **Run ASR on CPU** (slower but reliable) if your GPU is small.
4. **Test with a 10-second clip** first — if that returns quickly, it confirms a
@@ -675,7 +675,7 @@ hammering — on a 1-worker machine, concurrent requests serialize by design.
recovering the underlying hang — the wedged thread keeps its VRAM until the app
exits. The error message will then recommend switching the ASR engine to
**Faster-Whisper (crash-isolated subprocess)** (`faster-whisper-isolated`) in
-**Model Catalogue → Engines**: it runs transcription in a separate process that can be
+**Model Catalogue**: it runs transcription in a separate process that can be
force-killed to reclaim a hung transcribe *and* its VRAM, at a small per-call
overhead. It reuses your existing faster-whisper install (nothing extra to
download). VoiceStudio never switches engines automatically — this stays your
diff --git a/docs/migration/real-time-voice-cloning.md b/docs/migration/real-time-voice-cloning.md
index 956f5c9f..89b48a2f 100644
--- a/docs/migration/real-time-voice-cloning.md
+++ b/docs/migration/real-time-voice-cloning.md
@@ -25,7 +25,7 @@ on your own machine: no accounts, no API keys, no cloud.
|---|---|---|
| Speaker encoder + reference utterance | Reference clip in the **Voice Clone** workflow ("From audio") | No separate embedding step — zero-shot engines condition on the clip directly |
| Saved speaker embeddings (`.npy`) | **Voice Profiles** — save a clone once, reuse it everywhere | Exportable as portable `.ovsvoice` bundles |
-| Synthesizer + vocoder choice (Tacotron 2 · WaveRNN / Griffin-Lim) | **TTS engine choice** — Model Catalogue → Engines | 14 engines, from CPU-realtime to GPU heavyweights; per-engine GPU preflight |
+| Synthesizer + vocoder choice (Tacotron 2 · WaveRNN / Griffin-Lim) | **TTS engine choice** — Model Catalogue | 14 engines, from CPU-realtime to GPU heavyweights; per-engine GPU preflight |
| The Toolbox GUI (`demo_toolbox.py`) | The app itself | Record or drop a clip, type text, synthesize — same loop, no `python demo_toolbox.py` |
| `demo_cli.py` / scripting your own pipeline | Local REST API (OpenAI-compatible, `http://localhost:3900/v1`), `omnivoice-infer` CLI, MCP server | See the [API section of the README](../../README.md#openai-api) |
| Training your own encoder / synthesizer / vocoder | Partial — see ["What RTVC did that VoiceStudio doesn't"](#what-rtvc-did-that-omnivoice-doesnt) | Fine-tuning the bundled model is documented; RTVC-style three-stage research training is not what this project is |
diff --git a/docs/performance.md b/docs/performance.md
index 347ff69e..2c41e8aa 100644
--- a/docs/performance.md
+++ b/docs/performance.md
@@ -38,7 +38,7 @@ Before touching any knob, check these — they account for most slowness reports
- **Settings → About → Run self-check** (the `/system/diagnose` endpoint)
warns explicitly: *"cpu (no GPU acceleration detected)"* with a hint
about drivers.
- - **Model Catalogue → Engines** shows a routing badge per engine — "GPU active",
+ - **Model Catalogue** shows a routing badge per engine — "GPU active",
"CPU fallback", or "CPU" — with the *reason* shown as small text under
the badge (full text on hover).
Note: **GPU acceleration on Windows is NVIDIA/CUDA-only** — AMD and Intel
@@ -183,7 +183,7 @@ drain, or restart the backend, and then Flush.
- **Unload all + flush** — the above **plus** fully unloads the resident
TTS model. Frees the most memory; the next generation pays the ~8 s
reload.
-- **Model Catalogue → Models** — rows whose weights are resident right now show an
+- **Model Catalogue → Downloaded weights** — rows whose weights are resident right now show an
"In memory" badge with the same per-model **Unload** button.
**From a script** (the local API on port 3900), the same operations:
@@ -234,7 +234,7 @@ CPU ASR, the crash-isolated ASR engine).
to 3-4 concurrent generations (API/batch workloads); ≤10 GB deliberately
serializes.
- **CPU-only**: expect ~2x slower than MPS, more against CUDA. Prefer the
- smaller/faster engines (see Model Catalogue → Engines) and short reference clips.
+ smaller/faster engines (see Model Catalogue) and short reference clips.
## Measuring instead of guessing
diff --git a/frontend/src/components/EngineQuickSwitch.jsx b/frontend/src/components/EngineQuickSwitch.jsx
index c128734e..03227b93 100644
--- a/frontend/src/components/EngineQuickSwitch.jsx
+++ b/frontend/src/components/EngineQuickSwitch.jsx
@@ -218,7 +218,7 @@ export default function EngineQuickSwitch({
type="button"
onClick={() => {
setOpen(false);
- useAppStore.getState().openCatalogue({ pane: 'engines', family });
+ useAppStore.getState().openCatalogue({ family });
}}
className="mt-[3px] flex items-center gap-[3px] border-0 bg-transparent px-[7px] py-[5px] text-left text-[11px] text-[color:var(--chrome-fg-muted)] hover:text-[color:var(--chrome-fg)]"
>
diff --git a/frontend/src/components/Header.jsx b/frontend/src/components/Header.jsx
index 4c9072f7..d1a4e11c 100644
--- a/frontend/src/components/Header.jsx
+++ b/frontend/src/components/Header.jsx
@@ -517,9 +517,7 @@ export default function Header({
className="flex items-center gap-1 border-0 bg-transparent p-2 text-xs text-[var(--chrome-fg-muted)] cursor-pointer hover:text-[var(--chrome-fg)]"
onClick={() => {
setFlushOpen(false);
- useAppStore
- .getState()
- .openCatalogue({ pane: 'engines', family: engineFamily });
+ useAppStore.getState().openCatalogue({ family: engineFamily });
}}
>
{t('header.label_catalogue')}
diff --git a/frontend/src/components/catalogue/SetupSummary.jsx b/frontend/src/components/catalogue/SetupSummary.jsx
new file mode 100644
index 00000000..dabce062
--- /dev/null
+++ b/frontend/src/components/catalogue/SetupSummary.jsx
@@ -0,0 +1,236 @@
+import React, { useEffect, useMemo, useState } from 'react';
+import { useQuery } from '@tanstack/react-query';
+import { useTranslation } from 'react-i18next';
+import { toast } from 'react-hot-toast';
+import { Download, RefreshCw } from 'lucide-react';
+import { apiJson } from '../../api/client';
+import { useEngines, useInstallModel, useRecommendations } from '../../api/hooks';
+import { useAppStore } from '../../store';
+import { Badge, Button } from '../../ui';
+
+/**
+ * SetupSummary — the Model Catalogue's first screen: what the app will use
+ * right now for speech, transcription, dictation and the language model, one
+ * line each, and a "Change" that jumps to that family's engine list below.
+ *
+ * Most users take the recommended engines and never come back; this is the
+ * view built for them. Everything that used to compete for attention on the
+ * old two-pane page (engine matrix, model store, stats) now sits under it,
+ * reached only through Change.
+ */
+const DEVICE_LABEL = { cuda: 'CUDA', mps: 'MPS', rocm: 'ROCm', cpu: 'CPU', directml: 'DirectML' };
+
+const STATUS_TONE = {
+ ready: 'success',
+ cpu: 'warn',
+ setup: 'warn',
+ off: 'neutral',
+ missing: 'warn',
+};
+
+/** Pure: reduce one family's /engines payload to {name, device, status}. */
+export function summarizeFamily(family, familyData) {
+ const active = familyData?.active;
+ const backends = familyData?.backends || [];
+ if (family === 'llm' && (!active || active === 'off')) {
+ return { name: null, device: null, status: 'off' };
+ }
+ const entry = backends.find((b) => b.id === active);
+ const name = entry?.display_name || active || null;
+ if (!entry || entry.available === false) return { name, device: null, status: 'setup' };
+ const routing = entry.routing_status;
+ const device =
+ routing === 'n/a' ? 'remote' : DEVICE_LABEL[entry.effective_device] || entry.effective_device;
+ return {
+ name,
+ device: device || null,
+ status: routing === 'cpu_fallback' ? 'cpu' : 'ready',
+ };
+}
+
+/** Pure: the dictation line from /dictation/models + the model_id pref. */
+export function summarizeDictation(data, modelId) {
+ const models = Array.isArray(data?.models) ? data.models : [];
+ const selected =
+ models.find((m) => m.id === modelId) || models.find((m) => m.recommended) || models[0];
+ if (!selected) return { name: null, device: null, status: 'setup' };
+ if (data?.engine_available === false)
+ return { name: selected.label, device: null, status: 'setup' };
+ return {
+ name: selected.label,
+ device: DEVICE_LABEL.cpu,
+ status: selected.installed ? 'ready' : 'missing',
+ };
+}
+
+export default function SetupSummary({ onChange }) {
+ const { t } = useTranslation();
+ const enginesQuery = useEngines();
+ const recoQuery = useRecommendations();
+ const installMutation = useInstallModel();
+ const [installing, setInstalling] = useState(false);
+
+ const modelId = useAppStore((s) => s.dictationModelId);
+ const loadPrefs = useAppStore((s) => s.loadDictationPrefs);
+ const dictationLoaded = useAppStore((s) => s.dictationLoaded);
+ useEffect(() => {
+ if (!dictationLoaded) loadPrefs?.();
+ }, [dictationLoaded, loadPrefs]);
+ // Same query key as the quick-menu picker so both read one cache entry.
+ const dictationQuery = useQuery({
+ queryKey: ['dictation-models'],
+ queryFn: () => apiJson('/dictation/models'),
+ staleTime: 10_000,
+ retry: false,
+ });
+
+ const engines = enginesQuery.data;
+ const rows = useMemo(
+ () => [
+ {
+ key: 'speech',
+ family: 'tts',
+ label: t('header.speech'),
+ ...summarizeFamily('tts', engines?.tts),
+ },
+ {
+ key: 'transcription',
+ family: 'asr',
+ label: t('projects.transcription'),
+ ...summarizeFamily('asr', engines?.asr),
+ },
+ {
+ key: 'dictation',
+ family: 'asr',
+ label: t('settings.dictation'),
+ ...summarizeDictation(dictationQuery.data, modelId),
+ },
+ {
+ key: 'llm',
+ family: 'llm',
+ label: t('models.role_llm'),
+ ...summarizeFamily('llm', engines?.llm),
+ },
+ ],
+ [t, engines, dictationQuery.data, modelId],
+ );
+
+ const statusLabel = {
+ ready: t('models.ready_badge'),
+ cpu: t('catalogue.status_cpu'),
+ setup: t('catalogue.status_setup'),
+ off: t('catalogue.status_off'),
+ missing: t('models.not_installed'),
+ };
+
+ const reco = recoQuery.data;
+ const missing = reco?.models?.filter((m) => !m.installed) || [];
+ const installRest = async () => {
+ if (missing.length === 0) return;
+ setInstalling(true);
+ try {
+ await Promise.all(missing.map((m) => installMutation.mutateAsync(m.repo_id)));
+ toast.success(t('models.started_downloading', { count: missing.length }));
+ } catch (e) {
+ toast.error(t('models.install_failed', { message: e?.message || e }));
+ } finally {
+ setInstalling(false);
+ }
+ };
+
+ return (
+
+