// HighlightsView: all saved highlights across all recordings const { useState: useStateHV, useMemo: useMemoHV } = React; function HighlightsView({ data, highlights, setSelectedRecording, setView }) { const [filterTag, setFilterTag] = useStateHV(''); const [sortBy, setSortBy] = useStateHV('recent'); const allTags = [...new Set(highlights.map(h => h.tag))].sort(); const sorted = useMemoHV(() => { let hs = [...highlights]; if (filterTag) hs = hs.filter(h => h.tag === filterTag); if (sortBy === 'recent') hs.sort((a, b) => b.createdAt - a.createdAt); else if (sortBy === 'recording') hs.sort((a, b) => { const ra = data.recordings.find(r => r.id === a.recordingId); const rb = data.recordings.find(r => r.id === b.recordingId); return (rb?.startTime || 0) - (ra?.startTime || 0); }); else if (sortBy === 'tag') hs.sort((a, b) => a.tag.localeCompare(b.tag)); return hs; }, [highlights, filterTag, sortBy, data]); const TAG_COLORS = { important: 'var(--red)', todo: 'var(--amber)', idea: 'var(--teal)', 'follow-up': 'var(--amber-dim)', meeting: 'var(--green)', personal: 'var(--text2)', work: 'var(--teal)', }; function formatTime(secs) { const h = Math.floor(secs / 3600); const m = Math.floor((secs % 3600) / 60); const s = Math.floor(secs % 60); if (h > 0) return `${h}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`; return `${m}:${String(s).padStart(2, '0')}`; } function openRecording(highlight) { const rec = data.recordings.find(r => r.id === highlight.recordingId); if (rec) { // Pass the highlight's startSeconds to seek to that position setSelectedRecording(rec, highlight.startSeconds); } } // Group by tag for summary const tagCounts = {}; for (const h of highlights) tagCounts[h.tag] = (tagCounts[h.tag] || 0) + 1; return (
{/* Left panel: tag breakdown */}
By Tag · {highlights.length} total
{Object.entries(tagCounts).sort((a, b) => b[1] - a[1]).map(([tag, count]) => { const color = TAG_COLORS[tag] || 'var(--text3)'; const isActive = filterTag === tag; return ( ); })}
{/* Right: highlights list */}
{sorted.length} highlight{sorted.length !== 1 ? 's' : ''} {filterTag ? ` · ${filterTag}` : ''}
Sort: {['recent', 'recording', 'tag'].map(s => ( ))}
{sorted.length === 0 ? (
No highlights yet.

Open a recording and press ◈ Highlight to mark moments.
) : (
{sorted.map(h => { const rec = data.recordings.find(r => r.id === h.recordingId); const color = TAG_COLORS[h.tag] || 'var(--text3)'; const seg = rec?.transcript?.find(s => s.id === h.segmentId); return (
openRecording(h)} style={{ background: 'var(--bg2)', border: `1px solid var(--border)`, borderLeft: `3px solid ${color}`, borderRadius: '0 6px 6px 0', padding: '12px 14px', cursor: 'pointer', transition: 'border-color 0.1s, background 0.1s', }} onMouseEnter={e => { e.currentTarget.style.background = 'var(--bg3)'; e.currentTarget.style.borderColor = color; }} onMouseLeave={e => { e.currentTarget.style.background = 'var(--bg2)'; e.currentTarget.style.borderColor = 'var(--border)'; e.currentTarget.style.borderLeftColor = color; }} >
{h.tag} {rec && ( <> {rec.startTime.toLocaleDateString([], { month: 'short', day: 'numeric' })} {rec.source} )} {formatTime(h.startSeconds)}
{seg && (
"{seg.text}"
)} {h.note && (
{h.note}
)}
); })}
)}
); } Object.assign(window, { HighlightsView });