---
title: "官方物料 cards — bottom-left filename overlay"
type: quick-work
date: 2026-06-03
---

# 官方物料 cards — bottom-left filename overlay

## Task
In every content card classified as 官方物料 (`categorySlug === "official-assets"`), display the source filename at the bottom-left of the card — image filename for image bubbles, video filename for video bubbles.

## Data Source — Confirmed
- Endpoint: `/api/posts` (list, called by `src/components/messageStream/hooks/usePostStream.ts:89`) and `/api/posts/:id` (single, used by `MessageStream` deep-links).
- Field: `Post.attachments[*].filename` (`src/types/post.ts`).
- Category gate: `Post.categorySlug === "official-assets"` (also referenced in `src/lib/categorySvgSlug.ts:13`, `src/pages/Categories/index.tsx:23`, `src/pages/Home/index.tsx:33`).
- No additional API call is needed — `filename` already ships with the post payload.

## Changes
- `src/components/messageStream/AttachmentFilenameLabel.tsx` (new) — small dark pill overlay using `filenameWithExtension(att.filename, att.mime)`, positioned `absolute bottom-2 left-2`, with `pointer-events-none`, `max-w-[calc(100%-1rem)]`, `truncate`, and a `title` attribute so the full filename is available on hover. Style mirrors the `AttachmentDownloadPill` (`bg-black/80` + `ring-white/20` + `backdrop-blur-md`) for visual consistency.
- `src/components/messageStream/bubbles/SingleImageFrame.tsx` — accepts new `showFilename?: boolean` prop; when true, renders `AttachmentFilenameLabel` alongside the existing top-left download pill.
- `src/components/messageStream/bubbles/ImageBubble.tsx` — passes `showFilename={post.categorySlug === "official-assets"}`.
- `src/components/messageStream/bubbles/ImageWithTextBubble.tsx` — same gate, threaded through `SingleImageFrame`.
- `src/components/messageStream/bubbles/AlbumBubble.tsx` — when category matches, renders the filename label inside each visible tile (skipping the `+N` overflow tile so its overlay stays clean).
- `src/components/messageStream/bubbles/VideoBubble.tsx` — `VideoAttachmentCard` accepts `showFilename`, threaded into both the multi-video grid layout and the single-video layout. Skipped on the `+N` overflow tile.

## Verification
- `npx tsc --noEmit` — clean.
- `npm run format` then `npm run format:check` — clean.
- `npm test` — 49/49 passing.
- Visual confirmation pending on a posts feed that contains `official-assets` content.

## Notes
- Reuses existing `filenameWithExtension` util so attachments without an extension still get a sensible label from their MIME type.
- The label is `pointer-events-none` so it never blocks the bubble's own tap target (open lightbox / play video).
- Only single/album/video bubbles surface this. `FileDocBubble` already renders the filename inline, so no overlay is needed there.
- If a future requirement adds more "show filename" categories, switch `showFilename` from a boolean gate to a derived util (e.g. `shouldShowAttachmentFilename(post)`).
