From d5a45f433aa31201e597a8b15cd66132ea4b31dc Mon Sep 17 00:00:00 2001 From: vadimwit Date: Sat, 11 Jul 2026 16:51:09 +0100 Subject: [PATCH] =?UTF-8?q?feat(rail):=20500px-column=20adaptation=20?= =?UTF-8?q?=E2=80=94=20margin-hardened=20cells,=20wrap-safe=20basis=20(tas?= =?UTF-8?q?k=20D-51)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Chrome shave (rail p-2, rows p-1.5, dense cells p-1.5) so the 284+160 piano pair fits with real margin even under a classic Windows scrollbar (was a genuine break: 452 > 439 at old paddings); gallery basis 300->320 so a piano recommended cell can never share line one (prominence for free) while guitar keeps 4 cells per line; recommended-cell border strengthened to full accent. Playhead highlight-only contract proven intact; non-dense mounts byte-identical (28/28 at the gate). Critic PASS (arithmetic recomputed from source AND the built CSS; flex-basis override confirmed in dist). Co-Authored-By: Claude Fable 5 --- src/components/GlanceRail.jsx | 33 ++++++++++++++++++++++++------- src/components/VoicingBrowser.jsx | 21 ++++++++++++++------ 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/src/components/GlanceRail.jsx b/src/components/GlanceRail.jsx index fb0fe9f..87b0e1e 100644 --- a/src/components/GlanceRail.jsx +++ b/src/components/GlanceRail.jsx @@ -1,6 +1,17 @@ // GlanceRail — ALL loop stations expanded, always (task D-41, per // docs/design/integrated-glance.md §4; supersedes the L-33 playhead accordion). // +// D-51 (docs/design/one-screen.md §4/§6.2) adapts the rail to the dashboard's +// 500px right column. Margin-hardening arithmetic, worst case = a classic +// Windows scrollbar (~17px) inside the column's overflow-y-auto wrapper: +// row interior = 500 − 17 (scrollbar) − 18 (section border + p-2) +// − 14 (row border + p-1.5) = 451px +// guitar cell 89 (75 SVG + p-1.5 + border) → 4/line (374 ≤ 451; a 5th = 469 ✗) +// piano 1-octave 156.4 / 2-octave 279.6 → the mixed pair 279.6+6+156.4 = 442 +// fits with ~9px margin even under the scrollbar (26px without) — the fit the +// doc calls fragile at the old paddings is now robust. Without the scrollbar +// the interior is 468px; every count above is unchanged. +// // One VERTICAL ROW per loop station, canonical KB order (the same order the // banner's loop shows after rotation). Every row renders its full voicing // gallery PERMANENTLY — the playhead HIGHLIGHTS the active row (accent ring + @@ -149,7 +160,7 @@ function StationRow({ `${st.label}${st.rn ? ` (${st.rn})` : ''} — every ${instrument} voicing` + `${isNow ? ', now playing' : ''}${isNext ? ', up next' : ''}` } - className={`rounded-lg border p-2 ${stateClass}`} + className={`rounded-lg border p-1.5 ${stateClass}`} style={{ opacity: isNow || isFocused ? 1 : 0.85 }} > {/* ── Header line: identity + the folded roadmap education ── */} @@ -190,11 +201,19 @@ function StationRow({ - {/* ── Gallery: own-voicing cell + the full dense browser; cells WRAP, - never scroll horizontally (D-40 §4). ── */} -
+ {/* ── Gallery: own-voicing cell FIRST (the recommended cell — full-accent + border reads as "the answer", one-screen.md §4; same size, no new + colour) + the full dense browser; cells WRAP, never scroll + horizontally (D-40 §4). Browser basis 320px: a guitar own-cell + (89px) still shares line one (89+6+320 = 415 ≤ 451) so the first + visual line shows play + 3 shapes = 4 across, while a piano + own-cell (≥156.4px) can never share it (156.4+6+320 = 482.4 > 468 + even scrollbar-free) — the recommended voicing sits alone on line + one, prominence for free, and the gallery pairs at the FULL row + interior below it (the 279.6+156.4 mixed-pair fit needs all 451px). ── */} +
{(ownGuitar || ownPiano) && ( -
+
{ownGuitar && ( @@ -210,7 +229,7 @@ function StationRow({ )}
)} -
+
{/* dense: the rail shows the mic-feedback microcopy once, below. */}
@@ -243,7 +262,7 @@ export default function GlanceRail({ return (

diff --git a/src/components/VoicingBrowser.jsx b/src/components/VoicingBrowser.jsx index d907d7c..0002b01 100644 --- a/src/components/VoicingBrowser.jsx +++ b/src/components/VoicingBrowser.jsx @@ -46,8 +46,13 @@ // section chrome (border/panel background/heading — the row header // already names the chord) and suppresses the per-mount // mic-feedback microcopy (the rail shows it ONCE for the whole -// rail, D-31 §2.5). Every pre-existing mount renders identically -// with no prop. +// rail, D-31 §2.5). D-51 (one-screen.md §4) additionally tightens +// the DENSE cell spacing for the 500px right column — cell p-2 → +// p-1.5 and gallery gap-2 → gap-1.5 — which hardens the column's +// two-octave + one-octave piano pair fit (279.6 + 6 + 156.4 = 442 +// ≤ 451px row interior even with a classic Windows scrollbar in +// the rail's scroller; it was 452 vs 456, a ~4px squeak, before). +// Every pre-existing mount renders identically with no prop. import { useEffect, useMemo, useRef } from 'react' import ChordDiagram from './ChordDiagram' @@ -126,9 +131,11 @@ function SectionHeading({ children }) { // One gallery cell: label on top, diagram thumb, its own ▶ underneath. // bg-surface inside the bg-panel section gives the cells a quiet inlay border; // label is gray-300 on surface (AA comfortable at 11px semibold). -function GalleryCell({ label, playLabel, onPlay, children }) { +// `dense` (D-51): p-1.5 instead of p-2 — 4px off each cell's box width, part of +// the right-column margin-hardening. Non-dense output is byte-identical. +function GalleryCell({ label, playLabel, onPlay, dense = false, children }) { return ( -
+
{guitarShapes.map((shape, i) => ( {/* pianoVoicing() output carries no rootPc, and without it VoicingPiano falls back to the LOWEST voice for its "R" badge — wrong for rootless @@ -271,6 +279,7 @@ export default function VoicingBrowser({ rootPc = 0, quality = 'maj', show = 'bo label={voicing.label} playLabel={`Play ${name} — ${voicing.label} piano voicing`} onPlay={() => playPiano(voicing)} + dense={dense} >