diff --git a/assets/scripts/hidden-details.js b/assets/scripts/hidden-details.js
index 72ebbc9..dd700c0 100755
--- a/assets/scripts/hidden-details.js
+++ b/assets/scripts/hidden-details.js
@@ -27,45 +27,6 @@
const hour = now.getHours();
const path = window.location.pathname;
const state = readState();
- const CHARACTER_AVATARS = {
- "lima": {
- avatar: "/assets/avatars/lima.jpg",
- fallback: "/assets/avatars/z.jpeg",
- aliases: ["lima"],
- glow: "#f2c58b"
- },
- "aphy": {
- avatar: "/assets/avatars/aphy.jpeg",
- fallback: "/assets/avatars/z.jpeg",
- aliases: ["aphy"],
- glow: "#9dd3a6"
- },
- "sensei chi": {
- avatar: "/assets/avatars/sensei-chi.jpeg",
- fallback: "/assets/avatars/z.jpeg",
- aliases: ["sensei chi", "sensei"],
- glow: "#9ccddd"
- },
- "young z": {
- avatar: "/assets/avatars/young-z.jpeg",
- fallback: "/assets/avatars/z.jpeg",
- aliases: ["young z", "young"],
- glow: "#f0b85c"
- },
- "future z": {
- avatar: "/assets/avatars/future-z.jpeg",
- fallback: "/assets/avatars/z.jpeg",
- aliases: ["future z", "future"],
- glow: "#c2a4ee"
- },
- "z": {
- avatar: "/assets/avatars/z.jpeg",
- fallback: "/assets/avatars/z.jpeg",
- aliases: ["z", "zaine"],
- glow: "#ead68e"
- }
- };
-
// -----------------------------
// EDITABLE CONTENT
// -----------------------------
@@ -151,39 +112,13 @@
// UI HELPERS
// -----------------------------
- function characterForMessage(message) {
- const text = String(message || "").toLowerCase();
- return Object.entries(CHARACTER_AVATARS).find(([, config]) => {
- return (config.aliases || []).some((alias) => {
- const escaped = alias.toLowerCase().replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
- return new RegExp(`(^|[^a-z0-9-])${escaped}([^a-z0-9-]|$)`).test(text);
- });
- })?.[0] || "";
- }
-
- function avatarMarkup(character) {
- const config = CHARACTER_AVATARS[character];
- if (!config) return "";
- return `
`;
- }
-
function hiddenMessageMarkup(message) {
- const character = characterForMessage(message);
- return character
- ? `${avatarMarkup(character)}${escapeHtml(message)}`
- : `${escapeHtml(message)}`;
+ return `${escapeHtml(message)}`;
}
function setHiddenMessage(el, message) {
- const character = characterForMessage(message);
- el.classList.toggle("has-hidden-avatar", Boolean(character));
- if (character) {
- el.dataset.hiddenCharacter = character;
- el.innerHTML = hiddenMessageMarkup(message);
- } else {
- delete el.dataset.hiddenCharacter;
- el.textContent = message;
- }
+ delete el.dataset.hiddenCharacter;
+ el.innerHTML = hiddenMessageMarkup(message);
}
function toast(message, duration) {
@@ -372,8 +307,7 @@
function showDriftNote(text, art) {
const panel = document.createElement("aside");
panel.className = "hidden-drift-note";
- const character = characterForMessage(text);
- panel.innerHTML = `
${hiddenMessageMarkup(text)}
${escapeHtml(art)}`;
+ panel.innerHTML = `${hiddenMessageMarkup(text)}
${escapeHtml(art)}`;
panel.querySelector("button").addEventListener("click", () => panel.remove());
document.body.appendChild(panel);
}
@@ -568,8 +502,7 @@
const visits = readState().layerVisits || {};
target.innerHTML = familyLayers.map((line, layer) => {
const count = visits[layer] || 0;
- const character = characterForMessage(line);
- return `${hiddenMessageMarkup(line)}
local encounters: ${count}`;
+ return `${hiddenMessageMarkup(line)}
local encounters: ${count}`;
}).join("");
}
diff --git a/src/authoring_service/constants.py b/src/authoring_service/constants.py
index c5e094c..e5a6453 100755
--- a/src/authoring_service/constants.py
+++ b/src/authoring_service/constants.py
@@ -94,13 +94,6 @@ CHARACTER_REGISTRY = {
"aliases": ["Young Z", "young z", "young-z", "young_z", "young"],
"territoryColor": "#d8a95a",
"glow": "#f0b85c",
- "avatar": "https://zainezq.com/assets/avatars/young-z.jpeg",
- "fallbackAvatar": "https://zainezq.com/assets/avatars/z.jpeg",
- "expressions": {
- "default": "https://zainezq.com/assets/avatars/young-z.jpeg",
- "nostalgic": "https://zainezq.com/assets/avatars/young-z.jpeg",
- "playful": "https://zainezq.com/assets/avatars/young-z.jpeg",
- },
"sizes": {"tiny": 18, "medium": 42, "close": 56, "focus": 82},
"observatory": {"territory": "nostalgic/playful memories", "shimmer": "soft"},
"presenceTones": ["nostalgic", "funny", "hopeful"],
@@ -116,31 +109,6 @@ CHARACTER_REGISTRY = {
"aliases": ["Z", "z", "zaine"],
"territoryColor": "#d6c38a",
"glow": "#ead68e",
- "avatar": "https://zainezq.com/assets/avatars/z.jpeg",
- "fallbackAvatar": "https://zainezq.com/assets/avatars/z.jpeg",
- "expressions": {"default": "https://zainezq.com/assets/avatars/z.jpeg", "calm": "https://zainezq.com/assets/avatars/z.jpeg"},
- "sizes": {"tiny": 18, "medium": 42, "close": 56, "focus": 82},
- "observatory": {"territory": "self/archive memories", "shimmer": "steady"},
- "presenceTones": ["warm", "soft", "hopeful"],
- "presenceKeywords": ["website", "archive", "home", "self", "making", "return", "zaine"],
- "symbol": "Z",
- "motifs": ["archive", "website", "home"],
- "themes": ["selfhood", "return", "making"],
- "affinities": ["lima", "young z", "future z"],
- },
- "aphy": {
- "id": "aphy",
- "displayLabel": "aphy",
- "aliases": ["Aphy", "aphy", "aphy_bot", "aphy bot"],
- "territoryColor": "#7fb089",
- "glow": "#9dd3a6",
- "avatar": "https://zainezq.com/assets/avatars/aphy.jpeg",
- "fallbackAvatar": "https://zainezq.com/assets/avatars/z.jpeg",
- "expressions": {
- "default": "https://zainezq.com/assets/avatars/aphy.jpeg",
- "system": "https://zainezq.com/assets/avatars/aphy.jpeg",
- "amused": "https://zainezq.com/assets/avatars/aphy.jpeg",
- },
"sizes": {"tiny": 18, "medium": 42, "close": 56, "focus": 82},
"observatory": {"territory": "system/symbolic areas", "shimmer": "diagnostic"},
"presenceTones": ["funny", "strange", "hopeful"],
@@ -156,14 +124,6 @@ CHARACTER_REGISTRY = {
"aliases": ["Lima", "lima"],
"territoryColor": "#d06b78",
"glow": "#f2c58b",
- "avatar": "https://zainezq.com/assets/avatars/lima.jpg",
- "fallbackAvatar": "https://zainezq.com/assets/avatars/z.jpeg",
- "expressions": {
- "default": "https://zainezq.com/assets/avatars/lima.jpg",
- "calm": "https://zainezq.com/assets/avatars/lima.jpg",
- "warm": "https://zainezq.com/assets/avatars/lima.jpg",
- "protective": "https://zainezq.com/assets/avatars/lima.jpg",
- },
"sizes": {"tiny": 18, "medium": 42, "close": 56, "focus": 82},
"observatory": {"territory": "warm/protective arcs", "shimmer": "warm"},
"presenceTones": ["warm", "protective", "soft"],
@@ -179,13 +139,6 @@ CHARACTER_REGISTRY = {
"aliases": ["Sensei Chi", "sensei chi", "sensei-chi", "sensei_chi", "sensei"],
"territoryColor": "#75a9bd",
"glow": "#9ccddd",
- "avatar": "https://zainezq.com/assets/avatars/sensei-chi.jpeg",
- "fallbackAvatar": "https://zainezq.com/assets/avatars/z.jpeg",
- "expressions": {
- "default": "https://zainezq.com/assets/avatars/sensei-chi.jpeg",
- "reflective": "https://zainezq.com/assets/avatars/sensei-chi.jpeg",
- "wise": "https://zainezq.com/assets/avatars/sensei-chi.jpeg",
- },
"sizes": {"tiny": 18, "medium": 42, "close": 56, "focus": 82},
"observatory": {"territory": "reflective areas", "shimmer": "quiet"},
"presenceTones": ["wise", "melancholy", "soft"],
@@ -201,13 +154,6 @@ CHARACTER_REGISTRY = {
"aliases": ["Future Z", "future z", "future-z", "future_z", "future"],
"territoryColor": "#a58ac9",
"glow": "#c2a4ee",
- "avatar": "https://zainezq.com/assets/avatars/future-z.jpeg",
- "fallbackAvatar": "https://zainezq.com/assets/avatars/z.jpeg",
- "expressions": {
- "default": "https://zainezq.com/assets/avatars/future-z.jpeg",
- "temporal": "https://zainezq.com/assets/avatars/future-z.jpeg",
- "reassuring": "https://zainezq.com/assets/avatars/future-z.jpeg",
- },
"sizes": {"tiny": 18, "medium": 42, "close": 56, "focus": 82},
"observatory": {"territory": "temporal regions", "shimmer": "temporal"},
"presenceTones": ["hopeful", "melancholy", "wise"],
diff --git a/src/authoring_service/templates.py b/src/authoring_service/templates.py
index dbe1643..e87da33 100755
--- a/src/authoring_service/templates.py
+++ b/src/authoring_service/templates.py
@@ -1263,22 +1263,27 @@ HIDDEN_APP_HTML = r"""
background: rgba(251, 241, 220, 0.08);
font-size: 12px;
}
- .avatar-pill, .avatar-row, .character-tag {
+ .character-pill, .character-row, .character-tag {
display: inline-flex;
align-items: center;
gap: 7px;
}
- .avatar-thumb {
- width: var(--avatar-size, 28px);
- height: var(--avatar-size, 28px);
- flex: 0 0 var(--avatar-size, 28px);
+ .character-mark {
+ width: var(--character-size, 28px);
+ height: var(--character-size, 28px);
+ flex: 0 0 var(--character-size, 28px);
border-radius: 50%;
- object-fit: cover;
border: 1px solid rgba(255, 246, 223, 0.72);
background: rgba(251, 241, 220, 0.08);
- box-shadow: 0 0 18px color-mix(in srgb, var(--avatar-glow, #d3a64d) 42%, transparent);
+ box-shadow: 0 0 18px color-mix(in srgb, var(--character-glow, #d3a64d) 42%, transparent);
+ color: #fff4d7;
+ display: inline-grid;
+ font-size: calc(var(--character-size, 28px) * 0.46);
+ font-weight: 900;
+ line-height: 1;
+ place-items: center;
}
- .avatar-row {
+ .character-row {
width: 100%;
justify-content: space-between;
margin-top: 10px;
@@ -1287,7 +1292,7 @@ HIDDEN_APP_HTML = r"""
border-radius: 8px;
background: rgba(251, 241, 220, 0.055);
}
- .avatar-row .subtle { font-size: 12px; }
+ .character-row .subtle { font-size: 12px; }
.character-tag {
min-height: 32px;
border: 1px solid rgba(232, 202, 139, 0.2);
@@ -1298,7 +1303,7 @@ HIDDEN_APP_HTML = r"""
font-size: 12px;
font-weight: 800;
}
- .character-tag .avatar-thumb { --avatar-size: 24px; }
+ .character-tag .character-mark { --character-size: 24px; }
.layer-key { display: grid; gap: 8px; margin-top: 14px; }
.layer-row {
display: grid;
@@ -1346,7 +1351,7 @@ HIDDEN_APP_HTML = r"""
.memory-node.selected { opacity: 1; filter: drop-shadow(0 0 18px rgba(255, 231, 173, 0.45)); }
.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; }
.hit-target { fill: transparent; stroke: transparent; pointer-events: all; }
- .memory-node .node-shell, .memory-node .node-portrait, .memory-node .node-marker, .memory-node .fragment-star {
+ .memory-node .node-shell, .memory-node .node-shell, .memory-node .node-marker, .memory-node .fragment-star {
stroke: rgba(255, 246, 223, 0.82);
stroke-width: 1.4;
filter: drop-shadow(0 0 7px var(--node-glow, rgba(255, 232, 177, 0.18)));
@@ -1354,16 +1359,16 @@ HIDDEN_APP_HTML = r"""
}
.memory-node .fragment-star { stroke-width: 1.1; }
.memory-node .fragment-ring { fill: none; stroke: rgba(255, 246, 223, 0.24); stroke-width: 1; stroke-dasharray: 2 7; }
- .memory-node .node-breathe, .cluster-node .node-breathe { animation: avatar-breathe 8s ease-in-out infinite; transform-box: fill-box; transform-origin: center; }
+ .memory-node .node-breathe, .cluster-node .node-breathe { animation: character-breathe 8s ease-in-out infinite; transform-box: fill-box; transform-origin: center; }
.sequence-badge { fill: rgba(18, 16, 13, 0.88); stroke: rgba(255, 243, 199, 0.68); stroke-width: 1; }
.sequence-number { fill: #ffe7ad; font-size: 10px; font-weight: 850; paint-order: stroke; stroke: rgba(14, 11, 8, 0.72); stroke-width: 2px; }
.selection-halo { fill: rgba(255, 231, 173, 0.1); stroke: rgba(255, 243, 199, 0.95); stroke-width: 3; stroke-dasharray: 4 9; filter: drop-shadow(0 0 22px rgba(255, 231, 173, 0.62)); animation: selection-pulse 2.8s ease-in-out infinite; pointer-events: none; }
- .memory-node:hover .node-shell, .memory-node:hover .node-portrait, .memory-node:hover .fragment-star { filter: drop-shadow(0 0 20px var(--node-glow, rgba(255, 232, 177, 0.4))); }
- .memory-node.selected .node-shell, .memory-node.selected .node-portrait, .memory-node.selected .fragment-star { stroke: #fff3c7; stroke-width: 3; }
- .memory-node.dragging .node-shell, .memory-node.dragging .node-portrait { stroke: #f3cd7a; stroke-width: 3; opacity: 0.72; }
- .memory-node.compatible .node-shell, .memory-node.compatible .node-portrait { stroke: rgba(255, 243, 199, 0.92); stroke-width: 2.4; filter: drop-shadow(0 0 16px rgba(255, 232, 177, 0.46)); }
- .memory-node.drop-target .node-shell, .memory-node.drop-target .node-portrait { stroke: #fff5c9; stroke-width: 4; filter: drop-shadow(0 0 24px rgba(255, 232, 177, 0.7)); }
- .memory-node.invalid-drop .node-shell, .memory-node.invalid-drop .node-portrait { stroke: rgba(208, 107, 120, 0.9); stroke-width: 3; }
+ .memory-node:hover .node-shell, .memory-node:hover .node-shell, .memory-node:hover .fragment-star { filter: drop-shadow(0 0 20px var(--node-glow, rgba(255, 232, 177, 0.4))); }
+ .memory-node.selected .node-shell, .memory-node.selected .node-shell, .memory-node.selected .fragment-star { stroke: #fff3c7; stroke-width: 3; }
+ .memory-node.dragging .node-shell, .memory-node.dragging .node-shell { stroke: #f3cd7a; stroke-width: 3; opacity: 0.72; }
+ .memory-node.compatible .node-shell, .memory-node.compatible .node-shell { stroke: rgba(255, 243, 199, 0.92); stroke-width: 2.4; filter: drop-shadow(0 0 16px rgba(255, 232, 177, 0.46)); }
+ .memory-node.drop-target .node-shell, .memory-node.drop-target .node-shell { stroke: #fff5c9; stroke-width: 4; filter: drop-shadow(0 0 24px rgba(255, 232, 177, 0.7)); }
+ .memory-node.invalid-drop .node-shell, .memory-node.invalid-drop .node-shell { stroke: rgba(208, 107, 120, 0.9); stroke-width: 3; }
.cluster-node { cursor: pointer; transition: transform 160ms ease, filter 160ms ease; }
.cluster-node circle {
stroke: rgba(255, 243, 199, 0.75);
@@ -1671,7 +1676,7 @@ HIDDEN_APP_HTML = r"""
0%, 100% { opacity: 0.72; }
50% { opacity: 1; }
}
- @keyframes avatar-breathe {
+ @keyframes character-breathe {
0%, 100% { transform: scale(1); opacity: 0.92; }
50% { transform: scale(1.035); opacity: 1; }
}
@@ -1890,7 +1895,7 @@ HIDDEN_APP_HTML = r"""
Choose a memory
The side panel opens from the constellation.
-
+
@@ -2017,7 +2022,6 @@ HIDDEN_APP_HTML = r"""
const storyFields = ["storyTitle","storyDescription","storyTone","storyDiscovery","storyCharacters","storySymbols","storyMarkers","storyLayers","storyUnlock","storyHidden"];
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" };
- const defaultAvatarPath = "/assets/avatars/z.jpeg";
const appearanceLabels = {
observatory: "Observatory stars",
tooltip: "Soft tooltips",
@@ -2062,30 +2066,25 @@ HIDDEN_APP_HTML = r"""
function entryById(id) {
return state.entries.find((entry) => entry.id === id);
}
- function avatarRegistry() {
+ function characterRegistry() {
return state.meta.characterRegistry || {};
}
function characterConfig(character) {
- const registry = avatarRegistry();
+ const registry = characterRegistry();
return registry[character] || registry.z || {};
}
function characterLabel(character) {
return characterConfig(character).displayLabel || character || "z";
}
- function avatarPath(character, expression = "default") {
- const config = characterConfig(character);
- const expressions = config.expressions || {};
- return expressions[expression] || expressions.default || config.avatar || config.fallbackAvatar || defaultAvatarPath;
- }
- function avatarGlow(character) {
+ function characterGlow(character) {
const config = characterConfig(character);
return config.glow || config.territoryColor || "#d3a64d";
}
- function avatarSize(character, size = "medium") {
+ function characterSize(character, size = "medium") {
const sizes = characterConfig(character).sizes || {};
return Number(sizes[size] || sizes.medium || 42);
}
- function avatarExpressionForEntry(entry) {
+ function characterExpressionForEntry(entry) {
const tone = entry?.emotionalTone || "";
if (tone === "protective") return "protective";
if (tone === "wise") return "wise";
@@ -2095,10 +2094,10 @@ HIDDEN_APP_HTML = r"""
if (String(entry?.type || "").includes("future")) return "temporal";
return "default";
}
- function avatarImg(character, size = "medium", expression = "default", className = "") {
- const px = avatarSize(character, size);
+ function characterImg(character, size = "medium", expression = "default", className = "") {
+ const px = characterSize(character, size);
const label = characterLabel(character);
- return `
`;
+ return `${html(characterSymbol(character))}`;
}
function entryTextCorpus(entry) {
return [
@@ -2127,7 +2126,7 @@ HIDDEN_APP_HTML = r"""
return explicit + tone + keywords + motifs;
}
function primaryCharacterForEntry(entry) {
- const candidates = Object.keys(avatarRegistry());
+ const candidates = Object.keys(characterRegistry());
const scored = candidates.map((character) => ({ character, score: characterPresenceScore(entry, character) })).sort((a, b) => b.score - a.score);
return scored[0]?.score > 0 ? scored[0].character : (entry.characters || [])[0] || "z";
}
@@ -2136,13 +2135,11 @@ HIDDEN_APP_HTML = r"""
const primary = primaryCharacterForEntry(entry);
return [primary, ...listed.filter((character) => character !== primary)].filter(Boolean);
}
- function svgAvatar(character, radius, clipId, expression = "default", extraClass = "") {
- const diameter = radius * 2;
+ function svgCharacter(character, radius, clipId, expression = "default", extraClass = "") {
const fallback = characterSymbol(character);
- return `
-
+ return `
- ${html(fallback)}`;
+ `;
}
function normalizeCharacterName(value) {
const raw = String(value || "").trim();
@@ -2511,11 +2508,11 @@ HIDDEN_APP_HTML = r"""
}
function renderCharacterPresence(entry) {
const characters = charactersForEntry(entry);
- $("drawerAvatars").innerHTML = characters.map((character, index) => `${avatarImg(character, index === 0 ? "medium" : "tiny", avatarExpressionForEntry(entry))}${html(characterLabel(character))}`).join("");
- $("characterTags").innerHTML = characters.map((character) => `${avatarImg(character, "tiny", avatarExpressionForEntry(entry))}${html(characterLabel(character))}`).join("");
+ $("drawerCharacters").innerHTML = characters.map((character, index) => `${characterImg(character, index === 0 ? "medium" : "tiny", characterExpressionForEntry(entry))}${html(characterLabel(character))}`).join("");
+ $("characterTags").innerHTML = characters.map((character) => `${characterImg(character, "tiny", characterExpressionForEntry(entry))}${html(characterLabel(character))}`).join("");
const primary = primaryCharacterForEntry(entry);
const territory = characterConfig(primary).observatory?.territory || "this memory territory";
- $("presencePreview").innerHTML = `${avatarImg(primary, "medium", avatarExpressionForEntry(entry))}${html(characterLabel(primary))}
presence preview: ${html(territory)}`;
+ $("presencePreview").innerHTML = `${characterImg(primary, "medium", characterExpressionForEntry(entry))}${html(characterLabel(primary))}
presence preview: ${html(territory)}`;
}
function layerName(layer) {
return (state.meta.layers || []).find((item) => item.id === String(layer))?.name || `Layer ${layer}`;
@@ -2867,13 +2864,13 @@ HIDDEN_APP_HTML = r"""
const label = labelForCluster(cluster, pos, size, occupied);
const activeDrop = state.drag?.targetKind === "cluster" && state.drag.targetId === cluster.id;
const grow = activeDrop ? 1.08 : 1;
- const portraitRadius = Math.max(28, size * 0.46) * grow;
+ const symbolRadius = Math.max(28, size * 0.46) * grow;
const clipId = `clip-cluster-${slugify(cluster.id)}`;
- defs.push(``);
- return `
+ defs.push(``);
+ return `
- ${state.zoom > 0 || state.mode === "character" ? svgAvatar(character, portraitRadius, clipId, "default", "node-breathe") : ""}
+ ${state.zoom > 0 || state.mode === "character" ? svgCharacter(character, symbolRadius, clipId, "default", "node-breathe") : ""}
${cluster.entries.length}
${label}`;
}).join("");
@@ -2884,7 +2881,7 @@ HIDDEN_APP_HTML = r"""
const color = characterColor(character) || toneColors[entry.emotionalTone] || "#d3a64d";
const isFragment = (entry.contentClass || "fragment") === "fragment" || (entry.observatoryRole || "") === "ambient";
const baseSize = (isFragment ? 10 : 18) + Math.min(isFragment ? 10 : 18, Number(entry.resonanceScore || 3) * (isFragment ? 1.6 : 2.2)) + (entry.rarity === "rare" || entry.rarity === "very rare" ? 8 : 0);
- const zoomSize = state.zoom === 0 ? avatarSize(character, "tiny") : state.zoom === 1 ? avatarSize(character, "medium") : state.zoom === 2 ? avatarSize(character, "close") : avatarSize(character, "focus");
+ const zoomSize = state.zoom === 0 ? characterSize(character, "tiny") : state.zoom === 1 ? characterSize(character, "medium") : state.zoom === 2 ? characterSize(character, "close") : characterSize(character, "focus");
const size = isFragment ? baseSize : Math.max(baseSize, zoomSize / 2);
const label = labelForEntry(entry, pos, size, occupied);
const dragClass = dragClassForEntry(entry.id);
@@ -2899,14 +2896,14 @@ HIDDEN_APP_HTML = r"""
const sequenceBadge = sequenceNumber && (state.camera.k > 0.26 || activeStoryIds.has(entry.id) || related)
? `${sequenceNumber}`
: "";
- return `
+ return `
${isSelected ? `` : ""}
${isFragment
? `${html(characterSymbol(character))}`
: distant
? `${html(characterSymbol(character))}`
- : svgAvatar(character, size, clipId, avatarExpressionForEntry(entry), state.zoom >= 2 ? "node-breathe" : "")}
+ : svgCharacter(character, size, clipId, characterExpressionForEntry(entry), state.zoom >= 2 ? "node-breathe" : "")}
${sequenceBadge}
${label}`;
}).join("");
@@ -3055,7 +3052,7 @@ HIDDEN_APP_HTML = r"""
function renderBreadcrumb(scope) {
const filters = activeFilters();
const parts = [``, ``];
- filters.forEach((filter) => parts.push(``));
+ filters.forEach((filter) => parts.push(``));
if (scope.focusedCluster) parts.push(``);
if (state.focusIds) parts.push(``);
$("breadcrumb").innerHTML = parts.join("");
@@ -3085,7 +3082,7 @@ HIDDEN_APP_HTML = r"""
$("activeState").innerHTML = `Whole sky visible. You can always return here.`;
return;
}
- $("activeState").innerHTML = active.map((item) => `${item.character ? avatarImg(item.character, "tiny") : ""}${html(item.label)}${item.id === "selection" ? "" : ``}`).join("") + ``;
+ $("activeState").innerHTML = active.map((item) => `${item.character ? characterImg(item.character, "tiny") : ""}${html(item.label)}${item.id === "selection" ? "" : ``}`).join("") + ``;
$("activeState").querySelectorAll("[data-clear]").forEach((button) => button.addEventListener("click", () => clearExplorationPart(button.dataset.clear)));
$("activeState").querySelector("[data-action='soft']")?.addEventListener("click", softReset);
$("activeState").querySelector("[data-action='full']")?.addEventListener("click", fullReset);
@@ -3554,8 +3551,8 @@ HIDDEN_APP_HTML = r"""
}
function renderPreview(entry) {
const body = html(entry.content || "").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/\*([^*]+)\*/g, "$1");
- const avatarPills = charactersForEntry(entry).map((character) => `${avatarImg(character, "tiny", avatarExpressionForEntry(entry))}${html(characterLabel(character))}`).join("");
- $("preview").innerHTML = `${body || "This entry is waiting for words."}${avatarPills}${html(entry.contentClass || "fragment")}${html(entry.observatoryRole || "ambient")}${html(layerName(depth(entry)))}${html(entry.emotionalTone)}${html(entry.rarity)}${(entry.surfaces || []).map((item) => `${html(item)}`).join("")}${(entry.symbols || []).map((item) => `${html(item)}`).join("")}
`;
+ const characterPills = charactersForEntry(entry).map((character) => `${characterImg(character, "tiny", characterExpressionForEntry(entry))}${html(characterLabel(character))}`).join("");
+ $("preview").innerHTML = `${body || "This entry is waiting for words."}${characterPills}${html(entry.contentClass || "fragment")}${html(entry.observatoryRole || "ambient")}${html(layerName(depth(entry)))}${html(entry.emotionalTone)}${html(entry.rarity)}${(entry.surfaces || []).map((item) => `${html(item)}`).join("")}${(entry.symbols || []).map((item) => `${html(item)}`).join("")}
`;
}
function renderConnections(entry) {
const stories = storiesForEntry(entry.id);
@@ -3582,7 +3579,7 @@ HIDDEN_APP_HTML = r"""
$("storyFlow").innerHTML = "";
return;
}
- const characters = (story.characters || []).map((character) => `${avatarImg(character, "tiny")}${html(characterLabel(character))}`).join("");
+ const characters = (story.characters || []).map((character) => `${characterImg(character, "tiny")}${html(characterLabel(character))}`).join("");
$("storyCover").innerHTML = `${html(story.title)}${html(story.description || "A quiet route waiting for a summary.")}
${characters}${html(story.tone || "warm")}${html(story.discoveryStyle || "gradual")}${story.hidden ? "hidden" : ""}${(story.markers || []).map((item) => `${html(item)}`).join("")}
`;
$("storyCover").querySelector("[data-read-story]")?.addEventListener("click", () => openStoryReader(story.id));
const items = storyItems(story);
@@ -3631,7 +3628,7 @@ HIDDEN_APP_HTML = r"""
const pool = state.entries.filter((entry) => !inStory.has(entry.id) && (!query || entryTextCorpus(entry).includes(query))).slice(0, 24);
$("memoryPool").innerHTML = pool.map((entry) => {
const character = primaryCharacterForEntry(entry);
- return ``;
+ return ``;
}).join("") || "Every visible memory is already in this story.
";
$("memoryPool").querySelectorAll("[data-id]").forEach((button) => {
button.addEventListener("click", () => selectNode(button.dataset.id));
@@ -3672,7 +3669,7 @@ HIDDEN_APP_HTML = r"""
$("readerStage").innerHTML = `Story scene
${html(item.title || "Story section")}
${html(item.content || "This scene is waiting for words.")}
`;
} else {
const entry = entryById(item.id);
- const characters = charactersForEntry(entry).map((character) => `${avatarImg(character, "tiny", avatarExpressionForEntry(entry))}${html(characterLabel(character))}`).join("");
+ const characters = charactersForEntry(entry).map((character) => `${characterImg(character, "tiny", characterExpressionForEntry(entry))}${html(characterLabel(character))}`).join("");
const kind = (entry.contentClass || "fragment") === "fragment" ? "Reusable memory light" : "Reusable memory";
$("readerStage").innerHTML = `${html(kind)}
${html(entry.title || "Untitled memory")}
${html(entry.content || "This memory is waiting for words.")}
${characters}${html(entry.emotionalTone || "warm")}appears in ${storiesForEntry(entry.id).length} ${storiesForEntry(entry.id).length === 1 ? "story" : "stories"}
`;
}
@@ -3916,10 +3913,10 @@ HIDDEN_APP_HTML = r"""
$("toneFilter").innerHTML = opt("", "All tones") + state.meta.tones.map((value) => opt(value)).join("");
$("rarityFilter").innerHTML = opt("", "All rarities") + state.meta.rarities.map((value) => opt(value)).join("");
$("layerKey").innerHTML = (state.meta.layers || []).map((layer) => `${html(layer.id)}${html(layer.name)}${html(layer.meaning)}
`).join("");
- $("characterAtlas").innerHTML = Object.keys(avatarRegistry()).map((character) => {
+ $("characterAtlas").innerHTML = Object.keys(characterRegistry()).map((character) => {
const config = characterConfig(character);
const territory = config.observatory?.territory || "memory territory";
- return ``;
+ return ``;
}).join("");
$("characterAtlas").querySelectorAll("[data-character]").forEach((button) => button.addEventListener("click", () => {
snapshotExploration(`character territory: ${button.dataset.character}`);
diff --git a/src/authoring_service/web.py b/src/authoring_service/web.py
index 7b569f4..30cc872 100755
--- a/src/authoring_service/web.py
+++ b/src/authoring_service/web.py
@@ -3,14 +3,12 @@
from __future__ import annotations
import json
-import mimetypes
import os
import sys
import time
from email.utils import formatdate
from http import HTTPStatus
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
-from pathlib import Path
from typing import Any
from urllib.parse import parse_qs, urlparse
@@ -89,27 +87,6 @@ class Handler(BaseHTTPRequestHandler):
except Exception as exc:
self.send_json({"error": str(exc)}, HTTPStatus.INTERNAL_SERVER_ERROR)
return
- if parsed.path.startswith("/assets/avatars/"):
- try:
- relative = Path(parsed.path.lstrip("/"))
- if relative.is_absolute() or ".." in relative.parts:
- self.send_error(HTTPStatus.NOT_FOUND)
- return
- asset_path = (ROOT / relative).resolve()
- avatars_root = (ROOT / "assets" / "avatars").resolve()
- if not asset_path.is_file() or avatars_root not in asset_path.parents:
- self.send_error(HTTPStatus.NOT_FOUND)
- return
- body = asset_path.read_bytes()
- self.send_response(HTTPStatus.OK)
- self.send_header("Content-Type", mimetypes.guess_type(asset_path.name)[0] or "application/octet-stream")
- self.send_header("Content-Length", str(len(body)))
- self.send_header("Cache-Control", "no-store")
- self.end_headers()
- self.wfile.write(body)
- except Exception:
- self.send_error(HTTPStatus.NOT_FOUND)
- return
self.send_error(HTTPStatus.NOT_FOUND)
def do_POST(self) -> None: