mirror of
https://github.com/open-webui/open-webui.git
synced 2026-08-13 01:02:25 -06:00
fix: show a placeholder when an image cannot be loaded (#27730)
A message referencing a file that no longer exists rendered a broken image, because nothing anywhere noticed the failed load. What the user saw depended on the caller's alt text: in a response the alt is the message content, so the entire reply was rendered inside the image frame, and the preview still opened full-screen on a dead image. Image.svelte now tracks a failed load and renders an 'Image unavailable' placeholder instead, suppressing the preview for a source that cannot be shown. The failed state resets when the source changes, so a replaced or corrected URL is retried. An onError callback lets a caller react without changing behaviour for the eight existing call sites, which are untouched. Responses now pass the file name (or 'Generated Image') as alt rather than the message content, which was never a description of the image.
This commit is contained in:
@@ -690,7 +690,7 @@
|
||||
{#each message.files.filter((f) => ['image', 'file'].includes(f.type)) as file}
|
||||
<div>
|
||||
{#if file.type === 'image' || (file?.content_type ?? '').startsWith('image/')}
|
||||
<Image src={file.url} alt={message.content} />
|
||||
<Image src={file.url} alt={file.name || $i18n.t('Generated Image')} />
|
||||
{:else}
|
||||
<FileItem
|
||||
item={file}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import { settings } from '$lib/stores';
|
||||
import ImagePreview from './ImagePreview.svelte';
|
||||
import XMark from '$lib/components/icons/XMark.svelte';
|
||||
import Photo from '$lib/components/icons/Photo.svelte';
|
||||
import { getContext } from 'svelte';
|
||||
|
||||
export let src = '';
|
||||
@@ -18,27 +19,62 @@
|
||||
export let dismissible = false;
|
||||
export let onDismiss = () => {};
|
||||
|
||||
/** Called when the image fails to load, e.g. its file has since been deleted. */
|
||||
export let onError: () => void = () => {};
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
let _src = '';
|
||||
$: _src = safeImageUrl(src.startsWith('/') ? `${WEBUI_BASE_URL}${src}` : src, allowExternal);
|
||||
|
||||
let showImagePreview = false;
|
||||
|
||||
let failed = false;
|
||||
let attemptedSrc = '';
|
||||
$: if (_src !== attemptedSrc) {
|
||||
attemptedSrc = _src;
|
||||
failed = false;
|
||||
}
|
||||
|
||||
const handleError = () => {
|
||||
failed = true;
|
||||
showImagePreview = false;
|
||||
onError();
|
||||
};
|
||||
</script>
|
||||
|
||||
<ImagePreview bind:show={showImagePreview} src={_src} {alt} />
|
||||
{#if !failed}
|
||||
<ImagePreview bind:show={showImagePreview} src={_src} {alt} />
|
||||
{/if}
|
||||
|
||||
<div class=" relative group w-fit flex items-center">
|
||||
<button
|
||||
class={className}
|
||||
on:click={() => {
|
||||
showImagePreview = true;
|
||||
}}
|
||||
aria-label={$i18n.t('Show image preview')}
|
||||
type="button"
|
||||
>
|
||||
<img src={_src} {alt} class={imageClassName} draggable="false" data-cy="image" />
|
||||
</button>
|
||||
{#if failed}
|
||||
<div
|
||||
class="{imageClassName} flex items-center gap-2 px-3 py-2.5 border border-dashed border-gray-200 dark:border-gray-800 text-gray-400 dark:text-gray-600"
|
||||
data-cy="image-unavailable"
|
||||
>
|
||||
<Photo className="size-4 shrink-0" strokeWidth="1.5" />
|
||||
<span class="text-xs">{$i18n.t('Image unavailable')}</span>
|
||||
</div>
|
||||
{:else}
|
||||
<button
|
||||
class={className}
|
||||
on:click={() => {
|
||||
showImagePreview = true;
|
||||
}}
|
||||
aria-label={$i18n.t('Show image preview')}
|
||||
type="button"
|
||||
>
|
||||
<img
|
||||
src={_src}
|
||||
{alt}
|
||||
class={imageClassName}
|
||||
draggable="false"
|
||||
data-cy="image"
|
||||
on:error={handleError}
|
||||
/>
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
{#if dismissible}
|
||||
<div class=" absolute -top-1 -right-1">
|
||||
|
||||
@@ -1406,6 +1406,7 @@
|
||||
"Image Prompt Generation": "",
|
||||
"Image Prompt Generation Prompt": "",
|
||||
"Image Size": "",
|
||||
"Image unavailable": "",
|
||||
"image/*, video/*": "",
|
||||
"Images": "",
|
||||
"Import": "",
|
||||
|
||||
Reference in New Issue
Block a user