# Music Player Page — Nostr-Based Music Search & Play

## Goal

Integrate the working greyscale PoC music search/playback into the client template system as `music.html`, matching the project's monochrome aesthetic (CSS variables, monospace fonts, grayscale images, `.btn` class, etc.).

## Architecture

```mermaid
flowchart TB
    subgraph www - music.html
        HTML[music.html - template-based page]
        STYLE[page-specific style block]
        INLINE[inline module script - glue code]
    end
    subgraph www/js - reusable modules
        API[js/greyscale-api.mjs - search + streaming API]
        PLAYER[js/greyscale-player.mjs - audio + dash.js playback]
        NDK[js/init-ndk.mjs - auth + worker]
    end
    subgraph External
        CDN[dash.js CDN]
        TIDAL[Tidal API instances]
        UPTIME[Uptime workers - instance list]
    end
    subgraph Shared
        CSS[css/client.css - global styles + variables]
        NOSTR[nostr.bundle.js + nostr-lite.js]
    end
    HTML --> CSS
    HTML --> NOSTR
    HTML --> CDN
    INLINE --> API
    INLINE --> PLAYER
    INLINE --> NDK
    API -->|fetch| UPTIME
    API -->|fetch| TIDAL
    PLAYER -->|uses| CDN
```

## Key Design Decisions

### 1. Copy api.js and player.js as `.mjs` modules into `www/js/`

The greyscale PoC files (`greyscale/greyscale-app/js/api.js` and `player.js`) are already self-contained ES modules with zero dependencies (except dash.js global for player). We copy them into `www/js/` as:

- `www/js/greyscale-api.mjs` — renamed to `.mjs` to match project convention
- `www/js/greyscale-player.mjs` — renamed to `.mjs` to match project convention

No code changes needed to these files — they export `GreyscaleAPI` and `SimplePlayer` classes that work as-is.

### 2. Use the template layout, not the greyscale layout

The greyscale PoC has its own `style.css` with a different dark theme. We discard that entirely and use:

- `client.css` global styles (CSS variables, `.btn` class, monospace fonts)
- Page-specific `<style>` block in `music.html` for music-specific elements
- All colors via CSS variables (`--primary-color`, `--secondary-color`, `--accent-color`, `--muted-color`, `--border-color`)
- No hardcoded colors

### 3. Player bar sits inside `#divBody`, not as a fixed footer

The template already has a fixed `#divFooter` for relay status. The music player bar will be positioned at the bottom of `#divBody` using flexbox, keeping the standard footer intact. The `#divBody` layout will be overridden to `flex-direction: column` with the search area scrollable and the player bar pinned at the bottom.

### 4. Search UI follows project conventions

- Search input uses `var(--font-family)`, `var(--border-color)`, etc.
- Search button uses the `.btn` class from `client.css`
- Results list uses `var(--border-color)` borders, `var(--muted-color)` for secondary text
- Cover art images get the standard grayscale filter from `client.css` (`img` rule already applies globally)

## Page Layout

```
┌─────────────────────────────────────────┐
│  ☰  [header]                            │  ← #divHeader (standard)
├─────────────────────────────────────────┤
│                                         │
│  [Search input...............] [Search] │  ← search form
│  Status: Ready.                         │  ← status text
│                                         │
│  ┌─────────────────────────────────┐    │
│  │ [img] Track Title        3:42   │    │  ← results list (scrollable)
│  │       Artist — Album            │    │
│  ├─────────────────────────────────┤    │
│  │ [img] Track Title        4:15   │    │
│  │       Artist — Album            │    │
│  └─────────────────────────────────┘    │
│                                         │
│─────────────────────────────────────────│
│ [img] Title    [◀][▶/❚❚][▶▶]          │  ← player bar (pinned bottom)
│       Artist   0:00 ═══════○═══ 3:42   │
├─────────────────────────────────────────┤
│  [relay dots]  [center]  [right] [sats] │  ← #divFooter (standard)
└─────────────────────────────────────────┘
```

## Implementation Steps

### Step 1: Copy greyscale modules to `www/js/`

Copy `greyscale/greyscale-app/js/api.js` → `www/js/greyscale-api.mjs`
Copy `greyscale/greyscale-app/js/player.js` → `www/js/greyscale-player.mjs`

No modifications needed — these are already clean ES modules.

### Step 2: Update `music.html` — Head section

- Change `<title>` from "TEMPLATE" to "Music"
- Add dash.js CDN script: `<script src="https://cdn.dashjs.org/latest/dash.all.min.js"></script>`

### Step 3: Update `music.html` — Add `<style>` block

Page-specific styles following the CSS placement rules:

- Override `#divBody` to `flex-direction: column; flex-wrap: nowrap;`
- Style the search form (`.music-search-form`) — grid layout with input + `.btn`
- Style the status text (`.music-status`)
- Style the results list (`.music-results`) — bordered list with hover states
- Style result items (`.music-result-item`) — grid: cover image + meta + duration
- Style the player bar (`.music-player-bar`) — pinned to bottom of `#divBody`
- Style player controls and progress bar
- All colors use CSS variables only
- Responsive: stack player bar vertically on narrow screens

### Step 4: Update `music.html` — Add HTML content in `#divBody`

Add inside `#divBody`:

1. **Search section** — form with input + `.btn` button + status paragraph
2. **Results section** — scrollable `<ul>` for track results
3. **Player bar** — cover image, track info, prev/play-pause/next buttons, progress slider with time displays
4. **Hidden audio element** — `<audio id="music-audio" preload="metadata"></audio>`

### Step 5: Update `music.html` — Add inline module script

Wire everything together in the existing `<script type="module">` block:

1. Import `GreyscaleAPI` from `./js/greyscale-api.mjs`
2. Import `SimplePlayer` from `./js/greyscale-player.mjs`
3. After NDK init completes, initialize the greyscale API (`api.initInstances()`)
4. Create `SimplePlayer` instance bound to audio element and progress controls
5. Set up search form submit handler → `api.searchTracks()` → render results
6. Set up result click handler → `player.setQueue()` + `player.playCurrent()`
7. Set up play/pause, next, prev button handlers
8. Set up `player.onTrackChanged` callback to update player bar metadata

This is essentially the same logic as `greyscale/greyscale-app/js/app.js` but inlined into the template's module script, alongside the existing NDK/hamburger/sidenav/footer code.

### Step 6: Test

- Verify search works
- Verify track playback works (both direct URL and DASH streams)
- Verify player controls (play/pause, next, prev, progress scrubbing)
- Verify light/dark mode theming
- Verify responsive layout on narrow screens
- Verify standard template features still work (hamburger, sidenav, relay status, logout)

## Files Changed

| File | Action | Description |
|------|--------|-------------|
| `www/js/greyscale-api.mjs` | **New** | Copy of `greyscale/greyscale-app/js/api.js` |
| `www/js/greyscale-player.mjs` | **New** | Copy of `greyscale/greyscale-app/js/player.js` |
| `www/music.html` | **Modified** | Add title, styles, HTML content, dash.js CDN, music glue code |
