mirror of
https://github.com/multica-ai/multica.git
synced 2026-08-02 10:05:41 +02:00
Internal navigation on web feels laggy because clicking a sidebar link blocks 0.2–0.6s with zero visual feedback — no prefetch, no Suspense fallback in the dashboard segment, and no React transition to mark the route commit as pending. This change adds the three pieces App Router needs to make the click→commit window feel instant, scoped to the (dashboard) segment so auth/landing keep their existing chrome: - NavigationAdapter gains an optional prefetch(path). The web adapter wires it to router.prefetch; desktop leaves it undefined (react-router has no equivalent and doesn't need one). AppLink prefetches on hover/focus and preserves caller-supplied onMouseEnter/onFocus/onClick. - NavigationProvider wraps push/replace in useTransition and exposes the pending flag via useIsNavigating(). Every useNavigation().push caller — sidebar AppLink, command palette, post-create modal jumps — picks this up automatically. - New apps/web/app/[workspaceSlug]/(dashboard)/loading.tsx renders a minimal skeleton during cold transitions inside the dashboard segment only. - DashboardLayout renders a 1px top progress bar driven by useIsNavigating. packages/views remains free of next/* imports; desktop is unaffected by construction (no prefetch, transition flips quickly, no loading.tsx). Co-authored-by: multica-agent <github@multica.ai>
48 lines
1.4 KiB
TypeScript
48 lines
1.4 KiB
TypeScript
"use client";
|
|
|
|
import type { ReactNode } from "react";
|
|
import { SidebarProvider, SidebarInset } from "@multica/ui/components/ui/sidebar";
|
|
import { ModalRegistry } from "../modals/registry";
|
|
import { AppSidebar } from "./app-sidebar";
|
|
import { DashboardGuard } from "./dashboard-guard";
|
|
import { NavigationProgress } from "./navigation-progress";
|
|
import { WorkspacePresencePrefetch } from "./workspace-presence-prefetch";
|
|
|
|
interface DashboardLayoutProps {
|
|
children: ReactNode;
|
|
/** Rendered inside SidebarInset (e.g. ChatWindow, ChatFab — absolute-positioned overlays) */
|
|
extra?: ReactNode;
|
|
/** Rendered inside sidebar header as a search trigger */
|
|
searchSlot?: ReactNode;
|
|
/** Loading indicator */
|
|
loadingIndicator?: ReactNode;
|
|
}
|
|
|
|
export function DashboardLayout({
|
|
children,
|
|
extra,
|
|
searchSlot,
|
|
loadingIndicator,
|
|
}: DashboardLayoutProps) {
|
|
return (
|
|
<DashboardGuard
|
|
loadingFallback={
|
|
<div className="flex h-svh items-center justify-center">
|
|
{loadingIndicator}
|
|
</div>
|
|
}
|
|
>
|
|
<SidebarProvider className="h-svh">
|
|
<WorkspacePresencePrefetch />
|
|
<AppSidebar searchSlot={searchSlot} />
|
|
<SidebarInset className="relative overflow-hidden">
|
|
<NavigationProgress />
|
|
{children}
|
|
<ModalRegistry />
|
|
{extra}
|
|
</SidebarInset>
|
|
</SidebarProvider>
|
|
</DashboardGuard>
|
|
);
|
|
}
|