From 8b06e0a2bc386b13c574cf401a00366c308d2d74 Mon Sep 17 00:00:00 2001 From: Caroline Hu <270949146+Carolinehu1924@users.noreply.github.com> Date: Wed, 16 Sep 2026 19:33:59 +0800 Subject: [PATCH 1/4] feat(annotations): add spotlight annotation --- .../video-editor/AnnotationOverlay.tsx | 17 +++ .../video-editor/AnnotationSettingsPanel.tsx | 40 +++++- src/components/video-editor/SettingsPanel.tsx | 8 ++ .../video-editor/SpotlightMaskOverlay.tsx | 81 ++++++++++++ src/components/video-editor/VideoPlayback.tsx | 104 +++++++++------ .../hooks/useAnnotationRegionCommands.test.ts | 14 +++ .../hooks/useAnnotationRegionCommands.ts | 35 ++++++ .../layout/useEditorSettingsPanelProps.ts | 2 + .../video-editor/projectPersistence.test.ts | 42 +++++++ .../video-editor/projectPersistence.ts | 16 ++- .../timeline/model/timelineModel.test.ts | 11 ++ .../timeline/model/timelineModel.ts | 6 +- src/components/video-editor/types.ts | 10 +- src/lib/exporter/annotationRenderer.ts | 84 ++++++++++++- src/lib/exporter/frameRenderer.ts | 8 +- src/lib/exporter/modernFrameRenderer.ts | 19 ++- src/lib/spotlight/spotlightMask.test.ts | 115 +++++++++++++++++ src/lib/spotlight/spotlightMask.ts | 118 ++++++++++++++++++ 18 files changed, 686 insertions(+), 44 deletions(-) create mode 100644 src/components/video-editor/SpotlightMaskOverlay.tsx create mode 100644 src/components/video-editor/hooks/useAnnotationRegionCommands.test.ts create mode 100644 src/lib/spotlight/spotlightMask.test.ts create mode 100644 src/lib/spotlight/spotlightMask.ts diff --git a/src/components/video-editor/AnnotationOverlay.tsx b/src/components/video-editor/AnnotationOverlay.tsx index 4bdd44eca..353dd2ae3 100644 --- a/src/components/video-editor/AnnotationOverlay.tsx +++ b/src/components/video-editor/AnnotationOverlay.tsx @@ -1,5 +1,6 @@ import { useRef } from "react"; import { Rnd } from "react-rnd"; +import { SPOTLIGHT_CORNER_RADIUS } from "@/lib/spotlight/spotlightMask"; import { cn } from "@/lib/utils"; import { getArrowComponent } from "./ArrowSvgs"; import { type AnnotationRegion, BASE_PREVIEW_WIDTH, BLUR_ANNOTATION_STRENGTH } from "./types"; @@ -201,6 +202,21 @@ export function AnnotationOverlay({ ); } + case "spotlight": + // The dimming itself is drawn by SpotlightMaskOverlay; this box only + // provides the drag and resize handles. + return ( +
+ ); + default: return null; } @@ -300,6 +316,7 @@ export function AnnotationOverlay({ annotation.type === "text" && "bg-transparent", annotation.type === "image" && "bg-transparent", annotation.type === "figure" && "bg-transparent", + annotation.type === "spotlight" && "bg-transparent", isSelected && "shadow-lg", )} > diff --git a/src/components/video-editor/AnnotationSettingsPanel.tsx b/src/components/video-editor/AnnotationSettingsPanel.tsx index af5326320..a50dcefb9 100644 --- a/src/components/video-editor/AnnotationSettingsPanel.tsx +++ b/src/components/video-editor/AnnotationSettingsPanel.tsx @@ -4,6 +4,7 @@ import { AlignRight, TextB as Bold, CaretDown as ChevronDown, + Flashlight, ImageSquare as ImageIcon, Info, TextItalic as Italic, @@ -43,6 +44,7 @@ interface AnnotationSettingsPanelProps { onFigureDataChange?: (figureData: FigureData) => void; onBlurIntensityChange?: (intensity: number) => void; onBlurColorChange?: (color: string) => void; + onSpotlightOpacityChange?: (opacity: number) => void; onDelete: () => void; } @@ -67,6 +69,7 @@ export function AnnotationSettingsPanel({ onFigureDataChange, onBlurIntensityChange, onBlurColorChange, + onSpotlightOpacityChange, onDelete, }: AnnotationSettingsPanelProps) { const t = useScopedT("editor"); @@ -157,7 +160,7 @@ export function AnnotationSettingsPanel({ onValueChange={(value) => onTypeChange(value as AnnotationType)} className="mb-6" > -