fix: address native cursor review findings
This commit is contained in:
@@ -118,7 +118,13 @@ export class WindowsNativeRecordingSession implements CursorRecordingSession {
|
||||
this.rejectReady(error);
|
||||
});
|
||||
|
||||
await this.waitUntilReady();
|
||||
try {
|
||||
await this.waitUntilReady();
|
||||
} catch (error) {
|
||||
this.terminateHelperProcess();
|
||||
this.cleanupHelperScript(helperScriptPath);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async stop(): Promise<CursorRecordingData> {
|
||||
@@ -126,9 +132,7 @@ export class WindowsNativeRecordingSession implements CursorRecordingSession {
|
||||
this.process = null;
|
||||
this.clearReadyState();
|
||||
|
||||
if (child && !child.killed) {
|
||||
child.kill();
|
||||
}
|
||||
this.killHelperProcess(child);
|
||||
|
||||
this.logDiagnostic("stop", {
|
||||
sampleCount: this.sampleCount,
|
||||
@@ -287,8 +291,16 @@ export class WindowsNativeRecordingSession implements CursorRecordingSession {
|
||||
|
||||
private failHelper(error: Error) {
|
||||
this.rejectReady(error);
|
||||
this.terminateHelperProcess();
|
||||
}
|
||||
|
||||
private terminateHelperProcess() {
|
||||
const child = this.process;
|
||||
this.process = null;
|
||||
this.killHelperProcess(child);
|
||||
}
|
||||
|
||||
private killHelperProcess(child: ChildProcessByStdio<null, Readable, Readable> | null) {
|
||||
if (child && !child.killed) {
|
||||
child.kill();
|
||||
}
|
||||
|
||||
@@ -89,6 +89,15 @@ import {
|
||||
import { UnsavedChangesDialog } from "./UnsavedChangesDialog";
|
||||
import VideoPlayback, { VideoPlaybackRef } from "./VideoPlayback";
|
||||
|
||||
function isClickInteractionType(interactionType: string | null | undefined) {
|
||||
return (
|
||||
interactionType === "click" ||
|
||||
interactionType === "double-click" ||
|
||||
interactionType === "right-click" ||
|
||||
interactionType === "middle-click"
|
||||
);
|
||||
}
|
||||
|
||||
export default function VideoEditor() {
|
||||
const {
|
||||
state: editorState,
|
||||
|
||||
@@ -188,6 +188,25 @@ function getResolvedVideoDuration(video: HTMLVideoElement): number | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
function getEndedVideoDuration(video: HTMLVideoElement): number | null {
|
||||
const currentTime = video.currentTime;
|
||||
if (!Number.isFinite(currentTime) || currentTime <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (video.ended) {
|
||||
return currentTime;
|
||||
}
|
||||
|
||||
const resolvedDuration = getResolvedVideoDuration(video);
|
||||
const durationEpsilonSeconds = 0.05;
|
||||
if (resolvedDuration && currentTime >= resolvedDuration - durationEpsilonSeconds) {
|
||||
return resolvedDuration;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
|
||||
(
|
||||
{
|
||||
|
||||
@@ -59,6 +59,43 @@ function clamp(value: number, min: number, max: number) {
|
||||
|
||||
const NATIVE_CURSOR_CLICK_ANIMATION_MS = 140;
|
||||
const NATIVE_CURSOR_MOTION_BLUR_MAX_PX = 6;
|
||||
const nativeCursorAssetMapCache = new WeakMap<
|
||||
CursorRecordingData,
|
||||
Map<string, NativeCursorAsset>
|
||||
>();
|
||||
|
||||
function findNativeCursorSampleIndexAtOrBefore(samples: CursorRecordingSample[], timeMs: number) {
|
||||
let low = 0;
|
||||
let high = samples.length - 1;
|
||||
let result = -1;
|
||||
|
||||
while (low <= high) {
|
||||
const middle = low + Math.floor((high - low) / 2);
|
||||
if (samples[middle].timeMs <= timeMs) {
|
||||
result = middle;
|
||||
low = middle + 1;
|
||||
} else {
|
||||
high = middle - 1;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function getNativeCursorAssetMap(recordingData: CursorRecordingData) {
|
||||
const cached = nativeCursorAssetMapCache.get(recordingData);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const assetMap = new Map(recordingData.assets.map((asset) => [asset.id, asset]));
|
||||
nativeCursorAssetMapCache.set(recordingData, assetMap);
|
||||
return assetMap;
|
||||
}
|
||||
|
||||
function getNativeCursorAsset(recordingData: CursorRecordingData, assetId: string) {
|
||||
return getNativeCursorAssetMap(recordingData).get(assetId) ?? null;
|
||||
}
|
||||
|
||||
interface PrettyNativeCursorAsset {
|
||||
imageDataUrl: string;
|
||||
@@ -296,12 +333,12 @@ export function getNativeCursorClickBounceProgress(
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (let index = recordingData.samples.length - 1; index >= 0; index -= 1) {
|
||||
for (
|
||||
let index = findNativeCursorSampleIndexAtOrBefore(recordingData.samples, timeMs);
|
||||
index >= 0;
|
||||
index -= 1
|
||||
) {
|
||||
const sample = recordingData.samples[index];
|
||||
if (sample.timeMs > timeMs) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const ageMs = timeMs - sample.timeMs;
|
||||
if (ageMs > NATIVE_CURSOR_CLICK_ANIMATION_MS) {
|
||||
return 0;
|
||||
@@ -397,17 +434,15 @@ export function resolveActiveNativeCursorFrame(
|
||||
return null;
|
||||
}
|
||||
|
||||
for (let index = recordingData.samples.length - 1; index >= 0; index -= 1) {
|
||||
const index = findNativeCursorSampleIndexAtOrBefore(recordingData.samples, timeMs);
|
||||
if (index >= 0) {
|
||||
const sample = recordingData.samples[index];
|
||||
if (sample.timeMs > timeMs) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sample.visible === false || !sample.assetId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const asset = recordingData.assets.find((candidate) => candidate.id === sample.assetId);
|
||||
const asset = getNativeCursorAsset(recordingData, sample.assetId);
|
||||
if (!asset) {
|
||||
return null;
|
||||
}
|
||||
@@ -427,14 +462,7 @@ export function resolveInterpolatedNativeCursorFrame(
|
||||
}
|
||||
|
||||
const samples = recordingData.samples;
|
||||
let activeIndex = -1;
|
||||
|
||||
for (let index = samples.length - 1; index >= 0; index -= 1) {
|
||||
if (samples[index].timeMs <= timeMs) {
|
||||
activeIndex = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const activeIndex = findNativeCursorSampleIndexAtOrBefore(samples, timeMs);
|
||||
|
||||
if (activeIndex < 0) {
|
||||
return null;
|
||||
@@ -445,7 +473,7 @@ export function resolveInterpolatedNativeCursorFrame(
|
||||
return null;
|
||||
}
|
||||
|
||||
const asset = recordingData.assets.find((candidate) => candidate.id === activeSample.assetId);
|
||||
const asset = getNativeCursorAsset(recordingData, activeSample.assetId);
|
||||
if (!asset) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -601,6 +601,7 @@ export class FrameRenderer {
|
||||
sample: displaySample,
|
||||
});
|
||||
if (!projectedPoint) {
|
||||
resetNativeCursorSmoothingState(this.nativeCursorSmoothingState);
|
||||
resetNativeCursorMotionBlurState(this.nativeCursorMotionBlurState);
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user