# Focus Audit — June 2026 (SUPERSEDED)

**NOTE:** This file is superseded by `references/2026-06-complete-focus-infrastructure-audit.md` (2026-06-02) which contains a full cross-referenced audit of all 29 files across `ui/focus/`, `ui/sidebar/`, and `ui/navigation/` with verified caller counts for every public symbol. The status table below was written before cross-reference verification and contained several inaccuracies — components listed as "Active" that actually had zero callers.

Full-stack focus audit across all Unspooled screens. 4 concrete bugs found and fixed.

## FocusRegistry Status

Dead code. Imported in `UnspooledHomeScreen.kt` but never instantiated. Two other files reference it in doc-comments only. Provides: keyed FocusRequester factory, scroll tracking, cleanup. None of these are called. Current screens replicate the factory with inline `List(N) { FocusRequester() }` or `Map<String, FocusRequester>`.

Not integrating now — overhead without payoff. The missing scroll restoration was fixed at the composable level (see Scroll Restoration below).

## Focus Infrastructure Files (UPDATED — 2026-06-02 cross-reference audit)

| File | Status | Caller Count |
|------|--------|-------------|
| `ui/focus/FocusRegistry.kt` | Dead code | 0 callers |
| `ui/focus/FocusLinks.kt` | Active | Used inline by GlassSidebar, PremiumContentCard |
| `ui/focus/FocusLinksBuilder.kt` | **DEAD** (was marked "Active" in error) | 0 callers for all 4 functions |
| `ui/focus/FocusUtils.kt` | Active | `rememberTvFocusState()` used by sidebars |
| `ui/focus/SettledFocusDetector.kt` | **DEAD** (was marked "Active" in error) | 0 callers for `rememberSettledFocusState` |
| `ui/focus/FocusVelocityTracker.kt` | Active (class), dead (consumers) | Referenced by dead SettledFocus/SettledFocusDetector |
| `ui/focus/AppFocusMemory.kt` | Active | Used by settings screens (but uses deprecated FocusZone) |
| `ui/focus/ContentFocusEntry.kt` | **DEAD** (was marked "Active" in error) | 0 callers |
| `ui/focus/TvFocusRequester.kt` | **DEAD** (was marked "Active" in error) | 0 callers |
| `ui/focus/TvFocusZone.kt` | Active | 8-zone TvFocusZone + deprecated 6-zone FocusZone |

## Bug 1: DetailsScreen — Zero focusProperties

**701 lines, zero `focusProperties` calls.** Every interactive element relied on Compose auto-focus. On a real TV remote this misroutes focus when rows have different lengths.

### Fixes applied

- **BackButton**: Added `downRequester` parameter, wired to Play button
- **ActionButtonRow (Play→Watchlist→Trailer)**: Added per-button `FocusRequester`, Left/Right wiring between adjacent buttons, Up link to Back
- **StreamSection**: Added FocusRequesters for Best→Secondary→Free→SeeAll, sequential Up/Down wiring
- **PlayQualityButton**: Added optional `focusRequester`, `upRequester`, `downRequester` params, conditionally applied via `.then()`
- **SeeAllSourcesButton**: Added `focusRequester`, `upRequester` params
- **"Show more" button**: Added `focusProperties { up = FocusRequester.Default }`
- **MoreLikeThisSection**: Added `focusGroup()` + `focusRestorer()` on LazyRow

Files: `DetailsScreen.kt`, `StreamSection.kt`

## Bug 2: ModernHomeContent — Filter Chips Focus Trap

`SourceFilterChipsRow` used `.focusable()` on Box elements without `focusProperties`. Once user D-padded into filter chips, there was no way to move down into the catalog rows.

### Fix

- Added `downFocusRequester: FocusRequester?` parameter to `SourceFilterChipsRow`
- Wired `.focusProperties { down = downFocusRequester }` on the last chip
- Created `firstRowRequester` in `ModernHomeContent` to bridge filters → first catalog row

File: `ModernHomeContent.kt`

## Bug 3: PlayerScreen — Transport Controls No Directional Wiring

All 8 buttons in the bottom transport bar (⏪10, ⏯, ⏩10, CC, 🔊, ⚙, 📺, ⋮) were in a `Row` with zero `focusProperties`. Compose LRU focus search picks the wrong button after overlay show/hide.

### Fix

- Created 8 FocusRequesters per button
- Wired Left/Right sequentially: REW→PLAY→FF→CC→AUDIO→SOURCE→(EP)→MORE
- Episode picker (📺) dynamically adjusts the chain based on `isShow`
- Last button wired `right = FocusRequester.Cancel`
- Updated `SeekButton`, `PlayPauseButton`, `ControlIconButton` to accept optional `focusRequester`, `leftRequester`, `rightRequester` params

File: `PlayerControls.kt`

## Bug 4: No Per-Row Scroll Restoration

Home screen's `savedScrollPosition` only saved vertical LazyColumn position. Horizontal scroll within catalog rows was lost on back-navigation.

### Fix

- Added `rowScrollPositions: Map<String, Int>` to `HomeUiState`
- Added `HomeIntent.RowScrollChanged(rowKey, firstVisibleIndex)` intent
- `CatalogLazyRow` now uses explicit `rememberLazyListState()` + `LaunchedEffect(firstVisibleItemIndex)` to fire save
- `CatalogRow` passes `initialScrollIndex` from `uiState.rowScrollPositions[rowKey]`
- `HomeContent` gained `onRowScrollChanged` callback wired to the intent

Files: `HomeIntent.kt`, `HomeViewModel.kt`, `UnspooledHomeScreen.kt`

## Screens NOT Fixed (remaining work)

### NetflixHomeScreen SourceFilterChips
Has `up`/`down` wiring via parameters, but individual chips use `focusable()` without per-chip left/right. Compose auto-orders by layout position which usually works for horizontal rows — low risk, not fixed.

### ModernHomeContent ModernCatalogRow
Uses `LazyRow` without `focusGroup()` or `focusRestorer()`. Cards use `Card` (tv-material3) which manages focus internally. No `focusProperties` on individual cards. Low-moderate risk — works for simple rows but could fail with dynamic row lengths. Not fixed (scope decision).

### PlayerOverlayScaffold
No explicit `focusProperties` — relies on `onKeyEvent` for Back/Center. Fine for simple overlays (More menu, sheets). Low risk.

### ResolverOverlay
Has `focusProperties` on individual stream cards but edge-to-edge transitions (UP from first card, DOWN from last) rely on auto-focus. Moderate risk if the source list has more cards than visible. Not fixed (scope decision).

## Build Verification

```bash
BUILD SUCCESSFUL in 18s
44 actionable tasks: 8 executed, 36 up-to-date
65M app-debug.apk
Zero banned patterns (verified via grep)
```
