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 `${html(label)} portrait`; + 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)}`; + ${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: