# Hotkey Presets Feature

This document provides comprehensive information about the Hotkey Presets feature in MPV.Rocks Installer.

## Overview

The Hotkey Presets feature lets users browse the default MPV keyboard shortcuts and apply complete keybinding profiles (presets) with one click. It is available in both the Web UI and the TUI, and manages the user's `input.conf` with automatic timestamped backups and atomic writes.

## Table of Contents

- [Feature Description](#feature-description)
- [Preset Profiles](#preset-profiles)
- [TUI Implementation](#tui-implementation)
- [Web UI Implementation](#web-ui-implementation)
- [API Reference](#api-reference)
- [Data Structures](#data-structures)
- [input.conf Management](#inputconf-management)
- [Testing](#testing)

---

## Feature Description

### Purpose

MPV keybindings live in `input.conf` (next to `mpv.conf`). Editing it by hand is error-prone, and users coming from other players often want familiar bindings. This feature ships ready-made keybinding profiles and applies them safely, always backing up the previous `input.conf` first.

### Key Features

**Shortcut Browser:**
- 120 default MPV shortcuts across 10 categories (Playback, Seeking, Audio & Volume, Subtitles, Video & Display, Playlist, Screenshots, Advanced, Menu Navigation, Multimedia Keys)
- Priority-sorted within each category (most common first)
- Search across keys, description, category, and aliases (Web UI and TUI)

**Preset Profiles:**
- Three presets: mpv Default, VLC, MPC-HC
- Embedded in the binary; applied with one click
- Active preset auto-detected by comparing `input.conf` against each preset
- Custom (non-preset) configurations detected and reported

**Safe Writes:**
- Timestamped backup of any existing `input.conf` before every write
- Atomic write (temp file + rename) — never a partial file
- Reset to mpv defaults at any time

---

## Preset Profiles

Presets are embedded at build time and loaded via `assets.ReadHotkeyPreset()`.

| ID | Name | Description | Embedded source |
|----|------|-------------|-----------------|
| `default` | mpv Default | Standard mpv keybindings as shipped upstream | `internal/assets/hotkeys/default.conf` |
| `vlc` | VLC | Keybindings modeled after VLC media player | `internal/assets/hotkeys/vlc.conf` |
| `mpc-hc` | MPC-HC | Keybindings modeled after Media Player Classic Home Cinema | `internal/assets/hotkeys/mpc-hc.conf` |

`hotkeys.GetPresetList()` returns this list; `hotkeys.LoadPreset(id)` returns the raw preset content.

---

## TUI Implementation

### States

Defined in `pkg/tui/models.go`:
- `StateHotkeysPreset` - Keybinding Presets selection list (entry point)
- `StateHotkeysCategory` - Shortcut category list
- `StateHotkeys` - Shortcuts within a category (supports `/` search)

### Screens

**Keybinding Presets** (`pkg/tui/hotkeys.go`, `hotkeysPresetListView`):
- Lists all presets from `hotkeys.GetPresetList()`
- "View All Shortcuts" entry opens the category browser
- Applying a preset shows a ✓/✗ status message (`presetStatusMessage`)

**Keyboard Shortcuts browser** (`hotkeysCategoryListView`, `hotkeysListView`):
- Category list, then priority-sorted shortcuts per category
- `/` opens search filtering over keys, descriptions, and aliases

---

## Web UI Implementation

### Pages and Routes

- `GET /hotkeys` - Hotkeys page (`handleHotkeys` in `pkg/web/server.go`, template `internal/webassets/templates/hotkeys.html`)
  - Preset cards with Apply button and active-preset badge
  - Searchable, category-filterable browser of all shortcuts
  - Reset-to-defaults action

### API Endpoints

| Endpoint | Method | Handler | Purpose |
|----------|--------|---------|---------|
| `/api/hotkeys/presets` | GET | `handleHotkeyPresetsAPI` | List presets + active preset |
| `/api/hotkeys/apply` | POST | `handleHotkeyApplyPresetAPI` | Apply a preset |
| `/api/hotkeys/reset` | POST | `handleHotkeyResetAPI` | Reset to mpv defaults |
| `/api/hotkeys/current` | GET | `handleHotkeyCurrentAPI` | Current bindings + custom flag |

All endpoints sit behind the `/api/` auth middleware (token cookie required); mutating endpoints are POST-only.

---

## API Reference

### GET /api/hotkeys/presets

**Response:**
```json
{
  "presets": [
    {"ID": "default", "Name": "mpv Default", "Description": "Standard mpv keybindings as shipped upstream"}
  ],
  "active_preset": "default"
}
```

`active_preset` is the preset whose content exactly matches the current `input.conf`. It is `""` for a custom (non-preset) config, and `"default"` when no `input.conf` exists (mpv then uses its built-in defaults).

### POST /api/hotkeys/apply

**Request:**
```json
{"preset": "vlc"}
```

The preset name is validated with `ValidateHotkeyPreset()` (`pkg/web/validation.go`).

**Response:**
```json
{"success": true, "message": "VLC keybindings applied successfully"}
```

### POST /api/hotkeys/reset

Applies the `default` preset (equivalent to `apply` with `{"preset": "default"}`).

**Response:**
```json
{"success": true, "message": "Keybindings reset to mpv defaults"}
```

### GET /api/hotkeys/current

**Response:**
```json
{
  "bindings": [{"key": "SPACE", "command": "cycle pause", "comment": ""}],
  "has_custom": false,
  "binding_count": 107
}
```

`has_custom` is true when the current `input.conf` matches no shipped preset. (`binding_count` above shows the `default` preset; it varies with the applied preset.)

---

## Data Structures

### PresetInfo (`pkg/hotkeys/inputconf.go`)

```go
type PresetInfo struct {
    ID          string // Machine identifier: "default", "vlc", "mpc-hc"
    Name        string // Display name: "mpv Default", "VLC", "MPC-HC"
    Description string // Short description of the preset
}
```

### Hotkey (`pkg/hotkeys/hotkeys.go`)

```go
type Hotkey struct {
    ID          string   // Unique identifier
    Keys        string   // Key combination display (e.g., "SPACE / p")
    Description string   // What the shortcut does
    Category    string   // Category for filtering
    Priority    int      // Higher = shown first within category
    Aliases     []string // Search aliases
}
```

### Binding / InputConfig (`pkg/hotkeys/inputconf.go`)

Parsed representation of `input.conf`. Comment lines, section headers, and blank lines are preserved via `RawLine` for round-trip fidelity; `SetBinding`, `RemoveBinding`, and `FindBinding` operate on the parsed structure.

---

## input.conf Management

### Location

`hotkeys.GetInputConfPath()` places `input.conf` in the same directory as `mpv.conf`:
- Linux/macOS: `~/.config/mpv/input.conf`
- Windows: `C:\Users\<USER>\mpv\portable_config\input.conf`

### Apply / Reset Flow

1. `ApplyPreset(name, path)` loads the embedded preset content
2. The parent directory is created if missing
3. Any existing `input.conf` is backed up (see below)
4. Content is written atomically: temp file `input.conf.tmp`, then `os.Rename` (`writeFileAtomically`)

Reset uses the same flow with the `default` preset.

### Backup Behavior

- Before every write, an existing `input.conf` is copied to a timestamped sibling:
  `input.conf-input.conf.bak.YYYY-MM-DD_HHMMSS` (`createInputConfBackup`)
- A failed backup only logs a warning; the write still proceeds
- Backups accumulate over time; restore manually by copying a backup back over `input.conf`

---

## Testing

### Unit Tests

`pkg/hotkeys/inputconf_test.go` covers:
- `ParseInputConf` round-trip (bindings, comments, sections, blank lines)
- Backup creation and atomic write behavior
- Preset loading

```bash
go test -v ./pkg/hotkeys/
```

### Manual Testing

1. Open the Hotkeys page (Web UI) or Keybinding Presets (TUI)
2. Apply the VLC preset → verify `input.conf` was written and a `input.conf-input.conf.bak.*` backup of the old file exists
3. Re-open the page → VLC shows as the active preset
4. Edit `input.conf` by hand → the page reports a custom config
5. Reset → mpv default bindings are restored
