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 ? (