Fix playlist freeze: self-healing drag guard + identity-based row actions
A lost dragend (dragged node removed from the DOM) left dragFrom/cueDragging stuck, permanently deferring renderPlaylist: the list stopped redrawing and row buttons acted on stale indexes (delete/play appeared dead). The guard now unlocks itself when no .dragging element exists, and row actions resolve the track index at click time by object identity. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b04fe01a1a
commit
e89213b8f9
2 files changed
+25
-8
No files matched your search
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "dj-visualizer",
|
"name": "dj-visualizer",
|
||||||
"version": "1.7.1",
|
"version": "1.7.2",
|
||||||
"description": "Audio-reactive fractal & abstract visualizer for external display (Mac)",
|
"description": "Audio-reactive fractal & abstract visualizer for external display (Mac)",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"author": {
|
"author": {
|
||||||
|
|||||||
+24
-7
@@ -1153,7 +1153,13 @@ function renderPlaylist() {
|
|||||||
// Never rebuild the list mid-drag: replacing the dragged node would cancel
|
// Never rebuild the list mid-drag: replacing the dragged node would cancel
|
||||||
// the browser drag instantly (progress re-renders arrive every ~400ms while
|
// the browser drag instantly (progress re-renders arrive every ~400ms while
|
||||||
// a track plays, which made reordering impossible). Deferred to dragend.
|
// a track plays, which made reordering impossible). Deferred to dragend.
|
||||||
if (dragFrom >= 0 || cueDragging) { plDirty = true; return; }
|
if (dragFrom >= 0 || cueDragging) {
|
||||||
|
if (document.querySelector('#playlist .dragging')) { plDirty = true; return; }
|
||||||
|
// Leaked guard: dragend never fires when the dragged node left the DOM,
|
||||||
|
// which would freeze the list forever. No .dragging element = no real
|
||||||
|
// drag in progress, so unlock and render.
|
||||||
|
dragFrom = -1; cueDragging = false; clearDropMarks();
|
||||||
|
}
|
||||||
const ol = $('#playlist');
|
const ol = $('#playlist');
|
||||||
ol.innerHTML = '';
|
ol.innerHTML = '';
|
||||||
playlist.forEach((tr, i) => {
|
playlist.forEach((tr, i) => {
|
||||||
@@ -1214,20 +1220,31 @@ function renderPlaylist() {
|
|||||||
(isCur ? '<i class="tprog" style="width:' + prog.toFixed(1) + '%"></i>' : '') +
|
(isCur ? '<i class="tprog" style="width:' + prog.toFixed(1) + '%"></i>' : '') +
|
||||||
'</div>' + body;
|
'</div>' + body;
|
||||||
|
|
||||||
|
// Resolve the track's index at CLICK time by identity: if the list was
|
||||||
|
// mutated while this DOM was on screen, the render-time index is stale and
|
||||||
|
// would hit the wrong track (or none).
|
||||||
|
const liveIdx = () => playlist.indexOf(tr);
|
||||||
// Clicking the name (re)starts the track from the beginning.
|
// Clicking the name (re)starts the track from the beginning.
|
||||||
li.querySelector('.tname').addEventListener('click', () => playIndex(i));
|
li.querySelector('.tname').addEventListener('click', () => playIndex(liveIdx()));
|
||||||
// On the current track the button pauses/resumes/replays; on others it starts that track.
|
// On the current track the button pauses/resumes/replays; on others it starts that track.
|
||||||
li.querySelector('.play-btn').addEventListener('click', () => {
|
li.querySelector('.play-btn').addEventListener('click', () => {
|
||||||
if (i === currentIndex) togglePlayPause();
|
const k = liveIdx();
|
||||||
else playIndex(i);
|
if (k < 0) return;
|
||||||
|
if (k === currentIndex) togglePlayPause();
|
||||||
|
else playIndex(k);
|
||||||
|
});
|
||||||
|
li.querySelector('.del-btn').addEventListener('click', () => {
|
||||||
|
const k = liveIdx();
|
||||||
|
if (k >= 0) removeTrack(k);
|
||||||
});
|
});
|
||||||
li.querySelector('.del-btn').addEventListener('click', () => removeTrack(i));
|
|
||||||
li.querySelector('.key-btn').addEventListener('click', () => {
|
li.querySelector('.key-btn').addEventListener('click', () => {
|
||||||
capturingFor = capturingFor === i ? -1 : i;
|
const k = liveIdx();
|
||||||
|
capturingFor = capturingFor === k ? -1 : k;
|
||||||
renderPlaylist();
|
renderPlaylist();
|
||||||
});
|
});
|
||||||
li.querySelector('.scene-btn').addEventListener('click', () => {
|
li.querySelector('.scene-btn').addEventListener('click', () => {
|
||||||
sceneEditing = sceneEditing === i ? -1 : i;
|
const k = liveIdx();
|
||||||
|
sceneEditing = sceneEditing === k ? -1 : k;
|
||||||
renderPlaylist();
|
renderPlaylist();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user