diff --git a/mobile.html b/mobile.html index ba563df..5253df5 100644 --- a/mobile.html +++ b/mobile.html @@ -34,14 +34,14 @@ --txt:#e7edf5; --muted:#8b96a5; --link:#6cb6ff; --panel-bg:#161b22; --panel-bd:#2a313c; --field-bg:#0e1116; --field-bd:#222a36; --cyan:#0AB3F7; --amber:#ffd166; - --led-off:#1b2330; --ring:#2a3340; --glow:rgba(10,179,247,.55); --aglow:rgba(255,209,102,.55); --poly:#bb8cff; + --led-off:#1b2330; --ring:#2a3340; --glow:rgba(10,179,247,.55); --aglow:rgba(255,209,102,.55); --poly:#bb8cff; --staff:rgba(199,208,219,.17); --btn1:#2b323d; --btn2:#1b212a; --btn-bd:#39424f; --chip-bg:#1b2230; --chip-bd:#2c3545; } :root[data-theme="light"]{ --bg1:#eef3f9; --bg2:#cfd9e6; --txt:#10202f; --muted:#5c6776; --link:#1769c4; --panel-bg:#ffffff; --panel-bd:#d2dae4; --field-bg:#f1f4f8; --field-bd:#cdd6e0; - --led-off:#c4cedb; --ring:#c9d4e1; --glow:rgba(10,179,247,.40); --aglow:rgba(230,160,30,.45); --poly:#7a3df0; + --led-off:#c4cedb; --ring:#c9d4e1; --glow:rgba(10,179,247,.40); --aglow:rgba(230,160,30,.45); --poly:#7a3df0; --staff:rgba(28,40,63,.15); --btn1:#ffffff; --btn2:#e7edf4; --btn-bd:#c8d2de; --chip-bg:#eef2f7; --chip-bd:#d3dbe5; } html,body{ height:100%; } @@ -82,8 +82,13 @@ transition:transform .12s ease-out, box-shadow .12s ease-out, border-color .12s ease-out; touch-action:none; cursor:pointer; } #pulse.hit{ transform:scale(1.045); border-color:var(--cyan); box-shadow:0 0 60px var(--glow); } #pulse.hit.acc{ border-color:var(--amber); box-shadow:0 0 72px var(--aglow); } - #bpm{ font-size:clamp(46px,15vmin,140px); font-weight:800; line-height:.85; font-variant-numeric:tabular-nums; letter-spacing:-.01em; } - #bpmlab{ font-size:clamp(10px,2vmin,16px); letter-spacing:.3em; color:var(--muted); margin-top:.6em; } + /* tempo as an engraved marking: ♩ = N */ + #bpm{ display:inline-flex; align-items:center; justify-content:center; gap:.12em; line-height:.82; } + #bpmNum{ font-size:clamp(44px,14vmin,128px); font-weight:800; font-variant-numeric:tabular-nums; letter-spacing:-.01em; } + .metmark{ display:inline-flex; align-items:center; gap:.12em; color:var(--muted); font-size:clamp(20px,5.6vmin,50px); font-weight:600; } + .metmark .rhythm{ height:1.2em; width:auto; } + .metmark::after{ content:"="; } + #bpmlab{ font-size:clamp(9px,1.8vmin,14px); letter-spacing:.18em; text-transform:uppercase; color:var(--muted); margin-top:.7em; opacity:.8; } #bpmIn{ display:none; width:64%; text-align:center; font:inherit; font-size:clamp(42px,13vmin,120px); font-weight:800; background:transparent; color:var(--txt); border:none; border-bottom:2px solid var(--cyan); outline:none; font-variant-numeric:tabular-nums; -moz-appearance:textfield; } #bpmIn::-webkit-outer-spin-button, #bpmIn::-webkit-inner-spin-button{ -webkit-appearance:none; margin:0; } @@ -91,7 +96,10 @@ /* ---- track panel (repeat/end/ramp/gap/string) + editable lanes ---- */ #detail{ flex:0 1 auto; width:100%; max-height:32vh; overflow-y:auto; display:flex; flex-direction:column; gap:8px; padding:2px 0; } - #lanes{ display:flex; flex-direction:column; gap:6px; } + #lanes{ display:flex; flex-direction:column; gap:6px; position:relative; } + #lanes::before{ content:""; position:absolute; left:-2px; right:-2px; top:7%; bottom:7%; pointer-events:none; z-index:0; + background:repeating-linear-gradient(to bottom, var(--staff) 0 1.5px, transparent 1.5px 25%); } + #lanes > *{ position:relative; z-index:1; } #trackpanel{ width:100%; background:var(--chip-bg); border:1px solid var(--chip-bd); border-radius:10px; padding:8px 11px; display:flex; flex-direction:column; gap:8px; font-size:12px; color:var(--muted); } #trackpanel .tp-row{ display:flex; align-items:center; gap:8px 18px; flex-wrap:nowrap; } #trackpanel label{ display:flex; align-items:center; gap:6px; white-space:nowrap; } @@ -257,9 +265,9 @@
-
120
+
120
-
BPM
+
per minute
@@ -743,7 +751,7 @@ $("trkSel").onchange=(e)=>{ gotoItem(+e.target.value, state.running); }; /* ========================= RENDER ============================================ */ let lastCur=null; function renderInfo(){ - if(!editingBpm) $("bpm").textContent=state.bpm; + if(!editingBpm) $("bpmNum").textContent=state.bpm; const ts=$("trkSel"); if(ts && ts.value!==String(idx)) ts.value=String(idx); const nm=currentName(); if(nm!==lastCur){ lastCur=nm; lsSet(LS_CURTRACK,nm); } const sig=laneSignature(); if(sig!==laneSig){ laneSig=sig; buildLanes(); } else renderPadLevels(); @@ -787,7 +795,7 @@ function draw(){ /* ========================= BPM: tap=tap-tempo · hold=type · drag=scrub ======== */ let editingBpm=false; function openBpmEdit(){ editingBpm=true; const i=$("bpmIn"); $("bpm").style.display="none"; i.style.display="block"; i.value=state.bpm; i.focus(); i.select(); } -function closeBpmEdit(commit){ const i=$("bpmIn"); if(commit){ const v=parseInt(i.value,10); if(v){ ramp.on=false; setBpm(v); } } editingBpm=false; i.style.display="none"; $("bpm").style.display="block"; renderAll(); } +function closeBpmEdit(commit){ const i=$("bpmIn"); if(commit){ const v=parseInt(i.value,10); if(v){ ramp.on=false; setBpm(v); } } editingBpm=false; i.style.display="none"; $("bpm").style.display=""; renderAll(); } $("bpmIn").addEventListener("keydown",(e)=>{ if(e.key==="Enter"){ closeBpmEdit(true); } else if(e.key==="Escape"){ closeBpmEdit(false); } }); $("bpmIn").addEventListener("blur",()=>{ if(editingBpm) closeBpmEdit(true); }); (function(){ const p=$("pulse"); let dragging=false, moved=false, lpFired=false, startY=0, startBpm=120, lpTimer=null; @@ -906,6 +914,7 @@ if(location.hash && /(p|sl)=/.test(location.hash)) loadFromHash(location.hash); else restoreState(); if(!setlist){ slKey="b0"; setlist=BUILTIN[0]; idx=0; loadSetup(setlist.items[0]); } buildSetlistOptions(); buildTrackOptions(); +$("bpmMark").innerHTML=rhythmSVG(1); // ♩ in the "♩ = N" tempo marking $("vol").value=Math.round(state.volume*100); if(masterGain) masterGain.gain.value=state.volume; renderAll(); renderSessionBar(); requestAnimationFrame(draw);