From 28ff0fb7bf41aa43b4badd093cacb5cc071f281e Mon Sep 17 00:00:00 2001 From: EtienneLescot Date: Sun, 3 May 2026 16:53:04 +0200 Subject: [PATCH] fix: restore cursor pipeline build after rebase --- .gitignore | 3 ++ electron/ipc/handlers.ts | 44 ++++++++++++++++++- electron/preload.ts | 1 + src/components/video-editor/SettingsPanel.tsx | 2 - src/components/video-editor/VideoEditor.tsx | 11 +++-- src/components/video-editor/VideoPlayback.tsx | 4 +- src/hooks/useScreenRecorder.ts | 3 +- src/lib/exporter/frameRenderer.ts | 2 +- 8 files changed, 60 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index 771c4bd..494da30 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,6 @@ __screenshots__/ result result-* .direnv/ + +#kilocode +.kilo/ \ No newline at end of file diff --git a/electron/ipc/handlers.ts b/electron/ipc/handlers.ts index d50cab3..3a4dd3b 100644 --- a/electron/ipc/handlers.ts +++ b/electron/ipc/handlers.ts @@ -451,9 +451,12 @@ async function storeRecordedSessionFiles(payload: StoreRecordedSessionInput) { export function registerIpcHandlers( createEditorWindow: () => void, createSourceSelectorWindow: () => BrowserWindow, + _createCountdownOverlayWindow: () => BrowserWindow, getMainWindow: () => BrowserWindow | null, getSourceSelectorWindow: () => BrowserWindow | null, + _getCountdownOverlayWindow?: () => BrowserWindow | null, onRecordingStateChange?: (recording: boolean, sourceName: string) => void, + _switchToHud?: () => void, ) { ipcMain.handle("get-sources", async (_, opts) => { const sources = await desktopCapturer.getSources(opts); @@ -472,7 +475,7 @@ export function registerIpcHandlers( // Reuse the exact source object returned during enumeration to avoid // Windows window-source id mismatches across separate getSources() calls. selectedDesktopSource = - typeof source.id === "string" ? lastEnumeratedSources.get(source.id) ?? null : null; + typeof source.id === "string" ? (lastEnumeratedSources.get(source.id) ?? null) : null; if (!selectedDesktopSource && typeof source.id === "string") { try { @@ -602,6 +605,45 @@ export function registerIpcHandlers( } }); + async function storeRecordedSessionFiles(payload: StoreRecordedSessionInput) { + const createdAt = + typeof payload.createdAt === "number" && Number.isFinite(payload.createdAt) + ? payload.createdAt + : Date.now(); + const screenVideoPath = path.join(RECORDINGS_DIR, payload.screen.fileName); + await fs.writeFile(screenVideoPath, Buffer.from(payload.screen.videoData)); + + let webcamVideoPath: string | undefined; + if (payload.webcam) { + webcamVideoPath = path.join(RECORDINGS_DIR, payload.webcam.fileName); + await fs.writeFile(webcamVideoPath, Buffer.from(payload.webcam.videoData)); + } + + const session: RecordingSession = webcamVideoPath + ? { screenVideoPath, webcamVideoPath, createdAt } + : { screenVideoPath, createdAt }; + setCurrentRecordingSessionState(session); + currentVideoPath = screenVideoPath; + currentProjectPath = null; + + const telemetryPath = `${screenVideoPath}.cursor.json`; + if (pendingCursorRecordingData && pendingCursorRecordingData.samples.length > 0) { + await fs.writeFile( + telemetryPath, + JSON.stringify(pendingCursorRecordingData, null, 2), + "utf-8", + ); + } + pendingCursorRecordingData = null; + + return { + success: true, + path: screenVideoPath, + session, + message: "Recording session stored successfully", + }; + } + ipcMain.handle("store-recorded-video", async (_, videoData: ArrayBuffer, fileName: string) => { try { return await storeRecordedSessionFiles({ diff --git a/electron/preload.ts b/electron/preload.ts index 5ff110d..5345aab 100644 --- a/electron/preload.ts +++ b/electron/preload.ts @@ -1,4 +1,5 @@ import { contextBridge, ipcRenderer } from "electron"; +import type { RecordingSession, StoreRecordedSessionInput } from "../src/lib/recordingSession"; import { NATIVE_BRIDGE_CHANNEL, type NativeBridgeRequest } from "../src/native/contracts"; // Asset base URL is passed from the main process via webPreferences.additionalArguments diff --git a/src/components/video-editor/SettingsPanel.tsx b/src/components/video-editor/SettingsPanel.tsx index 110f025..ad74239 100644 --- a/src/components/video-editor/SettingsPanel.tsx +++ b/src/components/video-editor/SettingsPanel.tsx @@ -435,8 +435,6 @@ export function SettingsPanel({ const [selectedColor, setSelectedColor] = useState("#ADADAD"); const [gradient, setGradient] = useState(GRADIENTS[0]); - const [showCropModal, setShowCropModal] = useState(false); - const cropSnapshotRef = useRef(null); const [cropAspectLocked, setCropAspectLocked] = useState(false); const [cropAspectRatio, setCropAspectRatio] = useState(""); const isPortraitCanvas = isPortraitAspectRatio(aspectRatio); diff --git a/src/components/video-editor/VideoEditor.tsx b/src/components/video-editor/VideoEditor.tsx index 842767a..5394eee 100644 --- a/src/components/video-editor/VideoEditor.tsx +++ b/src/components/video-editor/VideoEditor.tsx @@ -38,12 +38,12 @@ import { saveUserPreferences, } from "@/lib/userPreferences"; import { BackgroundLoadError } from "@/lib/wallpaper"; +import { nativeBridgeClient, useCursorRecordingData, useCursorTelemetry } from "@/native"; import { getAspectRatioValue, getNativeAspectRatioValue, isPortraitAspectRatio, } from "@/utils/aspectRatioUtils"; -import { nativeBridgeClient, useCursorRecordingData, useCursorTelemetry } from "@/native"; import { ExportDialog } from "./ExportDialog"; import PlaybackControls from "./PlaybackControls"; import { @@ -216,7 +216,12 @@ export default function VideoEditor() { } const project = candidate; - const sourcePath = project.videoPath; + const projectMedia = resolveProjectMedia(project); + if (!projectMedia) { + return false; + } + const sourcePath = projectMedia.screenVideoPath; + const webcamSourcePath = projectMedia.webcamVideoPath ?? null; const normalizedEditor = normalizeProjectEditor(project.editor); const inferredDurationMs = Math.max( 0, @@ -405,7 +410,7 @@ export default function VideoEditor() { setVideoPath(toFileUrl(result.path)); setCurrentProjectPath(null); setLastSavedSnapshot( - createProjectSnapshot({ screenVideoPath: sourcePath }, INITIAL_EDITOR_STATE), + createProjectSnapshot({ screenVideoPath: result.path }, INITIAL_EDITOR_STATE), ); } else { setError("No video to load. Please record or select a video."); diff --git a/src/components/video-editor/VideoPlayback.tsx b/src/components/video-editor/VideoPlayback.tsx index 32db9d2..d7b3836 100644 --- a/src/components/video-editor/VideoPlayback.tsx +++ b/src/components/video-editor/VideoPlayback.tsx @@ -25,14 +25,14 @@ import { type WebcamLayoutPreset, type WebcamSizePreset, } from "@/lib/compositeLayout"; -import { classifyWallpaper, DEFAULT_WALLPAPER, resolveImageWallpaperUrl } from "@/lib/wallpaper"; -import { getCssClipPath } from "@/lib/webcamMaskShapes"; import { getNativeCursorDisplayMetrics, hasNativeCursorRecordingData, projectNativeCursorToStage, resolveInterpolatedNativeCursorFrame, } from "@/lib/cursor/nativeCursor"; +import { classifyWallpaper, DEFAULT_WALLPAPER, resolveImageWallpaperUrl } from "@/lib/wallpaper"; +import { getCssClipPath } from "@/lib/webcamMaskShapes"; import type { CursorRecordingData } from "@/native/contracts"; import { type AspectRatio, diff --git a/src/hooks/useScreenRecorder.ts b/src/hooks/useScreenRecorder.ts index 67b0d96..8aa673e 100644 --- a/src/hooks/useScreenRecorder.ts +++ b/src/hooks/useScreenRecorder.ts @@ -1,7 +1,8 @@ import { fixWebmDuration } from "@fix-webm-duration/fix"; import { useCallback, useEffect, useRef, useState } from "react"; import { toast } from "sonner"; -import { nativeBridgeClient } from "@/native"; +import { useScopedT } from "@/contexts/I18nContext"; +import { requestCameraAccess } from "@/lib/requestCameraAccess"; const TARGET_FRAME_RATE = 60; const MIN_FRAME_RATE = 30; diff --git a/src/lib/exporter/frameRenderer.ts b/src/lib/exporter/frameRenderer.ts index 6f605b0..c43908f 100644 --- a/src/lib/exporter/frameRenderer.ts +++ b/src/lib/exporter/frameRenderer.ts @@ -614,7 +614,7 @@ export class FrameRenderer { return image; } - private updateLayout(): void { + private updateLayout(webcamFrame?: VideoFrame | null): void { if (!this.app || !this.videoSprite || !this.maskGraphics || !this.videoContainer) return; const { width, height } = this.config;