From b11ff4b7b6370fb3976aa6f08e82bcd2ffdaf3db Mon Sep 17 00:00:00 2001 From: teahubxyz Date: Wed, 21 Oct 2026 06:56:22 +0000 Subject: [PATCH] reactions: the tray re-ranks after every tap; ticks that land within a second of each other stack upward instead of hiding one another Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01K6897PWcUEwHiqZMcVnNF6 --- index.html | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/index.html b/index.html index 0bb1efe..0c16fad 100644 --- a/index.html +++ b/index.html @@ -1162,7 +1162,7 @@ function toCooking(job) { if (!job.stepping) { state.history.push(spec); state.played = (state.played || 0) + 1; spec.stationId ||= state.stationId; } keepLabel(); for (const t of $("#bar").querySelectorAll("em")) t.remove(); - if (spec.hits) { const dur = spec.seconds || 120; for (const h of spec.hits) { const tick = document.createElement("em"); tick.textContent = h.e; tick.style.left = Math.min(100, h.t / dur * 100) + "%"; tick.title = `${h.e} at ${mmss(h.t)}`; $("#bar").append(tick); } } + if (spec.hits) { const dur = spec.seconds || 120; for (const h of spec.hits) placeTick($("#bar"), h.e, h.t, dur); } $("#title").textContent = spec.break ? "✱ " + spec.title : spec.title; $("#tags").textContent = `${spec.bpm} bpm · ${spec.key}${spec.break ? " · " + (spec.sponsored ? "sponsored" : "intermission") : spec.remixOf ? " · remix" : ""}`; $("#lyric").textContent = spec.line || ""; showTags(spec); @@ -1377,7 +1377,14 @@ function markReaction(spec, emoji, t) { const dur = state.audio.duration || spec.seconds || 120; spec.hits = (spec.hits || []).concat([{ e: emoji, t: Math.round(t), s: sectionAt(spec, t) }]).slice(-24); const bar = $("#bar"); if (!bar || state.current.spec !== spec) return; - const tick = document.createElement("em"); tick.textContent = emoji; tick.style.left = Math.min(100, t / dur * 100) + "%"; tick.title = `${emoji} at ${mmss(t)}`; bar.append(tick); + placeTick(bar, emoji, t, dur); +} +/* a tick on the bar; ones within a second of each other stack upward so none hides another */ +function placeTick(bar, emoji, t, dur) { + const tick = document.createElement("em"); tick.textContent = emoji; tick.title = `${emoji} at ${mmss(t)}`; + const left = Math.min(100, t / dur * 100); + const near = [...bar.querySelectorAll("em")].filter((x) => Math.abs(parseFloat(x.dataset.t) - t) < 1.5).length; + tick.dataset.t = t; tick.style.left = left + "%"; tick.style.top = (-16 - near * 13) + "px"; bar.append(tick); } async function react(emoji, text) { const cur = state.current && state.current.spec; if (!cur) return; @@ -1614,7 +1621,7 @@ function showEmojiRow() { for (const b of [...row.querySelectorAll("button[data-e]")]) b.remove(); for (const e of top) { const b = document.createElement("button"); b.dataset.e = e; b.textContent = e; b.title = (EMOJI_MEANING[e] && (EMOJI_MEANING[e].like || EMOJI_MEANING[e].avoid)) || ""; row.insertBefore(b, more); } } -$("#emojis").addEventListener("click", (e) => { const b = e.target.closest("button[data-e]"); if (!b) return; b.classList.add("hit"); setTimeout(() => b.classList.remove("hit"), 300); bumpEmoji(b.dataset.e); react(b.dataset.e); }); +$("#emojis").addEventListener("click", (e) => { const b = e.target.closest("button[data-e]"); if (!b) return; b.classList.add("hit"); setTimeout(() => b.classList.remove("hit"), 300); bumpEmoji(b.dataset.e); react(b.dataset.e); setTimeout(showEmojiRow, 350); }); function openPicker() { const p = $("#picker"); p.innerHTML = ""; const top = document.createElement("div"); top.className = "top";