feat: establish unified administration and model context baseline
This commit is contained in:
@@ -1,23 +1,32 @@
|
||||
import { useEffect, useState, useSyncExternalStore, type ReactNode } from "react";
|
||||
import { useEffect, useLayoutEffect, useRef, useState, type ReactNode } from "react";
|
||||
import { Button } from "../components/ui/button";
|
||||
import { Dialog, DialogContent, DialogDescription, DialogTitle, DialogTrigger } from "../components/ui/dialog";
|
||||
|
||||
const query = "(max-width: 767px)";
|
||||
function subscribe(listener: () => void) {
|
||||
const media = window.matchMedia(query);
|
||||
media.addEventListener("change", listener);
|
||||
return () => media.removeEventListener("change", listener);
|
||||
}
|
||||
const isNarrow = () => window.matchMedia(query).matches;
|
||||
|
||||
/** Keep the archive content readable on phones, using the shared accessible dialog. */
|
||||
export function ArchiveNavigation({ surface, children }: { surface: string; children: ReactNode }) {
|
||||
const narrow = useSyncExternalStore(subscribe, isNarrow, () => false);
|
||||
/** Measure the embedded application, which can be narrower than the viewport. */
|
||||
export function ArchiveNavigation({ surface, forceDrawer = false, children }: { surface: string; forceDrawer?: boolean; children: ReactNode }) {
|
||||
const marker = useRef<HTMLSpanElement>(null);
|
||||
const [narrow, setNarrow] = useState(false);
|
||||
const [open, setOpen] = useState(false);
|
||||
useLayoutEffect(() => {
|
||||
const shell = marker.current?.closest<HTMLElement>(".thot-app-shell");
|
||||
if (!shell) return;
|
||||
const update = () => {
|
||||
const width = shell.getBoundingClientRect().width || window.innerWidth;
|
||||
const collapsed = forceDrawer || width < (surface === "core" ? 768 : 1000);
|
||||
setNarrow(collapsed);
|
||||
shell.dataset.administrationNarrow = String(collapsed);
|
||||
};
|
||||
update();
|
||||
const observer = typeof ResizeObserver !== "undefined" ? new ResizeObserver(update) : null;
|
||||
observer?.observe(shell);
|
||||
window.addEventListener("resize", update);
|
||||
return () => { observer?.disconnect(); window.removeEventListener("resize", update); delete shell.dataset.administrationNarrow; };
|
||||
}, [surface, forceDrawer]);
|
||||
useEffect(() => { setOpen(false); }, [surface, narrow]);
|
||||
if (!narrow || !["memory-management", "evidence-management"].includes(surface)) return children;
|
||||
return <Dialog open={open} onOpenChange={setOpen}>
|
||||
<div className="fixed inset-x-0 top-0 z-30 flex h-12 items-center justify-between border-b bg-background px-4">
|
||||
const anchor = <span ref={marker} hidden />;
|
||||
if (!narrow) return <>{anchor}{children}</>;
|
||||
return <>{anchor}<Dialog open={open} onOpenChange={setOpen}>
|
||||
<div className="thot-administration-mobile-nav">
|
||||
<span className="font-heading font-semibold">ThothII</span>
|
||||
<DialogTrigger render={<Button variant="outline" size="sm" />}>Navigation</DialogTrigger>
|
||||
</div>
|
||||
@@ -26,5 +35,5 @@ export function ArchiveNavigation({ surface, children }: { surface: string; chil
|
||||
<DialogDescription className="sr-only">Choose an administration page or a session.</DialogDescription>
|
||||
<div className="min-h-0 overflow-y-auto [&>aside]:w-full">{children}</div>
|
||||
</DialogContent>
|
||||
</Dialog>;
|
||||
</Dialog></>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user