add: 20 seconds -> 2 seconds fix, for now behind a flag to be sure to be able to revert.

This commit is contained in:
Alan Trebugeais
2026-05-10 13:01:45 +02:00
parent f6a40d7324
commit 3d8ae1942a
10 changed files with 361 additions and 143 deletions
+39
View File
@@ -0,0 +1,39 @@
# Plan de Diagnostic de Performance : "Stop Recording"
L'objectif est d'identifier précisément les goulots d'étranglement qui causent un délai de 10-15 secondes après l'arrêt d'un enregistrement natif sur Windows.
## 1. Instrumentation Frontend (`src/hooks/useScreenRecorder.ts`)
Nous allons mesurer le temps total côté UI et le temps d'attente pour chaque appel IPC majeur.
- **Total Stop Sequence** : Du clic sur "Stop" à l'ouverture de l'éditeur.
- **IPC: stopNativeScreenRecording** : Temps d'arrêt du processus de capture natif.
- **IPC: muxNativeWindowsRecording** : Temps passé dans FFmpeg pour le padding et le muxing audio.
- **Store Sidecar** : Temps d'écriture de l'audio du microphone enregistré par le navigateur.
## 2. Instrumentation Main Process IPC (`electron/ipc/register/recording.ts`)
Nous allons mesurer le temps passé dans les handlers IPC pour différencier le temps de traitement de l'overhead de communication.
- **Handler: stop-native-screen-recording**
- **Handler: mux-native-windows-recording**
## 3. Instrumentation Logique Interne (`electron/ipc/recording/windows.ts`)
C'est ici que se trouvent les opérations FFmpeg suspectées.
- **extendNativeWindowsVideoToDuration** : Temps de re-encodage pour ajouter du padding (Suspect #1).
- **muxNativeWindowsVideoWithAudio** : Temps de muxing audio (Suspect #2).
- **Probing** : Temps passé dans `ffprobe`.
## Méthodologie d'analyse
Une fois les logs ajoutés :
1. Lancer l'app en mode dev.
2. Faire un enregistrement court (10s).
3. Arrêter l'enregistrement.
4. Récupérer les logs dans :
- La **Console du navigateur** (pour le frontend).
- Le **Terminal** (pour le processus main d'Electron).
Les logs seront préfixés par `[PERF:RENDERER]` et `[PERF:MAIN]`.
+7
View File
@@ -38,3 +38,10 @@ export function shouldKeepRecordingAudioSidecars(env: NodeJS.ProcessEnv = proces
const value = env[RECORDING_AUDIO_SIDECAR_DEBUG_ENV]?.trim().toLowerCase();
return value === "1" || value === "true" || value === "yes" || value === "on";
}
/**
* Toggle for the recording finalization optimizations.
* - true: Fast probe (no -count_frames), separate audio tracks (no muxing).
* - false: slow probe (full decode), combined audio/video (heavy muxing).
*/
export const OPTIMIZE_RECORDING_FINALIZATION = true;
+12 -1
View File
@@ -2,6 +2,7 @@ import { execFile } from "node:child_process";
import fs from "node:fs/promises";
import { promisify } from "node:util";
import { COMPANION_AUDIO_LAYOUTS } from "../constants";
import { OPTIMIZE_RECORDING_FINALIZATION } from "./audioFilters";
import { getFfmpegBinaryPath, getFfprobeBinaryPath } from "../ffmpeg/binary";
import { lastNativeCaptureDiagnostics, setLastNativeCaptureDiagnostics } from "../state";
import type { CompanionAudioCandidate, NativeCaptureDiagnostics } from "../types";
@@ -190,6 +191,7 @@ export function summarizeMicrophoneChunkTiming(
/** Probe the duration of a media file (in seconds) using the container header. */
export async function probeMediaDurationSeconds(filePath: string): Promise<number> {
const start = Date.now();
const ffmpegPath = getFfmpegBinaryPath();
try {
await execFileAsync(ffmpegPath, ["-i", filePath, "-hide_banner"], { timeout: 5000 });
@@ -199,6 +201,10 @@ export async function probeMediaDurationSeconds(filePath: string): Promise<numbe
if (duration !== null) {
return duration;
}
} finally {
console.log(
`[PERF:MAIN] probeMediaDurationSeconds: COMPLETED in ${Date.now() - start}ms`,
);
}
return 0;
}
@@ -265,6 +271,7 @@ export function parseFfprobeVideoStreamDuration(output: string): VideoStreamDura
export async function probeVideoStreamDuration(
filePath: string,
): Promise<VideoStreamDurationProbe | null> {
const start = Date.now();
try {
const result = await execFileAsync(
getFfprobeBinaryPath(),
@@ -273,7 +280,7 @@ export async function probeVideoStreamDuration(
"error",
"-select_streams",
"v:0",
"-count_frames",
...(OPTIMIZE_RECORDING_FINALIZATION ? [] : ["-count_frames"]),
"-show_entries",
"stream=duration,nb_frames,nb_read_frames,avg_frame_rate,r_frame_rate",
"-of",
@@ -286,6 +293,10 @@ export async function probeVideoStreamDuration(
return parseFfprobeVideoStreamDuration(stdout);
} catch {
return null;
} finally {
console.log(
`[PERF:MAIN] probeVideoStreamDuration: COMPLETED in ${Date.now() - start}ms`,
);
}
}
+2 -1
View File
@@ -29,6 +29,7 @@ import {
} from "../state";
import type { AudioSyncAdjustment } from "../types";
import { isAutoRecordingPath, moveFileWithOverwrite } from "../utils";
import { OPTIMIZE_RECORDING_FINALIZATION } from "./audioFilters";
import {
getFileSizeIfPresent,
getRecordingAudioMuxTimeoutMs,
@@ -318,7 +319,7 @@ export function attachNativeCaptureLifecycle(process: ChildProcessWithoutNullStr
export async function finalizeStoredVideo(videoPath: string) {
// Safety net: if companion audio files still exist, the mux was skipped — attempt it now
if (videoPath.endsWith(".mp4")) {
if (!OPTIMIZE_RECORDING_FINALIZATION && videoPath.endsWith(".mp4")) {
const companionCandidates = await getUsableCompanionAudioCandidates(videoPath);
for (const { systemPath, micPath, platform } of companionCandidates) {
if (platform === "mac" || platform === "win") {
+69 -14
View File
@@ -24,6 +24,7 @@ import {
import type { AudioSyncAdjustment } from "../types";
import { moveFileWithOverwrite } from "../utils";
import {
OPTIMIZE_RECORDING_FINALIZATION,
shouldKeepRecordingAudioSidecars,
WINDOWS_NATIVE_MIC_PRE_FILTERS,
} from "./audioFilters";
@@ -193,6 +194,9 @@ export async function extendNativeWindowsVideoToDuration(
videoPath: string,
targetDurationMs: number | null | undefined,
): Promise<NativeWindowsVideoPaddingResult> {
const start = Date.now();
console.log("[PERF:MAIN] extendNativeWindowsVideoToDuration: STARTED");
try {
if (!Number.isFinite(targetDurationMs) || (targetDurationMs ?? 0) <= 0) {
return {
padded: false,
@@ -267,8 +271,13 @@ export async function extendNativeWindowsVideoToDuration(
padDurationSeconds,
};
} catch (error) {
await fs.rm(paddedOutputPath, { force: true }).catch(() => undefined);
throw error;
await fs.rm(paddedOutputPath, { force: true }).catch(() => undefined);
throw error;
}
} finally {
console.log(
`[PERF:MAIN] extendNativeWindowsVideoToDuration: COMPLETED in ${Date.now() - start}ms`,
);
}
}
@@ -277,6 +286,9 @@ export async function muxNativeWindowsVideoWithAudio(
systemAudioPath: string | null,
micAudioPath: string | null,
): Promise<NativeWindowsAudioMuxResult> {
const start = Date.now();
console.log("[PERF:MAIN] muxNativeWindowsVideoWithAudio: STARTED");
try {
const ffmpegPath = getFfmpegBinaryPath();
const keepAudioSidecars = shouldKeepRecordingAudioSidecars();
const inputs: string[] = ["-i", videoPath];
@@ -385,6 +397,44 @@ export async function muxNativeWindowsVideoWithAudio(
durationDeltaMs: 0,
};
if (OPTIMIZE_RECORDING_FINALIZATION) {
console.log("[mux-win] Optimization enabled: skipping heavy muxing, keeping tracks separate.");
const videoPathWithoutExt = videoPath.replace(/\.[^.]+$/u, "");
// Move audio sidecars to final companion paths if they exist
if (systemAudioPath) {
const finalSystemPath = `${videoPathWithoutExt}.system.wav`;
if (systemAudioPath !== finalSystemPath) {
try {
await moveFileWithOverwrite(systemAudioPath, finalSystemPath);
if (audio.system) audio.system.path = finalSystemPath;
} catch (err) {
console.error(`[mux-win] Failed to move system audio to ${finalSystemPath}:`, err);
}
}
}
if (micAudioPath) {
const finalMicPath = `${videoPathWithoutExt}.mic.wav`;
if (micAudioPath !== finalMicPath) {
try {
await moveFileWithOverwrite(micAudioPath, finalMicPath);
if (audio.mic) audio.mic.path = finalMicPath;
} catch (err) {
console.error(`[mux-win] Failed to move mic audio to ${finalMicPath}:`, err);
}
}
}
return {
muxed: false,
videoDurationSeconds: videoDuration,
muxTimeoutMs,
audioInputs,
audio,
keptAudioSidecars: true,
};
}
try {
if (audioInputs.length === 2) {
const filterParts: string[] = [];
@@ -469,17 +519,22 @@ export async function muxNativeWindowsVideoWithAudio(
await validateRecordedVideo(mixedOutputPath);
await moveFileWithOverwrite(mixedOutputPath, videoPath);
} catch (error) {
await fs.rm(mixedOutputPath, { force: true }).catch(() => undefined);
throw error;
}
await fs.rm(mixedOutputPath, { force: true }).catch(() => undefined);
throw error;
}
return {
muxed: true,
videoDurationSeconds: videoDuration,
muxTimeoutMs,
audioInputs,
audio,
outputPath: videoPath,
keptAudioSidecars: true,
};
return {
muxed: true,
videoDurationSeconds: videoDuration,
muxTimeoutMs,
audioInputs,
audio,
outputPath: videoPath,
keptAudioSidecars: true,
};
} finally {
console.log(
`[PERF:MAIN] muxNativeWindowsVideoWithAudio: COMPLETED in ${Date.now() - start}ms`,
);
}
}
+139 -125
View File
@@ -40,6 +40,7 @@ import {
import { rememberApprovedLocalReadPath } from "../project/manager";
import {
getBrowserMicSidecarFilters,
OPTIMIZE_RECORDING_FINALIZATION,
shouldKeepRecordingAudioSidecars,
} from "../recording/audioFilters";
import {
@@ -821,6 +822,9 @@ export function registerRecordingHandlers(
);
ipcMain.handle("stop-native-screen-recording", async () => {
const start = Date.now();
console.log("[PERF:MAIN] Handler: stop-native-screen-recording: STARTED");
try {
// Windows native capture stop path
if (process.platform === "win32" && windowsNativeCaptureActive) {
try {
@@ -1093,11 +1097,16 @@ export function registerRecordingHandlers(
return recovered;
}
return {
success: false,
message: "Failed to stop native ScreenCaptureKit recording",
error: String(error),
};
return {
success: false,
message: "Failed to stop native ScreenCaptureKit recording",
error: String(error),
};
}
} finally {
console.log(
`[PERF:MAIN] Handler: stop-native-screen-recording: COMPLETED in ${Date.now() - start}ms`,
);
}
});
@@ -1258,139 +1267,144 @@ export function registerRecordingHandlers(
});
ipcMain.handle("mux-native-windows-recording", async (_event, expectedDurationMs?: number) => {
const videoPath = windowsPendingVideoPath;
const orphanedMicAudioPath = windowsOrphanedMicAudioPath;
const diagnosticsSystemAudioPath = windowsSystemAudioPath;
const diagnosticsMicAudioPath = windowsMicAudioPath;
setWindowsPendingVideoPath(null);
setWindowsOrphanedMicAudioPath(null);
if (!videoPath) {
return { success: false, message: "No native Windows video pending for mux" };
}
const start = Date.now();
console.log("[PERF:MAIN] Handler: mux-native-windows-recording: STARTED");
try {
await writeWindowsRecordingDiagnostics(videoPath, {
phase: "mux-start",
expectedDurationMs,
outputPath: videoPath,
systemAudioPath: diagnosticsSystemAudioPath,
microphonePath: diagnosticsMicAudioPath,
details: {
hasSystemAudio: Boolean(diagnosticsSystemAudioPath),
hasMicrophone: Boolean(diagnosticsMicAudioPath),
hasOrphanedMicrophone: Boolean(orphanedMicAudioPath),
},
});
try {
const padding = await extendNativeWindowsVideoToDuration(
videoPath,
expectedDurationMs,
);
await writeWindowsRecordingDiagnostics(videoPath, {
phase: "pad",
expectedDurationMs,
outputPath: videoPath,
systemAudioPath: diagnosticsSystemAudioPath,
microphonePath: diagnosticsMicAudioPath,
details: { ...padding },
});
if (padding.padded) {
console.log(
`[mux-win] Extended native Windows video to ${padding.durationSeconds.toFixed(3)}s using the final frame`,
);
}
} catch (paddingError) {
console.warn(
"[mux-win] Failed to extend native Windows video duration:",
paddingError,
);
await writeWindowsRecordingDiagnostics(videoPath, {
phase: "pad",
expectedDurationMs,
outputPath: videoPath,
systemAudioPath: diagnosticsSystemAudioPath,
microphonePath: diagnosticsMicAudioPath,
error: String(paddingError),
});
const videoPath = windowsPendingVideoPath;
const orphanedMicAudioPath = windowsOrphanedMicAudioPath;
const diagnosticsSystemAudioPath = windowsSystemAudioPath;
const diagnosticsMicAudioPath = windowsMicAudioPath;
setWindowsPendingVideoPath(null);
setWindowsOrphanedMicAudioPath(null);
if (!videoPath) {
return { success: false, message: "No native Windows video pending for mux" };
}
let muxDetails: unknown = null;
if (diagnosticsSystemAudioPath || diagnosticsMicAudioPath) {
muxDetails = await muxNativeWindowsVideoWithAudio(
videoPath,
diagnosticsSystemAudioPath,
diagnosticsMicAudioPath,
);
try {
await writeWindowsRecordingDiagnostics(videoPath, {
phase: "mux-start",
expectedDurationMs,
outputPath: videoPath,
systemAudioPath: diagnosticsSystemAudioPath,
microphonePath: diagnosticsMicAudioPath,
details: {
hasSystemAudio: Boolean(diagnosticsSystemAudioPath),
hasMicrophone: Boolean(diagnosticsMicAudioPath),
hasOrphanedMicrophone: Boolean(orphanedMicAudioPath),
},
});
if (!OPTIMIZE_RECORDING_FINALIZATION && expectedDurationMs) {
try {
const padding = await extendNativeWindowsVideoToDuration(
videoPath,
expectedDurationMs,
);
await writeWindowsRecordingDiagnostics(videoPath, {
phase: "pad",
expectedDurationMs,
outputPath: videoPath,
systemAudioPath: diagnosticsSystemAudioPath,
microphonePath: diagnosticsMicAudioPath,
details: { ...padding },
});
} catch (paddingError) {
console.warn(
"[mux-win] Failed to extend native Windows video duration:",
paddingError,
);
await writeWindowsRecordingDiagnostics(videoPath, {
phase: "pad",
expectedDurationMs,
outputPath: videoPath,
systemAudioPath: diagnosticsSystemAudioPath,
microphonePath: diagnosticsMicAudioPath,
error: String(paddingError),
});
}
}
let muxDetails: unknown = null;
if (diagnosticsSystemAudioPath || diagnosticsMicAudioPath) {
muxDetails = await muxNativeWindowsVideoWithAudio(
videoPath,
diagnosticsSystemAudioPath,
diagnosticsMicAudioPath,
);
setWindowsSystemAudioPath(null);
setWindowsMicAudioPath(null);
}
recordNativeCaptureDiagnostics({
backend: "windows-wgc",
phase: "mux",
outputPath: videoPath,
fileSizeBytes: await getFileSizeIfPresent(videoPath),
});
await writeWindowsRecordingDiagnostics(videoPath, {
phase: "mux-complete",
expectedDurationMs,
outputPath: videoPath,
systemAudioPath: diagnosticsSystemAudioPath,
microphonePath: diagnosticsMicAudioPath,
details: {
fileSizeBytes: await getFileSizeIfPresent(videoPath),
mux: muxDetails,
},
});
await cleanupWindowsOrphanedMicAudioPath(orphanedMicAudioPath);
return await finalizeStoredVideo(videoPath);
} catch (error) {
console.error("Failed to mux native Windows recording:", error);
recordNativeCaptureDiagnostics({
backend: "windows-wgc",
phase: "mux",
outputPath: videoPath,
systemAudioPath: diagnosticsSystemAudioPath,
microphonePath: diagnosticsMicAudioPath,
fileSizeBytes: await getFileSizeIfPresent(videoPath),
error: String(error),
});
await writeWindowsRecordingDiagnostics(videoPath, {
phase: "mux-error",
expectedDurationMs,
outputPath: videoPath,
systemAudioPath: diagnosticsSystemAudioPath,
microphonePath: diagnosticsMicAudioPath,
error: String(error),
details: {
fileSizeBytes: await getFileSizeIfPresent(videoPath),
},
});
setWindowsSystemAudioPath(null);
setWindowsMicAudioPath(null);
}
recordNativeCaptureDiagnostics({
backend: "windows-wgc",
phase: "mux",
outputPath: videoPath,
fileSizeBytes: await getFileSizeIfPresent(videoPath),
});
await writeWindowsRecordingDiagnostics(videoPath, {
phase: "mux-complete",
expectedDurationMs,
outputPath: videoPath,
systemAudioPath: diagnosticsSystemAudioPath,
microphonePath: diagnosticsMicAudioPath,
details: {
fileSizeBytes: await getFileSizeIfPresent(videoPath),
mux: muxDetails,
},
});
await cleanupWindowsOrphanedMicAudioPath(orphanedMicAudioPath);
return await finalizeStoredVideo(videoPath);
} catch (error) {
console.error("Failed to mux native Windows recording:", error);
recordNativeCaptureDiagnostics({
backend: "windows-wgc",
phase: "mux",
outputPath: videoPath,
systemAudioPath: diagnosticsSystemAudioPath,
microphonePath: diagnosticsMicAudioPath,
fileSizeBytes: await getFileSizeIfPresent(videoPath),
error: String(error),
});
await writeWindowsRecordingDiagnostics(videoPath, {
phase: "mux-error",
expectedDurationMs,
outputPath: videoPath,
systemAudioPath: diagnosticsSystemAudioPath,
microphonePath: diagnosticsMicAudioPath,
error: String(error),
details: {
fileSizeBytes: await getFileSizeIfPresent(videoPath),
},
});
setWindowsSystemAudioPath(null);
setWindowsMicAudioPath(null);
await cleanupWindowsOrphanedMicAudioPath(orphanedMicAudioPath);
try {
return await finalizeStoredVideo(videoPath);
} catch {
await cleanupWindowsOrphanedMicAudioPath(orphanedMicAudioPath);
try {
await validateRecordedVideo(videoPath);
return await finalizeStoredVideo(videoPath);
} catch {
try {
await validateRecordedVideo(videoPath);
return {
success: false,
path: videoPath,
message: "Failed to mux native Windows recording",
error: String(error),
};
} catch {
// The fallback path is not safely playable; surface the original mux error.
}
return {
success: false,
path: videoPath,
message: "Failed to mux native Windows recording",
error: String(error),
};
} catch {
// The fallback path is not safely playable; surface the original mux error.
}
return {
success: false,
message: "Failed to mux native Windows recording",
error: String(error),
};
}
} finally {
console.log(
`[PERF:MAIN] Handler: mux-native-windows-recording: COMPLETED in ${Date.now() - start}ms`,
);
}
});
+4 -2
View File
@@ -696,6 +696,8 @@ function loadPackagedEditorWindow(win: BrowserWindow) {
}
export function createEditorWindow(): BrowserWindow {
const perfStart = Date.now();
console.log("[PERF:MAIN] createEditorWindow: STARTED");
const isMac = process.platform === "darwin";
const { workArea, workAreaSize } = getScreen().getPrimaryDisplay();
const initialWidth = isMac ? Math.round(workAreaSize.width * 0.85) : workArea.width;
@@ -735,12 +737,12 @@ export function createEditorWindow(): BrowserWindow {
});
win.once("ready-to-show", () => {
console.log("[editor-window] ready-to-show");
console.log(`[PERF:MAIN] Editor Window: ready-to-show in ${Date.now() - perfStart}ms`);
win.show();
});
win.webContents.on("did-finish-load", () => {
console.log("[editor-window] did-finish-load", win.webContents.getURL());
console.log(`[PERF:MAIN] Editor Window: did-finish-load in ${Date.now() - perfStart}ms`);
win?.webContents.send("main-process-message", new Date().toLocaleString());
// Fallback for Linux/Wayland where `ready-to-show` may not fire reliably.
if (!win.isDestroyed() && !win.isVisible()) {
+51
View File
@@ -0,0 +1,51 @@
import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
const skeletonVariants = cva(
"relative overflow-hidden rounded-md transition-shadow",
{
variants: {
variant: {
default: "bg-muted/40",
glass: "bg-white/5 backdrop-blur-sm border border-white/10",
dark: "bg-black/20",
subtle: "bg-foreground/[0.03]",
},
animation: {
none: "",
pulse: "animate-pulse",
shimmer: "before:absolute before:inset-0 before:-translate-x-full before:animate-shimmer before:bg-gradient-to-r before:from-transparent before:via-foreground/[0.04] before:to-transparent",
"shimmer-glass": "before:absolute before:inset-0 before:-translate-x-full before:animate-shimmer before:bg-gradient-to-r before:from-transparent before:via-white/[0.08] before:to-transparent",
},
},
defaultVariants: {
variant: "default",
animation: "shimmer",
},
},
);
export interface SkeletonProps
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof skeletonVariants> {}
/**
* A magnificent Skeleton component designed with Apple-inspired aesthetics.
* Supports shimmer animations, pulse effects, and glassmorphism.
*/
const Skeleton = React.forwardRef<HTMLDivElement, SkeletonProps>(
({ className, variant, animation, ...props }, ref) => {
return (
<div
ref={ref}
className={cn(skeletonVariants({ variant, animation, className }))}
{...props}
/>
);
},
);
Skeleton.displayName = "Skeleton";
export { Skeleton, skeletonVariants };
+32
View File
@@ -620,6 +620,8 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
const finalizeRecordingSession = useCallback(
async (videoPath: string, webcamPath: string | null) => {
const start = performance.now();
console.log("[PERF:RENDERER] Finalize Session & Switch to Editor: STARTED");
const shouldHideOverlayCursor = hideEditorOverlayCursorByDefault.current;
try {
if (webcamPath) {
@@ -648,6 +650,9 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
setFinalizing(false);
await window.electronAPI.switchToEditor();
console.log(
`[PERF:RENDERER] Finalize Session & Switch to Editor: COMPLETED in ${(performance.now() - start).toFixed(2)}ms`,
);
},
[],
);
@@ -1004,6 +1009,9 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
setFinalizing(true);
void (async () => {
const stopStart = performance.now();
console.log("[PERF:RENDERER] Total Stop Sequence: STARTED");
const fallbackStartDelayMs = micFallbackStartDelayMs.current;
const fallbackTrackSettings = micFallbackTrackSettings.current;
const stoppedAtMs = Date.now();
@@ -1014,7 +1022,13 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
const isNativeWindows = nativeWindowsRecording.current;
nativeWindowsRecording.current = false;
const ipcStopStart = performance.now();
console.log("[PERF:RENDERER] IPC: stopNativeScreenRecording: STARTED");
const result = await window.electronAPI.stopNativeScreenRecording();
console.log(
`[PERF:RENDERER] IPC: stopNativeScreenRecording: COMPLETED in ${(performance.now() - ipcStopStart).toFixed(2)}ms`,
);
await window.electronAPI?.setRecordingState(false);
const webcamPath = await webcamPathPromise;
@@ -1030,6 +1044,9 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
fallbackStartDelayMs,
);
if (recoveredPath) {
console.log(
`[PERF:RENDERER] Total Stop Sequence (RECOVERED) in ${(performance.now() - stopStart).toFixed(2)}ms`,
);
return;
}
} catch (recoveryError) {
@@ -1049,8 +1066,14 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
let finalPath = result.path;
if (isNativeWindows) {
const ipcMuxStart = performance.now();
console.log("[PERF:RENDERER] IPC: muxNativeWindowsRecording: STARTED");
const muxResult =
await window.electronAPI.muxNativeWindowsRecording(expectedDurationMs);
console.log(
`[PERF:RENDERER] IPC: muxNativeWindowsRecording: COMPLETED in ${(performance.now() - ipcMuxStart).toFixed(2)}ms`,
);
if (!muxResult?.success || !muxResult.path) {
void logNativeCaptureDiagnostics("mux-native-windows-recording");
const fallbackPath = muxResult?.path ?? finalPath;
@@ -1068,14 +1091,23 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
}
}
const sidecarStart = performance.now();
console.log("[PERF:RENDERER] Store Sidecar: STARTED");
await storeMicrophoneSidecar(
micFallbackBlobPromise,
finalPath,
fallbackStartDelayMs,
fallbackTrackSettings,
);
console.log(
`[PERF:RENDERER] Store Sidecar: COMPLETED in ${(performance.now() - sidecarStart).toFixed(2)}ms`,
);
await finalizeRecordingSession(finalPath, webcamPath);
console.log(
`[PERF:RENDERER] Total Stop Sequence: COMPLETED in ${(performance.now() - stopStart).toFixed(2)}ms`,
);
})();
return;
}
+6
View File
@@ -13,10 +13,16 @@ module.exports = {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: "0" },
},
shimmer: {
"100%": {
transform: "translateX(100%)",
},
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
shimmer: "shimmer 2s infinite",
},
borderRadius: {
lg: "var(--radius)",