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 { LinkPreviewProvider } from "./context/LinkPreviewContext";
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 { applyLiveEventFreeQueryDefaults } from "./lib/live-reconcile";
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 { startPersistedQueryCache } from "./lib/persisted-query-cache";
import { hasPendingWrites, subscribePendingWrites } from "./lib/reload-safety";
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();
// 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,
},
},
},
);
// Instance and personal preference reads have no live events; see live-reconcile.ts.
applyLiveEventFreeQueryDefaults(queryClient);
// Outages pause queries and replayable mutations instead of failing them, and
// live queries refresh once when the server answers again.
bindConnectivity(queryClient, connectivity);
startPersistedQueryCache(queryClient, {
buildId: typeof __PAPERCLIP_BUILD_COMMIT__ === "string" ? __PAPERCLIP_BUILD_COMMIT__ : null,
});
window.addEventListener("load", () => {
startServiceWorkerUpdates({
canReload: () => connectivity.getSnapshot().status === "online"
&& connectivity.getSnapshot().pendingWrites === 0 && queryClient.isMutating() === 0 && !hasPendingWrites(),
subscribeReloadSafety: listener => {
const cleanups = [connectivity.subscribe(listener), queryClient.getMutationCache().subscribe(listener), subscribePendingWrites(listener)];
return () => { for (const cleanup of cleanups) cleanup(); };
},
});
});
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};
}
function CompanyLinkPreviews({ children }: { children: React.ReactNode }) {
const { selectedCompanyId } = useCompany();
const { userId, settled } = useAccountIdentity();
return {children};
}
const rootElement = document.getElementById("root");
if (!rootElement) throw new Error("Paperclip root element is missing");
getOrCreatePaperclipReactRoot(window, rootElement).render(
);