# NuvioTV Porting & Settings Wiring Reference (2026-05-28)

## NuvioTV → UNSPOOLED Porting Rules

### DO NOT blindly copy files
NuvioTV dependency chains run deep. Direct copies produce uncompilable code because of:
- `NuvioColors` → `NexColors` (different field names, PascalCase convention)
- `R.string.*` resources → inline text strings (UNSPOOLED has different resource set)
- `AuthManager` → `TraktAuthManager` (different API surface)
- `ProfileManager` → `PreferencesManager` (profile-scoped vs flat)
- `Supabase` integrations → must be replaced with local equivalents
- `ExperienceMode`, `AppFeaturePolicy` → no UNSPOOLED equivalents

### Correct workflow
1. Read the NuvioTV source to understand WHAT it does
2. Identify dependencies that need replacement
3. Write a clean UNSPOOLED version from scratch using NuvioTV as reference
4. Build after EACH file — catch dependency issues early

### Dependency mapping
| NuvioTV | UNSPOOLED |
|---|---|
| `NuvioColors.Secondary` | `NexColors.TextPrimary` |
| `NuvioColors.TextSecondary` | `NexColors.TextSecondary` |
| `NuvioColors.TextTertiary` | `NexColors.TextTertiary` |
| `NuvioColors.Surface` | `NexColors.Surface` |
| `NuvioTV` theme colors | `NexColors` (PascalCase!) |
| `R.string.*` | Inline text strings |
| `AuthManager` | `TraktAuthManager` |
| `ProfileManager` | `PreferencesManager` or `ProfileViewModel` |
| `PluginManager` | `ProviderRegistry` + `AddonRepository` |
| `Supabase` APIs | NexStream catalog worker or TMDB direct |

## Settings Screen Wiring Checklist

Adding a new settings screen requires exactly 4 changes in 4 files:

### 1. Screen.kt — add route
```kotlin
data object XxxSettings : Screen("settings/xxx")
```

### 2. NexStreamNavHost.kt — add composable + import
```kotlin
import com.nexstream.app.ui.screens.settings.XxxSettingsScreen

composable(
    route = Screen.XxxSettings.route,
    enterTransition = { enterTransition },
    exitTransition = { exitTransition },
) {
    XxxSettingsScreen(navController = navController)
}
```

### 3. SettingsScreen.kt — add rail item
```kotlin
RailItem("Label", Icons.Default.IconName, Screen.XxxSettings.route)
```

### 4. XxxSettingsScreen.kt — write screen
```kotlin
@Composable
fun XxxSettingsScreen(
    navController: NavHostController? = null,
) {
    // Screen content here
}
```

Screen signature MUST accept `navController: NavHostController? = null` to match the NavHost call pattern.

## NexColors Naming Convention

NexColors uses **PascalCase** (not camelCase):
- `NexColors.TextPrimary`, `NexColors.TextSecondary`, `NexColors.TextTertiary`
- `NexColors.Surface`, `NexColors.SurfaceHigh`, `NexColors.SurfaceTop`
- `NexColors.Accent`, `NexColors.Success`, `NexColors.Warning`, `NexColors.Error`
- `NexColors.Background`, `NexColors.Gold`, `NexColors.Debrid`

## Force-Unwrap Elimination

### Correct search pattern
The pattern `!!\\.` (force-unwrap followed by dot) misses `!!,` `!!)` and `!! ` patterns.
Use: `grep -rn '!!' --include='*.kt' app/src/main/` then filter false positives manually.

### Safe replacement patterns (ranked)
1. `?.let { }` — for block-level replacement
2. `?: return` / `?: return@label` — for guard-clause exits
3. `requireNotNull(x)` — when isNullOrBlank() proves non-null but Kotlin can't smart-cast
4. Local `val` + null check — `val member = selectedCastMember; if (member != null) { ... }`
5. `?: error("unreachable")` — compile-time safety net for provably non-null paths

### Common scenario: when-isNullOrBlank-no-smartcast
```kotlin
// BROKEN — isNullOrBlank() doesn't enable smart-cast
val url = when {
    !stream.debridUrl.isNullOrBlank() -> stream.debridUrl  // still String?
}
// FIXED — requireNotNull compiles with String return
val url: String = when {
    !stream.debridUrl.isNullOrBlank() -> requireNotNull(stream.debridUrl)
}
```

### Common scenario: var property with if-null guard
```kotlin
// BROKEN — var property, smart-cast doesn't work
if (selectedCastMember != null) {
    val member = selectedCastMember!!  // force-unwrap
}
// FIXED — local val captures snapshot, enables smart-cast
val member = selectedCastMember
if (member != null) {
    // member is smart-cast to non-null
}
```

## What Was NOT Ported (and Why)

| Feature | Files | Reason |
|---|---|---|
| Sync orchestration | 16 | Supabase push/pull — replaced by NexStream catalog worker |
| MDBList settings | 2 | NuvioTV-specific metadata source |
| Experience mode | 2 | NuvioTV-specific feature flags |
| Supporters/Contributors | 2 | Community-specific |
| Backup/restore | ~35 refs | Deep Supabase + ProfileManager dependency |
| Layout settings | 1192 lines | NuvioTV-specific LayoutPreview composables |
| Playback sub-sections | 3 files, ~2000 lines | Coupled to NuvioTV's mpv/libvlc options |
| Account screen | 9 files | Auth flow adaptation incomplete |
