fix: serve images via FastAPI endpoint to fix dialog preview

NiceGUI's ui.image with a local file path fails to register static
files when inside a ui.dialog, showing alt text instead of the image.
Added /api/image-preview?path=... endpoint that streams the file via
FileResponse, and updated frame path thumbnails to use this URL.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-04 11:54:50 +02:00
parent 783f07e57a
commit 783da171e7
2 changed files with 13 additions and 2 deletions
+9
View File
@@ -9,6 +9,7 @@ from pathlib import Path
from typing import Any from typing import Any
from fastapi import HTTPException, Query from fastapi import HTTPException, Query
from fastapi.responses import FileResponse
from nicegui import app from nicegui import app
from db import ProjectDB from db import ProjectDB
@@ -30,6 +31,7 @@ def register_api_routes(db: ProjectDB) -> None:
app.add_api_route("/api/projects/{name}/files/{file_name}/sequences", _list_sequences, methods=["GET"]) app.add_api_route("/api/projects/{name}/files/{file_name}/sequences", _list_sequences, methods=["GET"])
app.add_api_route("/api/projects/{name}/files/{file_name}/data", _get_data, methods=["GET"]) app.add_api_route("/api/projects/{name}/files/{file_name}/data", _get_data, methods=["GET"])
app.add_api_route("/api/projects/{name}/files/{file_name}/keys", _get_keys, methods=["GET"]) app.add_api_route("/api/projects/{name}/files/{file_name}/keys", _get_keys, methods=["GET"])
app.add_api_route("/api/image-preview", _serve_image, methods=["GET"])
def _get_db() -> ProjectDB: def _get_db() -> ProjectDB:
@@ -102,3 +104,10 @@ def _get_keys(name: str, file_name: str, seq: int = Query(default=1)) -> dict[st
logger.info("API _get_keys %s/%s seq=%d (%d keys): %.3fs", logger.info("API _get_keys %s/%s seq=%d (%d keys): %.3fs",
name, file_name, seq, len(keys), time.perf_counter() - t0) name, file_name, seq, len(keys), time.perf_counter() - t0)
return {"keys": keys, "types": types, "total_sequences": total} return {"keys": keys, "types": types, "total_sequences": total}
def _serve_image(path: str = Query(...)) -> FileResponse:
p = Path(path)
if not p.exists() or not p.is_file():
raise HTTPException(status_code=404, detail="Image not found")
return FileResponse(str(p))
+4 -2
View File
@@ -6,6 +6,7 @@ import math
import random import random
import time import time
from pathlib import Path from pathlib import Path
from urllib.parse import quote
from nicegui import ui from nicegui import ui
@@ -571,9 +572,10 @@ def _render_sequence_card(i, seq, batch_list, data, file_path, state,
img_path = Path(seq.get(img_key, '')) if seq.get(img_key) else None img_path = Path(seq.get(img_key, '')) if seq.get(img_key) else None
if (img_path and img_path.exists() and if (img_path and img_path.exists() and
img_path.suffix.lower() in IMAGE_EXTENSIONS): img_path.suffix.lower() in IMAGE_EXTENSIONS):
img_url = f'/api/image-preview?path={quote(str(img_path))}'
with ui.dialog() as img_dlg, ui.card().style('max-width:90vw'): with ui.dialog() as img_dlg, ui.card().style('max-width:90vw'):
ui.image(str(img_path)).style('max-width:80vw; max-height:80vh') ui.image(img_url).style('max-width:80vw; max-height:80vh')
ui.image(str(img_path)).style( ui.image(img_url).style(
'width:36px; height:36px; object-fit:cover;' 'width:36px; height:36px; object-fit:cover;'
' border-radius:4px; cursor:pointer; flex-shrink:0' ' border-radius:4px; cursor:pointer; flex-shrink:0'
).on('click', img_dlg.open) ).on('click', img_dlg.open)