WaveformPanel

Panel that wraps WaveSurfer to provide audio playback, a canvas-based region lane (showing speaker segments), a minimap, a time ruler, and a zoom system. Communicates back to the workspace via onRegionHover, onRegionSelect, and onRegionActivate callbacks.

Constructor

new WaveformPanel(workspace, callbacks)

Parameters:
NameTypeDescription
workspaceobjectthe Workspace controller instance
callbacksobjectcallback functions for region interactions
Properties
NameTypeAttributesDescription
onRegionHoverfunctioncalled with region index when a region is hovered
onRegionSelectfunctioncalled with region index when a region is selected
onRegionActivatefunctioncalled with region index when the playhead enters a region
onWordActivatefunction<optional>
called with word index when the playhead enters a word

Classes

WaveformPanel

Methods

applyZoom(newZoom, anchorFracopt)

Applies a new zoom level to the WaveSurfer instance, updates the zoom label, and redraws the time ruler, minimap, and regions.
Parameters:
NameTypeAttributesDefaultDescription
newZoomnumberdesired zoom level (clamped to MIN_ZOOM..#maxZoom())
anchorFracnumber | null<optional>
nullif provided, the scroll position will be restored to this left-edge time fraction after the zoom re-renders

attachScrollListener()

Attaches the scroll handler to WaveSurfer's scroll container. Must be called after WaveSurfer finishes loading (in onReady).

basePxPerSec() → {number}

Computes the number of pixels per second that makes the entire track fit exactly in the waveform container at zoom level 1.
Returns:
pixels per second
Type: 
number

clearWaveformPanel()

Destroys the WaveSurfer instance and resets all audio and UI state to defaults.

clientXToTime(clientX) → {number}

Converts a clientX coordinate over the region lane to a time in seconds.
Parameters:
NameTypeDescription
clientXnumberviewport X coordinate
Returns:
time in seconds
Type: 
number

doZoom(newZoom)

Zooms to `newZoom` while keeping a fixed time point under the cursor or transport position. In cursor-anchor mode, computes the time fraction under the mouse and restores it after zoom so that pixel stays in place.
Parameters:
NameTypeDescription
newZoomnumberdesired zoom level

drawMinimap()

Redraws the minimap canvas, coloring bars by played/unplayed and viewport/non-viewport states, and positions the thumb overlay div.

drawRegions()

Redraws the region lane canvas from scratch using the cached render runs. Called on every frame during playback and on zoom/scroll/segment edits. Visual design: - Segments are grouped into "runs" (consecutive same-speaker segments with no large gap) and rendered as color bars that grow upward from the bottom. - Normal height ≈ 70% of lane; hovered ≈ 88%; selected/active = 100%. - Only the first and last segment of a run get rounded outer corners. - A speaker name label is drawn left-anchored inside each visible run. - If a split popup is open, a dashed vertical line shows the split preview.

getScrollEl() → {HTMLElement|null}

Returns the scrollable container element that WaveSurfer renders into. this is the parent of WaveSurfer's internal wrapper div. Returns null if no WaveSurfer instance exists yet.
Returns:
Type: 
HTMLElement | null

(async) initAudio(file)

Entry point for loading an audio file into the player. Validates the file, updates all relevant UI and AppState, then delegates the actual data work to loadAudioFile. Once loading completes, initialises WaveSurfer with the pre-computed peaks for fast zoom.
Parameters:
NameTypeDescription
fileFilemust have type starting with "audio/"

initWaveSurfer()

Creates (or re-creates) the WaveSurfer instance and registers all event handlers. Called both on initial file load and when switching projects. The old instance must be destroyed before calling this (resetWorkspace does it).

loadAudioFile(file)

Loads a local audio file: reads it as an ArrayBuffer, decodes it via the Web Audio API to extract sample rate and multi-channel buffers, then offloads peak extraction to an inline Web Worker so the UI stays responsive during processing. When the worker finishes, WaveSurfer is initialised with the pre-computed peaks for fast zoom.
Parameters:
NameTypeDescription
fileFilemust have type starting with "audio/"

loadFromProject(project)

Loads waveform data from the given project and draws regions.
Parameters:
NameTypeDescription
projectProjectthe project to load waveform data from

(async) loadWaveform()

Initialises WaveSurfer and loads the waveform URL from the active project. For server files without pre-computed peaks, checks the remote file size via a HEAD request and — if the file is large — extracts peaks in chunks using HTTP Range requests before handing off to WaveSurfer, preventing the tab from running out of memory on long recordings. Logs a warning if the waveform URL is not defined.

onFinish()

WaveSurfer 'finish' handler — playback reached the end naturally.

onLoading(percent)

WaveSurfer 'loading' handler — fires periodically during remote audio fetch.
Parameters:
NameTypeDescription
percentnumberloading progress 0–100

onPause()

WaveSurfer 'pause' handler — updates button icon and status indicators.

onPlay()

WaveSurfer 'play' handler — updates button icon and status indicators.

onReady()

WaveSurfer 'ready' handler — fires once the audio is fully decoded and ready. Two-pass peak extraction strategy: 1. First ready (no cachedWaveformPeaks): extract peaks from the decoded buffer and reload the file with them. this enables fast zooming without re-decode. 2. Second ready (_peaksInjected = true): skip extraction and finish normal setup. For server-streamed files, peaks weren't pre-computed before loading, so this path always runs. For local drag-drop, peaks are pre-computed in the Worker inside this.loadAudioFile(), so cachedWaveformPeaks is already set and the first pass is skipped.

onTimeUpdate(t)

WaveSurfer 'audioprocess' handler — fires frequently during playback. Coalesces updates via requestAnimationFrame to avoid redundant DOM writes. Handles: timecode display, playback-progress fraction, minimap, region highlight, transcript highlight, and follow-playhead auto-scroll.
Parameters:
NameTypeDescription
tnumbercurrent playback time in seconds

onWaveformScroll()

Scroll handler attached to WaveSurfer's internal scroll container. If a pendingScrollFrac is set (from applyZoom), applies the stored scroll position immediately and clears it; otherwise defers redraw to the next frame.

regionIndexAtTime(time) → {number}

Returns the index of the region at the given time
Parameters:
NameTypeDescription
timenumbertime in seconds to look up
Returns:
the segment index at the given time, or -1 if none
Type: 
number

regionIndexAtX(clientX) → {number}

Given a mouse clientX coordinate over the region lane, returns the index of the transcript segment whose time range covers that position.
Parameters:
NameTypeDescription
clientXnumbermouse X in viewport coordinates
Returns:
segment index, or -1 if no segment at that position
Type: 
number

resetZoom()

Resets the zoom level to 1× (fit entire track in visible width).

resizeMinimap()

Sizes the minimap canvas to the wrapper's current CSS dimensions × dpr. Called on resize and after WaveSurfer reports 'ready'.

setHoveredRegion(regionIdx)

Updates the hovered region index and redraws the region canvas.
Parameters:
NameTypeDescription
regionIdxnumbersegment index, or -1 to clear

setSelectedRegion(regionIdx)

Seeks the playhead to the start of the selected segment and redraws.
Parameters:
NameTypeDescription
regionIdxnumbersegment index, or -1 to clear

setStatus(left, rightopt)

Updates the status bar text. If `right` is omitted, only the left side changes.
Parameters:
NameTypeAttributesDescription
leftstringe.g. 'PLAYING', 'READY'
rightstring<optional>
e.g. '42.3%'

setWaveformHeight(h)

Sets the waveform panel height (clamped to 40–400px), updates the CSS variable, and redraws the minimap and regions.
Parameters:
NameTypeDescription
hnumberdesired height in pixels

setupHandleDrag(el, side)

Registers a mousedown listener on a minimap edge handle that initiates a resize drag (changes zoom level by adjusting the visible fraction).
Parameters:
NameTypeDescription
elHTMLElementthe handle element
side'left' | 'right'which edge is being dragged

skipN(skipSeconds)

Seeks the playhead forward or backward by skipSeconds.
Parameters:
NameTypeDescription
skipSecondsnumberseconds to skip (negative = backward)

timeToClientX(time) → {number}

Converts a time (seconds) to a clientX coordinate over the region lane.
Parameters:
NameTypeDescription
timenumberseconds
Returns:
clientX in viewport pixels
Type: 
number

togglePlay()

Toggles WaveSurfer playback.

updateTimeRuler()

Rebuilds the time ruler tick marks to match the current zoom level and scroll position. Tick interval is chosen from a set of "nice" values so there are approximately 8 visible ticks at any zoom. At very high zoom (≤10s visible) timestamps include decimal seconds.

updateTransportLabel()

Repositions the yellow transport line label to match the current playback position. Hides the label when the transport cursor is scrolled out of view.

zoomIn(amount)

Increases the zoom level by multiplying by amount.
Parameters:
NameTypeDescription
amountnumbermultiplier (e.g. 1.25)

zoomOut(amount)

Decreases the zoom level by dividing by amount.
Parameters:
NameTypeDescription
amountnumberdivisor (e.g. 1.25)

zoomToParagraph(paragraphIdx)

Zooms the waveform to the time range of the paragraph at the given index.
Parameters:
NameTypeDescription
paragraphIdxnumberindex of the paragraph to zoom to

zoomToRegion(regionIdx)

Zooms the waveform so the specified segment occupies ~50% of the visible width, then scrolls to center it. Uses two nested rAF calls to wait for WaveSurfer to finish re-rendering at the new zoom level before scrolling.
Parameters:
NameTypeDescription
regionIdxnumbersegment index to zoom to