-
-
Notifications
You must be signed in to change notification settings - Fork 268
macOS: Cursor telemetry not saved after DMG install (Apple Silicon, macOS 26) #129
Description
Bug Description
After installing Recordly v1.1.7 via the DMG on macOS 26.2 (Apple Silicon M3), cursor telemetry .cursor.json files are not being created alongside recordings. The editor shows "No cursor telemetry available — Record a screencast first to generate cursor-based suggestions."
Environment
- OS: macOS 26.2 (Build 25C56) — Apple Silicon (M3 MacBook Pro)
- Recordly version: v1.1.7 (DMG install to
/Applications/Recordly.app) - Permissions granted: Screen Recording ✅, Accessibility ✅
- Quarantine flags: Cleared via
xattr -cr
Reproduction Steps
- Install Recordly v1.1.7 via DMG
- Grant Screen Recording and Accessibility permissions in System Settings
- Record a screencast (any duration, tried multiple 30s+ recordings)
- Open recording in editor
- Click the zoom suggestions / wand button
- See toast: "No cursor telemetry available"
Evidence
Recordings made with npm run dev (dev build) do produce .cursor.json files. Recordings made with the DMG-installed app do not.
# Recordings WITH cursor.json (dev build, same machine, same day):
recording-1774718957877.mp4.cursor.json (1.5MB, ~10:35am)
recording-1774719579103.mp4.cursor.json (1.5MB, ~10:45am)
recording-1774743781164.mp4.cursor.json (77KB, ~5:23pm)
# Recordings WITHOUT cursor.json (DMG install, 7pm onward):
recording-1774749616308.mp4 ← no .cursor.json
recording-1774750149033.mp4 ← no .cursor.json
recording-1774750394518.mp4 ← no .cursor.json
recording-1774751693887.mp4 ← no .cursor.json
The cursor.json files that exist from the dev build look correct:
{
"version": 2,
"samples": [
{ "timeMs": 1, "cx": 0.462, "cy": 0.940, "interactionType": "move", "cursorType": "arrow" }
]
}Analysis
After reviewing the source code, the cursor telemetry flow is:
- Recording start (
set-recording-statehandler,handlers.ts:3901) → starts 33ms interval sampling viasampleCursorPoint() - Recording stop (
handlers.ts:3916) → callssnapshotCursorTelemetryForPersistence()to moveactiveCursorSamples→pendingCursorSamples - Finalization (
finalizeStoredVideo(),handlers.ts:2604) → callspersistPendingCursorTelemetry()which writesvideoPath + ".cursor.json" - Editor load (
VideoEditor.tsx:1740) → reads the.cursor.jsonfile viaget-cursor-telemetryIPC handler
Likely root causes (in order of probability):
1. persistPendingCursorTelemetry() fails silently or never runs
At handlers.ts:2575-2585, there is no try-catch around the fs.writeFile() call. If the write fails (permissions, path issue, etc.), the error propagates up through finalizeStoredVideo() which also lacks error handling at line 2610. However, since the recording itself saves successfully, the error may be swallowed somewhere upstream or the function may not be called at all in the DMG build's code path.
2. activeCursorSamples is empty when recording stops
If sampleCursorPoint() never executes during recording (interval not starting, or getNormalizedCursorPoint() returning null), then snapshotCursorTelemetryForPersistence() exits early at line 2588 because activeCursorSamples.length === 0, and no file is written.
3. DMG build uses different app data path
The dev build stores data in ~/Library/Application Support/Recordly-dev/ while the DMG uses ~/Library/Application Support/Recordly/. If there's a path resolution mismatch for the cursor file write (e.g., currentVideoPath doesn't match where the recording was actually saved), the .cursor.json could be written to the wrong location or fail.
4. Native cursor monitor binary fails to spawn in DMG context
The startNativeCursorMonitor() function at handlers.ts:2257 spawns the bundled openscreen-native-cursor-monitor binary. While cursor position sampling uses Electron's getScreen().getCursorScreenPoint() (which should work regardless), the overall cursor capture initialization could be affected if the native monitor spawn throws and isn't caught properly.
5. uiohook native module not rebuilt for DMG Electron version
The interaction capture (startInteractionCapture()) uses uiohook-napi for mouse click events. If this native module wasn't properly rebuilt for the DMG's Electron version, it could cause cursor capture to fail at initialization.
Suggested Fix
Add error handling around persistPendingCursorTelemetry() in finalizeStoredVideo(), and add diagnostic logging to identify where the pipeline breaks:
// handlers.ts:2604
async function finalizeStoredVideo(videoPath: string) {
const validation = await validateRecordedVideo(videoPath)
snapshotCursorTelemetryForPersistence()
currentVideoPath = videoPath
currentProjectPath = null
try {
await persistPendingCursorTelemetry(videoPath)
} catch (error) {
console.error('Failed to persist cursor telemetry:', error)
// Don't let telemetry failure break video finalization
}
// ...
}And add logging in snapshotCursorTelemetryForPersistence():
function snapshotCursorTelemetryForPersistence() {
console.log(`[cursor] snapshot: ${activeCursorSamples.length} active samples`)
if (activeCursorSamples.length === 0) {
console.warn('[cursor] No active cursor samples to snapshot')
return
}
// ...
}Workaround
Running via npm run dev from source (~/Source/recordly) produces cursor telemetry correctly on the same machine. DMG install does not.