add diagnostics report
This commit is contained in:
Vendored
+6
@@ -150,6 +150,12 @@ interface Window {
|
||||
setHasUnsavedChanges: (hasChanges: boolean) => void;
|
||||
onRequestSaveBeforeClose: (callback: () => Promise<boolean> | boolean) => () => void;
|
||||
setLocale: (locale: string) => Promise<void>;
|
||||
saveDiagnostic: (payload: {
|
||||
error: string;
|
||||
stack?: string;
|
||||
projectState: unknown;
|
||||
logs: string[];
|
||||
}) => Promise<{ success: boolean; path?: string; canceled?: boolean; error?: string }>;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import fs from "node:fs/promises";
|
||||
import { createRequire } from "node:module";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
@@ -1317,4 +1318,45 @@ export function registerIpcHandlers(
|
||||
return { success: false, error: String(error) };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle(
|
||||
"save-diagnostic",
|
||||
async (
|
||||
_,
|
||||
payload: { error: string; stack?: string; projectState: unknown; logs: string[] },
|
||||
) => {
|
||||
const { filePath, canceled } = await dialog.showSaveDialog({
|
||||
title: "Save Diagnostic File",
|
||||
defaultPath: `openscreen-diagnostic-${Date.now()}.json`,
|
||||
filters: [{ name: "JSON", extensions: ["json"] }],
|
||||
});
|
||||
|
||||
if (canceled || !filePath) return { success: false, canceled: true };
|
||||
|
||||
const diagnostic = {
|
||||
timestamp: new Date().toISOString(),
|
||||
appVersion: app.getVersion(),
|
||||
platform: process.platform,
|
||||
arch: process.arch,
|
||||
osRelease: os.release(),
|
||||
osVersion: os.version(),
|
||||
totalMemoryMB: Math.round(os.totalmem() / 1024 / 1024),
|
||||
nodeVersion: process.versions.node,
|
||||
electronVersion: process.versions.electron,
|
||||
chromeVersion: process.versions.chrome,
|
||||
error: payload.error,
|
||||
stack: payload.stack,
|
||||
projectState: payload.projectState,
|
||||
recentLogs: payload.logs,
|
||||
};
|
||||
|
||||
try {
|
||||
await fs.writeFile(filePath, JSON.stringify(diagnostic, null, 2), "utf-8");
|
||||
return { success: true, path: filePath };
|
||||
} catch (error) {
|
||||
console.error("Failed to write diagnostic file:", error);
|
||||
return { success: false, error: String(error) };
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -134,6 +134,14 @@ contextBridge.exposeInMainWorld("electronAPI", {
|
||||
setLocale: (locale: string) => {
|
||||
return ipcRenderer.invoke("set-locale", locale);
|
||||
},
|
||||
saveDiagnostic: (payload: {
|
||||
error: string;
|
||||
stack?: string;
|
||||
projectState: unknown;
|
||||
logs: string[];
|
||||
}) => {
|
||||
return ipcRenderer.invoke("save-diagnostic", payload);
|
||||
},
|
||||
setMicrophoneExpanded: (expanded: boolean) => {
|
||||
ipcRenderer.send("hud:setMicrophoneExpanded", expanded);
|
||||
},
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
ChevronDown,
|
||||
Crop,
|
||||
Download,
|
||||
FileDown,
|
||||
Film,
|
||||
Image,
|
||||
Lock,
|
||||
@@ -240,6 +241,7 @@ interface SettingsPanelProps {
|
||||
webcamSizePreset?: WebcamSizePreset;
|
||||
onWebcamSizePresetChange?: (size: WebcamSizePreset) => void;
|
||||
onWebcamSizePresetCommit?: () => void;
|
||||
onSaveDiagnostic?: () => Promise<void>;
|
||||
}
|
||||
|
||||
export default SettingsPanel;
|
||||
@@ -327,6 +329,7 @@ export function SettingsPanel({
|
||||
webcamSizePreset = DEFAULT_WEBCAM_SIZE_PRESET,
|
||||
onWebcamSizePresetChange,
|
||||
onWebcamSizePresetCommit,
|
||||
onSaveDiagnostic,
|
||||
}: SettingsPanelProps) {
|
||||
const t = useScopedT("settings");
|
||||
// Resolved URLs are for DOM rendering only (backgroundImage). The canonical
|
||||
@@ -1682,6 +1685,16 @@ export function SettingsPanel({
|
||||
<Bug className="w-3 h-3 text-[#34B27B]" />
|
||||
{t("links.reportBug")}
|
||||
</button>
|
||||
{onSaveDiagnostic && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onSaveDiagnostic}
|
||||
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"
|
||||
>
|
||||
<FileDown className="w-3 h-3 text-slate-400" />
|
||||
Save Diagnostics
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
|
||||
@@ -1730,6 +1730,19 @@ export default function VideoEditor() {
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleSaveDiagnostic = useCallback(async () => {
|
||||
const result = await window.electronAPI.saveDiagnostic({
|
||||
error: exportError ?? "Manual diagnostic export",
|
||||
projectState: editorState,
|
||||
logs: [],
|
||||
});
|
||||
if (result.success) {
|
||||
toast.success("Diagnostic file saved");
|
||||
} else if (!result.canceled) {
|
||||
toast.error("Failed to save diagnostic file");
|
||||
}
|
||||
}, [exportError, editorState]);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-screen bg-background">
|
||||
@@ -2100,6 +2113,7 @@ export default function VideoEditor() {
|
||||
onSpeedDelete={handleSpeedDelete}
|
||||
unsavedExport={unsavedExport}
|
||||
onSaveUnsavedExport={handleSaveUnsavedExport}
|
||||
onSaveDiagnostic={handleSaveDiagnostic}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user