| 1940 | 1940 | | * The app: three surfaces that never blur together. The rail, a narrow |
| 1941 | 1941 | | * column of icons down the window's left edge; the mode's sidebar, flat |
| 1942 | 1942 | | * against it with a hairline between, which folds away with Ctrl B and is |
| 1943 | | − | * a drawer below 1024px; and the page, a rounded panel inset from the |
| 1944 | | − | * window, with its header across the top: the sidebar's toggle, where you |
| 1945 | | − | * are, search, Ask g1t and Create new. On a phone the rail is a bar along |
| 1946 | | − | * the bottom. Only for someone signed in; without a workspace yet, there |
| 1943 | + | * a drawer below 1024px; and the page, flat against the sidebar and |
| 1944 | + | * filling the rest of the window, with its top bar across the top and a |
| 1945 | + | * line under it: the sidebar's toggle, where you are, search, Ask g1t and |
| 1946 | + | * Create new. On a phone the rail is a bar along the bottom and the page |
| 1947 | + | * a rounded panel above it. Only for someone signed in; without a workspace yet, there |
| 1947 | 1948 | | * is no rail. The rail and the sidebar are drawn from the sidebar kit |
| 1948 | 1949 | | * (components/ui/sidebar.tsx): one provider holds whether the sidebar is |
| 1949 | 1950 | | * open on a computer and whether the drawer is. |
| ⋯ |
| 1976 | 1977 | | usePaletteShortcut(() => setPalette((open) => !open)); |
| 1977 | 1978 | | const leaving = useLeaving(); |
| 1978 | 1979 | | useVisualViewport(); |
| 1979 | | − | // Whether the page has scrolled under the top bar, which then draws its line (see data-page-head). |
| 1980 | | − | const [scrolled, setScrolled] = useState(false); |
| 1981 | | − | useEffect(() => { |
| 1982 | | − | const onScroll = () => setScrolled(window.scrollY > 2); |
| 1983 | | − | onScroll(); |
| 1984 | | − | window.addEventListener("scroll", onScroll, { passive: true }); |
| 1985 | | − | return () => window.removeEventListener("scroll", onScroll); |
| 1986 | | − | }, []); |
| 1987 | | − | |
| 1988 | 1980 | | const going = useNavigation().location?.pathname; |
| 1989 | 1981 | | const ws = shell.workspace; |
| 1990 | 1982 | | const mode = modeOf(going ?? pathname, ws?.slug ?? null); |
| ⋯ |
| 2070 | 2062 | | // On a phone (below 768px): the bottom bar, and a conversation full screen. |
| 2071 | 2063 | | const conversation = isConversation(going ?? pathname); |
| 2072 | 2064 | | // Where the page panel starts: 8px past the rail (56px), or 8px past the sidebar (56 + 240px) when it is open. |
| 2073 | | − | const left = ws ? (inline ? "md:left-16 lg:left-[19rem]" : "md:left-16") : inline ? "md:left-2 lg:left-[15.5rem]" : "md:left-2"; |
| 2074 | | − | const pad = ws ? (inline ? "md:pl-16 lg:pl-[19rem]" : "md:pl-16") : inline ? "md:pl-2 lg:pl-[15.5rem]" : "md:pl-2"; |
| 2065 | + | // The page starts where the rail (3.5rem) and, open, the sidebar (15rem) end. |
| 2066 | + | const pad = ws ? (inline ? "md:pl-14 lg:pl-[18.5rem]" : "md:pl-14") : inline ? "lg:pl-[15rem]" : ""; |
| 2075 | 2067 | | |
| 2076 | 2068 | | return ( |
| 2077 | 2069 | | <SidebarProvider open={!closed} onOpenChange={(open) => setClosed(!open)} openMobile={drawer} onOpenMobileChange={setDrawer} toggleSidebar={toggleSidebar}> |
| ⋯ |
| 2100 | 2092 | | </SidebarMobile> |
| 2101 | 2093 | | )} |
| 2102 | 2094 | | |
| 2103 | | − | <div className={`min-h-dvh px-1.5 pt-(--frame-top) pb-(--tabbar-h) md:pr-2 ${pad}`}> |
| 2104 | | − | {/* A page whose own header sits right under this bar (data-page-head: a conversation's, a project's) reads as one |
| 2105 | | − | block with it: no line between them until the page scrolls under the bar. A sticky one (data-page-head="sticky", |
| 2106 | | − | an open artifact's) stays under the bar, so the line is always its. */} |
| 2107 | | − | <div className="flex min-h-[calc(100dvh-var(--frame-top)-var(--tabbar-h))] min-w-0 flex-col rounded-[14px] bg-bg [&:has([data-page-head])>header:not([data-scrolled])]:border-b-transparent [&:has([data-page-head=sticky])>header]:border-b-transparent"> |
| 2095 | + | <div className={`min-h-dvh pt-(--frame-top) pb-(--tabbar-h) max-md:px-1.5 ${pad}`}> |
| 2096 | + | {/* The top bar always has its line, so a page's own header (data-page-head: a conversation's, a project's, |
| 2097 | + | a sticky one under an open artifact) sits under it as its own band. */} |
| 2098 | + | <div className="flex min-h-[calc(100dvh-var(--frame-top)-var(--tabbar-h))] min-w-0 flex-col bg-bg max-md:rounded-[14px]"> |
| 2108 | 2099 | | <header |
| 2109 | | − | data-scrolled={scrolled || undefined} |
| 2110 | | − | className={`sticky top-(--frame-top) z-30 flex h-14 items-center gap-1 rounded-t-[14px] border-b border-line bg-bg pr-2 pl-2 transition-[border-color] duration-150 sm:pr-3 ${inline ? "lg:pl-3.5" : ""} ${conversation ? "max-md:hidden" : ""}`} |
| 2100 | + | className={`sticky top-(--frame-top) z-30 flex h-14 items-center gap-1 border-b border-line bg-bg pr-2 pl-2 max-md:rounded-t-[14px] sm:pr-3 ${inline ? "lg:pl-3.5" : ""} ${conversation ? "max-md:hidden" : ""}`} |
| 2111 | 2101 | | > |
| 2112 | 2102 | | {panel && ( |
| 2113 | 2103 | | <SidebarTrigger |
| ⋯ |
| 2155 | 2145 | | </main> |
| 2156 | 2146 | | </div> |
| 2157 | 2147 | | </div> |
| 2158 | | − | {/* The panel's edge: a rounded frame over the page, with the background beyond it, so what scrolls stays inside. |
| 2159 | | − | Every page has it, a conversation too; only a phone's conversation, full screen over everything, goes without. */} |
| 2148 | + | {/* On a phone the page is a rounded panel above the bottom bar: a frame over it, with the background beyond, |
| 2149 | + | so what scrolls stays inside. A phone's conversation, full screen over everything, goes without. */} |
| 2160 | 2150 | | <div |
| 2161 | 2151 | | aria-hidden="true" |
| 2162 | | − | className={`pointer-events-none fixed top-(--frame-top) right-1.5 bottom-(--tabbar-h) left-1.5 z-[35] rounded-[14px] shadow-[0_0_0_1px_var(--color-line),0_0_0_100vmax_var(--color-shell)] md:right-2 ${left} ${conversation ? "max-md:hidden" : ""}`} |
| 2152 | + | className={`pointer-events-none fixed top-(--frame-top) right-1.5 bottom-(--tabbar-h) left-1.5 z-[35] rounded-[14px] shadow-[0_0_0_1px_var(--color-line),0_0_0_100vmax_var(--color-shell)] md:hidden ${conversation ? "max-md:hidden" : ""}`} |
| 2163 | 2153 | | /> |
| 2164 | 2154 | | {ws && <BottomBar user={user} workspace={ws} pins={shell.pins ?? []} unread={unread} onReselect={panel ? () => setDrawer(true) : undefined} />} |
| 2165 | 2155 | | <CommandPalette open={palette} onOpenChange={setPalette} commands={commands} repo={shell.repo ? `${shell.repo.namespace}/${shell.repo.name}` : null} /> |