/** * ProgressBar — one-row, click-to-seek playback progress bar for the * player pane. Played portion renders as full blocks (█) in the theme's * primary color, the remainder as light shade blocks (░) in the muted * color. The header time/percent text lives in PlayerPage — this is only * the bar itself. */ import { useTerminalDimensions } from "@opentui/solid"; import type { Renderable } from "@opentui/core"; import { useAudio } from "@/hooks/useAudio"; import { useTheme } from "@/context/ThemeContext"; import { usePaneLayout } from "@/stores/pane-layout"; // ── Component ──────────────────────────────────────────────────────── export function ProgressBar() { const audio = useAudio(); const { theme } = useTheme(); const dimensions = useTerminalDimensions(); const layout = usePaneLayout(); // The bar's renderable, captured for its absolute left edge: MouseEvent.x // is terminal-absolute (not bar-relative), so local x needs the offset // of the bar inside the 2-pane row (parent pane = left split of the width). let bar: Renderable | undefined; // Full content width of the player pane: the player is a 2-pane row // (parent = left split, current = the rest of the terminal width). Track // the live split so a dragged border re-sizes the bar. Subtract ~8 chars // of border/padding chrome (same math as RealtimeWaveform's numBars). const width = () => Math.max(8, Math.floor(dimensions().width * (1 - layout.splits().left)) - 8); const clamp01 = (value: number) => Math.max(0, Math.min(1, value)); const playedChars = () => { const duration = audio.duration(); if (duration <= 0) return 0; return Math.round(clamp01(audio.position() / duration) * width()); }; const remainingColor = theme.muted || theme.text; return ( { bar = el; }} onMouseDown={(e: { x: number }) => { const duration = audio.duration(); if (duration <= 0 || !bar) return; // localX = 0 is the box border; content starts at localX = 1. const localX = e.x - bar.x; const ratio = Math.max(0, Math.min(1, (localX - 1) / width())); void audio.seek(ratio * duration); }} > {playedChars() > 0 && ( {"\u2588".repeat(playedChars())} )} {"\u2591".repeat(width() - playedChars())} ); }