From d8412eca9f68256c42d0f86f80f884a12e5ec8f7 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Thu, 19 Feb 2026 18:42:39 -0800 Subject: [PATCH] error component --- apps/web/src/diffRouteSearch.test.ts | 23 ++++++- apps/web/src/diffRouteSearch.ts | 6 +- apps/web/src/routes/__root.tsx | 82 ++++++++++++++++++++++++- apps/web/src/routes/_chat.$threadId.tsx | 14 ++--- 4 files changed, 113 insertions(+), 12 deletions(-) diff --git a/apps/web/src/diffRouteSearch.test.ts b/apps/web/src/diffRouteSearch.test.ts index becf9e335f0ff4111e8d5725f9cb0db56adf9f29..ef00874bd266fbef26ff4b4ee595cb3dc0d15e7c 100644 --- a/apps/web/src/diffRouteSearch.test.ts +++ b/apps/web/src/diffRouteSearch.test.ts @@ -17,6 +17,28 @@ describe("parseDiffRouteSearch", () => { }); }); + it("treats numeric and boolean diff toggles as open", () => { + expect( + parseDiffRouteSearch({ + diff: 1, + diffTurnId: "turn-1", + }), + ).toEqual({ + diff: "1", + diffTurnId: "turn-1", + }); + + expect( + parseDiffRouteSearch({ + diff: true, + diffTurnId: "turn-1", + }), + ).toEqual({ + diff: "1", + diffTurnId: "turn-1", + }); + }); + it("drops turn and file values when diff is closed", () => { const parsed = parseDiffRouteSearch({ diff: "0", @@ -50,4 +72,3 @@ describe("parseDiffRouteSearch", () => { }); }); }); - diff --git a/apps/web/src/diffRouteSearch.ts b/apps/web/src/diffRouteSearch.ts index fd3d6cae7087bb84286b5dfa75009a330c4541cd..7a2544d6639538f120c9ccf9a5fc54fdc6cec50f 100644 --- a/apps/web/src/diffRouteSearch.ts +++ b/apps/web/src/diffRouteSearch.ts @@ -4,6 +4,10 @@ export interface DiffRouteSearch { diffFilePath?: string; } +function isDiffOpenValue(value: unknown): boolean { + return value === "1" || value === 1 || value === true; +} + function normalizeSearchString(value: unknown): string | undefined { if (typeof value !== "string") { return undefined; @@ -20,7 +24,7 @@ export function stripDiffSearchParams>( } export function parseDiffRouteSearch(search: Record): DiffRouteSearch { - const diff = search.diff === "1" ? "1" : undefined; + const diff = isDiffOpenValue(search.diff) ? "1" : undefined; const diffTurnId = diff ? normalizeSearchString(search.diffTurnId) : undefined; const diffFilePath = diff && diffTurnId ? normalizeSearchString(search.diffFilePath) : undefined; diff --git a/apps/web/src/routes/__root.tsx b/apps/web/src/routes/__root.tsx index 78b887c6fec38cb6983a6fefa94925f80d1fc81b..1e7bd0d84d949ba15c212fc0fff6424fc0192669 100644 --- a/apps/web/src/routes/__root.tsx +++ b/apps/web/src/routes/__root.tsx @@ -1,7 +1,14 @@ -import { Outlet, createRootRouteWithContext, useParams } from "@tanstack/react-router"; +import { + Outlet, + createRootRouteWithContext, + type ErrorComponentProps, + useParams, +} from "@tanstack/react-router"; import { useEffect, useRef } from "react"; import { QueryClient, useQueryClient } from "@tanstack/react-query"; +import { APP_DISPLAY_NAME } from "../branding"; +import { Button } from "../components/ui/button"; import { AnchoredToastProvider, ToastProvider } from "../components/ui/toast"; import { isElectron } from "../env"; import { useNativeApi } from "../hooks/useNativeApi"; @@ -9,12 +16,12 @@ import { invalidateGitQueries } from "../lib/gitReactQuery"; import { DEFAULT_MODEL } from "../model-logic"; import { useStore } from "../store"; import { onServerWelcome } from "../wsNativeApi"; -import { APP_DISPLAY_NAME } from "../branding"; export const Route = createRootRouteWithContext<{ queryClient: QueryClient; }>()({ component: RootRouteView, + errorComponent: RootRouteErrorView, }); function RootRouteView() { @@ -44,6 +51,77 @@ function RootRouteView() { ); } +function RootRouteErrorView({ error, reset }: ErrorComponentProps) { + const message = errorMessage(error); + const details = errorDetails(error); + + return ( +
+
+
+
+
+ +
+

+ {APP_DISPLAY_NAME} +

+

+ Something went wrong. +

+

{message}

+ +
+ + +
+ +
+ + Show error details + Hide error details + +
+            {details}
+          
+
+
+
+ ); +} + +function errorMessage(error: unknown): string { + if (error instanceof Error && error.message.trim().length > 0) { + return error.message; + } + + if (typeof error === "string" && error.trim().length > 0) { + return error; + } + + return "An unexpected router error occurred."; +} + +function errorDetails(error: unknown): string { + if (error instanceof Error) { + return error.stack ?? error.message; + } + + if (typeof error === "string") { + return error; + } + + try { + return JSON.stringify(error, null, 2); + } catch { + return "No additional error details are available."; + } +} + function EventRouter() { const api = useNativeApi(); const { dispatch } = useStore(); diff --git a/apps/web/src/routes/_chat.$threadId.tsx b/apps/web/src/routes/_chat.$threadId.tsx index 8bb8aa389cfdcc7483ce6286eb1c28a335a4cfd0..5efee58f5bc7c944f40219ceb19f3e5cbca0f771 100644 --- a/apps/web/src/routes/_chat.$threadId.tsx +++ b/apps/web/src/routes/_chat.$threadId.tsx @@ -1,5 +1,5 @@ import { createFileRoute, useNavigate } from "@tanstack/react-router"; -import { Activity, Suspense, lazy, type ReactNode, useCallback, useEffect } from "react"; +import { Suspense, lazy, type ReactNode, useCallback, useEffect } from "react"; import ChatView from "../components/ChatView"; import { parseDiffRouteSearch, stripDiffSearchParams } from "../diffRouteSearch"; @@ -97,13 +97,11 @@ function ChatThreadRouteView() { return ( <> - - - - - - - + + + + + ); } -- 2.54.0