"use client"; import { useLayoutEffect, useRef, useState, type ReactNode } from "react"; import type { Person } from "@/lib/auth-client"; import { useTheme } from "@/lib/use-theme"; import { HexAvatar } from "./HexAvatar"; import { Logo } from "./Logo"; import { CompactLayoutContext, isCompactWidth } from "./layout-context"; const iconProps = { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.7, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, } as const; function SunIcon() { return ( ); } function MoonIcon() { return ( ); } /** Left account panel: logo, wordmark, theme toggle, the person, live status. */ export function AccountPanel({ person }: { person: Person }) { const { theme, toggleTheme } = useTheme(); const next = theme === "dark" ? "light" : "dark"; return (
MINNECT
{person.name} {person.email}
); } export interface AppShellProps { person: Person; /** Find experts panel (left column, under the account panel). */ search: ReactNode; /** Feed panel (center column). */ feed: ReactNode; /** Featured experts and Trending minnects panels (right column). */ discover: ReactNode; } /** Signed-in bento shell (spec "Desktop and tablet layout", ADR 0001 section 3). */ export function AppShell({ person, search, feed, discover }: AppShellProps) { const root = useRef(null); const [compact, setCompact] = useState(false); // The CSS switches layout with a container query on .app; the feed also // needs to know, because the phone feed is different markup (full-screen slides). useLayoutEffect(() => { const el = root.current; if (!el) return; const measure = () => setCompact(isCompactWidth(el.getBoundingClientRect().width)); measure(); if (typeof ResizeObserver === "undefined") return; const ro = new ResizeObserver(measure); ro.observe(el); return () => ro.disconnect(); }, []); return (
{compact ? null : search}
{feed}
); }