import {
Outlet,
createRootRouteWithContext,
type ErrorComponentProps,
useParams,
} from "@tanstack/react-router";
import { useEffect } from "react";
import { QueryClient } 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";
import { type AppState, useStore } from "../store";
import { onServerStateUpdate, onServerWelcome } from "../wsNativeApi";
export const Route = createRootRouteWithContext<{
queryClient: QueryClient;
}>()({
component: RootRouteView,
errorComponent: RootRouteErrorView,
});
function RootRouteView() {
const api = useNativeApi();
if (!api) {
return (
Connecting to {APP_DISPLAY_NAME} server...
);
}
return (
);
}
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();
const activeThreadId = useParams({
strict: false,
select: (params) => params.threadId,
});
useEffect(() => {
if (!api) return;
let mounted = true;
void api.state.getSnapshot().then((snapshot) => {
if (!mounted) return;
dispatch({ type: "SET_SERVER_STATE", state: snapshot as AppState });
});
return () => {
mounted = false;
};
}, [api, dispatch]);
useEffect(() => {
return onServerStateUpdate((snapshot) => {
dispatch({ type: "SET_SERVER_STATE", state: snapshot as AppState });
});
}, [dispatch]);
useEffect(() => {
if (!activeThreadId) return;
const visitedAt = new Date().toISOString();
if (!api) return;
void api.state.markThreadVisited({ threadId: activeThreadId, visitedAt });
}, [activeThreadId, api]);
return null;
}
function AutoProjectBootstrap() {
const { dispatch } = useStore();
useEffect(() => {
if (isElectron) return;
return onServerWelcome(() => {
dispatch({ type: "SET_THREADS_HYDRATED", hydrated: true });
});
}, [dispatch]);
return null;
}
function DesktopProjectBootstrap() {
const api = useNativeApi();
const { dispatch } = useStore();
useEffect(() => {
if (!isElectron || !api) return;
dispatch({ type: "SET_THREADS_HYDRATED", hydrated: true });
}, [api, dispatch]);
return null;
}