diff --git a/src/components/video-editor/AnnotationSettingsPanel.tsx b/src/components/video-editor/AnnotationSettingsPanel.tsx index f5c2a0b2..4c26c885 100644 --- a/src/components/video-editor/AnnotationSettingsPanel.tsx +++ b/src/components/video-editor/AnnotationSettingsPanel.tsx @@ -5,6 +5,7 @@ import { AlignRight, Bold, ChevronDown, + Copy, Image as ImageIcon, Info, Italic, @@ -45,6 +46,7 @@ interface AnnotationSettingsPanelProps { onTypeChange: (type: AnnotationType) => void; onStyleChange: (style: Partial) => void; onFigureDataChange?: (figureData: FigureData) => void; + onDuplicate?: () => void; onDelete: () => void; } @@ -67,6 +69,7 @@ export function AnnotationSettingsPanel({ onTypeChange, onStyleChange, onFigureDataChange, + onDuplicate, onDelete, }: AnnotationSettingsPanelProps) { const t = useScopedT("settings"); @@ -602,15 +605,28 @@ export function AnnotationSettingsPanel({ - +
+ + + +
diff --git a/src/components/video-editor/SettingsPanel.tsx b/src/components/video-editor/SettingsPanel.tsx index 4f63a14a..4fb41936 100644 --- a/src/components/video-editor/SettingsPanel.tsx +++ b/src/components/video-editor/SettingsPanel.tsx @@ -210,6 +210,7 @@ interface SettingsPanelProps { onAnnotationTypeChange?: (id: string, type: AnnotationType) => void; onAnnotationStyleChange?: (id: string, style: Partial) => void; onAnnotationFigureDataChange?: (id: string, figureData: FigureData) => void; + onAnnotationDuplicate?: (id: string) => void; onAnnotationDelete?: (id: string) => void; selectedBlurId?: string | null; blurRegions?: AnnotationRegion[]; @@ -301,6 +302,7 @@ export function SettingsPanel({ onAnnotationTypeChange, onAnnotationStyleChange, onAnnotationFigureDataChange, + onAnnotationDuplicate, onAnnotationDelete, selectedBlurId, blurRegions = [], @@ -569,6 +571,9 @@ export function SettingsPanel({ ? (figureData) => onAnnotationFigureDataChange(selectedAnnotation.id, figureData) : undefined } + onDuplicate={ + onAnnotationDuplicate ? () => onAnnotationDuplicate(selectedAnnotation.id) : undefined + } onDelete={() => onAnnotationDelete(selectedAnnotation.id)} /> ); diff --git a/src/components/video-editor/VideoEditor.tsx b/src/components/video-editor/VideoEditor.tsx index 8cf05360..9046cb48 100644 --- a/src/components/video-editor/VideoEditor.tsx +++ b/src/components/video-editor/VideoEditor.tsx @@ -987,6 +987,33 @@ export default function VideoEditor() { [pushState], ); + const handleAnnotationDuplicate = useCallback( + (id: string) => { + const duplicateId = `annotation-${nextAnnotationIdRef.current++}`; + const duplicateZIndex = nextAnnotationZIndexRef.current++; + pushState((prev) => { + const source = prev.annotationRegions.find((region) => region.id === id); + if (!source) return {}; + + const duplicate: AnnotationRegion = { + ...source, + id: duplicateId, + zIndex: duplicateZIndex, + position: { x: source.position.x + 4, y: source.position.y + 4 }, + size: { ...source.size }, + style: { ...source.style }, + figureData: source.figureData ? { ...source.figureData } : undefined, + }; + + return { annotationRegions: [...prev.annotationRegions, duplicate] }; + }); + setSelectedAnnotationId(duplicateId); + setSelectedZoomId(null); + setSelectedTrimId(null); + }, + [pushState], + ); + const handleAnnotationDelete = useCallback( (id: string) => { pushState((prev) => ({ @@ -1993,6 +2020,7 @@ export default function VideoEditor() { onAnnotationTypeChange={handleAnnotationTypeChange} onAnnotationStyleChange={handleAnnotationStyleChange} onAnnotationFigureDataChange={handleAnnotationFigureDataChange} + onAnnotationDuplicate={handleAnnotationDuplicate} onAnnotationDelete={handleAnnotationDelete} selectedBlurId={selectedBlurId} blurRegions={blurRegions}