# MPV.Rocks Installer - Comprehensive Improvement Plan

**Generated:** February 2026  
**Go Version:** 1.26  
**Analyzed by:** GLM-5

---

## Session Summary (February 2026)

### Completed in This Session

| Item | Description | Files Modified |
|------|-------------|----------------|
| Go 1.26 Upgrade | Updated from 1.25.6, updated bubbles to v1.0.0 | `go.mod`, `go.sum` |
| Static TailwindCSS | 3MB CDN → 16KB static build | `tailwind.config.js`, templates, Makefile |
| XDG Support | Respects XDG_CONFIG_HOME and XDG_DATA_HOME | `pkg/constants/paths.go` |
| Config Location Fix | Installer config now in MPV config directory | `pkg/constants/paths.go`, tests |
| Favicon Fix | Fixed path from `icon-64.png` to `favicon.png` | `pkg/web/server.go`, all templates |
| Confirmation Dialogs | Added for uninstall actions | `modal.js`, `install.js` |
| Debug Mode Fix | Set `debugMode: false` in languages.js | `languages.js` |
| Language Selector Fixes | Checkmarks, spacing, selected display | `languages.html`, `languages.js` |
| **Non-Blocking Jobs** | Background installation with real-time progress | `pkg/web/jobs.go`, `jobs.js`, `toast.js`, `sse.go` |
| **Toast Notifications** | Success/error notifications with view details | `toast.js`, `style.css` |
| **Tasks Page** | View active and completed jobs | `tasks.html` |
| **Auto-refresh Lists** | Installed apps/updates refresh on job complete | `jobs.js` |
| **Real-time Modal Updates** | Output modal updates as job progresses | `jobs.js` |

### Key Discoveries

1. **CSRF/CORS not needed** - Locally-run app, security boundary is local machine
2. **Global state is fine** - Single-user app, singleton pattern appropriate
3. **Boxicons conflict** - CSS overrides Tailwind's `hidden`, use `opacity` instead
4. **TailwindCSS safelist** - Must include dynamically-generated classes
5. **SSE for real-time updates** - Server-Sent Events work well for job progress streaming

---

## Executive Summary

This document outlines a comprehensive improvement plan for the MPV.Rocks Installer project based on deep analysis by GLM-5. The analysis covered:

- **Architecture & Code Quality**
- **Terminal UI (TUI)** 
- **Web UI**
- **Language Priority System**
- **Platform-Specific Code**
- **Go 1.26 Migration** ✅ (Completed)

### Overall Assessment

**Architecture Grade: B+ (Good)**

The project demonstrates strong fundamentals with:
- ✅ Clean package organization
- ✅ Excellent cross-platform support
- ✅ Strong security practices
- ✅ Comprehensive testing in key areas
- ✅ Minimal, well-chosen dependencies

**Key Areas for Improvement:**
- Global state usage affecting testability
- Large files needing refactoring
- Web UI performance (TailwindCSS via CDN)
- Security enhancements (CSRF, CORS)
- Accessibility compliance
- Test coverage gaps

---

## Go 1.26 Upgrade Summary ✅

### Completed Changes

| Change | Status |
|--------|--------|
| Go version updated to 1.26 | ✅ |
| `github.com/charmbracelet/bubbles` v0.21.0 → v1.0.0 | ✅ |
| Various transitive dependencies updated | ✅ |
| All tests passing | ✅ |

### Go 1.26 Features to Consider

1. **Green Tea Garbage Collector** - Now enabled by default, should improve performance
2. **`new()` with initial values** - Can simplify code patterns:
   ```go
   // Before
   x := int64(300)
   ptr := &x
   
   // After (Go 1.26+)
   ptr := new(int64(300))
   ```
3. **Generic self-references** - May enable simpler data structure implementations
4. **Experimental SIMD** (`simd/archsimd`) - Could accelerate GPU codec lookups
5. **`go fix` modernizers** - Run `go fix ./...` to modernize code

---

## Improvement Roadmap

### Phase 1: Critical & UX (Week 1-2) ✅ COMPLETED

**Priority: P0 - Must Do**

| ID | Task | Effort | Impact | Status |
|----|------|--------|--------|--------|
| P1 | Build TailwindCSS statically | 4h | Very High | ✅ DONE (16KB vs 3MB) |
| P2 | Add XDG environment variable support | 2h | Medium | ✅ DONE |
| A1 | Add ARIA landmarks to templates | 1h | High | ✅ DONE |
| A2 | Add focus management for modals | 2h | High | ✅ DONE |
| A3 | Add skip links | 30m | High | ✅ DONE |
| A4 | Add focus indicators | 30m | High | ✅ DONE |
| A5 | Add live regions for announcements | 30m | High | ✅ DONE |
| U1 | Add confirmation dialogs for destructive actions | 2h | Medium | ✅ DONE |
| U3 | Add loading states with spinners | 2h | Medium | ✅ DONE |
| FIX1 | Fix debugMode: true in production | 5m | Medium | ✅ DONE |
| FIX2 | Fix installer config file location | 1h | Medium | ✅ DONE (now in MPV dir) |
| FIX3 | Fix favicon path in templates | 30m | Low | ✅ DONE |

**Completed Items Details:**

1. **Static TailwindCSS Build (P1):**
   - Created `tailwind.config.js` with content paths
   - Added safelist for dynamically-generated classes (codec badge colors, state classes)
   - Built CSS minified to ~16KB (vs 3MB CDN)
   - Added `make tailwind` and `make tailwind-watch` targets

2. **XDG Support (P2):**
   - Added `getConfigHome()` and `getDataHome()` helper functions
   - Respects `XDG_CONFIG_HOME` and `XDG_DATA_HOME` environment variables
   - Updated all path functions to use XDG helpers

3. **Confirmation Dialogs (U1):**
   - Added confirmation for uninstall actions in Web UI
   - Uses modal system with Cancel/Confirm buttons

4. **Config Location Fix (FIX2):**
   - Installer config now stored in MPV config directory
   - Linux: `$XDG_CONFIG_HOME/mpv/mpv-manager.json`
   - macOS: `~/.config/mpv/mpv-manager.json`
   - Windows: `~/mpv/portable_config/mpv-manager.json`

**Key Discoveries:**
- CSRF/CORS not needed - locally-run app, security boundary is local machine
- Global config state is fine - single-user app, singleton pattern appropriate
- Boxicons CSS overrides Tailwind's `hidden` class - use `opacity-0/opacity-100` instead

### Phase 2: Performance & UX (Week 3-4)

**Priority: P1 - Should Do**

| ID | Task | Effort | Impact | Status |
|----|------|--------|--------|--------|
| P3 | Add non-blocking installation progress | 4h | High | ✅ DONE |
| U1 | Add drag-and-drop for language priority | 4h | Medium | ✅ DONE |
| U3 | Add loading states with spinners | 2h | Medium | ✅ DONE |

#### Drag-and-Drop for Language Priority (U1) ✅ DONE

**Completed:** February 2026

Added drag-and-drop reordering for audio and subtitle language priority lists:

**Features:**
- Drag handle with `bx-menu` icon on each list item
- Visual feedback during drag (50% opacity, 95% scale)
- Purple drop indicator showing where item will be placed
- Supports dragging to top, middle, or bottom of list
- Respects `prefers-reduced-motion` preference
- Existing up/down/remove buttons still work as fallback

**Files Modified:**
- `internal/webassets/static/languages.js` - Added drag handlers, updated renderPriorityList
- `internal/webassets/static/style.css` - Added drag-and-drop CSS classes

#### Non-Blocking Installation Progress (P3) ✅ DONE

**Completed:** February 2026

Implemented a complete job management system for non-blocking installations:

**Backend Components:**

| File | Lines | Purpose |
|------|-------|---------|
| `pkg/web/jobs.go` | ~450 | Job Manager with progress tracking, output history, error details |
| `pkg/web/sse.go` | ~175 | SSE streaming for job events |
| `pkg/web/api.go` | ~120 | Job API endpoints (install/job, uninstall/job, update/job) |

**Frontend Components:**

| File | Lines | Purpose |
|------|-------|---------|
| `internal/webassets/static/jobs.js` | ~850 | Job management, SSE connection, output viewer |
| `internal/webassets/static/toast.js` | ~250 | Toast notifications for job status |
| `internal/webassets/templates/tasks.html` | ~320 | Tasks page showing active/completed jobs |

**Features Implemented:**

1. **Job Manager System**
   - Thread-safe job tracking with `sync.RWMutex`
   - Progress parsing from output lines (auto-detects percentages)
   - Full output history with timestamps `[HH:MM:SS]`
   - Error details capture with helpful messages
   - Job history (last 10 completed jobs)
   - Duplicate job prevention

2. **SSE Streaming**
   - Real-time job events: `created`, `progress`, `output`, `status`, `complete`
   - Client disconnect handling
   - Optional job_id filtering

3. **API Endpoints**
   - `GET /api/jobs` - List active and recent jobs
   - `GET /api/jobs/{id}` - Get job details with full output
   - `DELETE /api/jobs/{id}` - Cancel an active job
   - `GET /api/jobs/stream` - SSE stream for real-time updates
   - `POST /api/install/job` - Start installation as background job
   - `POST /api/apps/update/job` - Start app update as background job
   - `POST /api/uninstall/job` - Start uninstallation as background job

4. **Frontend Features**
   - Toast notifications on job start/complete/error
   - Progress bars on app cards during installation
   - Expandable output viewer modal with full logs
   - Real-time modal updates as job progresses
   - Sidebar badge showing active tasks count
   - Tasks page with active/recent sections
   - Auto-refresh of installed apps/updates lists on job completion
   - Button state management (disabled with spinner during job)

**User Flow:**

```
User clicks "Install" on MPV card
        ↓
Toast shows "⏳ Installing MPV..." (clickable to view details)
        ↓
User can navigate anywhere while install runs
        ↓
SSE stream pushes real-time updates to UI
        ↓
On completion:
  - Toast shows "✅ MPV Installed" with "View Details" button
  - Installed Apps list auto-refreshes via HTMX
  - Available Updates list auto-refreshes via HTMX
  - Job archived to recent history
```

### Phase 3: Architecture & Code Quality (Week 5-6)

**Priority: P2 - Nice to Have**

| ID | Task | Effort | Impact | Files |
|----|------|--------|--------|-------|
| C1 | Add context support for cancellation | 4h | Medium | ✅ DONE |
| C2 | Extract interfaces for testability | 4h | Medium | ✅ DONE |
| C3 | Split large files (installer/*.go) | 6h | Medium | ✅ DONE |

#### Extract Interfaces for Testability (C2) ✅ DONE

**Completed:** February 2026

Created interfaces and mock implementations to enable unit testing without system dependencies.

**New Files:**

| File | Lines | Purpose |
|------|-------|---------|
| `pkg/installer/interfaces.go` | ~50 | Interface definitions |
| `pkg/installer/mocks.go` | ~400 | Mock implementations |
| `pkg/installer/interfaces_test.go` | ~450 | Tests for mocks (26 test cases) |

**Interfaces Defined:**
- `CommandExecutor` - Execute shell commands with context
- `FileSystem` - File system operations (read, write, copy, remove, etc.)
- `HTTPClient` - HTTP request execution
- `Downloader` - File download with progress
- `ArchiveExtractor` - Archive extraction
- `OutputWriter` - Output and error writing

**Mock Implementations:**
- `MockCommandExecutor` - Records commands, custom behavior, cancellation
- `MockFileSystem` - In-memory file system
- `MockHTTPClient` - Configurable responses
- `MockDownloader` - Tracks downloads
- `MockOutputWriter` - Records output/errors
- `MockArchiveExtractor` - Records extractions

**Tests Added:** 26 test cases for all mock implementations

#### Context Support for Cancellation (C1) ✅ DONE

**Completed:** February 2026

Added context support throughout the installer package to enable cancellation of long-running operations.

**Files Modified:**

| File | Changes |
|------|---------|
| `pkg/installer/command_runner.go` | Added ctx field, NewCommandRunnerWithContext(), exec.CommandContext |
| `pkg/installer/installer.go` | Added DownloadFileWithContext(), context-aware progress writer |
| `pkg/web/jobs.go` | Added ctx/cancel to Job struct, CancelJob() method |
| `pkg/installer/common_handler.go` | Updated ExecuteInstall/Uninstall to accept context |
| `pkg/web/api.go` | Pass job context to handlers, integrate CancelJob |
| `pkg/tui/models_messages.go` | Pass context.Background() to handler calls |

**Features:**
- Jobs can be cancelled via `DELETE /api/jobs/{id}`
- Context propagates: Job → CommandRunner → exec.CommandContext
- Partial downloads are cleaned up on cancellation
- Clear error messages for cancellation vs failure
- Backward compatible - existing functions still work

#### Split Large Files (C3) ✅ DONE

**Completed:** February 2026

Split the 5 largest installer files into focused modules:

| Original File | Before | After | New Files Created |
|---------------|--------|-------|-------------------|
| `windows.go` | 1,200 | 821 | `windows_shortcuts.go` (390) |
| `linux.go` | 1,080 | 740 | `linux_package.go` (364) |
| `installer.go` | 890 | 609 | `installer_archive.go` (304) |
| `macos.go` | 833 | 614 | `macos_appbundle.go` (230) |
| `common.go` | 709 | 398 | `common_handler.go` (318) |

**New Package Structure:**
```
pkg/installer/
├── installer.go           (609 lines) - Types, download, config, UOSC
├── installer_archive.go   (304 lines) - All archive extraction
├── windows.go             (821 lines) - Core Windows installer
├── windows_shortcuts.go   (390 lines) - Windows shortcut management
├── linux.go               (740 lines) - Core Linux installer
├── linux_package.go       (364 lines) - Package manager methods
├── macos.go               (614 lines) - Core macOS installer
├── macos_appbundle.go     (230 lines) - App bundle creation
├── common.go              (398 lines) - Types, paths, backup, helpers
└── common_handler.go      (318 lines) - InstallationHandler
```

**Results:**
- Largest file reduced from 1,200 to 821 lines (32% reduction)
- All files now under 850 lines
- No functionality changes - all tests pass
| C4 | Consolidate language data structures | 8h | Medium | `pkg/locale/`, `pkg/tui/`, `pkg/web/` |

**Note:** Global config state was considered but removed - the app is single-user and the global pattern is appropriate here.

### Phase 4: Testing & Documentation (Week 7-8)

**Priority: P2 - Nice to Have**

| ID | Task | Effort | Impact | Files |
|----|------|--------|--------|-------|
| T1 | Add TUI state transition tests | 8h | Medium | `pkg/tui/*_test.go` |
| T2 | Add platform detection edge case tests | 4h | Medium | `pkg/platform/*_test.go` |
| T3 | Add integration tests for install flow | 8h | Medium | New test files |
| D1 | Add godoc comments to public APIs | 4h | Low | Multiple packages |
| D2 | Document state machine transitions | 2h | Low | `pkg/tui/` |

---

## Detailed Improvement Areas

### 1. Performance Improvements

#### Static TailwindCSS Build (P1) ✅ DONE

**Completed:** February 2026

- Created `tailwind.config.js` with content paths for templates and JS files
- Added safelist for dynamically-generated classes (`bg-orange-600`, `bg-blue-600`, state classes)
- Built CSS minified to ~16KB (vs 3MB CDN - 187x improvement)
- Added `make tailwind` and `make tailwind-watch` targets
- Discovered: Boxicons CSS overrides Tailwind's `hidden` class - use `opacity-0/opacity-100` instead

**Files Modified:**
- `tailwind.config.js` - Configuration with safelist
- `internal/webassets/css/input.css` - TailwindCSS source
- `internal/webassets/static/css/tailwind.min.css` - Generated CSS (16KB)
- All HTML templates - Updated to use static CSS
- `package.json` - npm dependencies for TailwindCSS
- `Makefile` - Added tailwind build targets

#### XDG Environment Support (P2) ✅ DONE

**Completed:** February 2026

```go
// pkg/constants/paths.go - Implemented
func getConfigHome() string {
    if xdgConfig := os.Getenv("XDG_CONFIG_HOME"); xdgConfig != "" {
        return xdgConfig
    }
    homeDir, _ := os.UserHomeDir()
    return filepath.Join(homeDir, ".config")
}

func getDataHome() string {
    if xdgData := os.Getenv("XDG_DATA_HOME"); xdgData != "" {
        return xdgData
    }
    homeDir, _ := os.UserHomeDir()
    return filepath.Join(homeDir, ".local/share")
}
```

#### Loading States with Spinners (U3) ✅ DONE

**Completed:** February 2026

Added loading states and spinners to all async operations in the Web UI:

**JavaScript Button Spinners:**
- `config.html` - Apply Settings buttons (top and bottom)
- `hwaccel.html` - Apply Settings button
- `languages.js` - Save Preferences button

**Modal Confirm Button Spinners:**
- `modal.js` - Confirm button during executeModalAction()

**HTMX Button Spinners:**
- `apps.html` - File Associations Install/Remove buttons
- `logs.html` - Refresh button

**Implementation Patterns:**

1. **JavaScript fetch() pattern:**
```javascript
const button = document.getElementById('my-btn');
const originalContent = button.innerHTML;
button.disabled = true;
button.innerHTML = '<i class="bx bx-loader-alt bx-spin"></i> Loading...';

try {
    const response = await fetch('/api/endpoint', { ... });
    // ... handle response
} catch (error) {
    button.disabled = false;
    button.innerHTML = originalContent;
}
```

2. **HTMX pattern:**
```html
<button hx-post="/api/endpoint"
        hx-on::before-request="this.disabled = true; this.querySelector('i').classList.add('bx-spin')"
        hx-on::after-request="this.disabled = false; this.querySelector('i').classList.remove('bx-spin')">
    <i class="bx bx-refresh-cw"></i>
    Button Text
</button>
```

**Files Modified:**
- `internal/webassets/templates/config.html`
- `internal/webassets/templates/hwaccel.html`
- `internal/webassets/templates/apps.html`
- `internal/webassets/templates/logs.html`
- `internal/webassets/static/languages.js`
- `internal/webassets/static/modal.js`

### 3. Language System Improvements

#### Issues Identified

| Issue | Location | Fix |
|-------|----------|-----|
| Dead `saveLanguagePreferences()` function | `pkg/tui/language_preferences.go:823` | Remove or implement |
| Missing TUI retry logic | TUI language save | Add 3-retry pattern like Web UI |
| 5 different data structures for languages | Multiple files | Consolidate to 2-3 |
| TUI search state double-processing | `handleMajorLangUpdate()` | Fix Enter key handling |

#### Recommended Refactoring

```
Current:
  LocaleEntry → LanguageOption → majorLangItem → web.LanguageOption → JSON

Recommended:
  LocaleEntry → UnifiedLanguageItem (shared struct)
                      ↓
              TUI/Web adapters (convert to view-specific)
```

### 4. TUI Improvements

#### State Management

Current: 20+ states in single enum

**Recommendation:** Consider state pattern or state machine library

```go
// Current approach (hard to maintain)
type State int
const (
    StateMainMenu State = iota
    StateInstallMenu
    // ... 20+ more
)

// Alternative: State pattern
type AppState interface {
    Update(msg tea.Msg) (tea.Model, tea.Cmd)
    View() string
}
```

#### Missing Features

1. **Confirmation dialogs** for uninstall/config reset
2. **Loading spinners** for async operations
3. **Search** on all lists (not just languages)
4. **Undo capability** for config changes

### 5. Web UI Improvements

#### Accessibility (WCAG 2.1 Compliance)

```html
<!-- Add skip links -->
<a href="#main-content" class="sr-only">Skip to main content</a>

<!-- Add ARIA landmarks -->
<nav aria-label="Main navigation">...</nav>
<main id="main-content" role="main">...</main>

<!-- Add live regions for dynamic updates -->
<div aria-live="polite" id="status-announcer" class="sr-only"></div>

<!-- Add focus indicators -->
:focus-visible {
    outline: 2px solid var(--color-focus);
    outline-offset: 2px;
}
```

#### UX Improvements

1. **Non-blocking installation progress** ✅ DONE - Uses sidebar/toast instead of full-screen modal
2. **Drag-and-drop priority lists** - For language preferences
3. **Breadcrumb navigation** - Show location in app
4. **Collapsible sidebar** - For mobile responsiveness
5. **Global search** - Search across settings/logs

### 6. Platform-Specific Improvements

#### Missing Support

| Platform | Issue | Priority |
|----------|-------|----------|
| FreeBSD | Treated as Linux, may fail | Medium |
| SUSE | Detected but no zypper support | Medium |
| Alpine | No apk support | Low |
| Gentoo | No emerge support | Low |
| NixOS | No nix support | Low |

#### Windows CPU Detection

Current: Heuristic-based AVX2 detection

```go
// Current (unreliable)
if strings.Contains(cpuName, "Ryzen") || 
   strings.Contains(cpuName, "Core") {
    hasAVX2 = true
}

// Recommended: Use CPUID via CGO or helper executable
```

---

## Metrics & Success Criteria

### Before/After Targets

| Metric | Before | Current | Target |
|--------|--------|---------|--------|
| Test Coverage (pkg/web) | 98% | 98% | 95% (maintain) ✅ |
| Test Coverage (pkg/tui) | ~5% | ~5% | 60% |
| Test Coverage (overall) | ~75% | ~75% | 85% |
| Web UI First Load | ~3MB | ~16KB | <200KB ✅ |
| Lighthouse Accessibility | ~60% | ~70% | 90% |
| XDG Compliance | Partial | Full | Full ✅ |
| Debug Mode in Prod | Yes | No | No ✅ |
| Config Location | Wrong dir | MPV dir | MPV dir ✅ |
| Blocking Install UI | Yes | No | No ✅ |
| Real-time Job Progress | No | Yes | Yes ✅ |
| Job History | No | Yes (10) | Yes ✅ |

### Definition of Done

For each improvement:
- [ ] Code implemented
- [ ] Tests passing
- [ ] Documentation updated
- [ ] Code reviewed
- [ ] No regressions

---

## Appendix: File Reference

### Key Files by Size

| File | Lines | Improvement Priority |
|------|-------|---------------------|
| `pkg/installer/installer.go` | ~890 | Split into modules |
| `pkg/installer/linux.go` | ~1080 | Extract common patterns |
| `pkg/installer/windows.go` | ~1200 | Extract common patterns |
| `pkg/tui/models_update.go` | ~1200 | Split by state |
| `pkg/tui/language_preferences.go` | 1037 | Refactor duplicate code |
| `pkg/platform/gpu.go` | ~1100 | Move DB to external JSON |
| `pkg/web/api.go` | ~1500 | Already well-organized |

### Files Needing Tests

| File | Current Tests | Need |
|------|--------------|------|
| `pkg/tui/language_preferences.go` | 1 placeholder | 20+ tests |
| `pkg/tui/models_update.go` | 0 | 30+ tests |
| `pkg/platform/linux.go` | Basic | Edge cases |
| `pkg/platform/cpu.go` | Basic | Windows detection |

---

## References

- [Go 1.26 Release Notes](https://go.dev/blog/go1.26)
- [docs/ARCHITECTURE.md](ARCHITECTURE.md) - System architecture
- [docs/TESTING.md](TESTING.md) - Testing procedures
- [docs/FEATURES/LANGUAGE_PREFERENCES.md](FEATURES/LANGUAGE_PREFERENCES.md) - Language system docs
- [docs/FEATURES/GPU_CODEC_DETECTION.md](FEATURES/GPU_CODEC_DETECTION.md) - GPU detection docs
