chore: stage all pending work — caption styling, media server, processing modal, docs, summaries
Includes:
- Extended caption styling (font, shadow, dimmed color, bg toggle)
- Media server, subtitle downloader, VTT parser, processing modal
- Waveform tiers, thumbnail/timeline improvements, transport controls
- Hybrid download model, dependency management, clip export enhancements
- 21 chat summaries, 2 implementation plans, 2 design specs
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-22 10:48:16 -04:00
|
|
|
import type { WaveformTiers } from '$lib/bindings/mediaAnalysis';
|
2026-09-21 11:12:36 -04:00
|
|
|
import type { TimelineState } from './renderer';
|
|
|
|
|
|
chore: stage all pending work — caption styling, media server, processing modal, docs, summaries
Includes:
- Extended caption styling (font, shadow, dimmed color, bg toggle)
- Media server, subtitle downloader, VTT parser, processing modal
- Waveform tiers, thumbnail/timeline improvements, transport controls
- Hybrid download model, dependency management, clip export enhancements
- 21 chat summaries, 2 implementation plans, 2 design specs
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-22 10:48:16 -04:00
|
|
|
export interface WaveformData {
|
|
|
|
|
tiers: WaveformTiers | null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Pick the best tier for the current viewport.
|
|
|
|
|
* Returns the tier array that gives at least 1 peak per pixel,
|
|
|
|
|
* preferring the lowest-resolution tier that meets that threshold.
|
|
|
|
|
*/
|
|
|
|
|
function pickTier(
|
|
|
|
|
tiers: WaveformTiers,
|
|
|
|
|
visibleStart: number,
|
|
|
|
|
visibleEnd: number,
|
|
|
|
|
duration: number,
|
|
|
|
|
widthPx: number
|
|
|
|
|
): number[] {
|
|
|
|
|
const visibleRange = visibleEnd - visibleStart;
|
|
|
|
|
const visibleFraction = visibleRange / duration;
|
|
|
|
|
|
|
|
|
|
const candidates: [number[], number][] = [
|
|
|
|
|
[tiers.tier0, (tiers.tier0.length * visibleFraction) / widthPx],
|
|
|
|
|
[tiers.tier1, (tiers.tier1.length * visibleFraction) / widthPx],
|
|
|
|
|
[tiers.tier2, (tiers.tier2.length * visibleFraction) / widthPx],
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
for (const [tier, peaksPerPx] of candidates) {
|
|
|
|
|
if (peaksPerPx >= 1) return tier;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return tiers.tier2;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-21 11:12:36 -04:00
|
|
|
export function drawWaveform(
|
|
|
|
|
ctx: CanvasRenderingContext2D,
|
|
|
|
|
state: TimelineState,
|
chore: stage all pending work — caption styling, media server, processing modal, docs, summaries
Includes:
- Extended caption styling (font, shadow, dimmed color, bg toggle)
- Media server, subtitle downloader, VTT parser, processing modal
- Waveform tiers, thumbnail/timeline improvements, transport controls
- Hybrid download model, dependency management, clip export enhancements
- 21 chat summaries, 2 implementation plans, 2 design specs
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-22 10:48:16 -04:00
|
|
|
waveform: WaveformData,
|
2026-09-21 11:12:36 -04:00
|
|
|
duration: number,
|
|
|
|
|
y: number,
|
|
|
|
|
height: number
|
|
|
|
|
): void {
|
chore: stage all pending work — caption styling, media server, processing modal, docs, summaries
Includes:
- Extended caption styling (font, shadow, dimmed color, bg toggle)
- Media server, subtitle downloader, VTT parser, processing modal
- Waveform tiers, thumbnail/timeline improvements, transport controls
- Hybrid download model, dependency management, clip export enhancements
- 21 chat summaries, 2 implementation plans, 2 design specs
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-22 10:48:16 -04:00
|
|
|
if (duration <= 0 || !waveform.tiers) return;
|
2026-09-21 11:12:36 -04:00
|
|
|
|
|
|
|
|
const { width, visibleStart, visibleEnd } = state;
|
|
|
|
|
const visibleRange = visibleEnd - visibleStart;
|
chore: stage all pending work — caption styling, media server, processing modal, docs, summaries
Includes:
- Extended caption styling (font, shadow, dimmed color, bg toggle)
- Media server, subtitle downloader, VTT parser, processing modal
- Waveform tiers, thumbnail/timeline improvements, transport controls
- Hybrid download model, dependency management, clip export enhancements
- 21 chat summaries, 2 implementation plans, 2 design specs
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-22 10:48:16 -04:00
|
|
|
const peaks = pickTier(waveform.tiers, visibleStart, visibleEnd, duration, width);
|
|
|
|
|
const peakCount = peaks.length;
|
|
|
|
|
|
|
|
|
|
// Find local maximum across the visible region for dynamic scaling.
|
|
|
|
|
// This makes the waveform fill the available height when zoomed in,
|
|
|
|
|
// rather than staying relative to the global max.
|
|
|
|
|
const visStartIdx = Math.max(0, Math.floor((visibleStart / duration) * peakCount));
|
|
|
|
|
const visEndIdx = Math.min(peakCount - 1, Math.ceil((visibleEnd / duration) * peakCount));
|
|
|
|
|
let localMax = 0;
|
|
|
|
|
for (let i = visStartIdx; i <= visEndIdx; i++) {
|
|
|
|
|
localMax = Math.max(localMax, peaks[i]);
|
|
|
|
|
}
|
|
|
|
|
// Avoid division by zero; use 1.0 if all peaks are silent
|
|
|
|
|
const scale = localMax > 0 ? 1 / localMax : 1;
|
2026-09-21 11:12:36 -04:00
|
|
|
|
|
|
|
|
ctx.fillStyle = '#89b4fa44';
|
|
|
|
|
ctx.strokeStyle = '#89b4fa88';
|
|
|
|
|
ctx.lineWidth = 1;
|
|
|
|
|
|
|
|
|
|
ctx.beginPath();
|
|
|
|
|
ctx.moveTo(0, y + height);
|
|
|
|
|
|
|
|
|
|
for (let px = 0; px < width; px++) {
|
chore: stage all pending work — caption styling, media server, processing modal, docs, summaries
Includes:
- Extended caption styling (font, shadow, dimmed color, bg toggle)
- Media server, subtitle downloader, VTT parser, processing modal
- Waveform tiers, thumbnail/timeline improvements, transport controls
- Hybrid download model, dependency management, clip export enhancements
- 21 chat summaries, 2 implementation plans, 2 design specs
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-22 10:48:16 -04:00
|
|
|
const timeStart = visibleStart + ((px - 0.5) / width) * visibleRange;
|
|
|
|
|
const timeEnd = visibleStart + ((px + 0.5) / width) * visibleRange;
|
|
|
|
|
|
|
|
|
|
const s0 = Math.floor((timeStart / duration) * peakCount);
|
|
|
|
|
const s1 = Math.floor((timeEnd / duration) * peakCount);
|
2026-09-21 11:12:36 -04:00
|
|
|
|
|
|
|
|
let maxPeak = 0;
|
chore: stage all pending work — caption styling, media server, processing modal, docs, summaries
Includes:
- Extended caption styling (font, shadow, dimmed color, bg toggle)
- Media server, subtitle downloader, VTT parser, processing modal
- Waveform tiers, thumbnail/timeline improvements, transport controls
- Hybrid download model, dependency management, clip export enhancements
- 21 chat summaries, 2 implementation plans, 2 design specs
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-22 10:48:16 -04:00
|
|
|
for (let i = Math.max(0, s0); i <= Math.min(s1, peakCount - 1); i++) {
|
2026-09-21 11:12:36 -04:00
|
|
|
maxPeak = Math.max(maxPeak, peaks[i]);
|
|
|
|
|
}
|
|
|
|
|
|
chore: stage all pending work — caption styling, media server, processing modal, docs, summaries
Includes:
- Extended caption styling (font, shadow, dimmed color, bg toggle)
- Media server, subtitle downloader, VTT parser, processing modal
- Waveform tiers, thumbnail/timeline improvements, transport controls
- Hybrid download model, dependency management, clip export enhancements
- 21 chat summaries, 2 implementation plans, 2 design specs
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-22 10:48:16 -04:00
|
|
|
const barHeight = maxPeak * scale * height;
|
2026-09-21 11:12:36 -04:00
|
|
|
ctx.lineTo(px, y + height - barHeight);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ctx.lineTo(width, y + height);
|
|
|
|
|
ctx.closePath();
|
|
|
|
|
ctx.fill();
|
|
|
|
|
ctx.stroke();
|
|
|
|
|
}
|