From e8db65afcb70b8669cbd49e3d7afa6210135932e Mon Sep 17 00:00:00 2001 From: Byron Gamatos Date: Sat, 20 Jun 2026 21:09:27 +0200 Subject: [PATCH] feat(highway-2d): render unpitched slides (slu) (#524) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 2D highway drew pitched slides (sl) but ignored unpitched slides (slu) — drawNote read only opts.sl. The 3D highway already renders both (slideTrailEnd). drawNote now draws slu as a dashed diagonal with no arrowhead (no definite target pitch), keeping the solid arrow+arrowhead for pitched sl. The two are mutually exclusive in the data. Chord notes flow through the same drawNote, so chord-note unpitched slides are covered too. Also fixes a latent pre-existing bug flagged in review: `opts?.sl || -1` discarded a pitched slide-to-open (sl: 0); now `?? -1` preserves fret-0 targets and keeps pitched precedence. Codex-reviewed: no P1/P2; dash state reset on all paths, no pitched-slide regression. node --check clean. Closes #336. Part of got-feedback/feedback#334. Co-authored-by: Claude Opus 4.8 (1M context) --- static/highway.js | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/static/highway.js b/static/highway.js index 6ac1f2c..4ad74d8 100644 --- a/static/highway.js +++ b/static/highway.js @@ -1444,7 +1444,8 @@ function createHighway() { const isPinchHarmonic = opts?.hp || false; const isChord = opts?.chord || false; const bend = opts?.bn || 0; - const slide = opts?.sl || -1; + const slide = opts?.sl ?? -1; // pitched slide-to fret (-1 = none; 0 = slide to open) + const slu = opts?.slu ?? -1; // unpitched slide-to fret (-1 = none) const hammerOn = opts?.ho || false; const pullOff = opts?.po || false; const tap = opts?.tp || false; @@ -1637,20 +1638,29 @@ function createHighway() { if (sz < 14) return; // Skip small technique labels - // Slide indicator (diagonal arrow) - if (slide >= 0) { - const dir = slide > fret ? -1 : 1; // arrow direction (up or down the neck); mirror handles lefty + // Slide indicator (diagonal arrow). Pitched (sl) draws a solid arrow to + // the target fret; unpitched (slu) draws a dashed diagonal with no + // arrowhead (no definite target pitch). The two are mutually exclusive + // in the data; the 3D highway makes the same pitched/unpitched split. + if (slide >= 0 || slu >= 0) { + const pitched = slide >= 0; + const target = pitched ? slide : slu; + const dir = target > fret ? -1 : 1; // up or down the neck; mirror handles lefty ctx.strokeStyle = '#fff'; ctx.lineWidth = Math.max(2, sz / 10); + if (!pitched) ctx.setLineDash([Math.max(2, sz / 8), Math.max(2, sz / 8)]); ctx.beginPath(); ctx.moveTo(x - sz * 0.3, y + dir * sz * 0.3); ctx.lineTo(x + sz * 0.3, y - dir * sz * 0.3); ctx.stroke(); - // Arrowhead - ctx.beginPath(); - ctx.moveTo(x + sz * 0.3, y - dir * sz * 0.3); - ctx.lineTo(x + sz * 0.15, y - dir * sz * 0.15); - ctx.stroke(); + if (!pitched) ctx.setLineDash([]); + // Arrowhead only for a pitched slide (definite target pitch). + if (pitched) { + ctx.beginPath(); + ctx.moveTo(x + sz * 0.3, y - dir * sz * 0.3); + ctx.lineTo(x + sz * 0.15, y - dir * sz * 0.15); + ctx.stroke(); + } } // H/P/T label above note