Site phase 2: embeddable widget standard (?embed=1 + embed.js + embed.html)

- Every form factor supports ?embed=1: a head flag (set on <html>, no flash)
  strips the site chrome (base.css [data-embed]) + page-specific panels, leaving
  just the widget, and posts its height so the host can auto-size it.
- Config/settings string preloads via the existing #p=/#sl= hash. Added that
  hash handling to micro.html (it previously only loaded built-in tracks).
- New embed.js loader: <div data-varasys-metronome="micro" data-patch="…"> + one
  <script> → an auto-sizing iframe to <page>?embed=1#p=…. New embed.html documents
  it and dogfoods a live embedded widget.
- "Embed" nav link added across pages; build.sh/deploy.sh build embed.html and
  serve embed.js.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Me Here 2026-05-26 11:46:26 -05:00
parent dd362530ab
commit fc05cfe4c7
10 changed files with 238 additions and 5 deletions

View file

@ -30,6 +30,8 @@ def build(name):
out.write_text(src) out.write_text(src)
return out.stat().st_size return out.stat().st_size
for name in ("index.html","player.html","stage.html","micro.html","concepts.html"): for name in ("index.html","player.html","stage.html","micro.html","concepts.html","embed.html"):
print("built %s (%dKB)" % (name, build(name) // 1024)) print("built %s (%dKB)" % (name, build(name) // 1024))
pathlib.Path("dist/embed.js").write_text(pathlib.Path("embed.js").read_text()) # loader, served as-is
print("copied embed.js")
PY PY

View file

@ -54,6 +54,7 @@
<nav class="site-nav"> <nav class="site-nav">
<a href="/index.html">Editor</a> <a href="/index.html">Editor</a>
<span class="here">Concepts</span> <span class="here">Concepts</span>
<a href="/embed.html">Embed</a>
<button id="themeBtn" class="tbtn" title="toggle light / dark theme"></button> <button id="themeBtn" class="tbtn" title="toggle light / dark theme"></button>
</nav> </nav>
</header> </header>

View file

@ -40,10 +40,11 @@ fi
# stamp the version into the built copy only (source stays clean) # stamp the version into the built copy only (source stays clean)
echo "deployed v$BUILD -> $DEST_DIR" echo "deployed v$BUILD -> $DEST_DIR"
for f in index.html player.html stage.html micro.html concepts.html; do for f in index.html player.html stage.html micro.html concepts.html embed.html; do
sed "s|const APP_VERSION = \"[^\"]*\";|const APP_VERSION = \"$BUILD\";|" "$DIST_DIR/$f" > "$DEST_DIR/$f" sed "s|const APP_VERSION = \"[^\"]*\";|const APP_VERSION = \"$BUILD\";|" "$DIST_DIR/$f" > "$DEST_DIR/$f"
echo " $f ($(stat -c '%s' "$DEST_DIR/$f") bytes)" echo " $f ($(stat -c '%s' "$DEST_DIR/$f") bytes)"
done done
cp "$DIST_DIR/embed.js" "$DEST_DIR/embed.js"; echo " embed.js ($(stat -c '%s' "$DEST_DIR/embed.js") bytes)"
rm -f "$DEST_DIR/player-asbuilt.html" # renamed to stage.html rm -f "$DEST_DIR/player-asbuilt.html" # renamed to stage.html
# If real audio samples are added later (see the plan's GM-sample note), # If real audio samples are added later (see the plan's GM-sample note),

118
embed.html Normal file
View file

@ -0,0 +1,118 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Embed a PolyMeter widget — VARASYS</title>
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,@BUILD:favicon@">
<!-- Docs for the embeddable PolyMeter widget. Dogfoods embed.js with a live example. -->
<script>
(function(){ try{
var p = localStorage.getItem("metronome.theme");
if (p!=="light" && p!=="dark" && p!=="system") p = "system";
document.documentElement.dataset.theme = p==="system" ? (matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark") : p;
} catch(e){ document.documentElement.dataset.theme = "dark"; } })();
</script>
<style>
/*@BUILD:include:src/base.css@*/
:root{ --bg1:#12151c; --bg2:#05070a; --txt:#c7d0db; --muted:#7f8b9a; --link:#6cb6ff;
--panel-bg:#161b22; --panel-bd:#2a313c; --field-bg:#0e1116; }
:root[data-theme="light"]{ --bg1:#f5f8fc; --bg2:#dde4ec; --txt:#1e2630; --muted:#5c6776; --link:#1769c4;
--panel-bg:#ffffff; --panel-bd:#d2dae4; --field-bg:#f1f4f8; }
body{ margin:0; min-height:100vh; padding:22px 16px 56px; color:var(--txt);
background:radial-gradient(circle at 50% -8%, var(--bg1), var(--bg2)); }
a{ color:var(--link); }
main{ width:100%; max-width:760px; margin:26px auto 0; }
h1{ font-size:24px; margin:0 0 4px; } h2{ font-size:16px; margin:26px 0 8px; }
p{ color:var(--muted); font-size:14px; line-height:1.6; } p.lead{ max-width:60ch; }
pre{ background:var(--field-bg); border:1px solid var(--panel-bd); border-radius:9px; padding:12px 14px;
overflow:auto; font-size:12.5px; line-height:1.5; color:var(--txt); }
code{ background:var(--field-bg); border:1px solid var(--panel-bd); border-radius:4px; padding:1px 5px; font-size:12px; }
table{ border-collapse:collapse; font-size:13px; width:100%; }
th,td{ text-align:left; padding:5px 8px; border-bottom:1px solid var(--panel-bd); vertical-align:top; }
th{ color:var(--muted); font-weight:600; font-size:11px; text-transform:uppercase; letter-spacing:.04em; }
td.k{ white-space:nowrap; color:var(--cyan); font-family:"Courier New",monospace; }
.demo{ background:var(--panel-bg); border:1px solid var(--panel-bd); border-radius:14px; padding:14px; margin-top:8px; }
.site-foot{ max-width:760px; margin:40px auto 0; font-size:12px; color:var(--muted); }
</style>
</head>
<body>
<header class="site-head">
<div class="head-left">
<a class="brand" href="/" title="VARASYS — Simplifying Complexity">
<img class="brand-logo brand-dark" src="data:image/png;base64,@BUILD:logo-dark@" alt="VARASYS — Simplifying Complexity" />
<img class="brand-logo brand-light" src="data:image/png;base64,@BUILD:logo-light@" alt="VARASYS — Simplifying Complexity" />
</a>
<span class="page-name"><b>PolyMeter</b> · Embed</span>
</div>
<nav class="site-nav">
<a href="/index.html">Editor</a>
<a href="/concepts.html">Concepts</a>
<span class="here">Embed</span>
<button id="themeBtn" class="tbtn" title="toggle light / dark theme"></button>
</nav>
</header>
<main>
<h1>Embed a PolyMeter widget</h1>
<p class="lead">Every PolyMeter form factor doubles as an embeddable widget. Drop one into any page with
a placeholder + one script tag — no build step, no dependencies. It loads in an iframe and is preloaded
with whatever <b>program / settings string</b> you give it.</p>
<h2>Drop-in (recommended)</h2>
<pre>&lt;div data-varasys-metronome="micro"
data-patch="v1;t120;kick:4;snare:4=.X.X;hatClosed:4/2"&gt;&lt;/div&gt;
&lt;script src="https://metronome.varasys.io/embed.js"&gt;&lt;/script&gt;</pre>
<p>The script replaces the <code>&lt;div&gt;</code> with an auto-sizing iframe. Here it is, live on this page:</p>
<div class="demo">
<div data-varasys-metronome="micro" data-patch="v1;t120;kick:4;snare:4=.X.X;hatClosed:4/2"></div>
</div>
<h2>Or a plain iframe</h2>
<pre>&lt;iframe src="https://metronome.varasys.io/micro.html?embed=1#p=v1;t120;kick:4"
width="360" height="300" style="border:0"&gt;&lt;/iframe&gt;</pre>
<h2>Form factors — <code>data-varasys-metronome</code></h2>
<table>
<thead><tr><th>value</th><th>widget</th></tr></thead>
<tbody>
<tr><td class="k">editor</td><td>PE1 PolyMeter Editor (full app)</td></tr>
<tr><td class="k">initial</td><td>PM1 Initial (idealized device)</td></tr>
<tr><td class="k">stage</td><td>PM1 Stage (pedalboard device)</td></tr>
<tr><td class="k">micro</td><td>PMµ Micro (practice unit)</td></tr>
</tbody>
</table>
<h2>Configuration / settings string</h2>
<table>
<thead><tr><th>attribute</th><th>what</th></tr></thead>
<tbody>
<tr><td class="k">data-patch</td><td>A PolyMeter program string, e.g. <code>v1;t120;kick:4;snare:4=.X.X;hatClosed:4/2</code>. Copy it from the editor's program field.</td></tr>
<tr><td class="k">data-setlist</td><td>A base64url setlist code (a whole set list) — share it from the editor.</td></tr>
<tr><td class="k">data-width / data-height</td><td>iframe size (default <code>100% × 300</code>; height autogrows to the widget).</td></tr>
</tbody>
</table>
<p>Under the hood the loader builds <code>&lt;page&gt;?embed=1#p=&lt;patch&gt;</code>; the page's <code>?embed=1</code> mode strips the
site chrome so only the widget shows. That's the same way our own Concept &amp; Info pages embed it.</p>
</main>
<div class="site-foot">VARASYS · Simplifying Complexity — <span id="appVersion">v0.0.1-dev</span></div>
<script>
const APP_VERSION = "v0.0.1-dev";
const $ = (id)=>document.getElementById(id);
try{ $("appVersion").textContent = "v"+APP_VERSION.replace(/^v/,""); }catch(e){}
const THEMES=["system","light","dark"];
function effectiveTheme(p){ return p==="system" ? (matchMedia("(prefers-color-scheme: light)").matches?"light":"dark") : p; }
function themePref(){ try{ const p=localStorage.getItem("metronome.theme"); return (p==="light"||p==="dark"||p==="system")?p:"system"; }catch(e){ return "system"; } }
function applyTheme(p){ try{ localStorage.setItem("metronome.theme",p); }catch(e){}
document.documentElement.dataset.theme = effectiveTheme(p);
$("themeBtn").textContent = p==="system" ? "◐" : p==="light" ? "☀" : "☾"; $("themeBtn").title="Theme: "+p; }
$("themeBtn").onclick = ()=> applyTheme(THEMES[(THEMES.indexOf(themePref())+1)%THEMES.length]);
matchMedia("(prefers-color-scheme: light)").addEventListener("change", ()=>{ if(themePref()==="system") applyTheme("system"); });
applyTheme(themePref());
</script>
<script src="/embed.js"></script>
</body>
</html>

54
embed.js Normal file
View file

@ -0,0 +1,54 @@
/* VARASYS PolyMeter embed loader (zero-dependency, ~1 KB).
*
* Drop a placeholder + this script into any page:
* <div data-varasys-metronome="micro" data-patch="v1;t120;kick:4;snare:4=.X.X"></div>
* <script src="https://metronome.varasys.io/embed.js"></script>
*
* Attributes:
* data-varasys-metronome editor | initial | stage | micro (which form factor)
* data-patch a PolyMeter program/settings string (preloads it)
* data-setlist a base64url set-list code (alternative to data-patch)
* data-width / data-height iframe size (default 100% × 300; height auto-grows)
*
* Each placeholder becomes an <iframe src=".../<page>?embed=1#p=<patch>"> (the page's
* own ?embed=1 mode strips the site chrome) and auto-resizes to the widget's content.
*/
(function () {
var PAGES = { editor: "index.html", initial: "player.html", stage: "stage.html", micro: "micro.html" };
var me = document.currentScript;
var ORIGIN = me ? me.src.replace(/\/embed\.js(\?.*)?$/, "") : location.origin;
function build(el) {
var v = (el.getAttribute("data-varasys-metronome") || "micro").toLowerCase();
var page = PAGES[v] || "micro.html";
var patch = el.getAttribute("data-patch");
var sl = el.getAttribute("data-setlist");
var hash = patch ? "#p=" + encodeURIComponent(patch)
: sl ? "#sl=" + encodeURIComponent(sl) : "";
var f = document.createElement("iframe");
f.src = ORIGIN + "/" + page + "?embed=1" + hash;
f.title = "VARASYS PolyMeter — " + v;
f.loading = "lazy";
f.setAttribute("allow", "autoplay");
f.setAttribute("data-vmeter", v);
f.style.cssText = "border:0;display:block;max-width:100%;width:" +
(el.getAttribute("data-width") || "100%") + ";height:" +
(el.getAttribute("data-height") || "300") + "px";
(el.replaceWith ? el.replaceWith(f) : el.parentNode.replaceChild(f, el));
}
function init() {
var els = document.querySelectorAll("[data-varasys-metronome]");
for (var i = 0; i < els.length; i++) build(els[i]);
}
// auto-resize: the widget posts { type:'varasys-h', h } on load/resize
window.addEventListener("message", function (e) {
if (!e.data || e.data.type !== "varasys-h" || typeof e.data.h !== "number") return;
var f = document.querySelectorAll("iframe[data-vmeter]");
for (var i = 0; i < f.length; i++) if (f[i].contentWindow === e.source) f[i].style.height = e.data.h + "px";
});
if (document.readyState !== "loading") init();
else document.addEventListener("DOMContentLoaded", init);
})();

View file

@ -5,6 +5,14 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PE1 — PolyMeter Editor</title> <title>PE1 — PolyMeter Editor</title>
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,@BUILD:favicon@"> <link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,@BUILD:favicon@">
<script>
/* ?embed=1 → strip site chrome (base.css [data-embed]) + auto-size to the host */
(function(){ if(!/[?&]embed=1/.test(location.search)) return;
document.documentElement.dataset.embed="1";
function ph(){ try{ parent.postMessage({type:"varasys-h",h:Math.ceil(document.documentElement.getBoundingClientRect().height)},"*"); }catch(e){} }
addEventListener("load",ph); addEventListener("resize",ph); setTimeout(ph,300); setTimeout(ph,1000);
})();
</script>
<script> <script>
// Set theme before first paint (avoids a flash). Preference is system|light|dark // Set theme before first paint (avoids a flash). Preference is system|light|dark
// (default system → follows the OS); "system" resolves to the OS scheme here. // (default system → follows the OS); "system" resolves to the OS scheme here.
@ -210,6 +218,8 @@
.menu { position:absolute; top:36px; right:0; background:var(--panel-2); border:1px solid var(--edge); border-radius:10px; padding:6px; display:flex; flex-direction:column; gap:4px; box-shadow:0 12px 30px rgba(0,0,0,.5); z-index:70; min-width:150px; } .menu { position:absolute; top:36px; right:0; background:var(--panel-2); border:1px solid var(--edge); border-radius:10px; padding:6px; display:flex; flex-direction:column; gap:4px; box-shadow:0 12px 30px rgba(0,0,0,.5); z-index:70; min-width:150px; }
.menu[hidden] { display:none; } .menu[hidden] { display:none; }
.menu button { text-align:left; } .menu button { text-align:left; }
/* embed mode: drop the header + legend, keep the editor */
[data-embed] .appheader, [data-embed] .kbd-legend { display:none !important; }
</style> </style>
</head> </head>
<body> <body>
@ -219,6 +229,7 @@
<h1 style="margin:0">PE1 <span style="font-weight:400; opacity:.75">PolyMeter Editor</span> <span class="lane-meta" id="appVersion" title="build version">v0.0.1-dev</span></h1> <h1 style="margin:0">PE1 <span style="font-weight:400; opacity:.75">PolyMeter Editor</span> <span class="lane-meta" id="appVersion" title="build version">v0.0.1-dev</span></h1>
<div class="appheader-ctrls" style="display:flex; align-items:center; gap:10px"> <div class="appheader-ctrls" style="display:flex; align-items:center; gap:10px">
<a href="/concepts.html" style="font-size:13px; color:var(--muted); text-decoration:none" title="Concept gallery — hardware & widget form factors">Concepts</a> <a href="/concepts.html" style="font-size:13px; color:var(--muted); text-decoration:none" title="Concept gallery — hardware & widget form factors">Concepts</a>
<a href="/embed.html" style="font-size:13px; color:var(--muted); text-decoration:none" title="Embed a PolyMeter widget">Embed</a>
<button id="themeBtn" title="toggle light / dark theme"></button> <button id="themeBtn" title="toggle light / dark theme"></button>
<button id="helpBtn" title="keyboard shortcuts (?)">?</button> <button id="helpBtn" title="keyboard shortcuts (?)">?</button>
<a class="brand" href="https://varasys.io" target="_blank" rel="noopener" <a class="brand" href="https://varasys.io" target="_blank" rel="noopener"

View file

@ -5,6 +5,14 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
<title>VARASYS PMµ — micro (home practice)</title> <title>VARASYS PMµ — micro (home practice)</title>
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,@BUILD:favicon@"> <link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,@BUILD:favicon@">
<script>
/* ?embed=1 → strip site chrome (base.css [data-embed]) + auto-size to the host */
(function(){ if(!/[?&]embed=1/.test(location.search)) return;
document.documentElement.dataset.embed="1";
function ph(){ try{ parent.postMessage({type:"varasys-h",h:Math.ceil(document.documentElement.getBoundingClientRect().height)},"*"); }catch(e){} }
addEventListener("load",ph); addEventListener("resize",ph); setTimeout(ph,300); setTimeout(ph,1000);
})();
</script>
<!-- <!--
"Micro" — a stripped-down home-practice metronome on the same RP2040 firmware. "Micro" — a stripped-down home-practice metronome on the same RP2040 firmware.
Hardware is just: ONE depressable scroll/rotary encoder (tempo), a red 7-seg Hardware is just: ONE depressable scroll/rotary encoder (tempo), a red 7-seg
@ -85,6 +93,8 @@
.usb .port{ width:24px; height:9px; border-radius:4px; background:#0a0c0f; border:2px solid #5b6470; box-shadow:inset 0 0 2px #000 } .usb .port{ width:24px; height:9px; border-radius:4px; background:#0a0c0f; border:2px solid #5b6470; box-shadow:inset 0 0 2px #000 }
.hint{ max-width:330px; text-align:center; font-size:11px; color:var(--muted); line-height:1.5 } .hint{ max-width:330px; text-align:center; font-size:11px; color:var(--muted); line-height:1.5 }
/* embed mode: just the device */
[data-embed] .hint { display:none !important; }
</style> </style>
</head> </head>
<body> <body>
@ -100,6 +110,7 @@
<nav class="site-nav"> <nav class="site-nav">
<a href="/index.html">Editor</a> <a href="/index.html">Editor</a>
<a href="/concepts.html">Concepts</a> <a href="/concepts.html">Concepts</a>
<a href="/embed.html">Embed</a>
<button id="themeBtn" class="tbtn" title="toggle light / dark theme"></button> <button id="themeBtn" class="tbtn" title="toggle light / dark theme"></button>
</nav> </nav>
</header> </header>
@ -157,8 +168,17 @@ function stopAudio(){ state.running=false; clearInterval(schedulerTimer); schedu
function toggle(){ state.running ? stopAudio() : startAudio(); } function toggle(){ state.running ? stopAudio() : startAudio(); }
/* ========================= TRACKS (built-in seed grooves, flattened) ========= */ /* ========================= TRACKS (built-in seed grooves, flattened) ========= */
const tracks = SEED_SETLISTS.flatMap(sl => sl.items.map(([n,p]) => ({ name:n, ...patchToSetup(p) }))); let tracks = SEED_SETLISTS.flatMap(sl => sl.items.map(([n,p]) => ({ name:n, ...patchToSetup(p) })));
let trackIdx=0, previewIdx=0; let trackIdx=0, previewIdx=0;
// preload from a share link / embed config string (#p=<patch> or #sl=<set-list code>)
function tracksFromHash(){
const m=(location.hash||"").match(/[#&](p|sl)=([^&]+)/); if(!m) return null;
let payload=m[2]; try{ payload=decodeURIComponent(payload); }catch(e){}
try{
if(m[1]==="sl"){ const sl=codeToSetlist(payload); return sl.items.length ? sl.items.map(it=>({...it})) : null; }
const s=patchToSetup(payload); return s.lanes.length ? [{name:"Patch",...s}] : null;
}catch(e){ return null; }
}
function loadTrack(i){ function loadTrack(i){
const n=tracks.length; if(!n) return; trackIdx=((i%n)+n)%n; previewIdx=trackIdx; const n=tracks.length; if(!n) return; trackIdx=((i%n)+n)%n; previewIdx=trackIdx;
const t=tracks[trackIdx]; setBpm(t.bpm||120); meters=buildMeters(t.lanes); // ramps/bars ignored — a steady practice loop const t=tracks[trackIdx]; setBpm(t.bpm||120); meters=buildMeters(t.lanes); // ramps/bars ignored — a steady practice loop
@ -240,6 +260,7 @@ addEventListener("keydown",(e)=>{
}); });
/* ========================= INIT ============================================== */ /* ========================= INIT ============================================== */
{ const ht=tracksFromHash(); if(ht) tracks=ht; } // a #p=/#sl= link (or embed config) overrides the built-ins
loadTrack(0); loadTrack(0);
render(); render();
</script> </script>

View file

@ -5,6 +5,14 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
<title>VARASYS PM1 — hardware player (mockup)</title> <title>VARASYS PM1 — hardware player (mockup)</title>
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,@BUILD:favicon@"> <link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,@BUILD:favicon@">
<script>
/* ?embed=1 → strip site chrome (base.css [data-embed]) + auto-size to the host */
(function(){ if(!/[?&]embed=1/.test(location.search)) return;
document.documentElement.dataset.embed="1";
function ph(){ try{ parent.postMessage({type:"varasys-h",h:Math.ceil(document.documentElement.getBoundingClientRect().height)},"*"); }catch(e){} }
addEventListener("load",ph); addEventListener("resize",ph); setTimeout(ph,300); setTimeout(ph,1000);
})();
</script>
<!-- <!--
Hardware-device MOCKUP / simulator for the Pi Pico (RP2040) build of the Hardware-device MOCKUP / simulator for the Pi Pico (RP2040) build of the
Stackable Metronome. The physical unit can't show the multi-lane editor or Stackable Metronome. The physical unit can't show the multi-lane editor or
@ -190,6 +198,8 @@
background:var(--bg1); color:var(--txt); font-size:20px; text-align:center; padding:24px } background:var(--bg1); color:var(--txt); font-size:20px; text-align:center; padding:24px }
body.stage .rotate-hint .rh-icon{ font-size:64px; line-height:1; color:var(--cyan) } body.stage .rotate-hint .rh-icon{ font-size:64px; line-height:1; color:var(--cyan) }
} }
/* embed mode: just the device */
[data-embed] .panel { display:none !important; }
</style> </style>
</head> </head>
<body> <body>
@ -205,6 +215,7 @@
<nav class="site-nav"> <nav class="site-nav">
<a href="/index.html">Editor</a> <a href="/index.html">Editor</a>
<a href="/concepts.html">Concepts</a> <a href="/concepts.html">Concepts</a>
<a href="/embed.html">Embed</a>
<button id="fsBtn" class="tbtn" title="Full screen (landscape)"></button> <button id="fsBtn" class="tbtn" title="Full screen (landscape)"></button>
<button id="themeBtn" class="tbtn" title="toggle light / dark theme"></button> <button id="themeBtn" class="tbtn" title="toggle light / dark theme"></button>
</nav> </nav>

View file

@ -29,5 +29,7 @@ body {
.tbtn { background:transparent; color:var(--muted,#7f8b9a); border:1px solid var(--panel-bd,#2a313c); .tbtn { background:transparent; color:var(--muted,#7f8b9a); border:1px solid var(--panel-bd,#2a313c);
border-radius:8px; padding:3px 9px; font-size:14px; line-height:1; cursor:pointer; } border-radius:8px; padding:3px 9px; font-size:14px; line-height:1; cursor:pointer; }
.tbtn:hover { color:var(--txt,#c7d0db); } .tbtn:hover { color:var(--txt,#c7d0db); }
/* embed mode: pages opened with ?embed=1 strip all site chrome (the widget only) */ /* embed mode: pages opened with ?embed=1 strip all site chrome (the widget only).
body[data-embed] .site-head, body[data-embed] .site-foot { display:none !important; } The flag is set on <html> in a head pre-paint script (no flash). */
[data-embed] .site-head, [data-embed] .site-foot { display:none !important; }
[data-embed] body { padding:10px !important; }

View file

@ -5,6 +5,14 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
<title>VARASYS PM1 — stage (pedalboard build)</title> <title>VARASYS PM1 — stage (pedalboard build)</title>
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,@BUILD:favicon@"> <link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,@BUILD:favicon@">
<script>
/* ?embed=1 → strip site chrome (base.css [data-embed]) + auto-size to the host */
(function(){ if(!/[?&]embed=1/.test(location.search)) return;
document.documentElement.dataset.embed="1";
function ph(){ try{ parent.postMessage({type:"varasys-h",h:Math.ceil(document.documentElement.getBoundingClientRect().height)},"*"); }catch(e){} }
addEventListener("load",ph); addEventListener("resize",ph); setTimeout(ph,300); setTimeout(ph,1000);
})();
</script>
<!-- <!--
"As-built" variant of the PM-1 player: the same firmware/engine, drawn with the "As-built" variant of the PM-1 player: the same firmware/engine, drawn with the
parts you'd actually solder for an RP2040 build — parts you'd actually solder for an RP2040 build —
@ -192,6 +200,9 @@
.bom .part{ color:var(--txt) } .bom .part{ color:var(--txt) }
.bom .part .spec{ color:var(--muted); font-weight:400 } .bom .part .spec{ color:var(--muted); font-weight:400 }
.bom tr.total td{ font-weight:700; color:var(--txt); border-top:2px solid var(--panel-bd); border-bottom:none; padding-top:8px } .bom tr.total td{ font-weight:700; color:var(--txt); border-top:2px solid var(--panel-bd); border-bottom:none; padding-top:8px }
/* embed mode: just the device (drop the top-edge view, dims, loader, BOM) */
[data-embed] .topview, [data-embed] .bom-panel, [data-embed] .panel,
[data-embed] .dim-y, [data-embed] .dim-x { display:none !important; }
</style> </style>
</head> </head>
<body> <body>
@ -207,6 +218,7 @@
<nav class="site-nav"> <nav class="site-nav">
<a href="/index.html">Editor</a> <a href="/index.html">Editor</a>
<a href="/concepts.html">Concepts</a> <a href="/concepts.html">Concepts</a>
<a href="/embed.html">Embed</a>
<button id="themeBtn" class="tbtn" title="toggle light / dark theme"></button> <button id="themeBtn" class="tbtn" title="toggle light / dark theme"></button>
</nav> </nav>
</header> </header>