From fa7e4983c789caa7e89b5f2657103d97018a6dd5 Mon Sep 17 00:00:00 2001 From: jlightner Date: Sat, 4 Apr 2026 10:44:45 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20Replaced=20thin=203px=20line=20markers?= =?UTF-8?q?=20with=2012px=20color-coded=20circle=20pins,=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - "frontend/src/components/ChapterMarkers.tsx" - "frontend/src/components/PlayerControls.tsx" - "frontend/src/App.css" GSD-Task: S02/T01 --- frontend/src/App.css | 55 +++++++++++++++++----- frontend/src/components/ChapterMarkers.tsx | 44 ++++++++++++++--- frontend/src/components/PlayerControls.tsx | 2 +- 3 files changed, 83 insertions(+), 18 deletions(-) diff --git a/frontend/src/App.css b/frontend/src/App.css index 12c601d..24a5f0e 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -29,6 +29,12 @@ --color-accent-subtle: rgba(34, 211, 238, 0.1); --color-accent-focus: rgba(34, 211, 238, 0.15); + /* Content-type pin colors */ + --color-pin-technique: #22d3ee; /* cyan */ + --color-pin-settings: #f59e0b; /* amber */ + --color-pin-reasoning: #a855f7; /* purple */ + --color-pin-workflow: #22c55e; /* green */ + /* Shadows / overlays */ --color-shadow: rgba(0, 0, 0, 0.2); --color-shadow-heavy: rgba(0, 0, 0, 0.4); @@ -6238,42 +6244,69 @@ a.app-footer__about:hover, pointer-events: none; } -.chapter-marker__tick { +.chapter-marker__pin { position: absolute; - width: 3px; - height: 100%; + width: 12px; + height: 12px; + border-radius: 50%; background: var(--color-accent, #22d3ee); - opacity: 0.6; + opacity: 0.7; pointer-events: all; cursor: pointer; - transform: translateX(-50%); - border: none; + transform: translate(-50%, -50%); + top: 50%; + border: 2px solid rgba(0, 0, 0, 0.4); padding: 0; font: inherit; + transition: transform 150ms ease, opacity 150ms ease; + /* Ensure touch-friendly hit area */ + min-width: 12px; + min-height: 12px; } -.chapter-marker__tick:hover { +.chapter-marker__pin::before { + content: ""; + position: absolute; + inset: -6px; +} + +.chapter-marker__pin--technique { background: var(--color-pin-technique); } +.chapter-marker__pin--settings { background: var(--color-pin-settings); } +.chapter-marker__pin--reasoning { background: var(--color-pin-reasoning); } +.chapter-marker__pin--workflow { background: var(--color-pin-workflow); } + +.chapter-marker__pin--active { opacity: 1; + transform: translate(-50%, -50%) scale(1.3); + z-index: 2; +} + +.chapter-marker__pin:hover { + opacity: 1; + transform: translate(-50%, -50%) scale(1.2); +} + +.chapter-marker__pin--active:hover { + transform: translate(-50%, -50%) scale(1.3); } .chapter-marker__tooltip { position: absolute; - bottom: 100%; + bottom: calc(100% + 4px); left: 50%; transform: translateX(-50%); background: var(--color-bg-surface, #1e293b); color: var(--text-primary, #e2e8f0); - padding: 4px 8px; + padding: 4px 10px; border-radius: 4px; font-size: 0.75rem; white-space: nowrap; opacity: 0; pointer-events: none; transition: opacity 150ms; - margin-bottom: 4px; } -.chapter-marker__tick:hover .chapter-marker__tooltip { +.chapter-marker__pin:hover .chapter-marker__tooltip { opacity: 1; } diff --git a/frontend/src/components/ChapterMarkers.tsx b/frontend/src/components/ChapterMarkers.tsx index e1f1bf3..6048df9 100644 --- a/frontend/src/components/ChapterMarkers.tsx +++ b/frontend/src/components/ChapterMarkers.tsx @@ -4,33 +4,65 @@ interface ChapterMarkersProps { chapters: Chapter[]; duration: number; onSeek: (time: number) => void; + currentTime?: number; } +/** Format seconds as m:ss */ +function formatTime(seconds: number): string { + if (!Number.isFinite(seconds) || seconds < 0) return "0:00"; + const m = Math.floor(seconds / 60); + const s = Math.floor(seconds % 60); + return `${m}:${s.toString().padStart(2, "0")}`; +} + +const PIN_COLOR_CLASS: Record = { + technique: "chapter-marker__pin--technique", + settings: "chapter-marker__pin--settings", + reasoning: "chapter-marker__pin--reasoning", + workflow: "chapter-marker__pin--workflow", +}; + /** - * Absolutely-positioned overlay that renders tick marks on the seek bar - * for each chapter. Ticks are clickable and show a tooltip on hover. + * Absolutely-positioned overlay that renders color-coded circle pins + * on the seek bar for each chapter. Pins are clickable and show a + * rich tooltip on hover. The active pin (matching current playback + * position) is enlarged. */ -export default function ChapterMarkers({ chapters, duration, onSeek }: ChapterMarkersProps) { +export default function ChapterMarkers({ + chapters, + duration, + onSeek, + currentTime, +}: ChapterMarkersProps) { if (!duration || chapters.length === 0) return null; return (
{chapters.map((ch) => { const leftPct = (ch.start_time / duration) * 100; + const isActive = + currentTime != null && + ch.start_time <= currentTime && + currentTime <= ch.end_time; + + const colorClass = PIN_COLOR_CLASS[ch.content_type] ?? ""; + const activeClass = isActive ? " chapter-marker__pin--active" : ""; + return ( ); })} diff --git a/frontend/src/components/PlayerControls.tsx b/frontend/src/components/PlayerControls.tsx index 8d36040..8c0feb0 100644 --- a/frontend/src/components/PlayerControls.tsx +++ b/frontend/src/components/PlayerControls.tsx @@ -140,7 +140,7 @@ export default function PlayerControls({ mediaSync, containerRef, chapters }: Pl style={{ "--progress": `${seekProgress}%` } as React.CSSProperties} /> {chapters && chapters.length > 0 && ( - + )}