-
+ {document.hasThumbnail && !thumbFailed ? (
+ // Leading-edge preview tile. `loading="lazy"` + the timeline's
+ // virtualization means only cards near the viewport fetch their
+ // thumbnail. Decorative (alt="") — the title beside it names the
+ // document; an authed same-origin subresource, never cached
+ // cross-user. onError falls back to the kind icon.
+
+ {/* Authed, private (no-store) same-origin subresource — next/image
+ would try to proxy/optimize it, which is wrong for a PHI blob
+ we deliberately never cache. */}
+ {/* eslint-disable-next-line @next/next/no-img-element */}
+
setThumbFailed(true)}
+ className="size-full object-cover"
+ />
+
+ ) : (
+
+ )}
{title}
{document.conditionLinks.length > 0 ||
- document.servingClass === "attachment" ||
- document.hasContentIndex ? (
+ document.servingClass === "attachment" ? (
{document.conditionLinks.map((link) => (
) : null}
- {document.hasContentIndex ? (
- // Subtle searchable marker — icon-only to keep the row calm; the
- // label rides an accessible name. An AI-read document gets the
- // highlighted Sparkles (primary) so the richer read is legible at
- // a glance; a locally-indexed one keeps the quiet muted glass.
- isAiReadSource(document.contentIndexSource) ? (
-
-
-
- {t("documents.card.aiReadBadge")}
-
-
- ) : (
-
-
-
- {t("documents.card.searchableBadge")}
-
-
- )
- ) : null}
) : null}
diff --git a/src/components/documents/document-filter-bar.tsx b/src/components/documents/document-filter-bar.tsx
index 74312ce90..9febb0244 100644
--- a/src/components/documents/document-filter-bar.tsx
+++ b/src/components/documents/document-filter-bar.tsx
@@ -10,9 +10,8 @@
* The controls sit on ONE row at every width: the search flexes and can
* shrink to nothing while each dropdown trigger stays a fixed compact
* control, so the bar never wraps to a second line and never grows a
- * horizontal chip scroller. The content-search hint (indexed-content match
- * + corpus backfill) rides its own helper line below, only when indexing is
- * live.
+ * horizontal chip scroller. The corpus-backfill prompt rides its own helper
+ * line below, only while some documents remain un-indexed.
*
* Purely presentational — the filter state lives in the page URL and is
* owned by `documents-view.tsx`; this bar only renders the controls. It
@@ -82,7 +81,6 @@ export function DocumentFilterBar({
onToggleYear,
activeCount,
onClearAll,
- contentSearchActive = false,
showIndexAll = false,
indexAllPending = false,
onIndexAll,
@@ -101,8 +99,6 @@ export function DocumentFilterBar({
onToggleYear: (year: number) => void;
activeCount: number;
onClearAll: () => void;
- /** Search already matches indexed document CONTENT (whole words). */
- contentSearchActive?: boolean;
/** Some documents are not yet indexed — offer the corpus backfill. */
showIndexAll?: boolean;
indexAllPending?: boolean;
@@ -323,32 +319,28 @@ export function DocumentFilterBar({
) : null}
- {contentSearchActive || showIndexAll ? (
+ {showIndexAll ? (