avatars plus other changes
All checks were successful
Build Org Website / build (push) Successful in 51s

This commit is contained in:
2026-05-11 15:44:30 +01:00
parent ae591de204
commit 5df4871ecc
7 changed files with 532 additions and 18 deletions

View File

@@ -66,6 +66,7 @@ IMAGE_ASSETS_DIR = ROOT / "assets" / "images"
HZONE_ASSETS_DIR = IMAGE_ASSETS_DIR / "hzone"
HIDDEN_DETAILS_JS = ROOT / "assets" / "scripts" / "hidden-details.js"
HIDDEN_CONTENT_JSON = ROOT / "assets" / "content" / "hidden-details.json"
AVATAR_REGISTRY_JS = ROOT / "assets" / "scripts" / "avatar-registry.js"
HIDDEN_BACKUP_DIR = ROOT / "backups" / "hidden-details"
EXCLUDED_CONTENT_DIR_NAMES = {
".agents",
@@ -270,6 +271,115 @@ CHARACTER_REGISTRY = {
"affinities": ["z", "young z", "lima", "sensei chi"],
},
}
AVATAR_REGISTRY_META = {
"young z": {
"avatar": "/assets/avatars/young-z.jpeg",
"glow": "#d8a95a",
"accent": "#f4bf5f",
"fallback": "z",
"expressions": {
"calm": "/assets/avatars/young-z.jpeg",
"happy": "/assets/avatars/young-z.jpeg",
"surprised": "/assets/avatars/young-z.jpeg",
},
"sizes": {"marker": 18, "tiny": 22, "small": 32, "medium": 44, "large": 64, "focus": 88},
"observatory": {
"territory": "nostalgic-playful",
"presenceKeywords": ["childhood", "play", "crayon", "sun", "blanket", "game", "desk", "drawing", "nostalgia"],
"atmosphere": "warm childhood light",
"focusScale": 1.08,
},
},
"z": {
"avatar": "/assets/avatars/z.jpeg",
"glow": "#d6c38a",
"accent": "#d8c891",
"fallback": "z",
"expressions": {"calm": "/assets/avatars/z.jpeg", "receptive": "/assets/avatars/z.jpeg"},
"sizes": {"marker": 18, "tiny": 22, "small": 32, "medium": 44, "large": 64, "focus": 88},
"observatory": {
"territory": "archive-return",
"presenceKeywords": ["website", "archive", "observer", "traveler", "making", "return", "home"],
"atmosphere": "steady archive lantern",
"focusScale": 1,
},
},
"aphy": {
"avatar": "/assets/avatars/aphy.jpeg",
"glow": "#7fb089",
"accent": "#83d4df",
"fallback": "z",
"expressions": {
"calm": "/assets/avatars/aphy.jpeg",
"curious": "/assets/avatars/aphy.jpeg",
"thinking": "/assets/avatars/aphy.jpeg",
},
"sizes": {"marker": 18, "tiny": 22, "small": 32, "medium": 44, "large": 64, "focus": 88},
"observatory": {
"territory": "system-symbolic",
"presenceKeywords": ["system", "terminal", "logic", "error", "debug", "console", "css", "search", "backup", "symbol"],
"atmosphere": "soft diagnostic glow",
"focusScale": 1.04,
},
},
"lima": {
"avatar": "/assets/avatars/lima.jpg",
"glow": "#d06b78",
"accent": "#e7a176",
"fallback": "z",
"expressions": {
"calm": "/assets/avatars/lima.jpg",
"happy": "/assets/avatars/lima.jpg",
"protective": "/assets/avatars/lima.jpg",
},
"sizes": {"marker": 18, "tiny": 22, "small": 32, "medium": 44, "large": 64, "focus": 88},
"observatory": {
"territory": "warm-protective",
"presenceKeywords": ["warm", "home", "safe", "protect", "kitchen", "ring", "love", "rest", "care", "light"],
"atmosphere": "protective kitchen warmth",
"focusScale": 1.12,
},
},
"sensei chi": {
"avatar": "/assets/avatars/sensei-chi.jpeg",
"glow": "#75a9bd",
"accent": "#9ec4be",
"fallback": "z",
"expressions": {"calm": "/assets/avatars/sensei-chi.jpeg", "reflective": "/assets/avatars/sensei-chi.jpeg"},
"sizes": {"marker": 18, "tiny": 22, "small": 32, "medium": 44, "large": 64, "focus": 88},
"observatory": {
"territory": "reflective-patient",
"presenceKeywords": ["quiet", "reflect", "patience", "tea", "garden", "lesson", "wisdom", "still"],
"atmosphere": "slow reflective tea light",
"focusScale": 1.06,
},
},
"future z": {
"avatar": "/assets/avatars/future-z.jpeg",
"glow": "#a58ac9",
"accent": "#b7a6cf",
"fallback": "z",
"expressions": {
"calm": "/assets/avatars/future-z.jpeg",
"nostalgic": "/assets/avatars/future-z.jpeg",
"warning": "/assets/avatars/future-z.jpeg",
},
"sizes": {"marker": 18, "tiny": 22, "small": 32, "medium": 44, "large": 64, "focus": 88},
"observatory": {
"territory": "temporal",
"presenceKeywords": ["future", "time", "age 40", "later", "return", "warning", "ordinary", "clock"],
"atmosphere": "distant temporal shimmer",
"focusScale": 1.1,
},
},
}
for character_id, avatar_meta in AVATAR_REGISTRY_META.items():
if character_id in CHARACTER_REGISTRY:
CHARACTER_REGISTRY[character_id]["avatar"] = {**CHARACTER_REGISTRY[character_id].get("avatar", {}), **avatar_meta}
CHARACTER_REGISTRY[character_id]["glow"] = avatar_meta["glow"]
CHARACTER_REGISTRY[character_id]["accent"] = avatar_meta["accent"]
HIDDEN_CHARACTERS = list(CHARACTER_REGISTRY)
HIDDEN_TONES = ["warm", "funny", "nostalgic", "wise", "strange", "soft", "hopeful", "protective", "melancholy"]
HIDDEN_RARITIES = ["common", "uncommon", "rare", "very rare", "seasonal", "timed"]
@@ -2604,7 +2714,9 @@ HIDDEN_APP_HTML = r"""<!doctype html>
overflow: hidden;
vertical-align: middle;
}
.avatar svg { width: 100%; height: 100%; display: block; shape-rendering: geometricPrecision; }
.avatar img, .avatar-fallback { width: 100%; height: 100%; display: block; }
.avatar img { object-fit: cover; border-radius: inherit; }
.avatar-fallback:not([hidden]) { display: grid; place-items: center; color: #fff4d3; font-size: 11px; font-weight: 850; }
.avatar--xs { --avatar-size: 18px; }
.avatar--sm { --avatar-size: 24px; }
.avatar--md { --avatar-size: 34px; }
@@ -2693,8 +2805,12 @@ HIDDEN_APP_HTML = r"""<!doctype html>
.memory-node text, .node-label text, .cluster-label text { fill: #f7ead3; paint-order: stroke; stroke: rgba(14, 11, 8, 0.82); stroke-width: 3px; stroke-linejoin: round; }
.memory-node circle { stroke: rgba(255, 246, 223, 0.82); stroke-width: 1.4; filter: drop-shadow(0 0 7px rgba(255, 232, 177, 0.18)); transition: r 160ms ease, stroke 160ms ease, opacity 160ms ease; }
.memory-node .character-halo { fill: color-mix(in oklab, var(--node-character-color, rgba(211, 166, 77, 0.46)) 8%, transparent); stroke: var(--node-character-color, rgba(211, 166, 77, 0.46)); stroke-width: 1.6; opacity: 0.34; stroke-dasharray: 2 7; animation: spirit-shimmer 8s ease-in-out infinite; }
.memory-node .node-avatar { opacity: 0; transform: scale(0.72); transition: opacity 220ms ease, transform 220ms ease; }
.zoom-2 .memory-node .node-avatar, .zoom-3 .memory-node .node-avatar, .memory-node.selected .node-avatar { opacity: 0.96; transform: scale(1); }
.memory-node .node-avatar { opacity: 0; transform: scale(0.58); transition: opacity 220ms ease, transform 220ms ease; }
.zoom-1 .memory-node .node-avatar { opacity: 0.5; transform: scale(0.7); }
.zoom-2 .memory-node .node-avatar { opacity: 0.9; transform: scale(0.95); }
.zoom-3 .memory-node .node-avatar, .memory-node.selected .node-avatar { opacity: 1; transform: scale(1.12); }
.zoom-0 .memory-node text { opacity: 1; }
.zoom-3 .memory-node text, .memory-node.selected text { transform: translateY(27px); }
.memory-node.selected circle { stroke: #fff3c7; stroke-width: 3; }
.memory-node.dragging circle { stroke: #f3cd7a; stroke-width: 3; opacity: 0.72; }
.memory-node.compatible circle { stroke: rgba(255, 243, 199, 0.92); stroke-width: 2.4; filter: drop-shadow(0 0 16px rgba(255, 232, 177, 0.46)); }
@@ -3041,6 +3157,7 @@ HIDDEN_APP_HTML = r"""<!doctype html>
const fields = ["title","type","familyLayer","characters","tone","rarity","discoveryDifficulty","mysteryLevel","resonanceScore","pageLocation","triggerConditions","content","symbols","narrativeArcs","tags","emotionalRole","continuationLinks","echoes","thematicLinks","symbolicLinks","triggerLinks","parentLinks","childLinks","mirroredEntries","enabled","cssClassHooks","audioSettings","animationTrigger","notes"];
const $ = (id) => document.getElementById(id);
const toneColors = { warm: "#d3a64d", funny: "#7fb089", nostalgic: "#d06b78", wise: "#75a9bd", strange: "#a58ac9", soft: "#e6bd8c", hopeful: "#9fcf9f", protective: "#d98f71", melancholy: "#8fa7c6" };
let avatarClipSeq = 0;
function html(value) {
return String(value || "").replace(/[&<>"']/g, (char) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" }[char]));
@@ -3060,6 +3177,18 @@ HIDDEN_APP_HTML = r"""<!doctype html>
function avatarSvg(character, expression = "soft") {
const config = characterConfig(character);
const avatar = config.avatar || {};
const srcSvg = avatar.expressions?.[expression] || avatar.avatar || config.avatarPath || config.portrait || state.meta.characterRegistry?.z?.avatar?.avatar || "/assets/avatars/z.jpeg";
const colorSvg = config.glow || avatar.glow || config.territoryColor || avatar.accent || "#d3a64d";
const labelSvg = html(config.displayLabel || character || "z");
const symbolSvg = html(config.symbol || "?");
const clipSvg = `avatarClip-${String(character || "z").replace(/[^a-z0-9]+/gi, "-")}-${String(expression || "calm").replace(/[^a-z0-9]+/gi, "-")}-${avatarClipSeq += 1}`;
return `<svg width="36" height="36" viewBox="0 0 36 36" aria-label="${labelSvg}" role="img" focusable="false">
<defs><clipPath id="${clipSvg}"><circle cx="18" cy="18" r="16"></circle></clipPath></defs>
<circle cx="18" cy="18" r="17" fill="${html(colorSvg)}" opacity=".18"></circle>
<circle cx="18" cy="18" r="16.5" fill="rgba(18,16,13,.72)" stroke="${html(colorSvg)}" stroke-width="1.6"></circle>
<image href="${html(srcSvg)}" x="2" y="2" width="32" height="32" preserveAspectRatio="xMidYMid slice" clip-path="url(#${clipSvg})"></image>
<text x="28.8" y="31.4" text-anchor="middle" font-size="7" fill="#fff7e8" font-family="monospace">${symbolSvg}</text>
</svg>`;
const accent = avatar.accent || config.territoryColor || "#d3a64d";
const secondary = avatar.secondary || "#f6dfb4";
const hair = avatar.hair || "#5d4632";
@@ -3108,8 +3237,10 @@ HIDDEN_APP_HTML = r"""<!doctype html>
}
function avatarHtml(character, size = "sm", expression = "soft", extraClass = "") {
const config = characterConfig(character);
const color = config.territoryColor || config.avatar?.accent || "#d3a64d";
return `<span class="avatar avatar--${size} ${extraClass}" style="--avatar-color:${html(color)}" title="${html(config.displayLabel || character)} - ${html(config.avatar?.description || "observatory spirit")}">${avatarSvg(character, expression)}</span>`;
const avatar = config.avatar || {};
const color = config.glow || avatar.glow || config.territoryColor || avatar.accent || "#d3a64d";
const src = avatar.expressions?.[expression] || avatar.avatar || state.meta.characterRegistry?.z?.avatar?.avatar || "/assets/avatars/z.jpeg";
return `<span class="avatar avatar--${size} ${extraClass}" style="--avatar-color:${html(color)}" title="${html(config.displayLabel || character)} - ${html(config.avatar?.description || avatar.observatory?.atmosphere || "observatory spirit")}"><img src="${html(src)}" alt="" loading="lazy" decoding="async" onerror="this.hidden=true;this.nextElementSibling.hidden=false"><span class="avatar-fallback" hidden>${html(config.symbol || "?")}</span></span>`;
}
function avatarChip(character, entry = null) {
const config = characterConfig(character);