Knowledge Center behaviour while a jam plays, zero clicks: the station under the playhead auto-expands to the full D-30 voicing gallery and advances with the music; level-filtered licks strip below with context-first sort; pin gesture inherits onFocusChord semantics; auto-follow never emits focus or auto-plays audio. Honest space math (piano rail scrolls as normal state), 5-commit migration order for L-33, four rejected alternatives. Critic returned once (piano gallery width understated ~45%), fixed, PASS on scoped re-gate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
20 KiB
Glance Mode — the Knowledge Center while the jam plays (task D-31)
Thesis: while a loop is live, the Jam Guide section stops being "Roadmap + one thumbnail per chord, tap to see more" and becomes a playhead accordion: a station-aligned rail under the Roadmap where the current station's column is expanded to the full D-30 voicing gallery (every placeable guitar shape, or every piano style, side by side) and every other station shows its recommended thumb. The expansion follows the playhead — over one loop cycle you see every variation of every chord with zero clicks. A licks strip for the matched style sits directly below, auto-sorted to the chord you're on.
User directive (2026-07-10, verbatim): "expand the learn session so we see as much as possible in voicing variations and styles without having to click buttons, the idea is to have it open as the jam is playing."
Decision authority: per the sprint header (user away, no user gate), Muse picks the strongest layout and records rationale + rejected alternatives (§6). Implementation is task L-33; this doc is its blueprint. Depends on D-30 (VoicingBrowser becomes an all-variations gallery — chips removed, every shape/style rendered simultaneously, each with its own ▶).
0. What exists today (read from the code, not assumed)
JamGuide.jsx§jam rendersRoadmapAssembly:RoadmapTrackon top, then a voicing strip — ONE thumb per station (ChordDiagram thumb 75×72 px, or MiniPiano D-24 cropped thumb ≈142 px for a 1-octave voicing / ≈266 px for a 2-octave one), tap-to-enlarge → full diagram + aVoicingBrowsermount. Variations are therefore one click away per chord — the exact thing the directive kills.canonicalPos(playhead station index, canonical KB order) already exists and already drivesscrollIntoViewon the strip. All glance data is already in the component.VoicingBrowser(post-D-30) renders ALL matching guitar shapes side by side (cells ≈90–120 px wide) and all four piano styles side by side (cropped MiniPiano thumbs ≈140 px+), each independently playable. Placeable guitar shapes per chord: 3–5 (maj: 4 movable + 1 native open; min7/dom7/maj7: 3–4; dim/half_dim: 2). Piano styles: always 4 (root/shell/rootlessA/rootlessB), or the authored recipe (L-24) plus the computed four.LickCardhassize="thumb"andLicksSectionalready knows how to readkb[style].instruments.guitar.licksdefensively.
1. Chosen layout — the playhead accordion
┌ KNOWLEDGE CENTER — ii–V–I in C major ────────────────────────────────── ▲ ┐
│ [▶ Jam Guide ●] [Explore] [Voicings] [Licks & Techniques] │
│ [🎸 Guitar][🎹 Piano][🎵 Bass] · [Jazz●][Blues][Rock]… │
│ ┌─ ROADMAP ──────────────────────────────────────────────────────────────┐ │
│ │ Dm7 (ii) ──C→B── G7 (V) «NOW» ──F→E── Cmaj7 (I) ▁▁▁▂█▂▁▁▁ beat │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
│ ┌─ VARIATIONS · follows the playhead ────────────────────────────────────┐ │
│ │ Dm7 ┃ G7 — every shape ┃ Cmaj7 │ │
│ │ [thumb] ┃ [E Barre][A7 Barre][D Shape][Open G7] ┃ [thumb] │ │
│ │ (recmd.) ┃ ▶ ▶ ▶ ▶ ┃ (recmd.) │ │
│ └─────────────┸───────────────────────────────────────┸───────────────────┘ │
│ ┌─ JAZZ LICKS · over the V7 first ─────────────────────────── (scroll) ──┐ │
│ │ [lick thumb «fits G7»] [lick thumb] [lick thumb] … │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
- Columns = stations (canonical KB order — the kb-plan §4 grid intent, finally literal): the rail's columns sit under the Roadmap's stations, same order, same labels/rn.
- Active column = expanded: the station at
canonicalPosrenders the full D-30 gallery (<VoicingBrowser rootPc quality show={instrument}/>). Collapsed columns render today's recommended thumb (the KB play's shape / the threaded piano voicing) — so the loop context and lookahead never disappear. - The accordion advances with the playhead. Chord changes are the animation; nothing else moves. Constant footprint: expanding one column collapses the previous one, so the rail never grows taller mid-jam.
- Licks strip below: thumb LickCards for the active style, level-filtered, sorted current-chord-context first (see §2.4).
Why this wins: it satisfies "as much as possible without clicking" without pretending everything fits at once (§3 shows it can't) — across one loop cycle the player is shown 100% of the variations, always for the chord their hands are on, which is when a variation is actually learnable.
2. Auto-follow rules (zero clicks, precisely)
- Driver =
canonicalPos(already computed in JamGuide fromfindLoopPosition+match.rotation). Expanded column =canonicalPos;position === -1(loop known but playhead unknown) → station 0 expands, so the rail is never all-collapsed. The active column auto-scrolls into view (scrollIntoView({inline:'center'})— the existing strip pattern, prop-driven offposition, no rAF, nothing tied to the audio thread). Respectprefers-reduced-motion:behavior: 'auto'instead of'smooth'(new — today's strip smooth-scrolls unconditionally; fix while in there). - Instrument tab (existing) selects the gallery family — guitar shapes vs piano styles. It's a mode preference, not a per-chord click; it keeps its current default/behaviour.
- Loop change (
match.id/ style / instrument change): rail re-derives from the newstationVoicings, any pin (§4) resets — this is the existingselectedStationreset effect, renamed. No loop matched but chords are committing (currentChordset,match.matchedfalse): the rail degrades to a single "heard live" gallery —parseChord(currentChord)(the samesrc/lib/voicings.jsparserVoicingsSectionuses) → one expanded VoicingBrowser, re-aimed on every chord commit. Nothing heard at all: the existing dashed empty state; no rail, no licks strip. - Licks strip:
kb[activeStyle].instruments.guitar.licks(the defensive readLicksSection's locallicksForhelper already implements), shared level filter applied. Sort: licks whosechordContextmatches the current station's rn or quality first, with an accent ring + "fits G7 — now" microcopy on those; re-sorts as the playhead advances (a reorder of ≤4 thumb cards, cheap).chordContextis free text — the match MUST be token-boundary on rn/quality (a naive substring makes rn "I" match "♭VII", "Imaj7", "I7"); never ship "fits G7" on a ♭VII lick. The ring + "fits — now" microcopy are strip-owned chrome rendered around the card — LickCard itself showschordContextonly atsize="full"and stays untouched, as §5 promises. Licks are guitar-only in the KB: when the instrument tab is piano, the strip still shows the guitar licks (consistent with the existing Licks & Techniques section, which does the same) and says so in its heading ("guitar licks"). Style has no licks → the strip hides entirely; an empty state would steal glance space to say nothing. - No auto-sound, ever. Auto-follow never triggers
chordAudio— speaker output feeds the live mic and would poison the detection that drives the playhead (the L-20 caveat becomes a feedback loop). Every ▶ stays a gesture. The rail shows the mic-feedback microcopy once, not per gallery. onFocusChord(D-03 Fretboard link) stays gesture-driven. Auto-follow does NOT emit focus-chord — repainting the main fretboard every 2 s uninvited would fight the player's own key view. Only a pin (§4) emits it, preserving today's semantics exactly.
3. Density & space budget — honest math
Geometry sources: dock body = 70vh; RoadmapTrack station min-width 168 px + 34 px rail; ChordDiagram thumb 75×72 px (+label); MiniPiano cropped thumb 142 px (1-octave voicing) to 266 px (2-octave, e.g. most rootless voicings) × ~51 px tall; D-30 gallery cells ≈ 90–120 px (guitar) / ≈ 140–280 px (piano) wide, ≈ 140 px (guitar) / ≈ 125 px (piano) tall including label + ▶.
1280 × 900 (the target the task names)
Vertical: dock body 630 px − section nav ≈ 49 − instrument/style row ≈ 47 − content padding 32 − RoadmapTrack ≈ 230 − gap 16 = ≈ 256 px glance budget.
- Guitar rail (heading + one row of cells ≈ 190 px): fits. ~66 px spare.
- Piano rail (≈ 175 px): fits.
- Licks strip (thumb LickCard ≈ 165 px; heading + thumb row ≈ 190 px): does NOT also fit — 190 + 190 = 380 > 256. It sits just below the fold, reachable by the dock's existing vertical scroll (a flick, not a click). Fully above the fold only from ≈ 1070 px window height. Said plainly: at 1280×900 you get Roadmap + the full variations rail without scrolling; licks are one scroll-flick down.
Horizontal (~1200 px usable): collapsed guitar column ≈ 95 px; collapsed piano column ≈ 160–280 px (D-24 crop width varies with voicing span); expanded guitar gallery 3–5 cells ≈ 340–540 px. Expanded piano gallery — the honest figure: root/shell cells are 1-octave crops ≈ 160 px, but rootless A/B of any true-7th chord span past one octave (e.g. Dm7 rootlessA → notes [17, 21, 24, 28]), so D-24's crop gives them a 2-octave keyboard = 266 px thumb ≈ 284 px cell. The gallery is therefore 160 + 160 + 284 + 284 + gaps + section p-3 ≈ 940 px — not the ~640 px four 1-octave cells would suggest.
| Loop | Guitar rail width | Piano rail width | One row? |
|---|---|---|---|
| 3–4 stations | 2–3×95 + ~450 ≈ 640–740 px | 2–3×~200 + ~940 ≈ 1350–1550 px | guitar ✓ · piano ✗ (scrolls at ~1200 usable) |
| 8 stations (post-L-30) | 7×95 + ~450 ≈ 1115 px | 7×~200 + ~940 ≈ 2300 px | guitar ✓ (just) · piano ✗ |
Cut order (what goes first):
- Licks strip drops below the fold (never cut, just deferred to scroll).
- Rail scrolls horizontally with the active column auto-centred (the existing strip pattern) — cells are never shrunk below the D-30 sizes; a diagram you can't read is worth less than one you scroll to. This is the piano rail's normal state on any loop containing 7th chords — i.e. most of them (a rootless gallery is ~940 px on its own), not just long loops; L-33's commit-5 viewport check must expect the piano rail to scroll, never assert "one row fits".
- Narrow only (§ below): collapsed columns reduce to "next" only.
~640 px wide (half-snapped window)
~576 px usable. The rail flips to the kb-plan §4 narrow rule — one thing per row:
- Row 1: the expanded gallery for the current station (guitar: 5 cells wrap to 2 rows ≈ 300 px; piano: 4 cells wrap to 2 rows ≈ 260 px).
- Row 2: a single "next: Cmaj7" collapsed thumb (lookahead is the one piece of context worth its pixels at this width); other stations are dropped — the Roadmap above still shows the whole loop.
- Licks strip: single-column, below, via vertical scroll. Glance guarantee at narrow = current chord's full gallery + the next chord's thumb, no interaction.
1280 × 800 (the older Electron-default figure in D-20)
Budget shrinks to ≈ 186 px — the guitar rail (~190 px) is 1 row of cells with the heading merged into the rail's top edge (drop the standalone heading line, −18 px) and fits; everything else as at 900.
4. Interaction stays optional (but stays)
Nothing essential is behind a click; everything useful still responds to one:
- ▶ per gallery cell (D-30): audition that voicing. Keyboard-reachable,
descriptive
aria-labels, one-at-a-time playback (D-30's stop-previous contract). - Pin a station: tapping a collapsed column pins its gallery open,
overriding auto-follow (the accordion stops moving) — this replaces
today's tap-to-enlarge and inherits its semantics: the pinned station emits
{rootPc, quality}viaonFocusChord(D-03 Fretboard guide tones), and the existing reset effect (loop/style/instrument change → null) clears it. Unpin = tap again or tap the visible "follow the jam" chip that appears while pinned.aria-pressedon columns,aria-currenton the live one. - Tap a lick thumb → the card enlarges inline (
size="full"); tap again to collapse. The thumb already shows the full tab shape — enlarging is comfort, not information. - Chord label tap →
onChordClick→ ChordDetailModal, as everywhere else.
Focus order follows DOM order (columns left→right, then licks); all targets keep
focus-visible:ring-2 ring-accent, min 32 px height. Tokens only — everything
here uses existing surface/panel/border/accent + amber; no new colour.
Contrast inherits the measured D-20 §8 commitments (accent small text on
bg-surface cards; gray-400 floor for load-bearing labels; gray-500 microcopy
only).
5. Migration order for L-33 (green at every commit)
App.jsx contract: UNTOUCHED — zero changes, not even additive. Every input
glance mode needs (detectedProgression, keyInfo, chordHistory, bpm,
currentChord, onFocusChord, onChordClick) already flows into JamGuide.
Audio callbacks are therefore untouched by construction. If L-33 finds a gap,
the rule is prop-addition-only on the existing mount, never a callback/ref edit.
Files (for Maestro to re-lock at promotion):
| File | Kind of change |
|---|---|
src/components/GlanceRail.jsx |
NEW — pure presentational: props { stations, activeIndex, pinnedIndex, onPin, instrument, keyRoot }; composes collapsed thumbs (existing ChordDiagram/MiniPiano) + one <VoicingBrowser show={instrument}/> for the expanded column |
src/components/JamGuide.jsx |
Restructured (jam section only) — RoadmapAssembly's voicing strip + enlarge block replaced by GlanceRail + LicksStrip; selectedStation becomes pinnedStation (same reset effect, same onFocusChord wiring); licksFor — today a closure-local function inside LicksSection (JamGuide.jsx:489), not an export — is lifted out during the restructure and shared with the strip. Sections 2–4, the shell, nav, and collapsed bar untouched |
src/components/VoicingBrowser.jsx |
Additive-optional — a dense prop (trim section padding, suppress the per-mount mic microcopy since the rail shows it once). Skip entirely if the D-30 cells already sit within the §3 budget |
src/components/LickCard.jsx |
Untouched (thumb/full already exist) |
src/App.jsx, RoadmapTrack.jsx, MiniPiano.jsx, ChordDiagram.jsx, all src/lib/** |
Untouched |
Commit order:
- Extract
GlanceRail.jsxrendering today's strip behaviour verbatim (thumbs + tap-to-enlarge), JamGuide mounts it — a pure move, zero visual diff. - Accordion: expanded column =
activeIndex(auto-follow offcanonicalPos), pin/unpin replaces tap-to-enlarge, the VoicingBrowser mount moves from the enlarge block into the expanded column; reduced-motion guard on the auto-scroll. - No-loop fallback:
parseChord(currentChord)single gallery. - LicksStrip: thumb cards, level filter, context-first sort + live ring, hide-when-empty, tap-to-enlarge.
- Narrow reflow + cut rules (§3): horizontal-scroll behaviour, ≤640 px next-only collapse; check 1280×900, 1280×800, ~640 px.
Each commit: npm run build + node scripts/smoke.mjs green; Critic gates.
6. Rationale + rejected alternatives (no user gate this sprint)
Chosen — playhead accordion, because: (a) it is the kb-plan §4 grid (columns = chords, active-column highlight, narrow → one-per-row) applied to variations; (b) constant footprint that survives 8-station loops (L-30) instead of degrading; (c) 100% of variations shown per loop cycle with zero clicks, and always for the chord under the player's hands — the moment a grip is learnable; (d) it reuses D-30's gallery as-is and App.jsx needs nothing.
Rejected A — "everything expanded" grid (every station × every variation at once — the literal maximal reading of the directive). Killed by §3's math: piano is 4 stations × 4 styles = 16 cells at the honest §3 sizes (rootless cells ≈ 284 px) ≈ 3,500+ px wide, or 3+ wrapped rows ≈ 480 px tall — both force scrolling while playing, which is worse than zero clicks: it's continuous manual tracking. At 8-station loops it's hopeless on any viewport. Guitar-only at exactly 3 stations barely fits — a layout that only works for its demo case is not a design.
Rejected B — "Now panel" (full-width mega-view of only the current chord: giant gallery + licks + scale, no station columns). Maximum per-chord detail, but it discards lookahead — mid-jam the next chord matters more than the one already sounding — and at 120 BPM a 4-chord loop swaps the entire panel every ~2 s: visually violent, nothing is on screen long enough to read. The accordion keeps 80% of the detail and all of the context.
Rejected C — side-by-side split (Roadmap left, variations dock right). A 4-station track already needs 4×168 + 3×34 ≈ 774 px; splitting 1280 gives it ~640 — the flagship Roadmap gains a permanent horizontal scrollbar to make room for a panel that duplicates its station order 90° rotated. Collapses entirely at narrow. Rejected on viewport economics.
Rejected D — timed carousel (auto-cycle variations for the current chord every N seconds). Zero clicks, technically — but motion untied to the music is the most distracting thing a stage display can do, and it invents a second clock next to the playhead. The music already provides the rhythm of change; follow it.
7. Out of scope / flags for Maestro
- Explore/Voicings/Licks sections: unchanged; glance mode lives entirely in
the jam section. (The Voicings section already follows
currentChord— that behaviour is untouched.) - Piano collapsed-thumb width variance (142–266 px per D-24's span-crop) is the piano rail's main width pressure; if Critic finds real loops where it reads badly, a future D-task could cap collapsed piano thumbs to a 1-octave window around the bass — not in L-33's scope.
denseprop on VoicingBrowser is optional and Muse-owned; L-33 should attempt the rail with plain D-30 cells first.- No new tokens, no new dependencies, no KB/theory/audio changes anywhere in this design.