From 3254e44281b2be02c25a1838ecf1d2601771c0ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Wed, 12 Aug 2026 21:09:11 +0200 Subject: [PATCH] fix(documents): keep the read action visible for an unread document under auto-read The AI section of the document detail sheet collapsed whenever auto-read was on and nothing was pending review, without checking whether the document actually has a content index. A document the automatic pass never reached (uploaded before the preference, or the pass did not complete) then had NO visible way to be read; turning auto-read off was the only workaround. Collapse only once the document is read (or when indexing is unavailable on the transport, where no action could be offered anyway). An unread document keeps the Read with AI action, introduced by a short honest note in all six locales; the suggest and summarise extras stay hidden because the auto-read promise still covers them. The three states stay distinct: unread shows the note plus the action, an in-flight read shows the reading label, a read document collapses the block. Refs #777 --- messages/de.json | 3 +- messages/en.json | 3 +- messages/es.json | 3 +- messages/fr.json | 3 +- messages/it.json | 3 +- messages/pl.json | 3 +- .../__tests__/document-ai-section.test.tsx | 48 ++++++++++++++++--- .../documents/document-ai-section.tsx | 48 +++++++++++++++++-- 8 files changed, 96 insertions(+), 18 deletions(-) diff --git a/messages/de.json b/messages/de.json index a7340279a..9386be584 100644 --- a/messages/de.json +++ b/messages/de.json @@ -9579,7 +9579,8 @@ "readDone": "Die KI hat dein Dokument gelesen.", "read": "Mit KI auslesen", "reread": "Erneut lesen", - "reading": "Liest…" + "reading": "Liest…", + "notReadYet": "Dieses Dokument wurde noch nicht gelesen." }, "contentIndex": { "indexPrompt": "Dokumentinhalte durchsuchbar machen.", diff --git a/messages/en.json b/messages/en.json index 953e6a93d..18df41bfc 100644 --- a/messages/en.json +++ b/messages/en.json @@ -9579,7 +9579,8 @@ "readDone": "The AI read your document.", "read": "Read with AI", "reread": "Read again", - "reading": "Reading…" + "reading": "Reading…", + "notReadYet": "This document has not been read yet." }, "contentIndex": { "indexPrompt": "Make document contents searchable.", diff --git a/messages/es.json b/messages/es.json index 07bd9f645..5bc566a52 100644 --- a/messages/es.json +++ b/messages/es.json @@ -9579,7 +9579,8 @@ "readDone": "La IA leyó tu documento.", "read": "Leer con IA", "reread": "Leer de nuevo", - "reading": "Leyendo…" + "reading": "Leyendo…", + "notReadYet": "Este documento aún no se ha leído." }, "contentIndex": { "indexPrompt": "Haz que el contenido de los documentos sea buscable.", diff --git a/messages/fr.json b/messages/fr.json index d9d598454..def066936 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -9579,7 +9579,8 @@ "readDone": "L’IA a lu votre document.", "read": "Lire avec l’IA", "reread": "Relire", - "reading": "Lecture…" + "reading": "Lecture…", + "notReadYet": "Ce document n’a pas encore été lu." }, "contentIndex": { "indexPrompt": "Rendre le contenu des documents consultable.", diff --git a/messages/it.json b/messages/it.json index bc486c3f8..c3fe2cfe2 100644 --- a/messages/it.json +++ b/messages/it.json @@ -9579,7 +9579,8 @@ "readDone": "L’IA ha letto il tuo documento.", "read": "Leggi con l’IA", "reread": "Leggi di nuovo", - "reading": "Lettura…" + "reading": "Lettura…", + "notReadYet": "Questo documento non è ancora stato letto." }, "contentIndex": { "indexPrompt": "Rendi ricercabile il contenuto dei documenti.", diff --git a/messages/pl.json b/messages/pl.json index dcf8cf7b1..c8bf4c825 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -9579,7 +9579,8 @@ "readDone": "AI odczytało Twój dokument.", "read": "Odczytaj z AI", "reread": "Odczytaj ponownie", - "reading": "Odczytywanie…" + "reading": "Odczytywanie…", + "notReadYet": "Ten dokument nie został jeszcze odczytany." }, "contentIndex": { "indexPrompt": "Udostępnij treść dokumentów do wyszukiwania.", diff --git a/src/components/documents/__tests__/document-ai-section.test.tsx b/src/components/documents/__tests__/document-ai-section.test.tsx index e8768e73d..9eecdbefb 100644 --- a/src/components/documents/__tests__/document-ai-section.test.tsx +++ b/src/components/documents/__tests__/document-ai-section.test.tsx @@ -159,16 +159,50 @@ describe("", () => { expect(html).not.toContain('data-slot="document-read-ai"'); }); - it("hides the manual AI action row when auto-read is on, keeping status", () => { - const html = render(base({ aiEnabled: true, autoReadEnabled: true })); - // Auto-read reads on upload — the manual per-document actions are gone. - expect(html).not.toContain('data-slot="document-read-ai"'); + it("collapses entirely when auto-read is on and the document is read", () => { + const html = render( + base({ + aiEnabled: true, + autoReadEnabled: true, + hasContentIndex: true, + contentIndexSource: "vision", + }), + ); + // v1.28.22 — with auto-read ON, the document read, and nothing pending + // the WHOLE block collapses: no chrome, no status pill; the sheet content + // starts straight with the document fields under the preview. + expect(html).toBe(""); + }); + + it("keeps the read action for an unread document even with auto-read on", () => { + // Auto-read promises reading on upload — but a document with no content + // index never got that read (it may predate the preference, or the pass + // did not complete). Collapsing here would leave the document with no + // path to a read at all; the recovery action must stay visible. + const html = render( + base({ aiEnabled: true, autoReadEnabled: true, hasContentIndex: false }), + ); + expect(html).toContain('data-slot="document-read-ai"'); + expect(html).toContain("Read with AI"); + expect(html).toContain('data-slot="document-unread-note"'); + expect(html).toContain("This document has not been read yet."); + // The auto-read promise still covers the extras: no suggest / summarise. expect(html).not.toContain('data-slot="assist-suggest"'); expect(html).not.toContain("Summarise"); expect(html).not.toContain("Show extracted text"); - // v1.28.22 — with auto-read ON and nothing pending the WHOLE block - // collapses: no chrome, no status pill; the sheet content starts straight - // with the document fields under the preview. + }); + + it("still collapses an unread document when indexing is unavailable", () => { + // No transport can index here, so there is no action to recover with — + // an empty chrome box would be the only thing left to render. + const html = render( + base({ + aiEnabled: true, + autoReadEnabled: true, + hasContentIndex: false, + indexEnabled: false, + }), + ); expect(html).toBe(""); }); diff --git a/src/components/documents/document-ai-section.tsx b/src/components/documents/document-ai-section.tsx index 7497ae834..0636fcc5e 100644 --- a/src/components/documents/document-ai-section.tsx +++ b/src/components/documents/document-ai-section.tsx @@ -17,8 +17,13 @@ * - "Suggest details" drafts a title / type / date to review before saving. * - "Summarise" / "Show text" surface a transient, session-only read-out. * - * With auto-read ON and nothing pending to review the block collapses entirely — - * the sheet content starts straight with the document fields under the preview. + * With auto-read ON, the document already read, and nothing pending to review + * the block collapses entirely — the sheet content starts straight with the + * document fields under the preview. An UNREAD document keeps the read action + * visible whatever the preference says: auto-read is a promise that reading + * happens on upload, and a document the automatic pass never reached (it may + * predate the preference, or the pass did not complete) would otherwise have + * no path to a read at all. * * Presentational: the sheet owns the mutations + capability probe and passes * their state + handlers down. When `aiEnabled` is false the actions collapse to @@ -117,12 +122,16 @@ export function DocumentAiSection({ // v1.28.22 — the block carries NO header/status chrome any more (the // provenance pill moved to the vault card's meta row; auto-read makes an - // in-sheet "read by AI" banner redundant). With auto-read ON and nothing - // pending to review, nothing below would render — collapse the whole box so - // the content under the preview starts straight with the fields. + // in-sheet "read by AI" banner redundant). With auto-read ON, the document + // read, and nothing pending to review, nothing below would render — + // collapse the whole box so the content under the preview starts straight + // with the fields. An unindexed document does NOT collapse (unless indexing + // is unavailable on this transport): it still needs its read, and this + // section is the only place that action lives. if ( aiEnabled && autoReadEnabled && + (hasContentIndex || !indexEnabled) && !suggestErrorKey && !suggestion && !summaryOutput @@ -191,6 +200,35 @@ export function DocumentAiSection({ {t("documents.summary.showText")} + ) : !hasContentIndex && indexEnabled ? ( + // Auto-read is ON but this document has no content index: the + // automatic pass never reached it or did not complete. The + // suggest / summarise extras stay hidden (auto-read's promise + // covers them), but the read itself must stay reachable. +
+

+ {t("documents.ai.notReadYet")} +

+ +
) : null} {suggestErrorKey ? (