From 614ac2dbb836b63eb82d3fc56aaaca5b19a934fc Mon Sep 17 00:00:00 2001 From: Lycoon Date: Tue, 11 Aug 2026 00:14:31 +0200 Subject: [PATCH] fixed revision mode stripes rendering on hide, prevent project navbar shift during scroll on mobile, fixed content shift when switching between paginated/endless scroll mode, improved closing behaviour on project page sidebars --- components/editor/EditorBottomBar.tsx | 8 +- .../editor/MobileFormatToolbar.module.css | 10 + components/navbar/HomeNavbar.tsx | 16 +- components/navbar/ProjectNavbarMobile.tsx | 14 +- src/app/projects/page.tsx | 2 +- src/context/ViewContext.tsx | 4 +- src/lib/editor/use-view-mode-scroll-anchor.ts | 243 ++++++++++++++---- .../extensions/revisions-extension.ts | 33 ++- src/lib/utils/hooks.ts | 107 ++++++++ .../repro/revisions-overlay-clear.test.ts | 113 ++++++++ .../repro/view-mode-scroll-anchor.test.ts | 213 +++++++++++++++ 11 files changed, 689 insertions(+), 74 deletions(-) create mode 100644 src/tests/repro/revisions-overlay-clear.test.ts create mode 100644 src/tests/repro/view-mode-scroll-anchor.test.ts diff --git a/components/editor/EditorBottomBar.tsx b/components/editor/EditorBottomBar.tsx index 9e2eaef0..9b089bdd 100644 --- a/components/editor/EditorBottomBar.tsx +++ b/components/editor/EditorBottomBar.tsx @@ -4,7 +4,7 @@ import { useEffect, useRef, useState } from "react"; import { useTranslations } from "next-intl"; import { ChevronDown, ChevronUp } from "lucide-react"; -import { useIsPhone, useIsTouch, useViewportBottomInset } from "@src/lib/utils/hooks"; +import { useIsPhone, useIsTouch, usePagePanLock, useViewportBottomInset } from "@src/lib/utils/hooks"; import { KEYBOARD_MIN_HEIGHT } from "@src/lib/editor/visible-band"; import { useActiveEditor } from "@src/lib/editor/use-active-editor"; import { useEditorFocused } from "@src/lib/editor/use-editor-focused"; @@ -120,10 +120,16 @@ const EditorBottomBar = () => { const hasIslands = isTouch && !isPhone; + // A drag across the row must never pan the page: this bar is fixed chrome, so + // the pan would carry it off the screen (see usePagePanLock). The pill's own + // sideways scroll and its upward menus are exempted there. + const panLockRef = usePagePanLock(); + if (!isTouch || !isWriting) return null; return (
void; + /** + * Phone only: toggles the projects sidebar drawer (the burger lives here now). + * Both phone drawers sit below the navbar, so their opener stays tappable while + * they're open and has to close them again — see [ProjectPageContainer.module.css]. + */ + onToggleSidebar?: () => void; } -const HomeNavbar = ({ onOpenSidebar }: HomeNavbarProps) => { - const { openDashboard } = useContext(DashboardContext); +const HomeNavbar = ({ onToggleSidebar }: HomeNavbarProps) => { + const { isOpen, openDashboard, closeDashboard } = useContext(DashboardContext); const { user, isLoading } = useCookieUser(); const isPhone = useIsPhone(); const tNav = useTranslations("navbar"); @@ -29,6 +33,8 @@ const HomeNavbar = ({ onOpenSidebar }: HomeNavbarProps) => { // While the auth state is still loading, omit the tab arg so the modal // opens on its current activeTab instead of guessing wrong. const onOpen = () => { + // Second tap on the Settings icon closes the drawer it opened. + if (isOpen) return closeDashboard(); if (isLoading) openDashboard(); else openDashboard(user ? "Profile" : "Auth"); }; @@ -44,7 +50,7 @@ const HomeNavbar = ({ onOpenSidebar }: HomeNavbarProps) => {