Skip to content

Add webcam mask shape support#288

Open
gulivan wants to merge 1 commit intosiddharthvaddem:mainfrom
gulivan:feature/webcam-mask-shapes
Open

Add webcam mask shape support#288
gulivan wants to merge 1 commit intosiddharthvaddem:mainfrom
gulivan:feature/webcam-mask-shapes

Conversation

@gulivan
Copy link
Copy Markdown

@gulivan gulivan commented Apr 2, 2026

Summary

  • Adds configurable webcam mask shapes (circle, rounded rectangle, etc.) to the video editor
  • Implements webcamMaskShapes.ts with shape definitions and rendering logic
  • Integrates mask shape selection into SettingsPanel, VideoPlayback, composite layout, and export pipeline
  • Includes i18n support (en, es, zh-CN) and updated tests

Demo:

export-1775164482142.mp4

Test plan

  • Verify mask shape options appear in the Settings panel
  • Confirm each shape renders correctly in the video preview
  • Test that exported videos/GIFs apply the selected mask shape
  • Run existing tests (compositeLayout.test.ts, projectPersistence.test.ts)

Summary by CodeRabbit

Release Notes

  • New Features
    • Added webcam mask shape customization in picture-in-picture layout mode, allowing users to select from rectangle, circle, square, or rounded shapes.
    • Webcam mask shape selections are now persisted in projects and applied during playback and export.
    • Added multilingual support for mask shape settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 2, 2026

📝 Walkthrough

Walkthrough

This PR adds configurable webcam mask shape support ("rectangle", "circle", "square", "rounded") to the video editor. Changes include new UI controls in SettingsPanel, state management through VideoEditor and projectPersistence, rendering updates in VideoPlayback with CSS clip-path, canvas utilities, and layout computation adjustments.

Changes

Cohort / File(s) Summary
Type Definitions & Utilities
src/components/video-editor/types.ts, src/lib/webcamMaskShapes.ts
Introduced WebcamMaskShape union type, DEFAULT_WEBCAM_MASK_SHAPE constant, and two utility functions: getCssClipPath() for CSS clip-path generation and drawCanvasClipPath() for canvas-based clipping with shape-specific geometry.
Settings UI
src/components/video-editor/SettingsPanel.tsx
Added webcamMaskShape prop and onWebcamMaskShapeChange callback; created new conditional UI control in "layout" section rendering four selectable mask-shape options when picture-in-picture preset is active.
State Management
src/components/video-editor/VideoEditor.tsx, src/components/video-editor/projectPersistence.ts, src/components/video-editor/projectPersistence.test.ts, src/hooks/useEditorHistory.ts
Extended EditorState and ProjectEditorState with webcamMaskShape field; added state persistence via pushState, project snapshots, and undo/redo; implemented normalization logic to validate and default invalid shapes to "rectangle"; added test coverage for normalization.
Rendering & Layout
src/components/video-editor/VideoPlayback.tsx, src/lib/compositeLayout.ts, src/lib/compositeLayout.test.ts, src/components/video-editor/videoPlayback/layoutUtils.ts
Threaded webcamMaskShape through layout computation; updated VideoPlayback to apply CSS clip-path or borderRadius with shape-specific styling; adjusted webcam rect dimensions for "circle" and "square" shapes; added comprehensive test coverage for mask geometry assertions.
Export & Frame Rendering
src/lib/exporter/frameRenderer.ts, src/lib/exporter/gifExporter.ts, src/lib/exporter/videoExporter.ts
Extended FrameRenderConfig, GifExporterConfig, and VideoExporterConfig with optional webcamMaskShape field; updated frame renderer to use drawCanvasClipPath() helper instead of inline ctx.roundRect() calls for shape-aware clipping.
Internationalization
src/i18n/locales/en/settings.json, src/i18n/locales/es/settings.json, src/i18n/locales/zh-CN/settings.json
Added new translation key layout.webcamShape ("Camera Shape" / "Forma de cámara" / "摄像头形状") across English, Spanish, and Simplified Chinese locales.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant SettingsPanel
    participant VideoEditor
    participant ProjectPersistence
    participant VideoPlayback
    participant LayoutUtils
    participant CompositeLayout

    User->>SettingsPanel: Select webcam mask shape<br/>(circle, square, etc.)
    SettingsPanel->>VideoEditor: onWebcamMaskShapeChange(shape)
    VideoEditor->>VideoEditor: pushState({webcamMaskShape: shape})
    VideoEditor->>ProjectPersistence: createProjectData(editor)
    ProjectPersistence->>ProjectPersistence: Persist webcamMaskShape
    
    VideoEditor->>VideoPlayback: Re-render with webcamMaskShape prop
    VideoPlayback->>LayoutUtils: layoutVideoContentUtil(..., webcamMaskShape)
    LayoutUtils->>CompositeLayout: computeCompositeLayout({..., webcamMaskShape})
    CompositeLayout->>CompositeLayout: Calculate shape-specific<br/>dimensions & borderRadius
    CompositeLayout-->>VideoPlayback: Return webcamRect with maskShape
    
    VideoPlayback->>VideoPlayback: Apply CSS clip-path<br/>or borderRadius based on shape
    VideoPlayback-->>User: Render webcam with mask shape
Loading
sequenceDiagram
    participant VideoEditor
    participant Exporter
    participant FrameRenderer
    participant Canvas
    participant WebcamMaskShapes

    VideoEditor->>Exporter: Export with webcamMaskShape config
    Exporter->>FrameRenderer: new FrameRenderer({..., webcamMaskShape})
    
    FrameRenderer->>Canvas: Render frame with webcam
    Canvas->>FrameRenderer: requestAnimationFrame callback
    FrameRenderer->>Canvas: computeCompositeLayout({..., webcamMaskShape})
    
    FrameRenderer->>WebcamMaskShapes: drawCanvasClipPath(ctx, x, y, w, h,<br/>shape, borderRadius)
    WebcamMaskShapes->>Canvas: ctx.beginPath()<br/>ctx.arc() or ctx.roundRect()<br/>ctx.closePath()
    Canvas->>Canvas: ctx.clip()
    Canvas->>Canvas: ctx.drawImage(webcam)
    
    FrameRenderer-->>VideoEditor: Frame with masked webcam rendered
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • openscreen#229: Adds webcam overlay support with similar modifications to VideoPlayback, VideoEditor, projectPersistence, and exporter/frame-rendering interfaces, indicating parallel feature development.

Suggested reviewers

  • siddharthvaddem

Poem

🐰 A shape-shifter's delight, the webcam now bends,
Round circles and squares, where the masking commences,
With clip-paths and canvas, the rendering transcends,
Your frame-in-a-frame finds new aesthetic defences! ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The PR description lacks key required template sections including Motivation, Type of Change, Related Issue(s), and Testing details. While it provides a summary and demo, it does not follow the repository's PR template structure. Add the missing sections from the template: expand Motivation to explain why mask shapes are needed, explicitly mark Type of Change, link any related issues, and detail testing steps beyond the checklist items.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add webcam mask shape support' directly summarizes the main change: introducing configurable webcam mask shapes to the video editor.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9d0ccf3bde

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +1167 to +1169
<div
className="absolute"
style={{
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Disable pointer capture on webcam wrapper in non-PiP mode

The new wrapper <div> around the webcam video defaults to pointer-events: auto, so in vertical-stack mode (where the inner <video> is explicitly pointer-events-none) this wrapper still becomes the hit target and swallows clicks/drags over the webcam area. That blocks underlying editor interactions in that region (e.g. timeline/annotation manipulations routed through lower layers) whenever a webcam track is present. Add pointer-events: none on the wrapper (and re-enable on the video for PiP drag) to preserve the prior pass-through behavior.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
src/lib/exporter/videoExporter.ts (1)

35-35: Consider importing WebcamMaskShape at the top with other types.

Same as in gifExporter.ts — the inline import type is inconsistent with how other types from @/components/video-editor/types are imported (Lines 1-8).

♻️ Suggested refactor
 import type {
 	AnnotationRegion,
 	CropRegion,
 	SpeedRegion,
 	TrimRegion,
 	WebcamLayoutPreset,
+	WebcamMaskShape,
 	ZoomRegion,
 } from "@/components/video-editor/types";

Then update Line 35:

-	webcamMaskShape?: import("@/components/video-editor/types").WebcamMaskShape;
+	webcamMaskShape?: WebcamMaskShape;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/exporter/videoExporter.ts` at line 35, The inline type import for
webcamMaskShape should be replaced by importing WebcamMaskShape alongside the
other types at the top of videoExporter.ts; update the top imports to include
WebcamMaskShape from "@/components/video-editor/types" and change the
webcamMaskShape property declaration (webcamMaskShape?:
import("@/components/video-editor/types").WebcamMaskShape;) to use the imported
WebcamMaskShape type instead.
src/lib/exporter/gifExporter.ts (1)

44-44: Consider importing WebcamMaskShape at the top with other types.

The inline import type syntax works but is inconsistent with how other types from @/components/video-editor/types are imported (Lines 2-8). Moving it to the top improves readability and consistency.

♻️ Suggested refactor
 import type {
 	AnnotationRegion,
 	CropRegion,
 	SpeedRegion,
 	TrimRegion,
 	WebcamLayoutPreset,
+	WebcamMaskShape,
 	ZoomRegion,
 } from "@/components/video-editor/types";

Then update Line 44:

-	webcamMaskShape?: import("@/components/video-editor/types").WebcamMaskShape;
+	webcamMaskShape?: WebcamMaskShape;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/exporter/gifExporter.ts` at line 44, The inline type import for
WebcamMaskShape should be moved to the top alongside the other imports from
"@/components/video-editor/types" for consistency and readability; add
WebcamMaskShape to the existing import list (the same statement that currently
imports other types from "@/components/video-editor/types") and then replace the
inline reference "webcamMaskShape?:
import(\"@/components/video-editor/types\").WebcamMaskShape;" with
"webcamMaskShape?: WebcamMaskShape;" in the gifExporter.ts declaration.
src/lib/webcamMaskShapes.ts (1)

40-45: Verify roundRect browser support for your target environments.

CanvasRenderingContext2D.roundRect() is widely available: Chrome/Edge 99+, Firefox 112+, Safari 16.4+, and Opera 85+ (all supporting since April 2023). For older browser support, consider a polyfill or manual path drawing implementation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/webcamMaskShapes.ts` around lines 40 - 45, The code uses
CanvasRenderingContext2D.roundRect (ctx.roundRect) in the switch cases for
"rectangle"/"rounded"/"square"/default which may not exist in older browsers;
add a fallback that detects if ctx.roundRect is undefined and then draws a
rounded-rect path manually (using ctx.beginPath, moveTo/lineTo/arcTo or arc for
corners, closePath, and ctx.fill()/ctx.stroke() as appropriate) or include a
small polyfill that defines ctx.roundRect before use; update the switch branch
in webcamMaskShapes (where ctx.roundRect is called) to call the fallback
function (e.g., drawRoundedRect(ctx, x, y, w, h, borderRadius)) when
ctx.roundRect is unavailable.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/video-editor/SettingsPanel.tsx`:
- Around line 631-711: The shape option labels are hardcoded and the button
group doesn't expose pressed state; update the shape list used in the
webcamLayoutPreset === "picture-in-picture" block (the array mapped to render
buttons) to use localization keys via t(...) instead of hardcoded
"Rect"/"Circle"/"Square"/"Rounded", and add aria-pressed={webcamMaskShape ===
shape.value} to each button rendered by the map so assistive tech can detect the
selected state; ensure the onClick still calls
onWebcamMaskShapeChange(shape.value) and keep existing className logic using
webcamMaskShape for visual selection.

---

Nitpick comments:
In `@src/lib/exporter/gifExporter.ts`:
- Line 44: The inline type import for WebcamMaskShape should be moved to the top
alongside the other imports from "@/components/video-editor/types" for
consistency and readability; add WebcamMaskShape to the existing import list
(the same statement that currently imports other types from
"@/components/video-editor/types") and then replace the inline reference
"webcamMaskShape?: import(\"@/components/video-editor/types\").WebcamMaskShape;"
with "webcamMaskShape?: WebcamMaskShape;" in the gifExporter.ts declaration.

In `@src/lib/exporter/videoExporter.ts`:
- Line 35: The inline type import for webcamMaskShape should be replaced by
importing WebcamMaskShape alongside the other types at the top of
videoExporter.ts; update the top imports to include WebcamMaskShape from
"@/components/video-editor/types" and change the webcamMaskShape property
declaration (webcamMaskShape?:
import("@/components/video-editor/types").WebcamMaskShape;) to use the imported
WebcamMaskShape type instead.

In `@src/lib/webcamMaskShapes.ts`:
- Around line 40-45: The code uses CanvasRenderingContext2D.roundRect
(ctx.roundRect) in the switch cases for "rectangle"/"rounded"/"square"/default
which may not exist in older browsers; add a fallback that detects if
ctx.roundRect is undefined and then draws a rounded-rect path manually (using
ctx.beginPath, moveTo/lineTo/arcTo or arc for corners, closePath, and
ctx.fill()/ctx.stroke() as appropriate) or include a small polyfill that defines
ctx.roundRect before use; update the switch branch in webcamMaskShapes (where
ctx.roundRect is called) to call the fallback function (e.g.,
drawRoundedRect(ctx, x, y, w, h, borderRadius)) when ctx.roundRect is
unavailable.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 45695159-3486-4219-a24d-749c90b97808

📥 Commits

Reviewing files that changed from the base of the PR and between 2f36160 and 9d0ccf3.

📒 Files selected for processing (17)
  • src/components/video-editor/SettingsPanel.tsx
  • src/components/video-editor/VideoEditor.tsx
  • src/components/video-editor/VideoPlayback.tsx
  • src/components/video-editor/projectPersistence.test.ts
  • src/components/video-editor/projectPersistence.ts
  • src/components/video-editor/types.ts
  • src/components/video-editor/videoPlayback/layoutUtils.ts
  • src/hooks/useEditorHistory.ts
  • src/i18n/locales/en/settings.json
  • src/i18n/locales/es/settings.json
  • src/i18n/locales/zh-CN/settings.json
  • src/lib/compositeLayout.test.ts
  • src/lib/compositeLayout.ts
  • src/lib/exporter/frameRenderer.ts
  • src/lib/exporter/gifExporter.ts
  • src/lib/exporter/videoExporter.ts
  • src/lib/webcamMaskShapes.ts

Comment on lines +631 to +711
{webcamLayoutPreset === "picture-in-picture" && (
<div className="mt-2 p-2 rounded-lg bg-white/5 border border-white/5">
<div className="text-[10px] font-medium text-slate-300 mb-1.5">
{t("layout.webcamShape")}
</div>
<div className="grid grid-cols-4 gap-1.5">
{(
[
{ value: "rectangle", label: "Rect" },
{ value: "circle", label: "Circle" },
{ value: "square", label: "Square" },
{ value: "rounded", label: "Rounded" },
] as Array<{ value: WebcamMaskShape; label: string }>
).map((shape) => (
<button
key={shape.value}
type="button"
onClick={() => onWebcamMaskShapeChange?.(shape.value)}
className={cn(
"h-10 rounded-lg border flex flex-col items-center justify-center gap-0.5 transition-all",
webcamMaskShape === shape.value
? "bg-[#34B27B] border-[#34B27B] text-white"
: "bg-white/5 border-white/10 hover:bg-white/10 hover:border-white/20 text-slate-400",
)}
>
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
{shape.value === "rectangle" && (
<rect
x="1"
y="3"
width="14"
height="10"
rx="2"
stroke="currentColor"
strokeWidth="1.5"
/>
)}
{shape.value === "circle" && (
<circle
cx="8"
cy="8"
r="6.5"
stroke="currentColor"
strokeWidth="1.5"
/>
)}
{shape.value === "square" && (
<rect
x="2"
y="2"
width="12"
height="12"
rx="1"
stroke="currentColor"
strokeWidth="1.5"
/>
)}
{shape.value === "rounded" && (
<rect
x="1"
y="3"
width="14"
height="10"
rx="5"
stroke="currentColor"
strokeWidth="1.5"
/>
)}
</svg>
<span className="text-[8px] leading-none">{shape.label}</span>
</button>
))}
</div>
</div>
)}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Localize shape labels and expose selected state to assistive tech.

The option labels are hardcoded in English, so they won’t translate in es / zh-CN. Also, this single-select button group should expose pressed state (aria-pressed) for better accessibility.

💡 Suggested patch
-											{(
-												[
-													{ value: "rectangle", label: "Rect" },
-													{ value: "circle", label: "Circle" },
-													{ value: "square", label: "Square" },
-													{ value: "rounded", label: "Rounded" },
-												] as Array<{ value: WebcamMaskShape; label: string }>
-											).map((shape) => (
+											{(
+												[
+													{ value: "rectangle", label: t("layout.webcamShapeRectangle") },
+													{ value: "circle", label: t("layout.webcamShapeCircle") },
+													{ value: "square", label: t("layout.webcamShapeSquare") },
+													{ value: "rounded", label: t("layout.webcamShapeRounded") },
+												] as Array<{ value: WebcamMaskShape; label: string }>
+											).map((shape) => (
 												<button
 													key={shape.value}
 													type="button"
 													onClick={() => onWebcamMaskShapeChange?.(shape.value)}
+													aria-pressed={webcamMaskShape === shape.value}
 													className={cn(
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{webcamLayoutPreset === "picture-in-picture" && (
<div className="mt-2 p-2 rounded-lg bg-white/5 border border-white/5">
<div className="text-[10px] font-medium text-slate-300 mb-1.5">
{t("layout.webcamShape")}
</div>
<div className="grid grid-cols-4 gap-1.5">
{(
[
{ value: "rectangle", label: "Rect" },
{ value: "circle", label: "Circle" },
{ value: "square", label: "Square" },
{ value: "rounded", label: "Rounded" },
] as Array<{ value: WebcamMaskShape; label: string }>
).map((shape) => (
<button
key={shape.value}
type="button"
onClick={() => onWebcamMaskShapeChange?.(shape.value)}
className={cn(
"h-10 rounded-lg border flex flex-col items-center justify-center gap-0.5 transition-all",
webcamMaskShape === shape.value
? "bg-[#34B27B] border-[#34B27B] text-white"
: "bg-white/5 border-white/10 hover:bg-white/10 hover:border-white/20 text-slate-400",
)}
>
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
{shape.value === "rectangle" && (
<rect
x="1"
y="3"
width="14"
height="10"
rx="2"
stroke="currentColor"
strokeWidth="1.5"
/>
)}
{shape.value === "circle" && (
<circle
cx="8"
cy="8"
r="6.5"
stroke="currentColor"
strokeWidth="1.5"
/>
)}
{shape.value === "square" && (
<rect
x="2"
y="2"
width="12"
height="12"
rx="1"
stroke="currentColor"
strokeWidth="1.5"
/>
)}
{shape.value === "rounded" && (
<rect
x="1"
y="3"
width="14"
height="10"
rx="5"
stroke="currentColor"
strokeWidth="1.5"
/>
)}
</svg>
<span className="text-[8px] leading-none">{shape.label}</span>
</button>
))}
</div>
</div>
)}
{webcamLayoutPreset === "picture-in-picture" && (
<div className="mt-2 p-2 rounded-lg bg-white/5 border border-white/5">
<div className="text-[10px] font-medium text-slate-300 mb-1.5">
{t("layout.webcamShape")}
</div>
<div className="grid grid-cols-4 gap-1.5">
{(
[
{ value: "rectangle", label: t("layout.webcamShapeRectangle") },
{ value: "circle", label: t("layout.webcamShapeCircle") },
{ value: "square", label: t("layout.webcamShapeSquare") },
{ value: "rounded", label: t("layout.webcamShapeRounded") },
] as Array<{ value: WebcamMaskShape; label: string }>
).map((shape) => (
<button
key={shape.value}
type="button"
onClick={() => onWebcamMaskShapeChange?.(shape.value)}
aria-pressed={webcamMaskShape === shape.value}
className={cn(
"h-10 rounded-lg border flex flex-col items-center justify-center gap-0.5 transition-all",
webcamMaskShape === shape.value
? "bg-[`#34B27B`] border-[`#34B27B`] text-white"
: "bg-white/5 border-white/10 hover:bg-white/10 hover:border-white/20 text-slate-400",
)}
>
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
{shape.value === "rectangle" && (
<rect
x="1"
y="3"
width="14"
height="10"
rx="2"
stroke="currentColor"
strokeWidth="1.5"
/>
)}
{shape.value === "circle" && (
<circle
cx="8"
cy="8"
r="6.5"
stroke="currentColor"
strokeWidth="1.5"
/>
)}
{shape.value === "square" && (
<rect
x="2"
y="2"
width="12"
height="12"
rx="1"
stroke="currentColor"
strokeWidth="1.5"
/>
)}
{shape.value === "rounded" && (
<rect
x="1"
y="3"
width="14"
height="10"
rx="5"
stroke="currentColor"
strokeWidth="1.5"
/>
)}
</svg>
<span className="text-[8px] leading-none">{shape.label}</span>
</button>
))}
</div>
</div>
)}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/video-editor/SettingsPanel.tsx` around lines 631 - 711, The
shape option labels are hardcoded and the button group doesn't expose pressed
state; update the shape list used in the webcamLayoutPreset ===
"picture-in-picture" block (the array mapped to render buttons) to use
localization keys via t(...) instead of hardcoded
"Rect"/"Circle"/"Square"/"Rounded", and add aria-pressed={webcamMaskShape ===
shape.value} to each button rendered by the map so assistive tech can detect the
selected state; ensure the onClick still calls
onWebcamMaskShapeChange(shape.value) and keep existing className logic using
webcamMaskShape for visual selection.

@siddharthvaddem
Copy link
Copy Markdown
Owner

awesome! while you are at it, can we also have this #285 fixed? Shape and size go hand in hand. Would really appreciate it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants