feat(webui): render image attachments inline
This commit is contained in:
parent
b043fcf531
commit
4b0d5e41c6
@ -307,6 +307,9 @@ fn inline_mime_allowed(mime: &str) -> bool {
|
|||||||
| "image/jpeg"
|
| "image/jpeg"
|
||||||
| "image/gif"
|
| "image/gif"
|
||||||
| "image/webp"
|
| "image/webp"
|
||||||
|
| "image/bmp"
|
||||||
|
| "image/avif"
|
||||||
|
| "image/x-icon"
|
||||||
| "audio/mpeg"
|
| "audio/mpeg"
|
||||||
| "audio/ogg"
|
| "audio/ogg"
|
||||||
| "audio/wav"
|
| "audio/wav"
|
||||||
@ -724,6 +727,7 @@ mod tests {
|
|||||||
"%E6%8A%A5%E5%91%8A%201.pdf"
|
"%E6%8A%A5%E5%91%8A%201.pdf"
|
||||||
);
|
);
|
||||||
assert!(inline_mime_allowed("image/png"));
|
assert!(inline_mime_allowed("image/png"));
|
||||||
|
assert!(inline_mime_allowed("image/bmp"));
|
||||||
assert!(!inline_mime_allowed("image/svg+xml"));
|
assert!(!inline_mime_allowed("image/svg+xml"));
|
||||||
assert!(!inline_mime_allowed("text/html"));
|
assert!(!inline_mime_allowed("text/html"));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -250,7 +250,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function canPreview(attachment) {
|
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) {
|
function addFiles(files) {
|
||||||
@ -439,13 +439,20 @@
|
|||||||
{#if message.attachments?.length}
|
{#if message.attachments?.length}
|
||||||
<div class="message-attachments">
|
<div class="message-attachments">
|
||||||
{#each message.attachments as attachment (`${message.id}:${attachment.index}`)}
|
{#each message.attachments as attachment (`${message.id}:${attachment.index}`)}
|
||||||
<article class="attachment-card">
|
|
||||||
{#if canPreview(attachment)}
|
{#if canPreview(attachment)}
|
||||||
<img src={attachmentUrl(message, attachment, true)} alt={attachment.name} />
|
<article class="image-attachment">
|
||||||
{:else}<span class="attachment-icon">▧</span>{/if}
|
<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>
|
<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>
|
<a href={attachmentUrl(message, attachment)} download={attachment.name} aria-label={`下载 ${attachment.name}`}>↓</a>
|
||||||
</article>
|
</article>
|
||||||
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@ -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.user .bubble { background: var(--user-bubble); border-color: var(--accent-border); }
|
||||||
.message-attachments { display: grid; gap: 7px; width: min(100%, 520px); }
|
.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 { 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, .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 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; }
|
.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); }
|
.typing .bubble { color: var(--muted); }
|
||||||
.active-turn .message-content { width: min(82%, 760px); }
|
.active-turn .message-content { width: min(82%, 760px); }
|
||||||
.streaming { border-color: var(--accent-border); }
|
.streaming { border-color: var(--accent-border); }
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user