Files
Recordly/electron/ipc/utils.ts
webadderall 673dfdadd3 refactor: extract registerIpcHandlers into 8 focused register/ modules
handlers.ts reduced from 2930 → 65 lines (pure delegation).

New files under electron/ipc/register/:
- sources.ts    — get-sources, select-source, show-source-highlight, open-source-selector
- recording.ts  — start/stop/pause native + ffmpeg, mux, store, set-recording-state, get-cursor-telemetry
- permissions.ts — accessibility/screen permissions, open-external-url
- assets.ts     — wallpaper thumbnails, asset-base-path, list-asset-dir, read-local-file
- export.ts     — native-video-export-*, save-exported-video
- captions.ts   — whisper model, file pickers, generate-auto-captions
- project.ts    — project files, recordings dir, video/session state, delete-recording
- settings.ts   — shortcuts, recording prefs, countdown, platform info

Also moved shared helpers:
- getMacPrivacySettingsUrl, approveUserPath → utils.ts
- isTrustedProjectPath → project/manager.ts
2026-04-17 20:13:47 +10:00

125 lines
3.4 KiB
TypeScript

import { createRequire } from "node:module";
import fs from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { app } from "electron";
import { RECORDINGS_DIR } from "../appPaths";
import { RECORDINGS_SETTINGS_FILE, AUTO_RECORDING_PREFIX } from "./constants";
import {
approvedLocalReadPaths,
customRecordingsDir,
setCustomRecordingsDir,
recordingsDirLoaded,
setRecordingsDirLoaded,
} from "./state";
const nodeRequire = createRequire(import.meta.url);
export function getScreen() {
if (!app.isReady()) {
throw new Error(
"getScreen() called before app is ready. Ensure all screen access happens after app.whenReady().",
);
}
return nodeRequire("electron").screen as typeof import("electron").screen;
}
export function normalizePath(filePath: string) {
return path.resolve(filePath);
}
export function normalizeVideoSourcePath(videoPath?: string | null): string | null {
if (typeof videoPath !== "string") {
return null;
}
const trimmed = videoPath.trim();
if (!trimmed) {
return null;
}
if (/^file:\/\//i.test(trimmed)) {
try {
return fileURLToPath(trimmed);
} catch {
// Fall through and keep best-effort string path below.
}
}
return trimmed;
}
export function parseWindowId(sourceId?: string) {
if (!sourceId) return null;
const match = sourceId.match(/^window:(\d+)/);
return match ? Number.parseInt(match[1], 10) : null;
}
export function getTelemetryPathForVideo(videoPath: string) {
return `${videoPath}.cursor.json`;
}
export function isAutoRecordingPath(filePath: string) {
return path.basename(filePath).startsWith(AUTO_RECORDING_PREFIX);
}
export async function moveFileWithOverwrite(sourcePath: string, destinationPath: string) {
await fs.mkdir(path.dirname(destinationPath), { recursive: true });
await fs.rm(destinationPath, { force: true });
try {
await fs.rename(sourcePath, destinationPath);
} catch (error) {
const nodeError = error as NodeJS.ErrnoException;
if (nodeError.code !== "EXDEV") {
throw error;
}
await fs.copyFile(sourcePath, destinationPath);
await fs.unlink(sourcePath);
}
}
async function loadRecordingsDirectorySetting() {
if (recordingsDirLoaded) {
return;
}
setRecordingsDirLoaded(true);
try {
const content = await fs.readFile(RECORDINGS_SETTINGS_FILE, "utf-8");
const parsed = JSON.parse(content) as { recordingsDir?: unknown };
if (typeof parsed.recordingsDir === "string" && parsed.recordingsDir.trim()) {
setCustomRecordingsDir(path.resolve(parsed.recordingsDir));
}
} catch {
setCustomRecordingsDir(null);
}
}
export async function getRecordingsDir() {
await loadRecordingsDirectorySetting();
const targetDir = customRecordingsDir ?? RECORDINGS_DIR;
await fs.mkdir(targetDir, { recursive: true });
return targetDir;
}
export function getMacPrivacySettingsUrl(pane: "screen" | "accessibility" | "microphone"): string {
if (pane === "screen")
return "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture";
if (pane === "microphone")
return "x-apple.systempreferences:com.apple.preference.security?Privacy_Microphone";
return "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility";
}
export function approveUserPath(filePath: string | null | undefined): void {
if (!filePath) return;
try {
approvedLocalReadPaths.add(path.resolve(filePath));
} catch {
// Ignore invalid paths; later reads will surface the underlying error.
}
}