feat(homepage): Audiobookshelf's own mark, and the voice endpoint in the model block
Audiobookshelf carried the improvised "AB" hex monogram — the fallback for apps with no icon. Simple Icons carries its brand mark, so the card now uses /img/apps/audiobookshelf.svg the way Plex and Jellyfin use theirs; the monogram went to .AGENTTRASH. frank-tts (Qwen3-TTS 1.7 B base talker on the gfx1151 iGPU) was an admin-only status tile, so the household read about five cloned voices in /docs and saw no sign of the door that speaks them. It is now a Model Information card on the user page beside the three chat models, carrying the ask-Frank permission note and a privacy note about cloned voices. miCard grew the shape that requires: art falls back to the card's own `art` when a card has no StarCraft `unit`, the accent falls back to `.mi-tool`, and a `tagline` with no `sfx` renders as text instead of a play button that plays nothing. No audio asset ships with the card on purpose: every pack on that endpoint is a replica of a real person's voice, so a sample clip would put a synthetic person's voice in the repo and on the page. Seams: the model-card contract splits unit cards (equivalence, supervision, playable voice line) from tool cards (permission note), and the voice stack's homepage seam now pins category=user with the reason.
This commit is contained in:
1 parent
e6a85176c4
commit
53e4a777b4
8 files changed
+60
-25
No files matched your search
@@ -79,15 +79,17 @@ function voiceBtn(text, files) {
|
||||
|
||||
function miCard(svc, state) {
|
||||
const m = svc.model;
|
||||
const art = svc.art || '/img/unit-' + m.unit + '.jpg';
|
||||
const parts = ['<div class="mi-top">' +
|
||||
'<img class="mi-art" src="/img/unit-' + esc(m.unit) + '.jpg" alt="' + esc(m.unit) + '">' +
|
||||
'<img class="mi-art" src="' + esc(art) + '" alt="' + esc(m.unit || svc.name) + '">' +
|
||||
'<div class="mi-head"><div class="mi-id"><span class="badge ' + state + '">' + BADGE_LABEL[state] + '</span>' +
|
||||
'<span class="mi-name">' + esc(svc.name) + '</span></div>' +
|
||||
'<div class="mi-role">' + esc(m.role) + '</div>' +
|
||||
(m.hf ? '<a class="mi-hf" href="' + esc(m.hf) +
|
||||
'" target="_blank" rel="noopener">model on huggingface ↗</a>' : '') +
|
||||
'</div></div>',
|
||||
voiceBtn(m.tagline, m.sfx)
|
||||
(m.sfx && m.sfx.length ? voiceBtn(m.tagline, m.sfx)
|
||||
: m.tagline ? '<div class="mi-tag">' + esc(m.tagline) + '</div>' : '')
|
||||
];
|
||||
if (m.equiv) parts.push('<div class="mi-equiv">' + esc(m.equiv) + '</div>');
|
||||
if (m.spec) parts.push('<div class="mi-spec">' + esc(m.spec) + '</div>');
|
||||
@@ -99,7 +101,7 @@ function miCard(svc, state) {
|
||||
if (m.warn) parts.push('<details class="mi-warn"><summary>⚠ privacy note</summary>' +
|
||||
'<div class="warn-body">' + esc(m.warn) + '</div></details>');
|
||||
const card = document.createElement('article');
|
||||
card.className = 'mi-card mi-' + esc(m.unit);
|
||||
card.className = 'mi-card mi-' + esc(m.unit || 'tool');
|
||||
card.innerHTML = parts.join('');
|
||||
return card;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user