pm-mobile: fix landscape right-edge overflow + per-lane mute toggle
- Landscape grid used 40%/60% columns plus a 4vw gap, summing past 100% and overhanging the right edge. Switch to minmax(0,2fr)/minmax(0,3fr) so the gap is subtracted before sizing, add min-width:0 to grid items, and widen the inter-section (row) gaps. Bump the portrait section gap too. - Add an inline speaker mute button at the left of every lane for quick selective muting; muted lanes dim the label/pads but keep the toggle crisp. The lane sheet's Mute checkbox stays in sync (both flip m.enabled). - Help: note the lane mute toggle in the "Edit the beat" step. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9b5d24edc0
commit
4e0a68f35b
1 changed files with 25 additions and 6 deletions
31
mobile.html
31
mobile.html
|
|
@ -81,7 +81,7 @@
|
|||
.icon:active{ background:rgba(127,139,154,.30); }
|
||||
|
||||
/* ---- middle: pulse + track panel + lanes + transport, centered as one block ---- */
|
||||
#mid{ flex:1 1 auto; min-height:0; display:flex; flex-direction:column; align-items:center; justify-content:flex-start; gap:clamp(16px,3.6vmin,30px); padding-top:clamp(8px,1.8vmin,16px); }
|
||||
#mid{ flex:1 1 auto; min-height:0; display:flex; flex-direction:column; align-items:center; justify-content:flex-start; gap:clamp(18px,4.2vmin,34px); padding-top:clamp(8px,1.8vmin,16px); }
|
||||
#stage{ flex:0 0 auto; display:flex; flex-direction:column; align-items:center; width:100%; }
|
||||
/* tempo plate: [TAP] [big BPM] [thumbwheel] — flashes on the beat */
|
||||
#pulse{ position:relative; width:100%; padding:clamp(8px,1.8vmin,14px); border-radius:16px;
|
||||
|
|
@ -137,7 +137,16 @@
|
|||
#trackpanel .tp-btn{ flex:0 0 auto; background:linear-gradient(180deg,var(--btn1),var(--btn2)); color:var(--txt); border:1px solid var(--btn-bd); border-radius:7px; padding:0 12px; font-size:12px; cursor:pointer; }
|
||||
#trackpanel .tp-msg{ color:#5fd08a; margin-left:auto; }
|
||||
.lane{ display:flex; align-items:center; gap:8px; }
|
||||
.lane.off{ opacity:.5; }
|
||||
/* dim only the label + pads of a muted lane, so the mute toggle stays crisp */
|
||||
.lane.off .lmeta, .lane.off .pads{ opacity:.45; }
|
||||
.lmute{ flex:0 0 auto; width:clamp(28px,5.4vmin,36px); height:clamp(28px,5.4vmin,36px); border-radius:8px; padding:0;
|
||||
display:flex; align-items:center; justify-content:center; cursor:pointer;
|
||||
background:var(--chip-bg); border:1px solid var(--chip-bd); color:var(--cyan); }
|
||||
.lmute:active{ background:rgba(127,139,154,.22); }
|
||||
.lmute.muted{ color:var(--muted); background:transparent; }
|
||||
.lmute svg{ width:62%; height:62%; }
|
||||
.lane.poly .lmute{ color:var(--poly); }
|
||||
.lane.poly .lmute.muted{ color:var(--muted); }
|
||||
.lmeta{ flex:0 0 auto; width:36%; max-width:168px; min-width:94px; display:flex; align-items:center; gap:5px; text-align:left;
|
||||
background:var(--chip-bg); border:1px solid var(--chip-bd); color:var(--txt); border-radius:8px; padding:5px 8px;
|
||||
font-size:clamp(10px,1.8vmin,13px); font-family:"Courier New",monospace; cursor:pointer; }
|
||||
|
|
@ -201,9 +210,12 @@
|
|||
on the left, selector + settings + lanes on the right */
|
||||
@media (orientation:landscape){
|
||||
#app{ --maxw:1060px; }
|
||||
#mid{ display:grid; align-items:stretch; gap:8px 4vw; padding-top:0;
|
||||
grid-template-columns:40% 60%; grid-template-rows:auto auto 1fr auto;
|
||||
/* fr columns (not 40%/60%) so the column gap is subtracted before sizing —
|
||||
percentages + gap summed past 100% and overhung the right edge */
|
||||
#mid{ display:grid; align-items:stretch; gap:clamp(12px,2.6vh,22px) clamp(16px,3vw,38px); padding-top:0;
|
||||
grid-template-columns:minmax(0,2fr) minmax(0,3fr); grid-template-rows:auto auto 1fr auto;
|
||||
grid-template-areas:"stage sels" "stage panel" "stage detail" "transport detail"; }
|
||||
#stage, .sels, #trackpanel, #detail, #transport{ min-width:0; }
|
||||
#stage{ grid-area:stage; align-self:center; justify-content:center; }
|
||||
.sels{ grid-area:sels; align-self:start; }
|
||||
#trackpanel{ grid-area:panel; align-self:start; }
|
||||
|
|
@ -546,10 +558,17 @@ function laneMetaHTML(m){ const eff=laneNoteValue(m);
|
|||
const poly=m.poly?"<span class='polybadge' title='polyrhythm — "+m.beatsPerBar+" over "+ref+"'>↻"+m.beatsPerBar+":"+ref+"</span>":"";
|
||||
return "<span class='ln-name'>"+esc(m.sound)+"</span><span class='rh-host' title='Note value'>"+rhythmSVG(eff)+"</span><span class='lg'>"+esc(m.groupsStr)+"</span>"+poly; }
|
||||
function setLaneMeta(m){ if(!m._meta) return; m._meta.innerHTML=laneMetaHTML(m); }
|
||||
// speaker glyphs for the inline per-lane mute toggle
|
||||
const SPK_ON='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M4 9v6h4l5 4V5L8 9H4z"/><path d="M16.5 8.5a5 5 0 0 1 0 7"/></svg>';
|
||||
const SPK_OFF='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M4 9v6h4l5 4V5L8 9H4z"/><path d="M17 9.5l4 5M21 9.5l-4 5"/></svg>';
|
||||
function toggleLaneMute(i){ const m=meters[i]; if(!m) return; m.enabled=!m.enabled; laneSig=null; renderAll(); saveState(); }
|
||||
function buildLanes(){
|
||||
const box=$("lanes"); box.innerHTML="";
|
||||
meters.forEach((m,i)=>{
|
||||
const lane=document.createElement("div"); lane.className="lane"+(m.enabled?"":" off")+(m.poly?" poly":"");
|
||||
const mute=document.createElement("button"); mute.className="lmute"+(m.enabled?"":" muted");
|
||||
mute.title=m.enabled?"Mute lane":"Unmute lane"; mute.setAttribute("aria-label",mute.title);
|
||||
mute.innerHTML=m.enabled?SPK_ON:SPK_OFF; mute.onclick=(e)=>{ e.stopPropagation(); toggleLaneMute(i); };
|
||||
const meta=document.createElement("button"); meta.className="lmeta"; m._meta=meta; m._idx=i; setLaneMeta(m);
|
||||
meta.onclick=()=>openLaneSheet(i);
|
||||
const pads=document.createElement("div"); pads.className="pads";
|
||||
|
|
@ -557,7 +576,7 @@ function buildLanes(){
|
|||
for(let b=0;b<m.beatsPerBar;b++){ const cell=document.createElement("div"); cell.className="beatcell";
|
||||
for(let s=0;s<spb;s++){ const k=b*spb+s; const p=document.createElement("button"); p.className="pad"; p.onclick=()=>cyclePad(m,k,p); cell.appendChild(p); m._padEls[k]=p; }
|
||||
pads.appendChild(cell); }
|
||||
lane.appendChild(meta); lane.appendChild(pads); box.appendChild(lane);
|
||||
lane.appendChild(mute); lane.appendChild(meta); lane.appendChild(pads); box.appendChild(lane);
|
||||
});
|
||||
const add=document.createElement("button"); add.className="addlane"; add.textContent="+ Add lane"; add.onclick=addLane; box.appendChild(add);
|
||||
renderPadLevels();
|
||||
|
|
@ -846,7 +865,7 @@ const TOUR=[
|
|||
{sel:".sels", title:"Pick what to play", text:"Choose a set list and the track within it. Tracks are your practice items — name them for whatever you're working on, even if two share the same beat."},
|
||||
{sel:"#saveBtn", title:"Save & library", text:"Save the current track — “Save as new”, or “Update” one of yours. The same sheet is your library: make set lists and rename / reorder / delete tracks. It all lives with the full editor too."},
|
||||
{sel:"#trackpanel", title:"Track settings", text:"Optional per-track extras: Repeat for N bars then stop / next / prev track, a tempo ramp, and practice gaps."},
|
||||
{sel:"#lanes", title:"Edit the beat", text:"Each lane is a row of pads that blink on the beat — tap a pad to cycle rest → beat → accent → ghost. Tap a lane's label to set its note value (eighths, triplets, sixteenths…), sound, grouping, mute or polymeter. “+ Add lane” for more."},
|
||||
{sel:"#lanes", title:"Edit the beat", text:"Each lane is a row of pads that blink on the beat — tap a pad to cycle rest → beat → accent → ghost. The speaker button at the left of each lane mutes/unmutes it. Tap a lane's label to set its note value (eighths, triplets, sixteenths…), sound, grouping or polymeter. “+ Add lane” for more."},
|
||||
{sel:"#bDn10,#bDown,#bUp,#bUp10", title:"Nudge the tempo", text:"Step the BPM up or down while it keeps playing: −10 / −1 / +1 / +10. Great for settling on a comfortable speed or pushing it faster as you improve."},
|
||||
{sel:"#bPrev,#bNext", title:"Previous / next track", text:"⏮ and ⏭ move to the previous or next track in the current set list. If the metronome is running it carries straight on into the new track."},
|
||||
{sel:"#bPrac", title:"Practice = a timed session", text:"Play just runs the metronome. Practice times your playing and logs it (not audio): it starts a session clock and Play becomes Stop — start/pause each track, then Stop to save the session."},
|
||||
|
|
|
|||
Loading…
Reference in a new issue