diff --git a/README.md b/README.md index 9e0ac30..9560e30 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ In the set‑list panel's **⋯** menu: | Key | Action | |-----|--------| -| `P` | play / stop | +| `Space` | play / stop (except while typing in a text field) | | `T` | tap tempo | | `↑` / `↓` | tempo ±1 (`Shift` = ±10) | | `A` | add meter lane | diff --git a/index.html b/index.html index 31a5d02..05b7d62 100644 --- a/index.html +++ b/index.html @@ -192,7 +192,7 @@

Stackable Metronome v0.0.1-dev

- P play · T tap · ↑↓ tempo (⇧×10) · A add · N next · ? help + Space play · T tap · ↑↓ tempo (⇧×10) · A add · N next · ? help
@@ -314,7 +314,7 @@

Keyboard shortcuts

- + @@ -1191,23 +1191,26 @@ $("shareQrExt").addEventListener("click", () => { window.open("https://api.qrserver.com/v1/create-qr-code/?size=320x320&data=" + encodeURIComponent($("shareUrl").value), "_blank", "noopener"); }); window.addEventListener("keydown", (e) => { - const t = e.target; - if (t && (t.tagName === "INPUT" || t.tagName === "SELECT" || t.tagName === "TEXTAREA" || t.isContentEditable)) return; + const t = e.target, tag = t ? t.tagName : "", type = (t && t.type ? String(t.type) : "").toLowerCase(); + // Text entry is sacred — never hijack typing in a text field. + if (t && (t.isContentEditable || tag === "TEXTAREA" || + (tag === "INPUT" && /^(text|number|search|email|url|tel|password)$/.test(type)))) return; const k = e.key; if (e.altKey && (k === "ArrowUp" || k === "ArrowDown")) { e.preventDefault(); moveActiveItem(k === "ArrowUp" ? -1 : 1); return; } // reorder selected item if (e.metaKey || e.ctrlKey || e.altKey) return; - if (k === "p" || k === "P") { e.preventDefault(); toggleTransport(); } - else if (k === "t" || k === "T") { tapTempo(); } - else if (k === "ArrowUp") { e.preventDefault(); setBpm(state.bpm + (e.shiftKey ? 10 : 1)); } - else if (k === "ArrowDown") { e.preventDefault(); setBpm(state.bpm - (e.shiftKey ? 10 : 1)); } - else if (k === "a" || k === "A") { addMeter("4", 1, "claves"); } - else if (k === "n" || k === "N") { nextItem(); } - else if (k === "?") { toggleShortcuts(true); } - else if (k === "Escape") { if (!$("shareOverlay").hidden) $("shareOverlay").hidden = true; else if (!$("shortcutsOverlay").hidden) toggleShortcuts(false); } - else if (k >= "1" && k <= "9") { - const m = meters[+k - 1]; - if (m) setLaneEnabled(m, !m.enabled); - } + // Transport: Space always = play/stop. preventDefault so it never scrolls the + // page, toggles a focused checkbox, or re-fires a focused button. + if (k === " " || e.code === "Space") { e.preventDefault(); toggleTransport(); return; } + // Leave arrow keys to a focused slider / menu so they still adjust it. + const arrowCtrl = tag === "SELECT" || (tag === "INPUT" && type === "range"); + if (k === "ArrowUp") { if (arrowCtrl) return; e.preventDefault(); setBpm(state.bpm + (e.shiftKey ? 10 : 1)); return; } + if (k === "ArrowDown") { if (arrowCtrl) return; e.preventDefault(); setBpm(state.bpm - (e.shiftKey ? 10 : 1)); return; } + if (k === "t" || k === "T") { tapTempo(); return; } + if (k === "a" || k === "A") { addMeter("4", 1, "claves"); return; } + if (k === "n" || k === "N") { nextItem(); return; } + if (k === "?") { toggleShortcuts(true); return; } + if (k === "Escape") { if (!$("shareOverlay").hidden) $("shareOverlay").hidden = true; else if (!$("shortcutsOverlay").hidden) toggleShortcuts(false); return; } + if (k >= "1" && k <= "9") { const m = meters[+k - 1]; if (m) setLaneEnabled(m, !m.enabled); } }); /* init */
PPlay / stop
SpacePlay / stop (works everywhere except while typing in a text field)
TTap tempo
Tempo ±1 BPM
⇧↑ ⇧↓Tempo ±10 BPM