import { VoiceCallProvider } from "./components/voice/VoiceCallProvider"; import { useAccountIdentity } from "./api/companies-query"; import * as React from "react"; import { StrictMode, useEffect } from "react"; import * as ReactDOM from "react-dom"; import { BrowserRouter } from "@/lib/router"; import { QueryClientProvider } from "@tanstack/react-query"; import { App } from "./App"; import { AppErrorBoundary } from "./components/AppErrorBoundary"; import { SentryGate } from "./components/SentryGate"; import { CompanyProvider, useCompany } from "./context/CompanyContext"; import { LiveUpdatesProvider } from "./context/LiveUpdatesProvider"; import { BreadcrumbProvider } from "./context/BreadcrumbContext"; import { PanelProvider } from "./context/PanelContext"; import { SidebarProvider } from "./context/SidebarContext"; import { DialogProvider } from "./context/DialogContext"; import { EditorAutocompleteProvider } from "./context/EditorAutocompleteContext"; import { PrimaryAgentProvider } from "./context/PrimaryAgentProvider"; import { ToastProvider, useToastActions, type ToastInput } from "./context/ToastContext"; import { ThemeProvider } from "./context/ThemeContext"; import { DesignSandboxProvider } from "./context/DesignSandboxContext"; import { TooltipProvider } from "@/components/ui/tooltip"; import { initPluginBridge } from "./plugins/bridge-init"; import { PluginLauncherProvider } from "./plugins/launchers"; import { startChunkLoadRecovery } from "./lib/chunk-load-recovery"; import { startPerfMeasureReaper } from "./lib/perf-measure-reaper"; import { getOrCreatePaperclipReactRoot } from "./lib/react-root"; import { startServiceWorkerUpdates } from "./lib/service-worker-updates"; import { connectivity } from "./lib/connectivity"; import { bindConnectivity, createAppQueryClient } from "./lib/query-client"; import { captureBrowserException } from "./lib/sentry"; import "@mdxeditor/editor/style.css"; import "./index.css"; initPluginBridge(React, ReactDOM); // React 19.2 emits an unbounded stream of performance.measure() entries for its // DevTools performance tracks and never clears them; on a long-lived tab they // accumulate into millions of native objects (GBs). Reap them periodically. startPerfMeasureReaper(); startChunkLoadRecovery(); // Parked SPA tabs never navigate, so beyond registering the worker this also // re-checks /sw.js on tab focus and hourly, and applies a discovered update // with one reload while the tab is hidden — otherwise an old worker and its // cached shell can outlive a deploy indefinitely. window.addEventListener("load", () => { startServiceWorkerUpdates(); }); // The global mutation error toast is raised from the MutationCache, outside // React; the bridge below hands it the ToastProvider's pushToast. let pushMutationErrorToast: ((input: ToastInput) => void) | null = null; function MutationErrorToastBridge() { const { pushToast } = useToastActions(); useEffect(() => { pushMutationErrorToast = pushToast; return () => { if (pushMutationErrorToast === pushToast) pushMutationErrorToast = null; }; }, [pushToast]); return null; } // Retry, pause-on-outage, and error-reporting defaults live in // lib/query-client.ts; app-specific cache tuning stays here. const queryClient = createAppQueryClient( { connectivity, reportError: (error) => captureBrowserException(error), notifyMutationError: ({ title, body }) => pushMutationErrorToast?.({ title, body, tone: "error" }), }, { defaultOptions: { queries: { staleTime: 30_000, // Explicit so cross-tab-published cache entries for resources this tab // isn't observing get collected promptly rather than lingering. Single // tuning point if we need to trim the cache footprint further. gcTime: 5 * 60_000, refetchOnWindowFocus: true, }, }, }, ); // Outages pause queries and replayable mutations instead of failing them, and // live queries refresh once when the server answers again. bindConnectivity(queryClient, connectivity); function CompanyAwareBreadcrumbProvider({ children }: { children: React.ReactNode }) { const { selectedCompany } = useCompany(); return {children}; } function CompanyVoiceCalls({ children }: { children: React.ReactNode }) { const { selectedCompanyId } = useCompany(); const { userId } = useAccountIdentity(); return {children}; } const rootElement = document.getElementById("root"); if (!rootElement) throw new Error("Paperclip root element is missing"); getOrCreatePaperclipReactRoot(window, rootElement).render( );