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.
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.
- 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.