diff --git a/frontend/js/app.js b/frontend/js/app.js index 40a1e94..8c91271 100644 --- a/frontend/js/app.js +++ b/frontend/js/app.js @@ -121,6 +121,21 @@ const EMPTY_DIFF_ICON = lucideIcon("FileDiff", { size: 44, strokeWidth: 1.6 }); const EMPTY_FILE_ICON = lucideIcon("File", { size: 44, strokeWidth: 1.6 }); const EMPTY_REPO_ICON = lucideIcon("FolderGit2", { size: 44, strokeWidth: 1.6 }); +function errorMessage(error, fallback = "Unknown error.") { + if (typeof error === "string" && error.trim()) return error; + if (error instanceof Error && error.message) return error.message; + if (error && typeof error === "object") { + if (typeof error.message === "string" && error.message.trim()) return error.message; + try { + const serialized = JSON.stringify(error); + if (serialized && serialized !== "{}") return serialized; + } catch { + // Fall through to the generic fallback. + } + } + return fallback; +} + function currentPlatform() { const platform = navigator.userAgentData?.platform || navigator.platform || ""; const userAgent = navigator.userAgent || ""; @@ -1670,7 +1685,7 @@ function bindServerFormEvents(existingServer = null) { render(); openServerForm(existingServer?.id || null); } catch (error) { - serverTestResult = `Connection test failed: ${error.message}`; + serverTestResult = `Connection test failed: ${errorMessage(error)}`; render(); openServerForm(existingServer?.id || null); } @@ -1733,7 +1748,7 @@ async function loadRepositories() { currentUserLogin = user.login || ""; repositories = repos; } catch (error) { - settingsNotice = `Using mock repositories. API fetch failed: ${error.message}`; + settingsNotice = `Using mock repositories. API fetch failed: ${errorMessage(error)}`; currentUserLogin = "alice"; repositories = [...mockRepos]; } @@ -1811,7 +1826,7 @@ async function refreshBranches() { try { state.branches.items = await listLocalRepoBranches(state.selectedRepoPath, selectedGitPath()); } catch (error) { - state.branches.error = `Unable to load branches: ${error.message}`; + state.branches.error = `Unable to load branches: ${errorMessage(error)}`; } finally { state.branches.loading = false; render(); @@ -1851,7 +1866,7 @@ async function refreshWorkingTree() { await loadSelectedDiff(tree.selectedPath, false); } } catch (error) { - tree.error = `Unable to load changes: ${error.message}`; + tree.error = `Unable to load changes: ${errorMessage(error)}`; } finally { tree.loading = false; render(); @@ -1905,7 +1920,7 @@ async function refreshSyncState({ silent = false } = {}) { state.workingTree.behind = sync.behind; } catch (error) { if (getState().selectedRepoPath !== repoPath) return; - sync.error = error.message; + sync.error = errorMessage(error); } finally { if (getState().selectedRepoPath === repoPath) { sync.loading = false; @@ -1927,7 +1942,7 @@ async function loadSelectedDiff(path, shouldRender = true) { tree.selectedDiff = await getFileDiff(state.selectedRepoPath, file.path, file.status, selectedGitPath()); } catch (error) { tree.selectedDiff = null; - tree.diffError = `Unable to load diff: ${error.message}`; + tree.diffError = `Unable to load diff: ${errorMessage(error)}`; } finally { tree.diffLoading = false; if (shouldRender) render(); @@ -1956,7 +1971,7 @@ async function refreshHistory() { await loadCommitDetail(history.selectedHash, false); } } catch (error) { - history.error = `Unable to load history: ${error.message}`; + history.error = `Unable to load history: ${errorMessage(error)}`; } finally { history.loading = false; render(); @@ -1972,7 +1987,7 @@ async function loadCommitDetail(hash, shouldRender = true) { try { state.history.selectedCommit = await getCommitDetail(state.selectedRepoPath, hash, selectedGitPath()); } catch (error) { - state.history.error = `Unable to load commit: ${error.message}`; + state.history.error = `Unable to load commit: ${errorMessage(error)}`; } finally { if (shouldRender) render(); } @@ -2020,7 +2035,7 @@ async function scanForLocalRepos() { } } catch (error) { state.localRepoScanResults = []; - state.localRepoScanError = `Repo scan failed: ${error.message}`; + state.localRepoScanError = `Repo scan failed: ${errorMessage(error)}`; } finally { state.localRepoScanLoading = false; render(); @@ -2059,7 +2074,7 @@ async function loadViewerPath(path = "") { render(); await autoLoadReadme(); } catch (error) { - viewer.error = `Unable to load repository contents: ${error.message}`; + viewer.error = `Unable to load repository contents: ${errorMessage(error)}`; viewer.loading = false; render(); } @@ -2108,7 +2123,7 @@ async function openViewerFile(path) { }; } } catch (error) { - viewer.error = `Unable to preview file: ${error.message}`; + viewer.error = `Unable to preview file: ${errorMessage(error)}`; } finally { viewer.loading = false; render(); @@ -2146,7 +2161,7 @@ async function openRemoteViewer(repo) { await loadViewerPath(""); } catch (error) { viewer.loading = false; - viewer.error = `Unable to open repository viewer: ${error.message}`; + viewer.error = `Unable to open repository viewer: ${errorMessage(error)}`; render(); } } @@ -2186,7 +2201,7 @@ async function openLocalViewer() { await loadViewerPath(""); } catch (error) { viewer.loading = false; - viewer.error = `Unable to open local viewer: ${error.message}`; + viewer.error = `Unable to open local viewer: ${errorMessage(error)}`; render(); } } @@ -2201,7 +2216,7 @@ async function refreshInstalledIdes() { state.installedIdes = await scanInstalledIdes(); } catch (error) { state.installedIdes = []; - state.installedIdeScanError = `Unable to scan installed IDEs: ${error.message}`; + state.installedIdeScanError = `Unable to scan installed IDEs: ${errorMessage(error)}`; } finally { state.installedIdeScanLoading = false; render(); @@ -2220,7 +2235,7 @@ async function openSelectedRepoInFileExplorer() { await openInFileExplorer(state.selectedRepoPath); gitOutput = `Opened in File Explorer:\n${state.selectedRepoPath}`; } catch (error) { - gitOutput = `Open in File Explorer failed: ${error.message}`; + gitOutput = `Open in File Explorer failed: ${errorMessage(error)}`; } finally { utilityMenuOpen = false; render(); @@ -2240,7 +2255,7 @@ async function openSelectedRepoInCodeEditor() { await openInExternalEditor(state.selectedRepoPath, editorCommand); gitOutput = `Opened in Code Editor with "${editorCommand}":\n${state.selectedRepoPath}`; } catch (error) { - gitOutput = `Open in Code Editor failed: ${error.message}`; + gitOutput = `Open in Code Editor failed: ${errorMessage(error)}`; } finally { utilityMenuOpen = false; render(); @@ -2262,8 +2277,9 @@ async function runRepoCommand(actionName, runner, operation = "") { const result = await runner(); gitOutput = `${actionName}: ${result.command}\n\n${result.stdout || "(no stdout)"}\n${result.stderr ? `\n${result.stderr}` : ""}`; } catch (error) { - gitOutput = `${actionName} failed: ${error.message}`; - state.sync.error = error.message; + const message = errorMessage(error); + gitOutput = `${actionName} failed: ${message}`; + state.sync.error = message; } finally { state.sync.operation = ""; } @@ -2550,7 +2566,7 @@ function bindDashboardEvents() { state.commitDescription = ""; await refreshRepoData(); } catch (error) { - gitOutput = `Commit failed: ${error.message}`; + gitOutput = `Commit failed: ${errorMessage(error)}`; render(); } }); @@ -2576,7 +2592,7 @@ function bindDashboardEvents() { state.branches.menuOpen = false; await refreshRepoData(); } catch (error) { - gitOutput = `Branch switch failed: ${error.message}`; + gitOutput = `Branch switch failed: ${errorMessage(error)}`; state.branches.menuOpen = false; render(); } @@ -2616,7 +2632,7 @@ function bindDashboardEvents() { Object.assign(dialog, { mode: "", target: "", value: "", error: "" }); await refreshRepoData(); } catch (error) { - dialog.error = error.message; + dialog.error = errorMessage(error); render(); } }); @@ -2649,7 +2665,7 @@ function bindDashboardEvents() { try { selectedDirectory = await browseDirectory(state.settings.defaultCloneDirectory || ""); } catch (error) { - gitOutput = `Could not open folder picker: ${error.message}`; + gitOutput = `Could not open folder picker: ${errorMessage(error)}`; render(); return; } @@ -2682,7 +2698,7 @@ function bindDashboardEvents() { activeView = "changes"; await refreshRepoData(); } catch (error) { - gitOutput = `Clone failed: ${error.message}`; + gitOutput = `Clone failed: ${errorMessage(error)}`; } render(); }); @@ -2698,7 +2714,7 @@ function bindDashboardEvents() { try { selectedApplication = await browseApplication(currentValue); } catch (error) { - settingsNotice = `Could not open application picker: ${error.message}`; + settingsNotice = `Could not open application picker: ${errorMessage(error)}`; render(); return; }