<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>OPHI-118 / Music Box widget</title>
<link rel="stylesheet" href="{{ asset('css/hud.css') }}">
<style>
  /* Widget is authored at 549×880 (bounds_type 0, no stretch). */
  html, body {
    background: var(--term-bg);
    color: var(--term-fg);
    font-family: var(--mono);
  }
  body {
    position: relative;
    display: flex;
    flex-direction: column;
    border: 1.5px solid var(--frame);
    box-shadow:
      0 0 18px var(--frame-glow),
      inset 0 0 50px rgba(0, 30, 0, 0.55);
    text-shadow: 0 0 4px var(--term-glow);
  }
  .corner {
    position: absolute;
    width: 14px; height: 14px;
    border-color: var(--frame);
    border-style: solid;
    border-width: 0;
  }
  .corner.tl { top: 4px;    left: 4px;   border-top-width: 2px;    border-left-width: 2px;  }
  .corner.tr { top: 4px;    right: 4px;  border-top-width: 2px;    border-right-width: 2px; }
  .corner.bl { bottom: 4px; left: 4px;   border-bottom-width: 2px; border-left-width: 2px;  }
  .corner.br { bottom: 4px; right: 4px;  border-bottom-width: 2px; border-right-width: 2px; }
  .header {
    display: flex; justify-content: space-between; align-items: center;
    padding: 14px 22px 10px;
    font-size: 13px; letter-spacing: 0.22em;
    color: var(--hud);
    border-bottom: 1px solid rgba(79, 210, 255, 0.22);
    flex: 0 0 auto;
  }
  .header .left  { display: inline-flex; gap: 10px; align-items: center; }
  .header .right { color: var(--hud-dim); font-size: 13px; letter-spacing: 0.12em; }
  .terminal {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    padding: 12px 22px 16px;
    font-size: 16px;
    line-height: 1.5;
    color: var(--term-fg);
    border: 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
  }
  .terminal::after { display: none; }
  .scanlines { opacity: 0.45; }
  .np-line { display: flex; gap: 10px; align-items: baseline; }
  .np-line .np-arrow { flex: 0 0 auto; margin-right: 0; }
  .np-line .np-title {
    flex: 1 1 auto;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
  }
  .np-line .np-time { flex: 0 0 auto; font-size: 14px; margin-left: 0; }
  .next-line { display: flex; gap: 8px; align-items: baseline; font-size: 14px; }
  .next-line .next-arrow { flex: 0 0 auto; }
  .next-line .next-label { flex: 0 0 auto; }
  .next-line .next-title {
    flex: 1 1 auto;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
  }
  .chat-line {
    font-size: 13px;
    color: var(--term-fg-dim);
    letter-spacing: 0.06em;
    border-top: 1px solid rgba(95, 220, 98, 0.18);
    padding-top: 8px;
    margin-top: 6px;
  }
  .chat-line .cmd { color: var(--term-fg-bright); }
  .pin-spacer { height: 10px; }
</style>
</head>
<body>
<span class="corner tl"></span>
<span class="corner tr"></span>
<span class="corner bl"></span>
<span class="corner br"></span>

<div class="header">
  <span class="left">NOW PLAYING</span>
  <span class="right">CH 118.0 // MUSIC</span>
</div>

<div class="terminal">
  <div id="termOutput"><div id="pinnedBlock"><div class="pin-spacer"></div><div class="term-line next-line"><span class="next-arrow">↳</span><span class="next-label">next:</span><span class="next-title empty" data-slot="0">— end of queue —</span></div><div class="term-line next-line"><span class="next-arrow">↳</span><span class="next-label">then:</span><span class="next-title empty" data-slot="1">—</span></div><div class="term-line next-line"><span class="next-arrow">↳</span><span class="next-label">then:</span><span class="next-title empty" data-slot="2">—</span></div><div class="pin-spacer"></div><div class="term-line np-line"><span class="np-arrow">▶</span><span class="np-title">connecting…</span><span class="np-time">[--:-- / --:--]</span></div><div class="term-line chat-line">chat: <span class="cmd">!skip</span> · <span class="cmd">!queue</span> · <span class="cmd">!info</span></div></div></div>
</div>

<div class="scanlines"></div>

@include('partials.obs-ws-scripts')
<script src="{{ asset('js/obs-ws-bootstrap.js') }}"></script>
<script>
  'use strict';
  const pad = n => String(n).padStart(2, '0');
  const fmtClock = s => (!isFinite(s) || s < 0)
    ? '--:--' : `${pad(Math.floor(s / 60))}:${pad(Math.floor(s % 60))}`;
  const term         = document.getElementById('termOutput');
  const pinnedBlock  = document.getElementById('pinnedBlock');
  const npTitle      = pinnedBlock.querySelector('.np-title');
  const npTime       = pinnedBlock.querySelector('.np-time');
  const nextSlots    = [0, 1, 2].map(i => pinnedBlock.querySelector(`[data-slot="${i}"]`));
  const MAX_TERM_LINES = 60;

  function pruneTerminal() {
    while (term.children.length > MAX_TERM_LINES) {
      const first = term.firstElementChild;
      if (!first || first === pinnedBlock) break;
      term.removeChild(first);
    }
    while (term.children.length > 1) {
      let total = 0;
      for (const c of term.children) total += c.getBoundingClientRect().height;
      if (total <= term.clientHeight) break;
      const first = term.firstElementChild;
      if (!first || first === pinnedBlock) break;
      term.removeChild(first);
    }
  }
  function logBeforePins(text, cls = 'term-out', prompt = '> ') {
    const div = document.createElement('div');
    div.className = 'term-line';
    if (prompt) {
      const p = document.createElement('span');
      p.className = 'term-prompt';
      p.textContent = prompt;
      div.appendChild(p);
    }
    const t = document.createElement('span');
    if (cls) t.className = cls;
    t.textContent = text;
    div.appendChild(t);
    term.insertBefore(div, pinnedBlock);
    pruneTerminal();
  }

  let lastTrackFile = null;
  function applyState(s) {
    if (lastTrackFile !== null && s.file && s.file !== lastTrackFile && s.title) {
      logBeforePins(`[audio] now: ${s.title}`);
    }
    if (s.file) lastTrackFile = s.file;
    npTitle.textContent = s.title || '—';
    npTime.textContent  = `[${fmtClock(s.currentTime)} / ${fmtClock(s.duration)}]`;
    const titles = Array.isArray(s.nextTitles) ? s.nextTitles : [];
    nextSlots.forEach((slot, i) => {
      const v = titles[i];
      if (v) {
        slot.textContent = v;
        slot.classList.remove('empty');
      } else {
        slot.textContent = i === 0 ? '— end of queue —' : '—';
        slot.classList.add('empty');
      }
    });
    pruneTerminal();
  }
  function setOffline(msg) {
    npTitle.textContent = msg;
    npTime.textContent  = '[--:-- / --:--]';
  }

  (async () => {
    await new Promise(r => setTimeout(r, 200));
    logBeforePins('[audio] subscribing to mpd:state', 'term-out');
    await new Promise(r => setTimeout(r, 180));
    logBeforePins('[audio] queue: shuffle on', 'term-out');
    await new Promise(r => setTimeout(r, 180));
    logBeforePins('[audio] standby ✓', 'term-out');
    await new Promise(r => setTimeout(r, 220));
    OBSWSBootstrap.onState(applyState, {
      onClose: () => { logBeforePins('daemon disconnected', 'term-dim'); setOffline('disconnected'); },
    });
  })();
</script>
</body>
</html>
