diff --git a/apps/web/src/pages/library-page.tsx b/apps/web/src/pages/library-page.tsx index 14e26c6b5..e53bf2235 100644 --- a/apps/web/src/pages/library-page.tsx +++ b/apps/web/src/pages/library-page.tsx @@ -32,6 +32,7 @@ import { ArtifactRenderer, artifactMatchesLibraryKindSegment, filterArtifacts, + isTextDecodableMediaType, libraryArtifactIdFromPath, libraryKindSegmentFromPath, resolveArtifactRendererKind, @@ -84,6 +85,7 @@ import { LIBRARY_BULK_OPERATION_IDS, mapArtifactListToSummaries, uploadArtifactFiles, + uploadMimeTypeFromSource, } from "../shell/library-artifacts"; import { StageTopBar } from "../shell/stage-top-bar"; @@ -250,6 +252,19 @@ function PreviewPane({ detail !== null && rendererKind === "html" && tenantId !== null ? artifactPreviewPath(tenantId, detail.id) : undefined; + // Empty `content` on a file artifact is ambiguous on its own: it's the + // honest "nothing here" for an inline-content artifact, but it's also + // what a real upload's row carries when its bytes live out-of-band and + // aren't text-decodable (an image, a real PDF, a legacy `.docx`/`.xlsx`). + // `source.upload.mimeType` disambiguates — present only when this + // artifact really does have stored bytes behind it. + const uploadMimeType = + detail !== null ? uploadMimeTypeFromSource(detail.source) : null; + const contentUnavailable = + detail !== null && + detail.content === "" && + uploadMimeType !== null && + !isTextDecodableMediaType(uploadMimeType); return (