diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index aa9439b..071770e 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -206,21 +206,12 @@ export const App: React.FC = () => { const [showProfileMenu, setShowProfileMenu] = useState(false); const connection = useConnectionState(); - // Height of the app header as --app-header-h: the page background paints the header colour up to there, - // so the scrollbar gutter on the right continues the header instead of showing the page colour - const [headerEl, setHeaderEl] = useState(null); + // Desktop: the window does not scroll; this area under the header does (phones keep the window scroll) + const scrollAreaRef = useRef(null); + // A new page starts at its top (the desktop scroll area would otherwise keep the old page's position) useEffect(() => { - if (!headerEl || typeof ResizeObserver === 'undefined') return; - const root = document.documentElement; - const update = () => root.style.setProperty('--app-header-h', `${headerEl.offsetHeight}px`); - update(); - const observer = new ResizeObserver(update); - observer.observe(headerEl); - return () => { - observer.disconnect(); - root.style.removeProperty('--app-header-h'); - }; - }, [headerEl]); + scrollAreaRef.current?.scrollTo?.({ top: 0 }); + }, [activeTab]); const [showAbout, setShowAbout] = useState(false); const [showTriageTrash, setShowTriageTrash] = useState(false); const { update: availableUpdate, dismiss: dismissUpdate } = useUpdateCheck(); @@ -848,7 +839,7 @@ export const App: React.FC = () => { if (isPhoneWidth()) { openMobileTriageCapture(); } else { - window.scrollTo({ top: 0, behavior: 'smooth' }); + scrollAreaRef.current?.scrollTo?.({ top: 0, behavior: 'smooth' }); setCaptureFocusCount((c) => c + 1); } }; @@ -920,13 +911,12 @@ export const App: React.FC = () => { return (
{/* Top Header Bar: Floating on mobile, solid panel on desktop */}
{/* Mobile Header: Floating Bar with Logo Action Dropdown Left & Profile Avatar Right */} @@ -1306,14 +1296,36 @@ export const App: React.FC = () => { )} {/* Main Container */} - {/* The workspace fills the window exactly and scrolls only inside the note, never the page */} + {/* Desktop: the window never scrolls, only this area under the header (room for the scrollbar kept on + both sides, so the content stays centred under the header and nothing shifts between pages). The + workspace fills it exactly and scrolls only inside the note. Phones scroll the window as before. */} +
setActiveTab('triage')}> -
+
{activeTab === 'triage' && ( {
+
= ({ setKanbanColHeight(null); return; } - // From the board's top to the window's bottom; then take back what still overflows (footer, paddings) - const top = el.getBoundingClientRect().top + window.scrollY; - setKanbanColHeight(Math.max(320, window.innerHeight - top - 16)); + // From the board's top to the bottom of the app's scroll area (the desktop window itself does not + // scroll); then take back what still overflows (paddings) + const area = el.closest('[data-app-scroll]'); + const areaTop = area ? area.getBoundingClientRect().top - area.scrollTop : -window.scrollY; + const areaHeight = area ? area.clientHeight : window.innerHeight; + const top = el.getBoundingClientRect().top - areaTop; + setKanbanColHeight(Math.max(320, areaHeight - top - 16)); requestAnimationFrame(() => { - const over = document.documentElement.scrollHeight - window.innerHeight; + const over = area ? area.scrollHeight - area.clientHeight : document.documentElement.scrollHeight - window.innerHeight; if (over > 0) setKanbanColHeight((h) => (h ? Math.max(320, h - over) : h)); }); }; diff --git a/frontend/src/features/triage/TriageDeck.tsx b/frontend/src/features/triage/TriageDeck.tsx index ff0c62f..eca3de5 100644 --- a/frontend/src/features/triage/TriageDeck.tsx +++ b/frontend/src/features/triage/TriageDeck.tsx @@ -488,7 +488,9 @@ export const TriageDeck: React.FC = ({ } return ( -
+ // Desktop: a column filling the height under the header; card and buttons keep their size, the queue + // takes the rest and scrolls inside (the page only scrolls when the window is too low even for that) +
{/* Top Deck Status & Undo Bar */}
@@ -931,7 +933,7 @@ export const TriageDeck: React.FC = ({ {/* Waiting Quick Dump Queue List (Touch-sized & Swipeable Left/Right) */} {cards.length > 1 && ( -
+
{/* Phones: the queue stays folded so the card and its buttons fit on the screen */} {/* Folds open/closed on phones; always open from sm upwards */}
-
+
@@ -970,7 +972,7 @@ export const TriageDeck: React.FC = ({
-
+
{cards.slice(1).map((item) => (