reactions: the tray re-ranks after every tap; ticks that land within a second of each other stack upward instead of hiding one another
shots / shots (push) Successful in 2m18s

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K6897PWcUEwHiqZMcVnNF6
This commit is contained in:
teahubxyzandClaude Fable 5.1 committed 2026-10-21 06:56:22 +00:00
1 parent 70c630711b
commit b11ff4b7b6
1 file changed
+10 -3
+10 -3
View File
@@ -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";