Files
pi-multifx-pedal/docs/api-frontend-gap-analysis.md
T

9.2 KiB
Raw Blame History

API ↔ Frontend Gap Analysis — Pi Multi-FX Pedal Web UI

Date: June 14, 2026 Scope: All backend API routes vs. calls from Legacy Jinja2 UI + React SPA


Summary

Metric Count
Total backend API routes 76
Routes called by at least one frontend 57
Routes with NO frontend consumer 10 (+ 2 alias endpoints)
Critical bugs found 2
Missing features (backend exists, no UI) 5

Changes Applied — Fixed Items

# Item Status Files Changed
B1 IR Install on IRs page broken — installToneIR added to irs.js Fixed irs.js, models.js
G6 Tuner pitch display — note + cents bar + string indicator on dashboard Added app.js, dashboard.html, style.css
G1G3 Audio profile selector — dropdown with JACK status in Settings > Audio Added app.js, settings.html, style.css
G4 FX chain clickable toggles — each block toggles ON/OFF via PATCH /api/blocks Added app.js, style.css
O1 FX type labels — proper label map (FX_TYPE_LABELS) instead of replace(/_/g,' ') Added app.js
B2 installToneIR removed from models.js (was duplicated, used wrong localStorage key) Fixed models.js

1. Backend APIs NOT Called by Any Frontend

These are routes defined in server.py that neither the legacy UI (static/) nor the React SPA (ui-dist/) ever calls.

Tier 1 — Full Features Without UI

# Route Method Endpoint exists but Why it matters
G1 /api/audio/profile GET No UI to read current audio profile Users can't see what latency profile is active
G2 /api/audio/profiles GET No UI to list available profiles Users can't see what options exist
G3 /api/audio/profile POST No UI to switch profiles Users can't switch between standard/low latency!
G4 PATCH /api/blocks PATCH No UI to enable/disable individual FX blocks FX chain shown on dashboard is display-only — can't toggle blocks on/off
G5 PATCH /api/block PATCH No UI to update individual block params Can't tweak per-block settings (drive, level, mix, etc.)
G6 GET /api/tuner/pitch GET No tuner pitch display UI Tuner toggle works but you can't see what note you're playing

Tier 2 — Secondary / Alternative Endpoints

# Route Method Notes
G7 GET /api/routing GET Both UIs only POST routing changes and rely on WS for state — GET is defined but never read separately
G8 GET /api/channel-mode GET SPA POSTs to set channel mode but never GETs current mode — relies on /api/state snapshot
G9 POST /api/bypass/toggle POST Alias — both UIs call /api/bypass directly

Tier 3 — External Content Sources (No UI)

# Route Method Notes
G10 GET /api/tonedownload/status GET Tone3000 connection status — never queried by UI
G11 GET /api/tonehub/search GET ToneHub search — never called (Tone3000 preferred)
G12 GET /api/tonehub/search/irs GET ToneHub IR search — never called

Tier 4 — Unused Alias

# Route Method Notes
G13 POST /api/volume POST POST alias for volume — legacy UI calls apiPut('/volume'), SPA... see below

2. Critical Bugs Found

Bug B1 — IR Install on IRs Page is Broken

File: src/web/static/irs.js:197 calls installToneIR() but function only exists in models.js:235

Root cause: The Tone3000 IR search results on the /irs page render "Install" buttons that call installToneIR(). This function is defined in models.js, which is loaded on the Models page (/models). The IRs page (/irs) loads irs.js, which does NOT define installToneIR() — the function appears in irs.js only as a call reference (line 197), not a definition.

Impact: Clicking "Install" on any Tone3000 IR result from the IRs page throws ReferenceError: installToneIR is not defined. The button action silently fails.

Fix: Either (a) copy the entire installToneIR() function (lines 235-256) from models.js into irs.js, or (b) extract the shared Tone3000 install logic into a shared module loaded by both pages.

# Confirm the gap
grep -n 'function installToneIR' src/web/static/*.js
# → only in models.js:235, NOT in irs.js
grep -n 'installToneIR' src/web/static/irs.js
# → line 197 is a CALL to it (onclick=...), not a definition

Bug B2 — installToneIR Duplicated Across Files

File: Models page (/models) also has IR install logic.

installToneIR is defined in models.js:235 (not just installToneModel). This means the Models page can install IR files via the Tone3000 button embedded in model search results — which makes no sense. The function is cargo-culted but only the installToneModel variant is relevant to the Models page.


3. Underserved Features (Present in Backend, Weak UI)

# Feature Has UI? Details
F1 Tuner pitch display GET /api/tuner/pitch returns frequency, note name, cents, string — but zero UI renders it. The tuner toggle exists but you get no visual feedback about what note you're playing
F2 Per-block FX control PATCH /api/blocks (toggle) and PATCH /api/block (params) are never called. The dashboard shows an FX chain but it's display-only — can't toggle blocks or tweak knobs from the web UI
F3 Audio profile switching GET/POST /api/audio/profile and GET /api/audio/profiles exist but no UI anywhere. Users must use curl to switch latency profiles
F4 Channel mode display ⚠️ SPA shows channel mode in the header (STEREO badge or GUITAR/BASS toggle). But GET /api/channel-mode is never called — the value comes from the /api/state snapshot. Legacy UI has no channel mode awareness at all
F5 Snapshots ⚠️ Snapshots exist in SPA only (save/recall/rename/delete via modal). Legacy UI has zero snapshot support

4. Feature Parity: Legacy UI vs React SPA

Feature Legacy (static/) React SPA (ui-dist/)
Dashboard + status Full Full
Preset browse + activate Full Full
Preset edit (rename, volume) Modal-based Inline
Preset create "New Preset" button
Preset delete With confirm With confirm (inline)
Model browse + load Full Full
Model upload
Tone3000 search (models)
Tone3000 install (models)
IR browse + load Full Full
IR upload
Tone3000 search (IRs) Broken (Bug B1)
Tone3000 install (IRs) Broken (Bug B1)
Bypass toggle
Volume control
Tuner toggle
Tuner pitch display
4CM routing toggle
Per-block FX toggle
Per-block param edit ⚠️ (block-params schema fetched)
Audio profile switch
WiFi scan + connect
Hotspot enable/disable
Bluetooth scan + pair + connect
Bluetooth MIDI enable/disable
Snapshots save/recall Full (CRUD)
Capture/record audio Full (start/stop/list/play)
Channel mode (dual-mono/stereo) (Settings → Routing)

5. Source of Truth Verification

# Complete list of backend API routes:
grep -oP '@app\.\w+\(["'"'"']/api/[^"'"'"']+' src/web/server.py | sed "s/@app\.//" | sort

# Complete list of API calls from legacy JS:
grep -roP "apiGet\('/[^']+|apiPost\('/[^']+|apiPut\('/[^']+|apiDelete\('/[^']+|fetch\('/api[^']+" src/web/static/ | sed "s/.*api\(Get\|Post\|Put\|Delete\)('//;s/fetch('/\/api/" | sort -u

# Complete list of API calls from React SPA bundle:
grep -oP '`/api/[^`]+`' src/web/ui-dist/assets/index-CxlwZhqE.js | sort -u

6. Remaining Gaps (Not Addressed)

These are lower-priority items that still have no frontend consumer:

# Route Method Notes
G7 GET /api/routing GET Both UIs only POST routing and rely on WS for state — GET exists but is redundant
G8 GET /api/channel-mode GET SPA POSTs to set mode but gets current state from /api/state snapshot — low value
G9 POST /api/bypass/toggle POST Alias endpoint — both UIs call /api/bypass directly
G10 GET /api/tonedownload/status GET Tone3000 connection status — no UI indicator, minor
G11 GET /api/tonehub/search GET ToneHub — never wired up in favor of Tone3000
G12 GET /api/tonehub/search/irs GET ToneHub IR search — never wired up
G13 POST /api/volume POST POST alias — legacy calls PUT, SPA calls POST; both work
G14 Per-block param editing (PATCH /api/block, GET /api/block-params/{fx_type}) PATCH/GET Block param sliders still need a UI — tracked in UX review test plan