node-mac-recorder 2.22.17 → 2.22.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cursorCapture/polling.js +144 -27
- package/package.json +1 -1
|
@@ -9,9 +9,13 @@ const IS_ELECTRON = !!(
|
|
|
9
9
|
process.versions.electron
|
|
10
10
|
);
|
|
11
11
|
|
|
12
|
-
const
|
|
12
|
+
const NATIVE_TEXT_INPUT_SAMPLE_MS = 120;
|
|
13
|
+
const NATIVE_TEXT_INPUT_GRACE_MS = 600;
|
|
13
14
|
|
|
14
|
-
const
|
|
15
|
+
const ELECTRON_SYNTH_GRACE_MS = 400;
|
|
16
|
+
const ELECTRON_SYNTH_THROTTLE_MS = 220;
|
|
17
|
+
|
|
18
|
+
const DEFAULT_CURSOR_INTERVAL_MS = IS_ELECTRON ? 50 : 20;
|
|
15
19
|
|
|
16
20
|
function shouldCaptureCursorSample(lastCapturedData, currentData) {
|
|
17
21
|
if (!lastCapturedData) {
|
|
@@ -33,6 +37,21 @@ function shouldCaptureCursorSample(lastCapturedData, currentData) {
|
|
|
33
37
|
return false;
|
|
34
38
|
}
|
|
35
39
|
|
|
40
|
+
function packDisplayInfoForExport(di) {
|
|
41
|
+
if (!di) return {};
|
|
42
|
+
return {
|
|
43
|
+
displayId: di.displayId,
|
|
44
|
+
displayX: Number.isFinite(Number(di.displayX))
|
|
45
|
+
? Number(di.displayX)
|
|
46
|
+
: Number(di.x) || 0,
|
|
47
|
+
displayY: Number.isFinite(Number(di.displayY))
|
|
48
|
+
? Number(di.displayY)
|
|
49
|
+
: Number(di.y) || 0,
|
|
50
|
+
width: di.displayWidth ?? di.width,
|
|
51
|
+
height: di.displayHeight ?? di.height,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
36
55
|
function transformGlobalToVideo(globalX, globalY, d) {
|
|
37
56
|
if (!d || !d.videoRelative) {
|
|
38
57
|
return {
|
|
@@ -76,6 +95,96 @@ function transformInputFrameGlobal(ifr, d) {
|
|
|
76
95
|
};
|
|
77
96
|
}
|
|
78
97
|
|
|
98
|
+
function tryAppendSyntheticTextInputRow(recorder, filepath, cursorData, timestamp) {
|
|
99
|
+
if (!IS_ELECTRON) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const ct = cursorData.cursorType || "";
|
|
103
|
+
if (ct !== "text" && ct !== "vertical-text") {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (timestamp < ELECTRON_SYNTH_GRACE_MS) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const wall = Date.now();
|
|
110
|
+
if (
|
|
111
|
+
wall - (recorder._electronSynthWallMs || 0) <
|
|
112
|
+
ELECTRON_SYNTH_THROTTLE_MS
|
|
113
|
+
) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const vx = cursorData.x;
|
|
117
|
+
const vy = cursorData.y;
|
|
118
|
+
if (!Number.isFinite(vx) || !Number.isFinite(vy)) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const tiRow = {
|
|
123
|
+
x: vx,
|
|
124
|
+
y: vy,
|
|
125
|
+
timestamp,
|
|
126
|
+
unixTimeMs: wall,
|
|
127
|
+
cursorType: "text",
|
|
128
|
+
type: "textInput",
|
|
129
|
+
caretX: vx,
|
|
130
|
+
caretY: vy,
|
|
131
|
+
inputFrame: {
|
|
132
|
+
x: vx - 1,
|
|
133
|
+
y: vy - 9,
|
|
134
|
+
width: 2,
|
|
135
|
+
height: 18,
|
|
136
|
+
},
|
|
137
|
+
coordinateSystem: cursorData.coordinateSystem,
|
|
138
|
+
recordingType: cursorData.recordingType,
|
|
139
|
+
videoInfo: cursorData.videoInfo || {},
|
|
140
|
+
displayInfo: cursorData.displayInfo || {},
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
if (cursorData.location) {
|
|
144
|
+
tiRow.location = cursorData.location;
|
|
145
|
+
}
|
|
146
|
+
if (cursorData.windowRelative) {
|
|
147
|
+
tiRow.windowRelative = cursorData.windowRelative;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (
|
|
151
|
+
recorder.cursorCaptureFirstWrite &&
|
|
152
|
+
recorder.cursorCaptureSessionTimestamp
|
|
153
|
+
) {
|
|
154
|
+
tiRow._syncMetadata = {
|
|
155
|
+
videoStartTime: recorder.cursorCaptureSessionTimestamp,
|
|
156
|
+
cursorStartTime: recorder.cursorCaptureStartTime,
|
|
157
|
+
offsetMs:
|
|
158
|
+
recorder.cursorCaptureStartTime -
|
|
159
|
+
recorder.cursorCaptureSessionTimestamp,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const le = recorder._lastTextInputEmitted;
|
|
164
|
+
if (
|
|
165
|
+
le &&
|
|
166
|
+
Math.abs(le.caretX - tiRow.caretX) < 0.75 &&
|
|
167
|
+
Math.abs(le.caretY - tiRow.caretY) < 0.75 &&
|
|
168
|
+
timestamp - le.timestamp < 220
|
|
169
|
+
) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
recorder._lastTextInputEmitted = {
|
|
173
|
+
caretX: tiRow.caretX,
|
|
174
|
+
caretY: tiRow.caretY,
|
|
175
|
+
timestamp,
|
|
176
|
+
};
|
|
177
|
+
recorder._electronSynthWallMs = wall;
|
|
178
|
+
|
|
179
|
+
const jsonString = JSON.stringify(tiRow);
|
|
180
|
+
if (recorder.cursorCaptureFirstWrite) {
|
|
181
|
+
fs.appendFileSync(filepath, jsonString);
|
|
182
|
+
recorder.cursorCaptureFirstWrite = false;
|
|
183
|
+
} else {
|
|
184
|
+
fs.appendFileSync(filepath, "," + jsonString);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
79
188
|
function tryAppendTextInput(
|
|
80
189
|
recorder,
|
|
81
190
|
nativeBinding,
|
|
@@ -83,10 +192,13 @@ function tryAppendTextInput(
|
|
|
83
192
|
position,
|
|
84
193
|
timestamp,
|
|
85
194
|
) {
|
|
195
|
+
if (IS_ELECTRON) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
86
198
|
if (typeof nativeBinding.getTextInputSnapshot !== "function") {
|
|
87
199
|
return;
|
|
88
200
|
}
|
|
89
|
-
if (timestamp <
|
|
201
|
+
if (timestamp < NATIVE_TEXT_INPUT_GRACE_MS) {
|
|
90
202
|
return;
|
|
91
203
|
}
|
|
92
204
|
const ct = position.cursorType || "";
|
|
@@ -94,7 +206,10 @@ function tryAppendTextInput(
|
|
|
94
206
|
return;
|
|
95
207
|
}
|
|
96
208
|
const wall = Date.now();
|
|
97
|
-
if (
|
|
209
|
+
if (
|
|
210
|
+
wall - (recorder._tiSampleWallMs || 0) <
|
|
211
|
+
NATIVE_TEXT_INPUT_SAMPLE_MS
|
|
212
|
+
) {
|
|
98
213
|
return;
|
|
99
214
|
}
|
|
100
215
|
recorder._tiSampleWallMs = wall;
|
|
@@ -141,13 +256,7 @@ function tryAppendTextInput(
|
|
|
141
256
|
offsetY: d.videoOffsetY,
|
|
142
257
|
}
|
|
143
258
|
: {},
|
|
144
|
-
displayInfo: d
|
|
145
|
-
? {
|
|
146
|
-
displayId: d.displayId,
|
|
147
|
-
width: d.displayWidth,
|
|
148
|
-
height: d.displayHeight,
|
|
149
|
-
}
|
|
150
|
-
: {},
|
|
259
|
+
displayInfo: packDisplayInfoForExport(d),
|
|
151
260
|
};
|
|
152
261
|
|
|
153
262
|
if (
|
|
@@ -194,10 +303,13 @@ function queueDeferredTextInputSample(
|
|
|
194
303
|
position,
|
|
195
304
|
timestamp,
|
|
196
305
|
) {
|
|
306
|
+
if (IS_ELECTRON) {
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
197
309
|
if (typeof nativeBinding.getTextInputSnapshot !== "function") {
|
|
198
310
|
return;
|
|
199
311
|
}
|
|
200
|
-
if (timestamp <
|
|
312
|
+
if (timestamp < NATIVE_TEXT_INPUT_GRACE_MS) {
|
|
201
313
|
return;
|
|
202
314
|
}
|
|
203
315
|
const ct = position.cursorType || "";
|
|
@@ -230,7 +342,7 @@ function queueDeferredTextInputSample(
|
|
|
230
342
|
|
|
231
343
|
async function startCursorCapture(recorder, nativeBinding, intervalOrFilepath, options = {}) {
|
|
232
344
|
let filepath;
|
|
233
|
-
let interval =
|
|
345
|
+
let interval = DEFAULT_CURSOR_INTERVAL_MS;
|
|
234
346
|
|
|
235
347
|
if (typeof intervalOrFilepath === "number") {
|
|
236
348
|
interval = Math.max(10, intervalOrFilepath);
|
|
@@ -283,6 +395,7 @@ async function startCursorCapture(recorder, nativeBinding, intervalOrFilepath, o
|
|
|
283
395
|
recorder.lastCapturedData = null;
|
|
284
396
|
recorder.cursorCaptureSessionTimestamp = recorder.sessionTimestamp;
|
|
285
397
|
recorder._tiSampleWallMs = 0;
|
|
398
|
+
recorder._electronSynthWallMs = 0;
|
|
286
399
|
recorder._lastTextInputEmitted = null;
|
|
287
400
|
recorder._tiDeferredPending = false;
|
|
288
401
|
|
|
@@ -330,13 +443,7 @@ async function startCursorCapture(recorder, nativeBinding, intervalOrFilepath, o
|
|
|
330
443
|
offsetY: di.videoOffsetY,
|
|
331
444
|
}
|
|
332
445
|
: {},
|
|
333
|
-
displayInfo: di
|
|
334
|
-
? {
|
|
335
|
-
displayId: di.displayId,
|
|
336
|
-
width: di.displayWidth,
|
|
337
|
-
height: di.displayHeight,
|
|
338
|
-
}
|
|
339
|
-
: {},
|
|
446
|
+
displayInfo: packDisplayInfoForExport(di),
|
|
340
447
|
};
|
|
341
448
|
|
|
342
449
|
if (di?.multiWindowBounds && di.multiWindowBounds.length > 0) {
|
|
@@ -405,13 +512,22 @@ async function startCursorCapture(recorder, nativeBinding, intervalOrFilepath, o
|
|
|
405
512
|
recorder.lastCapturedData = { ...cursorData };
|
|
406
513
|
}
|
|
407
514
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
515
|
+
if (IS_ELECTRON) {
|
|
516
|
+
tryAppendSyntheticTextInputRow(
|
|
517
|
+
recorder,
|
|
518
|
+
filepath,
|
|
519
|
+
cursorData,
|
|
520
|
+
timestamp,
|
|
521
|
+
);
|
|
522
|
+
} else {
|
|
523
|
+
queueDeferredTextInputSample(
|
|
524
|
+
recorder,
|
|
525
|
+
nativeBinding,
|
|
526
|
+
filepath,
|
|
527
|
+
position,
|
|
528
|
+
timestamp,
|
|
529
|
+
);
|
|
530
|
+
}
|
|
415
531
|
} catch (error) {
|
|
416
532
|
console.error("Cursor capture error:", error);
|
|
417
533
|
}
|
|
@@ -444,6 +560,7 @@ async function stopCursorCapture(recorder) {
|
|
|
444
560
|
recorder.cursorCaptureFirstWrite = true;
|
|
445
561
|
recorder.cursorDisplayInfo = null;
|
|
446
562
|
recorder._tiSampleWallMs = 0;
|
|
563
|
+
recorder._electronSynthWallMs = 0;
|
|
447
564
|
recorder._lastTextInputEmitted = null;
|
|
448
565
|
recorder._tiDeferredPending = false;
|
|
449
566
|
|