aether-garden / ui /avatars.py
kavyabhand's picture
Deploy Aether Garden application
781b9f7 verified
Raw
History Blame Contribute Delete
2.77 kB
"""SVG avatar sigils for entities — ornate monogram emblems per type.
Real photographic creature art can't be matched reliably to abstract,
quirky entities (a "Joke Dragon" is not a stock phoenix), so portraits use
an elegant type-coloured sigil with the entity's monogram. This reads as a
deliberate heraldic mark rather than a mismatched photo.
"""
from __future__ import annotations
TYPE_PALETTES = {
"character": {"bg": "#1f3a2c", "ring": "#d4a84b", "accent": "#8fbc8f", "mark": "✦"},
"creature": {"bg": "#3a281c", "ring": "#e07b4a", "accent": "#f0c878", "mark": "❧"},
"object": {"bg": "#1c2c3a", "ring": "#7fa8d0", "accent": "#a8c8e8", "mark": "◈"},
"place": {"bg": "#1c3a2c", "ring": "#60a060", "accent": "#90d090", "mark": "▲"},
}
LOCATION_TINTS = {
"library": "#c8a040",
"sea": "#7090b0",
"clock-forest": "#b06020",
"moon-market": "#8060b0",
"valley": "#506050",
"crossroads": "#c04020",
"mirror-bogs": "#406858",
"hollow-mountain": "#808890",
}
def render_avatar(entity: dict, size: int = 80, location_slug: str | None = None) -> str:
etype = entity.get("type", "character")
palette = TYPE_PALETTES.get(etype, TYPE_PALETTES["character"])
tint = LOCATION_TINTS.get(location_slug or "", palette["ring"])
name = entity.get("display_name") or entity.get("name", "?")
initial = name.replace("The ", "")[0].upper() if name else "?"
uid = (entity.get("id") or "00000000")[:8]
return f"""
<svg class="entity-avatar" width="{size}" height="{size}" viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg">
<defs>
<radialGradient id="av-{uid}" cx="50%" cy="38%" r="70%">
<stop offset="0%" stop-color="{palette['accent']}" stop-opacity="0.32"/>
<stop offset="55%" stop-color="{palette['bg']}"/>
<stop offset="100%" stop-color="#0c1410"/>
</radialGradient>
<filter id="gl-{uid}" x="-30%" y="-30%" width="160%" height="160%">
<feGaussianBlur stdDeviation="1.4" result="b"/>
<feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
</defs>
<circle cx="50" cy="50" r="47" fill="url(#av-{uid})"/>
<circle cx="50" cy="50" r="47" fill="none" stroke="{tint}" stroke-width="2" opacity="0.9"/>
<circle cx="50" cy="50" r="41" fill="none" stroke="{tint}" stroke-width="0.8" opacity="0.4"/>
<text x="50" y="60" text-anchor="middle" fill="{palette['accent']}"
font-family="'Cinzel', Georgia, serif" font-size="38" font-weight="600"
filter="url(#gl-{uid})">{initial}</text>
<text x="50" y="84" text-anchor="middle" fill="{tint}" font-size="11" opacity="0.85">{palette['mark']}</text>
</svg>
"""