webui.html becomes webui/ (index.html, style.css, ES modules in js/, no
build step); web.py serves it under / and /ui/<path> with the same
containment check as recordings.
How it feels with a screen reader:
- Days read "Today, Saturday 17 October"; recordings are named by their
wall-clock span ("22:00 to 23:00"), which is the row header and what
every button says. The filename moves to the last column.
- Focus is never dropped: busy buttons stay focused (aria-disabled),
finished analyses focus their first section, delete moves to the next
row, closing the clip bar (x or Escape) returns to where you were.
- Reloads rebuild only the days whose content changed; DOM ids are stable
per filename, so a rollover no longer resets the reading position.
- Only user-initiated actions are announced; highlights completion is
spoken (the progress area was stuck aria-busy before).
- Media Session previous/next track step sections like J/K (AirPods,
lock screen, media keys).
- Row layout: Time | Length | Loudness | Player | Options | File. The
player opens inside its cell; Download/Cut/Delete are always there,
and Cut toggles its start/end fields in place.
- Settings tucked behind a <details>; pre-roll and auto-advance are
remembered per browser; no CSS uppercase on headers or badges.
Fixes the REC badge showing on every row: its inline-flex display beat
the UA [hidden] rule. One global [hidden]{display:none !important} now
covers every case.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
79 lines
3.3 KiB
HTML
79 lines
3.3 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<meta name="color-scheme" content="dark">
|
|
<title>ISR Archive</title>
|
|
<link rel="stylesheet" href="/ui/style.css">
|
|
<script type="module" src="/ui/js/main.js"></script>
|
|
</head>
|
|
<body>
|
|
<div id="sr-announce" aria-live="polite" aria-atomic="true" class="sr"></div>
|
|
<a href="#days" class="skip">Skip to recordings</a>
|
|
|
|
<header>
|
|
<h1 id="page-title" tabindex="-1">ISR Archive</h1>
|
|
<p id="subtitle">Loading…</p>
|
|
<p id="storage-info"></p>
|
|
<button id="refresh-btn">Refresh</button>
|
|
</header>
|
|
|
|
<main>
|
|
<details id="settings">
|
|
<summary>Settings</summary>
|
|
<div class="settings-grid">
|
|
<label for="margin-input">Loudness margin</label>
|
|
<input type="number" id="margin-input" min="1" max="60" step="1" aria-describedby="margin-hint">
|
|
<span id="margin-hint" class="hint">dB above the background noise for a section to count as loud</span>
|
|
|
|
<label for="min-gap-input">Grace period</label>
|
|
<input type="number" id="min-gap-input" min="0" max="300" step="0.5" aria-describedby="min-gap-hint">
|
|
<span id="min-gap-hint" class="hint">seconds; loud sections closer than this are merged</span>
|
|
|
|
<label for="min-duration-input">Minimum length</label>
|
|
<input type="number" id="min-duration-input" min="0" max="60" step="0.1" aria-describedby="min-duration-hint">
|
|
<span id="min-duration-hint" class="hint">seconds; shorter loud sections are ignored</span>
|
|
|
|
<label for="preroll-input">Pre-roll</label>
|
|
<input type="number" id="preroll-input" min="0" max="30" step="0.5" value="3" aria-describedby="preroll-hint">
|
|
<span id="preroll-hint" class="hint">seconds played before each section starts</span>
|
|
</div>
|
|
</details>
|
|
|
|
<div class="filter-bar" role="search" aria-label="Filter recordings">
|
|
<label for="filter-name">Search</label>
|
|
<input type="search" id="filter-name" placeholder="filename…" autocomplete="off">
|
|
<label for="filter-from">From</label>
|
|
<input type="date" id="filter-from">
|
|
<label for="filter-to">To</label>
|
|
<input type="date" id="filter-to">
|
|
<button id="filter-clear">Clear filters</button>
|
|
</div>
|
|
|
|
<div id="days" tabindex="-1"></div>
|
|
<p id="empty" class="empty" hidden>No recordings found.</p>
|
|
</main>
|
|
|
|
<section id="clip-bar" hidden aria-labelledby="clip-heading">
|
|
<h2 id="clip-heading" class="sr">Clip player</h2>
|
|
<div class="clip-row">
|
|
<button id="clip-prev" aria-label="Previous section (J)" aria-keyshortcuts="J">Prev</button>
|
|
<button id="clip-next" aria-label="Next section (K)" aria-keyshortcuts="K">Next</button>
|
|
<p id="clip-label"></p>
|
|
<button id="clip-context" aria-label="Open in file (O)" aria-keyshortcuts="O">Open in file</button>
|
|
<button id="clip-close" aria-label="Close clip player">×</button>
|
|
</div>
|
|
<div class="clip-row">
|
|
<audio id="clip-audio" controls preload="auto" aria-label="Section clip"></audio>
|
|
<fieldset id="clip-adv">
|
|
<legend>When a clip ends</legend>
|
|
<label><input type="radio" name="clip-adv" value="off"> Stop</label>
|
|
<label><input type="radio" name="clip-adv" value="all" checked> Next in time</label>
|
|
<label><input type="radio" name="clip-adv" value="hl"> Next loudest</label>
|
|
</fieldset>
|
|
</div>
|
|
</section>
|
|
</body>
|
|
</html>
|