# Unspooled Focus Audit — Per-Screen Analysis (2026-06-03)

Full pass of every screen's focus handling in `com.unspooled.app.ui.*`.

## Audit Summary

| Screen | FocusProperties | FocusRequester | Pattern | Grade |
|--------|:---:|:---:|---------|:-----:|
| UnspooledHomeScreen | ✅ | ✅ | focusGroup+focusRestorer+LazyRow, per-card left/right | **A** |
| DebridStreamTopBar | ✅ | ✅ | List(N) requesters, left/right/cancel, down=content | **A** |
| GlassSidebar (active) | ✅ | ✅ | Map<String, FR>, left=Cancel, right=content | **A** |
| UnifiedSidebar (dead) | ✅ | ✅ | Map<String, FR> via rememberSidebarFocusRequesters — 0 callers | **N/A** |
| SettingsHubScreen | ✅ | ✅ | Map<String, FR>, left=Cancel, up/down=adjacent | **A** |
| ProfileSelectScreen | ✅ | ✅ | List(N) requesters, left/right/cancel, down=manage | **A** |
| SourceFilterRow (NuvioButtons) | ✅ | ✅ | List(N) requesters, left/right/cancel, down=cards | **A** |
| ResolverOverlay | ⚠️ | ✅ | Has focusProperties on stream cards, but UP from first/DOWN from last rely on auto | **B** |
| PosterOptionsDialog | ❌ | ✅ | No focusProperties — vertical stack, auto-focus fine for simple list | **B** |
| DetailsScreen | ❌ | ❌ | **701 lines, zero focusProperties, zero focusRequester** | **D** |
| PlayerScreen | ❌ | ⚠️ | Has focusRequester for overlay, zero focusProperties for transport buttons | **D** |
| ModernHomeContent FilterChips | ❌ | ❌ | `.focusable()` with zero focusProperties, no down link — **focus trap** | **F** |
| PlayerEnhancements (skip/intro/next-ep) | ❌ | ❌ | Prompts have no directional wiring | **D** |
| NetflixHomeScreen FilterChips | ⚠️ | ⚠️ | Has up/down wiring but no per-chip left/right between chips | **C** |

## FocusRegistry Usage

**Zero callers.** Imported in `UnspooledHomeScreen.kt` (lines 77, 79) but never instantiated.

All 3 features unused:
- Keyed FocusRequester factory → screens replicate with `List`/`MutableMap`
- Row scroll tracking (`saveRowScroll`/`getRowScroll`) → no screen calls them
- `removeMissing()` cleanup → no screen calls it

## 🔴 Cross-Reference Dead Code (2026-06-02 complete audit)

**16 dead components (~1,027 lines) found via caller-count verification:**
`FocusLinksBuilder` (all 4 functions), `UnifiedSidebar`, `SidebarFocusRequesters`, `ContentFocusEntry`, `ModalFocusHost`, `TvFocusRequester`, `DpadKeyHandler`, `PlaySoundEffect`, `rememberSettledFocus`, `rememberSettledFocusState`, `FocusRestorer` effects, `tvFocusableCard`, `handleTvDirectionalKeys`, `rememberFocusRestorationState`, `focusFirstSidebarItem`, `NavMenuData`.

See `unspooled-tv-development` skill `references/2026-06-complete-focus-infrastructure-audit.md` for the full cross-referenced inventory with line counts.

## Working Patterns (do NOT break these)

1. **Home screen catalog rows:** `focusGroup()` + `focusRestorer()` + per-card `focusProperties{left/right}` — tested and working
2. **Top bar:** per-tab `FocusRequester` + `focusProperties{left/right/up=Cancel/down=content}` — tested and working
3. **Profile select:** `List(FocusRequester)` + per-card `focusProperties{left/right/up=Cancel/down=manageButton}` — working
4. **Settings left pane:** `Map<String, FR>` + per-category `focusProperties{left=Cancel/up/down}` — working

## Actual Bugs (priority ordered)

1. **DetailsScreen** — 701 lines, zero `focusProperties`. Stream cards, season items, cast row, related titles ALL rely on Compose auto-focus. On TV, misroutes when row lengths differ or items have different dimensions.

2. **ModernHomeContent SourceFilterChipsRow** — `.focusable()` with no `focusProperties`. No down link to content rows. Focus gets trapped in filter chips, cannot reach catalog cards below.

3. **PlayerScreen overlay controls** — Play, RW, FF, Skip, Audio, Subtitle buttons have no directional wiring. After overlay show/hide, Compose LRU search may pick wrong button.

4. **No scroll restoration anywhere** — navigate away and back loses scroll position. FocusRegistry has `saveRowScroll()`/`getRowScroll()` but nothing calls them. Current inline `MutableMap` pattern also lacks save calls. Fix is independent of FocusRegistry — just call save in `LaunchedEffect(scrollState.firstVisibleItemIndex)`.

## Decision: FocusRegistry not urgently needed

The existing inline patterns (`List(N)`, `MutableMap`) handle keyed requesters fine. The real bugs above are missing `focusProperties` calls — adding a registry wouldn't fix any of them. Integrate FocusRegistry when scroll restoration becomes a priority (its `saveRowScroll`/`getRowScroll` API is well-designed for it).
