Skip to content

feat: Add webcam size with slider#345

Merged
siddharthvaddem merged 5 commits intosiddharthvaddem:mainfrom
GarryLaly:feature/webcam-resize-slider
Apr 8, 2026
Merged

feat: Add webcam size with slider#345
siddharthvaddem merged 5 commits intosiddharthvaddem:mainfrom
GarryLaly:feature/webcam-resize-slider

Conversation

@GarryLaly
Copy link
Copy Markdown
Contributor

@GarryLaly GarryLaly commented Apr 5, 2026

Pull Request Template

Description

Continues and improves the webcam resize feature from PR #289. Merges the original work with main, resolves all conflicts (including new webcamMaskShape feature), fixes broken type-checking and missing wiring, and replaces the small/medium/large preset dropdown with a continuous percentage slider (10%–50%) for finer control over webcam overlay size.

Motivation

PR #289 introduced webcam size presets but had several issues:

  • MAX_STAGE_FRACTION was referenced at module scope before being defined, causing TypeScript compilation errors
  • webcamSizePreset was never wired through the rendering pipeline (canvas preview, export, project persistence), so changing the preset had no visible effect
  • The fixed small/medium/large presets were too coarse — users need finer control over webcam sizing
  • Webcam size was inconsistent between landscape (16:9) and portrait (9:16) aspect ratios due to using canvasWidth/canvasHeight independently instead of a unified reference dimension

Type of Change

  • New Feature
  • Bug Fix
  • Refactor / Code Cleanup
  • Documentation Update
  • Other (please specify)

Related Issue(s)

Screenshots / Video

Screenshot:

Screenshot 2026-04-05 at 20 04 15

Video:

Screen.Recording.2026-04-05.at.20.05.27.mov

Testing

  1. Open the video editor with a webcam recording
  2. In the Layout accordion on the settings panel, verify the Webcam Size slider appears (range 10%–50%)
  3. Drag the slider — the webcam overlay on the canvas should resize in real-time
  4. Switch between 16:9 and 9:16 aspect ratios — verify the webcam size is visually consistent
  5. Export as MP4 and GIF — verify the exported video reflects the chosen webcam size
  6. Save and reload a project — verify the webcam size persists correctly
  7. Load an older project (without webcamSizePreset) — verify it defaults to 25%
  8. Run tests: npx vitest run --environment node src/lib/compositeLayout.test.ts — all 9 tests should pass

Checklist

  • I have performed a self-review of my code.
  • I have added any necessary screenshots or videos.
  • I have linked related issue(s) and updated the changelog if applicable.

Thank you for contributing!

Summary by CodeRabbit

  • New Features

    • Adjustable webcam size (10%–50%, default 25%) for picture‑in‑picture with a slider; setting is persisted with projects, applied in playback, and honored during exports.
  • Tests

    • Layout tests updated to check size monotonicity, clamping to bounds, and consistency across aspect ratios.
  • Localization

    • Added "Webcam Size" label in English, Spanish, and Simplified Chinese.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 5, 2026

📝 Walkthrough

Walkthrough

Adds a numeric webcamSizePreset (default 25) and slider UI for picture-in-picture; threads the preset through editor state/history, project persistence, SettingsPanel, VideoEditor, VideoPlayback, layout utils, compositeLayout, exporters, and frame rendering; updates i18n and tests.

Changes

Cohort / File(s) Summary
Types & Constants
src/components/video-editor/types.ts, src/lib/compositeLayout.ts
Introduce WebcamSizePreset type and DEFAULT_WEBCAM_SIZE_PRESET (25); computeCompositeLayout now accepts optional webcamSizePreset.
Editor State & History
src/hooks/useEditorHistory.ts, src/components/video-editor/projectPersistence.ts
Add webcamSizePreset to editor/project state and INITIAL_EDITOR_STATE; normalize/clamp values (10–50) when loading projects.
UI & Props Wiring
src/components/video-editor/SettingsPanel.tsx, src/components/video-editor/VideoEditor.tsx
SettingsPanel props extended with webcamSizePreset, onWebcamSizePresetChange, onWebcamSizePresetCommit; slider rendered for picture-in-picture; VideoEditor wires state, handlers, and passes prop down.
Playback & Layout Helpers
src/components/video-editor/VideoPlayback.tsx, src/components/video-editor/videoPlayback/layoutUtils.ts
VideoPlayback accepts and forwards webcamSizePreset; layoutVideoContent signature updated to accept optional webcamSizePreset.
Composite Layout Logic & Tests
src/lib/compositeLayout.ts, src/lib/compositeLayout.test.ts
Replace fixed overlay sizing with webcamSizeToFraction (clamps 10–50 → 0.10–0.50); use referenceDim = sqrt(w*h) for overlay bounds; tests updated for monotonic sizing and clamping.
Export & Frame Rendering
src/lib/exporter/frameRenderer.ts, src/lib/exporter/gifExporter.ts, src/lib/exporter/videoExporter.ts
Exporter configs and FrameRenderer accept/propagate webcamSizePreset so exports respect the preset.
I18n
src/i18n/locales/en/settings.json, src/i18n/locales/es/settings.json, src/i18n/locales/zh-CN/settings.json
Add layout.webcamSize translations: English "Webcam Size", Spanish "Tamaño de cámara", Chinese "摄像头大小".

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • siddharthvaddem

Poem

a tiny slider hums at 2am,
webcam grows from small to glam,
picture-in-picture now plays nice,
clamp it, sqrt it, roll the dice.
✨📹 — ship it, lowkey elegant.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed Title clearly and concisely summarizes the main feature: adding a webcam size slider control for resizing the overlay.
Description check ✅ Passed PR description follows template structure with all key sections filled: description, motivation, type of change, related issues, screenshots/video, testing steps, and checklist completion.
Linked Issues check ✅ Passed All coding requirements from #285 are met: webcam overlay is now resizable via slider (10-50%), properly wired through rendering pipeline, and addresses the portrait format visibility issue.
Out of Scope Changes check ✅ Passed All changes directly support the webcam sizing feature: type definitions, UI controls, state management, persistence, export integration, and i18n strings. No unrelated refactoring or scope creep detected.

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


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

@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 (2)
src/lib/compositeLayout.test.ts (1)

131-150: Rename this test or assert the centering contract again.

The title still says the stack is centered, but the assertions only verify full-canvas placement and never use maxContentSize. As written, this would stay green even if centering was removed accidentally.

🧪 Suggested adjustment
-it("centers the combined screen and webcam stack in vertical stack mode", () => {
+it("fills the canvas in vertical stack mode", () => {

If centering is still the intended contract, keep the current name and restore assertions that actually use maxContentSize.

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

In `@src/lib/compositeLayout.test.ts` around lines 131 - 150, The test "centers
the combined screen and webcam stack in vertical stack mode" currently verifies
full-canvas placement but never exercises maxContentSize, so either rename the
test to reflect full-canvas behavior or restore assertions to verify centering
using maxContentSize; update the test around computeCompositeLayout to assert
that the combined content width/height respects maxContentSize and is centered
within canvas (e.g., check that (canvas.width - maxContentSize.width)/2 equals
the left x offset of the stacked group and similarly for vertical offsets), or
change the test name to something like "fills canvas in vertical-stack mode when
maxContentSize >= canvas" if you intend to keep the existing assertions.
src/components/video-editor/types.ts (1)

6-9: Export the slider bounds from the same source as the default.

10 and 50 are now duplicated in src/lib/compositeLayout.ts and src/components/video-editor/projectPersistence.ts, while only 25 is shared here. If the range changes later, preview, export, persistence, and tests can drift silently.

♻️ Suggested cleanup
 /** Webcam size as a percentage of the canvas reference dimension (10–50). */
 export type WebcamSizePreset = number;
 
+export const MIN_WEBCAM_SIZE_PRESET: WebcamSizePreset = 10;
+export const MAX_WEBCAM_SIZE_PRESET: WebcamSizePreset = 50;
 export const DEFAULT_WEBCAM_SIZE_PRESET: WebcamSizePreset = 25;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/video-editor/types.ts` around lines 6 - 9, Declare and export
explicit min/max constants alongside DEFAULT_WEBCAM_SIZE_PRESET in the same
module (e.g., MIN_WEBCAM_SIZE_PRESET and MAX_WEBCAM_SIZE_PRESET) and change
usages in compositeLayout and projectPersistence to import these constants
instead of hardcoded 10 and 50; update any related types or comments for
WebcamSizePreset if needed so the slider bounds and default come from a single
source of truth.
🤖 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/lib/compositeLayout.ts`:
- Around line 61-65: webcamSizeToFraction currently lets NaN propagate through
Math.min/Math.max; update the function (webcamSizeToFraction) to guard the
incoming percent by first checking Number.isFinite(percent) (or
!Number.isNaN(percent)) and substituting a sensible default (e.g., 10) when it’s
invalid, then perform the existing clamping (clamped) and return clamped/100;
ensure the guard covers non-numeric and infinite values so scale/width/height
won't become NaN.

---

Nitpick comments:
In `@src/components/video-editor/types.ts`:
- Around line 6-9: Declare and export explicit min/max constants alongside
DEFAULT_WEBCAM_SIZE_PRESET in the same module (e.g., MIN_WEBCAM_SIZE_PRESET and
MAX_WEBCAM_SIZE_PRESET) and change usages in compositeLayout and
projectPersistence to import these constants instead of hardcoded 10 and 50;
update any related types or comments for WebcamSizePreset if needed so the
slider bounds and default come from a single source of truth.

In `@src/lib/compositeLayout.test.ts`:
- Around line 131-150: The test "centers the combined screen and webcam stack in
vertical stack mode" currently verifies full-canvas placement but never
exercises maxContentSize, so either rename the test to reflect full-canvas
behavior or restore assertions to verify centering using maxContentSize; update
the test around computeCompositeLayout to assert that the combined content
width/height respects maxContentSize and is centered within canvas (e.g., check
that (canvas.width - maxContentSize.width)/2 equals the left x offset of the
stacked group and similarly for vertical offsets), or change the test name to
something like "fills canvas in vertical-stack mode when maxContentSize >=
canvas" if you intend to keep the existing assertions.
🪄 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: 62b66e5b-2a2c-4608-9d85-de9d7876be55

📥 Commits

Reviewing files that changed from the base of the PR and between da16872 and 7920156.

📒 Files selected for processing (15)
  • src/components/video-editor/SettingsPanel.tsx
  • src/components/video-editor/VideoEditor.tsx
  • src/components/video-editor/VideoPlayback.tsx
  • 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

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.

🧹 Nitpick comments (1)
src/lib/compositeLayout.ts (1)

154-155: Consider lowercase naming for the computed variable.

MAX_STAGE_FRACTION uses constant-style naming (SCREAMING_CASE) but is now a computed value derived from input. A name like stageFraction would better signal that it varies per call. Very minor style nit.

♻️ Optional rename
-	const MAX_STAGE_FRACTION = webcamSizeToFraction(webcamSizePreset);
+	const stageFraction = webcamSizeToFraction(webcamSizePreset);

Then update line 214-215:

-	const maxWidth = Math.max(transform.minSize, referenceDim * MAX_STAGE_FRACTION);
-	const maxHeight = Math.max(transform.minSize, referenceDim * MAX_STAGE_FRACTION);
+	const maxWidth = Math.max(transform.minSize, referenceDim * stageFraction);
+	const maxHeight = Math.max(transform.minSize, referenceDim * stageFraction);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/compositeLayout.ts` around lines 154 - 155, Rename the computed
constant MAX_STAGE_FRACTION to a lowercased, non-constant-style name (e.g.,
stageFraction) because it’s derived from input via
webcamSizeToFraction(webcamSizePreset); update all references to
MAX_STAGE_FRACTION (including usages near the previous lines referenced around
where MAX_STAGE_FRACTION was used) to the new name so the code consistently
reflects that this value varies per call.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/lib/compositeLayout.ts`:
- Around line 154-155: Rename the computed constant MAX_STAGE_FRACTION to a
lowercased, non-constant-style name (e.g., stageFraction) because it’s derived
from input via webcamSizeToFraction(webcamSizePreset); update all references to
MAX_STAGE_FRACTION (including usages near the previous lines referenced around
where MAX_STAGE_FRACTION was used) to the new name so the code consistently
reflects that this value varies per call.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8aaa7c4a-60c9-4bca-9521-50bd05d5665f

📥 Commits

Reviewing files that changed from the base of the PR and between 7920156 and 2ee7ccd.

📒 Files selected for processing (1)
  • src/lib/compositeLayout.ts

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/video-editor/VideoEditor.tsx (1)

272-293: ⚠️ Potential issue | 🔴 Critical

Critical: webcamSizePreset is missing from the unsaved-change snapshot

Line 272-293 builds currentProjectSnapshot, but it never includes webcamSizePreset. So if the user only changes webcam size, unsaved-change detection won’t flip, which can silently drop edits on close. kinda cursed edge case, but real.

🐛 Minimal fix
 		return createProjectSnapshot(currentProjectMedia, {
 			wallpaper,
 			shadowIntensity,
 			showBlur,
 			motionBlurAmount,
 			borderRadius,
 			padding,
 			cropRegion,
 			zoomRegions,
 			trimRegions,
 			speedRegions,
 			annotationRegions,
 			aspectRatio,
 			webcamLayoutPreset,
 			webcamMaskShape,
+			webcamSizePreset,
 			webcamPosition,
 			exportQuality,
 			exportFormat,
 			gifFrameRate,
 			gifLoop,
 			gifSizePreset,
 		});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/video-editor/VideoEditor.tsx` around lines 272 - 293, The
snapshot created by createProjectSnapshot(currentProjectMedia, {...}) omits
webcamSizePreset so changes to only webcam size won't mark the project as dirty;
update the call site in VideoEditor.tsx to include webcamSizePreset in the
object passed to createProjectSnapshot (alongside webcamLayoutPreset,
webcamMaskShape, webcamPosition, etc.) so snapshots reflect webcam size changes
and unsaved-change detection works correctly.
🤖 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/types.ts`:
- Around line 6-9: Add exported constants for the webcam size bounds and use
them as the single source of truth: define and export MIN_WEBCAM_SIZE_PRESET
(10) and MAX_WEBCAM_SIZE_PRESET (50) alongside WebcamSizePreset and
DEFAULT_WEBCAM_SIZE_PRESET in types.ts, update any normalization, validation, or
UI/layout code that currently uses the magic numbers 10/50 to reference these
new constants (e.g., any functions that clamp or validate the preset value), and
ensure any docs/comments reference the constants instead of hard-coded values.

---

Outside diff comments:
In `@src/components/video-editor/VideoEditor.tsx`:
- Around line 272-293: The snapshot created by
createProjectSnapshot(currentProjectMedia, {...}) omits webcamSizePreset so
changes to only webcam size won't mark the project as dirty; update the call
site in VideoEditor.tsx to include webcamSizePreset in the object passed to
createProjectSnapshot (alongside webcamLayoutPreset, webcamMaskShape,
webcamPosition, etc.) so snapshots reflect webcam size changes and
unsaved-change detection works correctly.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: be420b9c-a564-4232-be69-cbb4af6ab5df

📥 Commits

Reviewing files that changed from the base of the PR and between 5320f76 and 0e1a69a.

📒 Files selected for processing (7)
  • src/components/video-editor/SettingsPanel.tsx
  • src/components/video-editor/VideoEditor.tsx
  • src/components/video-editor/projectPersistence.ts
  • src/components/video-editor/types.ts
  • src/i18n/locales/en/settings.json
  • src/i18n/locales/es/settings.json
  • src/i18n/locales/zh-CN/settings.json

@siddharthvaddem siddharthvaddem merged commit e7d5f51 into siddharthvaddem:main Apr 8, 2026
4 checks passed
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.

[Feature]: The webcam recording is not resizable

2 participants