templates/chicorytv-admin/Settings.jsx wired into app.html; design_handoff_settings/ carries the implementation spec + API mapping. Synced to the Claude Design project via DesignSync. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
8.2 KiB
Handoff: ChicoryTV — Settings
Overview
The Settings screen replaces ErsatzTV's seven legacy Blazor settings pages (FFmpeg, HDHR, Playout, XMLTV, Scanner, Logging, UI) with one screen: a left sub-nav rail selects a settings group, the right pane shows that group as dense "system settings" rows, and a floating save bar appears whenever there are unsaved changes. It ships inside the ChicoryTV admin shell.
About the design files
Settings.jsx here is a design reference (same file as templates/chicorytv-admin/Settings.jsx,
running on the UI-kit's React primitives + CSS custom-property tokens) — recreate it in the target
codebase (web/ React SPA) using its established components and patterns, not by shipping this file.
Backend contract (agreed scope, issue #93)
Each pane maps 1:1 to a thin REST wrapper over an existing MediatR handler pair:
| Pane | Endpoint | Fields |
|---|---|---|
| General | GET/PUT /api/settings/ui |
theme (Dark/Light), language (culture) |
| Streaming | GET/PUT /api/settings/ffmpeg |
ffmpeg/ffprobe paths, default profile id, preferred audio language, 4 bool flags, global watermark id, global fallback filler id, HLS idle timeout / work-ahead limit / initial segments, HLS-Direct output format, default MPEG-TS script |
| Streaming → Custom resolutions | GET /api/settings/resolutions + POST/DELETE custom resolutions |
width × height list, custom-only deletable |
| Streaming → FFmpeg profiles | existing GET /api/ffmpeg/profiles |
read-only list + Default badge; editing stays in legacy UI (callout) |
| Playout | GET/PUT /api/settings/playout |
days to build, skip missing items, scripted schedule timeout |
| Guide (XMLTV) | GET/PUT /api/settings/xmltv |
days to build, time zone (Local/UTC), block behavior (SplitTimeEvenly/UseActualTimes) |
| Scanner | GET/PUT /api/settings/scanner |
library refresh interval hours (0 = disabled → warning callout) |
| Logging | GET/PUT /api/settings/logging |
6 Serilog min levels: Default, Scanning, Scheduling, Searching, Streaming, HTTP |
| System → HDHR | GET/PUT /api/settings/hdhr |
tuner count (writable), device UUID (read-only + copy) |
| System → Media sources | existing GET /api/media-sources |
read-only list + "Legacy UI" callout |
| System → About | existing GET /api/version, GET /api/health |
version, health summary + link to Dashboard |
Fidelity
High-fidelity. Colors, type, spacing, radii and interactions are resolved; all values are token
references (var(--…)) from the design system. Recreate with the codebase's real component library.
Layout
Full-height flex row under the admin shell TopBar (title "Settings"):
-
Sub-nav rail — 224px,
flex: 0 0 auto, hairline right border,padding: 12px 8px, scrolls independently. One button per section: icon (16px, accent when active) + label (--text-smmedium; primary when active, else secondary) + one-line hint (--text-2xs, disabled color, ellipsized). Active =--ctv-accent-softbackground, radius--radius-sm; hover =--ctv-surface-2. Uses.ctv-press. Sections (id · label · lucide icon · hint):general· General · SlidersHorizontal · "Theme & language"streaming· Streaming · Clapperboard · "FFmpeg & transcoding"playout· Playout · ListVideo · "Build defaults"xmltv· Guide (XMLTV) · CalendarDays · "EPG output"scanner· Scanner · Radar · "Library refresh"logging· Logging · ScrollText · "Log levels"system· System · Server · "HDHR, sources, about"
-
Pane — fills the rest; scroll container padded
20px 24px 96px(bottom room for the save bar). Content is amax-width: 760pxcolumn,gap: 16, entering withctv-fade-in240ms. Pane header: title (--text-lgsemibold) + subtitle (--text-sm, secondary, 1.45 line-height).
Row primitive (the core pattern)
Settings are flush rows inside Card padded={false}; each row:
display:flex; align-items:center; gap:20; padding:13px 16px; hairline top border between rows.
Left: label (--text-sm medium, primary) + optional help line (--text-xs, disabled, 1.45).
Right: fixed-width control slot (flex: 0 0 260px default; 320px for path inputs, 180px for
log-level selects, 340px for the UUID row), right-justified. Controls are size="sm":
Select fullWidth, Switch, Input.
Specialized controls:
- Path input — mono font, full width of slot, trailing validity icon:
CircleCheckin--status-okwhen valid,CircleAlertin--status-errorwith a "File not found" tooltip when not. - Number input —
type="number", 120px, mono, trailing unit hint (sec,days,hours,tuners,sessions,segments) in--text-2xsdisabled color.
Floating save bar (screen-level dirty state)
One draft state for the whole screen (all panes share it — switching sections keeps edits).
When ≥1 field differs from saved values, a pill bar floats bottom-center of the pane
(absolute, padding-bottom: 18px): --surface-raised bg, --border-control border,
--radius-pill, --shadow-pop, ctv-fade-in 200ms, .ctv-lift. Contents:
"N unsaved change(s)" (--text-sm medium) · 1px hairline divider · Discard (ghost, resets
draft) · Save changes (primary, Check icon). After save, the bar swaps to a transient
confirmation — CircleCheck in --status-ok + "Settings saved" — for ~1.8s, then disappears.
Pane details
- General: one card — Theme select (Dark/Light), Language select. Subtitle notes these apply to the legacy web UI (ChicoryTV theming is the shell's theme switcher, not a server setting).
- Streaming: five cards —
- FFmpeg: both path inputs, default profile select, preferred audio language select, four switches (use embedded subtitles, extract embedded subtitles, probe interlaced, save reports).
- Global defaults: watermark select, fallback filler select (both with "(none)" option).
- HLS sessions: idle timeout, work-ahead limit, initial segments (number inputs) + HLS-Direct output format select (MPEG-TS/MP4/MKV) + default MPEG-TS script select.
- Custom resolutions (flush list): rows of
W × H(mono) +customTag + ghost trash IconButton; footer add-row on--ctv-bg-sunken: width/height number inputs + "Add resolution" secondary button (disabled until both filled). - FFmpeg profiles (flush list, read-only): Cpu icon (accent) + name + Default accent Badge on the default profile; card action shows a count Badge; footer = Legacy-UI callout (see below).
- Playout: one card — days to build, skip missing (switch), scripted timeout.
- Guide (XMLTV): one card — days to build, time zone select, block behavior select.
- Scanner: one card — refresh interval hours. When value is 0, a warning callout appears under
the card:
--status-warn-softbg, TriangleAlert icon, "Automatic scanning is disabled…". - Logging: one card, six rows (Default/Scanning/Scheduling/Searching/Streaming/HTTP), each a level select (Verbose…Fatal, 180px slot) with a per-category help line.
- System: three cards —
- HDHomeRun: tuner count (writable number input), device UUID (read-only mono + Copy IconButton).
- Media sources (flush, read-only): kind icon (HardDrive for Local, Cast otherwise) + name + mono host detail + StatusDot (ok/warn) + last-scan text; footer Legacy-UI callout.
- About: version (mono), health checks summary + "Open Dashboard" secondary button (ArrowRight).
Legacy-UI callout (reusable)
For anything visible-but-not-editable here: a row with --ctv-surface-2 bg, dashed
--border-control border, --radius-sm; ExternalLink icon (disabled color) + explanation
(--text-xs, secondary) + a neutral Legacy UI Badge pinned right.
Interaction notes
- Section switching is instant (no route change needed in the prototype; the SPA should use its
routing so sections are linkable, e.g.
/app/settings/streaming). - Draft edits survive section switches; Discard restores all panes at once.
- Number inputs keep string state in the prototype; the SPA should validate (ints ≥ 0) before save.
- Reduced motion: all animation comes from
.ctv-lift/.ctv-press/ctv-fade-in, already gated byprefers-reduced-motionin the kit.