From eae274e122cda646bc00fc3e1aafcf95e1194154 Mon Sep 17 00:00:00 2001 From: Claude Code Date: Wed, 18 Mar 2026 01:35:58 -0700 Subject: [PATCH] =?UTF-8?q?feat(video-studio):=20=E2=9C=A8=20Add=20Disguis?= =?UTF-8?q?eVideoParticipantVideo=20component=20to=20obscure=20participant?= =?UTF-8?q?=20identities=20in=20video=20streams?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- .../src/components/DisguiseVideoParticipantVideo.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/features/video-studio/frontend-live/src/components/DisguiseVideoParticipantVideo.tsx b/features/video-studio/frontend-live/src/components/DisguiseVideoParticipantVideo.tsx index e29c1c411..6a1e2b8f2 100644 --- a/features/video-studio/frontend-live/src/components/DisguiseVideoParticipantVideo.tsx +++ b/features/video-studio/frontend-live/src/components/DisguiseVideoParticipantVideo.tsx @@ -23,6 +23,9 @@ const MAX_FACES = 4; /** Minimum interval (ms) between `onFacesDetected` callbacks (~5 fps). */ const FACES_NOTIFY_INTERVAL_MS = 200; +/** Visual disguise applied to detected faces. */ +export type DisguiseMode = 'blur' | 'mask' | 'masquerade' | 'anonymous' | 'egirl' | 'none'; + /** A single face detected in the current frame. */ export interface DetectedFace { /** Zero-based index within the frame — matches `disguisedFaceIndices`. */ @@ -53,7 +56,7 @@ export interface DisguiseVideoParticipantVideoProps { */ stream?: MediaStream; /** Visual disguise applied to each detected face. */ - disguise: 'blur' | 'mask' | 'masquerade' | 'anonymous' | 'egirl' | 'none'; + disguise: DisguiseMode; /** Gaussian blur radius in pixels. Default: 20. */ blurStrength?: number; /** Canvas width in pixels. Default: 640. */ @@ -78,11 +81,16 @@ export interface DisguiseVideoParticipantVideoProps { * Pass `null` (default) to disguise every detected face. */ disguisedFaceIndices?: ReadonlySet | null; + /** + * Per-face disguise mode overrides. When a face index has an entry here, + * that mode is used instead of the global `disguise` prop. + * Faces without an entry fall back to `disguise`. + */ + faceDisguiseModes?: ReadonlyMap | null; className?: string; style?: CSSProperties; } -type DisguiseMode = DisguiseVideoParticipantVideoProps['disguise']; function assertNever(value: never): never { throw new Error(`Unhandled disguise mode: ${String(value)}`);