Zoom in monitor area under a mouse#39
Conversation
📝 WalkthroughWalkthroughThe program monitor now supports cursor-centered zooming, a 100% reset control, and panning with middle mouse or Alt-drag. Zoom and pan state are stored in the editor state, applied to the preview canvas, and reflected through monitor cursor styling. ChangesProgram Monitor View Controls
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant MonitorStage
participant EditorState
participant PreviewCanvas
User->>MonitorStage: Wheel over preview
MonitorStage->>EditorState: Update viewZoom and viewPan
EditorState->>PreviewCanvas: Apply CSS transform
User->>MonitorStage: Middle mouse or Alt-drag
MonitorStage->>EditorState: Update viewPan during pointer movement
EditorState->>PreviewCanvas: Apply translated transform
User->>MonitorStage: Click reset to 100%
MonitorStage->>EditorState: Reset zoom and pan
EditorState->>PreviewCanvas: Apply identity transform
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (3)
index.html (1)
65-69: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win"Reset to 100%" label/tooltip mismatches actual "fit stage" behavior.
state.viewZoom = 1is documented in app.js as"program-monitor display zoom (1 = fit stage)", i.e., it's a fit-to-stage baseline, not a literal native-pixel 100% zoom. Labeling the button "Reset to 100%" (and tooltip "Reset monitor zoom to fit (100%)") conflates these two concepts and may confuse users who expect true 1:1 pixel scale.✏️ Suggested wording
- <button class="btn tiny hidden" id="btnZoom100" title="Reset monitor zoom to fit (100%)">...Reset to 100%</button> + <button class="btn tiny hidden" id="btnZoom100" title="Reset monitor zoom to fit">...Reset zoom</button>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@index.html` around lines 65 - 69, Update the Program Monitor reset button identified by btnZoom100 so its visible label and title describe restoring the fit-to-stage baseline rather than “100%” or native-pixel zoom; keep the existing reset behavior unchanged.app.js (2)
4430-4457: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winNo bounds on
state.viewPan.Neither the wheel-zoom pan adjustment nor the drag-pan
pointermovehandler clampsstate.viewPan, so a user can drag the zoomed canvas entirely out of the visible stage. Recovery only via the "Reset to 100%" button, which is a usable fallback but still a rough edge.🔧 Suggested clamp using existing bounding-rect data
function applyMonitorView() { const z = state.viewZoom, pan = state.viewPan; const zoomed = z > 1.001; if (!zoomed) { state.viewZoom = 1; state.viewPan = { x: 0, y: 0 }; els.preview.style.transform = ""; } else { + const stage = els.monitorStage.getBoundingClientRect(); + const maxX = (stage.width * (z - 1)) / 2; + const maxY = (stage.height * (z - 1)) / 2; + pan.x = clamp(pan.x, -maxX, maxX); + pan.y = clamp(pan.y, -maxY, maxY); els.preview.style.transform = `translate(${pan.x}px, ${pan.y}px) scale(${z})`; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app.js` around lines 4430 - 4457, Clamp state.viewPan to valid bounds after both the wheel-zoom adjustment and drag-pan updates so the zoomed canvas cannot be moved entirely outside the visible stage. Reuse the existing monitor stage and content bounding-rect data where available, and apply the clamp before applyMonitorView() in the pointermove path and before storing the zoom-adjusted pan.
4398-4457: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win
updateSafeOverlay()forces layout on every wheel tick and pan pointermove.
applyMonitorView()callsupdateSafeOverlay()(twogetBoundingClientRect()reads) right after mutatingels.preview.style.transform, and this runs on everywheelevent and everypointermovewhile panning (whenstate.guidesis on). That's a write-then-read cycle repeated at high frequency, forcing synchronous layout each time and risking visible jank during zoom/pan.⚡ Suggested throttling via requestAnimationFrame
+let viewRafPending = false; +function scheduleApplyMonitorView() { + if (viewRafPending) return; + viewRafPending = true; + requestAnimationFrame(() => { viewRafPending = false; applyMonitorView(); }); +}Then call
scheduleApplyMonitorView()from thewheelandpointermovehandlers instead ofapplyMonitorView()directly (transform mutation itself can stay immediate if desired; the goal is to coalesce theupdateSafeOverlayreflow to once per frame).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app.js` around lines 4398 - 4457, Throttle monitor view updates with requestAnimationFrame: add a scheduling helper around applyMonitorView that coalesces repeated calls into one frame, and use it from the wheel and pointermove handlers instead of calling applyMonitorView directly. Preserve immediate reset behavior for resetMonitorView and ensure updateSafeOverlay runs at most once per animation frame during zooming and panning.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@app.js`:
- Around line 4430-4457: Clamp state.viewPan to valid bounds after both the
wheel-zoom adjustment and drag-pan updates so the zoomed canvas cannot be moved
entirely outside the visible stage. Reuse the existing monitor stage and content
bounding-rect data where available, and apply the clamp before
applyMonitorView() in the pointermove path and before storing the zoom-adjusted
pan.
- Around line 4398-4457: Throttle monitor view updates with
requestAnimationFrame: add a scheduling helper around applyMonitorView that
coalesces repeated calls into one frame, and use it from the wheel and
pointermove handlers instead of calling applyMonitorView directly. Preserve
immediate reset behavior for resetMonitorView and ensure updateSafeOverlay runs
at most once per animation frame during zooming and panning.
In `@index.html`:
- Around line 65-69: Update the Program Monitor reset button identified by
btnZoom100 so its visible label and title describe restoring the fit-to-stage
baseline rather than “100%” or native-pixel zoom; keep the existing reset
behavior unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 3637d4de-0f6f-4d48-ada1-f85ff1551fba
📒 Files selected for processing (3)
app.jsindex.htmlstyle.css
What does this PR do?
Sometimes a closer look is profitable. When hovering a mouse over a monitor, mousewheel action allows to zoom in the area under the cursor. There is also button to reset zoom to 100%.
Type of change
How was it verified?
node --check server.js && node --check app.js && node --check mcp-server.jspassesCLAUDE.md/README.mdif the schema, props, or API changedChecklist
Summary by CodeRabbit