🌱 Z0 — zero mode: blank dimension + first-visit choice card
The calm front door. Zero is not a muted world (moods do that) — it's an EMPTY
one you build by hand, the same matrix started from nothing.
- enterZero(): snapshots the current patch → gs_prezero, then loadPatch({}) empties
the field; sets clientMatrix so the hub can't re-populate the cables; gs_mood="0 zero".
- exitZeroToMood(name): restores gs_prezero (the full factory) then applies the mood —
leaving zero brings the whole world back.
- First-visit choice card ("one world, three doors"): 🌱 start from zero · 🌅 first
light · ⛈ the full storm. Replaces the silent default-into-first-light; backdrop/esc =
first light. Returning users keep their last mood; a returning zero-user re-enters zero.
- Mood submenu gains "0 zero — build from nothing"; F11 toggles zero (KEY_ACTIONS).
Verified live (logged-in preview): card renders with 3 doors; "start from zero" empties
the field (56 factory cables cleared, snapshotted); F11 restores all 56 + first light
(92 BPM locked); returning-zero reload re-enters zero with no card; console clean.
The 7+7 calm slot UI is Z1. Not deployed yet (deploy is Z6, post-review).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
35c094f8d8
commit
2a705e3a35
@ -2566,6 +2566,83 @@
|
||||
if (typeof p.bpm === "number") godtime.bpm = clamp(p.bpm, 40, 180);
|
||||
layoutDirty = true;
|
||||
}
|
||||
|
||||
// ---- zero mode — dimension zero -------------------------------------------
|
||||
// The calm front door: not a muted world (moods do that) but an EMPTY one — no
|
||||
// cables, nothing sounding — that you build by hand. It is the SAME matrix
|
||||
// (routesArr/tweaks/synth), just started from nothing; leaving zero restores
|
||||
// the world you had. Client-local, like moods — the hub/other users untouched.
|
||||
let zeroMode = false;
|
||||
function enterZero() {
|
||||
if (!zeroMode) { // snapshot what you had, so exiting can restore it
|
||||
try { localStorage.setItem("gs_prezero", JSON.stringify(serializePatch())); } catch (e) {}
|
||||
}
|
||||
clientMatrix = true; // own the matrix so the hub can't re-populate the cables
|
||||
loadPatch({ routes: [], tweaks: {}, seqs: {} }); // empty the field
|
||||
zeroMode = true;
|
||||
try { localStorage.setItem("gs_mood", "0 zero"); } catch (e) {}
|
||||
eventTicker.unshift({ text: "🌱 zero — the field is empty. build your own instrument: right-click a source → patch to → a voice, then press ♪", tAdded: now(), src: "sys" });
|
||||
layoutDirty = true;
|
||||
}
|
||||
function exitZeroToMood(name) { // leave zero back into the full world, then a mood
|
||||
if (zeroMode) {
|
||||
zeroMode = false;
|
||||
try { const pz = localStorage.getItem("gs_prezero"); if (pz) loadPatch(JSON.parse(pz)); } catch (e) {}
|
||||
}
|
||||
applyMood(name);
|
||||
}
|
||||
|
||||
// The three doors, shown once on a first-ever visit (replaces the silent drop
|
||||
// into first light). Backdrop/esc = first light, the gentle default.
|
||||
function showFirstVisitCard() {
|
||||
if (document.getElementById("firstcard")) return;
|
||||
const wrap = document.createElement("div");
|
||||
wrap.id = "firstcard";
|
||||
Object.assign(wrap.style, { position: "fixed", inset: "0", zIndex: "70", display: "flex",
|
||||
alignItems: "center", justifyContent: "center", background: "rgba(6,10,20,0.72)",
|
||||
backdropFilter: "blur(8px)", WebkitBackdropFilter: "blur(8px)" });
|
||||
const card = document.createElement("div");
|
||||
Object.assign(card.style, { maxWidth: "640px", width: "88%", textAlign: "center",
|
||||
color: "#d6e2f4", fontFamily: "inherit" });
|
||||
const h = document.createElement("div");
|
||||
h.textContent = "one world, three doors";
|
||||
Object.assign(h.style, { fontWeight: "800", fontSize: "clamp(22px,4vw,32px)", letterSpacing: "2px", marginBottom: "6px" });
|
||||
const sub = document.createElement("div");
|
||||
sub.textContent = "the planet is already playing. choose how you meet it.";
|
||||
Object.assign(sub.style, { color: "rgba(165,185,218,0.75)", fontStyle: "italic", marginBottom: "26px", fontSize: "14px" });
|
||||
card.appendChild(h); card.appendChild(sub);
|
||||
const row = document.createElement("div");
|
||||
Object.assign(row.style, { display: "flex", gap: "14px", flexWrap: "wrap", justifyContent: "center" });
|
||||
const doors = [
|
||||
["🌱", "start from zero", "an empty field. build the instrument yourself, one cable at a time.", () => enterZero()],
|
||||
["🌅", "first light", "a gentle arrangement — a bass line, soft hats, a small melody. ease in.", () => applyMood("first light")],
|
||||
["⛈", "the full storm", "every one of the world's hands on it at once. the factory instrument.", () => applyMood("the full storm")],
|
||||
];
|
||||
for (const [emoji, title, desc, act] of doors) {
|
||||
const b = document.createElement("div");
|
||||
Object.assign(b.style, { flex: "1 1 170px", minWidth: "160px", maxWidth: "200px", cursor: "pointer",
|
||||
padding: "20px 16px", borderRadius: "16px", background: "rgba(20,28,44,0.7)",
|
||||
border: "1px solid rgba(120,150,200,0.28)", transition: "transform .12s, border-color .12s" });
|
||||
b.onmouseenter = () => { b.style.transform = "translateY(-3px)"; b.style.borderColor = "rgba(150,200,255,0.7)"; };
|
||||
b.onmouseleave = () => { b.style.transform = ""; b.style.borderColor = "rgba(120,150,200,0.28)"; };
|
||||
b.innerHTML = '<div style="font-size:34px;margin-bottom:8px">' + emoji + '</div>'
|
||||
+ '<div style="font-weight:700;color:#eaf1ff;margin-bottom:6px">' + title + '</div>'
|
||||
+ '<div style="font-size:12px;color:rgba(180,195,225,0.75);line-height:1.5">' + desc + '</div>';
|
||||
b.onclick = () => { wrap.remove(); document.removeEventListener("keydown", onEsc, true); act(); };
|
||||
row.appendChild(b);
|
||||
}
|
||||
card.appendChild(row);
|
||||
const foot = document.createElement("div");
|
||||
foot.textContent = "you can switch any time — right-click the sky → 🌗 mood";
|
||||
Object.assign(foot.style, { marginTop: "22px", fontSize: "11px", color: "rgba(150,170,205,0.55)" });
|
||||
card.appendChild(foot);
|
||||
wrap.appendChild(card);
|
||||
const onEsc = (e) => { if (e.key === "Escape") { wrap.remove(); document.removeEventListener("keydown", onEsc, true); applyMood("first light"); } };
|
||||
wrap.onclick = (e) => { if (e.target === wrap) { wrap.remove(); document.removeEventListener("keydown", onEsc, true); applyMood("first light"); } };
|
||||
document.addEventListener("keydown", onEsc, true);
|
||||
document.body.appendChild(wrap);
|
||||
}
|
||||
|
||||
function makeGroupLocal(name, members) {
|
||||
if (!name || !members || !members.length) return;
|
||||
groupsInfo[name] = { label: name, members: members.slice() };
|
||||
@ -2841,10 +2918,13 @@
|
||||
try { firstEver = !localStorage.getItem("gs_seen"); } catch (e) {}
|
||||
try { saved = localStorage.getItem("gs_mood") || "first light"; } catch (e) {}
|
||||
setTimeout(() => {
|
||||
applyMood(saved);
|
||||
if (firstEver) {
|
||||
try { localStorage.setItem("gs_seen", "1"); } catch (e) {}
|
||||
eventTicker.unshift({ text: "🌅 first light — a gentle arrangement to start you off. right-click the sky → 🌗 mood for more (or the full storm when you're ready)", tAdded: now(), src: "sys" });
|
||||
showFirstVisitCard(); // three doors: zero / first light / storm
|
||||
} else if (saved === "0 zero") {
|
||||
enterZero(); // a returning zero-builder comes back to zero
|
||||
} else {
|
||||
applyMood(saved);
|
||||
}
|
||||
}, 900);
|
||||
}
|
||||
@ -5580,9 +5660,13 @@
|
||||
ctxItem(arr.open ? "close tracks" : "🎛 tracks — arrange clips",
|
||||
() => { arr.open ? closeArranger() : openArranger(); }, "T");
|
||||
const moodSub = ctxSub("🌗 mood — how much world");
|
||||
const z0 = document.createElement("div"); z0.className = "it";
|
||||
z0.textContent = zeroMode ? "0 zero — you're building it (a mood leaves)" : "0 zero — build from nothing";
|
||||
z0.onclick = () => { enterZero(); closeCtx(); };
|
||||
moodSub.appendChild(z0);
|
||||
for (const mn of Object.keys(MOODS)) {
|
||||
const it2 = document.createElement("div"); it2.className = "it"; it2.textContent = mn;
|
||||
it2.onclick = () => { applyMood(mn); closeCtx(); };
|
||||
it2.onclick = () => { zeroMode ? exitZeroToMood(mn) : applyMood(mn); closeCtx(); };
|
||||
moodSub.appendChild(it2);
|
||||
}
|
||||
if (mindChakra) ctxItem("step out of " + mindChakra.label, () => exitMind(), "esc");
|
||||
@ -6174,9 +6258,10 @@
|
||||
nudgeDown: { label: "lower the level of…", targeted: true, run: (k) => { const t = tw(k); t.gain = clamp((t.gain != null ? t.gain : 1) - 0.08, 0, 2); } },
|
||||
muteKey: { label: "mute / unmute…", targeted: true, run: (k) => toggleMuteLocal(k) },
|
||||
soloKeyA: { label: "solo…", targeted: true, run: (k) => { soloKey = soloKey === k ? null : k; } },
|
||||
zero: { label: "zero — build from nothing", run: () => { zeroMode ? exitZeroToMood("first light") : enterZero(); } },
|
||||
};
|
||||
const DEFAULT_KEYS = { F1: { a: "grimoire" }, F2: { a: "play" }, F3: { a: "chakraView" }, F4: { a: "skinNext" },
|
||||
F5: { a: "moodCycle" }, F6: { a: "tracks" }, F7: { a: "perform" }, F8: { a: "godLock" }, F9: { a: "earthEcho" }, F10: { a: "stage" } };
|
||||
F5: { a: "moodCycle" }, F6: { a: "tracks" }, F7: { a: "perform" }, F8: { a: "godLock" }, F9: { a: "earthEcho" }, F10: { a: "stage" }, F11: { a: "zero" } };
|
||||
let keymap = {};
|
||||
try { keymap = JSON.parse(localStorage.getItem("gs_keys")) || {}; } catch (e) {}
|
||||
if (!Object.keys(keymap).length) keymap = JSON.parse(JSON.stringify(DEFAULT_KEYS));
|
||||
|
||||
Loading…
Reference in New Issue
Block a user