From 52e4707c5016297259996712cb8e8e4b9fd444d2 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Sun, 12 Jul 2026 02:18:36 +0800 Subject: [PATCH 1/2] Compact document-viewer redesign: dedup chrome, honest cards, tighter PDF surface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses a 20-point design review of the document viewer: - Nav chrome: drop the redundant Answer|Documents toggle and the ‹Documents breadcrumb; header is now back-arrow + document

title + scope + actions. Mobile back uses ArrowLeft (was a Menu hamburger). - Overview cards: remove misleading chevrons; replace the fake wireframe page thumbnails with real "Page N" jump chips; de-dup the overview into a single header teaser; derive the type eyebrow instead of hardcoding "Clinical guideline"; fix uneven card heights. - Actions: rebalance to a clean grid-cols-2 sm:grid-cols-4 row; relabel "Scope" to "Add to scope". - PDF surface: collapse the toolbar to one row; stop the page indicator stretching full-width; replace the always-on helper paragraph and canvas/browser jargon with a "Sharper zoom"/"Standard view" toggle + tooltip; reduce reserved empty min-height. - Evidence/tables/layout: de-emphasise the empty pinned-evidence state; let the extracted table lead with the raw image behind a "Show source image" disclosure; let the summary rail flow with the page (drop the nested sticky/overflow scroll), which also clears the composer overlap. Preserves test invariants (h1 title, pinned-source-evidence/pdf-toolbar testids, viewer-nav anchors, fullscreen/Answer labels, static mobile toolbar). tsc/eslint/prettier clean; ui-smoke document-viewer suite green. Co-Authored-By: Claude Opus 4.8 --- src/components/DocumentViewer.tsx | 464 ++++++++++++++---------------- 1 file changed, 222 insertions(+), 242 deletions(-) diff --git a/src/components/DocumentViewer.tsx b/src/components/DocumentViewer.tsx index 28a40eafd..329570f0c 100644 --- a/src/components/DocumentViewer.tsx +++ b/src/components/DocumentViewer.tsx @@ -17,7 +17,6 @@ import { FileText, Loader2, Maximize2, - Menu, Minimize2, Minus, Plus, @@ -34,7 +33,8 @@ import { } from "lucide-react"; import { type FormEvent, useEffect, useMemo, useRef, useState } from "react"; import { AccessibleTable } from "@/components/AccessibleTable"; -import { documentDisplayTitle } from "@/components/DocumentOrganizationBadges"; +import { documentDisplayTitle, documentOrganizationProfile } from "@/components/DocumentOrganizationBadges"; +import { formatDocumentLabelDisplay } from "@/lib/document-tags"; import { DocumentActionAnchor, DocumentActionButton, @@ -447,6 +447,65 @@ function DocumentImage({ image }: { image: ImageRow }) { .join(" "), ); + const clinicalUseReasonLine = + image.clinicalUseClass && image.clinicalUseClass !== "clinical_evidence" && image.clinicalUseReason ? ( +

{image.clinicalUseReason}

+ ) : null; + + // The image preview and its extracted table are the same content; when a + // structured table is available it leads and the raw image collapses behind a + // disclosure so the card is not doubled in height. + const imageBlock = ( +
+ {failed ? ( +
+
+ + Image preview failed. + +
+
+ ) : ( + // Fixed-aspect frame: placeholder and image share one reserved box so + // the loaded image never resizes the layout (no content shift on load). +
+ {url ? ( + {cleanCaption setLoaded(true)} + onError={handleImageError} + className={cn( + "absolute inset-0 h-full w-full rounded-lg object-contain transition-opacity duration-300 motion-reduce:transition-none", + loaded ? "opacity-100" : "opacity-0", + )} + /> + ) : null} + {!url || !loaded ? ( +
+ {shouldLoad ? ( + <> + + Loading image + + ) : ( + "Image preview will load when visible" + )} +
+ ) : null} +
+ )} +
+ ); + return (

@@ -457,73 +516,43 @@ function DocumentImage({ image }: { image: ImageRow }) { ? ` · ${image.clinicalUseClass.replaceAll("_", " ")}` : ""}

-
- {failed ? ( -
-
- - Image preview failed. - -
-
- ) : ( - // Fixed-aspect frame: placeholder and image share one reserved box so - // the loaded image never resizes the layout (no content shift on load). -
- {url ? ( - {cleanCaption setLoaded(true)} - onError={handleImageError} - className={cn( - "absolute inset-0 h-full w-full rounded-lg object-contain transition-opacity duration-300 motion-reduce:transition-none", - loaded ? "opacity-100" : "opacity-0", - )} - /> - ) : null} - {!url || !loaded ? ( -
- {shouldLoad ? ( - <> - - Loading image - - ) : ( - "Image preview will load when visible" - )} -
+ {hasStructuredTable ? ( + <> +
+ {tableHeading ?

{tableHeading}

: null} + {showImageCaptionLine ?

{cleanCaption}

: null} + + {clinicalUseReasonLine} +
+
+ + + Show source image + +
{imageBlock}
+
+ + ) : ( + <> +
{imageBlock}
+
+ {tableHeading ?

{tableHeading}

: null} + {showImageCaptionLine ?

{cleanCaption}

: null} + {image.tableTextSnippet ? ( +

{image.tableTextSnippet}

) : null} -
- )} -
-
- {tableHeading ?

{tableHeading}

: null} - {showImageCaptionLine ?

{cleanCaption}

: null} - - {!hasStructuredTable && image.tableTextSnippet ? ( -

{image.tableTextSnippet}

- ) : null} - {image.clinicalUseClass && image.clinicalUseClass !== "clinical_evidence" && image.clinicalUseReason ? ( -

{image.clinicalUseReason}

- ) : null} -
+ {clinicalUseReasonLine} + + + )} {displayLabels.length ? (
{displayLabels.map((label) => ( @@ -670,26 +699,14 @@ function PinnedSourceEvidence({ : ""; if (!loading && !chunk) { + // Direct visits (not arrived via a cited answer passage) have nothing to + // pin, so keep this a slim, muted note rather than a prominent empty card. return ( -
-
- - - -
-

Source evidence

-

- Open a cited answer passage to pin the exact indexed excerpt here. -

-
-
+
+

+ + Open a cited answer passage to pin its exact indexed excerpt here. +

); } @@ -1315,49 +1332,51 @@ function PdfCanvasViewer({ url, title, initialPage }: { url: string; title: stri >
- - {pagesReady ? ( - - ) : ( -
- - {error ? "Page unavailable" : "Loading pages"} - {error ? "Unavailable" : "Loading"} -
- )} - -
+
+ + {pagesReady ? ( + + ) : ( +
+ + {error ? "Page unavailable" : "Loading pages"} + {error ? "Unavailable" : "Loading"} +
+ )} + +
+
-

{headerTitle}

{readyDocument ? ( @@ -2626,7 +2607,7 @@ export function DocumentViewer({ className={cn(secondaryButton, "min-h-12 justify-start text-xs")} > - Scope + Add to scope
@@ -2767,23 +2748,22 @@ export function DocumentViewer({
) : signedUrl && document?.file_type === "application/pdf" ? ( <> -
-

- Browser PDF mode keeps heavy-zoom pages crisp and is recommended when zoom quality looks soft. -

+
{useNativePdfViewer ? ( @@ -2866,7 +2846,7 @@ export function DocumentViewer({
-