mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-09-11 04:44:31 +00:00
fix(playlists): stay within the shipped Tailwind class set
Reverts the regenerated static/tailwind.min.css and reworks the tuning-check markup to use only classes already in the committed sheet. Regenerating that file is not reproducible off CI: nothing pins tailwindcss, autoprefixer or caniuse-lite, so a local `npx -y tailwindcss@3.4.19` resolves different browser data and rewrites unrelated bytes -- a clean checkout of main rebuilds with the -webkit-backdrop-filter prefixes dropped. Committing that output fails the tailwind-fresh gate no matter how many times it is regenerated. Six utilities were new: bg-fb-good/10, border-fb-accent/50, hover:bg-fb-accent/10, list-disc, list-inside, max-h-48, plus gap-x-3/gap-y-2. Substituted bg-fb-good/30, the amber border already used by the mismatch state, hover:bg-fb-card, a literal bullet in a div, max-h-32 and gap-3. Visual intent is unchanged. The removal-confirm test pinned the <li> markup; it now accepts either wrapper, since what it guards is that every song is named and escaped ahead of any DELETE, not which element wraps it. Signed-off-by: ChrisBeWithYou <christian.a.cowan@gmail.com>
This commit is contained in:
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+12
-5
@@ -153,9 +153,13 @@
|
|||||||
if (!total) return '';
|
if (!total) return '';
|
||||||
const mism = results.filter((r) => r.state === 'mismatch').length;
|
const mism = results.filter((r) => r.state === 'mismatch').length;
|
||||||
const unk = results.filter((r) => r.state === 'unknown').length;
|
const unk = results.filter((r) => r.state === 'unknown').length;
|
||||||
const box = 'mb-4 rounded-lg border px-3 py-2 text-sm flex flex-wrap items-center gap-x-3 gap-y-2 ';
|
// Plain gap-3 rather than gap-x-3/gap-y-2: the axis-specific pair isn't
|
||||||
|
// in the committed tailwind.min.css, and regenerating it is not
|
||||||
|
// reproducible outside CI (autoprefixer/caniuse drift changes unrelated
|
||||||
|
// bytes), so the summary bar stays within the shipped class set.
|
||||||
|
const box = 'mb-4 rounded-lg border px-3 py-2 text-sm flex flex-wrap items-center gap-3 ';
|
||||||
if (!mism) {
|
if (!mism) {
|
||||||
return '<div class="' + box + 'border-fb-good/40 bg-fb-good/10 text-fb-good">' +
|
return '<div class="' + box + 'border-fb-good/40 bg-fb-good/30 text-fb-good">' +
|
||||||
'<span>✓ All ' + total + ' songs are in your tuning.</span>' +
|
'<span>✓ All ' + total + ' songs are in your tuning.</span>' +
|
||||||
(unk ? '<span class="text-fb-textDim text-xs">' + unk + ' couldn\'t be checked (no tuning data).</span>' : '') +
|
(unk ? '<span class="text-fb-textDim text-xs">' + unk + ' couldn\'t be checked (no tuning data).</span>' : '') +
|
||||||
'</div>';
|
'</div>';
|
||||||
@@ -165,7 +169,7 @@
|
|||||||
(unk ? '<span class="text-fb-textDim text-xs">' + unk + ' couldn\'t be checked (no tuning data) — left alone.</span>' : '') +
|
(unk ? '<span class="text-fb-textDim text-xs">' + unk + ' couldn\'t be checked (no tuning data) — left alone.</span>' : '') +
|
||||||
'<span class="flex-1"></span>' +
|
'<span class="flex-1"></span>' +
|
||||||
'<button id="v3-pl-tune-only" class="text-xs px-2 py-1 rounded border border-fb-border text-fb-textDim hover:text-fb-text" aria-pressed="false">Show only these</button>' +
|
'<button id="v3-pl-tune-only" class="text-xs px-2 py-1 rounded border border-fb-border text-fb-textDim hover:text-fb-text" aria-pressed="false">Show only these</button>' +
|
||||||
'<button id="v3-pl-tune-remove" class="text-xs px-2 py-1 rounded border border-fb-accent/50 text-fb-accent hover:bg-fb-accent/10">Remove them…</button>' +
|
'<button id="v3-pl-tune-remove" class="text-xs px-2 py-1 rounded border border-amber-400/40 text-fb-text hover:bg-fb-card">Remove them…</button>' +
|
||||||
'</div>';
|
'</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,10 +205,13 @@
|
|||||||
// user data, so the confirm has to be a list, not a count.
|
// user data, so the confirm has to be a list, not a count.
|
||||||
const doomed = results.filter((r) => r.state === 'mismatch').map((r) => r.song);
|
const doomed = results.filter((r) => r.state === 'mismatch').map((r) => r.song);
|
||||||
if (!doomed.length) return;
|
if (!doomed.length) return;
|
||||||
const names = doomed.map((s) => '<li>' + esc(s.title || s.filename) + '</li>').join('');
|
const names = doomed.map((s) => '<div>• ' + esc(s.title || s.filename) + '</div>').join('');
|
||||||
const msg = 'Remove these ' + doomed.length + ' song' + (doomed.length === 1 ? '' : 's')
|
const msg = 'Remove these ' + doomed.length + ' song' + (doomed.length === 1 ? '' : 's')
|
||||||
+ ' from "' + esc(pl.name) + '"?'
|
+ ' from "' + esc(pl.name) + '"?'
|
||||||
+ '<ul class="mt-2 text-xs list-disc list-inside max-h-48 overflow-auto">' + names + '</ul>'
|
// Bulleted with a literal •, and sized with max-h-32, so the
|
||||||
|
// confirm needs no Tailwind class the committed CSS lacks —
|
||||||
|
// regenerating tailwind.min.css is not reproducible off CI.
|
||||||
|
+ '<div class="mt-2 text-xs max-h-32 overflow-y-auto">' + names + '</div>'
|
||||||
+ '<p class="text-xs text-fb-textDim mt-2">They stay in your library — only this playlist changes, and you can add them back.</p>';
|
+ '<p class="text-xs text-fb-textDim mt-2">They stay in your library — only this playlist changes, and you can add them back.</p>';
|
||||||
const ok = (typeof window.uiConfirm === 'function')
|
const ok = (typeof window.uiConfirm === 'function')
|
||||||
? await window.uiConfirm({
|
? await window.uiConfirm({
|
||||||
|
|||||||
@@ -261,8 +261,11 @@ test('the check itself never mutates the playlist', () => {
|
|||||||
|
|
||||||
test('bulk removal names every song and is confirmed before any DELETE', () => {
|
test('bulk removal names every song and is confirmed before any DELETE', () => {
|
||||||
const body = extractBlock(PL_SRC, 'async function applyTuningCheck(');
|
const body = extractBlock(PL_SRC, 'async function applyTuningCheck(');
|
||||||
// The confirm is built from the doomed titles …
|
// The confirm is built from the doomed titles, each escaped. The row markup
|
||||||
assert.match(body, /doomed\.map\(\(s\) => '<li>' \+ esc\(s\.title \|\| s\.filename\)/);
|
// moved from <li> to a bulleted <div> so the confirm needs no Tailwind class
|
||||||
|
// the committed CSS lacks — what matters is that every song is named and
|
||||||
|
// escaped, not which element wraps it.
|
||||||
|
assert.match(body, /doomed\.map\(\(s\) => '<(?:li|div)>[^']*' \+ esc\(s\.title \|\| s\.filename\)/);
|
||||||
// … it is awaited, and an early return happens before the delete loop.
|
// … it is awaited, and an early return happens before the delete loop.
|
||||||
const confirmAt = body.indexOf('uiConfirm');
|
const confirmAt = body.indexOf('uiConfirm');
|
||||||
const bailAt = body.indexOf('if (!ok) return;');
|
const bailAt = body.indexOf('if (!ok) return;');
|
||||||
|
|||||||
Reference in New Issue
Block a user