feat(media): show the full caption under the media in the viewer (#164)
A captioned image/video (MSC2530: `filename` set and `body` differs) put the caption in the header, truncated to one line, so a long caption was unreadable. The header now shows the file name; the caption is shown in full under the media (wrapped, scrolls past 25vh). The image alt text uses the caption, and Download now saves under the real file name instead of the caption. Verified in Chromium: header "lake-sunset.png", the 150-character caption fully visible below the image. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
ea07fb5087
commit
c088b4c0e6
@@ -106,6 +106,10 @@ export type LightboxItem = {
|
||||
mimeType?: string;
|
||||
msgtype: MsgType.Image | MsgType.Video;
|
||||
body: string;
|
||||
/** The file's name when the message also carries a caption (MSC2530). */
|
||||
filename?: string;
|
||||
/** Caption text: `body` when it differs from `filename`. */
|
||||
caption?: string;
|
||||
sender: string;
|
||||
ts: number;
|
||||
eventId: string;
|
||||
@@ -127,12 +131,19 @@ export function toLightboxItems(room: Room, events: MatrixEvent[]): LightboxItem
|
||||
.map((ev) => {
|
||||
const c = ev.getContent();
|
||||
const isEnc = !!c.file;
|
||||
const filename = typeof c.filename === 'string' && c.filename ? c.filename : undefined;
|
||||
const caption =
|
||||
filename && typeof c.body === 'string' && c.body && c.body !== filename
|
||||
? c.body
|
||||
: undefined;
|
||||
return {
|
||||
mxcUrl: c.file?.url ?? c.url ?? '',
|
||||
encInfo: isEnc ? c.file : undefined,
|
||||
mimeType: c.info?.mimetype,
|
||||
msgtype: c.msgtype as MsgType.Image | MsgType.Video,
|
||||
body: c.body ?? '',
|
||||
filename,
|
||||
caption,
|
||||
sender: getSenderName(room, ev.getSender() ?? ''),
|
||||
ts: ev.getTs(),
|
||||
eventId: ev.getId() ?? '',
|
||||
@@ -209,7 +220,7 @@ function LightboxMedia({
|
||||
) : (
|
||||
<img
|
||||
src={media.url}
|
||||
alt={item.body}
|
||||
alt={item.caption ?? item.body}
|
||||
draggable={false}
|
||||
onMouseDown={onMouseDown}
|
||||
onTouchStart={onTouchStart}
|
||||
@@ -356,7 +367,7 @@ export function Lightbox({
|
||||
>
|
||||
<Box grow="Yes" direction="Column" style={{ overflow: 'hidden' }}>
|
||||
<Text size="T400" truncate style={{ color: '#fff', fontWeight: 500 }}>
|
||||
{item.body || (item.msgtype === MsgType.Video ? 'Video' : 'Image')}
|
||||
{item.filename || item.body || (item.msgtype === MsgType.Video ? 'Video' : 'Image')}
|
||||
</Text>
|
||||
<Text size="T200" style={{ color: 'rgba(255,255,255,0.5)' }}>
|
||||
{item.sender} · {dateStr}
|
||||
@@ -412,7 +423,7 @@ export function Lightbox({
|
||||
{(ref) => (
|
||||
<span ref={ref}>
|
||||
<FileDownloadButton
|
||||
filename={mediaFilename(item.body, item.mimeType)}
|
||||
filename={mediaFilename(item.filename ?? item.body, item.mimeType)}
|
||||
url={item.mxcUrl}
|
||||
mimeType={item.mimeType ?? 'application/octet-stream'}
|
||||
encInfo={item.encInfo}
|
||||
@@ -515,6 +526,32 @@ export function Lightbox({
|
||||
</IconButton>
|
||||
)}
|
||||
</Box>
|
||||
{item.caption && (
|
||||
// [Gitea #164] The full caption, readable — the header only has room
|
||||
// for one truncated line.
|
||||
<Box
|
||||
shrink="No"
|
||||
justifyContent="Center"
|
||||
style={{
|
||||
padding: `${config.space.S200} ${config.space.S400} ${config.space.S400}`,
|
||||
maxHeight: '25vh',
|
||||
overflowY: 'auto',
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
size="T300"
|
||||
style={{
|
||||
color: 'rgba(255,255,255,0.88)',
|
||||
whiteSpace: 'pre-wrap',
|
||||
overflowWrap: 'anywhere',
|
||||
maxWidth: '70ch',
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
{item.caption}
|
||||
</Text>
|
||||
</Box>
|
||||
)}
|
||||
</div>
|
||||
</FocusTrap>
|
||||
</Overlay>
|
||||
|
||||
Reference in New Issue
Block a user