Skip to content

feat: implement zoom speed#373

Open
Moncef-Mhz wants to merge 5 commits intosiddharthvaddem:mainfrom
Moncef-Mhz:adjust-zoom-speed
Open

feat: implement zoom speed#373
Moncef-Mhz wants to merge 5 commits intosiddharthvaddem:mainfrom
Moncef-Mhz:adjust-zoom-speed

Conversation

@Moncef-Mhz
Copy link
Copy Markdown

@Moncef-Mhz Moncef-Mhz commented Apr 6, 2026

Description

Adds interactive region controls for zoom, trim, and speed segments in the video editor timeline. Users can now create, adjust, and delete these regions directly from the settings panel and timeline UI.

Motivation

Previously there was no way to fine-tune zoom, trim, or speed regions after placing them. This change gives users precise control over each region's parameters, improving the overall editing experience.

Type of Change

  • New Feature

Related Issue(s)

#265

Screenshots / Video

Video (if applicable):

2026-04-07.00-51-10.mp4

Testing

  1. Open the video editor and load a video.
  2. Add a zoom region on the timeline, verify you can adjust depth and focus mode in the settings panel.
  3. Add a trim region, verify it can be repositioned and deleted.
  4. Add a speed region, verify playback speed options appear and apply correctly.
  5. Confirm that selecting a region on the timeline highlights the correct settings panel section.
  6. Run npx biome check . and confirm no errors before committing.

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

    • Added a Zoom Speed selector with four presets (Instant, Fast, Smooth, Lazy).
    • Added per-region zoom transition controls: draggable handles on the timeline and a settings grid to adjust zoom-in/zoom-out durations, with automatic scaling to fit region length.
  • Localization

    • Added Zoom Speed translations for English, Spanish, and Chinese.

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: 112f02fe03

ℹ️ 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".

…le smooth transitions between connected regions
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 7, 2026

📝 Walkthrough

Walkthrough

Adds per-region zoom-in/out duration state and controls: UI selector, timeline drag handles to edit durations, propagation of duration changes through SettingsPanel → VideoEditor → TimelineEditor → Item, and playback strength computation updated to use per-region durations and proportional scaling.

Changes

Cohort / File(s) Summary
Settings & Editor UI
src/components/video-editor/SettingsPanel.tsx, src/components/video-editor/VideoEditor.tsx
SettingsPanel: new optional props selectedZoomInDuration, selectedZoomOutDuration, onZoomDurationChange(zoomIn, zoomOut) and a conditional "Zoom Speed" 4-button grid. VideoEditor: computes selected zoom durations (with fallbacks to defaults), adds handleZoomDurationChange, and passes onZoomDurationChange into TimelineEditor. Also removed h-7 from background TabsList.
Timeline items & interactions
src/components/video-editor/timeline/Item.tsx, src/components/video-editor/timeline/TimelineEditor.tsx
Item: added props zoomInDurationMs?, zoomOutDurationMs?, onZoomDurationChange?; converts pointer px→ms during drag, renders draggable left/right transition handles for variant === "zoom", clamps values, and emits updates. TimelineEditor: threaded new callback prop, populated per-item zoomInDurationMs/zoomOutDurationMs when building zoom timeline items and forwards callback into Item.
Core types & playback logic
src/components/video-editor/types.ts, src/components/video-editor/videoPlayback/zoomRegionUtils.ts
types.ts: ZoomRegion now includes optional zoomInDurationMs/zoomOutDurationMs. zoomRegionUtils: added DEFAULT_ZOOM_IN_MS/DEFAULT_ZOOM_OUT_MS, getDurations(region) with proportional scaling to fit region length, and rewrote computeRegionStrength to confine ramps to region start/end and use easing based on per-region durations (removed previous fixed overlap behavior).
Internationalization
src/i18n/locales/en/settings.json, src/i18n/locales/es/settings.json, src/i18n/locales/zh-CN/settings.json
Added zoom.speed localization keys (title and four options: instant, fast, smooth, lazy) for English, Spanish, and Simplified Chinese.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • siddharthvaddem

Poem

late-night tweaks, drag the handles slow,
instant, fast, smooth, lazy — pick your flow,
pixels turned to ms, math doing its thing,
timelines hum, defaults neatly cling,
kinda cursed, kinda tidy — ship that zoom ring 🚀

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: implement zoom speed' directly and clearly summarizes the main feature addition across all changed files—zoom speed controls for video editor regions.
Description check ✅ Passed PR description covers the main sections with purpose, motivation, type of change, related issue, video demo, testing steps, and completed checklist items.

✏️ 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.

@Moncef-Mhz
Copy link
Copy Markdown
Author

@siddharthvaddem hey, I have implemented zoom can u check it, I am new to open source contributions.

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: 6

🤖 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 169-175: The TODO comment near ZOOM_SPEED_OPTIONS indicates
configurability is planned but untracked; either remove the TODO or replace it
with a reference to a tracking issue and a brief comment explaining intent.
Update the SettingsPanel.tsx comment above the ZOOM_SPEED_OPTIONS constant to
either (a) remove the TODO if configurability won't be implemented soon, or (b)
replace it with a one-line comment like "Make zoom speed configurable — see
ISSUE-123" (or your issue ID) so the work is tracked; ensure the constant name
ZOOM_SPEED_OPTIONS remains unchanged.
- Line 927: The className on the TabsList in SettingsPanel.tsx contains a double
space between "grid-cols-3" and "rounded-lg"; update the TabsList component's
className value (the string passed to TabsList) to remove the extra space so it
reads "... grid grid-cols-3 rounded-lg" to keep classes consistent.
- Around line 572-574: selectedZoomInDuration/selectedZoomOutDuration in
SettingsPanel.tsx are floats coming from VideoEditor.tsx while
ZOOM_SPEED_OPTIONS defines integer zoomIn/zoomOut values, so the strict equality
check in the isActive computation never matches; fix by normalizing before
compare — either round (Math.round) or floor the selectedZoom* values or compare
after casting ZOOM_SPEED_OPTIONS entries to numbers with the same rounding
strategy, or alternatively change
ZOOM_IN_TRANSITION_WINDOW_MS/TRANSITION_WINDOW_MS to integer constants; update
the isActive logic that references selectedZoomInDuration,
selectedZoomOutDuration and ZOOM_SPEED_OPTIONS accordingly so default/smooth
options correctly become active.

In `@src/components/video-editor/timeline/Item.tsx`:
- Around line 117-143: Replace the repeated magic numbers with shared constants
and use the canonical duration logic: export DEFAULT_ZOOM_IN_MS and
DEFAULT_ZOOM_OUT_MS from zoomRegionUtils (or a central constants file), import
them into Item.tsx, and replace all literal occurrences of 1522.575 and 1015.05
with DEFAULT_ZOOM_IN_MS and DEFAULT_ZOOM_OUT_MS respectively (references:
zoomInDurationMs, zoomOutDurationMs, onZoomDurationChange). Additionally, when
computing handle positions/widths use the values returned from getDurations(...)
(or call getDurations with span and the defaults) so the UI uses the rescaled
durations getDurations may produce, preventing handle position divergence from
actual playback timing.
- Around line 127-154: The onPointerDown handler captures e.clientX inside the
onPointerMove closure which is fragile; capture the startX (e.clientX) and any
other needed initial values (e.g., zoomInDurationMs, zoomOutDurationMs) at the
top of the onPointerDown before creating onPointerMove so the closure uses those
captured primitives instead of reading e later; update the onPointerMove logic
to compute deltaPx = moveEvent.clientX - startX and keep the rest
(pixelsToValue, newDuration calc, onZoomDurationChange(id,...)) unchanged, and
ensure pointer capture/release and event listener cleanup still use the original
pointerId and target logic from onPointerDown.

In `@src/components/video-editor/VideoEditor.tsx`:
- Around line 1772-1786: The fallback durations for selectedZoomInDuration and
selectedZoomOutDuration use floating constants ZOOM_IN_TRANSITION_WINDOW_MS and
TRANSITION_WINDOW_MS which don't match the integer presets in
ZOOM_SPEED_OPTIONS, causing mismatches downstream; update the logic in the
selectedZoomInDuration/selectedZoomOutDuration expressions (which reference
selectedZoomId and zoomRegions) to round (or otherwise coerce to integer) the
fallback values so they match the integer presets (e.g., use
Math.round/Math.floor on ZOOM_IN_TRANSITION_WINDOW_MS and TRANSITION_WINDOW_MS)
before returning them to ensure comparisons with ZOOM_SPEED_OPTIONS succeed.
🪄 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: 9f0ff17d-d830-44ce-b4bc-7ee6510c0731

📥 Commits

Reviewing files that changed from the base of the PR and between 2492816 and 8f35cf0.

📒 Files selected for processing (9)
  • src/components/video-editor/SettingsPanel.tsx
  • src/components/video-editor/VideoEditor.tsx
  • src/components/video-editor/timeline/Item.tsx
  • src/components/video-editor/timeline/TimelineEditor.tsx
  • src/components/video-editor/types.ts
  • src/components/video-editor/videoPlayback/zoomRegionUtils.ts
  • src/i18n/locales/en/settings.json
  • src/i18n/locales/es/settings.json
  • src/i18n/locales/zh-CN/settings.json

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.

♻️ Duplicate comments (1)
src/components/video-editor/VideoEditor.tsx (1)

1773-1783: ⚠️ Potential issue | 🟡 Minor

Fallback duration values should be coerced to preset-compatible values.

Lowkey risky UX here: Line 1775 and Line 1781 can return float defaults (1522.575 / 1015.05). If preset matching is exact in the settings selector, the default option may not appear selected until user interaction.

nit: cleaner fallback coercion
 						selectedZoomInDuration={
 							selectedZoomId
 								? (zoomRegions.find((z) => z.id === selectedZoomId)?.zoomInDurationMs ??
-									ZOOM_IN_TRANSITION_WINDOW_MS)
+									Math.round(ZOOM_IN_TRANSITION_WINDOW_MS))
 								: undefined
 						}
 						selectedZoomOutDuration={
 							selectedZoomId
 								? (zoomRegions.find((z) => z.id === selectedZoomId)?.zoomOutDurationMs ??
-									TRANSITION_WINDOW_MS)
+									Math.round(TRANSITION_WINDOW_MS))
 								: undefined
 						}
🤖 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 1773 - 1783, The
selectedZoomInDuration and selectedZoomOutDuration fallbacks may be floats (from
ZOOM_IN_TRANSITION_WINDOW_MS / TRANSITION_WINDOW_MS) which won't match exact
preset options; when computing these props in VideoEditor (use of selectedZoomId
and zoomRegions), coerce the fallback values to preset-compatible integers or
mapped preset values (e.g., round or snap to the nearest preset) before
returning them so the settings selector can match correctly; implement this by
applying a normalization helper (e.g., normalizeToPresetDuration) or
Math.round/nearest-preset logic to the fallback expressions that reference
ZOOM_IN_TRANSITION_WINDOW_MS and TRANSITION_WINDOW_MS.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/components/video-editor/VideoEditor.tsx`:
- Around line 1773-1783: The selectedZoomInDuration and selectedZoomOutDuration
fallbacks may be floats (from ZOOM_IN_TRANSITION_WINDOW_MS /
TRANSITION_WINDOW_MS) which won't match exact preset options; when computing
these props in VideoEditor (use of selectedZoomId and zoomRegions), coerce the
fallback values to preset-compatible integers or mapped preset values (e.g.,
round or snap to the nearest preset) before returning them so the settings
selector can match correctly; implement this by applying a normalization helper
(e.g., normalizeToPresetDuration) or Math.round/nearest-preset logic to the
fallback expressions that reference ZOOM_IN_TRANSITION_WINDOW_MS and
TRANSITION_WINDOW_MS.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 08020be9-f425-492a-ba31-52ae7b8afd6b

📥 Commits

Reviewing files that changed from the base of the PR and between 8f35cf0 and 7409631.

📒 Files selected for processing (1)
  • src/components/video-editor/VideoEditor.tsx

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: 2

♻️ Duplicate comments (2)
src/components/video-editor/timeline/Item.tsx (1)

148-159: ⚠️ Potential issue | 🔴 Critical

Use the normalized durations in the drag handlers.

Lines 149-157 and Lines 193-201 still seed drag math from raw props/defaults instead of the normalized zoomIn / zoomOut values from getDurations(...). For any region shorter than the combined in/out duration, the available span goes negative and the first drag snaps a handle to 0ms, even though render/playback are showing scaled values.

♻️ Small fix
- const initialZoomIn = zoomInDurationMs ?? DEFAULT_ZOOM_IN_MS;
- const initialZoomOut = zoomOutDurationMs ?? DEFAULT_ZOOM_OUT_MS;
+ const initialZoomIn = zoomIn;
+ const initialZoomOut = zoomOut;

Apply the same swap in both pointer handlers so drag, render, and playback all use the same normalized durations.

Also applies to: 192-203

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

In `@src/components/video-editor/timeline/Item.tsx` around lines 148 - 159, The
drag handlers are using raw props/defaults (initialZoomIn/initialZoomOut)
instead of the normalized durations from getDurations, causing incorrect snap
behavior; update both pointer handlers (the onPointerMove and its counterpart
later) to use the normalized zoomIn and zoomOut values returned by getDurations
(replace initialZoomIn/initialZoomOut with zoomIn/zoomOut in the delta/duration
math and the onZoomDurationChange call) so drag, render, and playback use the
same normalized durations.
src/components/video-editor/SettingsPanel.tsx (1)

169-174: ⚠️ Potential issue | 🟠 Major

Smooth is still out of sync with the real default timing.

The canonical default zoom-in rounds to 1523ms, but this preset hardcodes 1522ms. Default zoom regions will still show no active speed button, which is confusing UX. Please derive this pair from the shared transition constants so it can’t drift again.

🔧 Keep the preset synced to the shared constants
+import { TRANSITION_WINDOW_MS, ZOOM_IN_TRANSITION_WINDOW_MS } from "./videoPlayback/constants";
+
 const ZOOM_SPEED_OPTIONS = [
 	{ label: "Instant", zoomIn: 0, zoomOut: 0 },
 	{ label: "Fast", zoomIn: 500, zoomOut: 350 },
-	{ label: "Smooth", zoomIn: 1522, zoomOut: 1015 },
+	{
+		label: "Smooth",
+		zoomIn: Math.round(ZOOM_IN_TRANSITION_WINDOW_MS),
+		zoomOut: Math.round(TRANSITION_WINDOW_MS),
+	},
 	{ label: "Lazy", zoomIn: 3000, zoomOut: 2000 },
 ];
🤖 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 169 - 174, The
"Smooth" preset in ZOOM_SPEED_OPTIONS hardcodes zoom timings (1522/1015) that
drift from the canonical defaults; update the "Smooth" entry in
ZOOM_SPEED_OPTIONS to derive its zoomIn/zoomOut values from the shared
transition constants instead of literals — import the appropriate constants
(e.g., DEFAULT_ZOOM_IN_MS and DEFAULT_ZOOM_OUT_MS or the names exported from the
shared transitions module) and use those constants for the "Smooth" object's
zoomIn and zoomOut so the preset always matches the canonical defaults.
🤖 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 564-590: The current SettingsPanel rendering uses opt.label from
ZOOM_SPEED_OPTIONS which bypasses i18n; update ZOOM_SPEED_OPTIONS to carry
translation keys (e.g., labelKey) instead of hardcoded English, and change the
render in SettingsPanel.tsx to call t(opt.labelKey) (or t(opt.labelKey, {
defaultValue: opt.label }) if you want a fallback) inside the Button span so
labels are localized; ensure any tests or consumers that construct
ZOOM_SPEED_OPTIONS are updated to use the new labelKey property.

In `@src/components/video-editor/VideoEditor.tsx`:
- Around line 852-863: handleZoomDurationChange currently calls pushState on
every pointermove (Item.tsx -> onZoomDurationChange), spamming undo/history;
change it to use the move-safe pattern: during pointermove call updateState
instead of pushState so intermediate drags do not create history entries, and
ensure a corresponding commitState is called on pointerup/end to record the
final change; locate handleZoomDurationChange and replace the pushState call
with updateState, and verify Item.tsx or the parent drag handlers call
commitState when the drag finishes.

---

Duplicate comments:
In `@src/components/video-editor/SettingsPanel.tsx`:
- Around line 169-174: The "Smooth" preset in ZOOM_SPEED_OPTIONS hardcodes zoom
timings (1522/1015) that drift from the canonical defaults; update the "Smooth"
entry in ZOOM_SPEED_OPTIONS to derive its zoomIn/zoomOut values from the shared
transition constants instead of literals — import the appropriate constants
(e.g., DEFAULT_ZOOM_IN_MS and DEFAULT_ZOOM_OUT_MS or the names exported from the
shared transitions module) and use those constants for the "Smooth" object's
zoomIn and zoomOut so the preset always matches the canonical defaults.

In `@src/components/video-editor/timeline/Item.tsx`:
- Around line 148-159: The drag handlers are using raw props/defaults
(initialZoomIn/initialZoomOut) instead of the normalized durations from
getDurations, causing incorrect snap behavior; update both pointer handlers (the
onPointerMove and its counterpart later) to use the normalized zoomIn and
zoomOut values returned by getDurations (replace initialZoomIn/initialZoomOut
with zoomIn/zoomOut in the delta/duration math and the onZoomDurationChange
call) so drag, render, and playback use the same normalized durations.
🪄 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: 41b07cc1-3e6b-40ba-8cd1-42b9122b1d98

📥 Commits

Reviewing files that changed from the base of the PR and between 7409631 and 0cb298d.

📒 Files selected for processing (4)
  • src/components/video-editor/SettingsPanel.tsx
  • src/components/video-editor/VideoEditor.tsx
  • src/components/video-editor/timeline/Item.tsx
  • src/components/video-editor/videoPlayback/zoomRegionUtils.ts

@Moncef-Mhz Moncef-Mhz changed the title feat: implement video editor timeline components with interactive zoo… feat: implement zoom speed Apr 8, 2026
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.

1 participant