Files
clawmates/handoff/visualizations/Clawmates World.dc.html
T
Omar SobhandClaude Opus 4.8 39bacbd1d2 World page: live WebGL Gource-style visualization (Phases A+B)
Replaces the static React-Flow world forest with a real-time WebGL view of the
swarm working, plus the live event contract that feeds it.

Phase A — the live contract + feed:
- frontend/src/lib/live/taxonomy.ts — the 13-event Clawmates Event Taxonomy as
  typed TS (the shared World/Observe contract).
- frontend/src/lib/live/useClawmatesLive.ts — native shared-singleton live client
  (EventSource to /api/world/live, typed fan-out, replay-of-last-state to late
  subscribers, refcounted, synthetic fallback so views are always alive).
- crates/cm-api/src/routes/world.rs — GET /api/world/live, authed + workspace-
  scoped SSE emitting the taxonomy (real agent.status from live-container state,
  a topology.update of the workspace's agents, telemetry with real doorsPending),
  mirroring run_events_sse. normalize() seam documented for run_events->world.touch.

Phase B — the WebGL engine:
- frontend/src/components/world/engine.ts — Gource-inspired force-directed model:
  org/company/team tree (sibling repulsion + parent spring + friction), agent
  pawns that converge on the touched node and beam it, world nodes that glow with
  heat and fade when idle. Framework-agnostic (renderer-independent) state+math.
- frontend/src/components/world/WorldCanvas.tsx — three.js scene (ortho cam,
  UnrealBloom), render loop syncing engine state, camera auto-fit, HTML labels,
  raycast click->select, Hierarchy/Flat/Live formation switch.
- Dashboard.tsx: swap <WorldFlow/> -> <WorldCanvas/> at the world-tier seam
  (shared claw-tier pieces untouched; WorldFlow.tsx kept for now).
- Adds three (+ @types/three).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-23 21:23:34 -07:00

465 lines
36 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="./support.js"></script>
</head>
<body>
<x-dc>
<helmet>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; background: #08080a; }
body { font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; }
@keyframes cm-flow { to { stroke-dashoffset: -24; } }
@keyframes cm-blink { 0%,100% { opacity: 1; } 50% { opacity: .25; } }
@keyframes cm-halo { 0% { transform: scale(.7); opacity: .5; } 100% { transform: scale(2); opacity: 0; } }
@keyframes cm-dash { to { stroke-dashoffset: -40; } }
</style>
</helmet>
<div style="width:100%; height:100vh; min-height:640px; background:#08080a; color:#f3f3f5; display:flex; flex-direction:column; overflow:hidden;">
<!-- TOP BAR -->
<div style="height:50px; flex:none; display:flex; align-items:center; gap:13px; padding:0 16px; border-bottom:1px solid rgba(255,255,255,.06); background:linear-gradient(180deg,#0d0d10,#0a0a0c);">
<div style="display:flex; align-items:center; gap:9px;">
<svg width="20" height="20" viewBox="0 0 22 22" fill="none"><path d="M11 3 L18.5 17 L3.5 17 Z" stroke="#ff6f61" stroke-width="1.3" stroke-linejoin="round" opacity="0.55"></path><circle cx="11" cy="3.5" r="2.2" fill="#ff6f61"></circle><circle cx="18" cy="17" r="2.2" fill="#ff6f61"></circle><circle cx="4" cy="17" r="2.2" fill="#ff6f61"></circle></svg>
<span style="font-size:14px; font-weight:700;">Clawmates</span>
</div>
<span style="font-family:'JetBrains Mono',monospace; font-size:12px; color:#f3f3f5; background:rgba(255,111,97,.12); border:1px solid rgba(255,111,97,.28); padding:3px 9px; border-radius:6px;">Large World</span>
<span style="color:#3a3a40;">/</span>
<span style="font-family:'JetBrains Mono',monospace; font-size:12px; color:#6a6a72;">Agents</span>
<div style="flex:1;"></div>
<div style="display:flex; align-items:center; gap:7px; font-family:'JetBrains Mono',monospace; font-size:11px; color:#5fd08a; padding:5px 10px; border:1px solid rgba(95,208,138,.25); border-radius:7px; background:rgba(95,208,138,.06);">
<span style="width:6px; height:6px; border-radius:50%; background:#5fd08a; animation:cm-blink 1.6s infinite;"></span>1 org · 2 agents
</div>
<div style="width:30px; height:30px; border-radius:8px; background:linear-gradient(135deg,#ff8a7a,#ff5f57); display:flex; align-items:center; justify-content:center; font-size:12px; font-weight:700; color:#2a0d0a;">O</div>
</div>
<!-- BODY -->
<div style="flex:1; display:flex; min-height:0;">
<!-- ICON RAIL -->
<div style="width:54px; flex:none; border-right:1px solid rgba(255,255,255,.06); background:#0a0a0c; display:flex; flex-direction:column; align-items:center; padding:12px 0; gap:6px;">
<div style="position:relative; width:40px; height:44px; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:4px; border-radius:9px; color:#ff6f61; background:rgba(255,111,97,.1);">
<span style="position:absolute; left:0; top:7px; bottom:7px; width:3px; border-radius:0 3px 3px 0; background:#ff6f61;"></span>
<svg width="18" height="18" viewBox="0 0 20 20"><circle cx="10" cy="10" r="7.5" stroke="currentColor" stroke-width="1.4" fill="none"></circle><circle cx="10" cy="10" r="2.4" fill="currentColor"></circle></svg>
<span style="font-family:'JetBrains Mono',monospace; font-size:7px; letter-spacing:.05em;">WORLD</span>
</div>
<div style="width:40px; height:44px; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:4px; border-radius:9px; color:#5a5a62;">
<svg width="18" height="18" viewBox="0 0 20 20"><rect x="4" y="4" width="12" height="12" rx="3.5" stroke="currentColor" stroke-width="1.4" fill="none"></rect><circle cx="10" cy="10" r="2.2" fill="currentColor"></circle></svg>
<span style="font-family:'JetBrains Mono',monospace; font-size:7px; letter-spacing:.05em;">AGENT</span>
</div>
<div style="flex:1;"></div>
<div style="width:28px; height:28px; border-radius:8px; border:1px dashed rgba(255,255,255,.16); display:flex; align-items:center; justify-content:center; color:#ff6f61; font-size:16px; font-weight:300;">+</div>
</div>
<!-- LEFT LIST -->
<div style="width:200px; flex:none; border-right:1px solid rgba(255,255,255,.06); background:#0b0b0e; display:flex; flex-direction:column; min-height:0;">
<div style="padding:14px 14px 10px;">
<div style="font-family:'JetBrains Mono',monospace; font-size:10px; letter-spacing:.12em; color:#5a5a62;">1 ORG · 2 AGENTS</div>
<div style="font-size:17px; font-weight:700; margin-top:3px;">Large World</div>
</div>
<div style="flex:1; overflow:hidden; padding:6px;">
<!-- WORLD_TREE_LIST -->
</div>
</div>
<!-- CENTER STAGE -->
<div style="flex:1; position:relative; min-width:0; overflow:hidden; background:radial-gradient(130% 100% at 50% 0%, #0e0e13 0%, #08080a 65%);">
<!-- VIEW SWITCHER -->
<div style="position:absolute; top:14px; left:50%; transform:translateX(-50%); z-index:20; display:flex; padding:4px; border-radius:11px; background:rgba(14,14,18,.85); border:1px solid rgba(255,255,255,.1); backdrop-filter:blur(10px); gap:3px;">
<sc-for list="{{ modes }}" as="m" hint-placeholder-count="3">
<div style="display:flex; align-items:center; gap:7px; font-family:'JetBrains Mono',monospace; font-size:12px; font-weight:600; padding:7px 15px; border-radius:8px; cursor:pointer; transition:all .2s; {{ m.style }}" onClick="{{ m.onPick }}">{{ m.icon }} {{ m.label }}</div>
</sc-for>
</div>
<!-- GRAPH TOOLS -->
<div style="position:absolute; top:14px; left:16px; z-index:15; width:150px; border-radius:11px; border:1px solid rgba(255,255,255,.08); background:rgba(14,14,18,.7); backdrop-filter:blur(8px); padding:10px;">
<div style="font-family:'JetBrains Mono',monospace; font-size:9px; letter-spacing:.12em; color:#5a5a62; margin-bottom:9px;">GRAPH TOOLS</div>
<div style="display:flex; flex-direction:column; gap:6px;">
<div style="display:flex; align-items:center; gap:8px; font-size:11px; color:#ff8a7a; padding:6px 8px; border-radius:7px; background:rgba(255,111,97,.08);">▣ Save layout</div>
<div style="display:flex; align-items:center; gap:8px; font-size:11px; color:#cfcfd5; padding:6px 8px; border-radius:7px; background:rgba(255,255,255,.03);">⤢ Fit to view</div>
<div style="display:flex; align-items:center; gap:8px; font-size:11px; color:#cfcfd5; padding:6px 8px; border-radius:7px; background:rgba(255,255,255,.03);">↺ Reset layout</div>
</div>
</div>
<!-- caption -->
<div style="position:absolute; bottom:16px; left:50%; transform:translateX(-50%); z-index:15; text-align:center;">
<div style="font-family:'JetBrains Mono',monospace; font-size:10px; color:#5a5a62;">{{ caption }}</div>
</div>
<!-- VIEW LAYERS -->
<div style="position:absolute; inset:0; display:{{ hShow }};">
<div style="position:absolute; inset:0;">
<svg viewBox="0 0 100 100" preserveAspectRatio="none" style="position:absolute; inset:0; width:100%; height:100%;">
<path d="M50,15 C50,22 42,24 42,31" fill="none" stroke="rgba(95,208,138,.4)" stroke-width="1.1" stroke-dasharray="2 3" vector-effect="non-scaling-stroke" style="animation:cm-dash 2s linear infinite;"></path>
<path d="M42,33 C42,42 50,44 50,51" fill="none" stroke="rgba(95,208,138,.4)" stroke-width="1.1" stroke-dasharray="2 3" vector-effect="non-scaling-stroke" style="animation:cm-dash 2.4s linear infinite;"></path>
<path d="M50,55 C50,64 38,66 36,74" fill="none" stroke="rgba(95,208,138,.35)" stroke-width="1.1" stroke-dasharray="2 3" vector-effect="non-scaling-stroke" style="animation:cm-dash 2.1s linear infinite;"></path>
<path d="M50,55 C50,64 60,66 62,74" fill="none" stroke="rgba(255,111,97,.5)" stroke-width="1.2" stroke-dasharray="2 3" vector-effect="non-scaling-stroke" style="animation:cm-dash 1.6s linear infinite;"></path>
</svg>
<!-- Zeus (ORG) -->
<div style="position:absolute; left:50%; top:15%; transform:translate(-50%,-50%); display:flex; align-items:center; gap:10px; padding:10px 14px; border-radius:12px; background:#11101a; border:1px solid rgba(201,138,240,.4); box-shadow:0 8px 24px rgba(0,0,0,.4); cursor:pointer;">
<div style="width:32px; height:32px; border-radius:9px; background:linear-gradient(135deg,#c98af0,#9a5ad8); display:flex; align-items:center; justify-content:center; font-weight:700; color:#1a0a2a; font-size:14px;">Z</div>
<div><div style="font-size:14px; font-weight:700;">Zeus</div><div style="font-family:'JetBrains Mono',monospace; font-size:9px; color:#c98af0;">ORG · 1 company</div></div>
<span style="color:#5a5a62; font-size:11px; margin-left:6px;"></span>
</div>
<!-- thor (COMPANY) -->
<div style="position:absolute; left:42%; top:32%; transform:translate(-50%,-50%); display:flex; align-items:center; gap:10px; padding:10px 14px; border-radius:12px; background:#0e0f1a; border:1px solid rgba(138,154,240,.4); box-shadow:0 8px 24px rgba(0,0,0,.4); cursor:pointer;">
<div style="width:32px; height:32px; border-radius:9px; background:linear-gradient(135deg,#8a9af0,#5a6ad8); display:flex; align-items:center; justify-content:center; font-weight:700; color:#0a0e2a; font-size:14px;">T</div>
<div><div style="font-size:14px; font-weight:700;">thor</div><div style="font-family:'JetBrains Mono',monospace; font-size:9px; color:#8a9af0;">COMPANY · 1 team</div></div>
<span style="color:#5a5a62; font-size:11px; margin-left:6px;"></span>
</div>
<!-- POD01 (TEAM) -->
<div style="position:absolute; left:50%; top:53%; transform:translate(-50%,-50%); display:flex; align-items:center; gap:10px; padding:10px 14px; border-radius:12px; background:#0a1412; border:1px solid rgba(95,208,138,.4); box-shadow:0 8px 24px rgba(0,0,0,.4); cursor:pointer;">
<div style="width:32px; height:32px; border-radius:9px; background:linear-gradient(135deg,#6fd0c0,#4aa3b8); display:flex; align-items:center; justify-content:center; font-weight:700; color:#06201f; font-size:14px;">P</div>
<div><div style="font-size:14px; font-weight:700;">POD01</div><div style="font-family:'JetBrains Mono',monospace; font-size:9px; color:#5fd08a;">TEAM · 2 agents</div></div>
<span style="color:#5a5a62; font-size:11px; margin-left:6px;"></span>
</div>
<!-- Morpheus -->
<div style="position:absolute; left:36%; top:76%; transform:translate(-50%,-50%); display:flex; flex-direction:column; align-items:center; gap:7px; cursor:pointer;">
<div style="width:44px; height:44px; border-radius:50%; background:linear-gradient(135deg,#ff8a7a,#ff5f57); display:flex; align-items:center; justify-content:center; font-size:16px; font-weight:700; color:#2a0d0a; box-shadow:0 0 22px rgba(255,111,97,.35); position:relative;">M<span style="position:absolute; right:1px; bottom:1px; width:9px; height:9px; border-radius:50%; background:#5fd08a; border:2px solid #08080a;"></span></div>
<div style="text-align:center;"><div style="font-size:12px; font-weight:600;">Morpheus</div><div style="font-family:'JetBrains Mono',monospace; font-size:9px; color:#6a6a72;">Project Manager</div></div>
</div>
<!-- Smith (selected) -->
<div style="position:absolute; left:62%; top:76%; transform:translate(-50%,-50%); display:flex; flex-direction:column; align-items:center; gap:7px; cursor:pointer;">
<div style="position:relative; width:50px; height:50px;">
<div style="position:absolute; inset:-5px; border-radius:50%; border:2px solid #ff6f61; box-shadow:0 0 0 4px rgba(255,111,97,.12);"></div>
<div style="position:absolute; inset:0; border-radius:50%; background:rgba(255,111,97,.4); animation:cm-halo 1.8s ease-out infinite;"></div>
<div style="position:relative; width:50px; height:50px; border-radius:50%; background:linear-gradient(135deg,#ff8a7a,#ff5f57); display:flex; align-items:center; justify-content:center; font-size:18px; font-weight:700; color:#2a0d0a; box-shadow:0 0 28px rgba(255,111,97,.5);">S<span style="position:absolute; right:2px; bottom:2px; width:10px; height:10px; border-radius:50%; background:#5fd08a; border:2px solid #08080a;"></span></div>
</div>
<div style="text-align:center;"><div style="font-size:12px; font-weight:700; color:#fff;">Smith</div><div style="font-family:'JetBrains Mono',monospace; font-size:9px; color:#ff8a7a;">Research Specialist</div></div>
</div>
</div>
</div>
<div style="position:absolute; inset:0; display:{{ fShow }};">
<div style="position:absolute; inset:0;">
<svg viewBox="0 0 100 100" preserveAspectRatio="none" style="position:absolute; inset:0; width:100%; height:100%;">
<!-- flat peer mesh: no hierarchy, every node links to peers -->
<path d="M50,20 L82,44" stroke="rgba(255,255,255,.1)" stroke-width="1" vector-effect="non-scaling-stroke"></path>
<path d="M82,44 L68,78" stroke="rgba(255,255,255,.1)" stroke-width="1" vector-effect="non-scaling-stroke"></path>
<path d="M68,78 L32,78" stroke="rgba(255,111,97,.45)" stroke-width="1.2" stroke-dasharray="2 3" vector-effect="non-scaling-stroke" style="animation:cm-dash 1.8s linear infinite;"></path>
<path d="M32,78 L18,44" stroke="rgba(255,255,255,.1)" stroke-width="1" vector-effect="non-scaling-stroke"></path>
<path d="M18,44 L50,20" stroke="rgba(255,255,255,.1)" stroke-width="1" vector-effect="non-scaling-stroke"></path>
<path d="M50,20 L68,78" stroke="rgba(94,200,216,.35)" stroke-width="1" stroke-dasharray="2 3" vector-effect="non-scaling-stroke" style="animation:cm-dash 2.3s linear infinite;"></path>
<path d="M50,20 L32,78" stroke="rgba(255,255,255,.07)" stroke-width="1" vector-effect="non-scaling-stroke"></path>
<path d="M18,44 L82,44" stroke="rgba(255,255,255,.07)" stroke-width="1" vector-effect="non-scaling-stroke"></path>
</svg>
<!-- peer chip: Zeus -->
<div style="position:absolute; left:50%; top:20%; transform:translate(-50%,-50%); display:flex; flex-direction:column; align-items:center; gap:7px; cursor:pointer;">
<div style="width:40px; height:40px; border-radius:11px; background:linear-gradient(135deg,#c98af0,#9a5ad8); display:flex; align-items:center; justify-content:center; font-weight:700; color:#1a0a2a; font-size:15px;">Z</div>
<div style="text-align:center;"><div style="font-size:12px; font-weight:600;">Zeus</div><div style="font-family:'JetBrains Mono',monospace; font-size:9px; color:#6a6a72;">org</div></div>
</div>
<!-- thor -->
<div style="position:absolute; left:82%; top:44%; transform:translate(-50%,-50%); display:flex; flex-direction:column; align-items:center; gap:7px; cursor:pointer;">
<div style="width:40px; height:40px; border-radius:11px; background:linear-gradient(135deg,#8a9af0,#5a6ad8); display:flex; align-items:center; justify-content:center; font-weight:700; color:#0a0e2a; font-size:15px;">T</div>
<div style="text-align:center;"><div style="font-size:12px; font-weight:600;">thor</div><div style="font-family:'JetBrains Mono',monospace; font-size:9px; color:#6a6a72;">company</div></div>
</div>
<!-- POD01 -->
<div style="position:absolute; left:68%; top:78%; transform:translate(-50%,-50%); display:flex; flex-direction:column; align-items:center; gap:7px; cursor:pointer;">
<div style="width:40px; height:40px; border-radius:11px; background:linear-gradient(135deg,#6fd0c0,#4aa3b8); display:flex; align-items:center; justify-content:center; font-weight:700; color:#06201f; font-size:15px;">P</div>
<div style="text-align:center;"><div style="font-size:12px; font-weight:600;">POD01</div><div style="font-family:'JetBrains Mono',monospace; font-size:9px; color:#6a6a72;">team</div></div>
</div>
<!-- Smith (selected) -->
<div style="position:absolute; left:32%; top:78%; transform:translate(-50%,-50%); display:flex; flex-direction:column; align-items:center; gap:7px; cursor:pointer;">
<div style="position:relative; width:46px; height:46px;">
<div style="position:absolute; inset:-4px; border-radius:50%; border:2px solid #ff6f61;"></div>
<div style="position:relative; width:46px; height:46px; border-radius:50%; background:linear-gradient(135deg,#ff8a7a,#ff5f57); display:flex; align-items:center; justify-content:center; font-weight:700; color:#2a0d0a; font-size:16px;">S<span style="position:absolute; right:1px; bottom:1px; width:9px; height:9px; border-radius:50%; background:#5fd08a; border:2px solid #08080a;"></span></div>
</div>
<div style="text-align:center;"><div style="font-size:12px; font-weight:700; color:#fff;">Smith</div><div style="font-family:'JetBrains Mono',monospace; font-size:9px; color:#ff8a7a;">agent</div></div>
</div>
<!-- Morpheus -->
<div style="position:absolute; left:18%; top:44%; transform:translate(-50%,-50%); display:flex; flex-direction:column; align-items:center; gap:7px; cursor:pointer;">
<div style="width:46px; height:46px; border-radius:50%; background:linear-gradient(135deg,#ff8a7a,#ff5f57); display:flex; align-items:center; justify-content:center; font-weight:700; color:#2a0d0a; font-size:16px; position:relative;">M<span style="position:absolute; right:1px; bottom:1px; width:9px; height:9px; border-radius:50%; background:#5fd08a; border:2px solid #08080a;"></span></div>
<div style="text-align:center;"><div style="font-size:12px; font-weight:600;">Morpheus</div><div style="font-family:'JetBrains Mono',monospace; font-size:9px; color:#6a6a72;">agent</div></div>
</div>
</div>
</div>
<div style="position:absolute; inset:0; display:{{ lShow }};">
<div style="position:absolute; inset:0;">
<canvas ref="{{ canvasRef }}" style="width:100%; height:100%; display:block;"></canvas>
<!-- live legend -->
<div style="position:absolute; top:64px; left:16px; z-index:14; display:flex; flex-direction:column; gap:7px; padding:11px 13px; border-radius:11px; border:1px solid rgba(255,255,255,.08); background:rgba(14,14,18,.7); backdrop-filter:blur(8px);">
<div style="font-family:'JetBrains Mono',monospace; font-size:9px; letter-spacing:.12em; color:#5a5a62; margin-bottom:1px;">CONVERGING ON</div>
<div style="display:flex; align-items:center; gap:8px;"><span style="width:9px; height:9px; border-radius:50%; background:#ff8a7a;"></span><span style="font-family:'JetBrains Mono',monospace; font-size:11px; color:#cfcfd5;">services</span></div>
<div style="display:flex; align-items:center; gap:8px;"><span style="width:9px; height:9px; border-radius:50%; background:#5ec8d8;"></span><span style="font-family:'JetBrains Mono',monospace; font-size:11px; color:#cfcfd5;">events</span></div>
<div style="display:flex; align-items:center; gap:8px; margin-top:4px;"><span style="width:9px; height:9px; border-radius:50%; background:#ff5f57; box-shadow:0 0 8px #ff5f57;"></span><span style="font-family:'JetBrains Mono',monospace; font-size:11px; color:#cfcfd5;">agents (M · S)</span></div>
</div>
<!-- live HUD -->
<div style="position:absolute; top:64px; right:16px; z-index:14; display:flex; gap:8px;">
<div style="display:flex; align-items:center; gap:6px; padding:6px 11px; border-radius:8px; border:1px solid rgba(94,200,216,.25); background:rgba(94,200,216,.06); font-family:'JetBrains Mono',monospace; font-size:10px; color:#5ec8d8;"><span style="width:6px; height:6px; border-radius:50%; background:#5ec8d8; animation:cm-blink 1s infinite;"></span>9 active nodes</div>
<div style="display:flex; align-items:center; gap:6px; padding:6px 11px; border-radius:8px; border:1px solid rgba(255,255,255,.08); background:rgba(20,20,24,.6); font-family:'JetBrains Mono',monospace; font-size:10px; color:#9a9aa2;">▮▮ 14 touches/min</div>
</div>
</div>
</div>
<!-- ZOOM -->
<div style="position:absolute; bottom:16px; left:16px; z-index:15; display:flex; flex-direction:column; gap:5px;">
<div style="width:28px; height:28px; border-radius:7px; border:1px solid rgba(255,255,255,.1); background:rgba(20,20,24,.7); display:flex; align-items:center; justify-content:center; color:#9a9aa2; font-size:15px;">+</div>
<div style="width:28px; height:28px; border-radius:7px; border:1px solid rgba(255,255,255,.1); background:rgba(20,20,24,.7); display:flex; align-items:center; justify-content:center; color:#9a9aa2; font-size:15px;"></div>
<div style="width:28px; height:28px; border-radius:7px; border:1px solid rgba(255,255,255,.1); background:rgba(20,20,24,.7); display:flex; align-items:center; justify-content:center; color:#9a9aa2;"><svg width="12" height="12" viewBox="0 0 14 14"><path d="M2 2h3M2 2v3M12 2h-3M12 2v3M2 12h3M2 12v-3M12 12h-3M12 12v-3" stroke="currentColor" stroke-width="1.3"></path></svg></div>
</div>
<!-- MINIMAP -->
<div style="position:absolute; bottom:16px; right:16px; z-index:15; width:150px; height:96px; border-radius:9px; border:1px solid rgba(255,255,255,.08); background:rgba(12,12,15,.8); overflow:hidden;">
<div style="position:absolute; top:5px; left:8px; font-family:'JetBrains Mono',monospace; font-size:8px; letter-spacing:.1em; color:#5a5a62;">{{ minimapLabel }}</div>
<svg viewBox="0 0 150 96" style="position:absolute; inset:0; width:100%; height:100%; opacity:.7;">
<line x1="75" y1="48" x2="50" y2="30" stroke="rgba(255,255,255,.12)" stroke-width="1"></line>
<line x1="75" y1="48" x2="100" y2="34" stroke="rgba(255,255,255,.12)" stroke-width="1"></line>
<line x1="75" y1="48" x2="60" y2="66" stroke="rgba(255,255,255,.12)" stroke-width="1"></line>
<line x1="75" y1="48" x2="96" y2="64" stroke="rgba(255,255,255,.12)" stroke-width="1"></line>
<circle cx="75" cy="48" r="4" fill="#ff6f61"></circle>
<circle cx="50" cy="30" r="2.5" fill="#5ec8d8"></circle>
<circle cx="100" cy="34" r="2.5" fill="#5fd08a"></circle>
<circle cx="60" cy="66" r="2.5" fill="#c98af0"></circle>
<circle cx="96" cy="64" r="2.5" fill="#e8b465"></circle>
<rect x="60" y="58" width="42" height="30" rx="2" fill="none" stroke="rgba(255,255,255,.2)" stroke-width="1"></rect>
</svg>
</div>
</div>
<!-- RIGHT PANEL -->
<div style="width:300px; flex:none; border-left:1px solid rgba(255,255,255,.06); background:#0b0b0e; display:flex; flex-direction:column; min-height:0;">
<div style="display:flex; align-items:center; gap:8px; padding:12px 14px; border-bottom:1px solid rgba(255,255,255,.06);">
<span style="font-family:'JetBrains Mono',monospace; font-size:10px; letter-spacing:.14em; color:#5a5a62;">PANEL</span>
<span style="flex:1;"></span>
<span style="color:#5a5a62; font-size:13px;"></span>
</div>
<div style="flex:1; overflow-y:auto; padding:18px 16px;">
<div style="display:flex; flex-direction:column; align-items:center; text-align:center; margin-bottom:18px;">
<div style="position:relative; width:64px; height:64px; margin-bottom:11px;">
<div style="position:absolute; inset:-3px; border-radius:50%; border:1.5px solid rgba(255,111,97,.4);"></div>
<div style="width:64px; height:64px; border-radius:50%; background:linear-gradient(135deg,#ff8a7a,#ff5f57); display:flex; align-items:center; justify-content:center; font-size:24px; font-weight:700; color:#2a0d0a;">S</div>
</div>
<div style="font-size:17px; font-weight:700;">Smith</div>
<div style="font-family:'JetBrains Mono',monospace; font-size:10px; color:#6a6a72; margin-top:2px;">Research Specialist</div>
<span style="margin-top:8px; font-family:'JetBrains Mono',monospace; font-size:9px; color:#5fd08a; padding:3px 9px; border-radius:6px; background:rgba(95,208,138,.1); border:1px solid rgba(95,208,138,.25);">● online</span>
</div>
<div style="font-family:'JetBrains Mono',monospace; font-size:9px; letter-spacing:.14em; color:#5a5a62; margin-bottom:10px;">BELONGS TO</div>
<div style="display:flex; flex-direction:column; gap:1px; margin-bottom:18px;">
<div style="display:flex; align-items:center; gap:9px; padding:9px 10px; border-radius:8px;"><span style="width:7px; height:7px; border-radius:50%; background:#5fd08a;"></span><span style="font-family:'JetBrains Mono',monospace; font-size:9px; color:#6a6a72; width:54px;">TEAM</span><span style="font-size:13px; font-weight:600;">POD01</span></div>
<div style="display:flex; align-items:center; gap:9px; padding:9px 10px; border-radius:8px;"><span style="width:7px; height:7px; border-radius:50%; background:#8a9af0;"></span><span style="font-family:'JetBrains Mono',monospace; font-size:9px; color:#6a6a72; width:54px;">COMPANY</span><span style="font-size:13px; font-weight:600;">thor</span></div>
<div style="display:flex; align-items:center; gap:9px; padding:9px 10px; border-radius:8px;"><span style="width:7px; height:7px; border-radius:50%; background:#c98af0;"></span><span style="font-family:'JetBrains Mono',monospace; font-size:9px; color:#6a6a72; width:54px;">ORG</span><span style="font-size:13px; font-weight:600;">Zeus</span></div>
</div>
<div style="display:flex; gap:8px; margin-bottom:18px;">
<div style="flex:1; text-align:center; padding:12px 0; border-radius:10px; border:1px solid rgba(255,255,255,.07); background:#0d0d10;"><div style="font-size:18px; font-weight:700;">0</div><div style="font-family:'JetBrains Mono',monospace; font-size:9px; color:#6a6a72; margin-top:2px;">SKILLS</div></div>
<div style="flex:1; text-align:center; padding:12px 0; border-radius:10px; border:1px solid rgba(255,255,255,.07); background:#0d0d10;"><div style="font-size:18px; font-weight:700;">0</div><div style="font-family:'JetBrains Mono',monospace; font-size:9px; color:#6a6a72; margin-top:2px;">TOOLS</div></div>
<div style="flex:1; text-align:center; padding:12px 0; border-radius:10px; border:1px solid rgba(255,255,255,.07); background:#0d0d10;"><div style="font-size:18px; font-weight:700;">0</div><div style="font-family:'JetBrains Mono',monospace; font-size:9px; color:#6a6a72; margin-top:2px;">RUNNING</div></div>
</div>
</div>
<div style="flex:none; padding:12px 14px; border-top:1px solid rgba(255,255,255,.06);">
<div style="display:flex; align-items:center; justify-content:center; gap:8px; height:38px; border-radius:9px; background:linear-gradient(135deg,#ff8a7a,#ff5f57); color:#2a0d0a; font-size:13px; font-weight:700; cursor:pointer;">⛶ More details</div>
</div>
</div>
</div>
<!-- STATUS BAR -->
<div style="height:26px; flex:none; display:flex; align-items:center; gap:16px; padding:0 16px; border-top:1px solid rgba(255,255,255,.06); background:#0a0a0c; font-family:'JetBrains Mono',monospace; font-size:10px; color:#5a5a62;">
<span style="color:#5fd08a;">● durable runner ok</span>
<span>checkpoint 3s ago</span>
<span style="flex:1;"></span>
<span>§15 sandbox: isolated</span>
<span style="color:#e8b465;">doors awaiting approval</span>
</div>
</div>
</x-dc>
<script type="text/x-dc" data-dc-script>
class Component extends DCLogic {
state = { view: 'live', selected: 'smith' };
componentDidUpdate(prevProps, prevState) {
if (prevState && prevState.view !== this.state.view) {
if (this.state.view === 'live') this._startLive();
else this._stopLive();
}
}
componentWillUnmount() { this._stopLive(); }
_setCanvas = (el) => {
this._cv = el;
if (el && this.state.view === 'live') this._startLive();
else if (!el) this._stopLive();
};
_stopLive() { if (this._raf) { cancelAnimationFrame(this._raf); this._raf = null; } }
_startLive() {
const cv = this._cv;
if (!cv) return;
this._stopLive();
// Gource-style world: a root blooms into area/service/event nodes (the "files"),
// and agent particles stream toward whatever node is currently active, emitting beams.
const targets = [
{ id:'runtime', label:'cm-runtime', kind:'service', col:'#ff8a7a', ang:-0.5, rad:0.30 },
{ id:'pr214', label:'PR #214', kind:'event', col:'#5ec8d8', ang:0.15, rad:0.40 },
{ id:'advdb', label:'advisory-db',kind:'service', col:'#5fd08a', ang:0.9, rad:0.34 },
{ id:'slack', label:'#eng', kind:'event', col:'#c98af0', ang:1.7, rad:0.42 },
{ id:'orch', label:'cm-orch', kind:'service', col:'#e8b465', ang:2.5, rad:0.30 },
{ id:'deploy', label:'PROD deploy',kind:'event', col:'#ff6f61', ang:3.5, rad:0.40 },
{ id:'docs', label:'spec §15', kind:'service', col:'#6fd0c0', ang:4.3, rad:0.33 },
{ id:'bench', label:'topo-bench', kind:'service', col:'#8a9af0', ang:5.1, rad:0.41 },
{ id:'broker', label:'secret-broker',kind:'service',col:'#9a9aa2', ang:5.7, rad:0.28 },
];
const agents = [
{ id:'m', label:'M', col:'#ff5f57', ink:'#2a0d0a', t:0, target:1 },
{ id:'s', label:'S', col:'#4aa3b8', ink:'#06201f', t:2, target:2 },
];
const beams = []; // {ax, ay, tx, ty, col, life}
const sparks = []; // {x, y, col, r, life}
const dpr = Math.min(2, window.devicePixelRatio || 1);
const ctx = cv.getContext('2d');
let W = 0, H = 0, cx = 0, cy = 0, rmin = 0;
const resize = () => {
const r = cv.getBoundingClientRect();
W = r.width; H = r.height;
cv.width = W * dpr; cv.height = H * dpr;
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
cx = W / 2; cy = H / 2 + 8; rmin = Math.min(W, H);
};
resize();
this._onResize = resize;
window.addEventListener('resize', resize);
const tpos = (t) => ({ x: cx + Math.cos(t.ang) * rmin * t.rad, y: cy + Math.sin(t.ang) * rmin * t.rad });
// each agent orbits + drifts toward its current target; retargets periodically
agents.forEach(a => { a.x = cx; a.y = cy; a.retime = 1.5 + Math.random()*2; });
let last = performance.now();
const tick = (now) => {
const dt = Math.min(0.05, (now - last) / 1000); last = now;
ctx.clearRect(0, 0, W, H);
// faint tree: root -> each target
ctx.lineWidth = 1;
targets.forEach(t => {
const p = tpos(t);
ctx.strokeStyle = 'rgba(255,255,255,0.05)';
ctx.beginPath(); ctx.moveTo(cx, cy); ctx.lineTo(p.x, p.y); ctx.stroke();
});
// target nodes
targets.forEach(t => {
const p = tpos(t); t._x = p.x; t._y = p.y;
const pulse = t._hot ? 1 + 0.25 * Math.sin(now/120) : 1;
const baseR = (t.kind === 'service' ? 7 : 5) * pulse;
// glow when hot
if (t._hot) {
const g = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, 26);
g.addColorStop(0, t.col + '55'); g.addColorStop(1, t.col + '00');
ctx.fillStyle = g; ctx.beginPath(); ctx.arc(p.x, p.y, 26, 0, 7); ctx.fill();
}
ctx.fillStyle = t.col; ctx.globalAlpha = t._hot ? 1 : 0.55;
ctx.beginPath(); ctx.arc(p.x, p.y, baseR, 0, 7); ctx.fill();
ctx.globalAlpha = 1;
ctx.fillStyle = t._hot ? 'rgba(255,255,255,0.85)' : 'rgba(255,255,255,0.4)';
ctx.font = "600 10px 'JetBrains Mono', monospace";
ctx.textAlign = 'center';
ctx.fillText(t.label, p.x, p.y - baseR - 7);
t._hot = false;
});
// root
const rg = ctx.createRadialGradient(cx, cy, 0, cx, cy, 30);
rg.addColorStop(0, 'rgba(255,111,97,0.5)'); rg.addColorStop(1, 'rgba(255,111,97,0)');
ctx.fillStyle = rg; ctx.beginPath(); ctx.arc(cx, cy, 30, 0, 7); ctx.fill();
ctx.fillStyle = '#ff6f61'; ctx.beginPath(); ctx.arc(cx, cy, 9, 0, 7); ctx.fill();
ctx.fillStyle = '#fff'; ctx.font = "700 9px 'JetBrains Mono', monospace"; ctx.textAlign = 'center';
ctx.fillText('Z', cx, cy + 3);
// agents move + emit beams
agents.forEach(a => {
a.retime -= dt;
if (a.retime <= 0) { a.target = Math.floor(Math.random() * targets.length); a.retime = 1.2 + Math.random()*2.2; }
const tg = targets[a.target]; const p = tpos(tg);
// approach a point near the target (orbit a bit)
a.t += dt;
const ox = p.x + Math.cos(a.t*1.5) * 26;
const oy = p.y + Math.sin(a.t*1.5) * 26;
a.x += (ox - a.x) * Math.min(1, dt * 2.2);
a.y += (oy - a.y) * Math.min(1, dt * 2.2);
// proximity → emit beam + heat the node
const d = Math.hypot(a.x - p.x, a.y - p.y);
if (d < 60) {
tg._hot = true;
if (Math.random() < 0.5) beams.push({ ax:a.x, ay:a.y, tx:p.x, ty:p.y, col:a.col, life:1 });
if (Math.random() < 0.25) sparks.push({ x:p.x, y:p.y, col:tg.col, r:2, life:1 });
}
});
// beams
for (let i = beams.length - 1; i >= 0; i--) {
const b = beams[i]; b.life -= dt * 2.5;
if (b.life <= 0) { beams.splice(i, 1); continue; }
ctx.strokeStyle = b.col + Math.floor(b.life * 200).toString(16).padStart(2,'0');
ctx.lineWidth = 1.5 * b.life + 0.4;
ctx.beginPath(); ctx.moveTo(b.ax, b.ay); ctx.lineTo(b.tx, b.ty); ctx.stroke();
}
// sparks
for (let i = sparks.length - 1; i >= 0; i--) {
const s = sparks[i]; s.life -= dt * 1.8; s.r += dt * 22;
if (s.life <= 0) { sparks.splice(i, 1); continue; }
ctx.strokeStyle = s.col + Math.floor(s.life * 160).toString(16).padStart(2,'0');
ctx.lineWidth = 1.2;
ctx.beginPath(); ctx.arc(s.x, s.y, s.r, 0, 7); ctx.stroke();
}
// agent avatars on top
agents.forEach(a => {
ctx.fillStyle = a.col;
ctx.shadowColor = a.col; ctx.shadowBlur = 14;
ctx.beginPath(); ctx.arc(a.x, a.y, 11, 0, 7); ctx.fill();
ctx.shadowBlur = 0;
ctx.fillStyle = a.ink; ctx.font = "700 11px 'JetBrains Mono', monospace"; ctx.textAlign = 'center';
ctx.fillText(a.label, a.x, a.y + 4);
});
this._raf = requestAnimationFrame(tick);
};
this._raf = requestAnimationFrame(tick);
}
renderVals() {
const v = this.state.view;
const mk = (id, label, icon) => {
const active = v === id;
return { id, label, icon, active,
style: active ? 'background:rgba(255,111,97,.16); color:#ff8a7a;' : 'color:#9a9aa2;',
onPick: () => this.setState({ view: id }) };
};
const captions = {
hierarchy: 'hierarchy — org ▸ company ▸ team ▸ claw',
flat: 'flat topology — every node a peer, no nesting',
live: 'live — agents converge on the projects, services & events they touch (Gource-style)',
};
const minimaps = { hierarchy:'TREE', flat:'FLAT', live:'LIVE' };
return {
modes: [ mk('hierarchy','Hierarchy','▤'), mk('flat','Flat','⬡'), mk('live','Live','✦') ],
isHierarchy: v==='hierarchy', isFlat: v==='flat', isLive: v==='live',
hShow: v==='hierarchy'?'block':'none', fShow: v==='flat'?'block':'none', lShow: v==='live'?'block':'none',
caption: captions[v], minimapLabel: minimaps[v],
canvasRef: this._setCanvas,
};
}
}
</script>
</body>
</html>