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.
- 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
The HUD overlay snaps back to the centered default position whenever
recording starts because the idle-to-recording UI swap triggers a
resize, and applyHudOverlayBounds() always recomputes a centered
location. This is disruptive when the user has intentionally moved
the bar out of the way before a timed recording.
Remember the position after a drag ends and reuse it for subsequent
bounds updates, clamped to the current work area. The position resets
on app restart or when displays change so the bar cannot get stranded
off-screen.
- Add getHudOverlayDisplay() helper using getDisplayMatching()
to detect which display the HUD is currently on
- Replace getPrimaryDisplay() in getHudOverlayBounds(),
set-hud-overlay-compact-width, set-hud-overlay-measured-height
handlers, and createHudOverlayWindow() maxHeight constraint
- Falls back to primary display when HUD window doesn't exist
Tested on Windows 11 with dual-monitor setup. HUD stays on
the external monitor through recording start/stop and
expand/collapse cycles.
- replace path.normalize() with path.posix.normalize() in
resolveRequestedFilePath to preserve forward slashes on Windows
- add empty-string guard for degenerate pathnames
- remove win32 bypass in loadPackagedEditorWindow
path.normalize() converts / to \ on Windows, causing path.resolve
to escape to the drive root and return 403 Forbidden.
- Set setIgnoreMouseEvents(true, {forward: true}) on HUD overlay window
so transparent areas pass clicks to apps behind
- Add hud-overlay-set-ignore-mouse IPC to toggle mouse events
- Re-enable mouse events on mouseenter over visible HUD content,
disable again on mouseleave
- Use native OS Notification on Windows instead of transparent overlay
toast window (transparent BrowserWindows are unreliable on Windows)
- Set app user model ID on Windows for proper notification grouping
- Make toast window opaque with solid background on Windows
- Only set transparent body background on macOS for update-toast window
- Refactor UpdateToastWindow to inline styles for Windows compatibility
- Add primaryAction-based button rendering in toast UI
- Add polling fallback for toast payload in UpdateToastWindow
- Add global CSS variables for brand accent color
- Add CGPreflightScreenCaptureAccess pre-check in Swift helper
- Add microphone TCC pre-flight when mic capture is requested
- Warm up TCC via desktopCapturer.getSources before spawning helper
- Request mic access from Electron before helper spawn
- Remove unreliable systemPreferences.getMediaAccessStatus pre-checks
- Add camera entitlements to plist files for webcam support
- Guard getScreen() calls behind app.isReady()
- Make tray icons lazy to avoid early screen access
- Suppress duplicate permission error alerts in renderer
- Hardened IPC with safety guards for all webContents.send calls
- Scrubbed sensitive absolute paths from caption logs
- Realigned Windows artifact naming logic
with flex layout that reserves space above the bar for menu cards
- Increase HUD window size to 620x520 for menu card headroom
- Replace mic on/off toggle with mic selector card showing all available
microphones with live audio level meters per device
- Remove old separate mic controls panel
- Increase bar size with larger buttons, padding, and separators
- Fix TS null safety in stopRecording MediaRecorder accessfix
- Add pause, resume, and cancel recording controls with elapsed timer
- Move folder, language, and system audio into three-dots overflow menu with source highlight
wave