Files
mikkeli aed2d54b45 Match the desktop plot interactions in the web UI
- Wheel zoom around the cursor on both curve plots and the spectrogram:
  Ctrl zooms time only, Shift values only, scrolling over an axis zooms
  that axis; drag pans live; double-click resets. The zoom window survives
  redraws, overlay changes and resizes, and resets when units change.
- Guide and reference lines are painted in a uPlot draw hook, so a dragged
  reference line (now with a triangle handle and label) follows the cursor
  instead of jumping on release. Per-track integrated lines take the
  track's colour when overlaid.
- In-plot legend like pyqtgraph's: a swatch per track plus LRA / max dBTP.
- Metadata shows integrated loudness, LRA and max true peak (prefetched
  first) instead of debug rows; the file list shows each track's LUFS.
- UI, canvas and axis text prefer M PLUS 1 Code, else the system font.
- Spectrogram image is cached offscreen so panning is cheap; its tick
  labels adapt to the zoom level.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015UXA39Sh9coAx4FhRkd8R2
2026-09-15 17:15:01 +09:00

100 lines
3.7 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Audio Mastering Analysis Toolkit</title>
<link rel="icon" href="data:,">
<link rel="stylesheet" href="vendor/uPlot.min.css">
<link rel="stylesheet" href="app.css">
</head>
<body>
<header class="topbar">
<div class="brand">
<span class="logo">◉</span>
<span class="brand-name">uj-mastering-master</span>
<span class="brand-sub">web</span>
</div>
<div class="topbar-controls">
<label class="field">
<span>Metric</span>
<select id="metric-select" title="Which metric to display"></select>
</label>
<label class="field">
<span>X axis</span>
<select id="x-mode-select" title="Absolute time, or each track normalised to its own length">
<option value="absolute">Absolute (s)</option>
<option value="relative">Relative (%)</option>
</select>
</label>
<button id="toggle-y-log" class="toggle" type="button">Y: log</button>
<button id="refresh-plot" class="btn" type="button" title="Redraw from the computed results">Refresh</button>
<div class="separator"></div>
<button id="pick-files" class="btn primary" type="button">Add audio…</button>
<input id="file-input" type="file" accept=".mp3,.wav,.flac,.ogg,.oga,.m4a,.aif,.aiff,.aifc,.opus" multiple hidden>
</div>
</header>
<main class="layout">
<aside class="sidebar">
<section class="panel">
<h2>Files <span id="file-count" class="badge">0</span></h2>
<ul id="file-list" class="file-list"></ul>
<p id="file-empty" class="empty">Drop audio files here, or use <em>Add audio…</em>.
They are analysed in this tab and never uploaded.</p>
</section>
<section class="panel">
<h2>Metadata</h2>
<dl id="metadata" class="metadata">
<dt>Track</dt><dd class="muted">nothing selected</dd>
</dl>
</section>
<section class="panel">
<h2>Reference lines <span id="ref-count" class="badge">0</span></h2>
<ul id="ref-list" class="ref-list"></ul>
<div class="ref-actions">
<button id="add-ref" class="btn small" type="button">Add at centre</button>
<button id="clear-refs" class="btn small" type="button">Clear</button>
</div>
<p class="hint">Drag a line on the plot to move it. Sets are kept per metric.</p>
</section>
</aside>
<section class="stage">
<div class="plot-head">
<h1 id="plot-title">No track loaded</h1>
<div class="plot-hint">Scroll to zoom · Ctrl: time only · Shift: values only · drag to pan · double-click to reset</div>
</div>
<div id="plot-host" class="plot-host">
<div id="placeholder" class="placeholder">
<p class="ph-title">Audio Mastering Analysis Toolkit</p>
<p class="ph-body">Drop <code>.wav</code>, <code>.flac</code> or <code>.mp3</code> files to analyse.
Decoding and every metric run inside this tab: the audio is never uploaded
or saved anywhere (this page isn't even permitted to make network requests),
and closing the tab discards it. Analysis runs at the file's native sample rate.</p>
</div>
<canvas id="heatmap" class="heatmap" hidden></canvas>
<div id="plot-legend" class="plot-legend" hidden></div>
</div>
<div class="statusbar">
<span id="status" class="status">Ready</span>
<span id="timing" class="timing"></span>
</div>
</section>
</main>
<div id="drop-overlay" class="drop-overlay" hidden><span>Drop to analyse</span></div>
<script src="vendor/uPlot.iife.min.js"></script>
<!-- app.js is an ES module: it decodes with decode.js and runs the metrics
from dsp.js in analysis-worker.js. -->
<script type="module" src="app.js"></script>
</body>
</html>