feat(app): Jam Guide band promoted below the main module — one loop display, one instrument selector (task L-40)

The always-open zero-chrome band (GlanceRail + licks strip +
heard-live fallback) replaces CurrentJamPanel right below the
instrument view; the four-section dock becomes KnowledgeDock at the
bottom; RoadmapTrack unmounted (banner is the single loop display);
JamGuide's internal instrument/style tabs die — App's global
GUITAR/PIANO/BASS selector drives everything incl. the dock's
VoicingsSection; honest bass state (computed root-fifth-approach rows
+ notice, no galleries/licks); GlanceRail's scrollIntoView effect
deleted same-commit (would yank the document in page flow).
Ride-along: JamGuide resolveDegree exported (logic byte-identical) for
the C-40 drift guard. Critic PASS (SSR 22/22, audio-contract grep
zero hits, deletion sweep clean).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
vadimwit
2026-07-10 18:38:30 +01:00
parent 01a7c32f55
commit c899132b94
4 changed files with 288 additions and 296 deletions
+8 -24
View File
@@ -17,11 +17,14 @@
// (auto-follow must not feed the mic; every ▶ inside the gallery is a gesture).
//
// Space honesty (D-31 §3): cells are never shrunk below the D-30 sizes — the
// rail scrolls horizontally with the expanded column auto-centred. Scrolling is
// the piano rail's NORMAL state on most loops (a rootless 7th-chord gallery is
// ~940px on its own). Narrow (<640px, D-31 §3): one thing per row — the current
// station's full gallery (cells wrap), then a single "next" thumb; the Roadmap
// above still shows the whole loop.
// rail scrolls horizontally, USER-OWNED. The old auto-centre effect was deleted
// in L-40 (D-40 §4/§6.1): the rail now lives in page flow (the Jam Guide band,
// no 70vh scroller), where scrollIntoView's nearest scroller is the DOCUMENT —
// every playhead advance would yank the whole page. Scrolling is the piano
// rail's NORMAL state on most loops (a rootless 7th-chord gallery is ~940px on
// its own). Narrow (<640px, D-31 §3): one thing per row — the current
// station's full gallery (cells wrap), then a single "next" thumb; the loop
// display up top (ProgressionBanner) still shows the whole loop.
//
// Pure presentational. Props (the D-31 §5 contract):
// stations — [{ shape, voicing, rootPc, quality, label, rn }] canonical order
@@ -32,7 +35,6 @@
// instrument — 'guitar' | 'piano' (VoicingBrowser `show`)
// keyRoot — key tonic pitch class 011 (ChordDiagram fret placement)
import { useEffect, useRef } from 'react'
import ChordDiagram from './ChordDiagram'
import MiniPiano from './MiniPiano'
import VoicingBrowser from './VoicingBrowser'
@@ -49,23 +51,6 @@ export default function GlanceRail({
const followBase = activeIndex >= 0 ? activeIndex : 0
const nextIndex = n > 1 ? (followBase + 1) % n : -1
// Auto-centre the expanded column as the accordion advances. Prop-driven —
// no rAF, nothing tied to the audio thread. Respect prefers-reduced-motion
// (D-31 §2.1): jump instead of smooth-scrolling.
const expandedRef = useRef(null)
useEffect(() => {
const el = expandedRef.current
if (!el) return
const reduceMotion =
typeof window !== 'undefined' &&
window.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches
el.scrollIntoView({
behavior: reduceMotion ? 'auto' : 'smooth',
inline: 'center',
block: 'nearest',
})
}, [expandedIndex])
if (n === 0) return null
return (
@@ -99,7 +84,6 @@ export default function GlanceRail({
return (
<div
key={i}
ref={expandedRef}
role="listitem"
aria-current={isNow ? 'true' : undefined}
aria-label={`${st.label}${st.rn ? ` (${st.rn})` : ''} — every ${instrument} voicing`}