On Windows, the packaged app serves the renderer from http://127.0.0.1:PORT,
which causes Chromium to block file:// URLs in <video> elements — even with
webSecurity disabled. This affects all users running the packaged build.
Add a local media HTTP server (random port) that streams approved video files
with range request support for seeking. The renderer now resolves video paths
through this server instead of using file:// URLs directly.
- electron/mediaServer.ts: HTTP server with path validation against
approvedLocalReadPaths and Content-Range support
- IPC handler 'get-local-media-url' converts file paths to HTTP URLs
- resolveVideoUrl() in renderer falls back to file:// if server unavailable
- Improved <video> onError to log actual MediaError details
Fixes#244
The AND gate broke access to user-selected files outside the allowlist
(wallpapers, user videos, etc). Keep isPathInsideDirectory normalization fix,
revert the existsSync AND guard back to the original OR behavior.
- Remove dead helperExists local in recording/windows.ts
- Hoist fs/promises import out of close handler in recording/ffmpeg.ts
- Guard fs.readdir with mkdir in recording/prune.ts (ENOENT resilience)
- Derive companion audio suffixes from COMPANION_AUDIO_LAYOUTS in prune.ts
- Guard mousemove hook registration to Linux only in cursor/interaction.ts
- Replace dynamic require('electron') with static import in cursor/monitor.ts
- Wrap nodeRequire in try/catch in ffmpeg/binary.ts for fallback safety
- Fix hardcoded timeOffsetMs: 0 in project/session.ts (use normalizer)
- Fix isPathInsideDirectory to normalize candidatePath in project/manager.ts
- Fix isAllowedLocalReadPath security: require path to be in allowlist (AND not OR)
- Derive extension regex from constants in project/manager.ts
- Consolidate duplicate Duration parsers in recording/diagnostics.ts
- Refactor ensureReadableFile to use options object instead of description string
- Make swiftc compilation async (execFile) in paths/binaries.ts
- Add socket timeout to httpsGet in captions/whisper.ts
On macOS versions before Sonoma (14.0), SCStreamConfiguration does not
support the captureMicrophone/microphoneCaptureDeviceID selectors. The
native helper was throwing a fatal error, blocking recording entirely
when microphone was enabled.
Now the Swift helper logs MICROPHONE_CAPTURE_UNAVAILABLE and continues
capture without microphone. The Node handler detects this signal and
returns microphoneFallbackRequired to the renderer, which starts a
browser-side MediaRecorder capturing the mic via getUserMedia. On stop,
the audio blob is saved as a .mic.webm sidecar alongside the native
recording, where the existing companion audio detection picks it up.
Fixes: 'Native microphone capture is unavailable on this macOS/Xcode
runtime' error on older macOS versions.
- 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
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
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).
- 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
Catch errors from desktopCapturer.getSources() in the get-sources IPC
handler and return an empty array instead of crashing the handler.
Prevents repeated 'Failed to get sources' errors when screen recording
permission is not granted (common in dev mode on macOS 26).
- finalizeStoredVideo now catches validateRecordedVideo errors instead of
propagating them, so the editor opens even if ffmpeg validation fails
(restores v1.1.3/v1.1.5 resilient behavior)
- Use a single Date.now() call for all native capture filenames on macOS
to prevent timestamp drift between video/audio files
- 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