feat(webui): render image attachments inline

This commit is contained in:
xiaoxixi 2026-07-20 00:11:51 +08:00
parent b043fcf531
commit 4b0d5e41c6
3 changed files with 27 additions and 9 deletions

View File

@ -307,6 +307,9 @@ fn inline_mime_allowed(mime: &str) -> bool {
| "image/jpeg"
| "image/gif"
| "image/webp"
| "image/bmp"
| "image/avif"
| "image/x-icon"
| "audio/mpeg"
| "audio/ogg"
| "audio/wav"
@ -724,6 +727,7 @@ mod tests {
"%E6%8A%A5%E5%91%8A%201.pdf"
);
assert!(inline_mime_allowed("image/png"));
assert!(inline_mime_allowed("image/bmp"));
assert!(!inline_mime_allowed("image/svg+xml"));
assert!(!inline_mime_allowed("text/html"));
}

View File

@ -250,7 +250,7 @@
}
function canPreview(attachment) {
return ["image/png", "image/jpeg", "image/gif", "image/webp"].includes(attachment.mime_type);
return ["image/png", "image/jpeg", "image/gif", "image/webp", "image/bmp", "image/avif", "image/x-icon"].includes(attachment.mime_type);
}
function addFiles(files) {
@ -439,13 +439,20 @@
{#if message.attachments?.length}
<div class="message-attachments">
{#each message.attachments as attachment (`${message.id}:${attachment.index}`)}
<article class="attachment-card">
{#if canPreview(attachment)}
<img src={attachmentUrl(message, attachment, true)} alt={attachment.name} />
{:else}<span class="attachment-icon"></span>{/if}
<article class="image-attachment">
<a class="image-preview-link" href={attachmentUrl(message, attachment)} target="_blank" rel="noreferrer" aria-label={`查看原图 ${attachment.name}`}>
<img class="image-preview" src={attachmentUrl(message, attachment, true)} alt={attachment.name} />
</a>
<div class="attachment-meta"><div><strong>{attachment.name}</strong><small>{attachment.mime_type || attachment.media_type}</small></div><a href={attachmentUrl(message, attachment)} download={attachment.name} aria-label={`下载 ${attachment.name}`}>↓</a></div>
</article>
{:else}
<article class="attachment-card">
<span class="attachment-icon"></span>
<div><strong>{attachment.name}</strong><small>{attachment.mime_type || attachment.media_type}</small></div>
<a href={attachmentUrl(message, attachment)} download={attachment.name} aria-label={`下载 ${attachment.name}`}>↓</a>
</article>
{/if}
{/each}
</div>
{/if}

View File

@ -145,10 +145,17 @@ main { min-width: 0; height: 100vh; display: flex; flex-direction: column; }
.message.user .bubble { background: var(--user-bubble); border-color: var(--accent-border); }
.message-attachments { display: grid; gap: 7px; width: min(100%, 520px); }
.attachment-card { display: grid; grid-template-columns: 42px minmax(0, 1fr) 30px; gap: 9px; align-items: center; padding: 8px; border: 1px solid var(--line); border-radius: 10px; background: var(--panel); }
.attachment-card img, .attachment-icon { width: 42px; height: 42px; border-radius: 7px; object-fit: cover; background: var(--panel-2); display: grid; place-items: center; color: var(--accent); }
.attachment-icon { width: 42px; height: 42px; border-radius: 7px; background: var(--panel-2); display: grid; place-items: center; color: var(--accent); }
.attachment-card strong, .attachment-card small { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.attachment-card strong { font-size: 12px; }.attachment-card small { margin-top: 3px; color: var(--muted); font-size: 9px; }
.attachment-card a { width: 28px; height: 28px; display: grid; place-items: center; border: 1px solid var(--line); border-radius: 7px; color: var(--accent); text-decoration: none; }
.image-attachment { width: min(100%, 680px); overflow: hidden; border: 1px solid var(--line); border-radius: 12px; background: var(--panel); }
.image-preview-link { display: grid; max-height: 520px; overflow: hidden; place-items: center; background: var(--panel-2); }
.image-preview { display: block; max-width: 100%; max-height: 520px; object-fit: contain; }
.attachment-meta { display: grid; grid-template-columns: minmax(0, 1fr) 30px; gap: 9px; align-items: center; padding: 8px 10px; }
.attachment-meta strong, .attachment-meta small { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.attachment-meta strong { font-size: 12px; }.attachment-meta small { margin-top: 3px; color: var(--muted); font-size: 9px; }
.attachment-meta > a { width: 28px; height: 28px; display: grid; place-items: center; border: 1px solid var(--line); border-radius: 7px; color: var(--accent); text-decoration: none; }
.typing .bubble { color: var(--muted); }
.active-turn .message-content { width: min(82%, 760px); }
.streaming { border-color: var(--accent-border); }