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 ( <> ); } // Show onboarding over the workspace, keeping automatic thread navigation // and other startup dialogs suspended until setup finishes. if (pathname === "/welcome") { return ( ); } if (authGateState.status !== "authenticated" && authGateState.status !== "hosted-static") { return ( <> ); } const appShell = ( ); // 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 ( {primaryEnvironmentAuthenticated ? : null} {primaryEnvironmentAuthenticated ? : null} {primaryEnvironmentAuthenticated ? ( ) : null} {primaryEnvironmentAuthenticated ? : null} {primaryEnvironmentAuthenticated ? : null} {appShell} {/* Above the router: a theme draft is judged by walking the app, so the editor has to survive navigation away from settings. */} ); } /** 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 (

{APP_DISPLAY_NAME}

Something went wrong.

{message}

Error report

            {report}
          
); } /** 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 ( ); } 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(null); const skipInitialBootstrapNavigationRef = useRef(skipInitialBootstrapNavigation); const handledConfigEventRef = useRef(serverConfigEvent); const [keybindingsToastController] = useState(() => 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; return; } if (handledBootstrapThreadIdRef.current === payload.bootstrapThreadId) { return; } await navigate({ to: "/$environmentId/$threadId", params: { environmentId: payload.environment.environmentId, threadId: payload.bootstrapThreadId, }, replace: true, }); handledBootstrapThreadIdRef.current = payload.bootstrapThreadId; })().catch(() => undefined); }); const handleServerConfigUpdated = useEffectEvent(() => { const decision = keybindingsToastController.handle(serverConfigEvent); if (!decision) { return; } if (decision._tag === "Success") { toastManager.add({ type: "success", title: "Keybindings updated", description: "Keybindings configuration reloaded successfully.", }); return; } toastManager.add( stackedThreadToast({ type: "warning", title: "Invalid keybindings configuration", description: decision.message, actionVariant: "outline", actionProps: { children: "Open keybindings.json", onClick: () => { if (!serverConfig || !primaryEnvironment) { return; } const editor = resolveAndPersistPreferredEditor(serverConfig.availableEditors); if (!editor) { return; } void (async () => { const result = await openInEditor({ environmentId: primaryEnvironment.environmentId, input: { cwd: serverConfig.keybindingsConfigPath, editor, }, }); if (result._tag === "Success") { return; } const error = squashAtomCommandFailure(result); toastManager.add( stackedThreadToast({ type: "error", title: "Unable to open keybindings file", description: error instanceof Error ? error.message : "Unknown error opening file.", }), ); })(); }, }, }), ); }); useEffect(() => { if (!serverConfig) { return; } setActiveEnvironmentId(serverConfig.environment.environmentId); }, [serverConfig]); useEffect(() => { handleWelcome(serverWelcome); }, [serverWelcome]); useEffect(() => { if (serverConfigEvent === null || handledConfigEventRef.current === serverConfigEvent) { return; } handledConfigEventRef.current = serverConfigEvent; handleServerConfigUpdated(); }, [serverConfigEvent]); return null; }