diff --git a/index.html b/index.html index bfe5219..e80548e 100644 --- a/index.html +++ b/index.html @@ -299,7 +299,7 @@ input,textarea{font:inherit;color:inherit}

…

-

+

-
@@ -880,6 +879,18 @@ async function stationSheet(id) { palRow.append(b); } p.append(palRow); + if (state.stationId === id) { + const more = document.createElement("div"); more.className = "row"; more.style.marginTop = "10px"; more.style.justifyContent = "flex-start"; + const fromT = Object.assign(document.createElement("button"), { textContent: "* STATION FROM THIS TRACK", title: "a new station grown from the track playing now" }); + fromT.disabled = !(state.current && state.current.blob && !state.current.spec.break); + fromT.addEventListener("click", () => { p.classList.add("hidden"); stationFromCurrent(); }); + const shareS = Object.assign(document.createElement("button"), { textContent: "↗ SHARE STATION", title: "a link that opens this station's seed" }); + shareS.addEventListener("click", async () => { + const url = `${location.origin}/#${encodeURIComponent(s.seed).replace(/%20/g, "+")}`; + try { if (navigator.share) await navigator.share({ title: `${s.name || s.seed} · gandora`, url }); else { await navigator.clipboard.writeText(url); toast("Station link copied."); } } catch {} + }); + more.append(fromT, shareS); p.append(more); + } const row = document.createElement("div"); row.className = "row"; const save = Object.assign(document.createElement("button"), { className: "go", textContent: "SAVE" }); save.addEventListener("click", async () => { s.name = name.value.trim().slice(0, 60); await db.put(s, "stations"); if (state.stationId === id) { state.station = s.name; $("#stationName").textContent = s.name || s.seed; } p.classList.add("hidden"); listStations().catch(() => {}); toast("Renamed."); }); @@ -944,12 +955,12 @@ async function stationFrom(t) { state.refFor = { blob: t.blob, left: 2, seconds: s.seconds || 120 }; // the next two tracks are rendered with the source as reference toast(`New station from “${s.title}”.`, 3000); } -$("#fromTrack").addEventListener("click", async () => { +async function stationFromCurrent() { const cur = state.current && state.current.spec; if (!cur) return; const row = (await db.all()).find((t) => t.id === cur.id) || (state.current.blob ? { spec: cur, blob: state.current.blob } : null); if (!row) { toast("That one hasn't finished cooking."); return; } stationFrom(row); -}); +} const KEEP_UNKEPT = 40; // every rendered track is saved so the station can be scrolled back through; ❤️ pins one, the rest roll off after KEEP_UNKEPT async function remember(job, kept) { @@ -1048,7 +1059,6 @@ async function playJob(job) { if (state.current !== job) return; // skipped while cooking $("#cover").classList.remove("cooking"); crackleOff(); - $("#fromTrack").disabled = false; if (job.err) { crackleOff(); toast(job.err.message, 6000); $("#lyric").textContent = job.err.message + (/limit|budget/i.test(job.err.message) ? "" : " · skip to try the next"); return; } if (state.audio.src) URL.revokeObjectURL(state.audio.src); state.audio.src = URL.createObjectURL(job.blob);