Skip to content

Settings overhaul: routed sub-tabs, server logs, changelog, about page#294

Merged
jamiepine merged 4 commits intomainfrom
feat/settings-overhaul
Mar 16, 2026
Merged

Settings overhaul: routed sub-tabs, server logs, changelog, about page#294
jamiepine merged 4 commits intomainfrom
feat/settings-overhaul

Conversation

@jamiepine
Copy link
Owner

@jamiepine jamiepine commented Mar 16, 2026

Summary

  • Split the old Server tab into a full Settings area with routed sub-tabs: General, Generation, GPU, Logs, Changelog, About
  • Reusable SettingRow / SettingSection components for consistent layout across all settings pages
  • Server log viewer that streams logs from the Tauri sidecar event system (Tauri-only)
  • Changelog page rendered from CHANGELOG.md at build time via a Vite virtual module plugin
  • About page with version info, links, and license
  • Generation settings page with generations folder open button
  • New Toggle (switch) component replacing checkboxes
  • Sidebar correctly highlights on settings sub-routes via fuzzy matching
  • Toast viewport offsets when the audio player is open
  • /server redirects to /settings for backwards compat

Summary by CodeRabbit

  • New Features

    • Reorganized Settings with sub-pages: General, Generation, GPU, Logs, Changelog, About
    • New Logs viewer showing real-time server output with clear and auto-scroll controls
    • Changelog and About pages with formatted release notes and app/version info
    • GPU/CUDA management, generation settings, and network/update controls (including keep-server and restart flows)
  • UI Improvements

    • Animated status bars, new Toggle component, improved toast positioning
  • Chores

    • Build/tooling and config updates added

…elog, reusable setting components

- Rename Server tab to Settings with horizontal sub-tab navigation (General, Generation, GPU, Logs, Changelog)
- All sub-tabs are proper routes under /settings/* with /server redirect for backwards compat
- General: connection settings, link cards (docs + discord), API reference card, app updates
- Generation: auto-chunking, crossfade, normalize, autoplay as SettingRow components
- GPU: info card with platform-aware icons (Apple logo for MPS), CUDA management, explainer text
- Logs: real-time server log viewer piped from Tauri sidecar via event system (Tauri-only)
- Changelog: parsed from CHANGELOG.md at build time via Vite virtual module plugin
- New reusable SettingRow/SettingSection components for consistent settings layout
- New Toggle (switch) UI component replacing checkboxes in settings
- Toast viewport now offsets when audio player is open
- Sidebar stays active on settings sub-routes (fuzzy matching)
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 16, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 34100554-2936-42ae-b69c-1e73fa7c8048

📥 Commits

Reviewing files that changed from the base of the PR and between a8469b3 and 2ad4776.

📒 Files selected for processing (9)
  • app/src/components/ServerTab/ChangelogPage.tsx
  • app/src/components/ServerTab/GeneralPage.tsx
  • app/src/components/ServerTab/GpuPage.tsx
  • app/src/components/ServerTab/LogsPage.tsx
  • app/src/components/ServerTab/SettingRow.tsx
  • app/src/components/ui/toaster.tsx
  • app/src/components/ui/toggle.tsx
  • app/src/stores/logStore.ts
  • tauri/src/platform/lifecycle.ts

📝 Walkthrough

Walkthrough

Replaces ServerTab with a tabbed SettingsLayout (General, Generation, GPU, Logs, Changelog, About), adds server log streaming and a capped log store, exposes CHANGELOG.md via a Vite virtual plugin and parser, updates routing and Vite configs, and adds multiple settings UI components and backend packaging tweaks.

Changes

Cohort / File(s) Summary
Changelog Infrastructure
CHANGELOG.md, app/plugins/changelog.ts, app/src/global.d.ts, app/src/lib/utils/parseChangelog.ts, app/vite.config.ts, tauri/vite.config.ts, web/vite.config.ts
Adds narrative Unreleased changelog content, a Vite plugin exposing virtual:changelog, ambient typing for the virtual module, a parser for Keep‑a‑Changelog entries, and wires the plugin into platform Vite configs.
Server Logs System
tauri/src-tauri/src/main.rs, tauri/src/platform/lifecycle.ts, web/src/platform/lifecycle.ts, app/src/platform/types.ts, app/src/App.tsx, app/src/stores/logStore.ts, app/src/components/ServerTab/LogsPage.tsx
Backend/tauri now emits server-log events; lifecycle exposes subscribeToServerLogs (no-op on web); App subscribes and forwards entries to a new Zustand log store that caps entries; LogsPage renders and manages auto-scroll and clearing.
Settings Pages & Layout
app/src/components/ServerTab/ServerTab.tsx, app/src/router.tsx, app/src/components/ServerTab/GeneralPage.tsx, GenerationPage.tsx, GpuPage.tsx, ChangelogPage.tsx, AboutPage.tsx, SettingRow.tsx
Replaces ServerTab with SettingsLayout and nested routes; adds General, Generation, GPU, Changelog, Logs, and About pages plus layout primitives (SettingSection/SettingRow) and route redirects from /server/settings.
UI Components & Small UX
app/src/components/History/HistoryTable.tsx, app/src/components/ui/slider.tsx, app/src/components/ui/toggle.tsx, app/src/components/ui/toaster.tsx, app/src/components/Sidebar.tsx
Replaces Loader with an AudioBars status UI in HistoryTable; refactors Slider thumb rendering and exports Slider; adds Toggle component; Toaster adjusts viewport when player open; Sidebar renames Server → Settings and updates route matching.
Backend Packaging & Health
backend/build_binary.py, backend/voicebox-server.spec, backend/routes/health.py
Adds PyInstaller collections/hiddenimports (lazy_loader/librosa and others) for runtime stubs and metadata; health check paths now use resolved absolute paths.
TypeScript / Config
app/tsconfig.node.json, app/vite.config.ts, tauri/vite.config.ts, web/vite.config.ts
Includes plugin TS files in tsconfig.node, and adds the changelogPlugin to Vite plugin chains for app/tauri/web configs.
Docs
docs/notes/BACKEND_CODE_REVIEW.md
Adds a detailed backend code-review document listing findings, severities, and remediation notes.

Sequence Diagram(s)

sequenceDiagram
    participant Backend as Backend/Tauri
    participant TauriLC as TauriLifecycle
    participant App as App.tsx
    participant LogStore as useLogStore
    participant LogsPage as LogsPage

    Backend->>TauriLC: emit "server-log" event {stream,line}
    TauriLC->>App: invoke subscribed callback(entry)
    App->>LogStore: addEntry(entry)
    LogStore-->>LogsPage: updated entries (subscribe)
    LogsPage->>LogsPage: render entries, manage auto-scroll
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 A little rabbit hopped into the code,

Tabs and logs and changelogs in a neat new node,
Virtual pages, streams that softly hum,
Settings all arranged — hooray, here they come! 🥕✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.27% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and accurately summarizes the main change: a comprehensive Settings overhaul featuring routed sub-tabs and new pages (server logs, changelog, about page), which aligns directly with the changeset's primary objective.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/settings-overhaul
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@jamiepine jamiepine marked this pull request as ready for review March 16, 2026 19:34
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (8)
backend/voicebox-server.spec (1)

9-9: Consider de-duplicating the hidden import manifest.

Line 9 keeps a large inline import manifest that now also overlaps with backend/build_binary.py; keeping this in one shared source would reduce drift risk between build paths.

♻️ Suggested direction
+# backend/pyinstaller_manifest.py
+HIDDEN_IMPORTS = [
+    "backend",
+    "backend.main",
+    ...
+]
+
# backend/voicebox-server.spec
-from ... import ...
-hiddenimports = ['backend', 'backend.main', ...]
+from backend.pyinstaller_manifest import HIDDEN_IMPORTS
+hiddenimports = list(HIDDEN_IMPORTS)

# backend/build_binary.py
-args.extend(["--hidden-import", "backend", ...])
+for mod in HIDDEN_IMPORTS:
+    args.extend(["--hidden-import", mod])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/voicebox-server.spec` at line 9, The hiddenimports list in
voicebox-server.spec duplicates the large import manifest defined in
backend.build_binary.py; extract the shared manifest into a single constant
(e.g., HIDDEN_IMPORTS or HIDDEN_IMPORT_MANIFEST) in backend.build_binary (or a
new module like backend.build_shared) and have voicebox-server.spec import or
load that constant instead of inlining the list, then remove the duplicated
inline list from voicebox-server.spec and ensure any build scripts use the
shared constant so both build paths reference the same source of truth.
app/plugins/changelog.ts (1)

16-21: Consider adding error handling for missing changelog file.

If CHANGELOG.md is missing or unreadable, readFileSync will throw with a generic ENOENT error, which may be confusing during build failures. A descriptive error message would improve developer experience.

♻️ Proposed enhancement
+import { existsSync, readFileSync } from 'node:fs';
-import { readFileSync } from 'node:fs';
...
     load(id) {
       if (id === resolvedId) {
+        if (!existsSync(changelogPath)) {
+          throw new Error(`Changelog not found at ${changelogPath}`);
+        }
         const raw = readFileSync(changelogPath, 'utf-8');
         return `export default ${JSON.stringify(raw)};`;
       }
     },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/plugins/changelog.ts` around lines 16 - 21, The load function currently
calls readFileSync(changelogPath, 'utf-8') without handling failures; wrap the
file read in a try/catch inside load (the branch that checks id === resolvedId)
and on failure throw or log a new Error that includes changelogPath and the
original error message so build errors show a descriptive "missing/unreadable
CHANGELOG.md" style message rather than raw ENOENT; reference load, resolvedId,
changelogPath and readFileSync when making the change.
app/src/components/ServerTab/SettingRow.tsx (1)

49-54: Ensure htmlFor is paired with matching id on controls.

The label element's htmlFor attribute only creates an accessible association when the target control has a matching id. The cursor-pointer class also sets an expectation that clicking the label will activate something.

Consider documenting this requirement or conditionally applying cursor-pointer only when htmlFor is provided:

♻️ Conditional cursor styling
           <label
             htmlFor={htmlFor}
-            className="text-sm font-medium leading-none cursor-pointer select-none"
+            className={`text-sm font-medium leading-none select-none ${htmlFor ? 'cursor-pointer' : ''}`}
           >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/components/ServerTab/SettingRow.tsx` around lines 49 - 54, The label
in SettingRow.tsx uses htmlFor but the associated control may lack a matching id
and the cursor-pointer class creates a false affordance; update the SettingRow
component to ensure the control receives the same id when an htmlFor prop is
provided (or require/prop-drill an id prop to the child control) and/or
conditionally apply the "cursor-pointer" class only when htmlFor is truthy.
Locate the label usage in SettingRow (the label element using htmlFor and
{title}) and either (a) pass the htmlFor value through to the child control as
its id or (b) change the className logic to include "cursor-pointer" only if
htmlFor is present; also add a short prop comment or JSDoc on SettingRow
explaining that htmlFor requires a matching id on the input.
app/src/components/ui/toggle.tsx (1)

27-32: Consider adding visible focus styling for keyboard navigation.

The toggle lacks explicit focus indicator styles. While browsers provide a default focus ring, it may not be visible against all backgrounds or may have been reset by a global CSS reset. Adding a focus ring improves keyboard accessibility.

♿ Proposed focus styling
         className={cn(
           'relative inline-flex h-5 w-9 shrink-0 items-center rounded-full transition-colors',
+          'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
           checked ? 'bg-accent' : 'bg-muted-foreground/25',
           disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer',
           className,
         )}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/components/ui/toggle.tsx` around lines 27 - 32, The Toggle
component's root element (the className assembled via cn with checked and
disabled states) lacks an explicit keyboard focus indicator; update the
className list in the Toggle component (the cn call that currently composes
'relative inline-flex h-5 w-9 ...') to include focus-visible ring classes (e.g.,
focus-visible:ring, focus-visible:ring-offset and appropriate ring color/width)
and remove any conflicting outline-none so keyboard users get a clear visible
focus ring when the component receives focus; ensure the focus styles apply even
when checked or disabled by placing them alongside the existing checked/disabled
class entries.
web/vite.config.ts (1)

3-8: Minor inconsistency in plugin ordering.

The plugin order here is react(), tailwindcss(), changelogPlugin() while app/vite.config.ts uses tailwindcss(), react(), changelogPlugin(). This works but is inconsistent across the codebase.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/vite.config.ts` around lines 3 - 8, The plugins array in defineConfig
uses react(), tailwindcss(), changelogPlugin(...) but should match the other
config's ordering; update the plugins list so tailwindcss() comes first, then
react(), then changelogPlugin(path.resolve(__dirname, '..')), i.e., replace the
current plugins order with tailwindcss(), react(), changelogPlugin(...) in the
default export; ensure you keep the existing changelogPlugin call and
path.resolve argument unchanged.
app/src/components/ServerTab/GeneralPage.tsx (2)

157-166: Same state desync concern for network access toggle.

The setMode call should similarly be reverted if the underlying operation conceptually fails (though this one doesn't have a platform call that could fail).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/components/ServerTab/GeneralPage.tsx` around lines 157 - 166, The
network-access toggle currently calls setMode directly inside onCheckedChange
which can desync UI if the underlying change fails; capture the previous mode
(e.g., const prev = mode), set mode optimistically, then perform the operation
that applies the change (or a placeholder async call), and if that operation
throws or returns a failure revert by calling setMode(prev). Update the
onCheckedChange handler (the function passed to onCheckedChange) to use this
pattern and ensure toast is shown only after success (or show an error toast on
revert) so setMode and toast stay consistent.

132-143: State may desync if platform call fails.

setKeepServerRunningOnClose(checked) updates the store immediately, then platform.lifecycle.setKeepServerRunning(checked) is called. If the platform call fails, the UI shows the new state but the platform has the old state.

Consider reverting on failure:

  onCheckedChange={(checked: boolean) => {
    setKeepServerRunningOnClose(checked);
    platform.lifecycle.setKeepServerRunning(checked).catch((error) => {
      console.error('Failed to sync setting to Rust:', error);
+     setKeepServerRunningOnClose(!checked); // Revert on failure
+     toast({
+       title: 'Failed to update setting',
+       description: 'Could not sync setting to backend.',
+       variant: 'destructive',
+     });
    });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/components/ServerTab/GeneralPage.tsx` around lines 132 - 143, The UI
currently updates state optimistically by calling
setKeepServerRunningOnClose(checked) before the platform call, which can desync
if platform.lifecycle.setKeepServerRunning(checked) fails; change this to either
perform the platform call first and only call setKeepServerRunningOnClose on
success, or keep the optimistic update but revert it in the catch handler (call
setKeepServerRunningOnClose(!checked)), and surface the failure via toast or
console.error so users know the setting didn't persist; update the
onCheckedChange handler accordingly to ensure state and platform remain
consistent and reference setKeepServerRunningOnClose and
platform.lifecycle.setKeepServerRunning for the fix.
app/src/components/ServerTab/GpuPage.tsx (1)

165-167: Consider surfacing EventSource connection errors.

The onerror handler silently closes the connection without notifying the user. If the SSE stream fails due to network issues, the UI will show stale progress with no indication of failure.

  eventSource.onerror = () => {
    eventSource.close();
+   setDownloadProgress(null);
+   refetchCudaStatus();
  };

This resets the UI and refreshes the actual status from the server.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/components/ServerTab/GpuPage.tsx` around lines 165 - 167, In
GpuPage.tsx update the eventSource.onerror handler (currently calling
eventSource.close()) to surface the failure: set a component error state and/or
show a user-visible notification, call eventSource.close(), and then trigger the
existing status-refresh logic so the UI fetches the actual status from the
server (i.e., replace the silent close in eventSource.onerror with code that
sets an error flag/notification and invokes the component's status refresh
function).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/src/components/ServerTab/ChangelogPage.tsx`:
- Around line 51-68: The list mapping in ChangelogPage.tsx uses the item content
as the React key (items.map((item) => ...)), which can collide for duplicate
list entries; change the mapping to use a stable unique key instead (for example
include the parent block index/elements.length and the item index or a generated
unique id) by updating the items.map callback signature to items.map((item, idx)
=> ...) and using a composite key such as `${elements.length}-${idx}` (or
another stable unique scheme) for the <li> elements.
- Around line 99-117: The table uses non-unique content values as React keys
(headers.map key={h}, rows.map key={row.join('|')}, and row.map key={cell})
which can collide; update these to use stable positional keys or composite keys
instead (e.g., use the header index in headers.map like `${headerIndex}-${h}`,
use row index for rows.map like `${rowIndex}-${row.join('|')}` or ideally
`${rowIndex}`, and use `${rowIndex}-${cellIndex}` for cells) by updating the key
props in the header, row, and cell renderers (refer to the headers.map,
rows.map, and row.map calls) so keys are unique and stable across renders.

In `@app/src/components/ServerTab/GpuPage.tsx`:
- Around line 234-258: Both handleSwitchToCpu and handleRestart share a racey
sequence around starting/stopping health polling and calling
platform.lifecycle.restartServer; extract a shared helper (e.g.,
restartServerWithPolling) and move the coordinated logic there: ensure you clear
any existing healthPollRef before initiating restart, call
platform.lifecycle.restartServer and await it, only startHealthPolling after
restart completes (or avoid starting it if you immediately clear it), and always
clear healthPollRef in a finally block; update handleSwitchToCpu and
handleRestart to call this helper and remove their duplicated
polling/restart/clearInterval code to fix the race and centralize behavior.
- Around line 210-232: The handleRestart sequence races because
startHealthPolling() is called before platform.lifecycle.restartServer()
completes; adjust handleRestart (and related healthPollRef logic) to begin
polling only after the restart has been initiated and the server has been
observed to go unhealthy (i.e., call platform.lifecycle.restartServer() first or
wait for the first poll to return unhealthy before treating it as a restart in
progress), then startHealthPolling() to detect when it comes back healthy and
update restartPhase accordingly; ensure you still clear healthPollRef in both
success and catch branches and keep restartPhase transitions (stopping → waiting
→ ready → idle) consistent in functions handleRestart, startHealthPolling, and
any health check callbacks so the phase cannot be set to 'ready' while the
server is still healthy.

In `@app/src/components/ServerTab/LogsPage.tsx`:
- Line 99: The current entries.map((entry, i) => <LogLine
key={`${entry.timestamp}-${i}`} entry={entry} />) uses the array index i which
causes key churn when the capped buffer trims old entries; instead generate and
persist a stable unique id for each log entry in the log store (e.g., add an id
field when creating entries in the store where entries are pushed/trimmed, using
a UUID or incrementing counter) and then change the rendering to use that stable
id as the key (replace `${entry.timestamp}-${i}` with entry.id). Update code
that constructs entries (store functions that append/trim logs, MAX_LOG_ENTRIES
logic) to ensure the id is assigned once and survives buffer shifts so LogLine
components remain stable.

In `@app/src/components/ui/toaster.tsx`:
- Line 28: The toast viewport offset is too small and can overlap the fixed
audio player: update the conditional class on ToastViewport (the JSX using
ToastViewport and isPlayerOpen) to use a larger bottom spacing—e.g., replace the
'sm:bottom-32' value with 'sm:bottom-40' (or another value >=10rem) so toasts
sit above the AudioPlayer’s py-3 + min-h-[80px] footprint; adjust only the class
string in the ToastViewport JSX to avoid overlap.

In `@tauri/src/platform/lifecycle.ts`:
- Around line 90-101: subscribeToServerLogs currently returns a disposer
synchronously while listen(...) resolves asynchronously, causing a race where
the returned disposer can be called before unlisten is assigned; modify
subscribeToServerLogs to introduce a local boolean disposed = false, have the
returned disposer set disposed = true and call unlisten() if it's already
assigned, and in the listen(...).then handler set unlisten = fn and if
(disposed) { unlisten(); unlisten = null; } to ensure cleanup executes even if
disposal happened before registration completes; also attach a .catch handler to
the listen Promise to avoid unhandled rejections and handle cleanup state
consistently.

---

Nitpick comments:
In `@app/plugins/changelog.ts`:
- Around line 16-21: The load function currently calls
readFileSync(changelogPath, 'utf-8') without handling failures; wrap the file
read in a try/catch inside load (the branch that checks id === resolvedId) and
on failure throw or log a new Error that includes changelogPath and the original
error message so build errors show a descriptive "missing/unreadable
CHANGELOG.md" style message rather than raw ENOENT; reference load, resolvedId,
changelogPath and readFileSync when making the change.

In `@app/src/components/ServerTab/GeneralPage.tsx`:
- Around line 157-166: The network-access toggle currently calls setMode
directly inside onCheckedChange which can desync UI if the underlying change
fails; capture the previous mode (e.g., const prev = mode), set mode
optimistically, then perform the operation that applies the change (or a
placeholder async call), and if that operation throws or returns a failure
revert by calling setMode(prev). Update the onCheckedChange handler (the
function passed to onCheckedChange) to use this pattern and ensure toast is
shown only after success (or show an error toast on revert) so setMode and toast
stay consistent.
- Around line 132-143: The UI currently updates state optimistically by calling
setKeepServerRunningOnClose(checked) before the platform call, which can desync
if platform.lifecycle.setKeepServerRunning(checked) fails; change this to either
perform the platform call first and only call setKeepServerRunningOnClose on
success, or keep the optimistic update but revert it in the catch handler (call
setKeepServerRunningOnClose(!checked)), and surface the failure via toast or
console.error so users know the setting didn't persist; update the
onCheckedChange handler accordingly to ensure state and platform remain
consistent and reference setKeepServerRunningOnClose and
platform.lifecycle.setKeepServerRunning for the fix.

In `@app/src/components/ServerTab/GpuPage.tsx`:
- Around line 165-167: In GpuPage.tsx update the eventSource.onerror handler
(currently calling eventSource.close()) to surface the failure: set a component
error state and/or show a user-visible notification, call eventSource.close(),
and then trigger the existing status-refresh logic so the UI fetches the actual
status from the server (i.e., replace the silent close in eventSource.onerror
with code that sets an error flag/notification and invokes the component's
status refresh function).

In `@app/src/components/ServerTab/SettingRow.tsx`:
- Around line 49-54: The label in SettingRow.tsx uses htmlFor but the associated
control may lack a matching id and the cursor-pointer class creates a false
affordance; update the SettingRow component to ensure the control receives the
same id when an htmlFor prop is provided (or require/prop-drill an id prop to
the child control) and/or conditionally apply the "cursor-pointer" class only
when htmlFor is truthy. Locate the label usage in SettingRow (the label element
using htmlFor and {title}) and either (a) pass the htmlFor value through to the
child control as its id or (b) change the className logic to include
"cursor-pointer" only if htmlFor is present; also add a short prop comment or
JSDoc on SettingRow explaining that htmlFor requires a matching id on the input.

In `@app/src/components/ui/toggle.tsx`:
- Around line 27-32: The Toggle component's root element (the className
assembled via cn with checked and disabled states) lacks an explicit keyboard
focus indicator; update the className list in the Toggle component (the cn call
that currently composes 'relative inline-flex h-5 w-9 ...') to include
focus-visible ring classes (e.g., focus-visible:ring, focus-visible:ring-offset
and appropriate ring color/width) and remove any conflicting outline-none so
keyboard users get a clear visible focus ring when the component receives focus;
ensure the focus styles apply even when checked or disabled by placing them
alongside the existing checked/disabled class entries.

In `@backend/voicebox-server.spec`:
- Line 9: The hiddenimports list in voicebox-server.spec duplicates the large
import manifest defined in backend.build_binary.py; extract the shared manifest
into a single constant (e.g., HIDDEN_IMPORTS or HIDDEN_IMPORT_MANIFEST) in
backend.build_binary (or a new module like backend.build_shared) and have
voicebox-server.spec import or load that constant instead of inlining the list,
then remove the duplicated inline list from voicebox-server.spec and ensure any
build scripts use the shared constant so both build paths reference the same
source of truth.

In `@web/vite.config.ts`:
- Around line 3-8: The plugins array in defineConfig uses react(),
tailwindcss(), changelogPlugin(...) but should match the other config's
ordering; update the plugins list so tailwindcss() comes first, then react(),
then changelogPlugin(path.resolve(__dirname, '..')), i.e., replace the current
plugins order with tailwindcss(), react(), changelogPlugin(...) in the default
export; ensure you keep the existing changelogPlugin call and path.resolve
argument unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: dea2260f-6bec-4274-922f-7efbd7d95558

📥 Commits

Reviewing files that changed from the base of the PR and between e0a798d and a8469b3.

📒 Files selected for processing (32)
  • CHANGELOG.md
  • app/plugins/changelog.ts
  • app/src/App.tsx
  • app/src/components/History/HistoryTable.tsx
  • app/src/components/ServerTab/AboutPage.tsx
  • app/src/components/ServerTab/ChangelogPage.tsx
  • app/src/components/ServerTab/GeneralPage.tsx
  • app/src/components/ServerTab/GenerationPage.tsx
  • app/src/components/ServerTab/GpuPage.tsx
  • app/src/components/ServerTab/LogsPage.tsx
  • app/src/components/ServerTab/ServerTab.tsx
  • app/src/components/ServerTab/SettingRow.tsx
  • app/src/components/Sidebar.tsx
  • app/src/components/ui/slider.tsx
  • app/src/components/ui/toaster.tsx
  • app/src/components/ui/toggle.tsx
  • app/src/global.d.ts
  • app/src/lib/utils/parseChangelog.ts
  • app/src/platform/types.ts
  • app/src/router.tsx
  • app/src/stores/logStore.ts
  • app/tsconfig.node.json
  • app/vite.config.ts
  • backend/build_binary.py
  • backend/routes/health.py
  • backend/voicebox-server.spec
  • docs/notes/BACKEND_CODE_REVIEW.md
  • tauri/src-tauri/src/main.rs
  • tauri/src/platform/lifecycle.ts
  • tauri/vite.config.ts
  • web/src/platform/lifecycle.ts
  • web/vite.config.ts

@jamiepine jamiepine merged commit 15ba824 into main Mar 16, 2026
1 check was pending
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant