Skip to content

Commit b525571

Browse files
Merge pull request #552 from marcgabe15/feature/diagnostics
add diagnostics report
2 parents fe93050 + a0c423d commit b525571

5 files changed

Lines changed: 83 additions & 0 deletions

File tree

electron/electron-env.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,12 @@ interface Window {
150150
setHasUnsavedChanges: (hasChanges: boolean) => void;
151151
onRequestSaveBeforeClose: (callback: () => Promise<boolean> | boolean) => () => void;
152152
setLocale: (locale: string) => Promise<void>;
153+
saveDiagnostic: (payload: {
154+
error: string;
155+
stack?: string;
156+
projectState: unknown;
157+
logs: string[];
158+
}) => Promise<{ success: boolean; path?: string; canceled?: boolean; error?: string }>;
153159
};
154160
}
155161

electron/ipc/handlers.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import fs from "node:fs/promises";
22
import { createRequire } from "node:module";
3+
import os from "node:os";
34
import path from "node:path";
45
import { fileURLToPath } from "node:url";
56

@@ -1317,4 +1318,45 @@ export function registerIpcHandlers(
13171318
return { success: false, error: String(error) };
13181319
}
13191320
});
1321+
1322+
ipcMain.handle(
1323+
"save-diagnostic",
1324+
async (
1325+
_,
1326+
payload: { error: string; stack?: string; projectState: unknown; logs: string[] },
1327+
) => {
1328+
const { filePath, canceled } = await dialog.showSaveDialog({
1329+
title: "Save Diagnostic File",
1330+
defaultPath: `openscreen-diagnostic-${Date.now()}.json`,
1331+
filters: [{ name: "JSON", extensions: ["json"] }],
1332+
});
1333+
1334+
if (canceled || !filePath) return { success: false, canceled: true };
1335+
1336+
const diagnostic = {
1337+
timestamp: new Date().toISOString(),
1338+
appVersion: app.getVersion(),
1339+
platform: process.platform,
1340+
arch: process.arch,
1341+
osRelease: os.release(),
1342+
osVersion: os.version(),
1343+
totalMemoryMB: Math.round(os.totalmem() / 1024 / 1024),
1344+
nodeVersion: process.versions.node,
1345+
electronVersion: process.versions.electron,
1346+
chromeVersion: process.versions.chrome,
1347+
error: payload.error,
1348+
stack: payload.stack,
1349+
projectState: payload.projectState,
1350+
recentLogs: payload.logs,
1351+
};
1352+
1353+
try {
1354+
await fs.writeFile(filePath, JSON.stringify(diagnostic, null, 2), "utf-8");
1355+
return { success: true, path: filePath };
1356+
} catch (error) {
1357+
console.error("Failed to write diagnostic file:", error);
1358+
return { success: false, error: String(error) };
1359+
}
1360+
},
1361+
);
13201362
}

electron/preload.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,14 @@ contextBridge.exposeInMainWorld("electronAPI", {
134134
setLocale: (locale: string) => {
135135
return ipcRenderer.invoke("set-locale", locale);
136136
},
137+
saveDiagnostic: (payload: {
138+
error: string;
139+
stack?: string;
140+
projectState: unknown;
141+
logs: string[];
142+
}) => {
143+
return ipcRenderer.invoke("save-diagnostic", payload);
144+
},
137145
setMicrophoneExpanded: (expanded: boolean) => {
138146
ipcRenderer.send("hud:setMicrophoneExpanded", expanded);
139147
},

src/components/video-editor/SettingsPanel.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
ChevronDown,
44
Crop,
55
Download,
6+
FileDown,
67
Film,
78
Image,
89
Lock,
@@ -240,6 +241,7 @@ interface SettingsPanelProps {
240241
webcamSizePreset?: WebcamSizePreset;
241242
onWebcamSizePresetChange?: (size: WebcamSizePreset) => void;
242243
onWebcamSizePresetCommit?: () => void;
244+
onSaveDiagnostic?: () => Promise<void>;
243245
}
244246

245247
export default SettingsPanel;
@@ -327,6 +329,7 @@ export function SettingsPanel({
327329
webcamSizePreset = DEFAULT_WEBCAM_SIZE_PRESET,
328330
onWebcamSizePresetChange,
329331
onWebcamSizePresetCommit,
332+
onSaveDiagnostic,
330333
}: SettingsPanelProps) {
331334
const t = useScopedT("settings");
332335
// Resolved URLs are for DOM rendering only (backgroundImage). The canonical
@@ -1682,6 +1685,16 @@ export function SettingsPanel({
16821685
<Bug className="w-3 h-3 text-[#34B27B]" />
16831686
{t("links.reportBug")}
16841687
</button>
1688+
{onSaveDiagnostic && (
1689+
<button
1690+
type="button"
1691+
onClick={onSaveDiagnostic}
1692+
className="flex-1 flex items-center justify-center gap-1.5 text-[10px] text-slate-500 hover:text-slate-300 py-1.5 transition-colors"
1693+
>
1694+
<FileDown className="w-3 h-3 text-slate-400" />
1695+
Save Diagnostics
1696+
</button>
1697+
)}
16851698
<button
16861699
type="button"
16871700
onClick={() => {

src/components/video-editor/VideoEditor.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1730,6 +1730,19 @@ export default function VideoEditor() {
17301730
}
17311731
}, []);
17321732

1733+
const handleSaveDiagnostic = useCallback(async () => {
1734+
const result = await window.electronAPI.saveDiagnostic({
1735+
error: exportError ?? "Manual diagnostic export",
1736+
projectState: editorState,
1737+
logs: [],
1738+
});
1739+
if (result.success) {
1740+
toast.success("Diagnostic file saved");
1741+
} else if (!result.canceled) {
1742+
toast.error("Failed to save diagnostic file");
1743+
}
1744+
}, [exportError, editorState]);
1745+
17331746
if (loading) {
17341747
return (
17351748
<div className="flex items-center justify-center h-screen bg-background">
@@ -2100,6 +2113,7 @@ export default function VideoEditor() {
21002113
onSpeedDelete={handleSpeedDelete}
21012114
unsavedExport={unsavedExport}
21022115
onSaveUnsavedExport={handleSaveUnsavedExport}
2116+
onSaveDiagnostic={handleSaveDiagnostic}
21032117
/>
21042118
</div>
21052119
</div>

0 commit comments

Comments
 (0)