From 731a4f0dca23e7562bc01a5e3384c3c852e1fa2f Mon Sep 17 00:00:00 2001 From: agent_coder Date: Thu, 2 Jul 2026 22:02:37 +0300 Subject: [PATCH] refactor(#292 review): extract NAVBAR_COLLAPSE_BREAKPOINT constant (F2) The AppShell navbar breakpoint and both burger toggles' hiddenFrom/visibleFrom must be equal, or the sidebar becomes unreachable on tablet widths (the round-1 regression). A comment guarded that before; now a shared const does. Add NAVBAR_COLLAPSE_BREAKPOINT='md' to sidebar-atom.ts and reference it from the navbar breakpoint (global-app-shell) + both toggles (app-header). aside.breakpoint and the sm brand/search gates are intentionally separate contracts, left untouched. Co-Authored-By: Claude Opus 4.8 (1M context) --- apps/client/src/components/layouts/global/app-header.tsx | 5 +++-- .../src/components/layouts/global/global-app-shell.tsx | 3 ++- .../components/layouts/global/hooks/atoms/sidebar-atom.ts | 7 +++++++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/client/src/components/layouts/global/app-header.tsx b/apps/client/src/components/layouts/global/app-header.tsx index bf1add95..1cc4b5b6 100644 --- a/apps/client/src/components/layouts/global/app-header.tsx +++ b/apps/client/src/components/layouts/global/app-header.tsx @@ -12,6 +12,7 @@ import TopMenu from "@/components/layouts/global/top-menu.tsx"; import { Link } from "react-router-dom"; import { useAtom } from "jotai"; import { + NAVBAR_COLLAPSE_BREAKPOINT, desktopSidebarAtom, mobileSidebarAtom, } from "@/components/layouts/global/hooks/atoms/sidebar-atom.ts"; @@ -59,7 +60,7 @@ export function AppHeader() { // whole md navbar change). - hiddenFrom="md" + hiddenFrom={NAVBAR_COLLAPSE_BREAKPOINT} size="sm" /> @@ -69,7 +70,7 @@ export function AppHeader() { aria-label={t("Sidebar toggle")} opened={desktopOpened} onClick={toggleDesktop} - visibleFrom="md" + visibleFrom={NAVBAR_COLLAPSE_BREAKPOINT} size="sm" /> diff --git a/apps/client/src/components/layouts/global/global-app-shell.tsx b/apps/client/src/components/layouts/global/global-app-shell.tsx index 5bff0bab..1b41011e 100644 --- a/apps/client/src/components/layouts/global/global-app-shell.tsx +++ b/apps/client/src/components/layouts/global/global-app-shell.tsx @@ -6,6 +6,7 @@ import SettingsSidebar from "@/components/settings/settings-sidebar.tsx"; import { useAtom } from "jotai"; import { APP_NAVBAR_ID, + NAVBAR_COLLAPSE_BREAKPOINT, asideStateAtom, desktopSidebarAtom, mobileSidebarAtom, @@ -94,7 +95,7 @@ export default function GlobalAppShell({ // off-screen with no horizontal scroll. Collapsing the navbar to a toggle // drawer across the whole tablet band frees the full width for content // (the mobile drawer is closed by default, so nothing overlaps on load). - breakpoint: "md", + breakpoint: NAVBAR_COLLAPSE_BREAKPOINT, collapsed: { mobile: !mobileOpened, desktop: !desktopOpened, diff --git a/apps/client/src/components/layouts/global/hooks/atoms/sidebar-atom.ts b/apps/client/src/components/layouts/global/hooks/atoms/sidebar-atom.ts index f85f0fbc..c87c698c 100644 --- a/apps/client/src/components/layouts/global/hooks/atoms/sidebar-atom.ts +++ b/apps/client/src/components/layouts/global/hooks/atoms/sidebar-atom.ts @@ -7,6 +7,13 @@ import { atom } from "jotai"; // would create a shell -> chat-window -> shell import cycle). export const APP_NAVBAR_ID = "app-shell-navbar"; +// Single source of truth for the navbar collapse breakpoint. The AppShell navbar +// `breakpoint` and BOTH burger toggles' `hiddenFrom`/`visibleFrom` MUST use this +// exact value: if they drift, the sidebar becomes unreachable on tablet widths +// (the round-1 regression of #292). Kept here so the shell and the header share +// one constant the compiler enforces, instead of three hand-synced string literals. +export const NAVBAR_COLLAPSE_BREAKPOINT = "md"; + export const mobileSidebarAtom = atom(false); export const desktopSidebarAtom = atomWithWebStorage(