/* * fee[dB]ack v0.3.0 — Playlists + Saved for Later screens. * * Vanilla JS (constitution P-II). Core REST (/api/playlists, /api/saved/*), * no capability domain. Renders #v3-playlists (list + detail with drag- * reorder) and #v3-saved (the reserved system playlist). Exposes * window.v3Saved.toggle(filename) for the "Save for later" affordance on song * cards/rows (used by the library/dashboard). */ (function () { 'use strict'; const sm = window.slopsmith; const esc = (s) => String(s == null ? '' : s).replace(/[&<>"']/g, (c) => ( { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c])); // Return null (don't throw) on a network/connection failure so a fetch // rejection can't abort rendering — matches the "degrades gracefully" // contract and the other v3 modules' jget/jsend. async function jget(u) { try { const r = await fetch(u); return r.ok ? r.json() : null; } catch (e) { return null; } } async function jsend(method, u, body) { try { const r = await fetch(u, { method, headers: { 'Content-Type': 'application/json' }, body: body == null ? undefined : JSON.stringify(body), }); return r.ok ? r.json() : null; } catch (e) { return null; } } function songRow(s, opts) { opts = opts || {}; const handle = opts.draggable ? '⠿' : ''; const tuning = s.tuning_name ? '' + esc(s.tuning_name) + '' : ''; return '
No playlists yet. Create one to group songs.
') + 'Empty — add songs from the library.
') + 'Nothing saved yet. Use “Save for later” on a song to add it here.
') + '