build a feeling, save it, hand it to a friend. a vibe is a small bundle of cables — toggling one adds or removes its cables; anything you built by hand stays.
';
+ // import row
+ const imp = document.createElement("div"); imp.style.cssText = "display:flex;gap:8px;margin-bottom:6px";
+ const impIn = document.createElement("input"); impIn.type = "text"; impIn.placeholder = "paste a GSV1… vibe code";
+ Object.assign(impIn.style, { flex: "1", padding: "8px 10px", borderRadius: "8px", background: "rgba(8,12,22,.8)", border: "1px solid rgba(120,150,200,.3)", color: "#eaf1ff", fontSize: "12px", fontFamily: "inherit", boxSizing: "border-box" });
+ const impBtn = document.createElement("button"); impBtn.textContent = "import"; impBtn.className = "midibtn";
+ const impMsg = document.createElement("div"); impMsg.style.cssText = "font-size:11px;color:rgba(150,170,205,.7);min-height:14px;margin-bottom:10px";
+ impBtn.onclick = () => { const v = importVibeCode(impIn.value); if (v) { impIn.value = ""; impMsg.textContent = "added “" + v.name + "” · " + v.routes.length + " cables — toggle it on below"; render(); } else impMsg.textContent = "that's not a valid vibe code."; };
+ imp.appendChild(impIn); imp.appendChild(impBtn); card.appendChild(imp); card.appendChild(impMsg);
+ // save-current
+ const saveCur = document.createElement("button"); saveCur.textContent = "+ save current wiring as a vibe"; saveCur.className = "midibtn"; saveCur.style.cssText += "margin-bottom:12px";
+ saveCur.onclick = () => saveCurrentAsVibe();
+ card.appendChild(saveCur);
+ // list
+ const list = document.createElement("div"); card.appendChild(list);
+ const render = () => {
+ list.innerHTML = "";
+ const vibes = vibeStore.all();
+ if (!vibes.length) { const e = document.createElement("div"); e.style.cssText = "color:rgba(150,170,205,.55);font-size:12px;font-style:italic;padding:10px 0"; e.textContent = "no vibes yet — save an orb in zero (right-click it), or your current wiring above."; list.appendChild(e); return; }
+ for (const vibe of vibes.slice().reverse()) {
+ const row = document.createElement("div"); row.style.cssText = "display:flex;align-items:center;gap:10px;padding:9px 4px;border-top:1px solid rgba(140,170,230,.1)";
+ const on = activeVibes.has(vibe.name);
+ const tog = document.createElement("div"); tog.textContent = on ? "◉" : "○"; tog.title = on ? "applied — click to remove its cables" : "click to add its cables";
+ tog.style.cssText = "cursor:pointer;font-size:18px;color:" + (on ? "#7cffb2" : "rgba(150,180,255,.6)");
+ tog.onclick = () => { if (activeVibes.has(vibe.name)) unapplyVibe(vibe.name); else applyVibe(vibe); render(); };
+ const nm = document.createElement("div"); nm.style.cssText = "flex:1;min-width:0";
+ nm.innerHTML = '' + (vibe.emoji || "🎁") + " " + (vibe.name || "vibe").replace(/'
+ + '
' + vibe.routes.length + " cable" + (vibe.routes.length > 1 ? "s" : "") + '
';
+ const cp = document.createElement("div"); cp.textContent = "⧉"; cp.title = "copy share code"; cp.style.cssText = "cursor:pointer;font-size:15px;color:#9fd0ff;padding:2px 6px";
+ cp.onclick = () => copyVibeCode(vibe);
+ const del = document.createElement("div"); del.textContent = "×"; del.title = "delete"; del.style.cssText = "cursor:pointer;font-size:16px;color:#ff9a9a;padding:2px 6px";
+ del.onclick = () => { if (activeVibes.has(vibe.name)) unapplyVibe(vibe.name); vibeStore.remove(vibe.name); render(); };
+ row.appendChild(tog); row.appendChild(nm); row.appendChild(cp); row.appendChild(del); list.appendChild(row);
+ }
+ };
+ render();
+ const foot = document.createElement("div"); foot.style.cssText = "text-align:right;margin-top:14px";
+ const close = document.createElement("button"); close.textContent = "done"; close.className = "midibtn"; close.onclick = () => wrap.remove();
+ foot.appendChild(close); card.appendChild(foot);
+ wrap.appendChild(card); document.body.appendChild(wrap);
+ wrap.onclick = (e) => { if (e.target === wrap) wrap.remove(); };
+ }
+ function offerVibeImport(vibe) { // #vibe= fragment / shared code — never auto-applies
+ const wrap = document.createElement("div"); wrap.id = "vibeoffer";
+ Object.assign(wrap.style, { position: "fixed", inset: "0", zIndex: "96", display: "flex", alignItems: "center", justifyContent: "center", background: "rgba(6,10,20,.7)", backdropFilter: "blur(7px)", WebkitBackdropFilter: "blur(7px)" });
+ const card = document.createElement("div");
+ Object.assign(card.style, { background: "rgba(14,20,34,.98)", border: "1px solid rgba(120,150,200,.3)", borderRadius: "16px", padding: "22px 24px", width: "360px", maxWidth: "90%", textAlign: "center", color: "#dbe6f8", boxShadow: "0 12px 44px rgba(0,0,0,.5)" });
+ card.innerHTML = '
' + (vibe.emoji || "🎁") + '
'
+ + '
someone shared a vibe
'
+ + '
“' + (vibe.name || "vibe").replace(/ 1 ? "s" : "") + '
'
+ + '
saved to your ✨ vibes. add its cables now?
';
+ const row = document.createElement("div"); row.style.cssText = "display:flex;gap:10px;justify-content:center;margin-top:16px";
+ const apply = document.createElement("button"); apply.textContent = "add it"; apply.className = "midibtn";
+ const later = document.createElement("button"); later.textContent = "just save"; later.className = "midibtn"; later.style.opacity = ".7";
+ apply.onclick = () => { applyVibe(vibe); wrap.remove(); eventTicker.unshift({ text: "✨ added “" + vibe.name + "” — press ♪ to hear it", tAdded: now(), src: "sys" }); };
+ later.onclick = () => wrap.remove();
+ row.appendChild(apply); row.appendChild(later); card.appendChild(row);
+ wrap.appendChild(card); document.body.appendChild(wrap);
+ wrap.onclick = (e) => { if (e.target === wrap) wrap.remove(); };
+ vibeStore.save(vibe); // saved either way; applying is opt-in
+ }
+
function makeGroupLocal(name, members) {
if (!name || !members || !members.length) return;
groupsInfo[name] = { label: name, members: members.slice() };
@@ -3335,6 +3558,11 @@
} else {
applyMood(saved);
}
+ try { // #vibe=GSV1… — a shared vibe in the URL; offer, never auto-apply (Z3)
+ const m = /[#&]vibe=([^&]+)/.exec(location.hash || "");
+ if (m) { const v = codeToVibe(decodeURIComponent(m[1])); if (v) setTimeout(() => offerVibeImport(v), 400);
+ history.replaceState(null, "", location.pathname + location.search); }
+ } catch (e) {}
}, 900);
}
}
@@ -6010,6 +6238,15 @@
row.appendChild(lbl); row.appendChild(sl); row.appendChild(x);
cb.appendChild(row);
}
+ if (rel.length) ctxItem("💾 save these as a vibe", () => { // this node's cables → a shareable vibe (Z3)
+ closeCtx();
+ nameVibeCard(isSrc ? (node.label || node.key) : prettyDest(node.key), (name) => {
+ const vibe = vibeFromRoutes(rel, name, "✨");
+ if (!vibe) return;
+ vibeStore.save(vibe); applyVibe(vibe);
+ eventTicker.unshift({ text: "💾 saved vibe “" + vibe.name + "” · " + vibe.routes.length + " cable" + (vibe.routes.length > 1 ? "s" : "") + " — ⚙ → ✨ vibes to share it", tAdded: now(), src: "sys" });
+ });
+ });
// lay a new cable from here
const pb = ctxSub(isSrc ? "patch to →" : "add driver ←");
@@ -7265,6 +7502,17 @@
};
loadRow.appendChild(templateSelect); loadRow.appendChild(loadBtn); panelEl.appendChild(loadRow);
refreshTemplates();
+
+ // Vibes — small, shareable route bundles (Z3). A dimension is everything;
+ // a vibe is a feeling you can hand a friend.
+ const vh = document.createElement("h3"); vh.textContent = "✨ VIBES"; vh.style.marginTop = "12px";
+ panelEl.appendChild(vh);
+ const vMsg = document.createElement("div"); vMsg.className = "hint"; vMsg.style.margin = "2px 0 6px";
+ vMsg.textContent = "build a feeling, save it, share the code.";
+ panelEl.appendChild(vMsg);
+ const vBtn = document.createElement("button"); vBtn.textContent = "open ✨ vibes"; vBtn.className = "midibtn";
+ vBtn.onclick = () => openVibesPanel();
+ panelEl.appendChild(vBtn);
}
async function refreshTemplates() {