Lazy load character artwork
This commit is contained in:
parent
2bb01de94a
commit
08a6544661
@ -5,6 +5,11 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi
|
||||
|
||||
---
|
||||
|
||||
## [1.20.41] — 2026-08-19
|
||||
|
||||
### Changed
|
||||
- **Character portraits and concept art now lazy-load.** Off-screen images no longer load all at once when large cast lists are opened.
|
||||
|
||||
## [1.20.40] — 2026-08-19
|
||||
|
||||
### Added
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<meta name="theme-color" content="#2563EB">
|
||||
<meta name="app-version" content="1.20.40">
|
||||
<meta name="app-version" content="1.20.41">
|
||||
<link rel="manifest" href="/manifest.webmanifest">
|
||||
<link rel="icon" href="/static/icon.svg" type="image/svg+xml">
|
||||
<link rel="apple-touch-icon" href="/static/icon.svg">
|
||||
@ -27,7 +27,7 @@
|
||||
|
||||
<!-- ── Core styles (local — no CDN dependency for first paint) ────────── -->
|
||||
<link rel="stylesheet" href="/static/vendor/mdi/materialdesignicons.min.css">
|
||||
<link rel="stylesheet" href="/static/style.css?v=1.20.40">
|
||||
<link rel="stylesheet" href="/static/style.css?v=1.20.41">
|
||||
|
||||
|
||||
<!-- ── Flag icons — non-blocking (loaded async, icons appear after JS) ── -->
|
||||
@ -378,7 +378,7 @@ window.toggleNavTree = function(treeId, chevronId) {
|
||||
</script>
|
||||
|
||||
<!-- loader.js: fetches sections → loads JS modules → removes skeleton -->
|
||||
<script src="/static/loader.js?v=1.20.40"></script>
|
||||
<script src="/static/loader.js?v=1.20.41"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -951,7 +951,7 @@ function _charsTableHtml(chars, sortMode, sortDir) {
|
||||
const pct = score != null ? Math.max(0, Math.min(100, score)) : null;
|
||||
const tagList = String(rec.tags || '').split(',').map(function (t) { return t.trim(); }).filter(Boolean);
|
||||
const avatarInner = rec.image
|
||||
? '<img src="' + rec.image + '" alt="' + escHtml(rec.name) + '" style="width:100%;height:100%;object-fit:cover;border-radius:50%">'
|
||||
? '<img loading="lazy" decoding="async" src="' + rec.image + '" alt="' + escHtml(rec.name) + '" style="width:100%;height:100%;object-fit:cover;border-radius:50%">'
|
||||
: escHtml((rec.name || '?')[0].toUpperCase());
|
||||
const occupation = _libStr(sh.profession);
|
||||
const ageLabel = _charAgeLabel(sh);
|
||||
@ -1178,7 +1178,7 @@ async function _charDetailPage(rec, allChars, opts) {
|
||||
|
||||
const avatarHtml = '<div class="lcd-avatar-wrap">'
|
||||
+ (rec.image
|
||||
? '<div class="lcd-avatar lcd-avatar-upload" title="Profilbild öffnen"><img src="' + rec.image + '" alt="' + escHtml(rec.name) + '"></div>'
|
||||
? '<div class="lcd-avatar lcd-avatar-upload" title="Profilbild öffnen"><img loading="lazy" decoding="async" src="' + rec.image + '" alt="' + escHtml(rec.name) + '"></div>'
|
||||
: '<div class="lcd-avatar lcd-avatar-upload" style="background:hsl(' + hue + ',55%,38%)" title="Profilbild öffnen">' + escHtml((rec.name || '?')[0].toUpperCase()) + '</div>')
|
||||
+ '</div>';
|
||||
|
||||
@ -1197,7 +1197,7 @@ async function _charDetailPage(rec, allChars, opts) {
|
||||
+ '</button>'
|
||||
+ '</div>'
|
||||
+ (sh.concept_art_image
|
||||
? '<div class="lcd-conceptart-img" title="Konzeptbild ansehen"><img src="' + sh.concept_art_image + '" alt="Concept art — ' + escHtml(rec.name) + '"></div>'
|
||||
? '<div class="lcd-conceptart-img" title="Konzeptbild ansehen"><img loading="lazy" decoding="async" src="' + sh.concept_art_image + '" alt="Concept art — ' + escHtml(rec.name) + '"></div>'
|
||||
: '<div class="lcd-conceptart-empty' + (_libStr(sh.concept_art_prompt).trim() ? '' : ' is-noprompt') + '">'
|
||||
+ '<span class="mdi mdi-image-frame"></span>'
|
||||
+ '<span>' + (_libStr(sh.concept_art_prompt).trim() ? 'Kein Konzeptbild' : 'Kein Konzeptbild-Prompt — erst unten bei Generation Prompts erzeugen') + '</span>'
|
||||
@ -1594,7 +1594,7 @@ function _charDetailModal(rec, allChars) {
|
||||
const voiceId = rec.voice ? (typeof rec.voice === 'object' ? (rec.voice.id || '') : String(rec.voice)) : '';
|
||||
|
||||
const avatarHtml = rec.image
|
||||
? '<div class="lcd-avatar"><img src="' + rec.image + '" alt="' + escHtml(rec.name) + '"></div>'
|
||||
? '<div class="lcd-avatar"><img loading="lazy" decoding="async" src="' + rec.image + '" alt="' + escHtml(rec.name) + '"></div>'
|
||||
: '<div class="lcd-avatar" style="background:hsl(' + hue + ',55%,38%)">' + escHtml((rec.name || '?')[0].toUpperCase()) + '</div>';
|
||||
|
||||
const alignHtml = pct != null ? (
|
||||
|
||||
Loading…
Reference in New Issue
Block a user