- RecordingControls now accepts and wires toggleMicrophone so the mic
button actually works during recording
- gifOutputDimensions and desiredMp4SourceDimensions now use a reactive
videoDimensions state (set on loadedmetadata) instead of reading from
a ref in useMemo, fixing stale 1920x1080 fallback on first render
- Account button text now goes through i18n with translations for all
5 locales
Start webcam stop as a guarded promise so a rejection can't prevent
stopNativeScreenRecording from running. The webcam result is awaited
only after the native stop path completes.
GIF save-failure and MP4 save-failure paths had the same bug as the
export-failure paths: dropdown closes hiding the exportError UI. Now
keepExportDialogOpen = true in all non-smoke failure branches.
- Export dropdown now stays open on GIF/MP4 failure and top-level catch so
the exportError UI remains visible (previously dismissed by finally block)
- handleCancelExport always resets UI state even before exporter is constructed,
so Cancel works during the async setup phase
- fromFileUrl() now converts forward slashes to backslashes for Windows drive
paths (both primary and fallback parsing paths)
- Translated customFonts and color keys in es/ko/nl/zh-CN locales
stopNativeScreenRecording and muxNativeWindowsRecording can reject if IPC
fails or the bridge is disposed. Without try/catch, setRecordingState(false)
is never called, the finalization toast stays up indefinitely, and no failure
UI is shown to the user. Now exceptions are caught and routed through the
existing diagnostics/recovery paths.
- Reorder recorder.stop() before cleanupCapturedMedia to avoid dropping final chunk (recordingStopHelpers)
- Guard speed region divisor against 0/NaN/Infinity (audioEncoder shared)
- Gate audio level meter to selected mic device only (helperComponents)
- Clear webcam deviceId when 'Default' selected (LaunchWindow)
- Wire i18n for 'Custom Fonts' and 'Color' labels (AnnotationTextTab)
- Restore keyboard focus ring on sidebar buttons (EditorSidebar)
- Add visually-hidden DialogTitle for a11y (ExtensionDetailModal)
- Make extension cards keyboard-accessible with role/tabIndex (ExtensionManagerCards)
- Write smoke export reports for GIF exports before closing (editorExportWorkflow)
- Guard GIF export against zero video dimensions (useEditorExport)
- Allow 'Save Again' before requiring loaded video (useEditorExport)
The merge-macos-update-metadata job uses a local composite action at
./.github/actions/merge-macos-metadata but was missing a checkout step,
so the runner wouldn't have the action files.
- AnnotationTextTab: use ?? instead of || so empty text can be cleared
- ExtensionManager: add catch block for marketplace install failures
- useEditorExport: wrap retry save in try/catch for unhandled rejections
- editorExportWorkflow: move playback restore to finally block
Verifies the selected recording MIME type can also be decoded by the
in-app <video> element via canPlayType(), so that codecs like av1 —
which MediaRecorder.isTypeSupported() reports as supported for encoding
but are not decodable on fresh Windows installs without the AV1 Video
Extension — are not chosen.
Also passes undefined (not an unsupported fallback string) into
new MediaRecorder() when no preferred type is supported, letting the
browser pick a default instead of throwing NotSupportedError.
Replaces the previous 'as ProcessedDesktopSource' cast with a properly
typed LINUX_PORTAL_SOURCE constant.
Co-authored-by: meiiie <meiiie@users.noreply.github.com>
- electron/main.ts: treat missing sourceId on linux as portal sentinel
so the request handler never calls getSources() (which itself opens
an extra portal dialog) on fresh sessions.
- useScreenRecorder.ts: persist the synthesized portal sentinel via
selectSource() so main has the source set before getDisplayMedia.
Extract acquireLinuxPortalStream() helper to dedupe the three
duplicated getDisplayMedia constraint blocks.
- LaunchWindow.tsx: hide the screen-source selector button (and its
separator) on Linux so users cannot trigger an extra portal dialog
via the dropdown.
Previously on Linux/Wayland, starting a fullscreen recording required three
separate picker interactions: an in-app source dropdown plus two xdg-desktop-portal
dialogs. The duplicate portal dialogs were caused by:
1. resolveBrowserCaptureSource() calling desktopCapturer.getSources() in the
renderer, which itself triggers the portal on Wayland.
2. setDisplayMediaRequestHandler() in main also calling getSources(), which
triggers another portal.
3. Returning a pre-enumerated source id to Chromium, which on Wayland is stale
and forces Chromium to re-prompt via the portal during MediaStream creation.
Additionally, the editor window failed to appear after recording on some
Wayland sessions because 'ready-to-show' did not fire reliably.
Changes:
- LaunchWindow: skip the in-app source dropdown on Linux and start recording
directly; the OS portal becomes the source picker.
- useScreenRecorder: introduce a 'screen:linux-portal' sentinel source. When
set, route capture through navigator.mediaDevices.getDisplayMedia() so the
portal handles selection in a single dialog. Skip resolveBrowserCaptureSource
for the sentinel to avoid an extra getSources() call.
- electron/main: in setDisplayMediaRequestHandler, when the sentinel is set,
skip desktopCapturer.getSources() entirely and return a synthetic source so
Chromium opens the portal exactly once for the actual capture.
- electron/windows: in createEditorWindow, also call win.show() from
did-finish-load as a fallback for Linux/Wayland where ready-to-show may
not fire.
- Change ?? 99 fallback to ?? 100 in finalizing progress label
- Add editor.export.processingAudioEdits i18n key to all 5 locales
- Tighten sourceTimeToOutputTime return type to number (was number|null)
- Remove now-unused totalOutputDurationMs param from scheduleRegionForChunk
- Remove ?? fallbacks at call sites (function always returns a number)
- Add Linux GPU config: use EGL (better Wayland compat), disable VAAPI
video decoder/encoder (many distros ship broken drivers causing
'vaInitialize failed' and preventing renderer from loading)
- Add ready-to-show fallback for HUD window: if did-finish-load never
fires (GPU failure), show the window after 500ms via ready-to-show
Ref #261