mirror of
https://github.com/azaion/ui.git
synced 2026-06-21 08:11:10 +00:00
Reskin to v2 surface/accent tokens + JetBrains Mono headings to match _docs/ui_design/v2/plugin/annotations.html. Add scrubber with class-colored annotation marks, canvas top bar (zoom/cursor/dims), floating AI-detection banner, multi-band gradient rows in the annotations sidebar, class-distribution summary footer, and DOM-overlay bbox labels with affiliation icon + readiness dot. Split VideoPlayer chrome out into the page-level controls row (transport/frame-step/save/delete/AI-detect/mute/volume) and a new Scrubber component; player events replace 200ms polling. Other: - Auth dev bypass via VITE_DEV_AUTH_BYPASS (gated on import.meta.env.DEV). - Mount SavedAnnotationsProvider in App so AnnotationsPage doesn't crash. - Extract hexToRgba to src/class-colors and time helpers to src/features/annotations/time.ts (dedup across CanvasEditor / Sidebar / AnnotationsPage). - CanvasEditor: shallow-compare label chips before commit, NaN-guard annotation-time parser, cancel cursor RAF on unmount. - AnnotationsPage: track AI-banner close timer, push initial volume to the <video> on media change, drop the duplicate parent muted state. - Fixed sidebar widths (resize handles removed per design).
This commit is contained in:
@@ -1,7 +1,5 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { MdOutlineWbSunny, MdOutlineNightlightRound } from 'react-icons/md'
|
||||
import { FaRegSnowflake } from 'react-icons/fa'
|
||||
import { api, endpoints } from '../api'
|
||||
// classColors lives under 06_annotations until F3 moves it to its own home.
|
||||
// Importing through the 06_annotations barrel would create a cycle
|
||||
@@ -60,43 +58,71 @@ export default function DetectionClasses({ selectedClassNum, onSelect, photoMode
|
||||
}
|
||||
}, [classes, photoMode, selectedClassNum, onSelect])
|
||||
|
||||
const modeClasses = classes.filter(c => c.photoMode === photoMode)
|
||||
|
||||
const modes = [
|
||||
{ value: 0, label: t('annotations.regular'), icon: <MdOutlineWbSunny />, activeClass: 'bg-az-orange text-white', iconColor: 'text-az-orange' },
|
||||
{ value: 20, label: t('annotations.winter'), icon: <FaRegSnowflake />, activeClass: 'bg-az-blue text-white', iconColor: 'text-az-blue' },
|
||||
{ value: 40, label: t('annotations.night'), icon: <MdOutlineNightlightRound />, activeClass: 'bg-purple-600 text-white', iconColor: 'text-purple-400' },
|
||||
{ value: 0, label: t('annotations.regular') },
|
||||
{ value: 20, label: t('annotations.winter') },
|
||||
{ value: 40, label: t('annotations.night') },
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="border-t border-az-border p-2">
|
||||
<div className="text-xs text-az-muted mb-1 font-semibold">{t('annotations.classes')}</div>
|
||||
<div className="space-y-0.5 max-h-48 overflow-y-auto mb-2">
|
||||
{classes.filter(c => c.photoMode === photoMode).map((c, i) => (
|
||||
<button
|
||||
key={c.id}
|
||||
onClick={() => onSelect(c.id)}
|
||||
className={`w-full flex items-center gap-1.5 px-1.5 py-0.5 rounded text-xs text-left ${
|
||||
selectedClassNum === c.id ? 'bg-az-border text-white' : 'text-az-text hover:bg-az-bg'
|
||||
}`}
|
||||
>
|
||||
<span className="w-2.5 h-2.5 rounded-full shrink-0" style={{ backgroundColor: getClassColor(c.id) }} />
|
||||
<span className="text-az-muted">{i + 1}.</span>
|
||||
<span className="truncate">{c.name}</span>
|
||||
<span className="text-az-muted ml-auto">{c.shortName}</span>
|
||||
</button>
|
||||
))}
|
||||
<div className="border-t border-border-hair">
|
||||
{/* Section header */}
|
||||
<div className="flex items-center justify-between px-3 h-9 border-b border-border-hair">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="sect-head">{t('annotations.classes')}</span>
|
||||
<span className="mono text-[10px] text-text-muted">{modeClasses.length.toString().padStart(2, '0')}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-xs text-az-muted mb-1 font-semibold">{t('annotations.photoMode')}</div>
|
||||
<div className="flex gap-1">
|
||||
{modes.map(m => (
|
||||
<button
|
||||
key={m.value}
|
||||
onClick={() => onPhotoModeChange(m.value)}
|
||||
title={m.label}
|
||||
className={`flex-1 flex items-center justify-center px-2 py-1 rounded text-base ${photoMode === m.value ? m.activeClass : `bg-az-bg ${m.iconColor} hover:brightness-125`}`}
|
||||
>
|
||||
{m.icon}
|
||||
</button>
|
||||
))}
|
||||
|
||||
{/* Column headers */}
|
||||
<div className="grid grid-cols-[28px_1fr_auto] px-3 h-6 items-center border-b border-border-hair gap-2">
|
||||
<span className="micro">{t('annotations.colNum')}</span>
|
||||
<span className="micro">{t('annotations.colName')}</span>
|
||||
<span className="micro">{t('annotations.colKey')}</span>
|
||||
</div>
|
||||
|
||||
{/* Class rows */}
|
||||
<div>
|
||||
{modeClasses.map((c, i) => {
|
||||
const isActive = selectedClassNum === c.id
|
||||
return (
|
||||
<div
|
||||
key={c.id}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => onSelect(c.id)}
|
||||
onKeyDown={e => { if (e.key === 'Enter' || e.key === ' ') onSelect(c.id) }}
|
||||
className={`class-row${isActive ? ' active' : ''}`}
|
||||
>
|
||||
<span className="swatch" style={{ background: getClassColor(c.id) }} />
|
||||
<span className={`truncate${isActive ? ' text-text-primary font-medium' : ' text-text-primary'}`}>
|
||||
{c.name}
|
||||
</span>
|
||||
<span className="kbd">{i + 1}</span>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* PhotoMode segmented control */}
|
||||
<div className="p-3 border-t border-border-hair">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="micro">{t('annotations.photoMode')}</span>
|
||||
</div>
|
||||
<div className="seg" style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', width: '100%' }}>
|
||||
{modes.map(m => (
|
||||
<button
|
||||
key={m.value}
|
||||
type="button"
|
||||
className={`seg-btn${photoMode === m.value ? ' active' : ''}`}
|
||||
onClick={() => onPhotoModeChange(m.value)}
|
||||
>
|
||||
{m.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user