Skip to content
This repository was archived by the owner on Jun 7, 2026. It is now read-only.

Studio Dasboard with Separated UI for future scalability and Fixes#577

Merged
siddharthvaddem merged 4 commits into
siddharthvaddem:mainfrom
makaradam:feature/open-studio-and-import
May 31, 2026
Merged

Studio Dasboard with Separated UI for future scalability and Fixes#577
siddharthvaddem merged 4 commits into
siddharthvaddem:mainfrom
makaradam:feature/open-studio-and-import

Conversation

@makaradam

@makaradam makaradam commented May 11, 2026

Copy link
Copy Markdown
Contributor

Pull Request Template

Description

Studio / Editor Dashboard (Empty State)

Closes #576

Studio / Editor Dashboard (Empty State)

TOOLBAR BEFORE

Screenshot 2026-05-11 184414

TOOLBAR AFTER

image
  • Removed Open Video and Load Project from the main recorder toolbar; replaced with a single Studio button that opens the editor — a clean entry point that can scale to tutorials, FAQ, changelogs, or sponsor spots later

  • Built the editor empty state dashboard with Import Video File… and Load Project… buttons, supported formats hint, and a drag & drop zone

image

Project Management Logic

  • Importing a video now correctly creates an unsaved project (same treatment as a freshly recorded session), so the save flow is consistent from the start
  • File → New Project is now protected: if you have unsaved changes it shows a Save / Discard / Cancel dialog before clearing
  • File → Load Project (and the new header button) now has the same unsaved-changes protection — added a new "Load Project" variant to the dialog with its own copy
  • Added a New Project button to the editor header bar (next to Load Project) so you don't have to go through the File menu
  • Fixed a bug where New Project didn't actually clear the main process state — currentProjectPath was never nulled out, so switching to the recorder and back would reopen the discarded project

Design Decision — No "Import Video" in the File Menu

The File menu intentionally does not include an Import Video option.
The editor currently operates on a single video / single timeline model.
Adding File → Import Video would have effectively triggered a "new project"
action under the hood — silently discarding the current session and loading
a new video — which would be deeply confusing for end users with no visual
indication that their work was replaced.

The deliberate choice is to keep video import on the empty state dashboard
only
, where the context is unambiguous (there is nothing open to lose).
Once the editor evolves to support multi-clip timelines or video stitching,
a proper File → Import Video flow can be introduced with the right UX
scaffolding around it.

Drag & Drop

image
  • Drop zone in the empty state accepts .openscreen project files only
  • Dropping a non-.openscreen file shows an "Unsupported Format" dialog explaining to use Import Video instead
  • If the file can't be loaded, shows a "Could Not Open File" dialog
image
  • Fixed the actual drag & drop being broken entirely — File.path was removed in Electron 32+ (this project runs Electron 41); replaced with webUtils.getPathForFile()

Editor Loading & White Flash Fix

image
  • The editor window previously flashed a white screen before React had a chance to paint — a jarring UX issue on every open
  • Fixed at two levels: index.html now sets background: #09090b as an inline body style so the dark background is applied instantly before any JavaScript loads; VideoEditor is now lazy-loaded via React.lazy + Suspense with a custom dark-background spinner as the fallback, so the code-split chunk download no longer shows a blank white screen
  • The spinner displays contextual loading text: "Loading editor…" on cold open, switching to "Loading video…" when an existing session or video is detected — both messages are fully localized across all 11 languages

Bug Fixes

image
  • Webcam state leak — webcam video from a previous recording was bleeding into a new project after discarding or creating a new one; fixed by explicitly clearing webcam path state on both new project and video import
  • Device selector positioning — webcam and microphone selector panels were appearing behind the toolbar due to a mismatched pixel value (bottom-[68px] vs the actual 80px toolbar height); corrected so they float above it properly
  • Dialog variant flash (UnsavedChangesDialog) — when dismissing the unsaved changes dialog, content briefly flashed between the "New Project" and "Close" variants during the Radix UI closing animation; fixed with a frozen ref that preserves the last non-null variant throughout the animation
  • Dialog variant flash (drop error dialog) — same issue in the empty state: "Unsupported Format" was flashing to "Could Not Open File" for a frame on close; same ref pattern applied
  • Double dialog bug — clicking Load Project was opening two file picker dialogs back to back because both EditorEmptyState and VideoEditor were independently calling loadProjectFile(); fixed by unifying to a single callback

Custom Zoom Regression (Restored Fix)

image

The image represents that even if you wanted to use "Custom zoom" to put it to the corner, you couldn't because of an "invisible boundary". In the previous PR when "Custom-zoom" was created I already handled this edge case, but for some reason with a possible merge from an earlier version, the code might have been erased causing the bug again.

  • The custom zoom area drag-to-reposition feature had a known edge case where invisible boundaries near the edges of the canvas prevented the zoom region from being dragged to the desired position
  • This edge case was originally identified, handled, and shipped in a previous PR
  • It reappeared in this session, most likely due to a subsequent PR being merged that conflicted with or inadvertently overwrote that boundary handling code — a common occurrence in active parallel development branches
  • The fix has been restored to its original intended behavior

MOST functional bug fixes (based on recent main):

1103c93 — When isNativeWindowsCaptureAvailable returned reason="missing-helper", the code was throwing an error instead of gracefully falling back. This killed the recording entirely rather than continuing with the standard web MediaRecorder. Fixed by treating missing-helper the same as unsupported-os — silently return false and let the web recorder take over.
28819bd — Even after that fallback was unblocked, recording still failed on Windows because the fallback code path used navigator.mediaDevices.getDisplayMedia(), which requires a setDisplayMediaRequestHandler registered in the main process — that handler was never implemented. Fixed by replacing the platform-branched capture logic with a single getUserMedia + chromeMediaSource: "desktop" path that works on both macOS and Windows.

Localization

  • Full i18n for the empty state dashboard (title, buttons, formats hint, drag hint, drop overlay) across all 11 languages
  • Drop error dialog titles and messages translated to all 11 languages, including the "use Import Video File button" copy
  • Load Project unsaved-changes dialog translated to all 11 languages
  • Editor loading states ("Loading editor…" / "Loading video…") localized across all 11 languages

Motivation

Type of Change

  • New Feature
  • Bug Fix

Related Issue(s)

Closes #576

Screenshots / Video

Video (if applicable):

I'll send an unlisted YouTube Video for this, as this is quite of a big PR

Test Plan

Empty State & Navigation

  • Studio button opens editor; HUD toolbar no longer has Open Video / Load Project
  • Empty state shows Import Video, Load Project buttons, formats hint, drag hint

Drag & Drop

  • Drop a .openscreen file → project opens
  • Drop an unsupported file → "Unsupported Format" dialog, no flash on close
  • Drop an invalid/corrupt file → "Could Not Open File" dialog

Unsaved Changes Guard

  • New Project with unsaved changes → Save / Discard / Cancel dialog
  • Load Project with unsaved changes → same dialog with Load Project copy
  • Discard → switch to recorder → reopen Studio → empty state (not the old project)

UI & Layout

  • No white flash on editor open
  • Device selector panels appear above the toolbar
  • New Project button visible in editor header

Localization

  • Switch language → empty state, dialogs, and loading text all update correctly

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.

Edge Cases & Fixes

1103c93 + 28819bd — Recording fallback on Windows

Native Windows capture helper is absent in dev mode and optional in
production. The code was throwing instead of falling back, killing
recording entirely. Fixed by treating missing-helper as graceful
fallback to web getUserMedia + chromeMediaSource: "desktop" — works
on both macOS and Windows without the binary.

4cf966a — Drag & drop broken on Electron 32+ (File.path removed)

File.path was silently returning undefined on Electron 41 — dropped
files appeared to load but nothing happened. Replaced with
webUtils.getPathForFile() exposed via contextBridge in preload.

61de5ea — New Project didn't actually reset state

clearCurrentVideoPath() only nulled currentVideoPath. currentProjectPath
stayed set in the main process, so switching to the recorder and reopening
the editor reloaded the discarded project. Fixed by also clearing
currentProjectPath and currentRecordingSession.

c46aea5 — Double file picker on Load Project

Both EditorEmptyState and VideoEditor were independently calling
loadProjectFile(), opening two file pickers back to back. Fixed by
unifying to a single callback passed down as a prop.

4694ea4 + 2faa676 — Dialog content flash during close animation

Radix UI's closing animation sets the controlled value to null before
the exit animation completes. Both UnsavedChangesDialog (New Project vs
Close variants) and the drop error dialog (Unsupported Format vs Could Not
Open variants) were snapping to the wrong content mid-animation. Fixed with
a frozen ref that preserves the last non-null variant for the duration of
the animation.

801f1f6 — Webcam state leak between projects

After discarding a project or importing a new video, the webcam preview
path from the previous session was still mounted. Fixed by explicitly
clearing webcam state on both New Project and video import.

7dd438e — Device selector panels hidden behind toolbar

Webcam and microphone selector panels had bottom-[68px] but the toolbar
is actually 80px tall, causing them to render behind it. Corrected to
the right offset.

7e4595f + 7700c19 — Imported/recorded video not treated as unsaved

Importing a video or finishing a recording bypassed the unsaved changes
flag, so closing or starting a new project skipped the Save dialog
entirely. Fixed by marking the project dirty on both entry points.

96dc6d5 — Custom zoom drag stuck near canvas edges

The drag boundary clamping used a hardcoded scale instead of the custom
zoom scale, creating invisible walls near the canvas edges. The zoom region
couldn't be dragged to the intended position. Restored the correct
scale-aware boundary calculation.

edf5953 + 3c84f11 + 770f05f — White flash on editor open

Three compounding causes: (1) <body> had no background so the OS default
white showed before React painted; (2) VideoEditor was eagerly bundled,
so the JS parse delay showed a blank screen; (3) an insertCSS call wasn't
applied early enough. Fixed with an inline background: #09090b on <body>
in index.html and lazy-loading VideoEditor via React.lazy + Suspense
with a dark spinner fallback.


Thank you for contributing!

Summary by CodeRabbit

  • New Features

    • Added "New Project" menu option (Cmd+N) for creating blank projects
    • Drag-and-drop support for loading .openscreen project files
    • New editor empty state with import video and load project buttons
    • Support for additional video formats (.m4v, .wmv, .flv, .ts)
  • Improvements

    • Enhanced loading indicators during editor startup
    • Updated unsaved changes dialogs for new project and load project workflows
    • Reduced visual flashing on window initialization
  • Localization

    • Added multi-language translations for new UI elements and dialogs across 12+ supported languages

@coderabbitai

coderabbitai Bot commented May 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR builds out a studio dashboard entry point for the editor with full new-project and import-video workflows, complete with unsaved-changes guards and drag-and-drop project loading. It connects the renderer to Electron and native bridge via new IPC APIs for path-based project loading, adds a react-based empty state component, wires up the File menu with a New Project action, updates dialog handling for multi-flow navigation, and provides comprehensive i18n across 11 locales.

Changes

Studio Dashboard & Project Loading

Layer / File(s) Summary
Type Contracts & Renderer Bridges
electron/electron-env.d.ts, electron/preload.ts, src/native/contracts.ts
Added window.electronAPI methods for getPathForFile, loadProjectFileFromPath, and menu subscriptions; extended preload to import webUtils and expose these methods; updated native bridge request union for project loading.
Electron IPC Project Loading
electron/ipc/handlers.ts
Implemented load-project-file-from-path handler with path/extension validation, JSON parsing, session approval (error-tolerant), and project state updates; expanded allowed video formats; updated clear-current-video-path to reset project context.
Native Bridge Service Layer
electron/ipc/nativeBridge.ts, electron/native-bridge/services/projectService.ts, src/native/client.ts
Wired ProjectService with loadProjectFileFromPath callback; registered NativeBridgeContext capability; added project domain IPC handler routing; exposed client method.
Window Creation & Visual Polish
electron/windows.ts, index.html
Updated HUD overlay and editor window to start hidden and display on ready-to-show event; injected CSS to pre-apply dark background before React render; added dark class and body styling to HTML.
Menu & System Integration
electron/main.ts
Added "New Project" File menu entry with CmdOrCtrl+N accelerator; expanded sendEditorMenuAction channel union.
EditorEmptyState Component
src/components/video-editor/EditorEmptyState.tsx
New React component rendering editor landing screen with import video / load project buttons, drag-and-drop zone with .openscreen validation, error dialog with stable content during animations.
UnsavedChangesDialog Multi-Action Variants
src/components/video-editor/UnsavedChangesDialog.tsx
Extended dialog with variant prop (close/newProject/loadProject); dynamically renders detail text and button labels from i18n keys matching the selected variant.
Editor History State Reset
src/hooks/useEditorHistory.ts
Added resetState callback to clear undo/redo stacks and reinitialize editor state to a fresh baseline.
VideoEditor Integration & Multi-Flow Navigation
src/components/video-editor/VideoEditor.tsx
Major refactor: integrated EditorEmptyState conditional render; added confirmDialogVariant state for new-project/load-project unsaved-changes handling; refactored load/new handlers with confirm-save/discard callbacks; consolidated menu listeners; extended handleNewProject to clear video path via IPC and reset editor state.
App Editor Loading State
src/App.tsx
Updated to use scoped i18n (useScopedT) and render centered spinner with loadingEditor translation in editor Suspense fallback.
Launch Window Dashboard Simplification
src/components/launch/LaunchWindow.tsx
Removed local video/project picker helpers; replaced separate open-video and open-project buttons with single "open studio" action calling switchToEditor.
VideoPlayback Zoom Focus Clamping Refactor
src/components/video-editor/VideoPlayback.tsx
Replaced depth-based focus clamping (clampFocusToStage) with region-scale-based clamping (clampFocusForRegion using getZoomScale); updated imports and drag interaction handler.
Comprehensive i18n Across 11 Locales
src/i18n/locales/*/dialogs.json, src/i18n/locales/*/editor.json, src/i18n/locales/*/settings.json
Added unsavedChanges variants for new-project/load-project flows, loadingEditor key, emptyState blocks with import/load button labels, supported formats, drag-drop hints, and drop error messages; project.new setting label; openStudio tooltip (en); UTF-8 BOM to select JSON files.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes


Possibly related PRs

  • siddharthvaddem/openscreen#352: Both PRs modify Electron IPC project/video file loading in electron/ipc/handlers.ts (main PR adds load-project-file-from-path and path-validation handling).
  • siddharthvaddem/openscreen#521: Both PRs modify the unsaved-changes flow by extending UnsavedChangesDialog.tsx and wiring it from VideoEditor.tsx for multi-action navigation variants.
  • siddharthvaddem/openscreen#605: Both PRs modify src/hooks/useEditorHistory.ts by altering how editor history state is initialized and reset.

Suggested reviewers

  • siddharthvaddem
  • FabLrc

Poem

📦 a studio is born tonight
empty state glowing dark, drag-and-drop feels right
new projects spawn from menu bar
unsaved changes keep us safe so far
eleven tongues sing the same refrain
localized dreams on the window pane 🌙

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Studio Dashboard with Separated UI for future scalability and Fixes' is related to the main changes (empty state dashboard, toolbar refactor, bug fixes) but is vague and doesn't clearly highlight the primary accomplishment. Consider a more specific title like 'Add editor empty state dashboard and new project workflow with localization' to better convey the core changes.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The PR description comprehensively documents the empty state dashboard, new project flow, bug fixes, localization, and test checklist against the template structure.
Linked Issues check ✅ Passed The implementation fully addresses all coding requirements from issue #576: empty state dashboard, new/load project workflows with unsaved-changes guards, drag-and-drop support, localization across 11 languages, and documented bug fixes.
Out of Scope Changes check ✅ Passed All changes are scoped to the objectives: empty state UI, project management flows, drag-drop, localization, and related bug fixes. No unrelated modifications 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.

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

Copy link
Copy Markdown

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: 370323c417

ℹ️ 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 thread src/components/video-editor/VideoEditor.tsx Outdated
Comment thread src/hooks/useScreenRecorder.ts Outdated

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 10

Caution

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

⚠️ Outside diff range comments (1)
electron/main.ts (1)

114-127: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Lowkey risky: Line 117 can force-close an existing editor and nuke unsaved work.

The concern is valid. At Line 114, if the focused window isn't an editor, createEditorWindowWrapper() gets called (Line 117). That function force-closes mainWindow by setting isForceClosing = true (Line 365), which bypasses the unsaved-changes save prompt in the close handler (Line 374). So if an editor is already open but unfocused, this drops all unsaved changes without warning.

Better approach: first search BrowserWindow.getAllWindows() for an existing unfocused editor using isEditorWindow() and route the action there. Only call createEditorWindowWrapper() if no editor exists.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@electron/main.ts` around lines 114 - 127, The current logic calls
createEditorWindowWrapper() (which force-closes mainWindow) whenever the focused
window isn't an editor, risking loss of unsaved work; change it to first search
BrowserWindow.getAllWindows() for an existing editor via isEditorWindow() and
route the action to that unfocused editor (set targetWindow to that window and
send the channel or wait for did-finish-load if necessary); only call
createEditorWindowWrapper() to create a new editor and assign mainWindow if no
existing editor window is found. Ensure you reference
BrowserWindow.getFocusedWindow(), BrowserWindow.getAllWindows(),
isEditorWindow(), createEditorWindowWrapper(), and mainWindow when making the
change so you avoid invoking the force-close path when an editor already exists.
🧹 Nitpick comments (8)
src/i18n/locales/zh-CN/settings.json (1)

1-1: 💤 Low value

nit: utf-8 bom at the start

there's a byte order mark (BOM) before the opening brace. most modern parsers handle it fine, but json specs kinda discourage it. lowkey might wanna check if your editor is adding this automatically - could cause weird issues with stricter parsers down the line.

not blocking anything though, electron/node will parse this just fine.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/i18n/locales/zh-CN/settings.json` at line 1, The file settings.json
contains a UTF-8 BOM (U+FEFF) before the opening brace; remove the BOM so the
file starts with "{" to avoid issues with strict JSON parsers, then save the
file as UTF-8 without BOM (adjust your editor/IDE or use "Save with encoding" to
do this). Optionally add an .editorconfig or CI lint rule to enforce "utf-8"
without BOM for JSON files and re-commit the cleaned settings.json.
src/i18n/locales/ar/settings.json (1)

1-1: ⚡ Quick win

Remove BOM from all locale JSON files for consistency

All 11 settings.json files in src/i18n/locales/ have a UTF-8 BOM (U+FEFF) at the start. While Node.js 22.22.1 handles this fine, Vite's bundler and the i18n loader work correctly regardless, it's cleaner to strip it. There's already precedent in scripts/test-windows-native-cursor.mjs where BOM is explicitly stripped.

Affected files (all locale variants)
src/i18n/locales/ar/settings.json
src/i18n/locales/en/settings.json
src/i18n/locales/es/settings.json
src/i18n/locales/fr/settings.json
src/i18n/locales/ja-JP/settings.json
src/i18n/locales/ko-KR/settings.json
src/i18n/locales/ru/settings.json
src/i18n/locales/tr/settings.json
src/i18n/locales/vi/settings.json
src/i18n/locales/zh-CN/settings.json
src/i18n/locales/zh-TW/settings.json
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/i18n/locales/ar/settings.json` at line 1, Remove the UTF-8 BOM (U+FEFF)
at the start of each locale settings.json file listed (e.g.,
src/i18n/locales/ar/settings.json and the other 10 locale variants) so the files
begin with the normal JSON '{' character; open each settings.json, delete the
invisible BOM character at the very start, save the file as UTF-8 without BOM,
and re-run build/tests to confirm the i18n loader and Vite bundler behave the
same.
src/hooks/useScreenRecorder.ts (1)

776-811: 💤 Low value

nit: the video-only getUserMedia call is written twice.

low priority, but the audio: false, video: videoConstraints call shows up in both the systemAudio-catch branch and the else branch. a tiny helper would dedupe and make the control flow easier to read:

♻️ optional cleanup
+			const captureVideoOnly = () =>
+				navigator.mediaDevices.getUserMedia({
+					audio: false,
+					video: videoConstraints,
+				} as unknown as MediaStreamConstraints);
+
 			if (systemAudioEnabled) {
 				try {
 					screenMediaStream = await navigator.mediaDevices.getUserMedia({
 						audio: {
 							mandatory: {
 								chromeMediaSource: CHROME_MEDIA_SOURCE,
 								chromeMediaSourceId: selectedSource.id,
 							},
 						},
 						video: videoConstraints,
 					} as unknown as MediaStreamConstraints);
 				} catch (audioErr) {
 					console.warn("System audio capture failed, falling back to video-only:", audioErr);
 					toast.error(t("recording.systemAudioUnavailable"));
-					screenMediaStream = await navigator.mediaDevices.getUserMedia({
-						audio: false,
-						video: videoConstraints,
-					} as unknown as MediaStreamConstraints);
+					screenMediaStream = await captureVideoOnly();
 				}
 			} else {
-				screenMediaStream = await navigator.mediaDevices.getUserMedia({
-					audio: false,
-					video: videoConstraints,
-				} as unknown as MediaStreamConstraints);
+				screenMediaStream = await captureVideoOnly();
 			}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/hooks/useScreenRecorder.ts` around lines 776 - 811, Duplicate
getUserMedia calls for video-only should be extracted into a small helper to
reduce repetition: create a function (e.g., getVideoOnlyStream or
fetchVideoOnlyStream) that returns navigator.mediaDevices.getUserMedia({ audio:
false, video: videoConstraints } as MediaStreamConstraints) and replace both
occurrences where screenMediaStream is assigned in the systemAudioEnabled catch
block and the else branch; keep existing references to videoConstraints,
screenMediaStream, systemAudioEnabled, audioErr, toast and t unchanged so
behavior and error handling remain the same.
src/i18n/locales/ko-KR/editor.json (1)

1-1: ⚡ Quick win

BOM is present, but it's not actually a problem

Yeah, there's a UTF-8 BOM here (), but honestly this is a non-issue. 32 other translation files across all locales have the same thing, and Vite's JSON loader handles it gracefully at build time. If it was breaking JSON parsing, you'd already be seeing errors in like, half your locales.

If it bugs you, stripping BOM from all the translation files (dialogs.json, editor.json, settings.json across all locales) would be a nice cleanup—but that's optional polish, not a fix. Just keep it consistent with the rest of the codebase if you touch it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/i18n/locales/ko-KR/editor.json` at line 1, The file contains a UTF-8 BOM
character at the start of src/i18n/locales/ko-KR/editor.json; remove the leading
BOM (the invisible U+FEFF character) from this file and, if you touch other
locale files, remove the BOM consistently in dialogs.json, editor.json, and
settings.json across locales so all translation JSON files start with a plain
'{' and remain consistent with the rest of the codebase and Vite's JSON loader.
src/components/video-editor/EditorEmptyState.tsx (2)

110-111: Dialog onOpenChange could cause race

line 110: onOpenChange={(open) => !open && setDropError(null)} works but kinda subtle. if the dialog is opened programmatically (not by user interaction), this callback fires immediately and could clear the error before the animation completes. the lastDropErrorRef pattern saves you here but it's a bit fragile.

nit: could be clearer as onOpenChange={(open) => { if (!open) setDropError(null); }} but current code works fine.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/video-editor/EditorEmptyState.tsx` around lines 110 - 111, The
onOpenChange handler on the Dialog (onOpenChange={(open) => !open &&
setDropError(null)}) can clear dropError prematurely when the dialog is toggled
programmatically; change the handler to explicitly check for a closing
transition and only clear after the dialog has fully closed (e.g., use
onOpenChange={(open) => { if (!open) setDropError(null); }} combined with
waiting for the Dialog's onClose or animation end callback instead of
immediately calling setDropError, and remove reliance on the fragile
lastDropErrorRef pattern; target the Dialog component and the setDropError usage
to implement this safer clear-on-close behavior.

64-75: ⚡ Quick win

file extension check needs case handling

line 64 checks f.name.endsWith(".openscreen") which is case-sensitive. someone could drop project.OpenScreen or PROJECT.OPENSCREEN and it'd be rejected. probably rare but easy fix.

case-insensitive check
-		const projectFile = files.find((f) => f.name.endsWith(".openscreen"));
+		const projectFile = files.find((f) => f.name.toLowerCase().endsWith(".openscreen"));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/video-editor/EditorEmptyState.tsx` around lines 64 - 75, The
file extension check in EditorEmptyState.tsx uses files.find((f) =>
f.name.endsWith(".openscreen")) which is case-sensitive and will reject variants
like "project.OpenScreen"; change the predicate to perform a case-insensitive
comparison (e.g., compare f.name.toLowerCase() with ".openscreen") and guard
against missing/undefined names (use f?.name or String(f.name) before
lowercasing); keep the rest of the flow (setting projectFile, setDropError, and
calling window.electronAPI.getPathForFile) unchanged.
electron/electron-env.d.ts (1)

169-169: ⚡ Quick win

getPathForFile return type might be wrong

the type says (file: File) => string but webUtils.getPathForFile() can potentially throw or return empty string on failure. might wanna make this string | null or string | undefined for safety, or at least document that it can throw.

safer type
-		getPathForFile: (file: File) => string;
+		getPathForFile: (file: File) => string | null;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@electron/electron-env.d.ts` at line 169, The declaration for getPathForFile
is too narrow: change its return type from (file: File) => string to reflect
failures (e.g., (file: File) => string | null or string | undefined) and add a
brief JSDoc note that webUtils.getPathForFile may also throw; then update any
callers (or ensure callers already check for null/undefined and catch
exceptions) to handle the absence of a path or thrown errors. Reference symbols:
getPathForFile and webUtils.getPathForFile.
src/i18n/locales/en/editor.json (1)

1-1: ⚡ Quick win

BOM character at start of file

there's a UTF-8 BOM () at the beginning of this file. this can cause parsing issues with some JSON parsers or tools. most modern parsers handle it but it's cleaner without it.

nit: remove the BOM if it wasn't intentional. most editors can strip it on save.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/i18n/locales/en/editor.json` at line 1, The file begins with a UTF-8 BOM
character (U+FEFF) immediately before the opening '{' which can break some JSON
parsers; remove that BOM so the file starts with '{', re-save it as UTF-8
without BOM, and run the JSON validator/linters to confirm parsing succeeds;
also update your editor/save settings (or project pre-commit hook) to prevent
writing BOMs in the future.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@electron/ipc/handlers.ts`:
- Around line 1780-1783: Change the case-sensitive endsWith check in the
drop/project validation to compare the file extension case-insensitively: use
path.extname(filePath).toLowerCase() and compare it to ("." +
PROJECT_FILE_EXTENSION).toLowerCase() (or lowercased PROJECT_FILE_EXTENSION)
instead of filePath.endsWith(`.${PROJECT_FILE_EXTENSION}`) in the validation
block inside electron/ipc/handlers.ts so names like "MyProject.OPENSCREEN" are
accepted; ensure path is imported if not already and keep the same early-return
shape ({ success: false, message: ... }) used around this check.

In `@electron/preload.ts`:
- Line 127: Wrap the call to webUtils.getPathForFile(file) inside getPathForFile
in the preload API with a try/catch so any thrown errors (e.g., inaccessible or
synthetic files) are caught and the function returns null instead of
propagating; update the exposed signature in electron-env.d.ts to return string
| null so callers can handle the nullable path. Ensure you reference
getPathForFile (preload) and webUtils.getPathForFile in the change and log or
silently swallow the caught error per project convention.

In `@src/components/video-editor/EditorEmptyState.tsx`:
- Around line 70-75: Wrap the call to
window.electronAPI.getPathForFile(projectFile) in a try/catch inside
EditorEmptyState (the drop handler) so exceptions from getPathForFile are
caught; on catch call setDropError("load-failed") (and optionally log the error)
and return, and keep the existing !filePath check after the try to handle falsy
returns. This ensures projectFile conversion won't throw and crash the handler.

In `@src/components/video-editor/VideoEditor.tsx`:
- Around line 2179-2186: The quick-start click handler currently calls
window.electronAPI.startNewRecording() and ignores its promise, swallowing
failures; change the empty-state branch to reuse the existing
handleNewRecordingConfirm() flow (or call startNewRecording() and await its
result, then run the same success/error path as handleNewRecordingConfirm) so
errors and { success: false } outcomes are handled the same as when
setShowNewRecordingDialog(true) is used; update the onClick branch that checks
videoPath to invoke handleNewRecordingConfirm() (or await and check the returned
value) and propagate errors to the same UI/error handling logic.
- Around line 716-724: doNewProject only clears file/path state; add a full
per-project editor reset by extracting the media-path clears into a new
resetEditorState helper that also clears timeline/history, undo/redo stacks,
crop state, wallpaper, cursor/mode state, selection(s), and any project-scoped
transient UI flags, then call that helper from doNewProject (replace the
existing state clears with a call to resetEditorState) and reuse the same
resetEditorState from the import flow handler(s) (e.g. your
importVideo/handleImport functions) so importing into an empty project can't
carry over previous edits; reference the current symbols (doNewProject,
nativeBridgeClient.project.clearCurrentVideoPath, setVideoPath,
setVideoSourcePath, setWebcamVideoPath, setWebcamVideoSourcePath,
setCurrentProjectPath, setLastSavedSnapshot) when locating where to plug in the
new reset.

In `@src/hooks/useScreenRecorder.ts`:
- Around line 787-811: When getUserMedia with system audio constraints fails
inside the block that checks systemAudioEnabled, call the setter to disable the
system-audio toggle so UI state matches reality (similar to how the mic path
uses setMicrophoneEnabled(false)); specifically, in the catch handling audioErr
for the block that assigns screenMediaStream with
chromeMediaSource/chromeMediaSourceId, add setSystemAudioEnabled(false) before
showing the toast and falling back to the video-only getUserMedia so the
systemAudioEnabled flag is cleared and the toggle won’t remain on.

In `@src/i18n/locales/ja-JP/settings.json`:
- Line 1: The file begins with a UTF-8 BOM character before the opening brace
"{" which should be removed; open the locale file, delete the BOM at the very
start (so the first character is '{'), and re-save the file encoded as UTF-8
without BOM to ensure cleaner, portable JSON (no code changes required beyond
removing the BOM).

In `@src/i18n/locales/ru/settings.json`:
- Line 1: The JSON file ru/settings.json (and the other locale JSON files like
dialogs.json, editor.json, settings.json in each locale) currently contains a
UTF-8 BOM at the start; remove the leading BOM character from the beginning of
these files so the first byte is the opening brace '{' (perform a bulk cleanup
across all 33 files to strip BOMs), verify files are valid UTF-8 without BOM and
commit the cleaned files.

In `@src/i18n/locales/tr/settings.json`:
- Line 1: Remove the UTF-8 BOM (U+FEFF) present at the start of each locale
settings.json (e.g., settings.json in every locale folder) so the files begin
directly with '{'. Locate files that start with the invisible BOM character and
delete that first character (ensure the first byte is not 0xEF,0xBB,0xBF), then
save and run your JSON/lint checks to confirm parsing still succeeds. Keep all
file encoding as UTF-8 without BOM going forward.

In `@src/i18n/locales/vi/settings.json`:
- Line 1: The file src/i18n/locales/vi/settings.json contains a UTF-8 BOM at the
start which can break some JSON parsers; remove the BOM bytes so the file begins
directly with "{" (i.e., re-save the file without BOM/UTF-8 signature using your
editor or run a tool/command that strips BOM) and ensure the file is saved as
plain UTF-8 without BOM so json loaders consume it correctly.

---

Outside diff comments:
In `@electron/main.ts`:
- Around line 114-127: The current logic calls createEditorWindowWrapper()
(which force-closes mainWindow) whenever the focused window isn't an editor,
risking loss of unsaved work; change it to first search
BrowserWindow.getAllWindows() for an existing editor via isEditorWindow() and
route the action to that unfocused editor (set targetWindow to that window and
send the channel or wait for did-finish-load if necessary); only call
createEditorWindowWrapper() to create a new editor and assign mainWindow if no
existing editor window is found. Ensure you reference
BrowserWindow.getFocusedWindow(), BrowserWindow.getAllWindows(),
isEditorWindow(), createEditorWindowWrapper(), and mainWindow when making the
change so you avoid invoking the force-close path when an editor already exists.

---

Nitpick comments:
In `@electron/electron-env.d.ts`:
- Line 169: The declaration for getPathForFile is too narrow: change its return
type from (file: File) => string to reflect failures (e.g., (file: File) =>
string | null or string | undefined) and add a brief JSDoc note that
webUtils.getPathForFile may also throw; then update any callers (or ensure
callers already check for null/undefined and catch exceptions) to handle the
absence of a path or thrown errors. Reference symbols: getPathForFile and
webUtils.getPathForFile.

In `@src/components/video-editor/EditorEmptyState.tsx`:
- Around line 110-111: The onOpenChange handler on the Dialog
(onOpenChange={(open) => !open && setDropError(null)}) can clear dropError
prematurely when the dialog is toggled programmatically; change the handler to
explicitly check for a closing transition and only clear after the dialog has
fully closed (e.g., use onOpenChange={(open) => { if (!open) setDropError(null);
}} combined with waiting for the Dialog's onClose or animation end callback
instead of immediately calling setDropError, and remove reliance on the fragile
lastDropErrorRef pattern; target the Dialog component and the setDropError usage
to implement this safer clear-on-close behavior.
- Around line 64-75: The file extension check in EditorEmptyState.tsx uses
files.find((f) => f.name.endsWith(".openscreen")) which is case-sensitive and
will reject variants like "project.OpenScreen"; change the predicate to perform
a case-insensitive comparison (e.g., compare f.name.toLowerCase() with
".openscreen") and guard against missing/undefined names (use f?.name or
String(f.name) before lowercasing); keep the rest of the flow (setting
projectFile, setDropError, and calling window.electronAPI.getPathForFile)
unchanged.

In `@src/hooks/useScreenRecorder.ts`:
- Around line 776-811: Duplicate getUserMedia calls for video-only should be
extracted into a small helper to reduce repetition: create a function (e.g.,
getVideoOnlyStream or fetchVideoOnlyStream) that returns
navigator.mediaDevices.getUserMedia({ audio: false, video: videoConstraints } as
MediaStreamConstraints) and replace both occurrences where screenMediaStream is
assigned in the systemAudioEnabled catch block and the else branch; keep
existing references to videoConstraints, screenMediaStream, systemAudioEnabled,
audioErr, toast and t unchanged so behavior and error handling remain the same.

In `@src/i18n/locales/ar/settings.json`:
- Line 1: Remove the UTF-8 BOM (U+FEFF) at the start of each locale
settings.json file listed (e.g., src/i18n/locales/ar/settings.json and the other
10 locale variants) so the files begin with the normal JSON '{' character; open
each settings.json, delete the invisible BOM character at the very start, save
the file as UTF-8 without BOM, and re-run build/tests to confirm the i18n loader
and Vite bundler behave the same.

In `@src/i18n/locales/en/editor.json`:
- Line 1: The file begins with a UTF-8 BOM character (U+FEFF) immediately before
the opening '{' which can break some JSON parsers; remove that BOM so the file
starts with '{', re-save it as UTF-8 without BOM, and run the JSON
validator/linters to confirm parsing succeeds; also update your editor/save
settings (or project pre-commit hook) to prevent writing BOMs in the future.

In `@src/i18n/locales/ko-KR/editor.json`:
- Line 1: The file contains a UTF-8 BOM character at the start of
src/i18n/locales/ko-KR/editor.json; remove the leading BOM (the invisible U+FEFF
character) from this file and, if you touch other locale files, remove the BOM
consistently in dialogs.json, editor.json, and settings.json across locales so
all translation JSON files start with a plain '{' and remain consistent with the
rest of the codebase and Vite's JSON loader.

In `@src/i18n/locales/zh-CN/settings.json`:
- Line 1: The file settings.json contains a UTF-8 BOM (U+FEFF) before the
opening brace; remove the BOM so the file starts with "{" to avoid issues with
strict JSON parsers, then save the file as UTF-8 without BOM (adjust your
editor/IDE or use "Save with encoding" to do this). Optionally add an
.editorconfig or CI lint rule to enforce "utf-8" without BOM for JSON files and
re-commit the cleaned settings.json.
🪄 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: CHILL

Plan: Pro

Run ID: ad976fb7-5aa5-486b-848e-bbd49c9a6da7

📥 Commits

Reviewing files that changed from the base of the PR and between b0293e7 and 370323c.

📒 Files selected for processing (51)
  • electron/electron-env.d.ts
  • electron/ipc/handlers.ts
  • electron/ipc/nativeBridge.ts
  • electron/main.ts
  • electron/native-bridge/services/projectService.ts
  • electron/preload.ts
  • electron/windows.ts
  • index.html
  • src/App.tsx
  • src/components/launch/LaunchWindow.tsx
  • src/components/video-editor/EditorEmptyState.tsx
  • src/components/video-editor/UnsavedChangesDialog.tsx
  • src/components/video-editor/VideoEditor.tsx
  • src/components/video-editor/VideoPlayback.tsx
  • src/hooks/useScreenRecorder.ts
  • src/i18n/locales/ar/dialogs.json
  • src/i18n/locales/ar/editor.json
  • src/i18n/locales/ar/settings.json
  • src/i18n/locales/en/dialogs.json
  • src/i18n/locales/en/editor.json
  • src/i18n/locales/en/launch.json
  • src/i18n/locales/en/settings.json
  • src/i18n/locales/es/dialogs.json
  • src/i18n/locales/es/editor.json
  • src/i18n/locales/es/settings.json
  • src/i18n/locales/fr/dialogs.json
  • src/i18n/locales/fr/editor.json
  • src/i18n/locales/fr/settings.json
  • src/i18n/locales/ja-JP/dialogs.json
  • src/i18n/locales/ja-JP/editor.json
  • src/i18n/locales/ja-JP/settings.json
  • src/i18n/locales/ko-KR/dialogs.json
  • src/i18n/locales/ko-KR/editor.json
  • src/i18n/locales/ko-KR/settings.json
  • src/i18n/locales/ru/dialogs.json
  • src/i18n/locales/ru/editor.json
  • src/i18n/locales/ru/settings.json
  • src/i18n/locales/tr/dialogs.json
  • src/i18n/locales/tr/editor.json
  • src/i18n/locales/tr/settings.json
  • src/i18n/locales/vi/dialogs.json
  • src/i18n/locales/vi/editor.json
  • src/i18n/locales/vi/settings.json
  • src/i18n/locales/zh-CN/dialogs.json
  • src/i18n/locales/zh-CN/editor.json
  • src/i18n/locales/zh-CN/settings.json
  • src/i18n/locales/zh-TW/dialogs.json
  • src/i18n/locales/zh-TW/editor.json
  • src/i18n/locales/zh-TW/settings.json
  • src/native/client.ts
  • src/native/contracts.ts

Comment thread electron/ipc/handlers.ts
Comment thread electron/preload.ts Outdated
Comment thread src/components/video-editor/EditorEmptyState.tsx
Comment thread src/components/video-editor/VideoEditor.tsx Outdated
Comment thread src/components/video-editor/VideoEditor.tsx Outdated
Comment thread src/hooks/useScreenRecorder.ts Outdated
Comment thread src/i18n/locales/ja-JP/settings.json Outdated
Comment thread src/i18n/locales/ru/settings.json Outdated
Comment thread src/i18n/locales/tr/settings.json Outdated
Comment thread src/i18n/locales/vi/settings.json Outdated
@coderabbitai

coderabbitai Bot commented May 11, 2026

Copy link
Copy Markdown
Contributor

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{}

@makaradam

Copy link
Copy Markdown
Contributor Author

Hey @siddharthvaddem, I've sent you an email about this Feature as this is kinda gigantic like it was a 5-6hr session, that explains some things plus there's an unlisted video there that explains.

Also I've added now with the last commit in a6e91e3, for some reason after figuring out the edge case for the custom zoom, maybe a later merge had it removed or something, now it's more clearer (I'm explaining the bug in the video sent via email)

Hope you like so far, I'll check the cursor later (in the current main windows cursor gets baked in either way when cursor button is toggled)

I really hope that I tested the most of the edge cases (more than 20-30 QAs ran before shipping)

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@electron/native-bridge/cursor/recording/windowsNativeRecordingSession.ts`:
- Around line 227-246: The current emission block caches
lastEmittedCursorType/lastEmittedAssetId even when currentAssetId is present but
its bitmap/nativeAsset is not yet available, which suppresses a later real asset
emission; change the logic in the emission branch so you first resolve
nativeAsset = currentAssetId ? this.assets.get(currentAssetId) : null, then: if
currentAssetId && !nativeAsset, call
this.options.onCursorTypeChange(currentType, null) but do NOT update
this.lastEmittedCursorType or this.lastEmittedAssetId (so the real bitmap can
later trigger); otherwise (nativeAsset exists OR currentAssetId is null meaning
no custom asset) update lastEmittedCursorType/lastEmittedAssetId and emit the
payload including the nativeAsset properties when present; this uses
this.assets, this.options.onCursorTypeChange, this.lastEmittedCursorType, and
this.lastEmittedAssetId to locate the code to change.

In `@electron/windows.ts`:
- Around line 274-280: The overlay window is using
screen.getPrimaryDisplay().bounds so it only covers the primary monitor; replace
that with virtual-desktop bounds computed from screen.getAllDisplays() (or, if
you want the overlay on the display with the cursor, use
screen.getCursorScreenPoint() + screen.getDisplayNearestPoint()) and use that
computed bounds when constructing the BrowserWindow (update where bounds is read
and the BrowserWindow options in the win creation). Ensure you compute combined
x/y/width/height from displays' bounds (union) and then pass those values into
new BrowserWindow instead of screen.getPrimaryDisplay().bounds.

In `@src/components/launch/CursorOverlay.tsx`:
- Around line 243-246: The onCursorTypeChange handler currently only calls
setLiveAsset when asset is truthy, which leaves the previous bitmap set when the
main process sends null; update the handler registered via
window.electronAPI.onCursorTypeChange to always call setLiveAsset(asset) (or
setLiveAsset(asset ?? null)) regardless of truthiness and keep
setCursorType(type) as-is so the state clears correctly and the fallback SVG can
render; refer to the onCursorTypeChange registration, setCursorType, and
setLiveAsset symbols in CursorOverlay.tsx when making the change.

In `@src/components/video-editor/SettingsPanel.tsx`:
- Around line 1399-1419: The UI strings in SettingsPanel.tsx are hardcoded
(e.g., "Cursor Style", "Native OS", "Custom cursor", "Size") — update these to
use the existing i18n helper (t(...)) like the rest of the panel: replace
literal labels inside the Select/SelectTrigger/SelectItem blocks and other
occurrences around the cursor controls (including the range 1429-1490) with
t('key') calls, using the same translation keys/style used elsewhere in this
component; ensure cursorDisplayMode and onCursorDisplayModeChange behavior is
unchanged and only the displayed strings are wrapped with t(...) so translations
render 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: CHILL

Plan: Pro

Run ID: 2f2c8d81-b664-4712-92bf-a5600e33948d

📥 Commits

Reviewing files that changed from the base of the PR and between a6e91e3 and 52ddaf4.

📒 Files selected for processing (12)
  • electron/electron-env.d.ts
  • electron/ipc/handlers.ts
  • electron/native-bridge/cursor/recording/factory.ts
  • electron/native-bridge/cursor/recording/windowsNativeRecordingSession.ts
  • electron/native-bridge/cursor/recording/windowsNativeRecordingSession.types.ts
  • electron/preload.ts
  • electron/windows.ts
  • src/App.tsx
  • src/components/launch/CursorOverlay.tsx
  • src/components/video-editor/SettingsPanel.tsx
  • src/components/video-editor/VideoEditor.tsx
  • src/components/video-editor/VideoPlayback.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • electron/electron-env.d.ts
  • src/App.tsx
  • src/components/video-editor/VideoEditor.tsx

Comment thread electron/native-bridge/cursor/recording/windowsNativeRecordingSession.ts Outdated
Comment thread electron/windows.ts Outdated
Comment thread src/components/launch/CursorOverlay.tsx Outdated
Comment thread src/components/video-editor/SettingsPanel.tsx Outdated
@siddharthvaddem

Copy link
Copy Markdown
Owner

@makaradam good to merge once updated based off latest main and conflicts

makaradam added 3 commits May 31, 2026 09:40
…or anti-flash

- preload + native bridge: getPathForFile, loadProjectFileFromPath, and
  onMenuNewProject / onMenuImportVideo listeners.
- main process: New Project File-menu item (Cmd/Ctrl+N) + menu-new-project.
- handlers: broaden import video formats (.m4v, .wmv, .flv, .ts).
- windows: keep main's HUD geometry + draggable handler; re-apply the
  show-on-ready-to-show anti-flash for the HUD and editor windows.
- EditorEmptyState shown when no video is loaded: Import Video, Load
  Project, and drag-drop of .openscreen files.
- "Open Studio" button in the recording HUD (replaces the two separate
  open-video / open-project buttons).
- New Project flow that clears back to the dashboard, prompting to save
  on unsaved changes; Load Project now prompts too (variant-aware dialog).
- Rebuilt on current main, preserving main's export refactor, cursor-clip
  / zoom-preview / trim-waveform work and the vertical/horizontal HUD tray.
@makaradam makaradam force-pushed the feature/open-studio-and-import branch from a6e91e3 to 00191c4 Compare May 31, 2026 07:40
SourceSelector.test.tsx uses toBeInTheDocument but never imported
@testing-library/jest-dom, and vitest.config.ts has no setupFiles to
register the matchers globally. It only passed when test ordering
happened to leak the matcher from another file, making CI intermittently
red on main (e.g. the siddharthvaddem#652 merge). Importing jest-dom directly makes the
test self-sufficient and deterministic.
@makaradam

Copy link
Copy Markdown
Contributor Author

@siddharthvaddem

finally got this rebased onto the latest main and sorted out all the conflicts. it's mergeable + CI's green now

squashed my commits into 4 cleaner ones

few notes on how i handled the conflicts:

  • for VideoEditor.tsx i started from your current version and just layered my dashboard stuff on top — so all your recent work (export refactor, cursor-clip, zoom-preview, trim-waveform) is untouched.
  • left your HUD tray layout + window sizing alone, only re-added the little anti-flash bit.
  • threw out my old useScreenRecorder change completely — your native capture stuff already does it better.

Extra: also ended up fixing a flaky test while i was in there (SourceSelector.test.tsx was missing the jest-dom import, it's actually been randomly failing CI on main too, like on the #652 merge).

tested locally and the empty-state dashboard, new project (with the unsaved prompt), importing a video, and zoom all worked for me fine.

should be good to merge

@siddharthvaddem siddharthvaddem merged commit d2dd44a into siddharthvaddem:main May 31, 2026
9 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Studio/Editor dashboard instead of shortcuts, New Project state, with localization support

2 participants