The parallel agent's full session, committed now that it's solo: - Grammar: flam/drag/roll ornaments (f/F d/D z/Z, per-lane orns channel) across src/engine.js, pico-cp/pico-explorer/pico-scroll app.py, pico/main.py, rust/track-format, + golden vectors / conformance (tests/, rust/track-format/tests). - Live-sync deep-sync: SysEx 0x44 SLSYNC + 0x45 LOGSYNC (docs/livesync-protocol.md, src/livesync.js). - PM_E-2 notation: web engine (pm_e-2.html, build/deploy/index/embed wiring) + Rust device port (pm-ui draw_notation rewrite + LaneView.groups, pm-kit ViewMode, uisim notesim). Verified: node tests/run.mjs 47 pass / 1 known; ./rust/run.sh green; pm-kit firmware + uisim compile.
138 lines
7.5 KiB
HTML
138 lines
7.5 KiB
HTML
<!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; }
|
||
.pick{ display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-top:14px; }
|
||
.pick label{ font-size:13px; color:var(--txt); }
|
||
.pick select{ background:var(--field-bg); color:var(--txt); border:1px solid var(--panel-bd); border-radius:8px; padding:7px 10px; font-size:13px; }
|
||
.ff-name{ color:var(--cyan); font-weight:600; font-size:13px; }
|
||
.site-foot{ max-width:760px; margin:40px auto 0; font-size:12px; color:var(--muted); }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
/*@BUILD:include:src/header.html@*/
|
||
|
||
<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>
|
||
|
||
<p class="pick"><label for="ffSel">Show snippets for:</label>
|
||
<select id="ffSel">
|
||
<option value="editor">PM_E‑1 Editor</option>
|
||
<option value="pme2">PM_E‑2 Editor (Notation)</option>
|
||
<option value="teacher">PM_T‑1 Teacher</option>
|
||
<option value="stage">PM_S‑1 Stage</option>
|
||
<option value="micro" selected>PM_P‑1 Practice</option>
|
||
<option value="showcase">PM_D‑1 Display</option>
|
||
<option value="initial">PM_C‑1 Concept</option>
|
||
</select>
|
||
<span class="ff-name"></span></p>
|
||
|
||
<h2>Drop-in (recommended)</h2>
|
||
<pre id="snipDrop"></pre>
|
||
<p>The script replaces the <code><div></code> with an auto-sizing iframe. Here's the <span class="ff-name"></span>, live on this page:</p>
|
||
<div class="demo"><iframe id="demoFrame" title="live embed preview" allow="autoplay" style="border:0;display:block;width:100%;height:300px"></iframe></div>
|
||
|
||
<h2>Or a plain iframe</h2>
|
||
<pre id="snipIframe"></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>PM_E‑1 PolyMeter Editor (full web app)</td></tr>
|
||
<tr><td class="k">teacher</td><td>PM_T‑1 Teacher (studio / lesson console)</td></tr>
|
||
<tr><td class="k">stage</td><td>PM_S‑1 Stage (foot‑pedal stompbox)</td></tr>
|
||
<tr><td class="k">micro</td><td>PM_P‑1 Practice (inline practice bar)</td></tr>
|
||
<tr><td class="k">showcase</td><td>PM_D‑1 Display (RGB pendulum showpiece)</td></tr>
|
||
<tr><td class="k">initial</td><td>PM_C‑1 Concept (idealized render)</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 set‑list 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 auto‑grows to the widget).</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<p>Under the hood the loader builds <code><page>?embed=1#p=<patch></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 & Info pages embed it.</p>
|
||
</main>
|
||
|
||
<script>
|
||
const APP_VERSION = "v0.0.1-dev";
|
||
const $ = (id)=>document.getElementById(id);
|
||
|
||
/* Form-factor picker: updates every snippet + the live demo for the chosen version. */
|
||
const ORIGIN = "https://metronome.varasys.io";
|
||
const DEMO_PATCH = "v1;t120;kick:4;snare:4=.X.X;hatClosed:4/2";
|
||
const FF = [
|
||
{ k:"editor", name:"PM_E‑1 Editor", file:"editor.html", h:560 },
|
||
{ k:"pme2", name:"PM_E‑2 Editor", file:"pm_e-2.html", h:640 },
|
||
{ k:"kit", name:"PM_K‑1 Kit", file:"kit.html", h:560 },
|
||
{ k:"teacher", name:"PM_T‑1 Teacher", file:"teacher.html", h:440 },
|
||
{ k:"stage", name:"PM_S‑1 Stage", file:"stage.html", h:430 },
|
||
{ k:"micro", name:"PM_P‑1 Practice", file:"micro.html", h:240 },
|
||
{ k:"showcase", name:"PM_D‑1 Display", file:"showcase.html",h:540 },
|
||
{ k:"grid", name:"PM_G‑1 Grid", file:"grid.html", h:470 },
|
||
{ k:"initial", name:"PM_C‑1 Concept", file:"player.html", h:440 },
|
||
];
|
||
function updateFF(k){
|
||
const v = FF.find(x => x.k === k) || FF[0];
|
||
$("snipDrop").textContent =
|
||
'<div data-varasys-metronome="' + v.k + '"\n data-patch="' + DEMO_PATCH + '"></div>\n' +
|
||
'<script src="' + ORIGIN + '/embed.js"><\/script>';
|
||
$("snipIframe").textContent =
|
||
'<iframe src="' + ORIGIN + '/' + v.file + '?embed=1#p=' + DEMO_PATCH + '"\n' +
|
||
' width="360" height="' + v.h + '" style="border:0"><\/iframe>';
|
||
const f = $("demoFrame"); f.style.height = v.h + "px"; f.src = "/" + v.file + "?embed=1#p=" + encodeURIComponent(DEMO_PATCH);
|
||
document.querySelectorAll(".ff-name").forEach(el => el.textContent = v.name);
|
||
}
|
||
$("ffSel").addEventListener("change", (e) => updateFF(e.target.value));
|
||
addEventListener("message", (e) => {
|
||
if (e.data && e.data.type === "varasys-h" && typeof e.data.h === "number" && e.source === $("demoFrame").contentWindow)
|
||
$("demoFrame").style.height = e.data.h + "px";
|
||
});
|
||
updateFF($("ffSel").value || "micro");
|
||
/*@BUILD:include:src/chrome.js@*/
|
||
</script>
|
||
/*@BUILD:include:src/footer.html@*/
|
||
</body>
|
||
</html>
|