Spaces:
Running
Running
deploy(hf): sync szl-holdings/a11oy@main derived COPY set
Browse filesReusable Dockerfile-COPY-derived deploy from szl-holdings/a11oy main.
Files: 825 Pruned: 0
Derived from Dockerfile COPY sources (NO hand-maintained allowlist).
Signed-off-by: SZL Holdings <noreply@szlholdings.ai>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- web/hologram.html +184 -16
web/hologram.html
CHANGED
|
@@ -65,13 +65,38 @@
|
|
| 65 |
padding:7px 12px;letter-spacing:.05em;background:linear-gradient(0deg,#03060ce0,#03060c00)}
|
| 66 |
#foot code{color:var(--dim)}
|
| 67 |
a{color:var(--teal);text-decoration:none}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
/* no-webgl fallback */
|
| 69 |
#nogl{position:fixed;inset:0;display:none;place-items:center;text-align:center;padding:40px;
|
| 70 |
font-family:var(--mono);color:var(--dim)}
|
| 71 |
#nogl.show{display:grid}
|
| 72 |
-
@media(max-width:
|
| 73 |
-
#title{max-width:90vw} #mind,#flow{width:min(92vw,300px)}
|
| 74 |
-
#legend{display:none}
|
| 75 |
}
|
| 76 |
</style>
|
| 77 |
</head>
|
|
@@ -88,15 +113,22 @@
|
|
| 88 |
</div>
|
| 89 |
|
| 90 |
<div class="hud" id="title">
|
| 91 |
-
<h1>
|
| 92 |
<div class="sub">
|
| 93 |
-
The living <b>agentic-GPU organism</b> — RTX 5000 @ betterwithage as the <b>MIND</b>,
|
| 94 |
-
six proven
|
| 95 |
-
|
|
|
|
| 96 |
</div>
|
| 97 |
<span class="badge" id="conn">● connecting…</span>
|
| 98 |
</div>
|
| 99 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 100 |
<div class="hud" id="mind">
|
| 101 |
<h2>MIND CORE — /code/healthz</h2>
|
| 102 |
<div class="row"><span class="k">Posture</span><span class="v"><span class="pill grey" id="m_posture">unknown</span></span></div>
|
|
@@ -105,6 +137,14 @@
|
|
| 105 |
<div class="row"><span class="k">Λ trust</span><span class="v" id="m_lambda">Conjecture 1</span></div>
|
| 106 |
</div>
|
| 107 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 108 |
<div class="hud" id="flow">
|
| 109 |
<h2>CIRCULATION — /v1/energy/budget</h2>
|
| 110 |
<div class="row"><span class="k">Dominant source</span><span class="v" id="f_src">—</span></div>
|
|
@@ -113,6 +153,15 @@
|
|
| 113 |
<div class="row"><span class="k">Bekenstein</span><span class="v" id="f_bek">—</span></div>
|
| 114 |
</div>
|
| 115 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
<div class="hud" id="legend">
|
| 117 |
<h3>ENERGY SOURCES (flow color)</h3>
|
| 118 |
<div class="lg-row"><span class="dot" style="color:var(--solar)"></span>solar
|
|
@@ -124,15 +173,19 @@
|
|
| 124 |
<div class="lg-row"><span class="dot" style="color:var(--green)"></span>active (pulses)
|
| 125 |
<span class="dot" style="color:var(--amber);margin-left:10px"></span>degraded
|
| 126 |
<span class="dot" style="color:var(--grey);margin-left:10px"></span>unknown (unreachable — honest dim)</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 127 |
</div>
|
| 128 |
|
| 129 |
<div id="tip"></div>
|
| 130 |
|
| 131 |
<div class="hud" id="foot">
|
| 132 |
-
Three.js r128 vendored locally — <b>0 runtime CDN</b>. Energy figures are <b>SAMPLE/ESTIMATE</b>
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
Unreachable organs render dim — <b>no fabricated data</b>.
|
| 136 |
</div>
|
| 137 |
|
| 138 |
<!-- THREE r128 (MIT), vendored locally at /vendor/three.min.js — NO CDN (matches fleet-c2.html). -->
|
|
@@ -178,6 +231,23 @@
|
|
| 178 |
return 0x9aa6bd; // unknown source -> grid grey (honest neutral)
|
| 179 |
}
|
| 180 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 181 |
// ---------- WebGL guard ----------
|
| 182 |
if(!window.THREE || !(function(){try{var c=document.createElement("canvas");
|
| 183 |
return !!(window.WebGLRenderingContext && (c.getContext("webgl")||c.getContext("experimental-webgl")));
|
|
@@ -196,7 +266,7 @@
|
|
| 196 |
var renderer = new THREE.WebGLRenderer({canvas:canvas, antialias:true, alpha:true});
|
| 197 |
renderer.setPixelRatio(Math.min(window.devicePixelRatio||1, 2));
|
| 198 |
var scene = new THREE.Scene();
|
| 199 |
-
scene.fog = new THREE.FogExp2(0x03060c, 0.
|
| 200 |
|
| 201 |
var camera = new THREE.PerspectiveCamera(52, 1, 0.1, 400);
|
| 202 |
camera.position.set(0, 9, 34);
|
|
@@ -311,9 +381,64 @@
|
|
| 311 |
}
|
| 312 |
buildSwarm(7); // default 7-node fabric; replaced by live count when available
|
| 313 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 314 |
// ---------- interaction: drag-rotate, wheel-zoom, hover organs ----------
|
| 315 |
var ray = new THREE.Raycaster(), mouse = new THREE.Vector2();
|
| 316 |
-
var rotY=0.0, rotX=0.0, dragging=false, lx=0, ly=0, autoRot=true, zoom=34;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 317 |
canvas.addEventListener("pointerdown", function(e){ dragging=true; autoRot=false; lx=e.clientX; ly=e.clientY; });
|
| 318 |
window.addEventListener("pointerup", function(){ dragging=false; });
|
| 319 |
window.addEventListener("pointermove", function(e){
|
|
@@ -322,8 +447,8 @@
|
|
| 322 |
// hover tooltip
|
| 323 |
mouse.x=(e.clientX/window.innerWidth)*2-1; mouse.y=-(e.clientY/window.innerHeight)*2+1;
|
| 324 |
ray.setFromCamera(mouse, camera);
|
| 325 |
-
var hit = ray.intersectObjects(organMeshes.map(function(g){return g.userData.node;}));
|
| 326 |
var tip=$("tip");
|
|
|
|
| 327 |
if(hit.length){
|
| 328 |
var g = organMeshes.filter(function(gg){return gg.userData.node===hit[0].object;})[0];
|
| 329 |
var u=g.userData, st=u.status;
|
|
@@ -332,9 +457,20 @@
|
|
| 332 |
'<div class="d">'+esc(u.organ.role)+'</div>'+
|
| 333 |
'<div class="st"><span style="color:'+stc+'">●</span> '+esc(st)+' · <span style="color:var(--dim)">'+esc(u.organ.note)+'</span></div>';
|
| 334 |
tip.style.left=(e.clientX+14)+"px"; tip.style.top=(e.clientY+12)+"px"; tip.style.opacity=1;
|
| 335 |
-
} else
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 336 |
});
|
| 337 |
-
canvas.addEventListener("wheel", function(e){ e.preventDefault();
|
| 338 |
|
| 339 |
function resize(){
|
| 340 |
var w=window.innerWidth, h=window.innerHeight;
|
|
@@ -347,6 +483,7 @@
|
|
| 347 |
function tick(now){
|
| 348 |
var t=(now-t0)/1000;
|
| 349 |
if(autoRot) rotY += 0.0016;
|
|
|
|
| 350 |
// camera framing from rot + zoom
|
| 351 |
camera.position.x = Math.sin(rotY)*zoom*Math.cos(rotX);
|
| 352 |
camera.position.z = Math.cos(rotY)*zoom*Math.cos(rotX);
|
|
@@ -389,6 +526,15 @@
|
|
| 389 |
flowGeo.attributes.position.needsUpdate=true; flowGeo.attributes.color.needsUpdate=true;
|
| 390 |
|
| 391 |
swarmGrp.rotation.y += 0.0006;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 392 |
renderer.render(scene,camera);
|
| 393 |
requestAnimationFrame(tick);
|
| 394 |
}
|
|
@@ -461,9 +607,31 @@
|
|
| 461 |
g.userData.status = (ok===true)?"active":(ok===false?"degraded":"unknown");
|
| 462 |
}
|
| 463 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 464 |
function pollData(){
|
| 465 |
getJSON(API+"/code/healthz").then(applyPosture);
|
| 466 |
getJSON(API+"/v1/energy/budget").then(applyBudget);
|
|
|
|
| 467 |
// probe each organ endpoint; honest dim on null. Never blocks the scene.
|
| 468 |
organMeshes.forEach(function(g){
|
| 469 |
getJSON(g.userData.organ.probe).then(function(j){
|
|
|
|
| 65 |
padding:7px 12px;letter-spacing:.05em;background:linear-gradient(0deg,#03060ce0,#03060c00)}
|
| 66 |
#foot code{color:var(--dim)}
|
| 67 |
a{color:var(--teal);text-decoration:none}
|
| 68 |
+
/* council (Ayllu) card — mid right, under MIND */
|
| 69 |
+
#council{top:190px;right:22px;width:300px;border:1px solid var(--line);border-radius:14px;
|
| 70 |
+
background:linear-gradient(180deg,#0a1626dd,#060d18ee);padding:13px 16px;pointer-events:auto;backdrop-filter:blur(4px)}
|
| 71 |
+
#council h2{margin:0 0 8px;font-size:11px;letter-spacing:.16em;color:var(--dim);font-weight:600}
|
| 72 |
+
#council .row{display:flex;justify-content:space-between;font-size:12px;padding:3px 0;border-bottom:1px dashed #13203660}
|
| 73 |
+
#council .row:last-child{border-bottom:0}
|
| 74 |
+
#council .k{color:var(--dim)} #council .v{color:var(--ink);text-align:right}
|
| 75 |
+
#council .who{margin-top:7px;font-size:10.5px;color:var(--violet);line-height:1.5;word-spacing:1px}
|
| 76 |
+
/* lab (PINN research) card — mid left, under TITLE */
|
| 77 |
+
#lab{top:150px;left:22px;width:292px;border:1px solid var(--line);border-radius:14px;
|
| 78 |
+
background:linear-gradient(180deg,#0a1626dd,#060d18ee);padding:13px 16px;pointer-events:auto;backdrop-filter:blur(4px)}
|
| 79 |
+
#lab h2{margin:0 0 3px;font-size:11px;letter-spacing:.16em;color:var(--dim);font-weight:600}
|
| 80 |
+
#lab .rig{font-size:10.5px;color:var(--teal);margin-bottom:7px;line-height:1.5}
|
| 81 |
+
#lab .row{display:flex;justify-content:space-between;font-size:12px;padding:3px 0;border-bottom:1px dashed #13203660}
|
| 82 |
+
#lab .row:last-child{border-bottom:0}
|
| 83 |
+
#lab .k{color:var(--dim)} #lab .v{color:var(--ink);font-family:var(--mono)}
|
| 84 |
+
.measured{display:inline-block;margin-left:6px;padding:1px 6px;border:1px solid #123a2c;border-radius:6px;
|
| 85 |
+
font-size:9px;letter-spacing:.06em;color:var(--green);background:#08241a}
|
| 86 |
+
/* zoom preset bar — top center */
|
| 87 |
+
#zoombar{top:16px;left:50%;transform:translateX(-50%);display:flex;gap:6px;pointer-events:auto;
|
| 88 |
+
background:#070f1ccc;border:1px solid var(--line);border-radius:999px;padding:5px 6px;backdrop-filter:blur(3px)}
|
| 89 |
+
#zoombar button{font-family:var(--mono);font-size:10.5px;letter-spacing:.1em;color:var(--dim);
|
| 90 |
+
background:transparent;border:0;border-radius:999px;padding:5px 12px;cursor:pointer;transition:.15s}
|
| 91 |
+
#zoombar button:hover{color:var(--ink)}
|
| 92 |
+
#zoombar button.on{color:var(--bg0);background:var(--teal);font-weight:600}
|
| 93 |
/* no-webgl fallback */
|
| 94 |
#nogl{position:fixed;inset:0;display:none;place-items:center;text-align:center;padding:40px;
|
| 95 |
font-family:var(--mono);color:var(--dim)}
|
| 96 |
#nogl.show{display:grid}
|
| 97 |
+
@media(max-width:900px){
|
| 98 |
+
#title{max-width:90vw} #mind,#flow,#council,#lab{width:min(92vw,300px)}
|
| 99 |
+
#legend,#council,#lab{display:none}
|
| 100 |
}
|
| 101 |
</style>
|
| 102 |
</head>
|
|
|
|
| 113 |
</div>
|
| 114 |
|
| 115 |
<div class="hud" id="title">
|
| 116 |
+
<h1>SZL ESTATE — HOLOGRAPHIC BRIDGE</h1>
|
| 117 |
<div class="sub">
|
| 118 |
+
The living <b>agentic-GPU organism</b>, zoomed out to the whole estate — RTX 5000 @ betterwithage as the <b>MIND</b>,
|
| 119 |
+
six proven <b>organs</b> as the body, the <b>Ayllu council</b> as its deliberating minds,
|
| 120 |
+
and your <b>hardware & stack</b> as the surrounding estate.<br/>
|
| 121 |
+
Live nodes glow; declared nodes are labelled; nothing is fabricated.
|
| 122 |
</div>
|
| 123 |
<span class="badge" id="conn">● connecting…</span>
|
| 124 |
</div>
|
| 125 |
|
| 126 |
+
<div class="hud" id="zoombar">
|
| 127 |
+
<button data-zoom="council">COUNCIL</button>
|
| 128 |
+
<button data-zoom="organism" class="on">ORGANISM</button>
|
| 129 |
+
<button data-zoom="estate">ESTATE</button>
|
| 130 |
+
</div>
|
| 131 |
+
|
| 132 |
<div class="hud" id="mind">
|
| 133 |
<h2>MIND CORE — /code/healthz</h2>
|
| 134 |
<div class="row"><span class="k">Posture</span><span class="v"><span class="pill grey" id="m_posture">unknown</span></span></div>
|
|
|
|
| 137 |
<div class="row"><span class="k">Λ trust</span><span class="v" id="m_lambda">Conjecture 1</span></div>
|
| 138 |
</div>
|
| 139 |
|
| 140 |
+
<div class="hud" id="council">
|
| 141 |
+
<h2>COUNCIL — /v1/ayllu/roster</h2>
|
| 142 |
+
<div class="row"><span class="k">Personas</span><span class="v" id="cl_n">—</span></div>
|
| 143 |
+
<div class="row"><span class="k">Backend</span><span class="v" id="cl_be">—</span></div>
|
| 144 |
+
<div class="row"><span class="k">Loop</span><span class="v">bounded · Λ-gated</span></div>
|
| 145 |
+
<div class="who" id="cl_who">convening the core council…</div>
|
| 146 |
+
</div>
|
| 147 |
+
|
| 148 |
<div class="hud" id="flow">
|
| 149 |
<h2>CIRCULATION — /v1/energy/budget</h2>
|
| 150 |
<div class="row"><span class="k">Dominant source</span><span class="v" id="f_src">—</span></div>
|
|
|
|
| 153 |
<div class="row"><span class="k">Bekenstein</span><span class="v" id="f_bek">—</span></div>
|
| 154 |
</div>
|
| 155 |
|
| 156 |
+
<div class="hud" id="lab">
|
| 157 |
+
<h2>LAB — PhysicsNeMo PINN<span class="measured">MEASURED</span></h2>
|
| 158 |
+
<div class="rig">rosie · RTX 5050 (Blackwell sm_120)<br/>PhysicsNeMo 2.1.1 · torch 2.12.1+cu130</div>
|
| 159 |
+
<div class="row"><span class="k">Poisson rel-L2</span><span class="v">2.99e-4</span></div>
|
| 160 |
+
<div class="row"><span class="k">Burgers rel-L2</span><span class="v">0.644</span></div>
|
| 161 |
+
<div class="row"><span class="k">Duffing α err</span><span class="v">1.2e-5</span></div>
|
| 162 |
+
<div class="row"><span class="k">Snapshot</span><span class="v" style="color:var(--dim)">2026-07-02 · local</span></div>
|
| 163 |
+
</div>
|
| 164 |
+
|
| 165 |
<div class="hud" id="legend">
|
| 166 |
<h3>ENERGY SOURCES (flow color)</h3>
|
| 167 |
<div class="lg-row"><span class="dot" style="color:var(--solar)"></span>solar
|
|
|
|
| 173 |
<div class="lg-row"><span class="dot" style="color:var(--green)"></span>active (pulses)
|
| 174 |
<span class="dot" style="color:var(--amber);margin-left:10px"></span>degraded
|
| 175 |
<span class="dot" style="color:var(--grey);margin-left:10px"></span>unknown (unreachable — honest dim)</div>
|
| 176 |
+
<h3 style="margin-top:9px">SHELLS (zoom out)</h3>
|
| 177 |
+
<div class="lg-row"><span class="dot" style="color:var(--violet)"></span>Ayllu council (minds)
|
| 178 |
+
<span class="dot" style="color:var(--gold);margin-left:10px"></span>estate — hardware & stack</div>
|
| 179 |
+
<div class="lg-row" style="color:var(--grey)">estate nodes are <b style="color:var(--dim)">declared</b> (no live probe) unless marked</div>
|
| 180 |
</div>
|
| 181 |
|
| 182 |
<div id="tip"></div>
|
| 183 |
|
| 184 |
<div class="hud" id="foot">
|
| 185 |
+
Three.js r128 vendored locally — <b>0 runtime CDN</b>. Energy figures are <b>SAMPLE/ESTIMATE</b>;
|
| 186 |
+
PINN results are a <b>MEASURED</b> local snapshot (not live). Council is read live from <code>/v1/ayllu/roster</code>.
|
| 187 |
+
Estate nodes are <b>declared</b> (no live probe) unless marked. Sovereign glow only when <code>/code/healthz</code> reports
|
| 188 |
+
<code>sovereign:true</code>. Λ trust = <b>Conjecture 1</b> (advisory). Unreachable organs render dim — <b>no fabricated data</b>.
|
| 189 |
</div>
|
| 190 |
|
| 191 |
<!-- THREE r128 (MIT), vendored locally at /vendor/three.min.js — NO CDN (matches fleet-c2.html). -->
|
|
|
|
| 231 |
return 0x9aa6bd; // unknown source -> grid grey (honest neutral)
|
| 232 |
}
|
| 233 |
|
| 234 |
+
// ---- Ayllu council (deliberating minds) — the 3 core personas as an honest
|
| 235 |
+
// fallback; the live count/names come from /v1/ayllu/roster when reachable.
|
| 236 |
+
var COUNCIL_FALLBACK = ["Amaru","Kamachiq","Qhatuq"];
|
| 237 |
+
var COUNCIL_COL = 0xb98cff;
|
| 238 |
+
|
| 239 |
+
// ---- Estate shell: Rosa's real hardware & stack. These are DECLARED nodes
|
| 240 |
+
// (no per-node live probe) unless noted — labelled honestly, never faked.
|
| 241 |
+
var ESTATE = [
|
| 242 |
+
{id:"rosie · RTX 5050", col:0x34f5b0, kind:"GPU · research", note:"Blackwell sm_120 — PINN lab (PhysicsNeMo)", live:false},
|
| 243 |
+
{id:"betterwithage · RTX 5000", col:0x39d7ff, kind:"GPU · sovereign", note:"the MIND core — inference (probed at centre)", live:true},
|
| 244 |
+
{id:"Hetzner · prod box", col:0xffd66e, kind:"compute · host", note:"alloy-api + agents · /opt/alloyscape", live:false},
|
| 245 |
+
{id:"Neon · Postgres 17", col:0x5ad6ff, kind:"data · database", note:"managed Postgres — ledgers & state", live:false},
|
| 246 |
+
{id:"Hugging Face · Spaces", col:0xffcb57, kind:"edge · deploy", note:"a11oy / anatomy / killinchu / immune", live:false},
|
| 247 |
+
{id:"Backblaze B2 · backups", col:0x9aa6bd, kind:"storage · DR", note:"daily pg_dump safety net", live:false}
|
| 248 |
+
];
|
| 249 |
+
var ESTATE_COL_DEFAULT = 0xffd66e;
|
| 250 |
+
|
| 251 |
// ---------- WebGL guard ----------
|
| 252 |
if(!window.THREE || !(function(){try{var c=document.createElement("canvas");
|
| 253 |
return !!(window.WebGLRenderingContext && (c.getContext("webgl")||c.getContext("experimental-webgl")));
|
|
|
|
| 266 |
var renderer = new THREE.WebGLRenderer({canvas:canvas, antialias:true, alpha:true});
|
| 267 |
renderer.setPixelRatio(Math.min(window.devicePixelRatio||1, 2));
|
| 268 |
var scene = new THREE.Scene();
|
| 269 |
+
scene.fog = new THREE.FogExp2(0x03060c, 0.010); // gentler so the estate shell stays readable when zoomed out
|
| 270 |
|
| 271 |
var camera = new THREE.PerspectiveCamera(52, 1, 0.1, 400);
|
| 272 |
camera.position.set(0, 9, 34);
|
|
|
|
| 381 |
}
|
| 382 |
buildSwarm(7); // default 7-node fabric; replaced by live count when available
|
| 383 |
|
| 384 |
+
// ============ COUNCIL RING (Ayllu — deliberating minds) ============
|
| 385 |
+
var COUNCIL_R = 15.5;
|
| 386 |
+
var councilGrp = new THREE.Group(); scene.add(councilGrp);
|
| 387 |
+
var councilNodes = [];
|
| 388 |
+
function buildCouncil(names){
|
| 389 |
+
if(!councilGrp) return; // no-op in the no-WebGL path
|
| 390 |
+
while(councilGrp.children.length) councilGrp.remove(councilGrp.children[0]);
|
| 391 |
+
councilNodes = [];
|
| 392 |
+
names = (names && names.length) ? names : COUNCIL_FALLBACK;
|
| 393 |
+
names.forEach(function(nm,i){
|
| 394 |
+
var a=(i/names.length)*Math.PI*2 + 0.5;
|
| 395 |
+
var m=new THREE.Mesh(new THREE.TetrahedronGeometry(0.7,0),
|
| 396 |
+
new THREE.MeshStandardMaterial({color:COUNCIL_COL, emissive:COUNCIL_COL,
|
| 397 |
+
emissiveIntensity:0.4, metalness:0.3, roughness:0.4, transparent:true, opacity:0.8}));
|
| 398 |
+
m.position.set(Math.cos(a)*COUNCIL_R, Math.sin(i*1.3)*1.6, Math.sin(a)*COUNCIL_R);
|
| 399 |
+
m.userData={hover:{title:esc(nm), role:"Ayllu council persona", note:"bounded, Λ-gated deliberation", status:"council"}};
|
| 400 |
+
councilGrp.add(m); councilNodes.push(m);
|
| 401 |
+
});
|
| 402 |
+
}
|
| 403 |
+
buildCouncil(COUNCIL_FALLBACK);
|
| 404 |
+
|
| 405 |
+
// ============ ESTATE SHELL (hardware & stack — declared) ============
|
| 406 |
+
var ESTATE_R = 25;
|
| 407 |
+
var estateGrp = new THREE.Group(); scene.add(estateGrp);
|
| 408 |
+
var estateNodes = [];
|
| 409 |
+
ESTATE.forEach(function(o,i){
|
| 410 |
+
var a=(i/ESTATE.length)*Math.PI*2;
|
| 411 |
+
var g=new THREE.Group();
|
| 412 |
+
var col = o.col || ESTATE_COL_DEFAULT;
|
| 413 |
+
var box=new THREE.Mesh(new THREE.BoxGeometry(1.1,1.1,1.1),
|
| 414 |
+
new THREE.MeshStandardMaterial({color:col, emissive:col,
|
| 415 |
+
emissiveIntensity:o.live?0.4:0.16, metalness:0.5, roughness:0.4,
|
| 416 |
+
transparent:true, opacity:o.live?0.85:0.5, flatShading:true}));
|
| 417 |
+
var ring=new THREE.Mesh(new THREE.TorusGeometry(1.5,0.045,6,32),
|
| 418 |
+
new THREE.MeshBasicMaterial({color:col, transparent:true, opacity:o.live?0.4:0.18}));
|
| 419 |
+
ring.rotation.x=Math.PI/2;
|
| 420 |
+
g.add(box); g.add(ring);
|
| 421 |
+
g.position.set(Math.cos(a)*ESTATE_R, Math.sin(i*1.9)*2.6, Math.sin(a)*ESTATE_R);
|
| 422 |
+
box.userData={hover:{title:esc(o.id), role:esc(o.kind), note:esc(o.note),
|
| 423 |
+
status:o.live?"live (probed at centre)":"declared — no live probe"}};
|
| 424 |
+
estateGrp.add(g); estateNodes.push(box);
|
| 425 |
+
});
|
| 426 |
+
|
| 427 |
// ---------- interaction: drag-rotate, wheel-zoom, hover organs ----------
|
| 428 |
var ray = new THREE.Raycaster(), mouse = new THREE.Vector2();
|
| 429 |
+
var rotY=0.0, rotX=0.0, dragging=false, lx=0, ly=0, autoRot=true, zoom=34, zoomTarget=34;
|
| 430 |
+
var ZOOM_PRESET={council:22, organism:34, estate:80};
|
| 431 |
+
(function(){
|
| 432 |
+
var bar=$("zoombar"); if(!bar) return;
|
| 433 |
+
bar.addEventListener("click", function(e){
|
| 434 |
+
var b=e.target.closest("button"); if(!b) return;
|
| 435 |
+
var z=ZOOM_PRESET[b.getAttribute("data-zoom")]; if(z==null) return;
|
| 436 |
+
zoomTarget=z;
|
| 437 |
+
if(b.getAttribute("data-zoom")==="estate") autoRot=true; // let the estate slowly reveal itself
|
| 438 |
+
Array.prototype.forEach.call(bar.querySelectorAll("button"),function(x){x.classList.remove("on");});
|
| 439 |
+
b.classList.add("on");
|
| 440 |
+
});
|
| 441 |
+
})();
|
| 442 |
canvas.addEventListener("pointerdown", function(e){ dragging=true; autoRot=false; lx=e.clientX; ly=e.clientY; });
|
| 443 |
window.addEventListener("pointerup", function(){ dragging=false; });
|
| 444 |
window.addEventListener("pointermove", function(e){
|
|
|
|
| 447 |
// hover tooltip
|
| 448 |
mouse.x=(e.clientX/window.innerWidth)*2-1; mouse.y=-(e.clientY/window.innerHeight)*2+1;
|
| 449 |
ray.setFromCamera(mouse, camera);
|
|
|
|
| 450 |
var tip=$("tip");
|
| 451 |
+
var hit = ray.intersectObjects(organMeshes.map(function(g){return g.userData.node;}));
|
| 452 |
if(hit.length){
|
| 453 |
var g = organMeshes.filter(function(gg){return gg.userData.node===hit[0].object;})[0];
|
| 454 |
var u=g.userData, st=u.status;
|
|
|
|
| 457 |
'<div class="d">'+esc(u.organ.role)+'</div>'+
|
| 458 |
'<div class="st"><span style="color:'+stc+'">●</span> '+esc(st)+' · <span style="color:var(--dim)">'+esc(u.organ.note)+'</span></div>';
|
| 459 |
tip.style.left=(e.clientX+14)+"px"; tip.style.top=(e.clientY+12)+"px"; tip.style.opacity=1;
|
| 460 |
+
} else {
|
| 461 |
+
// council + estate nodes carry their own honest hover card
|
| 462 |
+
var xh = ray.intersectObjects(councilNodes.concat(estateNodes));
|
| 463 |
+
if(xh.length && xh[0].object.userData.hover){
|
| 464 |
+
var h=xh[0].object.userData.hover;
|
| 465 |
+
var col=(xh[0].object.material&&xh[0].object.material.color)?xh[0].object.material.color.getHexString():"dbe7ff";
|
| 466 |
+
tip.innerHTML='<div class="t" style="color:#'+col+'">'+h.title+'</div>'+
|
| 467 |
+
'<div class="d">'+h.role+'</div>'+
|
| 468 |
+
'<div class="st"><span style="color:var(--dim)">●</span> '+h.status+' · <span style="color:var(--dim)">'+h.note+'</span></div>';
|
| 469 |
+
tip.style.left=(e.clientX+14)+"px"; tip.style.top=(e.clientY+12)+"px"; tip.style.opacity=1;
|
| 470 |
+
} else tip.style.opacity=0;
|
| 471 |
+
}
|
| 472 |
});
|
| 473 |
+
canvas.addEventListener("wheel", function(e){ e.preventDefault(); zoomTarget=Math.max(16,Math.min(90, zoomTarget+e.deltaY*0.03)); }, {passive:false});
|
| 474 |
|
| 475 |
function resize(){
|
| 476 |
var w=window.innerWidth, h=window.innerHeight;
|
|
|
|
| 483 |
function tick(now){
|
| 484 |
var t=(now-t0)/1000;
|
| 485 |
if(autoRot) rotY += 0.0016;
|
| 486 |
+
zoom += (zoomTarget - zoom) * 0.08; // smooth glide between zoom presets
|
| 487 |
// camera framing from rot + zoom
|
| 488 |
camera.position.x = Math.sin(rotY)*zoom*Math.cos(rotX);
|
| 489 |
camera.position.z = Math.cos(rotY)*zoom*Math.cos(rotX);
|
|
|
|
| 526 |
flowGeo.attributes.position.needsUpdate=true; flowGeo.attributes.color.needsUpdate=true;
|
| 527 |
|
| 528 |
swarmGrp.rotation.y += 0.0006;
|
| 529 |
+
// council minds — slow counter-orbit, each tetra spins + breathes
|
| 530 |
+
councilGrp.rotation.y -= 0.0012;
|
| 531 |
+
councilNodes.forEach(function(m,i){
|
| 532 |
+
m.rotation.y += 0.02; m.rotation.x += 0.012;
|
| 533 |
+
m.material.emissiveIntensity = 0.35 + Math.abs(Math.sin(t*1.1 + i))*0.35;
|
| 534 |
+
});
|
| 535 |
+
// estate shell — gentle drift; each node slowly tumbles
|
| 536 |
+
estateGrp.rotation.y += 0.0004;
|
| 537 |
+
estateNodes.forEach(function(m){ m.rotation.y += 0.01; });
|
| 538 |
renderer.render(scene,camera);
|
| 539 |
requestAnimationFrame(tick);
|
| 540 |
}
|
|
|
|
| 607 |
g.userData.status = (ok===true)?"active":(ok===false?"degraded":"unknown");
|
| 608 |
}
|
| 609 |
|
| 610 |
+
function applyCouncil(j){
|
| 611 |
+
var nEl=$("cl_n"), beEl=$("cl_be"), whoEl=$("cl_who");
|
| 612 |
+
if(!j){ // roster unreachable -> honest fallback to the declared core 3
|
| 613 |
+
if(nEl) nEl.textContent="3 (core · declared)";
|
| 614 |
+
if(beEl) beEl.innerHTML='<span style="color:var(--grey)">roster unreachable</span>';
|
| 615 |
+
if(whoEl) whoEl.textContent=COUNCIL_FALLBACK.join(" · ");
|
| 616 |
+
buildCouncil(COUNCIL_FALLBACK);
|
| 617 |
+
return;
|
| 618 |
+
}
|
| 619 |
+
var people = j.personas || j.roster || [];
|
| 620 |
+
if(!Array.isArray(people)) people=[];
|
| 621 |
+
var names = people.map(function(p){ return (p && (p.name||p.persona||p.id)) || String(p); }).filter(Boolean);
|
| 622 |
+
if(!names.length) names=COUNCIL_FALLBACK;
|
| 623 |
+
var be = (j.backend && (j.backend.mode||j.backend.status)) || j.backend || j.mode || "—";
|
| 624 |
+
var beLive = /live/i.test(String(be));
|
| 625 |
+
if(nEl) nEl.textContent=String(j.count!=null?j.count:names.length);
|
| 626 |
+
if(beEl) beEl.innerHTML='<span style="color:'+(beLive?"var(--green)":"var(--amber)")+'">'+esc(String(be))+'</span>';
|
| 627 |
+
if(whoEl) whoEl.textContent=names.join(" · ");
|
| 628 |
+
buildCouncil(names);
|
| 629 |
+
}
|
| 630 |
+
|
| 631 |
function pollData(){
|
| 632 |
getJSON(API+"/code/healthz").then(applyPosture);
|
| 633 |
getJSON(API+"/v1/energy/budget").then(applyBudget);
|
| 634 |
+
getJSON(API+"/v1/ayllu/roster").then(applyCouncil);
|
| 635 |
// probe each organ endpoint; honest dim on null. Never blocks the scene.
|
| 636 |
organMeshes.forEach(function(g){
|
| 637 |
getJSON(g.userData.organ.probe).then(function(j){
|