# VJ Page: Auto-Announce Save Button & Real-Time Playlist View

## Problem Analysis

### Issue 1: Auto-Announce Settings Keep Reverting

**Root cause:** The auto-announce settings (toggle, template, URL base) use a dual-save mechanism that causes a race condition:

1. **On every input/change event**, the values are saved to `localStorage` immediately AND queued for remote save via `queueAutoAnnounceSettingsPatch()`.
2. `queueAutoAnnounceSettingsPatch()` has a **15-second debounce** (`AUTO_ANNOUNCE_SAVE_DEBOUNCE_MS = 15000`) before it calls `queueVjSettingsPatch()`, which itself has a 300ms debounce before calling `patchUserSettings()`.
3. Meanwhile, `onUserSettings()` fires whenever settings arrive from the relay/worker (including from other tabs or stale relay data). When it fires, `applyVjPageSettingsFromState()` **overwrites all form fields** with whatever the relay has — which may be the OLD values if the 15-second debounce hasn't flushed yet.

**The revert flow:**
- User changes template → localStorage updated, 15s timer starts
- Before 15s elapses, `onUserSettings` fires with stale relay data → `applyVjPageSettingsFromState()` overwrites the input fields back to old values
- The pending patch may still fire, but it patches the OLD values that were already overwritten

**Solution:** Remove auto-saving for auto-announce settings entirely. Add an explicit "Save" button. Only persist when the user clicks Save.

### Issue 2: No Real-Time Playlist View During Stream

**Root cause:** When a VJ goes live and songs are announced, `appendNowPlayingToPlaylist()` in `vj-stream.mjs` adds tracks to an in-memory `playlistTracks` array and publishes a Nostr event (kind 30078). However:

1. There is **no UI in vj.html** that displays the current episode's playlist tracks as they build up.
2. The `vj-playlist.html` page shows historical playlists but requires a page load/refresh — it does not subscribe to real-time updates.
3. The `playlistTracks` array in `vj-stream.mjs` is internal and not exposed to the UI.

**Solution:** Add a real-time playlist panel in the VJ stream card area that shows tracks as they are added during a live stream, with the ability to edit (remove/reorder) tracks.

---

## Implementation Plan

### Task 1: Add Save Button for Auto-Announce Settings

**File: `www/vj.html`**

#### 1a. Add Save button to HTML (around line 1557)
- Add a `<button id="btnSaveAutoAnnounce" class="btn" type="button">Save</button>` after the template textarea inside `#divAutoAnnounceCard`.

#### 1b. Remove auto-save from input event listeners (around lines 6130-6171)
- **`autoAnnounceSongToggle` change listener** (line 6131): Remove the call to `queueAutoAnnounceSettingsPatch()`. Keep the `localStorage` write for immediate local persistence.
- **`inputAutoAnnounceTemplate` input listener** (line 6152): Remove the call to `queueAutoAnnounceSettingsPatch()`. Keep the `localStorage` write.
- **`inputAutoAnnounceUrlBase` input listener** (line 6162): Remove the call to `queueAutoAnnounceSettingsPatch()`. Keep the `localStorage` write.

#### 1c. Add Save button click handler (after line 6171)
- On click of `btnSaveAutoAnnounce`:
  - Read current values from the three inputs (toggle, template, URL base)
  - Call `queueVjSettingsPatch()` directly (bypassing the 15s debounce) with the full `autoAnnounce` object:
    ```js
    queueVjSettingsPatch({
      autoAnnounce: {
        songEnabled: autoAnnounceSongToggle?.checked || false,
        template: String(inputAutoAnnounceTemplate?.value || '').trim() || DEFAULT_AUTO_ANNOUNCE_TEMPLATE,
        urlBase: String(inputAutoAnnounceUrlBase?.value || '').trim() || DEFAULT_AUTO_ANNOUNCE_URL_BASE,
      },
    });
    ```
  - Show brief visual feedback (e.g., change button text to "Saved!" for 1.5s)

#### 1d. Guard `applyVjPageSettingsFromState` against overwriting dirty auto-announce fields
- In `applyVjPageSettingsFromState()` (lines 2155-2176), add a check: if the auto-announce values from settings match what is already in localStorage (the user's latest local edits), apply them. Otherwise, prefer the localStorage values. This prevents relay-delivered stale settings from overwriting unsaved local changes.
- Alternatively (simpler): skip applying auto-announce fields from remote settings entirely — only apply them on initial page load. After that, the Save button is the only way to sync.

#### 1e. Remove `queueAutoAnnounceSettingsPatch` function and `autoAnnouncePatchTimer` variable
- These are no longer needed since we removed the debounced auto-save.
- Remove `AUTO_ANNOUNCE_SAVE_DEBOUNCE_MS` constant (line 2008).
- Remove `autoAnnouncePatchTimer` variable (line 2028).
- Remove `queueAutoAnnounceSettingsPatch` function (lines 2134-2143).

---

### Task 2: Real-Time Playlist View During Stream

**File: `www/js/vj-stream.mjs`**

#### 2a. Expose playlist tracks and add render callback
- Add an `onPlaylistChange` callback option to `initVjStreamPanel()` config.
- After every call to `playlistTracks.push(...)` in `appendNowPlayingToPlaylist()` (line 879), call the callback with the current tracks array.
- Also call it when tracks are cleared (go live, create show, etc.).
- Expose methods: `getPlaylistTracks()`, `removePlaylistTrack(index)`, `reorderPlaylistTrack(fromIndex, toIndex)` on the returned panel object.

#### 2b. Add playlist display section to HTML

**File: `www/vj.html`**

- Add a new section inside `#vjStreamCard` (after the auto-announce card, around line 1558), or as a collapsible section:
  ```html
  <div id="divEpisodePlaylist" class="divPostItem streamSection">
    <div class="musicSidebarTitle">EPISODE PLAYLIST</div>
    <div id="divEpisodePlaylistStatus" class="musicPlaylistMeta">No tracks yet.</div>
    <ul id="ulEpisodePlaylistTracks" class="musicPlaylistList"></ul>
  </div>
  ```

#### 2c. Render playlist tracks in real-time

**File: `www/vj.html`**

- Implement `renderEpisodePlaylist(tracks)` function that:
  - Clears and re-renders `#ulEpisodePlaylistTracks` with the current track list
  - Each item shows: track number, artist, title, timestamp
  - Each item has a remove button (X) and is draggable for reorder
  - Updates `#divEpisodePlaylistStatus` with track count

#### 2d. Wire up the callback

**File: `www/vj.html`**

- Pass `onPlaylistChange: renderEpisodePlaylist` in the `initVjStreamPanel()` config (around line 6093).

#### 2e. Enable editing (remove/reorder)

**File: `www/js/vj-stream.mjs`**

- `removePlaylistTrack(index)`: splice from `playlistTracks`, re-number remaining tracks, call `publishPlaylistEvent('live')`, call `onPlaylistChange`.
- `reorderPlaylistTrack(fromIndex, toIndex)`: reorder in `playlistTracks`, re-number, call `publishPlaylistEvent('live')`, call `onPlaylistChange`.

**File: `www/vj.html`**

- Wire remove button clicks to call `vjStreamPanel.removePlaylistTrack(index)`.
- Wire drag-and-drop reorder to call `vjStreamPanel.reorderPlaylistTrack(from, to)`.

#### 2f. Add minimal CSS for the episode playlist

**File: `www/vj.html`**

- Style `#divEpisodePlaylist`, `#ulEpisodePlaylistTracks` items with track number, artist/title, time, and remove button.
- Reuse existing `.musicPlaylistList` and `.musicQueueItem` patterns for consistency.

---

## Summary of Files to Modify

| File | Changes |
|------|---------|
| `www/vj.html` | Add Save button HTML, remove auto-save listeners, add save handler, add episode playlist section, render function, CSS |
| `www/js/vj-stream.mjs` | Expose playlist tracks, add callback, add remove/reorder methods, return new methods from panel |
