"use client"; import React, { useContext, useEffect, useMemo, useState } from "react"; import Stack from "@moduletrace/twui/layout/Stack"; import Row from "@moduletrace/twui/layout/Row"; import { AppContext } from "@/src/pages/__root"; import useConnectionEpoch from "@/src/hooks/use-connection-epoch"; import useOnlineStatus from "@/src/hooks/use-online-status"; import { RailDrawerContext } from "./(data)/rail-drawer-context"; import BotsRail from "./(sections)/bots-rail"; import LiveSocket from "./(sections)/live-socket"; import AddFlow from "./(partials)/add-flow"; import OfflineBanner from "./(partials)/offline-banner"; import isRaillessPage from "./(utils)/is-railless-page"; /** * Full-bleed shell for every `/admin/*` page: the rail of bot conversations * and workspaces, then the page (a conversation, a workspace, or * settings), inside the Add flow both can open. A graphic's own page * (`isRaillessPage`) goes without the rail. No cards, no shadows; * regions are separated by 1px hairlines. Below `md` the rail slides over * the page instead of taking width. Height is `dvh`, so the on-screen * keyboard doesn't push the composer out of view. */ export default function BotsLayout({ children, }: { children: React.ReactNode; }) { const { pageProps } = useContext(AppContext); const pathname = pageProps?.url?.pathname; const [drawer_open, setDrawerOpen] = useState(false); const epoch = useConnectionEpoch(); const online = useOnlineStatus(); /** * Opening a conversation closes the drawer on a phone. */ useEffect(() => { setDrawerOpen(false); }, [pathname]); useEffect(() => { if (!drawer_open) return; const onKeyDown = (event: KeyboardEvent) => { if (event.key == "Escape") setDrawerOpen(false); }; window.addEventListener("keydown", onKeyDown); return () => window.removeEventListener("keydown", onKeyDown); }, [drawer_open]); const has_rail = !isRaillessPage({ pathname }); const drawer = useMemo( () => ({ open: drawer_open, setOpen: setDrawerOpen, has_rail }), [drawer_open, has_rail], ); return ( {online ? null : } {has_rail ? : null} {children} ); }