- Extension loader with manifest validation, ID sanitization, and path traversal protection
- Permission-gated runtime API (render, cursor, audio, timeline, ui, assets, export)
- Extension host with lifecycle management (activate/deactivate)
- Marketplace client for browsing, downloading, and installing extensions
- Extension Manager UI panel with enable/disable/uninstall
- IPC bridge for extension discovery, management, and marketplace operations
- Proxy guard on window.electronAPI to block extension access to privileged IPC
- Zip-slip protection via post-extraction directory traversal validation
- Safe PowerShell invocation for Windows zip extraction (no string interpolation)
- File URL resolution with path escape prevention
- Remove dead time= progress matching (no decode pass = no progress output)
- Remove stale comments about ffmpeg success/fallback behavior
- Lower timeout from 30s to 5s (header read is near-instant)
- Drop maxBuffer override (minimal stderr output now)
- Add -hide_banner to reduce stderr noise
- Remove -f null - from probeMediaDurationSeconds() ffmpeg args
so the command always exits non-zero (no output file specified)
- Without -f null -, ffmpeg exits code 1 and stderr lands in the
catch block where Duration is parsed, fixing the bug where
duration returned 0 on valid recordings
- probeMediaDurationSeconds returning 0 caused the
videoDuration > 0 gate to skip all audio sync correction,
leaving system audio misaligned in the final recording
When deleting the Whisper model failed, the frontend kept stale state
(whisperModelDownloadStatus stayed as 'downloaded'), which blocked
re-downloading because the download handler saw the model as already
present. On the backend, no progress event was sent on failure, leaving
the renderer out of sync.
Frontend: reset whisperModelDownloadStatus and progress to idle on
delete failure so re-download is unblocked.
Backend: verify whether the file was actually removed despite the error
and send appropriate progress events in either case.
Closes#152
- 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.
When recording a window on a secondary monitor with a different DPI,
getNormalizedCursorPoint() used the primary display's scale factor for
both cursor and window-bounds conversion. This produced incorrect
normalised coordinates. Now resolves the display that contains the
target window and uses that display's scale factor instead.
Fixes#204
The Swift helper binaries were compiled with -target macos14.0, causing
dyld symbol-not-found crashes on macOS 13. The ScreenCaptureKit audio
APIs require macOS 13.0 minimum, so the deployment target is lowered to
13.0 and an #available guard is added for the macOS 14-only
ignoreShadowsSingleWindow API. README updated accordingly.
Fixes#199, fixes#200
Use F_OK instead of X_OK for Windows fs.access check (X_OK is meaningless
on Windows). Wrap spawn() in try/catch to prevent uncaught exception if the
binary is missing or blocked by antivirus.
On non-macOS platforms, platform APIs (iohook, GetWindowRect, xwininfo)
return physical pixel coordinates while Electron returns DIP coordinates.
Apply scaleFactor correction so cursor normalization uses a consistent
coordinate space. Fixes ~30-40px cursor offset on Windows 11 and Linux
Mint when display scaling is >100%.
Also fixes winget-releaser workflow (use v2 tag instead of invalid @latest).
- 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.
Chromium (Electron 17+) requires setDevicePermissionHandler in addition
to setPermissionCheckHandler/setPermissionRequestHandler. Without it,
device-level access is silently denied on Windows 11 even though the
permission check passes, causing getUserMedia to throw NotAllowedError.
- Check systemPreferences.getMediaAccessStatus on Windows startup
- Log warnings when camera/mic access is denied so users know to check
Windows Settings > Privacy > Camera/Microphone
- Call CoInitializeEx(MTA) at start of captureThread() and CoUninitialize() on exit
- Windows 11 enforces per-thread COM initialization more strictly than Win10,
causing GetBuffer/GetNextPacketSize to fail silently without this
- Add HRESULT error logging for WASAPI capture failures (GetNextPacketSize, GetBuffer)
- Remove AppleScript 'front window' bounds query which returned wrong window
for multi-window apps (e.g. Arc highlighting invisible auxiliary window)
- Use resolveMacWindowBounds() which queries by exact CGWindowID
- Keep AppleScript 'activate' call for bringing the app to front