()
| 398 | // Keep the mixer column and the waveform area scrolled in lockstep so stem |
| 399 | // controls stay aligned with their lanes when the stack overflows (#159). |
| 400 | function wireLaneScrollSync() { |
| 401 | const mixer = document.getElementById("mixer"); |
| 402 | if (!mixer || !waveScroll) return; |
| 403 | // Mirror scrollTop between the two panes by assigning only when the values |
| 404 | // differ. The echo stops on its own (once equal, the partner's handler is a |
| 405 | // no-op), so no reentrancy guard / rAF is needed — that frame-delayed guard |
| 406 | // was what made inertial scrolling stutter (#163). |
| 407 | const link = (src, dst) => |
| 408 | src.addEventListener("scroll", () => { |
| 409 | if (dst.scrollTop !== src.scrollTop) dst.scrollTop = src.scrollTop; |
| 410 | }, { passive: true }); |
| 411 | link(mixer, waveScroll); |
| 412 | link(waveScroll, mixer); |
| 413 | } |
| 414 | |
| 415 | // ─── Wire transport buttons ─── |
| 416 |
no test coverage detected