ember-playtest

Playtest deploy surface — thin consumer of the core client rebuilt with VITE_APP_CONTEXT=playtest. NO client source lives here; the bundle builds inside EMBER_REPO (default ../windsurf-project).

Live surface

Invite-phased (EMBER_REQUIRE_INVITE; phases P0–P6).

Build & deploy

Deploy cwd is deploy/pages-playtest — always --branch main.

  • scripts/macos/build-playtest.command→ dist/pages/playtest
  • build-playtest.command --deploy+ wrangler Pages
  • EMBER_PLAYTEST_DISTdeploy a prebuilt dist
  • deploy/pages-playtest/wrangler.tomlproject binding

Cross-links

The rest of the EmberWorlds orbit.

and may * set `window.EMBER_SURFACE = ""` first to stamp feedback/context * (default: the page filename). */ (function () { "use strict"; if (document.getElementById("cl-gear")) return; var LOCAL = location.hostname === "localhost" || location.hostname === "127.0.0.1"; var FILE = location.protocol === "file:"; var GAME = LOCAL ? "http://localhost:3000" : "https://play.stoongame.com"; var SITE = LOCAL ? "http://localhost:4000" : "https://stoongame.com"; var BAKEOFF = LOCAL ? "http://localhost:4010" : "https://bakeoff.stoongame.com"; var AZTHERION = LOCAL ? "http://localhost:3001/aztherion" : "https://aztheriontools.stoongame.com"; var API = FILE ? "https://hub.stoongame.com" : location.origin; var SURFACE = window.EMBER_SURFACE || (location.pathname.split("/").pop() || "page").replace(/\.html$/, ""); var INSP_KEY = "ember_ui_inspector"; var css = [ "#cl-gear,#cl-fb{position:fixed;top:12px;z-index:99990;font:inherit;font-size:14px;", "background:#1e1a15;color:#e0a458;border:1px solid #3a332a;border-radius:6px;", "padding:4px 9px;cursor:pointer;line-height:1}", "#cl-gear{right:12px}#cl-fb{right:46px}", "#cl-gear:hover,#cl-fb:hover{border-color:#e0a458}", "#cl-menu{position:fixed;top:42px;right:12px;z-index:99991;min-width:200px;", "background:#14100cf2;border:1px solid #3a332a;border-radius:8px;padding:6px;", "box-shadow:0 8px 24px rgba(0,0,0,.55);display:none;font-family:system-ui,sans-serif}", "#cl-menu.on{display:block}", ".cl-row{display:flex;align-items:center;gap:8px;width:100%;box-sizing:border-box;", "padding:6px 10px;border:none;border-radius:5px;background:transparent;color:#e8ded0;", "font:inherit;font-size:12px;cursor:pointer;text-align:left;text-decoration:none}", ".cl-row:hover{background:rgba(224,164,88,.14)}", ".cl-sep{height:1px;background:#3a332a;margin:4px 6px}", "#cl-fbpop{position:fixed;right:12px;bottom:12px;z-index:99991;width:300px;display:none;", "background:#14100cf8;border:1px solid #3a332a;border-radius:8px;padding:10px;", "box-shadow:0 8px 24px rgba(0,0,0,.55);font-family:system-ui,sans-serif}", "#cl-fbpop.on{display:block}", "#cl-fbpop textarea{width:100%;min-height:70px;box-sizing:border-box;font:inherit;", "font-size:12px;background:#201a14;color:#f0e6d2;border:1px solid #3a332a;", "border-radius:6px;padding:6px 8px;resize:vertical}", "#cl-fbpop .cl-fb-row{display:flex;gap:8px;margin-top:8px;align-items:center}", "#cl-fbpop .cl-msg{font-size:11px;color:#b3a48c}", "#cl-fbpop .cl-send{font-size:11px;padding:3px 8px;border-radius:10px;", "border:1px solid #3a332a;background:transparent;color:#b3a48c;cursor:pointer}", "#cl-fbpop .cl-send:hover{color:#e0a458;border-color:#e0a458}", "#cl-insp-box{position:fixed;z-index:99989;pointer-events:none;display:none;", "border:2px solid #e0a458;border-radius:3px;background:rgba(224,164,88,.08)}", "#cl-insp-tag{position:fixed;z-index:99989;pointer-events:none;display:none;", "background:#14100cf2;color:#e0a458;font:11px/1.4 system-ui,sans-serif;", "padding:3px 7px;border-radius:5px;border:1px solid #3a332a;white-space:nowrap}", ].join("\n"); function el(tag, id, text) { var e = document.createElement(tag); if (id) e.id = id; if (text !== undefined) e.textContent = text; return e; } var style = el("style"); style.textContent = css; document.head.appendChild(style); /* ── UI inspector (shared flag + lite hover overlay) ─────────────── */ function inspOn() { try { return localStorage.getItem(INSP_KEY) === "1"; } catch (e) { return false; } } var inspBox = el("div", "cl-insp-box"); var inspTag = el("div", "cl-insp-tag"); document.body.appendChild(inspBox); document.body.appendChild(inspTag); function inspSync() { var on = inspOn(); inspBox.style.display = "none"; inspTag.style.display = "none"; inspRow.textContent = "UI inspector — " + (on ? "on" : "off"); } function identify(target) { if (!target || target === document.body || target === document.documentElement) return null; if (target.id && /^cl-/.test(target.id)) return null; var label = (target.dataset && target.dataset.uid) || target.id || target.tagName.toLowerCase() + (target.className && typeof target.className === "string" ? "." + target.className.trim().split(/\s+/).slice(0, 2).join(".") : ""); var r = target.getBoundingClientRect(); inspBox.style.display = "block"; inspBox.style.left = r.left - 2 + "px"; inspBox.style.top = r.top - 2 + "px"; inspBox.style.width = r.width + 4 + "px"; inspBox.style.height = r.height + 4 + "px"; inspTag.style.display = "block"; inspTag.textContent = label; var ty = r.bottom + 6; if (ty + 24 > window.innerHeight) ty = r.top - 26; inspTag.style.top = ty + "px"; inspTag.style.left = Math.max(4, Math.min(r.left, window.innerWidth - 160)) + "px"; return target; } document.addEventListener("pointermove", function (e) { if (!inspOn()) return; identify(e.target); }); document.addEventListener("pointerdown", function (e) { if (!inspOn()) return; var hit = identify(e.target); if (hit) { // Freeze the current highlight a moment so the id can be read. inspTag.textContent += " 📌"; } }); // Same boot contract as @ember/ui-inspector: ?inspect=1 / ?uidebug=1. var q = new URLSearchParams(location.search); if (q.get("inspect") === "1" || q.get("uidebug") === "1") { try { localStorage.setItem(INSP_KEY, "1"); } catch (e) {} } /* ── feedback popover ────────────────────────────────────────────── */ var fbPop = el("div", "cl-fbpop"); var ta = document.createElement("textarea"); ta.placeholder = "What broke / what's missing?"; var frow = el("div"); frow.className = "cl-fb-row"; var send = el("button", "cl-fb-send", "Send"); send.className = "cl-send"; var msg = el("span"); msg.className = "cl-msg"; frow.appendChild(send); frow.appendChild(msg); fbPop.appendChild(ta); fbPop.appendChild(frow); document.body.appendChild(fbPop); send.addEventListener("click", function () { var text = ta.value.trim(); if (!text) return; send.setAttribute("disabled", "1"); var token = null; try { token = localStorage.getItem("ember_token"); } catch (e) {} fetch(API + "/api/ui-feedback", { method: "POST", headers: Object.assign( { "content-type": "application/json" }, token ? { authorization: "Bearer " + token } : {}, ), body: JSON.stringify({ elementId: "page-feedback:" + SURFACE, elementKind: "page", context: SURFACE, page: location.pathname + location.search, comment: text.slice(0, 1900), }), }) .then(function (res) { send.removeAttribute("disabled"); msg.textContent = res.ok ? "sent — danke!" : "send failed"; if (res.ok) { ta.value = ""; setTimeout(function () { fbPop.classList.remove("on"); }, 1200); } }) .catch(function () { send.removeAttribute("disabled"); msg.textContent = "send failed"; }); }); /* ── gear menu ───────────────────────────────────────────────────── */ var menu = el("div", "cl-menu"); function row(label, href, onClick) { var r = href ? el("a") : el("button"); r.className = "cl-row"; r.textContent = label; if (href) { r.href = href; r.target = "_blank"; r.rel = "noopener"; } if (onClick) r.addEventListener("click", onClick); menu.appendChild(r); return r; } function sep() { var s = el("div"); s.className = "cl-sep"; menu.appendChild(s); } row("Game / lobby", GAME); row("Navigation hub", SITE + "/hub.html"); row("Landing site", SITE); row("Art bake-off", BAKEOFF); row("Aztherion workbench", AZTHERION); sep(); var inspRow = row("", null, function () { try { localStorage.setItem(INSP_KEY, inspOn() ? "0" : "1"); } catch (e) {} inspSync(); }); sep(); row("🐞 Feedback", null, function () { menu.classList.remove("on"); fbPop.classList.add("on"); ta.focus(); }); document.body.appendChild(menu); var gear = el("button", "cl-gear", "⚙"); gear.title = "Options"; gear.setAttribute("aria-haspopup", "menu"); document.body.appendChild(gear); var fbBtn = el("button", "cl-fb", "🐞"); fbBtn.title = "Feedback / issue report"; document.body.appendChild(fbBtn); gear.addEventListener("click", function (e) { e.stopPropagation(); inspSync(); menu.classList.toggle("on"); }); fbBtn.addEventListener("click", function (e) { e.stopPropagation(); fbPop.classList.toggle("on"); }); document.addEventListener("pointerdown", function (e) { if (menu.classList.contains("on") && !menu.contains(e.target) && e.target !== gear) menu.classList.remove("on"); if ( fbPop.classList.contains("on") && !fbPop.contains(e.target) && e.target !== fbBtn ) fbPop.classList.remove("on"); }); document.addEventListener("keydown", function (e) { if (e.key === "Escape") { menu.classList.remove("on"); fbPop.classList.remove("on"); } }); inspSync(); })();