import { useMemo, useRef } from 'react'; import { useLocation } from 'react-router'; import { useDock, MusicPlayerHost, usePanelFullscreen } from 'officerdev'; import { useCapabilities } from 'hooks/useCapabilities'; import { ErrorBoundary } from '@/components/ErrorBoundary'; import { ScreenErrorFallback } from './ScreenErrorFallback'; import { Background } from './Background'; import { Header } from './Header'; import { Dock, CORE_DOCK_ITEMS, dockItemsFromPlugins, DEFAULT_DOCK_PATHS } from './Dock'; import { useIsTouch } from './useIsTouch'; import { usePageTitleSync } from '@/state/usePageTitle'; type DashboardLayoutProps = { children?: React.ReactNode; }; export function DashboardLayout({ children }: DashboardLayoutProps) { const { canVisit, plugins } = useCapabilities(); // 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 permitted = useMemo( () => [...CORE_DOCK_ITEMS, ...dockItemsFromPlugins(plugins)].filter((item) => canVisit(item.to)), [canVisit, plugins], ); const { items: visibleItems } = useDock(permitted, DEFAULT_DOCK_PATHS); const isTouch = useIsTouch(); const { pathname } = useLocation(); usePageTitleSync(); // 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 content region shrinks when the (in-flow) music dock takes its space; the nav dock measures its // reveal boundary from this element, so it always sits just above whatever's at the bottom. const regionRef = useRef(null); return (
); }