/** * The app frame every signed-in page shares, as a skeleton: the page root, the * Mountain View top bar, the vertical rail and the phone bottom bar. Class * lists mirror the real ones (components/layout/navigation-rail.tsx, * components/layout/mv-top-bar.tsx and the roots of mail-app, calendar-app, * contacts-app, files-app, settings-app), so the frame doesn't move when the * page takes over. */ import type { ReactNode } from "react"; import { Accessibility, Bell, CalendarEvent, Folder, Grid3x3, Keyboard, Mail, Menu, Plus, Search, Settings, Shield, Users, } from "@/components/icons"; import { cn } from "@/lib/cn"; import { Bar, Circle, SkeletonRegion, type SkeletonSurface } from "./kit"; import { LoginSkeleton } from "./login-skeleton"; import { ProFrameSkeleton } from "./pro-skeleton"; import { PRO_SURFACES, type BootSurface } from "@/lib/boot-skeleton/surfaces"; export type RailApp = "mail" | "calendar" | "contacts" | "files" | "settings"; /** A rail item drawn as current: an app, or the admin panel (NavigationRail adminActive). */ export type RailActive = RailApp | "admin"; /** Translated rail labels for the phone bottom bar; bars are drawn where one is missing. */ export type RailLabels = Partial>; const RAIL_BORDER = { borderRight: "1px solid rgba(128, 128, 128, 0.3)" } as const; const DIVIDER = { borderColor: "rgba(128, 128, 128, 0.3)" } as const; /** Hidden when the last session's rail didn't show the app (bulwark-boot-hints). */ const APP_HIDE: Record<"calendar" | "contacts" | "files", string> = { calendar: "boot-no-calendar:hidden", contacts: "boot-no-contacts:hidden", files: "boot-no-files:hidden", }; const APPS = [ { id: "mail", icon: Mail }, { id: "calendar", icon: CalendarEvent }, { id: "contacts", icon: Users }, { id: "files", icon: Folder }, ] as const; /** * The 56px vertical rail (NavigationRail collapsed). `className` decides at * which widths it shows. `search` draws the global search button the Pro * shell's rail starts with. Items carry `data-sk-rail` so the Pro frame can * mark the focused tab's app from CSS. */ export function RailSkeleton({ active, search = false, className, }: { active?: RailActive; search?: boolean; className?: string; }) { const item = (on: boolean) => cn( "relative flex items-center justify-center w-10 h-10 rounded-md max-lg:min-h-[44px]", on ? "bg-primary/10 text-primary" : "text-muted-foreground", ); const footerItem = "flex items-center justify-center w-10 h-10 rounded-md"; return (