import { useMemo, useRef } from 'react'; import { useLocation } from 'react-router'; import { ImpersonationBanner, useDock, usePanelFullscreen, useChatNotifications } from 'officerdev'; import { usePermissions } from 'hooks/usePermissions'; import { ErrorBoundary } from '@/components/ErrorBoundary'; import { ScreenErrorFallback } from './ScreenErrorFallback'; import { Background } from './Background'; import { Header } from './Header'; import { Dock, useDockItems, defaultDockPaths } from './Dock'; import { Spotlight } from './Spotlight/Spotlight'; import { ShortcutsModal } from './ShortcutsModal'; import { useIsTouch } from './useIsTouch'; import { usePageTitleSync } from '@/state/usePageTitle'; import { RouteGate } from './RouteGate'; type DashboardLayoutProps = { children?: React.ReactNode; }; export function DashboardLayout({ children }: DashboardLayoutProps) { const { canVisit, plugins } = usePermissions(); // Filtered BEFORE useDock, so a member's saved dock order cannot resurrect an icon their role no longer // reaches, and so the pinned-item defaults fall back to something they can actually open. Cosmetic // either way — every one of these routes is refused server-side too — but an app that offers a door it // will then slam is worse than one that never showed it. // The shell's own items plus whatever the installed sidecars contribute. `plugins` already excludes // anything uninstalled or disabled, so an absent feature has no tile at all rather than a dead one. const allDockItems = useDockItems(plugins); const permitted = useMemo(() => allDockItems.filter((item) => canVisit(item.to)), [canVisit, allDockItems]); const { items: visibleItems } = useDock( permitted, useMemo(() => defaultDockPaths(plugins), [plugins]), ); const isTouch = useIsTouch(); const { pathname } = useLocation(); usePageTitleSync(); useChatNotifications(); // A panel can maximize into the content region on its own, but it cannot paint over this header — the // region below is an `absolute z-2` stacking context and the header is a `fixed z-10` sibling of it. So // "full screen" is cooperative: the panel asks, and the chrome steps aside. const panelFullscreen = usePanelFullscreen(); // The nav dock measures its reveal boundary from this element, so it sits just above whatever is at the // bottom of the content region. Written for the in-flow music bar, which used to be rendered here and // left with `plugins/music/` on 2026-08-15; the measurement is not music-specific and still holds for // anything a screen puts at its foot. const regionRef = useRef(null); return (
{/* ABOVE the header, and outside everything that can be hidden. A fullscreen panel hides the header; it must not be able to hide the fact that you are somebody else. Renders nothing when you are not. */}
); }