-
-
Notifications
You must be signed in to change notification settings - Fork 303
fix: Two related bugs in v1.1.8 on macOS (reproducible on M1): #139
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2605,17 +2605,16 @@ function snapshotCursorTelemetryForPersistence() { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async function finalizeStoredVideo(videoPath: string) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let validation: { fileSizeBytes: number; durationSeconds: number | null } | null = null | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| validation = await validateRecordedVideo(videoPath) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.warn('Video validation failed (proceeding anyway):', error) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Persist cursor telemetry before validation so it's saved even if validation fails. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| snapshotCursorTelemetryForPersistence() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await persistPendingCursorTelemetry(videoPath).catch((err) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.warn('[finalizeStoredVideo] Failed to persist cursor telemetry:', err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const validation = await validateRecordedVideo(videoPath) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| currentVideoPath = videoPath | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| currentProjectPath = null | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await persistPendingCursorTelemetry(videoPath) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (isAutoRecordingPath(videoPath)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await pruneAutoRecordings([videoPath]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -2637,16 +2636,16 @@ async function finalizeStoredVideo(videoPath: string) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| supported: lastNativeCaptureDiagnostics.supported, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| helperExists: lastNativeCaptureDiagnostics.helperExists, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| processOutput: lastNativeCaptureDiagnostics.processOutput, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fileSizeBytes: validation?.fileSizeBytes ?? null, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fileSizeBytes: validation.fileSizeBytes, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| success: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| path: videoPath, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message: validation?.durationSeconds !== null && validation !== null | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message: validation.durationSeconds !== null | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? `Video stored successfully (${validation.fileSizeBytes} bytes, ${validation.durationSeconds.toFixed(2)}s)` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : `Video stored successfully` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : `Video stored successfully (${validation.fileSizeBytes} bytes)` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -2815,9 +2814,6 @@ export function registerIpcHandlers( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? await desktopCapturer.getSources({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ...opts, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| types: electronTypes, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }).catch((error) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.warn('desktopCapturer.getSources failed (screen recording permission may be missing):', error) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return [] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : [] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const ownWindowNames = new Set( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -2950,13 +2946,15 @@ export function registerIpcHandlers( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sourceType: 'window' as const, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .filter((source) => Boolean(source.thumbnail)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Apply the usable-thumbnail check before converting to a data URL.
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return [...screenSources, ...mergedWindowSources] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.warn('Falling back to Electron window enumeration on macOS:', error) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const windowSources = electronSources | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .filter((source) => source.id.startsWith('window:')) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .filter((source) => hasUsableSourceThumbnail(source.thumbnail)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .filter((source) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const normalizedName = normalizeDesktopSourceName(source.name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!normalizedName) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -3006,16 +3004,41 @@ export function registerIpcHandlers( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const isWindow = source.id?.startsWith('window:') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const windowId = isWindow ? parseWindowId(source.id) : null | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ── 1. Bring window to front ── | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ── 1. Bring window to front & get its bounds via AppleScript ── | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let asBounds: { x: number; y: number; width: number; height: number } | null = null | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (isWindow && process.platform === 'darwin') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const appName = source.appName || source.name?.split(' — ')[0]?.trim() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (appName) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Single AppleScript: activate AND return window bounds | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await execFileAsync('osascript', ['-e', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `tell application "${appName}" to activate` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], { timeout: 2000 }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await new Promise((resolve) => setTimeout(resolve, 350)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch { /* ignore */ } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { stdout } = await execFileAsync('osascript', ['-e', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `tell application "${appName}"\n` + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ` activate\n` + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `end tell\n` + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `delay 0.3\n` + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `tell application "System Events"\n` + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ` tell process "${appName}"\n` + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ` set frontWindow to front window\n` + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ` set {x1, y1} to position of frontWindow\n` + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ` set {w1, h1} to size of frontWindow\n` + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ` return (x1 as text) & "," & (y1 as text) & "," & (w1 as text) & "," & (h1 as text)\n` + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ` end tell\n` + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `end tell` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], { timeout: 4000 }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const parts = stdout.trim().split(',').map(Number) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (parts.length === 4 && parts.every(n => Number.isFinite(n))) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| asBounds = { x: parts[0], y: parts[1], width: parts[2], height: parts[3] } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Fallback: just activate without bounds | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await execFileAsync('osascript', ['-e', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `tell application "${appName}" to activate` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], { timeout: 2000 }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+3015
to
+3038
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: cd /root && find . -name "handlers.ts" -type f | head -20Repository: webadderall/Recordly Length of output: 119 🏁 Script executed: # Check if the file exists and its size
wc -l electron/ipc/handlers.ts 2>/dev/null || find . -name "handlers.ts" -path "*/ipc/*" -type fRepository: webadderall/Recordly Length of output: 93 🏁 Script executed: # Read the specific section mentioned
sed -n '3000,3050p' electron/ipc/handlers.tsRepository: webadderall/Recordly Length of output: 2356 🏁 Script executed: # Also search for the appName variable definition and context around those lines
sed -n '2950,3100p' electron/ipc/handlers.tsRepository: webadderall/Recordly Length of output: 5555 🏁 Script executed: # Search for other osascript calls in the file to see if there are similar patterns
rg -n "execFileAsync.*osascript" electron/ipc/handlers.ts -A 3Repository: webadderall/Recordly Length of output: 508 🏁 Script executed: # Also check if there are other vulnerable string interpolation patterns with execFileAsync and shell commands
rg -n "execFileAsync.*\$\{.*\}" electron/ipc/handlers.ts | head -20Repository: webadderall/Recordly Length of output: 46 🏁 Script executed: # Verify the AppleScript on run argv pattern is valid by checking if there are any existing uses in the codebase
rg -n "on run argv" . --type typescript --type javascript --type applescriptRepository: webadderall/Recordly Length of output: 98 Fix AppleScript code injection vulnerability via Both 🔒 Safer pattern- const { stdout } = await execFileAsync('osascript', ['-e',
- `tell application "${appName}"\n` +
- ` activate\n` +
- `end tell\n` +
- `delay 0.3\n` +
- `tell application "System Events"\n` +
- ` tell process "${appName}"\n` +
- ` set frontWindow to front window\n` +
- ` set {x1, y1} to position of frontWindow\n` +
- ` set {w1, h1} to size of frontWindow\n` +
- ` return (x1 as text) & "," & (y1 as text) & "," & (w1 as text) & "," & (h1 as text)\n` +
- ` end tell\n` +
- `end tell`
- ], { timeout: 4000 })
+ const script = `
+on run argv
+ set appName to item 1 of argv
+ tell application appName to activate
+ delay 0.3
+ tell application "System Events"
+ tell process appName
+ set frontWindow to front window
+ set {x1, y1} to position of frontWindow
+ set {w1, h1} to size of frontWindow
+ return (x1 as text) & "," & (y1 as text) & "," & (w1 as text) & "," & (h1 as text)
+ end tell
+ end tell
+end run`
+ const { stdout } = await execFileAsync('osascript', ['-e', script, appName], { timeout: 4000 })Also apply the same fix to the fallback call at line 3036. 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await new Promise((resolve) => setTimeout(resolve, 350)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch { /* ignore */ } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else if (windowId && process.platform === 'linux') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -3029,15 +3052,17 @@ export function registerIpcHandlers( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ── 2. Resolve bounds ── | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let bounds: { x: number; y: number; width: number; height: number } | null = null | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let bounds = asBounds | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (source.id?.startsWith('screen:')) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bounds = getDisplayBoundsForSource(source) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else if (isWindow) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (process.platform === 'darwin') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bounds = await resolveMacWindowBounds(source) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else if (process.platform === 'linux') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bounds = await resolveLinuxWindowBounds(source) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!bounds) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (source.id?.startsWith('screen:')) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bounds = getDisplayBoundsForSource(source) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else if (isWindow) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (process.platform === 'darwin') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bounds = await resolveMacWindowBounds(source) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else if (process.platform === 'linux') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bounds = await resolveLinuxWindowBounds(source) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+3055
to
3066
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Prefer the selected window's bounds over the app's front window.
🎯 Suggested fix- let bounds = asBounds
-
- if (!bounds) {
- if (source.id?.startsWith('screen:')) {
- bounds = getDisplayBoundsForSource(source)
- } else if (isWindow) {
- if (process.platform === 'darwin') {
- bounds = await resolveMacWindowBounds(source)
- } else if (process.platform === 'linux') {
- bounds = await resolveLinuxWindowBounds(source)
- }
- }
- }
+ let bounds: WindowBounds | null = null
+
+ if (source.id?.startsWith('screen:')) {
+ bounds = getDisplayBoundsForSource(source)
+ } else if (isWindow) {
+ if (process.platform === 'darwin') {
+ bounds = await resolveMacWindowBounds(source) ?? asBounds
+ } else if (process.platform === 'linux') {
+ bounds = await resolveLinuxWindowBounds(source)
+ }
+ }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -3353,15 +3378,14 @@ body{background:transparent;overflow:hidden;width:100vw;height:100vh} | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const helperPath = await ensureNativeCaptureHelperBinary() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const timestamp = Date.now() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const outputPath = path.join(recordingsDir, `recording-${timestamp}.mp4`) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const outputPath = path.join(recordingsDir, `recording-${Date.now()}.mp4`) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const capturesSystemAudio = Boolean(options?.capturesSystemAudio) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const capturesMicrophone = Boolean(options?.capturesMicrophone) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const systemAudioOutputPath = capturesSystemAudio | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? path.join(recordingsDir, `recording-${timestamp}.system.m4a`) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? path.join(recordingsDir, `recording-${Date.now()}.system.m4a`) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : null | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const microphoneOutputPath = capturesMicrophone | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? path.join(recordingsDir, `recording-${timestamp}.mic.m4a`) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? path.join(recordingsDir, `recording-${Date.now()}.mic.m4a`) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : null | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const config: Record<string, unknown> = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fps: 60, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Revert CRLF line endings to LF (Unix-style).
The AI summary indicates these lines were changed to use Windows-style CRLF line endings. This is problematic for a cross-platform project, especially one addressing macOS bugs.
.gitignorefiles should use LF (Unix) line endings to ensure consistent behavior across macOS, Linux, and Windows (Git automatically handles line ending conversions for repository files).CRLF line endings in
.gitignorecan cause Git to malfunction on Unix-like systems, potentially failing to ignore specified files and directories.Additionally, these formatting changes appear unrelated to the PR's stated objectives (fixing macOS video recording and cursor telemetry bugs).
🔧 Recommended action
Ensure the entire
.gitignorefile uses LF line endings. Most editors and Git tools can automatically convert line endings. If you're using Git, you can configure:Then re-stage the file to normalize line endings.
🤖 Prompt for AI Agents