diff --git a/authoring_server.py b/authoring_server.py index c6b5c4f..f709eaa 100755 --- a/authoring_server.py +++ b/authoring_server.py @@ -12,6 +12,7 @@ import subprocess import sys import threading import time +import mimetypes from dataclasses import dataclass, field from datetime import datetime from email.parser import BytesParser @@ -148,6 +149,18 @@ CHARACTER_REGISTRY = { "displayLabel": "young z", "aliases": ["Young Z", "young z", "young-z", "young_z", "young"], "territoryColor": "#d8a95a", + "glow": "#f0b85c", + "avatar": "/assets/avatars/young-z.jpeg", + "fallbackAvatar": "/assets/avatars/z.jpeg", + "expressions": { + "default": "/assets/avatars/young-z.jpeg", + "nostalgic": "/assets/avatars/young-z.jpeg", + "playful": "/assets/avatars/young-z.jpeg", + }, + "sizes": {"tiny": 18, "medium": 42, "close": 56, "focus": 82}, + "observatory": {"territory": "nostalgic/playful memories", "anchor": {"x": 0.23, "y": 0.7}, "shimmer": "soft"}, + "presenceTones": ["nostalgic", "funny", "hopeful"], + "presenceKeywords": ["childhood", "play", "desk", "crayon", "young", "small", "blanket", "memory"], "symbol": "Y", "motifs": ["crayon sun", "blanket cape", "childhood desk"], "themes": ["childhood", "play", "memory", "safety"], @@ -158,6 +171,14 @@ CHARACTER_REGISTRY = { "displayLabel": "z", "aliases": ["Z", "z", "zaine"], "territoryColor": "#d6c38a", + "glow": "#ead68e", + "avatar": "/assets/avatars/z.jpeg", + "fallbackAvatar": "/assets/avatars/z.jpeg", + "expressions": {"default": "/assets/avatars/z.jpeg", "calm": "/assets/avatars/z.jpeg"}, + "sizes": {"tiny": 18, "medium": 42, "close": 56, "focus": 82}, + "observatory": {"territory": "self/archive memories", "anchor": {"x": 0.5, "y": 0.52}, "shimmer": "steady"}, + "presenceTones": ["warm", "soft", "hopeful"], + "presenceKeywords": ["website", "archive", "home", "self", "making", "return", "zaine"], "symbol": "Z", "motifs": ["archive", "website", "home"], "themes": ["selfhood", "return", "making"], @@ -168,6 +189,18 @@ CHARACTER_REGISTRY = { "displayLabel": "aphy", "aliases": ["Aphy", "aphy", "aphy_bot", "aphy bot"], "territoryColor": "#7fb089", + "glow": "#9dd3a6", + "avatar": "/assets/avatars/aphy.jpeg", + "fallbackAvatar": "/assets/avatars/z.jpeg", + "expressions": { + "default": "/assets/avatars/aphy.jpeg", + "system": "/assets/avatars/aphy.jpeg", + "amused": "/assets/avatars/aphy.jpeg", + }, + "sizes": {"tiny": 18, "medium": 42, "close": 56, "focus": 82}, + "observatory": {"territory": "system/symbolic areas", "anchor": {"x": 0.77, "y": 0.35}, "shimmer": "diagnostic"}, + "presenceTones": ["funny", "strange", "hopeful"], + "presenceKeywords": ["system", "console", "diagnostic", "keyboard", "search", "terminal", "backup", "symbolic", "query"], "symbol": "A", "motifs": ["console", "diagnostic", "backup"], "themes": ["humor", "systems", "care through tools"], @@ -178,6 +211,19 @@ CHARACTER_REGISTRY = { "displayLabel": "lima", "aliases": ["Lima", "lima"], "territoryColor": "#d06b78", + "glow": "#f2c58b", + "avatar": "/assets/avatars/lima.jpg", + "fallbackAvatar": "/assets/avatars/z.jpeg", + "expressions": { + "default": "/assets/avatars/lima.jpg", + "calm": "/assets/avatars/lima.jpg", + "warm": "/assets/avatars/lima.jpg", + "protective": "/assets/avatars/lima.jpg", + }, + "sizes": {"tiny": 18, "medium": 42, "close": 56, "focus": 82}, + "observatory": {"territory": "warm/protective arcs", "anchor": {"x": 0.29, "y": 0.36}, "shimmer": "warm"}, + "presenceTones": ["warm", "protective", "soft"], + "presenceKeywords": ["lima", "love", "warm", "kitchen", "light", "ring", "home", "protect", "eat"], "symbol": "L", "motifs": ["warmth", "kitchen light", "ring"], "themes": ["love", "home", "grounding"], @@ -188,6 +234,18 @@ CHARACTER_REGISTRY = { "displayLabel": "sensei chi", "aliases": ["Sensei Chi", "sensei chi", "sensei-chi", "sensei_chi", "sensei"], "territoryColor": "#75a9bd", + "glow": "#9ccddd", + "avatar": "/assets/avatars/sensei-chi.jpeg", + "fallbackAvatar": "/assets/avatars/z.jpeg", + "expressions": { + "default": "/assets/avatars/sensei-chi.jpeg", + "reflective": "/assets/avatars/sensei-chi.jpeg", + "wise": "/assets/avatars/sensei-chi.jpeg", + }, + "sizes": {"tiny": 18, "medium": 42, "close": 56, "focus": 82}, + "observatory": {"territory": "reflective areas", "anchor": {"x": 0.68, "y": 0.68}, "shimmer": "quiet"}, + "presenceTones": ["wise", "melancholy", "soft"], + "presenceKeywords": ["reflection", "patience", "wisdom", "lesson", "tea", "garden", "quiet", "sensei"], "symbol": "S", "motifs": ["tea", "garden", "quiet lesson"], "themes": ["reflection", "patience", "wisdom"], @@ -198,6 +256,18 @@ CHARACTER_REGISTRY = { "displayLabel": "future z", "aliases": ["Future Z", "future z", "future-z", "future_z", "future"], "territoryColor": "#a58ac9", + "glow": "#c2a4ee", + "avatar": "/assets/avatars/future-z.jpeg", + "fallbackAvatar": "/assets/avatars/z.jpeg", + "expressions": { + "default": "/assets/avatars/future-z.jpeg", + "temporal": "/assets/avatars/future-z.jpeg", + "reassuring": "/assets/avatars/future-z.jpeg", + }, + "sizes": {"tiny": 18, "medium": 42, "close": 56, "focus": 82}, + "observatory": {"territory": "temporal regions", "anchor": {"x": 0.73, "y": 0.18}, "shimmer": "temporal"}, + "presenceTones": ["hopeful", "melancholy", "wise"], + "presenceKeywords": ["future", "time", "clock", "older", "tomorrow", "age", "reassurance", "continuity"], "symbol": "F", "motifs": ["clock", "age 40", "future log"], "themes": ["time", "reassurance", "continuity"], @@ -2533,6 +2603,42 @@ HIDDEN_APP_HTML = r""" background: rgba(251, 241, 220, 0.08); font-size: 12px; } + .avatar-pill, .avatar-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); + 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); + } + .avatar-row { + width: 100%; + justify-content: space-between; + margin-top: 10px; + padding: 8px; + border: 1px solid rgba(232, 202, 139, 0.15); + border-radius: 8px; + background: rgba(251, 241, 220, 0.055); + } + .avatar-row .subtle { font-size: 12px; } + .character-tag { + min-height: 32px; + border: 1px solid rgba(232, 202, 139, 0.2); + border-radius: 999px; + padding: 3px 9px 3px 4px; + background: rgba(251, 241, 220, 0.075); + color: #f3ddb2; + font-size: 12px; + font-weight: 800; + } + .character-tag .avatar-thumb { --avatar-size: 24px; } .layer-key { display: grid; gap: 8px; margin-top: 14px; } .layer-row { display: grid; @@ -2576,12 +2682,19 @@ HIDDEN_APP_HTML = r""" .memory-node { cursor: grab; transition: opacity 160ms ease, filter 180ms ease; } .memory-node:active { cursor: grabbing; } .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.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)); } - .memory-node.drop-target circle { stroke: #fff5c9; stroke-width: 4; filter: drop-shadow(0 0 24px rgba(255, 232, 177, 0.7)); } - .memory-node.invalid-drop circle { stroke: rgba(208, 107, 120, 0.9); stroke-width: 3; } + .memory-node .node-shell, .memory-node .node-portrait, .memory-node .node-marker { + 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))); + transition: r 160ms ease, stroke 160ms ease, opacity 160ms ease, filter 180ms ease; + } + .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:hover .node-shell, .memory-node:hover .node-portrait { 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 { 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; } .cluster-node { cursor: pointer; transition: transform 160ms ease, filter 160ms ease; } .cluster-node circle { stroke: rgba(255, 243, 199, 0.75); @@ -2600,6 +2713,10 @@ HIDDEN_APP_HTML = r""" } .cluster-node .count { font-size: 18px; font-weight: 850; } .cluster-halo { fill: rgba(211, 166, 77, 0.08); stroke: rgba(211, 166, 77, 0.22); stroke-dasharray: 3 8; } + .territory-anchor { pointer-events: none; opacity: 0.72; } + .territory-anchor circle { fill: rgba(251, 241, 220, 0.035); stroke-width: 1.2; stroke-dasharray: 5 11; } + .territory-anchor image { opacity: 0.88; } + .territory-anchor text { fill: rgba(248, 234, 209, 0.66); font-size: 18px; paint-order: stroke; stroke: rgba(14, 11, 8, 0.78); stroke-width: 3px; } .drag-preview-line { stroke: rgba(255, 232, 177, 0.78); stroke-width: 3; stroke-dasharray: 8 8; pointer-events: none; } .drag-preview-halo { fill: rgba(211, 166, 77, 0.1); stroke: rgba(255, 232, 177, 0.58); stroke-width: 2; stroke-dasharray: 4 7; pointer-events: none; } .drag-preview-label rect { fill: rgba(18, 16, 13, 0.86); stroke: rgba(255, 232, 177, 0.42); rx: 7; } @@ -2744,6 +2861,15 @@ HIDDEN_APP_HTML = r""" 0%, 100% { opacity: 0.72; } 50% { opacity: 1; } } + @keyframes avatar-breathe { + 0%, 100% { transform: scale(1); opacity: 0.92; } + 50% { transform: scale(1.035); opacity: 1; } + } + @keyframes constellation-shimmer { + 0%, 100% { opacity: 0.48; } + 50% { opacity: 0.82; } + } + .constellation-shimmer { animation: constellation-shimmer 9s ease-in-out infinite; } .core-glow { animation: breathe 7s ease-in-out infinite; } @media (max-width: 1120px) { body { overflow: auto; } @@ -2807,6 +2933,10 @@ HIDDEN_APP_HTML = r""" +
+

Character Territories

+
+

Integrity

checking character territories.
@@ -2845,6 +2975,7 @@ HIDDEN_APP_HTML = r"""

Choose a memory

The side panel opens from the constellation.
+
@@ -2854,6 +2985,8 @@ HIDDEN_APP_HTML = r""" +
+