diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 5eeb0f47c..c107206c7 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -560,4 +560,5 @@ Use this ledger to prevent repeated branch and PR reviews when the reviewed HEAD | 2026-07-15 | codex/outstanding-work-cleanup | 0c56f27a37af88a073d2bb695d2cf4c05067ff4f + working-tree diff | repo-wide outstanding-work reconciliation and cleanup | No high-confidence P0/P1 remained in the locally executable scope. Fixed the stale architecture index for DSM/specifier routes, pruned redundant Knip configuration, removed the unused `SignedImage` default export, and reconciled maintained docs so completed/superseded plans no longer present as active work. Retained explicitly blocked work: provider-gated operator actions, the live-eval shadow reindex harness, deep-memory section-ownership design, and RAG follow-ups that require live evidence or product/security decisions. Full Knip findings were triaged rather than mass-deleted; unresolved-import scan is clean. | Offline `npm run verify:cheap` passed with 2,417 tests/1 skipped; docs links 806, script refs 264, codebase index 35/35; Knip unresolved scan clean; typecheck clean; focused Vitest 15/15; env-name parity clean; `git diff --check`. Provider-backed Supabase/OpenAI/GitHub/Railway, dependency audit, browser, build, drift, and release gates not run. | | 2026-07-15 | codex/design-polish-pass | 0c56f27a37af88a073d2bb695d2cf4c05067ff4f + reviewed working diff | full live design, responsive, UX, accessibility, design-system, routing, performance, lint, testing, documentation, and release-readiness review | No P0/P1 reproduced. Fixed the P2 duplicate phone scroll surface in the shared standalone shell and added a regression test; fixed mode-menu Tab dismissal; explicitly hid decorative dynamic icons; restricted press scaling to motion-safe environments; moved sheet backdrops and forced-colour glass/backdrop behavior onto design tokens. External target fidelity remains blocked because no independent Figma/mockup source was supplied. | Live 30-route desktop + 21-route phone sweep; targeted 320/390/639/768/1440/1920 proofs; `npm run verify:cheap` (2417 passed/1 skipped); `npm run verify:ui` (175/175); focused keyboard 1/1; accessibility media/axe 5/5; scoped Prettier, ESLint, TypeScript, type-scale, and icon-scale passed. Provider-backed checks not run. | | 2026-07-15 | codex/documents-closed-default | 49f63791bced2b1764a11ab723aea94b45b026b6 | documents viewer disclosure defaults and related defect hunt | Fixed the inconsistent default-open document viewer sections by making indexed text, high-yield summary, tables/diagrams, and indexing details a native mutually exclusive closed disclosure group. The section navigation opens its requested disclosure and deep-linked evidence still reveals its target. The hunt also removed the explicitly open nested table-review queue, preserved printable summary content through the browser print lifecycle, and added cold-server readiness guards to the affected viewer tests. No other high-confidence default-open defect remains in the live Documents scope. | `npm run verify:cheap`; TypeScript; focused ESLint/Prettier; clean-worktree mocked Chromium coverage for deep-linked evidence, structured summary, closed/mutually-exclusive disclosures, navigation opening, and print state restore; `git diff --check`. Turbopack could not run through the local external `node_modules` junction, so clean browser verification used Next's supported Webpack dev mode. No Supabase/OpenAI/live-provider checks run. | +| 2026-07-17 | codex/header-footer-scroll-timing-20260717 | 8298bfdcb40c207dbac1128e83c07b4aba782e32 | header and bottom-composer scroll timing, motion, responsive behavior, and merge readiness | Added deliberate hide/reveal travel thresholds with direction-reset handling, aligned header and composer easing/durations, and preserved reduced-motion and breakpoint behavior. No remaining high-confidence P0-P2 defect was found in the scoped diff or focused live behavior. | Focused Vitest 7/7; targeted Chromium UI 5/5; scoped ESLint; Prettier; full TypeScript; full lint; `git diff --check`. `verify:cheap` reached the aggregate Vitest phase, where two repository graph scans exceeded their 30-second test timeout under local disk contention; isolated assertions passed until the same timeout. No Supabase/OpenAI/live-provider checks run. | | 2026-07-15 | HEAD / main snapshot (detached review worktree) | 0c56f27a37af88a073d2bb695d2cf4c05067ff4f | comprehensive repository review | Changes requested: two P1 defects (high-risk clinical claim support can accept a different trigger condition on lexical overlap; document-mode URL auto-run loops on navigation and leaves search loading indefinitely), one P2 supply-chain guard gap (the action-pin checker accepts mutable major tags and ignores SHA-pinned major versions), and one P3 orientation-doc gap (DSM and legacy specifier routes are absent from the codebase index). No P0 found. | Node 24/npm 11 and `npm ls --depth=0`; format, runtime, lint, TypeScript, sitemap/brand/icon/type-scale, docs links/scripts, CI/action/Codex guards; coverage 259 files passed/1 skipped and 2,417 tests passed/1 skipped; offline RAG 36 fixtures plus 282 tests; production build/client-secret scan; deployment boot smoke; critical Chromium 8/10 with two reproducible document-search failures; accessibility 5/5; viewport/focus checks through 1920x1080. Provider-backed governance, quality, drift, tenancy, hosted CI, and the cross-browser matrix were blocked or skipped by policy/targeted failures. | diff --git a/src/components/clinical-dashboard/master-search-header.tsx b/src/components/clinical-dashboard/master-search-header.tsx index 40a7be517..6c9102785 100644 --- a/src/components/clinical-dashboard/master-search-header.tsx +++ b/src/components/clinical-dashboard/master-search-header.tsx @@ -1243,7 +1243,12 @@ export function MasterSearchHeader({ usesCompactMobileBottomStyle && "document-mobile-search-compact", showFooterSearchChips && "flex flex-col items-center gap-2.5", shouldHideBottomOnScroll && - "max-sm:transition-transform max-sm:duration-200 max-sm:ease-out motion-reduce:transition-none", + cn( + "max-sm:transition-transform motion-reduce:transition-none", + bottomComposerHidden + ? "max-sm:duration-[240ms] max-sm:ease-[cubic-bezier(0.4,0,0.2,1)]" + : "max-sm:duration-200 max-sm:ease-[cubic-bezier(0.22,1,0.36,1)]", + ), )} > {usesBottomComposerPlacement ?
: null} @@ -1516,8 +1521,18 @@ export function MasterSearchHeader({ // fixed-position mobile mode menu keeps the viewport as its containing block. hideStrategy === "overlay" && (overlayAllBreakpoints - ? "transition-transform duration-200 ease-out motion-reduce:transition-none" - : "max-sm:transition-transform max-sm:duration-200 max-sm:ease-out motion-reduce:transition-none"), + ? cn( + "transition-transform motion-reduce:transition-none", + headerChromeHidden + ? "duration-[240ms] ease-[cubic-bezier(0.4,0,0.2,1)]" + : "duration-200 ease-[cubic-bezier(0.22,1,0.36,1)]", + ) + : cn( + "max-sm:transition-transform motion-reduce:transition-none", + headerChromeHidden + ? "max-sm:duration-[240ms] max-sm:ease-[cubic-bezier(0.4,0,0.2,1)]" + : "max-sm:duration-200 max-sm:ease-[cubic-bezier(0.22,1,0.36,1)]", + )), hideStrategy === "overlay" && headerChromeHidden && (overlayAllBreakpoints ? "-translate-y-full" : "max-sm:-translate-y-full"), @@ -1734,7 +1749,10 @@ export function MasterSearchHeader({ data-scroll-hidden={headerChromeHidden ? "true" : undefined} data-testid="universal-header-collapse" className={cn( - "max-sm:grid max-sm:transition-[grid-template-rows] max-sm:duration-200 max-sm:ease-out motion-reduce:transition-none", + "max-sm:grid max-sm:transition-[grid-template-rows] motion-reduce:transition-none", + headerChromeHidden + ? "max-sm:duration-[240ms] max-sm:ease-[cubic-bezier(0.4,0,0.2,1)]" + : "max-sm:duration-200 max-sm:ease-[cubic-bezier(0.22,1,0.36,1)]", headerChromeHidden ? "max-sm:[grid-template-rows:0fr]" : "max-sm:[grid-template-rows:1fr]", )} {...chromeFocusProps} diff --git a/src/components/clinical-dashboard/use-hide-on-scroll.ts b/src/components/clinical-dashboard/use-hide-on-scroll.ts index 37a8586ea..750721013 100644 --- a/src/components/clinical-dashboard/use-hide-on-scroll.ts +++ b/src/components/clinical-dashboard/use-hide-on-scroll.ts @@ -16,26 +16,58 @@ const topRevealOffset = 8; // Minimum per-event delta (px) before we treat movement as intentional, to // avoid jitter from momentum settling and fractional scroll positions. const minimumDelta = 4; +// Once the header's activation band has passed, require a little more +// continuous downward travel before hiding. Reappearing should be easier, but +// still deliberate enough that trackpad/touch momentum cannot flicker the +// chrome at a direction change. +const hideIntentDistance = 24; +const revealIntentDistance = 12; + +type ScrollDirection = "down" | "up" | null; /** Pure scroll-direction evaluation used by the hook; exported for unit tests. */ -export function computeScrollHideUpdate(params: { offset: number; lastOffset: number; currentlyHidden: boolean }): { +export function computeScrollHideUpdate(params: { + offset: number; + lastOffset: number; + currentlyHidden: boolean; + direction?: ScrollDirection; + directionTravel?: number; +}): { hidden: boolean; lastOffset: number; + direction: ScrollDirection; + directionTravel: number; } { - const { offset, lastOffset, currentlyHidden } = params; + const { offset, lastOffset, currentlyHidden, direction = null, directionTravel = 0 } = params; // Ignore iOS rubber-band overscroll at the top. - if (offset < 0) return { hidden: currentlyHidden, lastOffset }; + if (offset < 0) return { hidden: currentlyHidden, lastOffset, direction, directionTravel }; const delta = offset - lastOffset; if (offset <= topRevealOffset) { - return { hidden: false, lastOffset: offset }; + return { hidden: false, lastOffset: offset, direction: null, directionTravel: 0 }; } if (Math.abs(delta) < minimumDelta) { - return { hidden: currentlyHidden, lastOffset }; + return { hidden: currentlyHidden, lastOffset, direction, directionTravel }; } - if (delta > 0) { - return { hidden: offset > hideActivationOffset, lastOffset: offset }; + + const nextDirection: Exclude