UI consolidation round: pace checkbox, unified Limit notes section, fixes

- Pace dropdown REMOVED from Phrase setup — replaced by a 'Sync to
  exercise' checkbox in the Metronome widget (analog to 'Auto' in
  single-note modes). pace is derived from the checkbox (checked = tempo,
  unchecked = step).
- Preview clicks now registered in PREVIEW_GROUP: stopping/abandoning a
  preview cancels its scheduled clicks — no more parallel ghost
  metronomes after preview->stop->next exercise.
- Chord pool replaces the chord dropdown entirely (single chord AND
  sequences): togglable degree list, empty = all 7.
- Giuliani figures corrected to NOTE sequences (p always takes the bass/
  root): p-i-m, p-i-m-a, p-a-m-i, a-i-m, p-i-m-a-m-i, p-m-a-m-i-m,
  p-e-a-m-a-m (replaced the fingering-driven patterns that made no
  musical sense).
- 'Limit notes' is now one bordered section like Tuner/Metronome, with a
  header, Clear-all link, and Strings behind a toggle (row hidden until
  restricted).
This commit is contained in:
OpenClaw
2026-09-14 21:08:15 +00:00
parent 6253194632
commit 9acf32a32f
6 changed files with 120 additions and 116 deletions

View File

@@ -27,7 +27,10 @@ interface MetronomeWidgetProps {
showAutoAdvance?: boolean;
/** Compact layout (phrase transport row). */
compact?: boolean;
/** 'option': show the sync/free toggle (Phrase Mode). null: plain on/off. */
/** 'option': show the sync checkbox (Phrase Mode) that replaces the pace
* dropdown: checked = clicks/beats only with the exercise (preview &
* run, count-in included); unchecked = free-running metronome.
* null: plain on/off (single-note modes). */
syncMode?: 'option' | null;
/** External gate (sync mode): true while the exercise runs. */
gate?: boolean | null;
@@ -85,18 +88,14 @@ export const MetronomeWidget: React.FC<MetronomeWidgetProps> = ({
<span>Metronome</span>
</label>
{syncMode === 'option' ? (
<button
className={`switch-button ${rhythm.syncToExercise ? 'active' : ''}`}
onClick={() => onUpdate({
syncToExercise: !rhythm.syncToExercise,
active: true, // armed in either mode; the mode decides behavior
})}
title={rhythm.syncToExercise
? 'Sync: starts with the exercise (count-in). Click while idle: off.'
: 'Free: runs independently — start/stop it yourself.'}
>
<div className="switch-thumb" />
</button>
<label style={{ fontSize: '10px', display: 'flex', alignItems: 'center', gap: '4px' }}>
<input
type="checkbox"
checked={!!rhythm.syncToExercise}
onChange={(e) => onUpdate({ syncToExercise: e.target.checked })}
/>
Sync to exercise
</label>
) : (
<button
className={`switch-button ${rhythm.active ? 'active' : ''}`}