// 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 });