# VJ Page Plan — `vj.html`

## Overview

Merge [`stream-ctrl.html`](../www/stream-ctrl.html) and [`music.html`](../www/music.html) into a single **5-column VJ dashboard** at [`vj.html`](../www/vj.html). The page is designed for wide monitors and serves as a live DJ streaming control center: manage the stream, interact with chat, and build/play music playlists — all in one view.

## Layout

```
┌──────────────┬──────────────┬──────────────┬──────────────┬──────────────┐
│   Column 1   │   Column 2   │   Column 3   │   Column 4   │   Column 5   │
│              │              │              │              │              │
│   STREAM     │   CHAT       │   PLAYLIST   │   SEARCH /   │   QUEUE      │
│   CARD       │              │   SIDEBAR    │   RESULTS    │   PANEL      │
│              │              │              │              │              │
│ - Video HLS  │ - Chat input │ - My lists   │ - Search bar │ - Queue list │
│ - Play/Pause │ - Chat feed  │ - Friend     │ - Track/     │ - Player bar │
│ - Mute       │   kind 1311  │   lists      │   album/     │ - Playback   │
│ - Status     │              │ - AI import  │   artist     │   mode       │
│ - Title      │              │ - Create new │   results    │              │
│ - Summary    │              │ - Viewer     │ - Playlist   │              │
│ - Image URL  │              │   pubkey     │   profile    │              │
│ - Stream URL │              │   loader     │   card       │              │
│ - Go Live    │              │              │              │              │
│ - End Stream │              │              │              │              │
│ - Save Plan  │              │              │              │              │
│ - Copy naddr │              │              │              │              │
│ - Viewer cnt │              │              │              │              │
└──────────────┴──────────────┴──────────────┴──────────────┴──────────────┘
```

### Column widths

| Column | Content | Width |
|--------|---------|-------|
| 1 | Stream card | `320px` fixed |
| 2 | Chat | `280px` fixed |
| 3 | Playlist sidebar | `280px` fixed — same as music.html |
| 4 | Search / results | `1fr` — fills remaining space |
| 5 | Queue panel | `300px` fixed — same as music.html |

## Base File

Copy [`music.html`](../www/music.html) → [`vj.html`](../www/vj.html) because it is the more complex page with ~5,333 lines of code including the full music player, playlist management, search, queue, drag-and-drop, AI import, and share features.

## Architecture Diagram

```mermaid
graph TD
    subgraph vj.html
        A[Column 1: Stream Card]
        B[Column 2: Chat]
        C[Column 3: Playlist Sidebar]
        D[Column 4: Search/Results]
        E[Column 5: Queue Panel]
    end

    subgraph From stream-ctrl.html
        S1[HLS Video Player]
        S2[Stream Metadata Inputs]
        S3[Stream Control Buttons]
        S4[Viewer Count Polling]
        S5[NIP-53 Chat - kind 1311]
        S6[Chat Composer]
        S7[Stream Event Subscriptions]
    end

    subgraph From music.html
        M1[Greyscale Music API]
        M2[SimplePlayer Audio]
        M3[Playlist CRUD + Nostr Publish]
        M4[Search - tracks/albums/artists]
        M5[Queue Management]
        M6[AI Import]
        M7[Share Modal]
    end

    S1 --> A
    S2 --> A
    S3 --> A
    S4 --> A
    S5 --> B
    S6 --> B
    S7 --> B

    M3 --> C
    M6 --> C
    M4 --> D
    M5 --> E
    M2 --> E
    M1 --> D
    M7 --> D
```

## Implementation Steps

### Step 1: Copy base file
- Copy `www/music.html` → `www/vj.html`
- Update `<title>` to `VJ`
- Update all log prefixes from `template.html` / `music.html` to `vj.html`

### Step 2: Add stream-ctrl dependencies
- Add `<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>` in `<head>`
- Add `<link rel="stylesheet" href="./css/post-composer.css" />` in `<head>`
- Add additional imports from `init-ndk.mjs` that stream-ctrl uses but music does not:
  - `subscribe` — already imported in music
  - `publishEvent` — already imported in music
  - `ndkFetchEvents`, `queryCache`, `storeProfile` — needed for post-interactions
- Add imports for `mountComposer` from `post-composer.mjs`
- Add imports for `initInteractions`, `renderPostItem` from `post-interactions.mjs`

### Step 3: Restructure HTML layout
- Rename `#musicApp` to `#vjApp` or wrap it in a new 5-column container
- Add Column 1 HTML before the existing music columns:
  ```html
  <aside id="vjStreamCard">
    <video id="videoStream" playsinline muted></video>
    <div id="divVideoControls">...</div>
    <input id="inputStreamTitle" ... />
    <textarea id="inputStreamSummary" ... />
    <input id="inputStreamingUrl" ... />
    <input id="inputStreamImage" ... />
    <div id="divStreamControls">
      <button id="btnSaveStream">Save Planned</button>
      <button id="btnGoLive">Go Live</button>
      <button id="btnEndStream">End Stream</button>
      <button id="btnCopyNaddr">Copy naddr</button>
    </div>
    <span id="spanStreamHealth">STATUS: OFFLINE</span>
    <span id="divViewerCount">TOTAL: — | HLS: — | RTMP: —</span>
  </aside>
  ```
- Add Column 2 HTML:
  ```html
  <aside id="vjChatPanel">
    <div class="musicSidebarTitle">CHAT</div>
    <div id="divChatComposer"></div>
    <div id="divChatFeed"></div>
  </aside>
  ```
- Columns 3-5 remain the existing `#musicSidebar`, `#musicMain`, `#musicQueuePanel`

### Step 4: Add CSS for 5-column layout
- Change `#vjApp` to use `display: flex; flex-direction: row; gap: 10px;`
- Column 1 `#vjStreamCard`: `width: 320px; min-width: 280px;`
- Column 2 `#vjChatPanel`: `width: 280px; min-width: 220px;`
- Columns 3-5 keep their existing widths from music.html
- Style the stream card inputs and buttons to match the existing music UI aesthetic
- Style the chat feed to scroll vertically within its column

### Step 5: Merge stream-ctrl JavaScript
Port these functions/state from stream-ctrl.html into the vj.html module script:

**State variables:**
- `STREAM_KIND`, `CHAT_KIND`, `DEFAULT_STREAM_D_TAG`
- `streamAuthorPubkey`, `streamDTag`, `streamCoordinate`, `latestStreamEvent`
- `streamSubId`, `chatSubId`, `chatComposer`, `chatComposerHostEl`
- `hlsInstance`, `streamListenersBound`, `renderedChatIds`, `chatParticipantPubkeys`
- `currentNaddr`, `currentStreamStatus`
- `STATS_URL`, `STATS_POLL_INTERVAL`, `viewerCount`, `statsIntervalId`, `wasStreamLive`

**Functions:**
- `getTagValue` — helper for reading event tags
- `parseCoordinate`, `parseStreamTargetFromUrl` — URL parsing
- `buildStreamTags`, `publishStreamStatus` — stream event publishing
- `setStreamPlayerSource` — HLS player management
- `buildNaddr`, `updateNaddrDisplay` — naddr encoding
- `updateStreamButtonStates`, `updateStreamUi` — UI state management
- `appendChatEvent` — render chat messages using `renderPostItem`
- `subscribeToStream`, `subscribeToChat` — Nostr subscriptions
- `pollViewerCount`, `startViewerCountPolling`, `stopViewerCountPolling` — stats polling
- `handleIncomingEvent`, `bindStreamEventListeners` — event routing
- `mountChatComposerIfAllowed` — chat input setup
- `initializeStreamPage` — stream page initialization + button wiring

### Step 6: Wire up initialization
In the `main()` IIFE, after `initMusicFeature()`:
- Call `initializeStreamPage()` to set up stream controls, subscriptions, and chat
- The stream event listener must coexist with the music NDK event listener — both listen on `window ndkEvent` but handle different event kinds

### Step 7: Handle shared concerns
- Both pages use `initInteractions` / `renderPostItem` — stream-ctrl needs these for chat rendering. Import them and call `initInteractions` once during auth setup.
- Both pages share the same footer, sidenav, hamburger, relay UI, blossom UI, AI section — these are already in the music.html base, no duplication needed.
- The `post-composer.css` stylesheet is needed for the chat composer styling.

### Step 8: Remove announcement composer
The user wants NIP-53 chat only in Column 2. Do not port the `divAnnouncementComposer` or `mountAnnouncementComposer` from stream-ctrl.html.

### Step 9: Header title
Replace the static header text with the stream title input from stream-ctrl.html, or keep it simple with a "VJ" title. The stream title input can live in Column 1 instead.

## Future Integration Ideas — not in scope for v1
- Auto-post now-playing: when a new song plays in the music queue, automatically publish a kind-1 note or update the stream title
- Update stream summary with current track info
- Visual effects / OBS integration
- Strudel live coding integration

## Key Technical Notes

1. **No ID conflicts**: stream-ctrl uses IDs like `videoStream`, `btnGoLive`, `divChatFeed` etc. Music uses IDs like `musicSearchInput`, `musicAudio`, `musicPlayerBar` etc. No overlaps.

2. **Two audio/video elements**: The page will have both `<video id="videoStream">` for the HLS stream preview and `<audio id="musicAudio">` for music playback. These are independent — the video is the stream monitor, the audio is the DJ music source.

3. **Event routing**: Both stream and music features listen on `window ndkEvent`. The stream handler checks for `kind === 30311` and `kind === 1311`. The music handler checks for `kind === 30004` playlist events. No conflicts.

4. **Auth mode**: Use `optional` auth mode from music.html since it supports public viewing with login-on-demand.
