import { type ServerLifecycleWelcomePayload } from "@t3tools/contracts";import { scopedProjectKey, scopeProjectRef } from "@t3tools/client-runtime/environment";import { squashAtomCommandFailure } from "@t3tools/client-runtime/state/runtime";import { Outlet, redirect, createRootRoute, type ErrorComponentProps, useLocation, useNavigate,} from "@tanstack/react-router";import { CheckIcon, CopyIcon } from "lucide-react";import { useEffect, useEffectEvent, useMemo, useRef, useState } from "react"; import { APP_BASE_NAME, APP_DISPLAY_NAME, APP_STAGE_LABEL, APP_VERSION } from "../branding";import { resolveServerBackedAppDisplayName } from "../branding.logic";import { AppSidebarLayout } from "../components/AppSidebarLayout";import { CommandPalette } from "../components/CommandPalette";import { ConfirmDialogHost } from "../components/ConfirmDialogHost";import { FirstRunGate } from "../components/onboarding/FirstRunGate";import { ConnectOnboardingDialog } from "../components/cloud/ConnectOnboardingDialog";import { RelayClientInstallDialog } from "../components/cloud/RelayClientInstallDialog";import { SshPasswordPromptDialog } from "../components/desktop/SshPasswordPromptDialog";import { SnapShotCoordinator } from "../components/desktop/SnapShotCoordinator";import { DesktopAppActivationCoordinator } from "../components/desktop/DesktopAppActivationCoordinator";import { ProviderUpdateLaunchNotification } from "../components/ProviderUpdateLaunchNotification";import { SlowRpcRequestToastCoordinator } from "../components/SlowRpcRequestToastCoordinator";import { ThemeEditorHost } from "../components/settings/ThemeEditorHost";import { useCopyToClipboard } from "../hooks/useCopyToClipboard";import { useDefaultThemeAdoption } from "../hooks/useDefaultTheme";import { useEnvironmentThemeSync } from "../hooks/useEnvironmentTheme";import { Button } from "../components/ui/button";import { AnchoredToastProvider, stackedThreadToast, ToastProvider, toastManager,} from "../components/ui/toast";import { resolveAndPersistPreferredEditor } from "../editorPreferences";import { applyAppearanceFontVariables } from "~/appearanceFonts";import { applyAppearanceContrast } from "~/appearanceContrast";import { useClientSettings } from "../hooks/useSettings";import { PlanAgentSelectionHeal } from "../planAgentSelectionHeal";import { deriveLogicalProjectKeyFromSettings, derivePhysicalProjectKeyFromPath, selectProjectGroupingSettings,} from "../logicalProject";import { useUiStateStore } from "../uiStateStore";import { syncBrowserChromeTheme } from "../hooks/useTheme";import { configureClientTracing } from "../observability/clientTracing";import { resolveInitialServerAuthGateState } from "../environments/primary";import { hasHostedPairingRequest, isHostedStaticApp } from "../hostedPairing";import { shellEnvironment } from "../state/shell";import { useAtomValue } from "@effect/atom-react";import { useAtomCommand } from "../state/use-atom-command";import { useEnvironments, usePrimaryEnvironment } from "../state/environments";import { primaryServerConfigAtom, primaryServerConfigEventAtom, primaryServerWelcomeAtom,} from "../state/server";import { readProject, setActiveEnvironmentId, useActiveEnvironmentId } from "../state/entities";import { createKeybindingsUpdateToastController, type KeybindingsUpdateToastController,} from "../components/KeybindingsUpdateToast.logic"; import { getDesktopSnapShotBridge } from "../lib/desktopSnapShot";import { shouldResumeSnapShotSetupOnStartup } from "../lib/snapShotSetupResume"; export const Route = createRootRoute({ beforeLoad: async ({ location }) => { if (location.pathname === "/pair" && hasHostedPairingRequest(new URL(window.location.href))) { return { authGateState: { status: "hosted-pairing", } as const, }; } if (isHostedStaticApp(new URL(window.location.href))) { return { authGateState: { status: "hosted-static", } as const, }; } const authGateState = await resolveInitialServerAuthGateState(); if ( authGateState.status === "authenticated" && getDesktopSnapShotBridge() && shouldResumeSnapShotSetupOnStartup() && location.pathname !== "/settings/snap-shot" ) { throw redirect({ to: "/settings/snap-shot", replace: true }); } return { authGateState, }; }, component: RootRouteView, errorComponent: RootRouteErrorView, head: () => ({ meta: [{ name: "title", content: APP_DISPLAY_NAME }], }),}); function RootRouteView() { const pathname = useLocation({ select: (location) => location.pathname }); const { authGateState } = Route.useRouteContext(); const primaryEnvironmentAuthenticated = authGateState.status === "authenticated"; const returningFromWelcomeRef = useRef(pathname === "/welcome"); useEffect(() => { if (pathname === "/welcome") { returningFromWelcomeRef.current = true; } }, [pathname]); useEffect(() => { const frame = window.requestAnimationFrame(() => { syncBrowserChromeTheme(); }); return () => { window.cancelAnimationFrame(frame); }; }, [pathname]); if (pathname === "/pair" || pathname === "/connect" || pathname.startsWith("/connect/")) { return ( <> <DocumentTitleSync /> <Outlet /> </> ); } // Show onboarding over the workspace, keeping automatic thread navigation // and other startup dialogs suspended until setup finishes. if (pathname === "/welcome") { return ( <ToastProvider> <AnchoredToastProvider> <DocumentTitleSync /> <ContrastAppearanceSync /> <EnvironmentThemeSync /> <GlassAppearanceSync /> <FontAppearanceSync /> <CommandPalette> <AppSidebarLayout> <Outlet /> </AppSidebarLayout> </CommandPalette> </AnchoredToastProvider> </ToastProvider> ); } if (authGateState.status !== "authenticated" && authGateState.status !== "hosted-static") { return ( <> <DocumentTitleSync /> <Outlet /> </> ); } const appShell = ( <CommandPalette> <AppSidebarLayout> <Outlet /> </AppSidebarLayout> </CommandPalette> ); // FirstRunGate holds back everything below it — including EventRouter, // whose welcome payload navigates into a thread — until the first-run // decision is known, so a fresh install renders nothing (not the shell, // not a flash of threads) before landing on the welcome wizard. return ( <ToastProvider> <AnchoredToastProvider> <DocumentTitleSync /> <ContrastAppearanceSync /> <EnvironmentThemeSync /> <GlassAppearanceSync /> <FontAppearanceSync /> <FirstRunGate enabled={primaryEnvironmentAuthenticated} hostedStatic={authGateState.status === "hosted-static"} > {primaryEnvironmentAuthenticated ? <AuthenticatedTracingBootstrap /> : null} {primaryEnvironmentAuthenticated ? <DesktopAppActivationCoordinator /> : null} <RelayClientInstallDialog /> <ConnectOnboardingDialog /> <SshPasswordPromptDialog /> <SnapShotCoordinator /> <ConfirmDialogHost /> <SlowRpcRequestToastCoordinator /> <HostedStaticEnvironmentBootstrap /> {primaryEnvironmentAuthenticated ? ( <EventRouter skipInitialBootstrapNavigation={returningFromWelcomeRef.current} /> ) : null} {primaryEnvironmentAuthenticated ? <PlanAgentSelectionHeal /> : null} {primaryEnvironmentAuthenticated ? <ProviderUpdateLaunchNotification /> : null} {appShell} {/* Above the router: a theme draft is judged by walking the app, so the editor has to survive navigation away from settings. */} <ThemeEditorHost /> </FirstRunGate> </AnchoredToastProvider> </ToastProvider> );} /** Follows the palette the primary environment's machine publishes, if any. */function EnvironmentThemeSync() { useEnvironmentThemeSync(); // Ordered after the palette sync so a first-run client adopting the // environment's own theme finds it already in the library. useDefaultThemeAdoption(); return null;} function ContrastAppearanceSync() { const appearanceContrast = useClientSettings((settings) => settings.appearanceContrast); const diffColorScheme = useClientSettings((settings) => settings.diffColorScheme); useEffect(() => { document.documentElement.dataset.diffColorScheme = diffColorScheme; }, [diffColorScheme]); useEffect(() => { applyAppearanceContrast(document.documentElement, appearanceContrast); }, [appearanceContrast]); return null;} function GlassAppearanceSync() { const glassOpacity = useClientSettings((settings) => settings.glassOpacity); useEffect(() => { const style = document.documentElement.style; style.setProperty("--glass-opacity", `${glassOpacity}%`); if (glassOpacity === 100) { style.setProperty("--glass-blur", "0px"); } else { style.removeProperty("--glass-blur"); } }, [glassOpacity]); return null;} function FontAppearanceSync() { const fontFamilySans = useClientSettings((settings) => settings.fontFamilySans); const fontFamilyCode = useClientSettings((settings) => settings.fontFamilyCode); const fontFamilyComposer = useClientSettings((settings) => settings.fontFamilyComposer); const fontSizeInterface = useClientSettings((settings) => settings.fontSizeInterface); const fontSizePrompt = useClientSettings((settings) => settings.fontSizePrompt); const fontSizeCode = useClientSettings((settings) => settings.fontSizeCode); const fontSmoothing = useClientSettings((settings) => settings.fontSmoothing); useEffect(() => { applyAppearanceFontVariables(document.documentElement, { sans: fontFamilySans, code: fontFamilyCode, composer: fontFamilyComposer, sizeInterface: fontSizeInterface, sizePrompt: fontSizePrompt, sizeCode: fontSizeCode, smoothing: fontSmoothing, }); }, [ fontFamilyCode, fontFamilyComposer, fontFamilySans, fontSizeCode, fontSizeInterface, fontSizePrompt, fontSmoothing, ]); return null;} function DocumentTitleSync() { const primaryServerVersion = useAtomValue(primaryServerConfigAtom)?.environment.serverVersion ?? null; const title = resolveServerBackedAppDisplayName({ baseName: APP_BASE_NAME, fallbackDisplayName: APP_DISPLAY_NAME, fallbackStageLabel: APP_STAGE_LABEL, primaryServerVersion, }); useEffect(() => { document.title = title; }, [title]); return null;} function HostedStaticEnvironmentBootstrap() { const { environments } = useEnvironments(); const activeEnvironmentId = useActiveEnvironmentId(); useEffect(() => { if ( environments.some( (environment) => environment.entry.target._tag === "PrimaryConnectionTarget", ) ) { return; } if (activeEnvironmentId) { return; } const firstSavedEnvironment = environments[0]; if (!firstSavedEnvironment) { return; } setActiveEnvironmentId(firstSavedEnvironment.environmentId); }, [activeEnvironmentId, environments]); return null;} function RootRouteErrorView({ error, reset }: ErrorComponentProps) { const message = errorMessage(error); // Router pathname rather than window.location: desktop uses hash history, where the window path is always "/". const pathname = useLocation({ select: (location) => location.pathname }); const report = useMemo(() => errorReport(error, pathname), [error, pathname]); return ( <div className="relative flex min-h-screen items-center justify-center overflow-hidden bg-background px-4 py-10 text-foreground sm:px-6"> <div className="pointer-events-none absolute inset-0 opacity-80"> <div className="absolute inset-x-0 top-0 h-44 bg-[radial-gradient(44rem_16rem_at_top,color-mix(in_srgb,var(--color-red-500)_16%,transparent),transparent)]" /> <div className="absolute inset-0 bg-[linear-gradient(145deg,color-mix(in_srgb,var(--background)_90%,var(--color-black))_0%,var(--background)_55%)]" /> </div> <section className="relative w-full max-w-xl rounded-2xl border border-border/80 bg-card/90 p-6 shadow-2xl shadow-black/20 backdrop-blur-md sm:p-8"> <p className="text-[11px] font-semibold tracking-[0.18em] text-muted-foreground uppercase"> {APP_DISPLAY_NAME} </p> <h1 className="mt-3 text-2xl font-semibold tracking-tight sm:text-3xl"> Something went wrong. </h1> <p className="mt-2 text-sm leading-relaxed text-muted-foreground">{message}</p> <div className="mt-5 flex flex-wrap gap-2"> <Button size="sm" onClick={() => reset()}> Try again </Button> <Button size="sm" variant="outline" onClick={() => window.location.reload()}> Reload app </Button> <CopyErrorButton report={report} /> </div> <div className="mt-5 overflow-hidden rounded-lg border border-border/70 bg-background/55"> <p className="px-3 py-1.5 text-xs font-medium text-muted-foreground">Error report</p> <pre className="max-h-64 overflow-auto border-t border-border/70 bg-background/80 px-3 py-2 text-xs whitespace-pre-wrap text-foreground/85"> {report} </pre> </div> </section> </div> );} /** Copies the full error report and swaps to a check mark for a moment as confirmation. */function CopyErrorButton({ report }: { report: string }) { const { copyToClipboard, isCopied } = useCopyToClipboard({ target: "error-report" }); return ( <Button size="sm" variant="outline" onClick={() => copyToClipboard(report)}> {isCopied ? <CheckIcon className="text-success" /> : <CopyIcon />} {isCopied ? "Copied" : "Copy error"} </Button> );} 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."; }} const MAX_ERROR_CAUSE_DEPTH = 5; /** * Full error text for bug reports: app build, page path, time, then the stack * and any cause chain. Takes the pathname only so tokens in the query never * land on the clipboard. */function errorReport(error: unknown, pathname: string): string { const lines = [ `${APP_DISPLAY_NAME} ${APP_VERSION}`, `Path: ${pathname}`, `Time: ${new Date().toISOString()}`, "", errorDetails(error), ]; let cause = error instanceof Error ? error.cause : undefined; for (let depth = 0; cause !== undefined && depth < MAX_ERROR_CAUSE_DEPTH; depth += 1) { lines.push("", "Caused by:", errorDetails(cause)); cause = cause instanceof Error ? cause.cause : undefined; } return lines.join("\n");} function AuthenticatedTracingBootstrap() { useEffect(() => { void configureClientTracing(); }, []); return null;} function EventRouter({ skipInitialBootstrapNavigation,}: { readonly skipInitialBootstrapNavigation: boolean;}) { const navigate = useNavigate(); const pathname = useLocation({ select: (loc) => loc.pathname }); const projectGroupingSettings = useClientSettings(selectProjectGroupingSettings); const primaryEnvironment = usePrimaryEnvironment(); const openInEditor = useAtomCommand(shellEnvironment.openInEditor, { reportFailure: false, }); const serverConfig = useAtomValue(primaryServerConfigAtom); const serverConfigEvent = useAtomValue(primaryServerConfigEventAtom); const serverWelcome = useAtomValue(primaryServerWelcomeAtom); const readPathname = useEffectEvent(() => pathname); const handledBootstrapThreadIdRef = useRef<string | null>(null); const skipInitialBootstrapNavigationRef = useRef(skipInitialBootstrapNavigation); const handledConfigEventRef = useRef(serverConfigEvent); const [keybindingsToastController] = useState<KeybindingsUpdateToastController>(() => createKeybindingsUpdateToastController({}), ); const handleWelcome = useEffectEvent((payload: ServerLifecycleWelcomePayload | null) => { if (!payload) return; setActiveEnvironmentId(payload.environment.environmentId); void (async () => { if (!payload.bootstrapProjectId || !payload.bootstrapThreadId) { return; } const bootstrapProject = readProject( scopeProjectRef(payload.environment.environmentId, payload.bootstrapProjectId), ); const bootstrapProjectKey = (bootstrapProject ? deriveLogicalProjectKeyFromSettings(bootstrapProject, projectGroupingSettings) : null) ?? (serverConfig?.cwd ? derivePhysicalProjectKeyFromPath(payload.environment.environmentId, serverConfig.cwd) : null) ?? scopedProjectKey( scopeProjectRef(payload.environment.environmentId, payload.bootstrapProjectId), ); useUiStateStore.getState().setProjectExpanded(bootstrapProjectKey, true); if (readPathname() !== "/") { return; } if (skipInitialBootstrapNavigationRef.current) { skipInitialBootstrapNavigationRef.current = false; handledBootstrapThreadIdRef.current = payload.bootstrapThreadId;Lines 1–500 of 598. Load more
import { type ServerLifecycleWelcomePayload } from "@t3tools/contracts"; import { scopedProjectKey, scopeProjectRef } from "@t3tools/client-runtime/environment"; import { squashAtomCommandFailure } from "@t3tools/client-runtime/state/runtime"; import { Outlet, redirect, createRootRoute, type ErrorComponentProps, useLocation, useNavigate, } from "@tanstack/react-router"; import { CheckIcon, CopyIcon } from "lucide-react"; import { useEffect, useEffectEvent, useMemo, useRef, useState } from "react"; import { APP_BASE_NAME, APP_DISPLAY_NAME, APP_STAGE_LABEL, APP_VERSION } from "../branding"; import { resolveServerBackedAppDisplayName } from "../branding.logic"; import { AppSidebarLayout } from "../components/AppSidebarLayout"; import { CommandPalette } from "../components/CommandPalette"; import { ConfirmDialogHost } from "../components/ConfirmDialogHost"; import { FirstRunGate } from "../components/onboarding/FirstRunGate"; import { ConnectOnboardingDialog } from "../components/cloud/ConnectOnboardingDialog"; import { RelayClientInstallDialog } from "../components/cloud/RelayClientInstallDialog"; import { SshPasswordPromptDialog } from "../components/desktop/SshPasswordPromptDialog"; import { SnapShotCoordinator } from "../components/desktop/SnapShotCoordinator"; import { DesktopAppActivationCoordinator } from "../components/desktop/DesktopAppActivationCoordinator"; import { ProviderUpdateLaunchNotification } from "../components/ProviderUpdateLaunchNotification"; import { SlowRpcRequestToastCoordinator } from "../components/SlowRpcRequestToastCoordinator"; import { ThemeEditorHost } from "../components/settings/ThemeEditorHost"; import { useCopyToClipboard } from "../hooks/useCopyToClipboard"; import { useDefaultThemeAdoption } from "../hooks/useDefaultTheme"; import { useEnvironmentThemeSync } from "../hooks/useEnvironmentTheme"; import { Button } from "../components/ui/button"; import { AnchoredToastProvider, stackedThreadToast, ToastProvider, toastManager, } from "../components/ui/toast"; import { resolveAndPersistPreferredEditor } from "../editorPreferences"; import { applyAppearanceFontVariables } from "~/appearanceFonts"; import { applyAppearanceContrast } from "~/appearanceContrast"; import { useClientSettings } from "../hooks/useSettings"; import { PlanAgentSelectionHeal } from "../planAgentSelectionHeal"; import { deriveLogicalProjectKeyFromSettings, derivePhysicalProjectKeyFromPath, selectProjectGroupingSettings, } from "../logicalProject"; import { useUiStateStore } from "../uiStateStore"; import { syncBrowserChromeTheme } from "../hooks/useTheme"; import { configureClientTracing } from "../observability/clientTracing"; import { resolveInitialServerAuthGateState } from "../environments/primary"; import { hasHostedPairingRequest, isHostedStaticApp } from "../hostedPairing"; import { shellEnvironment } from "../state/shell"; import { useAtomValue } from "@effect/atom-react"; import { useAtomCommand } from "../state/use-atom-command"; import { useEnvironments, usePrimaryEnvironment } from "../state/environments"; import { primaryServerConfigAtom, primaryServerConfigEventAtom, primaryServerWelcomeAtom, } from "../state/server"; import { readProject, setActiveEnvironmentId, useActiveEnvironmentId } from "../state/entities"; import { createKeybindingsUpdateToastController, type KeybindingsUpdateToastController, } from "../components/KeybindingsUpdateToast.logic"; import { getDesktopSnapShotBridge } from "../lib/desktopSnapShot"; import { shouldResumeSnapShotSetupOnStartup } from "../lib/snapShotSetupResume"; export const Route = createRootRoute({ beforeLoad: async ({ location }) => { if (location.pathname === "/pair" && hasHostedPairingRequest(new URL(window.location.href))) { return { authGateState: { status: "hosted-pairing", } as const, }; } if (isHostedStaticApp(new URL(window.location.href))) { return { authGateState: { status: "hosted-static", } as const, }; } const authGateState = await resolveInitialServerAuthGateState(); if ( authGateState.status === "authenticated" && getDesktopSnapShotBridge() && shouldResumeSnapShotSetupOnStartup() && location.pathname !== "/settings/snap-shot" ) { throw redirect({ to: "/settings/snap-shot", replace: true }); } return { authGateState, }; }, component: RootRouteView, errorComponent: RootRouteErrorView, head: () => ({ meta: [{ name: "title", content: APP_DISPLAY_NAME }], }), }); function RootRouteView() { const pathname = useLocation({ select: (location) => location.pathname }); const { authGateState } = Route.useRouteContext(); const primaryEnvironmentAuthenticated = authGateState.status === "authenticated"; const returningFromWelcomeRef = useRef(pathname === "/welcome"); useEffect(() => { if (pathname === "/welcome") { returningFromWelcomeRef.current = true; } }, [pathname]); useEffect(() => { const frame = window.requestAnimationFrame(() => { syncBrowserChromeTheme(); }); return () => { window.cancelAnimationFrame(frame); }; }, [pathname]); if (pathname === "/pair" || pathname === "/connect" || pathname.startsWith("/connect/")) { return ( <> <DocumentTitleSync /> <Outlet /> </> ); } // Show onboarding over the workspace, keeping automatic thread navigation // and other startup dialogs suspended until setup finishes. if (pathname === "/welcome") { return ( <ToastProvider> <AnchoredToastProvider> <DocumentTitleSync /> <ContrastAppearanceSync /> <EnvironmentThemeSync /> <GlassAppearanceSync /> <FontAppearanceSync /> <CommandPalette> <AppSidebarLayout> <Outlet /> </AppSidebarLayout> </CommandPalette> </AnchoredToastProvider> </ToastProvider> ); } if (authGateState.status !== "authenticated" && authGateState.status !== "hosted-static") { return ( <> <DocumentTitleSync /> <Outlet /> </> ); } const appShell = ( <CommandPalette> <AppSidebarLayout> <Outlet /> </AppSidebarLayout> </CommandPalette> ); // FirstRunGate holds back everything below it — including EventRouter, // whose welcome payload navigates into a thread — until the first-run // decision is known, so a fresh install renders nothing (not the shell, // not a flash of threads) before landing on the welcome wizard. return ( <ToastProvider> <AnchoredToastProvider> <DocumentTitleSync /> <ContrastAppearanceSync /> <EnvironmentThemeSync /> <GlassAppearanceSync /> <FontAppearanceSync /> <FirstRunGate enabled={primaryEnvironmentAuthenticated} hostedStatic={authGateState.status === "hosted-static"} > {primaryEnvironmentAuthenticated ? <AuthenticatedTracingBootstrap /> : null} {primaryEnvironmentAuthenticated ? <DesktopAppActivationCoordinator /> : null} <RelayClientInstallDialog /> <ConnectOnboardingDialog /> <SshPasswordPromptDialog /> <SnapShotCoordinator /> <ConfirmDialogHost /> <SlowRpcRequestToastCoordinator /> <HostedStaticEnvironmentBootstrap /> {primaryEnvironmentAuthenticated ? ( <EventRouter skipInitialBootstrapNavigation={returningFromWelcomeRef.current} /> ) : null} {primaryEnvironmentAuthenticated ? <PlanAgentSelectionHeal /> : null} {primaryEnvironmentAuthenticated ? <ProviderUpdateLaunchNotification /> : null} {appShell} {/* Above the router: a theme draft is judged by walking the app, so the editor has to survive navigation away from settings. */} <ThemeEditorHost /> </FirstRunGate> </AnchoredToastProvider> </ToastProvider> ); } /** Follows the palette the primary environment's machine publishes, if any. */ function EnvironmentThemeSync() { useEnvironmentThemeSync(); // Ordered after the palette sync so a first-run client adopting the // environment's own theme finds it already in the library. useDefaultThemeAdoption(); return null; } function ContrastAppearanceSync() { const appearanceContrast = useClientSettings((settings) => settings.appearanceContrast); const diffColorScheme = useClientSettings((settings) => settings.diffColorScheme); useEffect(() => { document.documentElement.dataset.diffColorScheme = diffColorScheme; }, [diffColorScheme]); useEffect(() => { applyAppearanceContrast(document.documentElement, appearanceContrast); }, [appearanceContrast]); return null; } function GlassAppearanceSync() { const glassOpacity = useClientSettings((settings) => settings.glassOpacity); useEffect(() => { const style = document.documentElement.style; style.setProperty("--glass-opacity", `${glassOpacity}%`); if (glassOpacity === 100) { style.setProperty("--glass-blur", "0px"); } else { style.removeProperty("--glass-blur"); } }, [glassOpacity]); return null; } function FontAppearanceSync() { const fontFamilySans = useClientSettings((settings) => settings.fontFamilySans); const fontFamilyCode = useClientSettings((settings) => settings.fontFamilyCode); const fontFamilyComposer = useClientSettings((settings) => settings.fontFamilyComposer); const fontSizeInterface = useClientSettings((settings) => settings.fontSizeInterface); const fontSizePrompt = useClientSettings((settings) => settings.fontSizePrompt); const fontSizeCode = useClientSettings((settings) => settings.fontSizeCode); const fontSmoothing = useClientSettings((settings) => settings.fontSmoothing); useEffect(() => { applyAppearanceFontVariables(document.documentElement, { sans: fontFamilySans, code: fontFamilyCode, composer: fontFamilyComposer, sizeInterface: fontSizeInterface, sizePrompt: fontSizePrompt, sizeCode: fontSizeCode, smoothing: fontSmoothing, }); }, [ fontFamilyCode, fontFamilyComposer, fontFamilySans, fontSizeCode, fontSizeInterface, fontSizePrompt, fontSmoothing, ]); return null; } function DocumentTitleSync() { const primaryServerVersion = useAtomValue(primaryServerConfigAtom)?.environment.serverVersion ?? null; const title = resolveServerBackedAppDisplayName({ baseName: APP_BASE_NAME, fallbackDisplayName: APP_DISPLAY_NAME, fallbackStageLabel: APP_STAGE_LABEL, primaryServerVersion, }); useEffect(() => { document.title = title; }, [title]); return null; } function HostedStaticEnvironmentBootstrap() { const { environments } = useEnvironments(); const activeEnvironmentId = useActiveEnvironmentId(); useEffect(() => { if ( environments.some( (environment) => environment.entry.target._tag === "PrimaryConnectionTarget", ) ) { return; } if (activeEnvironmentId) { return; } const firstSavedEnvironment = environments[0]; if (!firstSavedEnvironment) { return; } setActiveEnvironmentId(firstSavedEnvironment.environmentId); }, [activeEnvironmentId, environments]); return null; } function RootRouteErrorView({ error, reset }: ErrorComponentProps) { const message = errorMessage(error); // Router pathname rather than window.location: desktop uses hash history, where the window path is always "/". const pathname = useLocation({ select: (location) => location.pathname }); const report = useMemo(() => errorReport(error, pathname), [error, pathname]); return ( <div className="relative flex min-h-screen items-center justify-center overflow-hidden bg-background px-4 py-10 text-foreground sm:px-6"> <div className="pointer-events-none absolute inset-0 opacity-80"> <div className="absolute inset-x-0 top-0 h-44 bg-[radial-gradient(44rem_16rem_at_top,color-mix(in_srgb,var(--color-red-500)_16%,transparent),transparent)]" /> <div className="absolute inset-0 bg-[linear-gradient(145deg,color-mix(in_srgb,var(--background)_90%,var(--color-black))_0%,var(--background)_55%)]" /> </div> <section className="relative w-full max-w-xl rounded-2xl border border-border/80 bg-card/90 p-6 shadow-2xl shadow-black/20 backdrop-blur-md sm:p-8"> <p className="text-[11px] font-semibold tracking-[0.18em] text-muted-foreground uppercase"> {APP_DISPLAY_NAME} </p> <h1 className="mt-3 text-2xl font-semibold tracking-tight sm:text-3xl"> Something went wrong. </h1> <p className="mt-2 text-sm leading-relaxed text-muted-foreground">{message}</p> <div className="mt-5 flex flex-wrap gap-2"> <Button size="sm" onClick={() => reset()}> Try again </Button> <Button size="sm" variant="outline" onClick={() => window.location.reload()}> Reload app </Button> <CopyErrorButton report={report} /> </div> <div className="mt-5 overflow-hidden rounded-lg border border-border/70 bg-background/55"> <p className="px-3 py-1.5 text-xs font-medium text-muted-foreground">Error report</p> <pre className="max-h-64 overflow-auto border-t border-border/70 bg-background/80 px-3 py-2 text-xs whitespace-pre-wrap text-foreground/85"> {report} </pre> </div> </section> </div> ); } /** Copies the full error report and swaps to a check mark for a moment as confirmation. */ function CopyErrorButton({ report }: { report: string }) { const { copyToClipboard, isCopied } = useCopyToClipboard({ target: "error-report" }); return ( <Button size="sm" variant="outline" onClick={() => copyToClipboard(report)}> {isCopied ? <CheckIcon className="text-success" /> : <CopyIcon />} {isCopied ? "Copied" : "Copy error"} </Button> ); } 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."; } } const MAX_ERROR_CAUSE_DEPTH = 5; /** * Full error text for bug reports: app build, page path, time, then the stack * and any cause chain. Takes the pathname only so tokens in the query never * land on the clipboard. */ function errorReport(error: unknown, pathname: string): string { const lines = [ `${APP_DISPLAY_NAME} ${APP_VERSION}`, `Path: ${pathname}`, `Time: ${new Date().toISOString()}`, "", errorDetails(error), ]; let cause = error instanceof Error ? error.cause : undefined; for (let depth = 0; cause !== undefined && depth < MAX_ERROR_CAUSE_DEPTH; depth += 1) { lines.push("", "Caused by:", errorDetails(cause)); cause = cause instanceof Error ? cause.cause : undefined; } return lines.join("\n"); } function AuthenticatedTracingBootstrap() { useEffect(() => { void configureClientTracing(); }, []); return null; } function EventRouter({ skipInitialBootstrapNavigation, }: { readonly skipInitialBootstrapNavigation: boolean; }) { const navigate = useNavigate(); const pathname = useLocation({ select: (loc) => loc.pathname }); const projectGroupingSettings = useClientSettings(selectProjectGroupingSettings); const primaryEnvironment = usePrimaryEnvironment(); const openInEditor = useAtomCommand(shellEnvironment.openInEditor, { reportFailure: false, }); const serverConfig = useAtomValue(primaryServerConfigAtom); const serverConfigEvent = useAtomValue(primaryServerConfigEventAtom); const serverWelcome = useAtomValue(primaryServerWelcomeAtom); const readPathname = useEffectEvent(() => pathname); const handledBootstrapThreadIdRef = useRef<string | null>(null); const skipInitialBootstrapNavigationRef = useRef(skipInitialBootstrapNavigation); const handledConfigEventRef = useRef(serverConfigEvent); const [keybindingsToastController] = useState<KeybindingsUpdateToastController>(() => createKeybindingsUpdateToastController({}), ); const handleWelcome = useEffectEvent((payload: ServerLifecycleWelcomePayload | null) => { if (!payload) return; setActiveEnvironmentId(payload.environment.environmentId); void (async () => { if (!payload.bootstrapProjectId || !payload.bootstrapThreadId) { return; } const bootstrapProject = readProject( scopeProjectRef(payload.environment.environmentId, payload.bootstrapProjectId), ); const bootstrapProjectKey = (bootstrapProject ? deriveLogicalProjectKeyFromSettings(bootstrapProject, projectGroupingSettings) : null) ?? (serverConfig?.cwd ? derivePhysicalProjectKeyFromPath(payload.environment.environmentId, serverConfig.cwd) : null) ?? scopedProjectKey( scopeProjectRef(payload.environment.environmentId, payload.bootstrapProjectId), ); useUiStateStore.getState().setProjectExpanded(bootstrapProjectKey, true); if (readPathname() !== "/") { return; } if (skipInitialBootstrapNavigationRef.current) { skipInitialBootstrapNavigationRef.current = false; handledBootstrapThreadIdRef.current = payload.bootstrapThreadId;