From 3db361683094a200b1c73dd633dea35a1cd9a694 Mon Sep 17 00:00:00 2001 From: mARTin-B78 Date: Thu, 9 Jul 2026 02:09:20 +0200 Subject: [PATCH] Show character avatar images in casting pickers (v1.14.12) The Characters Found sidebar, the "Assign to" popup's character list, and the "merge with an already-found character" list now show each character's saved portrait when one exists (same source as the profile detail panel), falling back to the existing colour-keyed initial-letter dot otherwise. Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 7 +++++++ VERSION | 2 +- static/index.html | 6 +++--- static/js/audiobook.js | 19 ++++++++++++++++--- static/style.css | 6 +++++- 5 files changed, 32 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f56e903..358c1b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,13 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi --- +## [1.14.12] — 2026-07-09 + +### Added +- **Character avatar images in the casting pickers** — the Characters Found sidebar, the "Assign to" popup's character list, and the "merge with an already-found character" list now show the character's saved portrait (same source as the profile detail panel) when one exists, falling back to the colour-keyed initial-letter dot otherwise. + +--- + ## [1.14.11] — 2026-07-09 ### Fixed diff --git a/VERSION b/VERSION index f350950..4ed70fa 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.14.11 +1.14.12 diff --git a/static/index.html b/static/index.html index 9b6b493..4e2f24e 100644 --- a/static/index.html +++ b/static/index.html @@ -10,7 +10,7 @@ - + @@ -27,7 +27,7 @@ - + @@ -365,7 +365,7 @@ window.toggleNavTree = function(treeId, chevronId) { - + diff --git a/static/js/audiobook.js b/static/js/audiobook.js index debe97a..8227ba0 100644 --- a/static/js/audiobook.js +++ b/static/js/audiobook.js @@ -1566,6 +1566,16 @@ STRIKTE FORMAT- UND TEXTREGELN: else roster.get(name).color = color; return roster.get(name).color; }; + // Small round avatar for character pickers/lists — the character's saved + // portrait (same source as the profile detail panel) when one exists, + // otherwise the colour-keyed initial-letter dot used everywhere else. + const _abAvatarHtml = (name, color) => { + const img = recordForName(name)?.image; + const c = color || colorFor(name); + return img + ? `` + : `${escHtml((name || '?')[0].toUpperCase())}`; + }; const setCharacterColor = (name, color) => { const safe = _abNormalizeColor(color, name); const info = roster.get(name); @@ -1982,7 +1992,7 @@ STRIKTE FORMAT- UND TEXTREGELN: chars.innerHTML = items.map(([n, info]) => { const color = info.color || colorFor(n); return `
- ${escHtml((n||'?')[0].toUpperCase())} + ${_abAvatarHtml(n, color)} ${escHtml(n)} ${roster.get(n)?.count||0} @@ -2039,7 +2049,7 @@ STRIKTE FORMAT- UND TEXTREGELN: ${otherNames.length ? `
Or merge with an already-found character:
- ${otherNames.slice(0, 8).map(([n, info]) => ``).join('')} + ${otherNames.slice(0, 8).map(([n, info]) => ``).join('')}
` : ''}
@@ -2700,7 +2710,10 @@ STRIKTE FORMAT- UND TEXTREGELN: const btn = document.createElement('div'); btn.dataset.name = n; btn.style.cssText = 'padding:6px 8px; font-size:12px; cursor:pointer; border-radius:4px; display:flex; align-items:center; gap:6px; transition:background 0.1s; font-weight:600; color:var(--text);'; - btn.innerHTML = ` ${escHtml(n)}`; + const img = recordForName(n)?.image; + btn.innerHTML = (img + ? `` + : ``) + ` ${escHtml(n)}`; btn.onmouseover = () => btn.style.background = 'var(--panel)'; btn.onmouseout = () => btn.style.background = 'transparent'; btn.onclick = () => assignName(n); diff --git a/static/style.css b/static/style.css index 98cdc8e..cdffbd9 100644 --- a/static/style.css +++ b/static/style.css @@ -5530,6 +5530,8 @@ code { background: var(--panel); border-radius: 4px; padding: 1px 5px; font-fami display:flex; align-items:center; justify-content:center; font-size:11px; font-weight:700; color:#fff; } +.ab-char-dot-img { overflow: hidden; background: var(--panel); } +.ab-char-dot-img img { width: 100%; height: 100%; object-fit: cover; } .ab-char-name { flex:1; font-size:12px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; min-width: 0; } .ab-char-count { font-size:11px; color:var(--subtext); font-weight:700; flex-shrink:0; } .ab-char-alias-btn { @@ -5563,7 +5565,9 @@ code { background: var(--panel); border-radius: 4px; padding: 1px 5px; font-fami background: none; color: var(--text); cursor: pointer; text-align: left; } .ab-alias-merge-opt:hover { background: var(--panel); border-color: var(--border); } -.ab-alias-merge-count { font-size: 10.5px; color: var(--subtext); font-weight: 700; } +.ab-alias-merge-name { display: flex; align-items: center; gap: 6px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.ab-alias-merge-name .ab-char-dot { width: 18px; height: 18px; font-size: 9px; } +.ab-alias-merge-count { font-size: 10.5px; color: var(--subtext); font-weight: 700; flex-shrink: 0; } .ab-alias-popup-actions { display: flex; justify-content: flex-end; gap: 6px; } /* Character detail panel inside the casting feed area */ .ab-char-detail-panel {