From 8aebc0ce8993a639c627c5c9c1c74db36572be89 Mon Sep 17 00:00:00 2001 From: Julin <142230457+c-julin@users.noreply.github.com> Date: Tue, 16 Jun 2026 15:56:27 +0100 Subject: [PATCH 1/3] fix(frontend): re-root federated Console router to avoid host __root collision Embedded Console (MF v2) renders Cloud UI's root route instead of its own in dev, because both apps compile a module with the identical id ./src/routes/__root.tsx and the shared rsbuild/MF dev runtime resolves the host's. That substitutes Cloud UI's RootComponent (react NuqsAdapter, Builder.io , /KBar) as Console's embedded root, which: crashes on useKBar (no KBarProvider in this subtree) -> empty sidebar; and leaves Console's useQueryState consumers without a matching nuqs adapter context -> NUQS-404. Re-root the federated router onto Console's own federatedRootRoute (a Console-unique module path that can't collide), and give it RouterSync + RequireAuth (RequireAuth triggers the user-data fetch that gates the endpoint-compatibility fetch and the embedded sidebar items). Standalone (app.tsx/__root.tsx) and prod are unaffected. --- frontend/src/federation/console-app.tsx | 32 +++++++++++++++++++- frontend/src/federation/federated-routes.tsx | 24 ++++++++++++--- 2 files changed, 50 insertions(+), 6 deletions(-) diff --git a/frontend/src/federation/console-app.tsx b/frontend/src/federation/console-app.tsx index e509785826..3add7754f2 100644 --- a/frontend/src/federation/console-app.tsx +++ b/frontend/src/federation/console-app.tsx @@ -38,6 +38,7 @@ import { protobufRegistry } from 'protobuf-registry'; import { LONG_LIVED_CACHE_STALE_TIME } from 'react-query/react-query.utils'; import { FederatedProviders } from './federated-providers'; +import { federatedRootRoute } from './federated-routes'; import { TokenManager } from './token-manager'; import type { ConsoleAppProps } from './types'; import { NotFoundPage } from '../components/misc/not-found-page'; @@ -45,6 +46,35 @@ import { addBearerTokenInterceptor, checkExpiredLicenseInterceptor, config, getG import { routeTree } from '../routeTree.gen'; import { installUISettingsSideEffects } from '../state/ui'; +/** + * Re-root the generated route tree onto Console's federated root. + * + * In the federated dev build, the generated tree's root route (from + * `src/routes/__root.tsx`) can be substituted by Cloud UI's own `__root` route + * — both apps compile a module with the identical id `./src/routes/__root.tsx`, + * and in the shared rsbuild/MF dev runtime the host's wins. The result is that + * the embedded Console renders Cloud UI's root chrome (its react NuqsAdapter, + * Builder.io ``, and ``/KBar) instead of Console's own + * federated layout — which breaks nuqs (NUQS-404), crashes on KBar + * (`getState is not a function`, no `KBarProvider` in this subtree), and leaves + * the embedded sidebar empty. + * + * `federatedRootRoute` lives at a Console-unique module path + * (`src/federation/federated-routes.tsx`) that cannot collide with Cloud UI, so + * reattaching the generated child routes to it guarantees the embedded app + * renders Console's own root. Standalone (`app.tsx`) and the legacy embedded + * entry keep using the generated `routeTree` unchanged. + */ +function createFederatedRouteTree() { + const childRoutes = routeTree.children ? Object.values(routeTree.children) : []; + for (const child of childRoutes) { + child.options.getParentRoute = () => federatedRootRoute; + } + return federatedRootRoute._addFileChildren(childRoutes); +} + +const federatedRouteTree = createFederatedRouteTree(); + /** * Creates an interceptor that refreshes the token on 401 and retries the request. * Uses TokenManager for deduplication and abort support. @@ -255,7 +285,7 @@ function ConsoleAppInner({ }); const r = createRouter({ - routeTree, + routeTree: federatedRouteTree, history: memoryHistory, context: { basePath: '', diff --git a/frontend/src/federation/federated-routes.tsx b/frontend/src/federation/federated-routes.tsx index 8546f2a6ec..26743d8a77 100644 --- a/frontend/src/federation/federated-routes.tsx +++ b/frontend/src/federation/federated-routes.tsx @@ -21,6 +21,8 @@ import { ErrorBoundary } from '../components/misc/error-boundary'; import { ErrorDisplay } from '../components/misc/error-display'; import { ErrorModalsRenderer } from '../components/misc/error-modal'; import { NullFallbackBoundary } from '../components/misc/null-fallback-boundary'; +import { RouterSync } from '../components/misc/router-sync'; +import RequireAuth from '../components/require-auth'; import { ModalContainer } from '../utils/modal-container'; /** @@ -60,11 +62,20 @@ export const federatedRootRoute = createRootRouteWithContext - - - - + <> + + + + {/* RequireAuth triggers the user-data fetch (api.refreshUserData) that + gates Console's endpoint-compatibility fetch and, in turn, the + embedded sidebar items. The standalone root (__root.tsx) wraps its + embedded layout the same way. */} + + + + + + ); } @@ -73,6 +84,9 @@ function FederatedRootLayout() { * Similar to EmbeddedLayout from __root.tsx but optimized for MF v2.0. */ function FederatedAppContent() { + // Mirrors __root.tsx's EmbeddedLayout so the embedded experience matches + // production: AppPageHeader renders the page title (it already suppresses + // the breadcrumb/sidebar-trigger in embedded mode — the host supplies those). return (
From 243a1e9a528b7d7fd2028e82f0bd6109629c3c2d Mon Sep 17 00:00:00 2001 From: Julin <142230457+c-julin@users.noreply.github.com> Date: Tue, 16 Jun 2026 16:19:24 +0100 Subject: [PATCH 2/3] fix(frontend): render DebugHelper in federated root (Cmd+Shift+D parity) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The embedded MF-v2 Console renders federatedRootRoute, not __root.tsx's RootLayout, so it was missing — the component that registers the Cmd+Shift+D debug dialog (dev-only). Render it in the federated root too, gated on NODE_ENV=development, mirroring __root.tsx. --- frontend/src/federation/federated-routes.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frontend/src/federation/federated-routes.tsx b/frontend/src/federation/federated-routes.tsx index 26743d8a77..c934fe6c22 100644 --- a/frontend/src/federation/federated-routes.tsx +++ b/frontend/src/federation/federated-routes.tsx @@ -14,6 +14,7 @@ import type { QueryClient } from '@tanstack/react-query'; import { createRootRouteWithContext, Outlet } from '@tanstack/react-router'; import { NuqsAdapter } from 'nuqs/adapters/tanstack-router'; +import { DebugHelper } from '../components/debug-helper/debug-dialog'; import AppFooter from '../components/layout/footer'; import AppPageHeader from '../components/layout/header'; import { LicenseNotification } from '../components/license/license-notification'; @@ -74,6 +75,8 @@ function FederatedRootLayout() { + {/* Cmd+Shift+D debug dialog — mirrors __root.tsx; dev-only. */} + {process.env.NODE_ENV === 'development' && } ); From c980c61645d41ca6c301d9eb2c5b342dbd260477 Mon Sep 17 00:00:00 2001 From: Julin <142230457+c-julin@users.noreply.github.com> Date: Tue, 16 Jun 2026 16:35:58 +0100 Subject: [PATCH 3/3] fix(frontend): restore Toaster + content padding in federated root MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirror __root.tsx's embedded AppContent in the MF-v2 federated layout (which now renders in prod where enable-console-mf-v2 is on): - Render — sonner is not an MF-shared singleton, so the host's Toaster can't surface Console's toasts; without this, toast() is silent in embedded mode. - Wrap in the pt-8 spacing div to match AppContent. (Fullscreen-route branch is intentionally omitted — no route sets staticData.fullscreen on master; it lands with the SQL workspace.) --- frontend/src/federation/federated-routes.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/frontend/src/federation/federated-routes.tsx b/frontend/src/federation/federated-routes.tsx index c934fe6c22..a50b8a3468 100644 --- a/frontend/src/federation/federated-routes.tsx +++ b/frontend/src/federation/federated-routes.tsx @@ -23,6 +23,7 @@ import { ErrorDisplay } from '../components/misc/error-display'; import { ErrorModalsRenderer } from '../components/misc/error-modal'; import { NullFallbackBoundary } from '../components/misc/null-fallback-boundary'; import { RouterSync } from '../components/misc/router-sync'; +import { Toaster } from '../components/redpanda-ui/components/sonner'; import RequireAuth from '../components/require-auth'; import { ModalContainer } from '../utils/modal-container'; @@ -99,12 +100,18 @@ function FederatedAppContent() { - +
+ +
+ + {/* sonner isn't an MF-shared singleton, so the host's can't + surface Console's toasts; mirror __root.tsx's AppContent. */} +
); }