Skip to content

feat(export): add frame count, ETA, elapsed time, and cancel confirma…#773

Open
MB-Ndhlovu wants to merge 1 commit intoOpenCut-app:mainfrom
MB-Ndhlovu:feat/export-progress-enhancement
Open

feat(export): add frame count, ETA, elapsed time, and cancel confirma…#773
MB-Ndhlovu wants to merge 1 commit intoOpenCut-app:mainfrom
MB-Ndhlovu:feat/export-progress-enhancement

Conversation

@MB-Ndhlovu
Copy link
Copy Markdown

@MB-Ndhlovu MB-Ndhlovu commented Apr 18, 2026

…tion dialog

Implements #739 - Export progress now shows:

  • Frame count: 'Frame 120 / 1200'
  • Elapsed time: 'Elapsed 0:45'
  • Estimated time remaining: '~2:30 remaining'
  • Cancel confirmation dialog before cancelling mid-export

Changes:

  • Extend ExportState interface with totalFrames, currentFrame, elapsedMs
  • ProjectManager.export() computes totalFrames from duration and fps at start
  • onProgress updates currentFrame and elapsedMs via Date.now() delta
  • ExportCancelDialog component wraps cancel action with Radix AlertDialog
  • formatDuration() utility for MM:SS time formatting
  • UI renders frame count, elapsed, and ETA when exporting

⚠️ READ BEFORE SUBMITTING ⚠️

We are not currently accepting PRs except for critical bugs.

If this is a bug fix:

  • I've opened an issue first
  • This was approved by a maintainer

If this is a feature:

This PR will be closed. Please open an issue to discuss first.

Summary by CodeRabbit

  • New Features
    • Export cancellation now requires confirmation via a dialog to prevent accidental cancellation.
    • Enhanced export progress display with elapsed time, estimated remaining time, and frame counts during exporting.

…tion dialog

Implements OpenCut-app#739 - Export progress now shows:
- Frame count: 'Frame 120 / 1200'
- Elapsed time: 'Elapsed 0:45'
- Estimated time remaining: '~2:30 remaining'
- Cancel confirmation dialog before cancelling mid-export

Changes:
- Extend ExportState interface with totalFrames, currentFrame, elapsedMs
- ProjectManager.export() computes totalFrames from duration and fps at start
- onProgress updates currentFrame and elapsedMs via Date.now() delta
- ExportCancelDialog component wraps cancel action with Radix AlertDialog
- formatDuration() utility for MM:SS time formatting
- UI renders frame count, elapsed, and ETA when exporting
@vercel
Copy link
Copy Markdown

vercel bot commented Apr 18, 2026

Someone is attempting to deploy a commit to the OpenCut OSS Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 18, 2026

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2061664b-7371-48f3-bca3-f68e93fdf8c8

📥 Commits

Reviewing files that changed from the base of the PR and between 8bdc894 and 284c8a7.

📒 Files selected for processing (3)
  • apps/web/src/components/editor/export-button.tsx
  • apps/web/src/core/managers/project-manager.ts
  • apps/web/src/lib/export/index.ts
 __________________________________________________________________________________________________________________
< 🎵 Bugs, so boring, they've got me snoring... Bugs, so bad, they're driving me mad! Bugs, no fun, I am so done! 🎵 >
 ------------------------------------------------------------------------------------------------------------------
  \
   \   \
        \ /\
        ( )
      .( o ).
✨ 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.

Tip

You can validate your CodeRabbit configuration file in your editor.

If your editor has YAML language server, you can enable auto-completion and validation by adding # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json at the top of your CodeRabbit configuration file.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 18, 2026

📝 Walkthrough

Walkthrough

The pull request enhances the export feature with detailed progress tracking, displaying frame counts, elapsed time, and estimated time remaining in the UI, and introduces a confirmation dialog for canceling exports.

Changes

Cohort / File(s) Summary
Export Button UI
apps/web/src/components/editor/export-button.tsx
Added ExportCancelDialog component for confirming export cancellations; enhanced progress display with frame counts and time formatting; introduced formatDuration() helper to display mm:ss durations; destructures and renders totalFrames, currentFrame, and elapsedMs from export state during the exporting phase.
Export State & Core Logic
apps/web/src/core/managers/project-manager.ts, apps/web/src/lib/export/index.ts
Extended ExportState interface with optional totalFrames, currentFrame, and elapsedMs fields; updated ProjectManager.export() to initialize these fields and track exportStartTime; modified renderer onProgress handler to derive and populate frame counts and elapsed time into export state during progression.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Poem

🐰 Whiskers twitching with glee,
Progress frames dance wild and free,
Elapsed seconds tick away,
ETA blooms—export's ballet!
Cancel confirmed, no regrets today!

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description provides detailed context and implementation details. However, it does not address the critical repository requirement stated in the template that feature PRs require prior issue discussion and maintainer approval, yet the author proceeded with the feature implementation. The PR violates the repository's stated policy. Acknowledge in the description that this is a feature PR submitted despite the repository's note that feature PRs will be closed and issues should be opened first for discussion.
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 (1 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main changes: adding frame count display, ETA calculation, elapsed time tracking, and a cancel confirmation dialog to the export functionality.

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

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/components/editor/export-button.tsx`:
- Around line 54-58: The generic popover close handler (handlePopoverOpenChange)
is cancelling exports unconditionally via editor.project.cancelExport() and
editor.project.clearExportState(); change it to not cancel or clear when an
export is in progress—check the project's export status (e.g.,
editor.project.isExporting() or the export state object used elsewhere) and if
exporting, prevent closing the popover / bail out of the handler so the
confirmation flow can run; only call cancelExport() and clearExportState() when
no export is active.

In `@apps/web/src/core/managers/project-manager.ts`:
- Around line 214-217: activeProject.settings.fps (and options.fps) is a
FrameRate object, so before computing totalFrames convert exportFps to a numeric
value using the frameRateToFloat helper: import frameRateToFloat from
'@/lib/fps/utils', compute a numericFps = frameRateToFloat(exportFps) (or call
inline) and use numericFps when calculating totalFrames (the symbols to update
are exportFps and totalFrames in the block that references activeProject and
timeline.getTotalDuration()).
🪄 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: 2061664b-7371-48f3-bca3-f68e93fdf8c8

📥 Commits

Reviewing files that changed from the base of the PR and between 8bdc894 and 284c8a7.

📒 Files selected for processing (3)
  • apps/web/src/components/editor/export-button.tsx
  • apps/web/src/core/managers/project-manager.ts
  • apps/web/src/lib/export/index.ts

Comment on lines +54 to +58
const handlePopoverOpenChange = ({ open }: { open: boolean }) => {
if (!open) {
editor.project.cancelExport();
editor.project.clearExportState();
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don’t cancel exports from the generic popover close handler.

This bypasses the new confirmation flow: clicking outside the popover while exporting still cancels and clears state without asking.

🐛 Proposed fix: keep the popover open while exporting
 	const editor = useEditor();
 	const activeProject = useEditor((e) => e.project.getActiveOrNull());
+	const exportState = useEditor((e) => e.project.getExportState());
 	const hasProject = !!activeProject;
 
 	const handlePopoverOpenChange = ({ open }: { open: boolean }) => {
+		if (!open && exportState.isExporting) {
+			return;
+		}
 		if (!open) {
-			editor.project.cancelExport();
 			editor.project.clearExportState();
 		}
 		setIsExportPopoverOpen(open);
 	};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/editor/export-button.tsx` around lines 54 - 58, The
generic popover close handler (handlePopoverOpenChange) is cancelling exports
unconditionally via editor.project.cancelExport() and
editor.project.clearExportState(); change it to not cancel or clear when an
export is in progress—check the project's export status (e.g.,
editor.project.isExporting() or the export state object used elsewhere) and if
exporting, prevent closing the popover / bail out of the handler so the
confirmation flow can run; only call cancelExport() and clearExportState() when
no export is active.

Comment on lines +214 to +217
const activeProject = this.editor.project.getActive();
const duration = this.editor.timeline.getTotalDuration();
const exportFps = options.fps ?? activeProject.settings.fps;
const totalFrames = Math.round((duration / 1000) * exportFps);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect FrameRate-related helpers/usages. Expected: find a numeric conversion
# helper or the object fields needed to compute frames-per-second.
rg -n -C3 'FrameRate|fpsTo|toFps|framesPerSecond|numerator|denominator|num|den' --glob '!node_modules/**'

# Confirm the problematic arithmetic site.
rg -n -C3 '\(duration / 1000\) \* exportFps|totalFrames' apps/web/src/core/managers/project-manager.ts

Repository: OpenCut-app/OpenCut

Length of output: 50379


🏁 Script executed:

cat -n apps/web/src/core/managers/project-manager.ts | sed -n '200,230p'

Repository: OpenCut-app/OpenCut

Length of output: 1155


🏁 Script executed:

rg -n "interface ExportOptions|type ExportOptions|export.*ExportOptions" apps/web/src --max-count=5

Repository: OpenCut-app/OpenCut

Length of output: 261


🏁 Script executed:

rg -n "settings.*fps|fps.*:" apps/web/src --max-count=10 -A 2

Repository: OpenCut-app/OpenCut

Length of output: 27415


🏁 Script executed:

rg -n "export.*frameRateToFloat|function frameRateToFloat" apps/web/src --max-count=3 -B 1 -A 5

Repository: OpenCut-app/OpenCut

Length of output: 543


Convert FrameRate to a numeric value before computing totalFrames.

Line 217 multiplies by exportFps, which is a FrameRate object, not a number. Use the standard frameRateToFloat() helper:

Fix
-		const totalFrames = Math.round((duration / 1000) * exportFps);
+		const totalFrames = Math.round((duration / 1000) * frameRateToFloat(exportFps));

Import frameRateToFloat from @/lib/fps/utils.

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

In `@apps/web/src/core/managers/project-manager.ts` around lines 214 - 217,
activeProject.settings.fps (and options.fps) is a FrameRate object, so before
computing totalFrames convert exportFps to a numeric value using the
frameRateToFloat helper: import frameRateToFloat from '@/lib/fps/utils', compute
a numericFps = frameRateToFloat(exportFps) (or call inline) and use numericFps
when calculating totalFrames (the symbols to update are exportFps and
totalFrames in the block that references activeProject and
timeline.getTotalDuration()).

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