import { memo } from "react"; import type { TimelineTheme } from "./timelineTheme"; import { GUTTER, RULER_H, TRACKS_LEFT_PAD, formatTimelineTickLabel } from "./timelineLayout"; import { usePlayerStore } from "../store/playerStore"; import { secondsToFrame } from "../lib/time"; import type { MusicBeatAnalysis } from "@hyperframes/core/beats"; interface TimelineRulerProps { major: number[]; minor: number[]; pps: number; trackContentWidth: number; totalH: number; effectiveDuration: number; majorTickInterval: number; theme: TimelineTheme; beatAnalysis?: MusicBeatAnalysis | null; } export const TimelineRuler = memo(function TimelineRuler({ major, minor, pps, trackContentWidth, totalH, effectiveDuration, majorTickInterval, theme, beatAnalysis, }: TimelineRulerProps) { const timeDisplayMode = usePlayerStore((s) => s.timeDisplayMode); const beatTimes = beatAnalysis?.beatTimes ?? []; const beatStrengths = beatAnalysis?.beatStrengths ?? []; // Only draw beat lines when they'd be at least 5px apart const avgBeatInterval = beatTimes.length > 1 ? (beatTimes[beatTimes.length - 1]! - beatTimes[0]!) / (beatTimes.length - 1) : null; const showBeats = avgBeatInterval !== null && avgBeatInterval * pps >= 5; return ( <> {/* Background SVG — beat lines only; major-tick gridlines removed so only the ruler's own small ticks mark intervals (no full-height lines). */} {showBeats && beatTimes.map((t, i) => { const x = t * pps; // Louder beats → brighter line. Gamma curve widens the contrast. const strength = Math.pow(Math.min(1, beatStrengths[i] ?? 0.5), 2.2); const opacity = 0.08 + strength * 0.62; return ( ); })} {/* Ruler — sticky so the timestamps stay visible while the tracks scroll vertically. Opaque background (plus the gutter corner block) so clips scrolling underneath don't bleed through; z-index sits above the track rows and drag overlays but below the playhead (z 100). */}
{/* Left breathing pad — scrolls with the content, so 00:00 starts a beat right of the gutter (see TRACKS_LEFT_PAD). */}