mirror of
https://github.com/azaion/ui.git
synced 2026-04-22 10:36:34 +00:00
Enhance annotations: save/download, fallback classes, photo mode icons
Add local annotation save fallback, PNG+txt download with drawn boxes, shared classColors helper, photo mode icon toggles, and react-dropzone / react-icons dependencies.
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { FaDownload } from 'react-icons/fa'
|
||||
import { api } from '../../api/client'
|
||||
import { createSSE } from '../../api/sse'
|
||||
import { getClassColor } from './classColors'
|
||||
import type { Media, AnnotationListItem, PaginatedResponse } from '../../types'
|
||||
|
||||
interface Props {
|
||||
@@ -10,9 +12,10 @@ interface Props {
|
||||
selectedAnnotation: AnnotationListItem | null
|
||||
onSelect: (ann: AnnotationListItem) => void
|
||||
onAnnotationsUpdate: (anns: AnnotationListItem[]) => void
|
||||
onDownload?: (ann: AnnotationListItem) => void
|
||||
}
|
||||
|
||||
export default function AnnotationsSidebar({ media, annotations, selectedAnnotation, onSelect, onAnnotationsUpdate }: Props) {
|
||||
export default function AnnotationsSidebar({ media, annotations, selectedAnnotation, onSelect, onAnnotationsUpdate, onDownload }: Props) {
|
||||
const { t } = useTranslation()
|
||||
const [detecting, setDetecting] = useState(false)
|
||||
const [detectLog, setDetectLog] = useState<string[]>([])
|
||||
@@ -45,29 +48,32 @@ export default function AnnotationsSidebar({ media, annotations, selectedAnnotat
|
||||
const stops = ann.detections.map((d, i) => {
|
||||
const pct = (i / Math.max(ann.detections.length - 1, 1)) * 100
|
||||
const alpha = Math.min(1, d.confidence)
|
||||
return `${d.label ? getClassColor(d.classNum) : '#888'}${Math.round(alpha * 40).toString(16).padStart(2, '0')} ${pct}%`
|
||||
return `${getClassColor(d.classNum)}${Math.round(alpha * 40).toString(16).padStart(2, '0')} ${pct}%`
|
||||
})
|
||||
return `linear-gradient(to right, ${stops.join(', ')})`
|
||||
}
|
||||
|
||||
const classColors: Record<number, string> = {}
|
||||
const getClassColor = (classNum: number) => {
|
||||
if (classColors[classNum]) return classColors[classNum]
|
||||
const colors = ['#FF0000', '#00FF00', '#0000FF', '#FFFF00', '#FF00FF', '#00FFFF', '#188021', '#800000', '#008000', '#000080']
|
||||
return colors[classNum % colors.length]
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
<div className="p-2 border-b border-az-border flex items-center justify-between">
|
||||
<div className="p-2 border-b border-az-border flex items-center justify-between gap-1">
|
||||
<span className="text-xs font-semibold text-az-muted">{t('annotations.title')}</span>
|
||||
<button
|
||||
onClick={handleDetect}
|
||||
disabled={!media}
|
||||
className="text-xs bg-az-blue text-white px-2 py-0.5 rounded disabled:opacity-50"
|
||||
>
|
||||
{t('annotations.detect')}
|
||||
</button>
|
||||
<div className="flex items-center gap-1">
|
||||
<button
|
||||
onClick={handleDetect}
|
||||
disabled={!media}
|
||||
className="text-xs bg-az-blue text-white px-2 py-0.5 rounded disabled:opacity-50"
|
||||
>
|
||||
{t('annotations.detect')}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => selectedAnnotation && onDownload?.(selectedAnnotation)}
|
||||
disabled={!selectedAnnotation}
|
||||
title="Download annotation"
|
||||
className="text-xs bg-az-orange text-white p-1 rounded disabled:opacity-50"
|
||||
>
|
||||
<FaDownload size={12} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
|
||||
Reference in New Issue
Block a user