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
- | P | Play / stop |
+ | Space | Play / stop (works everywhere except while typing in a text field) |
| T | Tap tempo |
| ↑ ↓ | Tempo ±1 BPM |
| ⇧↑ ⇧↓ | Tempo ±10 BPM |
@@ -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 */