Commit Graph
254 Commits
Author SHA1 Message Date
webadderall 9469b30a4f fix(project): add named save IPC and stabilize recorder lifecycle 2026-04-21 11:10:31 +10:00
webadderall 8a22033a50 refactor: split large files (electron, lib, hooks, scripts) 2026-04-20 21:00:34 +10:00
webadderall 09f487d069 Merge pull request #282 from meiiie/fix/protect-project-recordings-from-prune
fix(recordings): preserve media referenced by saved projects
2026-04-20 20:37:07 +10:00
webadderall 0ccdc449e8 Merge pull request #278 from meiiie/fix/export-media-paths
fix(export): reopen saved videos and default to source quality
2026-04-20 20:35:21 +10:00
wiiiii123 3509ecf21c fix(recordings): fail closed during prune project scans 2026-04-20 02:03:45 +07:00
wiiiii123 f7e3494ee3 fix(recordings): preserve media referenced by saved projects 2026-04-20 01:49:40 +07:00
wiiiii123 cf676487b2 fix(recording): preserve mic companion audio when source has audio 2026-04-19 23:54:40 +07:00
wiiiii123 cac33005e3 test(export): tighten media path regression coverage 2026-04-19 23:44:10 +07:00
wiiiii123 4f739a058e fix(export): reopen saved videos and default to source quality 2026-04-19 21:42:06 +07:00
webadderall 7bf94ed309 Revert "fix(windows): normalize WGC sample timestamps" 2026-04-20 00:12:32 +10:00
wiiiii123 16e9a63eb6 fix(windows): normalize WGC sample timestamps 2026-04-19 20:20:06 +07:00
Uri b200deddca fix(linux/wayland): address PR review feedback
- electron/main.ts: treat missing sourceId on linux as portal sentinel
  so the request handler never calls getSources() (which itself opens
  an extra portal dialog) on fresh sessions.
- useScreenRecorder.ts: persist the synthesized portal sentinel via
  selectSource() so main has the source set before getDisplayMedia.
  Extract acquireLinuxPortalStream() helper to dedupe the three
  duplicated getDisplayMedia constraint blocks.
- LaunchWindow.tsx: hide the screen-source selector button (and its
  separator) on Linux so users cannot trigger an extra portal dialog
  via the dropdown.
2026-04-18 16:38:52 +03:00
Uri 571bbb9434 fix(linux/wayland): collapse 3-step capture flow into a single portal dialog
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.
2026-04-18 16:17:08 +03:00
webadderall fa97c94411 fix(linux): allow full-screen capture by matching display sources by position if IDs do not match (fixes #265) 2026-04-18 22:34:54 +10:00
webadderall c8db3f890f Merge pull request #264 from webadderall/fix/linux-gpu-hud-fallback
fix: Linux GPU fallback and HUD window show safety net
2026-04-18 22:12:25 +10:00
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 cbe0b9feac fix: replace real-time audio export with streaming decode + chunked offline rendering
Major rewrite of the audio export pipeline for stability and memory efficiency:

## Streaming Decode
- Primary decode path uses WebDemuxer + AudioDecoder (WebCodecs streaming)
- Falls back to bulk decodeAudioData if streaming fails
- Avoids holding full compressed file + decoded PCM simultaneously

## Chunked Offline Rendering
- Processes timeline in 30-second OfflineAudioContext chunks
- Memory bounded to ~30s of PCM per chunk regardless of recording length

## Chunked WAV Writing
- Writes PCM in ~256KB chunks instead of single massive ArrayBuffer
- Eliminates OOM for long recordings on the native/FFmpeg export path

## Chunked Encoding
- Single AudioEncoder kept alive across all chunks (clean AAC stream)
- Proper backpressure and error propagation per chunk

## scheduleBufferThroughTimeline chunk windowing
- Clips source nodes to chunk boundaries for correct cross-chunk audio
- Backwards compatible defaults when no chunk params provided

## Other export fixes
- Progress cap: 99% -> 100% in both exporters and UI
- Muxing errors: propagated instead of silently swallowed
- Mac recording: enhanced warning when no audio files for muxing
- Export timeout: eliminated by removing real-time rendering
2026-04-18 20:29:14 +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