web: hide PM_E-1 from landing (focus PM_E-2); clarify pm_e-2 device controls
Landing (index.html):
- Remove the PM_E-1 (editor.html) viewport; default the live viewport to PM_E-2.
- Repoint the vp-bar + the "design in the editor" link to pm_e-2.html. editor.html
still exists, just not featured on the landing.
PM_E-2 editor (pm_e-2.html) - the device-connection badge + Device-audio toggle:
- Group both in the header as matching .devctrl pills, side by side.
- Clear tooltips spelling out exactly what each does: the badge only REPORTS the
USB-MIDI link (green + name when a PM device is plugged in); Device audio is an
on/off switch that routes a connected device through the computer speakers and
does not require a device to toggle.
- Device-audio button now shows on/off state via colour (green when on), matching
the badge, instead of the .primary class (which clashed with the pill style).
- Fix _isDevicePort: it only matched pico/circuitpython/pimoroni/varasys, so the
native Rust devices ("PM_G-1 Grid" etc.) were never recognized -> the badge
stayed "no device" even when connected. Now matches pm_g/pm_k/pm_x/grid/polymeter.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5f7c85d910
commit
15392174aa
2 changed files with 31 additions and 15 deletions
12
index.html
12
index.html
|
|
@ -95,7 +95,7 @@
|
||||||
<div class="phil-grid">
|
<div class="phil-grid">
|
||||||
<div class="phil">
|
<div class="phil">
|
||||||
<h3>🛠️ Program on the web, play on any device</h3>
|
<h3>🛠️ Program on the web, play on any device</h3>
|
||||||
<p>The website is the workbench: design in the <a href="/editor.html">editor</a>, and the same
|
<p>The website is the workbench: design in the <a href="/pm_e-2.html">editor</a>, and the same
|
||||||
<b>program string</b> loads into whichever form factor fits the moment. One engine, one language.</p>
|
<b>program string</b> loads into whichever form factor fits the moment. One engine, one language.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="phil">
|
<div class="phil">
|
||||||
|
|
@ -110,7 +110,7 @@
|
||||||
<div class="panes" id="panes"></div>
|
<div class="panes" id="panes"></div>
|
||||||
|
|
||||||
<div class="viewport">
|
<div class="viewport">
|
||||||
<div class="vp-bar"><span id="vpName"><b>PM_E‑1 Editor</b></span><span><a id="vpInfo" href="/info-editor.html" target="_blank" rel="noopener">Specs & info ⓘ</a> · <a id="vpOpen" href="/editor.html" target="_blank" rel="noopener">Open full page ↗</a></span></div>
|
<div class="vp-bar"><span id="vpName"><b>PM_E‑2 Editor</b></span><span><a id="vpInfo" href="/info-pm_e-2.html" target="_blank" rel="noopener">Specs & info ⓘ</a> · <a id="vpOpen" href="/pm_e-2.html" target="_blank" rel="noopener">Open full page ↗</a></span></div>
|
||||||
<iframe id="vp" title="PolyMeter — live viewport" allow="autoplay"></iframe>
|
<iframe id="vp" title="PolyMeter — live viewport" allow="autoplay"></iframe>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -159,8 +159,8 @@ const SAMPLES = {}; let state = { bpm:120, volume:0.85 }, meters = [], muteWindo
|
||||||
/*@BUILD:include:src/setlists.js@*/
|
/*@BUILD:include:src/setlists.js@*/
|
||||||
|
|
||||||
const VERSIONS = [
|
const VERSIONS = [
|
||||||
{ key:"editor", file:"/editor.html", name:"PM_E‑1 Editor", chip:"app", h:620, sum:"Design grooves: stack meter lanes, per‑step accents/ghosts/mutes, swing & polyrhythm, set lists, per‑lane dB gain." },
|
// PM_E-1 (editor.html) is hidden from the landing — PM_E-2 is the focus. The page still exists.
|
||||||
{ key:"pme2", file:"/pm_e-2.html", name:"PM_E‑2 Editor", chip:"app", h:640, sum:"Second‑generation editor built around engraved drum notation — a 5‑line percussion staff (Bravura/SMuFL) with Staff / TUBS / Konnakol views, edit‑on‑staff, plus flams/drags/rolls, odd meters & clave." },
|
{ key:"pme2", file:"/pm_e-2.html", name:"PM_E‑2 Editor", chip:"app", h:640, sum:"The PolyMeter editor, built around engraved drum notation — a 5‑line percussion staff (Bravura/SMuFL) with Staff / TUBS / Konnakol views, edit‑on‑staff, plus flams/drags/rolls, odd meters & clave." },
|
||||||
{ key:"kit", file:"/kit.html", name:"PM_K‑1 Kit", chip:"hw", h:560, sum:"Build it today — a Raspberry Pi Pico on the 52Pi touchscreen kit; tap the 3.5″ screen, joystick tempo, RGB beat light, buzzer. MicroPython firmware included." },
|
{ key:"kit", file:"/kit.html", name:"PM_K‑1 Kit", chip:"hw", h:560, sum:"Build it today — a Raspberry Pi Pico on the 52Pi touchscreen kit; tap the 3.5″ screen, joystick tempo, RGB beat light, buzzer. MicroPython firmware included." },
|
||||||
{ key:"explorer", file:"/explorer.html", name:"PM_X‑1 Explorer", chip:"hw", h:500, sum:"Off‑the‑shelf — the Pimoroni Explorer (RP2350, 2.8″ LCD, 6 buttons, piezo) as a button‑driven sibling to the Kit. Edit on the web with Live sync; the device mirrors play/stop/tempo/track changes both ways." },
|
{ key:"explorer", file:"/explorer.html", name:"PM_X‑1 Explorer", chip:"hw", h:500, sum:"Off‑the‑shelf — the Pimoroni Explorer (RP2350, 2.8″ LCD, 6 buttons, piezo) as a button‑driven sibling to the Kit. Edit on the web with Live sync; the device mirrors play/stop/tempo/track changes both ways." },
|
||||||
{ key:"grid", file:"/grid.html", name:"PM_G‑1 Grid", chip:"hw", h:470, sum:"Off‑the‑shelf — a Pimoroni Pico Scroll Pack (17×7 white LED matrix + 4 buttons) on a Raspberry Pi Pico. The matrix IS the editor's lane × step pad grid in miniature; edit on the web with Live sync." },
|
{ key:"grid", file:"/grid.html", name:"PM_G‑1 Grid", chip:"hw", h:470, sum:"Off‑the‑shelf — a Pimoroni Pico Scroll Pack (17×7 white LED matrix + 4 buttons) on a Raspberry Pi Pico. The matrix IS the editor's lane × step pad grid in miniature; edit on the web with Live sync." },
|
||||||
|
|
@ -175,7 +175,7 @@ const VERSIONS = [
|
||||||
const infoOf = (f) => f.startsWith("/info-") ? f : f.replace("/", "/info-");
|
const infoOf = (f) => f.startsWith("/info-") ? f : f.replace("/", "/info-");
|
||||||
const DEFAULT_PROG = (typeof SEED_SETLISTS !== "undefined" && SEED_SETLISTS[0] && SEED_SETLISTS[0].items[0] && SEED_SETLISTS[0].items[0][1]) || "v1;t120;kick:4;snare:4=.X.X;hat:4/2";
|
const DEFAULT_PROG = (typeof SEED_SETLISTS !== "undefined" && SEED_SETLISTS[0] && SEED_SETLISTS[0].items[0] && SEED_SETLISTS[0].items[0][1]) || "v1;t120;kick:4;snare:4=.X.X;hat:4/2";
|
||||||
|
|
||||||
let cur = "editor", userEditing = false;
|
let cur = "pme2", userEditing = false;
|
||||||
const vp = $("vp"), box = $("prog");
|
const vp = $("vp"), box = $("prog");
|
||||||
const verOf = (k) => VERSIONS.find((v) => v.key === k);
|
const verOf = (k) => VERSIONS.find((v) => v.key === k);
|
||||||
|
|
||||||
|
|
@ -246,7 +246,7 @@ addEventListener("message", (e) => {
|
||||||
|
|
||||||
renderPanes();
|
renderPanes();
|
||||||
// default = each device's built-in set lists (no forced program); the box fills from what the device reports
|
// default = each device's built-in set lists (no forced program); the box fills from what the device reports
|
||||||
loadVersion("editor");
|
loadVersion("pme2");
|
||||||
/*@BUILD:include:src/chrome.js@*/
|
/*@BUILD:include:src/chrome.js@*/
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
34
pm_e-2.html
34
pm_e-2.html
|
|
@ -274,6 +274,11 @@
|
||||||
.sect > summary::before { content:"\25be\00a0"; } /* ▾ open */
|
.sect > summary::before { content:"\25be\00a0"; } /* ▾ open */
|
||||||
.sect:not([open]) > summary::before { content:"\25b8\00a0"; } /* ▸ closed */
|
.sect:not([open]) > summary::before { content:"\25b8\00a0"; } /* ▸ closed */
|
||||||
.sect > summary .shint { text-transform:none; letter-spacing:normal; font-size:11px; opacity:.7; }
|
.sect > summary .shint { text-transform:none; letter-spacing:normal; font-size:11px; opacity:.7; }
|
||||||
|
/* the two device controls in the header — matching pills (state shown via colour/border in JS) */
|
||||||
|
.devctrl{ font:inherit; font-size:12px; line-height:1.5; padding:3px 10px; border-radius:7px;
|
||||||
|
border:1px solid var(--edge); background:transparent; color:var(--muted);
|
||||||
|
white-space:nowrap; cursor:pointer; }
|
||||||
|
.devctrl:hover{ border-color:var(--muted); }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
@ -288,8 +293,11 @@
|
||||||
<img class="brand-logo brand-light" src="data:image/png;base64,@BUILD:logo-light@" alt="VARASYS — Simplifying Complexity" />
|
<img class="brand-logo brand-light" src="data:image/png;base64,@BUILD:logo-light@" alt="VARASYS — Simplifying Complexity" />
|
||||||
</a>
|
</a>
|
||||||
<h1 style="margin:0">PM_E‑2 <span style="font-weight:400; opacity:.75">PolyMeter Editor</span> <span style="font-weight:400; opacity:.5; font-size:12px">Notation</span></h1>
|
<h1 style="margin:0">PM_E‑2 <span style="font-weight:400; opacity:.75">PolyMeter Editor</span> <span style="font-weight:400; opacity:.5; font-size:12px">Notation</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:8px">
|
||||||
<span id="devBadge" title="USB-MIDI link to a PM_K-1 / PM_X-1 (Chrome/Edge/Firefox). Click to connect." style="cursor:pointer; font-size:12px; padding:3px 9px; border-radius:7px; border:1px solid var(--edge); color:var(--muted); white-space:nowrap">◎ connect device</span>
|
<span id="devBadge" class="devctrl"
|
||||||
|
title="Device link (USB-MIDI). Click to connect — Chrome, Edge or Firefox. Turns green and shows the name while a PM device (PM_K-1 / PM_X-1 / PM_G-1) is plugged in; ◎ means none is detected. This only reports the connection — it does not make sound on its own.">◎ connect device</span>
|
||||||
|
<button id="midiBtn" class="devctrl"
|
||||||
|
title="Device audio — on/off switch. When ON, the notes a connected device sends over USB-MIDI are played through THIS computer's speakers (the device drives the sound, locked to its clock). You can switch it on before plugging in — it doesn't need a device to toggle; plug one in while it's on and it sounds through the computer.">🎹 Device audio</button>
|
||||||
<button id="helpBtn" title="keyboard shortcuts (?)">?</button>
|
<button id="helpBtn" title="keyboard shortcuts (?)">?</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -311,7 +319,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="ctx" id="ctxDisplay"> </div>
|
<div class="ctx" id="ctxDisplay"> </div>
|
||||||
</div>
|
</div>
|
||||||
<div class="btnrow" style="margin-top:10px"><button class="primary" id="startBtn">▶ Start</button><button id="tapBtn">Tap</button><span id="saveItemWrap" style="display:inline-flex" title="Select a set-list item to enable Save"><button id="saveItemBtn" disabled>💾 Save</button></span><button id="midiBtn" title="Play a connected PM_K-1 device through this computer's speakers (Web MIDI · Chrome/Edge)">🎹 Device audio</button></div>
|
<div class="btnrow" style="margin-top:10px"><button class="primary" id="startBtn">▶ Start</button><button id="tapBtn">Tap</button><span id="saveItemWrap" style="display:inline-flex" title="Select a set-list item to enable Save"><button id="saveItemBtn" disabled>💾 Save</button></span></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="flex:1; min-width:200px">
|
<div style="flex:1; min-width:200px">
|
||||||
|
|
@ -1211,10 +1219,14 @@ async function loadFromDevice() {
|
||||||
let _midiAccess = null, _midiOn = false, _midiFlash = 0, _midiBeat = 0, _saveCb = null, _verCb = null;
|
let _midiAccess = null, _midiOn = false, _midiFlash = 0, _midiBeat = 0, _saveCb = null, _verCb = null;
|
||||||
function _midiInputs() { return _midiAccess ? [..._midiAccess.inputs.values()] : []; }
|
function _midiInputs() { return _midiAccess ? [..._midiAccess.inputs.values()] : []; }
|
||||||
function _midiOutputs() { return _midiAccess ? [..._midiAccess.outputs.values()] : []; }
|
function _midiOutputs() { return _midiAccess ? [..._midiAccess.outputs.values()] : []; }
|
||||||
function _isDevicePort(p) { // recognise PM_K-1 (Pico) and PM_X-1 (Pimoroni Explorer RP2350) USB-MIDI ports;
|
function _isDevicePort(p) { // recognise the PM devices' USB-MIDI ports by name;
|
||||||
const n = (p.name || "").toLowerCase(); // anything unrecognised triggers a broadcast to all outputs - bad for ACK routing.
|
const n = (p.name || "").toLowerCase(); // anything unrecognised triggers a broadcast to all outputs - bad for ACK routing.
|
||||||
return n.includes("pico") || n.includes("circuitpython") || n.includes("usb_midi") ||
|
return n.includes("pico") || n.includes("circuitpython") || n.includes("usb_midi") ||
|
||||||
n.includes("pimoroni") || n.includes("explorer") || n.includes("rp2350") || n.includes("varasys");
|
n.includes("pimoroni") || n.includes("explorer") || n.includes("rp2350") || n.includes("varasys") ||
|
||||||
|
// native Rust firmware enumerates as e.g. "PM_G-1 Grid" / "PM_K-1" / "PM_X-1" (VARASYS / PolyMeter)
|
||||||
|
n.includes("pm_g") || n.includes("pm_k") || n.includes("pm_x") ||
|
||||||
|
n.includes("pm-g") || n.includes("pm-k") || n.includes("pm-x") ||
|
||||||
|
n.includes("grid") || n.includes("polymeter");
|
||||||
}
|
}
|
||||||
function _send(bytes) { // send only to the PM_K-1 (not loopback ports like "Midi Through", which just echo)
|
function _send(bytes) { // send only to the PM_K-1 (not loopback ports like "Midi Through", which just echo)
|
||||||
const outs = _midiOutputs(), dev = outs.filter(_isDevicePort);
|
const outs = _midiOutputs(), dev = outs.filter(_isDevicePort);
|
||||||
|
|
@ -1260,10 +1272,14 @@ function _heartbeat(on) { // while Device audio is on: tell the device a host
|
||||||
}
|
}
|
||||||
function updateMidiBtn() {
|
function updateMidiBtn() {
|
||||||
const b = $("midiBtn"); if (!b) return;
|
const b = $("midiBtn"); if (!b) return;
|
||||||
if (!_midiOn) { b.textContent = "🎹 Device audio"; b.classList.remove("primary"); b.style.boxShadow = ""; return; }
|
if (!_midiOn) { // off: muted pill, matching the device badge's idle look
|
||||||
const names = _midiInputs().map((i) => i.name || "MIDI");
|
b.textContent = "🎹 Device audio";
|
||||||
b.textContent = names.length ? "🎹 " + names[0].slice(0, 16) : "🎹 no device";
|
b.style.color = "var(--muted)"; b.style.borderColor = "var(--edge)"; b.style.boxShadow = "";
|
||||||
b.classList.add("primary");
|
return;
|
||||||
|
}
|
||||||
|
const names = _midiInputs().map((i) => i.name || "MIDI"); // on: green, like the connected badge
|
||||||
|
b.textContent = "🎹 " + (names.length ? names[0].slice(0, 16) : "audio on");
|
||||||
|
b.style.color = "#2fe07a"; b.style.borderColor = "#2fe07a";
|
||||||
}
|
}
|
||||||
async function toggleDeviceAudio() {
|
async function toggleDeviceAudio() {
|
||||||
if (_midiOn) { _midiOn = false; _heartbeat(false); updateMidiBtn(); return; } // inputs stay bound (for Save ACKs)
|
if (_midiOn) { _midiOn = false; _heartbeat(false); updateMidiBtn(); return; } // inputs stay bound (for Save ACKs)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue