Commit Graph
238 Commits
Author SHA1 Message Date
webadderall 41249a9a19 refactor: remove redundant Linux platform guard in GPU config 2026-04-18 22:05:05 +10:00
webadderall d4a7c863e8 fix: Linux GPU fallback and HUD window show safety net
- 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
2026-04-18 21:32:18 +10:00
webadderall 8f3a62d811 fix: check both realpath and resolve forms in media URL handler, extract sync constants
- IPC get-local-media-url now checks both fs.realpath and path.resolve forms
  against approvedLocalReadPaths, so symlinks approved via approveUserPath work
- Extract duplicated sync correction parameters into shared constants in
  audioEncoder.ts (SYNC_SEEK_THRESHOLD_SEC, SYNC_PLAYBACK_RATE_OPTIONS)
2026-04-18 19:11:01 +10:00
webadderall dec004eddd fix: use drift-compensating scheduler in recording handler, fix media URL consistency
- Replace setInterval in recording handler with startCursorSampling() so
  the drift-compensating scheduler is used for actual recordings (was missed)
- Pass realpath-resolved path to buildMediaUrl for symlink consistency
- Use resolvedWebcamVideoUrl in thumbnail/export configs with file:// fallback
2026-04-18 19:03:36 +10:00
webadderall ffc5959994 fix: address CodeRabbit review feedback
- Fix telemetry drift reset: use nextExpectedMs - now for delay instead
  of stale drift value after baseline reset (avoids 1ms rapid sample)
- Use RECORDER_TIMESLICE_MS for mic fallback recorder (was hardcoded 1000)
- Return requested timeMs in extensionHost getCursorAt boundary clamps
- Remove premature null of webcam URL to prevent flicker on path change
- Use fs.realpath in IPC get-local-media-url to match media server check
2026-04-18 19:03:36 +10:00
webadderall 9100899adb fix: improve cursor telemetry accuracy under CPU load
- Replace setInterval with drift-compensating recursive setTimeout for
  cursor sampling. Under CPU load setInterval bunches or skips callbacks,
  creating irregular gaps in telemetry data.
- Add binary search + linear interpolation to export cursor lookup
  (modernFrameRenderer) matching the playback path. Was using O(n)
  nearest-neighbor which caused visible cursor jumping in exports.
- Apply same interpolation fix to extension API getCursorAt().
2026-04-18 19:03:36 +10:00
webadderall f024c89890 fix: always apply aresample sync filter and tighten audio sync tolerances
- Always route audio through aresample=async=1:first_pts=0 filter during
  muxing on both macOS and Windows, even when duration delta is small.
  Previously skipped when delta ≤50ms, which left progressive clock drift
  (from CPU load) completely uncorrected.
- Lower sync detection threshold from 50ms to 20ms so tempo correction
  kicks in earlier.
- Use explicit 48kHz AudioContext sample rate in both browser recording
  and export rendering to prevent sample rate mismatch drift.
- Reduce MediaRecorder timeslice from 1000ms to 250ms to reduce chunk
  loss under CPU pressure.
- Tighten export audio sync: seek threshold 300ms→150ms, tolerance
  15ms→8ms, correction window 2s→0.5s, max adjustment ±8%→±12%.
- Lower companion audio start delay threshold from 50ms to 25ms.
2026-04-18 19:03:36 +10:00
webadderall 7f587a39a8 fix: address CodeRabbit review feedback
- Use videoSourcePath instead of fromFileUrl(videoPath) for cursor
  telemetry — fixes broken path when videoPath is an HTTP media-server URL
- Validate filePath against approvedLocalReadPaths in get-local-media-url
  IPC handler before returning a URL
- Use fs.realpath() instead of path.resolve() to close symlink bypass
- Fix range parser to handle suffix ranges (bytes=-500) and guard
  against NaN values
- Add CORS headers (Access-Control-Allow-Origin) to media server
  responses to prevent canvas tainting when using video frames
- Handle OPTIONS preflight requests
- Clear stale resolvedWebcamVideoUrl before resolving new URL to
  prevent flash of stale content
2026-04-18 16:09:16 +10:00
webadderall 45b6879d86 fix: serve video files via local HTTP server to fix loading on Windows
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
2026-04-18 15:55:43 +10:00
webadderall 528a31b16f fix: clean up ipc edge cases 2026-04-17 21:25:13 +10:00
webadderall 0b3169ffff fix: address ipc review follow-ups 2026-04-17 21:11:58 +10:00
webadderall 87ec8fca93 fix: revert overly strict isAllowedLocalReadPath to OR logic
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.
2026-04-17 20:56:50 +10:00
webadderall 2ae0aa9a92 fix: address CodeRabbit review feedback
- 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
2026-04-17 20:35:08 +10:00
webadderall 673dfdadd3 refactor: extract registerIpcHandlers into 8 focused register/ modules
handlers.ts reduced from 2930 → 65 lines (pure delegation).

New files under electron/ipc/register/:
- sources.ts    — get-sources, select-source, show-source-highlight, open-source-selector
- recording.ts  — start/stop/pause native + ffmpeg, mux, store, set-recording-state, get-cursor-telemetry
- permissions.ts — accessibility/screen permissions, open-external-url
- assets.ts     — wallpaper thumbnails, asset-base-path, list-asset-dir, read-local-file
- export.ts     — native-video-export-*, save-exported-video
- captions.ts   — whisper model, file pickers, generate-auto-captions
- project.ts    — project files, recordings dir, video/session state, delete-recording
- settings.ts   — shortcuts, recording prefs, countdown, platform info

Also moved shared helpers:
- getMacPrivacySettingsUrl, approveUserPath → utils.ts
- isTrustedProjectPath → project/manager.ts
2026-04-17 20:13:47 +10:00
webadderall 099ce2bbb5 refactor: split handlers.ts into focused sub-modules
handlers.ts was ~5967 lines. Extracted into 22 focused modules:

- ipc/types.ts — shared TypeScript interfaces and types
- ipc/constants.ts — module-level constants
- ipc/state.ts — all mutable state with typed setters
- ipc/utils.ts — shared low-level utilities (getScreen, normalizePath, etc.)
- ipc/ffmpeg/binary.ts — ffmpeg binary resolution
- ipc/ffmpeg/filters.ts — audio sync/filter builders
- ipc/captions/parser.ts — SRT/Whisper JSON parsers
- ipc/captions/whisper.ts — Whisper model download/status
- ipc/captions/generate.ts — auto-caption generation
- ipc/paths/binaries.ts — native binary path resolution
- ipc/cursor/monitor.ts — cursor monitor process management
- ipc/cursor/telemetry.ts — cursor sampling and telemetry
- ipc/cursor/bounds.ts — window bounds capture and resolution
- ipc/cursor/interaction.ts — mouse hook and interaction capture
- ipc/recording/events.ts — recording lifecycle events
- ipc/recording/diagnostics.ts — media validation and diagnostics
- ipc/recording/prune.ts — auto-recording cleanup
- ipc/recording/ffmpeg.ts — FFmpeg screen capture
- ipc/recording/windows.ts — Windows native capture (WGC)
- ipc/recording/mac.ts — Mac ScreenCaptureKit integration
- ipc/export/native-video.ts — native video export sessions
- ipc/project/session.ts — recording session manifests
- ipc/project/manager.ts — project library and file management

handlers.ts reduced from 5967 → 2930 lines (registerIpcHandlers + helpers only)
2026-04-17 19:57:26 +10:00
webadderall 8d925281fc Merge remote-tracking branch 'origin/main' into feat/export-pipeline
# Conflicts:
#	electron/electron-env.d.ts
#	electron/ipc/handlers.ts
2026-04-17 13:47:15 +10:00
webadderall aa27e7cbd1 fix: address remaining export pipeline review feedback 2026-04-17 00:17:54 +10:00
webadderall 8071c6bb0e fix: address remaining ui overhaul review feedback 2026-04-16 23:39:27 +10:00
webadderall a83a58c671 fix: address ui overhaul review feedback 2026-04-16 22:36:23 +10:00
webadderall f81a740614 fix: address export pipeline review feedback 2026-04-16 18:49:01 +10:00
webadderall 8abceaa914 feat: add H.264 stream-copy export, overhaul exporter pipeline, rename native binaries
- IPC: new inputMode 'h264-stream' — browser VideoEncoder output stream-copied
  into MP4 via ffmpeg rather than piping raw RGBA frames, cuts memory usage
- Security: replace flat approvedLocalReadPaths with userApprovedPaths fed from
  dialog pickers and project load; isAllowedLocalReadPath uses it alongside
  dir prefix / existsSync checks
- nativeVideoExport: add buildNativeH264StreamExportArgs for stream-copy path
- frameRenderer / modernFrameRenderer: cursor-follow camera integration,
  annotation blur post-processing, shadow profile, squircle geometry
- audioEncoder / streamingDecoder / gifExporter / muxer: full pipeline
  hardening — drift correction, backpressure, proper teardown
- Rename native helpers openscreen-* → recordly-* on darwin-arm64 and
  darwin-x64; update helpers-manifest.json for win32-x64
- electron/main, windows, updater, rendererServer: startup and window
  lifecycle improvements
- preload: expose inputMode and new IPC surface to renderer
2026-04-16 17:51:41 +10:00
webadderall 38e54e2b12 fix: allow local export media files 2026-04-15 18:02:46 +10:00
webadderall 60d364a4ed fix: hotfix export regressions 2026-04-15 17:19:49 +10:00
webadderall 0026698b17 chore: apply low-risk lint cleanup 2026-04-15 14:59:02 +10:00
webadderall e34881fec4 fix(export): remove output path restriction 2026-04-14 22:28:30 +10:00
webadderall cb61814e78 Merge pull request #228 from Wing900/fix/silent-mp4-exports
Fix silent MP4 exports with FFmpeg audio fallback
2026-04-13 20:38:45 +10:00
webadderall 8f50cc1010 Merge pull request #224 from afostr/feature/preserve-hud-position
fix(hud): preserve user-dragged position across recording state changes
2026-04-13 20:37:26 +10:00
Wing900 654ed79ba8 Fix silent MP4 exports with FFmpeg audio fallback 2026-04-12 20:55:34 +08:00
webadderall b2342090b2 feat(editor): full-width timeline, extension i18n, lifecycle fixes
- Move timeline below preview+settings so it spans full editor width
- Remove react-resizable-panels vertical split
- Add scoped i18n for ExtensionManager (en/zh/ko/ja/es)
- Fix stale extension activation cleanup and deactivation on uninstall
- Cache-bust extension module URLs on reinstall
- Move bundled cursor packs to extension-sources/ for marketplace upload
- Track marketplace download counts (fire-and-forget POST)
2026-04-12 16:59:07 +10:00
webadderall 6296a05115 fix(audio): add browser microphone fallback for macOS < 14
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.
2026-04-12 16:59:06 +10:00
Andrew Foster dc39031990 resolve coderabbit nitpick listeners 2026-04-11 12:10:19 -05:00
Andrew Foster 192947cfed resolve coderabbit feedback on applyHudOverlayBounds 2026-04-11 12:10:19 -05:00
Andrew Foster a78f7d45d4 fix(hud): preserve user-dragged position across recording state changes
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.
2026-04-11 12:10:19 -05:00
webadderall d6f1225e45 feat(extensions): marketplace integration, wallpaper thumbnails, cursor packs, and bug fixes
- Extension marketplace UI with browsing, installing, screenshot gallery
- Wallpaper & cursor style registration API for extensions
- Wallpaper thumbnail system (nativeImage resize + disk cache, OOM-safe)
- Fix HUD crash: check configurable before redefining electronAPI
- Fix zip-slip false positive on macOS (fs.realpath for symlinks)
- Extension icon sizing and description truncation improvements
- Eager extension activation in SettingsPanel
2026-04-12 01:38:36 +10:00
webadderall d3fab9d83e security: restrict read-local-file and open-external-url IPC handlers
- read-local-file: whitelist to RECORDINGS_DIR, USER_DATA_PATH, asset root, and temp only
- open-external-url: reject non-http/https protocols (blocks file://, javascript:, etc.)

Prevents malicious renderer code from reading arbitrary files (SSH keys,
credentials) or opening dangerous URLs via shell.openExternal.
2026-04-12 01:37:28 +10:00
webadderall 7073d7deb5 feat: extension system with marketplace, permissions, and security hardening
- 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
2026-04-12 01:37:28 +10:00
webadderall 3150ed41f2 chore(audio): clean up probeMediaDurationSeconds after #216
- 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
2026-04-11 16:20:12 +10:00
Mohamed 0054bebef6 fix(audio): drop null muxer from probe to restore duration parsing
- 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
2026-04-10 19:33:26 +02:00
webadderall 433173e0c5 Merge branch 'pr-207' 2026-04-09 23:31:08 +10:00
webadderall d8e57f7600 fix(whisper): reset download state on model deletion failure
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
2026-04-09 23:27:14 +10:00
Mohamed 0c5fa40c1f fix(hud): resolve overlay snap to primary on multi-monitor
- 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.
2026-04-09 14:00:50 +02:00
webadderall 7d80678b38 fix(windows): use per-display scale factor for window cursor position
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
2026-04-09 21:23:48 +10:00
webadderall fb54994685 fix(macos): lower deployment target to macOS 13.0 for native helpers
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
2026-04-09 21:23:33 +10:00
webadderall ad426453c7 fix: harden cursor-monitor spawn against ENOENT on Windows
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.
2026-04-09 16:07:57 +10:00
webadderall 646dcf92f0 fix: cursor DPI normalization for Windows/Linux scaled displays
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).
2026-04-09 15:58:17 +10:00
Mohamed 20209cc02d fix(windows): use path.posix for URL path resolution
- 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.
2026-04-07 06:18:45 +02:00
webadderall 2692b28604 Hotfix 1.1.18 2026-04-06 22:39:31 +10:00
webadderall 2a155faaf6 Hotfix 1.1.17 2026-04-06 18:10:29 +10:00
webadderall 96d2ca9e5c Improve tray and packaged editor restore paths 2026-04-06 15:22:58 +10:00
webadderall 18812bda10 Fix Windows tray restore during recording 2026-04-05 23:23:16 +10:00