From 5ef7704f009c67ef306b89c4086a44ae01cee0a2 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 3 Jul 2026 14:00:47 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20Throttle=20Mermaid=20lightb?= =?UTF-8?q?ox=20panning=20with=20requestAnimationFrame?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements `requestAnimationFrame` to throttle high-frequency DOM style updates (`style.transform`) triggered by `pointermove` and `wheel` events in the Mermaid zoom lightbox. This reduces main thread blocking and ensures smoother scrolling when panning and zooming. Includes an `immediate` flag to bypass the throttle during initial setup to prevent a 1-frame visual flash. Co-authored-by: ImChong <74563097+ImChong@users.noreply.github.com> --- .jules/bolt.md | 4 ++++ assets/js/mermaid-zoom.js | 39 +++++++++++++++++++++++++++++---------- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index 984201f7..7eafe009 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -111,3 +111,7 @@ ## 2026-11-20 - Fast Section Length Extraction via str.find vs splitlines **Learning:** In string-processing logic where a specific document section's length needs to be calculated based on regex headings (like in `check_roadmap_quality.py`), calling `splitlines()` on the entire document and iterating through lines to match a regex is highly inefficient. It forces an O(N) memory allocation and executes a regex match for every line. **Action:** When extracting or measuring a document section between two headers, replace `splitlines()` with `re.search()` to find the start, then use native `str.find("\n## ")` to find the end. Extract the section via slicing (`content[start:end]`) and calculate metrics on that slice. This avoids full-document string array allocations and drops regex invocations to one. + +## 2024-05-19 - Throttle High-Frequency Pointer Events +**Learning:** Raw event listeners for `pointermove` and `wheel` that trigger DOM mutations (`style.transform`) can fire much faster than the display refresh rate (e.g., 1000Hz gaming mice vs 60Hz display). This forces the browser to calculate redundant layout and style changes, causing severe main thread blocking and micro-stutters during panning or zooming in lightboxes. +**Action:** Always throttle continuous, high-frequency DOM style updates using a `requestAnimationFrame` flag to sync visual updates with the browser's native rendering cadence. For initial positioning where you *want* synchronous updates to prevent 1-frame flashes, provide an `immediate` bypass flag. diff --git a/assets/js/mermaid-zoom.js b/assets/js/mermaid-zoom.js index 3e938668..01b4450a 100644 --- a/assets/js/mermaid-zoom.js +++ b/assets/js/mermaid-zoom.js @@ -22,6 +22,7 @@ x: 0, y: 0, }; + var transformTicking = false; function clampScale(scale) { return Math.min(MAX_SCALE, Math.max(MIN_SCALE, scale)); @@ -91,16 +92,34 @@ return lightbox; } - function applyTransform() { + function applyTransform(immediate) { if (!stage) return; - stage.style.transform = - 'translate(calc(-50% + ' + - state.x + - 'px), calc(-50% + ' + - state.y + - 'px)) scale(' + - state.scale + - ')'; + if (immediate) { + stage.style.transform = + 'translate(calc(-50% + ' + + state.x + + 'px), calc(-50% + ' + + state.y + + 'px)) scale(' + + state.scale + + ')'; + return; + } + if (!transformTicking) { + window.requestAnimationFrame(function () { + if (!stage) return; + stage.style.transform = + 'translate(calc(-50% + ' + + state.x + + 'px), calc(-50% + ' + + state.y + + 'px)) scale(' + + state.scale + + ')'; + transformTicking = false; + }); + transformTicking = true; + } } function measureSvg(svg) { @@ -141,7 +160,7 @@ state.scale = clampScale(fit); state.x = 0; state.y = 0; - applyTransform(); + applyTransform(true); } /** Hide stage until fitToViewport runs — avoids scale(1) flash. */