---
key: spa.autotune-detailpanel-slideover
title: '2026-07-18 — Auto-Tune DetailPanel SPA: reusable `SlideOver` + shared advanced-options model; decorative panes dropped to match the backend (#386)'
status: active
since: '2026-07-18'
supersedes: none
superseded-by: none
rule: The Auto-Tune DetailPanel SPA is a reusable `SlideOver` primitive sharing `useOverlayBehavior` with `Dialog`, plus a shared advanced-options model extracted from ChannelBuilder; decorative panes without backend support are dropped.
signals: 'Auto-Tune DetailPanel, SlideOver, shared advanced-options hook · paths: `web/src/components/overlay.tsx`, `web/src/builder/advancedOptions.tsx` · issues: #386, #384, #385, #425, #135'
mechanics: spa-conventions.md §11; `useAdvancedOverrides` hook
---

The SPA half of the Auto-Tune per-channel editor. It builds only what the shipped `/api/v1` surface
(#384 members read, #385 per-channel `templateId`/`logo`/`advanced`) can actually carry, so the panel
never presents a control with nowhere to send its value.

- **New reusable primitive `SlideOver`** (`web/src/components/overlay.tsx`), sharing one
  `useOverlayBehavior` hook with `Dialog` (focus/scroll-lock/Escape/scrim). Right-edge panels are a
  recurring need; this is the seam, not a screen-local drawer. See spa-conventions §11.
- **Advanced-options logic is shared, not re-implemented** — the `AdvancedPanel` internals in the 84 KB
  `ChannelBuilder.tsx` were extracted to `web/src/builder/advancedOptions.tsx` (enum catalogs,
  `ADVANCED_KEYS`, `effectiveValue`, and the INHERIT/omit `useAdvancedOverrides` hook). ChannelBuilder now
  imports them with zero behavioral change (its test suite passes byte-for-byte); the DetailPanel writes
  its own field JSX over the same hook. The #135 "inherit = omit, no None option" contract therefore has
  a single implementation. Field *layout* stays per-screen (presentation, not logic) — the deliberate,
  stated deviation from full component reuse.
- **Shipped panes:** identity (name/number + `uploadArtwork(_, 'logo')` dropzone), Playback (Shuffle →
  `advanced.playbackOrder`, Always-playing → `advanced.playoutMode`, merged into `advanced` only when
  diverged from the template), per-channel template picker, the Advanced disclosure, a lean read-only
  Query&size (order-from-axis + est items + effective streaming mode), and a read-only Content-sources
  member list via `GET /members`.
- **Dropped as backend-less decoration** (the prototype had them; the wire contract does not): the MiniEpg
  "example schedule", the bug-initials/colour generator, and the "generated smart-collection query" text —
  the proposal DTO carries no query string, so rendering one would be fabricated. **Deferred to #425** (its
  own end-to-end slice): the per-source rotation-weight steppers and exclude/add-untagged corrections; the
  Content-sources pane shows a "#425" hint so its read-only state reads as intentional.
- **Unsaved-changes guard is screen-scoped** (spa-conventions §8/§11): per-channel edits live in
  `AutoTuneScreen` draft state until bulk-create, so closing the panel keeps them (an "Edited" row badge
  makes that visible) and only screen navigation / full-page unload with uncommitted edits confirms.
