Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions apps/web/src/components/ChatView.desktop-window.browser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,25 @@ describe("ChatView (full app): the desktop app's window", () => {
root.classList.remove("electron", "wco");
}
});

it("mounts the layer that holds every thread's browser pages", async () => {
// Its code is a chunk only the desktop app fetches (routes/__root.tsx), so
// the window has to load it and mount it by itself, with no panel open.
const mounted = await mountChatView({
viewport: DEFAULT_VIEWPORT,
snapshot: createSnapshotForTargetUser({
targetMessageId: "msg-user-desktop-browser-host" as MessageId,
targetText: "desktop browser host",
}),
});

try {
await waitForElement(
() => document.querySelector<HTMLElement>('[data-testid="browser-host-layer"]'),
"The desktop app should mount the browser host layer at its root.",
);
} finally {
await mounted.cleanup();
}
});
});
21 changes: 18 additions & 3 deletions apps/web/src/routes/__root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,14 @@ import {
useNavigate,
useRouter,
} from "@tanstack/react-router";
import { useCallback, useEffect, useEffectEvent, useRef, useState } from "react";
import { Suspense, useCallback, useEffect, useEffectEvent, useRef, useState } from "react";
import { QueryClient, useQueryClient } from "@tanstack/react-query";

import { APP_DISPLAY_NAME } from "../branding";
import { AppSidebarLayout } from "../components/AppSidebarLayout";
import { CommandPalette, preloadCommandPaletteDialog } from "../components/CommandPalette";
import { preloadDestinationLists } from "../components/sidebar/destinationLists";
import { PushAlertsRuntime } from "../pushAlerts/PushAlertsRuntime";
import { BrowserHostLayer } from "../components/browser/BrowserHostLayer";
import { BrowserProfileReconciler } from "../components/desktop/BrowserProfileReconciler";
import { DesktopUpdateInstallDialog } from "../components/desktop/DesktopUpdateInstallDialog";
import { SshPasswordPromptDialog } from "../components/desktop/SshPasswordPromptDialog";
Expand Down Expand Up @@ -163,6 +162,17 @@ import {
type StaleBuildRecovery,
} from "../staleBuild";

// Every thread's browser pages and this window's browser connection for
// agents: the desktop app only, since the web build has no `<webview>`. A
// chunk of its own, so a browser tab or a phone never downloads it; the
// desktop app fetches it as it starts, beside everything else.
const BrowserHostLayerLazy = lazyWithPreload(() =>
import("../components/browser/BrowserHostLayer").then((module) => module.BrowserHostLayer),
);
if (isElectron) {
void BrowserHostLayerLazy.preload();
}

// The theme library's sync across computers: after first paint, never at startup.
const ThemeLibrarySyncLazy = lazyWithPreload(() =>
import("../themeLibrary/themeLibraryState").then((module) => module.ThemeLibrarySync),
Expand Down Expand Up @@ -276,7 +286,12 @@ function RootRouteView() {
{/* The Keep bar every theme trial shows, the import dialog, theme file drops. */}
<ThemeTrialBar />
{isElectron ? <BrowserProfileReconciler /> : null}
{isElectron ? <BrowserHostLayer /> : null}
{/* Plain Suspense: the browser panel needs it, so a failure shows. */}
{isElectron ? (
<Suspense fallback={null}>
<BrowserHostLayerLazy.Component />
</Suspense>
) : null}
<SshPasswordPromptDialog />
<QuitConfirmationDialog />
<DesktopUpdateInstallDialog />
Expand Down
8 changes: 5 additions & 3 deletions scripts/check-web-bundle-size.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,11 @@ const DEFAULT_ENTRY_GZIP_BUDGET_BYTES = 255_000;
// Raised to 915 KB (2026-11-13, Will's OK): main sat 49 B under the budget
// (911,951 B in CI), so the browser's search engine setting (#907), about
// 200 B of Settings catalog and schema once its address rules and search URLs
// were moved behind the browser panel, read 912,165 B. Worth trimming next:
// the browser panel's host and automation load at startup on the web and
// phones too, which have no browser panel.
// were moved behind the browser panel, read 912,165 B.
// Measured 890,863 B here (2026-11-14) once the browser panel's host and
// automation, which only the desktop app has a use for, became a chunk only
// it fetches (routes/__root.tsx): about 22 KB back. The budget stays, as
// headroom.
const DEFAULT_STARTUP_GZIP_BUDGET_BYTES = 915_000;

const entryBudgetBytes = Number(
Expand Down
Loading