metronome/src/progbox.js
Me Here 9e5c79b3b2 Split each form factor into a lean widget page + a separate info page
Previously every device .html bundled its full narrative (purpose, BOM,
dimensioned drawings, embedding docs) inside a #techinfo block that embed
mode (?embed=1) only CSS-hid — so every embedder and every landing-page
iframe downloaded all of it (showcase 77%, teacher 49% of the file) for
content no embedder ever sees.

Now:
  - <device>.html is the lean widget only: header + front view/controls +
    title + summary + program box. ?embed=1 still collapses to the bare
    widget; the heavy narrative is gone from the payload.
  - info-<device>.html (new, one per form factor) carries all the words —
    purpose, dimensions, priced BOM, embedding docs — and embeds the live
    widget at the top via the existing iframe + auto-resize protocol
    (new shared src/infoembed.html + src/infoembed.js).
  - Each device links out to its info page ("…dimensions & BOM →"); the
    landing panes and viewport bar now offer both Open ↗ and Specs & info ⓘ.
  - Dropped the now-dead "Show info" toggle (CSS + progbox.js).

Branding: adopt the official VARASYS "tagline on the bottom" logos from the
brand kit (light-background variant now matches; dark already did). The
tagline is baked into the PNGs, so remove the CSS .brand-tag / .dev-tag
spans and the showcase canvas-drawn tagline. Brand cyan #0AB3F7 / navy
#1C283F already match the official palette.

build.sh / deploy.sh: build + deploy the six new info-*.html pages.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 14:18:23 -05:00

55 lines
3.7 KiB
JavaScript

/* Per-device program I/O — assembled into each form-factor page (the thin widget).
Uses the engine codec (patchToSetup / setupToPatch / codeToSetlist) for decode + lint.
Host page provides: window.currentProgramString() and window.loadProgramString(plain).
Exposes window.progRefresh() — call it after the device's program changes. When the
page is embedded it posts {type:'varasys-prog'} to the parent instead of touching the box.
(Specs / dimensions / BOM live on the separate info-<device>.html page, not here.)
Defers to DOM-ready so the box is found wherever it sits on the page. */
(function () {
var embedded = document.documentElement.dataset.embed === "1";
var box, msg, editing = false;
function setMsg(t, ok) { if (!msg) return; msg.textContent = t || ""; msg.classList.toggle("ok", !!ok && !!t); msg.classList.toggle("bad", !ok && !!t); }
function lint(text) {
text = (text || "").trim(); if (!text) return { ok: false, msg: "empty" };
var m = text.match(/[#?&](p|sl)=([^&\s]+)/), kind = null, payload = text;
if (m) { kind = m[1]; try { payload = decodeURIComponent(m[2]); } catch (e) { payload = m[2]; } }
var b64 = /^[A-Za-z0-9_-]{12,}$/.test(payload) && !/[;:]/.test(payload);
try {
if (kind === "sl" || (kind !== "p" && b64)) {
var sl = codeToSetlist(payload); if (!sl.items || !sl.items.length) throw new Error("set-list code has no items");
return { ok: true, plain: setupToPatch(sl.items[0]), msg: "decoded set list “" + sl.title + "” — item 1" };
}
var s = patchToSetup(payload); if (!s.lanes.length) throw new Error("no lanes — try e.g. kick:4");
return { ok: true, plain: setupToPatch(s), msg: s.lanes.length + " lane" + (s.lanes.length > 1 ? "s" : "") + " · " + s.bpm + " BPM" };
} catch (e) { return { ok: false, msg: "✗ " + e.message }; }
}
function doLoad() {
var r = lint(box.value);
if (!r.ok) { box.classList.add("err"); setMsg(r.msg, false); return; }
box.classList.remove("err"); box.value = r.plain; setMsg("✓ " + r.msg, true);
try { if (window.loadProgramString) window.loadProgramString(r.plain); } catch (e) { setMsg("✗ " + e.message, false); }
}
// report the current program: to the parent when embedded, else into the box.
// No-op on pages without a program hook (e.g. panel-based Teacher/Player).
window.progRefresh = function () {
if (!window.currentProgramString) return;
var p = ""; try { p = window.currentProgramString(); } catch (e) {}
if (embedded) { try { parent.postMessage({ type: "varasys-prog", patch: p }, "*"); } catch (e) {} return; }
if (box && !editing) { box.value = p; setMsg("", true); }
};
function init() {
box = document.getElementById("dProg"); msg = document.getElementById("dProgMsg");
if (box) {
box.addEventListener("focus", function () { editing = true; });
box.addEventListener("blur", function () { editing = false; });
box.addEventListener("input", function () { box.classList.remove("err"); });
box.addEventListener("keydown", function (e) { if (e.key === "Enter") { e.preventDefault(); doLoad(); } });
var loadBtn = document.getElementById("dProgLoad"); if (loadBtn) loadBtn.addEventListener("click", doLoad);
var copyBtn = document.getElementById("dProgCopy"); if (copyBtn) copyBtn.addEventListener("click", function () {
try { navigator.clipboard.writeText(box.value); copyBtn.textContent = "Copied!"; setTimeout(function () { copyBtn.textContent = "Copy"; }, 1200); } catch (e) { box.select(); }
});
}
window.progRefresh();
}
if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", init); else init();
})();