diff --git a/src/components/AccessibleTable.tsx b/src/components/AccessibleTable.tsx
index b9e1a44fe..3afe28f49 100644
--- a/src/components/AccessibleTable.tsx
+++ b/src/components/AccessibleTable.tsx
@@ -288,6 +288,29 @@ function useMobileTableExpansion(enabledByDefault: boolean) {
return enabledByDefault && isMobile;
}
+// Mirrors the component's `normalized` computation so callers can decide layout
+// (e.g. whether to collapse a source image) using the exact same parse/normalize
+// rules AccessibleTable renders with. Returns false when the table would render
+// nothing — columns-only input, unparseable markdown, or an all-metadata grid.
+export function hasRenderableAccessibleTable({
+ markdown,
+ rows,
+ columns,
+ clinicalOnly = false,
+}: {
+ markdown?: string | null;
+ rows?: string[][] | null;
+ columns?: string[] | null;
+ clinicalOnly?: boolean;
+}): boolean {
+ const hasExplicitRows = Boolean(rows?.length);
+ const parsed = hasExplicitRows ? rows : parseMarkdownTable(markdown);
+ if (!parsed?.length) return false;
+ const table = normalizeAccessibleTable(parsed, hasExplicitRows ? columns : null);
+ if (!table) return false;
+ return Boolean(clinicalOnly ? clinicalOnlyTable(table) : table);
+}
+
export function AccessibleTable({
caption,
markdown,
diff --git a/src/components/DocumentViewer.tsx b/src/components/DocumentViewer.tsx
index 28a40eafd..7c46aa2ad 100644
--- a/src/components/DocumentViewer.tsx
+++ b/src/components/DocumentViewer.tsx
@@ -17,7 +17,6 @@ import {
FileText,
Loader2,
Maximize2,
- Menu,
Minimize2,
Minus,
Plus,
@@ -33,8 +32,9 @@ import {
X,
} from "lucide-react";
import { type FormEvent, useEffect, useMemo, useRef, useState } from "react";
-import { AccessibleTable } from "@/components/AccessibleTable";
-import { documentDisplayTitle } from "@/components/DocumentOrganizationBadges";
+import { AccessibleTable, hasRenderableAccessibleTable } from "@/components/AccessibleTable";
+import { documentDisplayTitle, documentOrganizationProfile } from "@/components/DocumentOrganizationBadges";
+import { formatDocumentLabelDisplay } from "@/lib/document-tags";
import {
DocumentActionAnchor,
DocumentActionButton,
@@ -437,7 +437,15 @@ function DocumentImage({ image }: { image: ImageRow }) {
: looksLikeTableText(image.tableTextSnippet)
? image.tableTextSnippet
: null;
- const hasStructuredTable = Boolean(tableMarkdown || image.tableRows?.length || image.tableColumns?.length);
+ // Only let the table "lead" (collapsing the source image) when AccessibleTable
+ // will actually render a table. Columns-only input or unparseable markdown
+ // render nothing, so those route to the image-first branch instead of leaving
+ // an empty caption above a hidden source image.
+ const hasStructuredTable = hasRenderableAccessibleTable({
+ markdown: tableMarkdown,
+ rows: image.tableRows,
+ columns: image.tableColumns,
+ });
const tableCaption = tableHeading || cleanCaption || "Document table";
const showImageCaptionLine = cleanCaption && cleanCaption !== tableCaption;
const displayLabels = smartEvidenceTags(
@@ -447,6 +455,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).
+
- ) : (
- // Fixed-aspect frame: placeholder and image share one reserved box so
- // the loaded image never resizes the layout (no content shift on load).
-
{displayLabels.map((label) => (
@@ -670,26 +708,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.
+