🌱 Z1.5 — stage nodes are spinning orbs (John's vision)
Each built group becomes a spinning orb on the stage, not a flat chip — a glowing core in the group's hue with its picked feeds as petals orbiting around it (CSS zspin/zpulse). They accumulate as you build: pick from fire → one orb; add cosmos → two; and on. Orbs breathe live — the core glows by the group's average value, each petal brightens with its own feed. More feeds widen the orbit ring. Verified live: fire (3 petals) + cosmos (2 petals) render as two spinning glowing orbs; console clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
d30cea469c
commit
4e44533cdd
@ -2697,10 +2697,16 @@
|
|||||||
+ ".zdot{width:40px;height:40px;border-radius:50%;flex:0 0 auto;position:relative}"
|
+ ".zdot{width:40px;height:40px;border-radius:50%;flex:0 0 auto;position:relative}"
|
||||||
+ ".zlbl{font-size:12.5px;color:#c6d4ec;letter-spacing:.3px;line-height:1.2}"
|
+ ".zlbl{font-size:12.5px;color:#c6d4ec;letter-spacing:.3px;line-height:1.2}"
|
||||||
+ ".zlbl small{display:block;color:rgba(150,170,205,.6);font-size:10px}"
|
+ ".zlbl small{display:block;color:rgba(150,170,205,.6);font-size:10px}"
|
||||||
+ ".zstage{position:absolute;left:210px;right:210px;top:84px;bottom:150px;pointer-events:auto;display:flex;flex-wrap:wrap;align-content:flex-start;gap:12px;padding:14px;overflow:auto}"
|
+ ".zstage{position:absolute;left:210px;right:210px;top:84px;bottom:150px;pointer-events:auto;display:flex;flex-wrap:wrap;align-content:center;justify-content:center;gap:22px;padding:14px;overflow:auto}"
|
||||||
+ ".zstage .zhint{margin:auto;color:rgba(150,170,205,.5);font-size:13px;font-style:italic;text-align:center}"
|
+ ".zstage .zhint{margin:auto;color:rgba(150,170,205,.5);font-size:13px;font-style:italic;text-align:center;max-width:280px}"
|
||||||
+ ".zchip{border-radius:13px;padding:11px 13px;min-width:130px;background:rgba(20,28,44,.72);border:1px solid;font-size:12px;color:#dbe6f8}"
|
+ ".zorb{position:relative;width:132px;height:150px;flex:0 0 auto;cursor:grab}"
|
||||||
+ ".zchip b{color:#eaf1ff}.zchip .mem{color:rgba(180,195,225,.8);font-size:11px;margin-top:5px;line-height:1.5}"
|
+ ".zorb .core{position:absolute;left:50%;top:44%;width:44px;height:44px;margin:-22px;border-radius:50%;animation:zpulse 3.2s ease-in-out infinite}"
|
||||||
|
+ ".zorb .orbit{position:absolute;left:0;right:0;top:0;height:132px;animation:zspin 15s linear infinite}"
|
||||||
|
+ ".zorb .pet{position:absolute;left:50%;top:50%;width:13px;height:13px;margin:-6.5px;border-radius:50%;box-shadow:0 0 6px currentColor}"
|
||||||
|
+ ".zorb .cap{position:absolute;left:0;right:0;bottom:0;text-align:center;font-size:11px;color:#dbe6f8}"
|
||||||
|
+ ".zorb .cap small{color:rgba(170,190,220,.6)}"
|
||||||
|
+ "@keyframes zspin{to{transform:rotate(360deg)}}"
|
||||||
|
+ "@keyframes zpulse{0%,100%{transform:scale(1);opacity:.9}50%{transform:scale(1.12);opacity:1}}"
|
||||||
+ ".zrack{position:absolute;left:210px;right:210px;bottom:20px;height:120px;pointer-events:auto;overflow:auto}"
|
+ ".zrack{position:absolute;left:210px;right:210px;bottom:20px;height:120px;pointer-events:auto;overflow:auto}"
|
||||||
+ ".zrack .grp{font-size:10px;letter-spacing:1px;color:rgba(150,170,205,.55);text-transform:uppercase;margin:2px 0 4px}"
|
+ ".zrack .grp{font-size:10px;letter-spacing:1px;color:rgba(150,170,205,.55);text-transform:uppercase;margin:2px 0 4px}"
|
||||||
+ ".zsock{display:inline-block;margin:0 7px 7px 0;padding:6px 10px;border-radius:9px;background:rgba(18,26,42,.7);border:1px solid rgba(120,150,200,.2);font-size:11.5px;color:#aab8d4}"
|
+ ".zsock{display:inline-block;margin:0 7px 7px 0;padding:6px 10px;border-radius:9px;background:rgba(18,26,42,.7);border:1px solid rgba(120,150,200,.2);font-size:11.5px;color:#aab8d4}"
|
||||||
@ -2791,21 +2797,31 @@
|
|||||||
},
|
},
|
||||||
renderStage() {
|
renderStage() {
|
||||||
if (!this.stage) return;
|
if (!this.stage) return;
|
||||||
this.stage.innerHTML = "";
|
this.stage.innerHTML = ""; this.orbs = [];
|
||||||
const keys = Object.keys(this.nodes);
|
const keys = Object.keys(this.nodes);
|
||||||
if (!keys.length) {
|
if (!keys.length) {
|
||||||
const hint = document.createElement("div"); hint.className = "zhint";
|
const hint = document.createElement("div"); hint.className = "zhint";
|
||||||
hint.textContent = "click a ring on either side → pick a feed → it lands here. then drag it to a voice (soon).";
|
hint.textContent = "click a ring on either side → pick a feed → it becomes a spinning orb here. gather more and the orbs multiply. (drag to a voice — soon.)";
|
||||||
this.stage.appendChild(hint); return;
|
this.stage.appendChild(hint); return;
|
||||||
}
|
}
|
||||||
for (const gk of keys) {
|
for (const gk of keys) { // each built group → a spinning orb; they accumulate
|
||||||
const meta = GROUP_META[gk], node = this.nodes[gk];
|
const meta = GROUP_META[gk], mem = [...this.nodes[gk].members];
|
||||||
const chip = document.createElement("div"); chip.className = "zchip"; chip.style.borderColor = meta.hue;
|
const orb = document.createElement("div"); orb.className = "zorb"; orb.title = mem.map(k => (sources.get(k) || {}).label || k).join(" · ");
|
||||||
const names = [...node.members].map(k => { const n = sources.get(k); return n ? n.label : k; });
|
const orbit = document.createElement("div"); orbit.className = "orbit";
|
||||||
chip.innerHTML = '<b style="color:' + meta.hue + '">' + meta.emoji + " " + gk + "</b> · " + node.members.size
|
const R = 44 + Math.min(mem.length, 8) * 1.5; // more feeds → a slightly wider ring
|
||||||
+ '<div class="mem">' + names.map(x => x.replace(/</g, "<")).join(" · ") + "</div>";
|
mem.forEach((k, i) => {
|
||||||
chip._group = gk;
|
const a = (i / mem.length) * Math.PI * 2;
|
||||||
this.stage.appendChild(chip);
|
const pet = document.createElement("div"); pet.className = "pet";
|
||||||
|
pet.style.color = meta.hue; pet.style.background = meta.hue;
|
||||||
|
pet.style.transform = "translate(" + (Math.cos(a) * R).toFixed(1) + "px," + (Math.sin(a) * R).toFixed(1) + "px)";
|
||||||
|
pet._src = k; orbit.appendChild(pet);
|
||||||
|
});
|
||||||
|
const core = document.createElement("div"); core.className = "core"; core.style.background = meta.hue;
|
||||||
|
const cap = document.createElement("div"); cap.className = "cap";
|
||||||
|
cap.innerHTML = meta.emoji + " " + gk + " <small>· " + mem.length + "</small>";
|
||||||
|
orb.appendChild(orbit); orb.appendChild(core); orb.appendChild(cap);
|
||||||
|
orb._group = gk; orb._core = core;
|
||||||
|
this.stage.appendChild(orb); this.orbs.push(orb);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
update() {
|
update() {
|
||||||
@ -2823,6 +2839,14 @@
|
|||||||
row._fill.style.width = Math.round((n ? (n.normDisp || n.norm || 0) : 0) * 100) + "%";
|
row._fill.style.width = Math.round((n ? (n.normDisp || n.norm || 0) : 0) * 100) + "%";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (this.orbs) for (const orb of this.orbs) { // orbs breathe: core glows by group avg, petals by feed
|
||||||
|
const v = _grpAvg(orb._group);
|
||||||
|
orb._core.style.boxShadow = "0 0 " + (8 + v * 24).toFixed(0) + "px " + (1 + v * 3).toFixed(1) + "px " + GROUP_META[orb._group].hue;
|
||||||
|
for (const pet of orb.querySelectorAll(".pet")) {
|
||||||
|
const n = sources.get(pet._src);
|
||||||
|
pet.style.opacity = (0.32 + (n ? (n.normDisp || n.norm || 0) : 0) * 0.68).toFixed(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user