diff --git a/CLAUDE.md b/CLAUDE.md index f58353b..465894c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -289,6 +289,13 @@ v2026.08.26.001 Showcase retired: `addShowcaseSong()` → one-time `retireShowc **§14's "showcase song" entry is now historical** — there is no shipped demo song, and the rule it existed to honour still stands: never hardcode a copyrighted song into the source, which is a public repo served publicly by GitHub Pages. +v2026.08.26.002 Pre-fix charts self-repair at render. The .21.004 cleanup runs only at import, so + songs already in a library kept raw `[Verse]` labels, `G#sA` slides and tab rows + as literal lyrics. `parseSong` now converts a bare bracketed non-chord label to a + heading, resolves slide shorthand, and emits a `tab` line type (`.tabline`, + monospace) for guitar-tab rows. **Slide rewrites must stay LENGTH-PRESERVING** + (`slide`→5 spaces, `s`→1 space) in BOTH paths: `pairToSegs` aligns chords by + column, so a shortened chord line drops every chord onto the first word. ``` A GitHub Actions workflow (`.github/workflows/check.yml`) enforces the version discipline on every push: it syntax-checks the extracted inline script and `sw.js`, **fails if the `` brand version ≠ `sw.js` CACHE version**, and fails on duplicate element ids in the markup. diff --git a/README.md b/README.md index 019b98f..5ee3756 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@
[![CI](https://github.com/cdburgess75/SetList69/actions/workflows/check.yml/badge.svg)](https://github.com/cdburgess75/SetList69/actions/workflows/check.yml) -[![Version](https://img.shields.io/badge/version-2026.08.26.001-informational?style=flat-square)](#versioning) +[![Version](https://img.shields.io/badge/version-2026.08.26.002-informational?style=flat-square)](#versioning) [![PWA](https://img.shields.io/badge/PWA-installable-5A0FC8?style=flat-square)](#save-it-to-your-phone) [![Offline](https://img.shields.io/badge/offline-first-2ea043?style=flat-square)](#features) [![Runtime deps](https://img.shields.io/badge/runtime_dependencies-0-f5a623?style=flat-square)](#tech-stack) diff --git a/docs/screenshots/demo.gif b/docs/screenshots/demo.gif index f15b05d..3ca4066 100644 Binary files a/docs/screenshots/demo.gif and b/docs/screenshots/demo.gif differ diff --git a/docs/screenshots/home.png b/docs/screenshots/home.png index aea672b..e0b8262 100644 Binary files a/docs/screenshots/home.png and b/docs/screenshots/home.png differ diff --git a/setlist69.html b/setlist69.html index 23129a3..beb7e64 100644 --- a/setlist69.html +++ b/setlist69.html @@ -403,6 +403,7 @@ .cc-cell.noshape{padding:.5rem .6rem;} /* name-only cell for chords with no known fingering */ .wgspace{display:inline-block;width:.32em;} /* breakable gap between word-groups */ .chorus{border-left:3px solid var(--accent);padding-left:.7rem;margin-left:-.1rem;} + .tabline{font-family:var(--mono);font-size:.62em;color:var(--muted);white-space:pre;overflow-x:auto;line-height:1.35;} .comment{color:var(--muted);font-style:italic;font-size:.7em;margin:.3rem 0 .5rem;font-family:var(--ui);} .swipehint{text-align:center;color:var(--faint);font-size:.7rem;font-family:var(--mono);margin-top:2rem;opacity:.6;transition:opacity .5s;} .modal.bottom{align-items:flex-end;} @@ -548,7 +549,7 @@
-
SetList69v2026.08.26.001
+
SetList69v2026.08.26.002
@@ -906,6 +907,21 @@

Songs

else if(k==="comment"||k==="c")out.push({type:"comment",text:v}); continue;} if(line.trim()===""){out.push({type:"blank"});continue;} + /* Repair pass for charts saved BEFORE the import cleanup existed (v2026.08.26.002). Their + stored body still holds raw [Verse] labels, "G#sA" slide shorthand and guitar-tab rows, and + an import-time fix cannot reach back for them — so handle it at render and the song heals + itself with no re-import. Slide rewrites are LENGTH-PRESERVING: pairToSegs aligns chords by + COLUMN, so shortening a chord line would slide every chord onto the wrong syllable. */ + const bareLbl=line.trim().match(/^\[([^\]]+)\]\s*(\(.*\)|x\s*\d+)?\s*$/i); + if(bareLbl&&!looksChord(bareLbl[1].trim())){ + out.push({type:"comment",text:bareLbl[1].trim()+(bareLbl[2]?" "+bareLbl[2].trim():"")});continue;} + if(/^[eEADGB]b?\s*\|[-0-9hpbrx\/\\~|\s]*\|?\s*$/.test(line.trim())){ + out.push({type:"tab",text:line});continue;} + if(!hasInline){ + const n=line.replace(/\b([A-G][#b]?)(\s+)slide(\s+)([A-G][#b]?)\b/g,"$1$2 $3$4") + .replace(/\b([A-G][#b]?)s([A-G][#b]?)\b/g,"$1 $2"); + if(n!==line&&isChordLine(n))line=n; + } if(hasInline&&/\[[^\]]+\]/.test(line)){out.push({type:"segs",segs:inlineToSegs(line),chorus:inChorus});} else if(!hasInline&&isChordLine(line)&&i+1Songs for(const ln of r.lines){ if(ln.type==="blank"){closeSect();continue;} if(ln.type==="comment"){sect+='
'+esc(ln.text)+'
';continue;} + if(ln.type==="tab"){sect+='
'+esc(ln.text)+'
';continue;} sect+='
'+renderLine(ln.segs)+'
'; } closeSect(); @@ -1522,7 +1539,7 @@

Songs

Only applied where the result is a genuine chord line, so lyrics saying "slide" are safe. */ lines=lines.map(l=>{ if(!l.trim()||/^\{/.test(l.trim()))return l; - const n=l.replace(/\b([A-G][#b]?)\s+slide\s+([A-G][#b]?)\b/g,"$1 $2") + const n=l.replace(/\b([A-G][#b]?)(\s+)slide(\s+)([A-G][#b]?)\b/g,"$1$2 $3$4") .replace(/\b([A-G][#b]?)s([A-G][#b]?)\b/g,"$1 $2"); return (n!==l&&isChordLine(n))?n:l;}); let title="",sub="",key=""; diff --git a/sw.js b/sw.js index dbfe429..b694108 100644 --- a/sw.js +++ b/sw.js @@ -1,4 +1,4 @@ -const CACHE = 'setlist69-v2026.08.26.001'; +const CACHE = 'setlist69-v2026.08.26.002'; const PRECACHE = [ './', './index.html',