One video surface: waveform scrubber, chapters, diarized speakers, bookmarks and transcript — every intelligence prop optional
Scroll over the band to zoom (double-click or Esc resets); scroll over the video for volume (Shift + scroll seeks). Hover a legend chip to raise that speaker on the timeline, click it to solo, and use the speaker icon to skip them. Chapter flags sit on the top edge, bookmark diamonds on the bottom.
Without an envelope the band falls back to a flat rail, and the slim lane beneath it carries who-spoke-when. Only ever one speaker surface, never two stacked strips saying the same thing.
Every extra prop is optional. With none of them you still get the band as a real scrubber (hover for time, click to seek, arrows to step) plus wheel volume and the keyboard map.
The same component is the presentation primitive: aspect locks the stage box from
the first frame (poster convention — the poster fills the ratio until the first frame decodes), autoplay starts it muted (honouring prefers-reduced-motion — under that
preference it holds on the poster), and loop repeats it.
| Prop | Type | Default | Description |
|---|---|---|---|
src | string | — | Video URL (required) |
poster | string | null | null | Poster frame |
captionsSrc | string | null | null | VTT captions track |
chapters | VideoChapter[] | [] | Flags on the top edge; sections in the envelope; hover card titles |
speakers | VideoSpeakerTurn[] | [] | Diarized turns — colour the envelope, or the fallback lane |
transcript | VideoTranscriptSegment[] | [] | Transcript pane + "what's being said here" on hover |
bookmarks | VideoBookmark[] | [] | Diamonds on the bottom edge |
waveform | VideoWaveform | null | null | Audio envelope; peaks normalise against their own max |
talkTime | VideoSpeakerTalkTime[] | [] | Precomputed totals; derived from turns/transcript when omitted |
startAt | number | null | null | Seek here once metadata lands |
transcriptPosition | 'below' | 'side' | 'below' | Transcript beneath, or beside on wide viewports |
onTimeUpdate | (video) => void | — | Every timeupdate tick (watch-progress reporting) |
onAddBookmark | (sec, label) => void | — | Present ⇒ renders the add-bookmark control |
onBookmarkContext | (sec, existing, x, y) => void | — | Right-click on the band; the host owns the panel |