Mobile width fix, 6/8 crash fix, more arpeggio+Giuliani variants

- PhraseSheetMusic: min width per bar scales with notation density
  (16ths 320px, 8ths 260px, quarters 210px) — narrow screens wrap into
  more rows instead of clipping notes off-screen.
- 6/8 crash fixed at the root: melody templatesFor had no 6/8 templates
  (empty pool -> pick on empty array). Added 6/8 template sets for levels
  1/2 plus an empty-pool fallback guard so generators can never pick from
  an empty array again (any meter/level combination stays playable).
- More arpeggio broken-chord figures: 1-3-5-3, 1-2-1-3-2-1, 1-5-4-5-3-5,
  1-3-2-5-3-2.
- More Giuliani patterns: p-i-a-i (wide), p-m-a-m-i, p-i-m-a-m-i (waltz
  feel), p-i-m-a-i-a.
- Tests 179/179.
This commit is contained in:
OpenClaw
2026-09-15 03:04:29 +00:00
parent ffb2776c8c
commit a7a1d34c11
5 changed files with 74 additions and 12 deletions

View File

@@ -105,7 +105,12 @@ export const PhraseSheetMusic: React.FC<PhraseSheetMusicProps> = ({
const events = scoreEvents(score);
const totalBars = score.measures.length;
const barsPerRow = Math.max(1, Math.min(4, Math.floor((width - 40) / 210)));
// Min width per bar grows with notation density (eighths/16ths need more
// horizontal room than quarters) so narrow screens wrap into more rows
// instead of clipping notes.
const densestTick = Math.min(...events.filter(e => e.pitch).map(e => e.durationTicks), 1920);
const minWidthPerBar = densestTick <= 120 ? 320 : densestTick <= 240 ? 260 : 210;
const barsPerRow = Math.max(1, Math.min(4, Math.floor((width - 40) / minWidthPerBar)));
const rowCount = Math.max(1, Math.ceil(totalBars / barsPerRow));
const isGrand = clef === 'grand';
const rowHeight = isGrand ? 250 : 140;