Skip to content

Conversation

spartan-vutrannguyen
Copy link
Contributor

@spartan-vutrannguyen spartan-vutrannguyen commented Aug 13, 2025

Description

  • Currently, zooming on a web frame while in preview mode zooms the entire canvas
Screen.Recording.2025-08-13.at.23.14.04.mp4
  • Added a handler to handle zoom action correctly
Screen.Recording.2025-08-13.at.23.18.31.mp4

Related Issues

Type of Change

  • Bug fix
  • New feature
  • Documentation update
  • Release
  • Refactor
  • Other (please describe):

Testing

Screenshots (if applicable)

Additional Notes


Important

Fix zoom handling in preview mode by adding a wheel event handler to forward events to the canvas container and updating event handling logic.

  • Behavior:
    • Adds onFrameWheel handler in web-frame.tsx to forward wheel events to the canvas container.
    • Introduces listenForWheelZoom in wheel.ts to intercept wheel events with ctrlKey or metaKey and prevent default behavior if not handled inside.
  • Event Handling:
    • Updates PenpalParentMethods in parent.ts to include onFrameWheel.
    • Adds listenForWheelZoom to index.ts to start listening for wheel events.
  • Misc:
    • Removes global wheel event listener in main.tsx to prevent unintended interception.

This description was created by Ellipsis for 2132243. You can customize this summary. It will automatically update as commits are pushed.


Summary by CodeRabbit

  • New Features

    • Ctrl/⌘ + mouse-wheel zoom now works when scrolling over embedded frames — wheel input is forwarded so zoom targets the outer canvas and centers on the cursor.
    • Wheel-zoom listening is initialized as part of DOM event wiring for consistent behavior.
  • Bug Fixes

    • Stopped globally intercepting Ctrl/⌘ wheel events; native scrolling/zoom now applies for scrollable, transformed, or media elements.
    • Layout overflow constrained to avoid unintended scrolling.

Copy link

vercel bot commented Aug 13, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
docs Ready Ready Preview Comment Aug 15, 2025 4:57pm
web Error Error Aug 15, 2025 4:57pm

Copy link

supabase bot commented Aug 13, 2025

This pull request has been ignored for the connected project wowaemfasoptxrdjhilu because there are no changes detected in apps/backend/supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@Kitenite
Copy link
Contributor

This is great!

Can we also try this: make it so that you can scroll but if you over-scroll on the iframe, you just scroll the canvas instead? I just want to see how that feels since a lot of people got confused when they can't scroll in preview mode.

Or if you're scrolling inside the canvas, let's just have an indication since we're now detecting it.

@Kitenite
Copy link
Contributor

One more bug and you can see it from the preview video, the mouse doesn't line up correctly with the mouse outside. If this is not fixable, I think it's ok to do nothing on zoom which is still an improvement.

Be sure to test this with projects that have some zoom functionality inside of it like a Figma clone.

@vercel vercel bot temporarily deployed to Preview – docs August 15, 2025 05:15 Inactive
Copy link

coderabbitai bot commented Aug 15, 2025

Walkthrough

Adds cross-frame wheel forwarding: a new Penpal parent method onFrameWheel, parent-side handler translating iframe wheel events into WheelEvents on the canvas container, preload wheel listener to capture Ctrl/Meta wheel in iframes and forward when appropriate, and removal of the prior global wheel interceptor in main.tsx.

Changes

Cohort / File(s) Summary
Penpal API extension
packages/penpal/src/parent.ts
Adds PenpalParentMethods.onFrameWheel(data: { deltaY, clientX, clientY, ctrlKey, metaKey }) => void and its promisified entry.
Parent frame wheel forwarding
apps/web/client/src/app/project/[id]/_components/canvas/frame/web-frame.tsx
Imports EditorAttributes; implements onFrameWheel to compute parent coords from iframe rect and dispatch a constructed WheelEvent to EditorAttributes.CANVAS_CONTAINER_ID; registers the method in the Penpal connect methods object with try/catch logging.
Preload event wiring
apps/web/preload/script/api/events/index.ts
Imports listenForWheelZoom and invokes it inside listenForDomChanges alongside existing DOM mutation/resize listeners.
Preload wheel listener
apps/web/preload/script/api/events/wheel.ts
New module exporting listenForWheelZoom(): attaches a capturing, non-passive wheel listener; filters for ctrlKey/metaKey; runs heuristics (scrollable overflow, CSS transform, touch-action: none, tag is `CANVAS
Removal of legacy global handler
apps/web/client/src/app/project/[id]/_components/main.tsx
Removes the useEffect that added a global wheel listener to intercept Ctrl/Meta wheel events outside the canvas; adds overflow-hidden to container classes.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant Iframe as Iframe (preload)
  participant Penpal as Penpal Link
  participant Parent as Parent Frame
  participant Canvas as Canvas Container

  User->>Iframe: wheel event (ctrl/meta)
  Iframe->>Iframe: run heuristics (scrollable/transform/touch-action/tag)
  alt Not handled locally
    Iframe->>Penpal: call onFrameWheel({deltaY, clientX, clientY, ctrlKey, metaKey})
    Penpal->>Parent: invoke registered handler
    Parent->>Parent: get iframe bounding rect, compute parent coords
    Parent->>Canvas: dispatch WheelEvent with adjusted coords and modifiers
  else Handled locally
    Iframe-->>User: allow default/local zoom/scroll
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

I’m a rabbit on the frame, ears tuned to wheel and zoom,
Icatch your scroll, map coordinates, and nudge the canvas’ room.
From iframe to parent, I skip—no tumble, no despair,
A tiny hop, a gentle push, I bridge the scrolling air. 🐇✨

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/handle-zoom-in-preview

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (5)
packages/penpal/src/parent.ts (1)

11-17: API addition looks good; consider extending payload for fidelity + plan for back-compat

The new method is fine. Two follow-ups:

  • Add deltaX/deltaZ/deltaMode for higher-fidelity wheel handling (trackpads, non-pixel deltas).
  • Ensure consumers are upgraded together (this is a breaking addition to PenpalParentMethods for any other implementers).

Proposed type extension:

-    onFrameWheel: (data: {
-        deltaY: number;
-        clientX: number;
-        clientY: number;
-        ctrlKey: boolean;
-        metaKey: boolean;
-    }) => void;
+    onFrameWheel: (data: {
+        // Wheel deltas
+        deltaY: number;
+        deltaX?: number;
+        deltaZ?: number;
+        deltaMode?: 0 | 1 | 2; // DOM_DELTA_PIXEL | DOM_DELTA_LINE | DOM_DELTA_PAGE
+        // Pointer position in iframe coords
+        clientX: number;
+        clientY: number;
+        // Modifiers
+        ctrlKey: boolean;
+        metaKey: boolean;
+        shiftKey?: boolean;
+        altKey?: boolean;
+    }) => void;

If there are other parent implementations, please confirm they are updated alongside this change or bump the package version appropriately.

apps/web/preload/script/api/events/index.ts (1)

7-8: Ensure idempotency to avoid duplicate listeners

If listenForDomChanges() can be invoked more than once, listenForWheelZoom() should guard against adding multiple global listeners.

Apply an init guard inside listenForWheelZoom():

 export function listenForWheelZoom() {
+    // Prevent duplicate registrations
+    if ((window as any).__onlookWheelZoomInit__) return;
+    (window as any).__onlookWheelZoomInit__ = true;
     function handleWheel(event: WheelEvent) {
       ...
     }
     window.addEventListener('wheel', handleWheel, { passive: false, capture: true });
 }
apps/web/preload/script/api/events/wheel.ts (3)

63-64: Make listener registration idempotent

Avoid multiple registrations if listenForWheelZoom() is called more than once.

-export function listenForWheelZoom() {
+let __onlookWheelZoomInit = false;
+export function listenForWheelZoom() {
+    if (__onlookWheelZoomInit) return;
+    __onlookWheelZoomInit = true;
     function handleWheel(event: WheelEvent) {
       ...
     }
     window.addEventListener('wheel', handleWheel, { passive: false, capture: true });
 }

3-61: Optional: implement “scroll chaining” on overscroll to improve preview UX

Per feedback, allow normal scrolling within the iframe, but when the user overscrolls (at scroll boundaries), route the wheel to the canvas. This can reuse the same onFrameWheel path without requiring a new API.

Apply this diff to add overscroll bridging while preserving existing zoom behavior:

 export function listenForWheelZoom() {
     function handleWheel(event: WheelEvent) {
-        if (!(event.ctrlKey || event.metaKey)) {
-            return;
-        }
-
-        const path = event.composedPath() as HTMLElement[];
+        const isZoom = event.ctrlKey || event.metaKey;
+        const path = event.composedPath();
 
         let zoomHandledInside = false;
+        let shouldForwardToParent = false;
 
         for (const el of path) {
             if (el instanceof Element) {
                 try {
                     const style = window.getComputedStyle(el);
 
                     // Heuristic #1: It's explicitly scrollable
                     const scrollable = /(auto|scroll)/.test(
                         style.overflow + style.overflowX + style.overflowY,
                     );
 
                     // Heuristic #2: It's explicitly interactive / transforms
                     const hasTransform = style.transform !== 'none';
 
                     // Heuristic #3: Prevents pinch gestures in browser defaults
                     const disablesDefaultTouch = style.touchAction === 'none';
 
                     // Heuristic #4: Specific tag types often used for zoomable content
                     const zoomableTags = ['CANVAS', 'SVG', 'IMG'];
                     const isZoomableTag = zoomableTags.includes(el.tagName);
 
-                    if (scrollable || hasTransform || disablesDefaultTouch || isZoomableTag) {
-                        zoomHandledInside = true;
-                        break;
-                    }
+                    if (isZoom) {
+                        if (scrollable || hasTransform || disablesDefaultTouch || isZoomableTag) {
+                            zoomHandledInside = true;
+                            break;
+                        }
+                    } else if (scrollable) {
+                        // Overscroll detection: if user tries to scroll beyond the element’s bounds,
+                        // we’ll forward to the parent canvas.
+                        const canScrollY = el.scrollHeight > el.clientHeight;
+                        const canScrollX = el.scrollWidth > el.clientWidth;
+                        const atTop = el.scrollTop <= 0;
+                        const atBottom = el.scrollTop + el.clientHeight >= el.scrollHeight - 1;
+                        const atLeft = el.scrollLeft <= 0;
+                        const atRight = el.scrollLeft + el.clientWidth >= el.scrollWidth - 1;
+
+                        const yOverscrollUp = event.deltaY < 0 && canScrollY && atTop;
+                        const yOverscrollDown = event.deltaY > 0 && canScrollY && atBottom;
+                        const xOverscrollLeft = event.deltaX < 0 && canScrollX && atLeft;
+                        const xOverscrollRight = event.deltaX > 0 && canScrollX && atRight;
+
+                        if (yOverscrollUp || yOverscrollDown || xOverscrollLeft || xOverscrollRight) {
+                            shouldForwardToParent = true;
+                            break;
+                        } else {
+                            // A scrollable ancestor can still consume this wheel; let it handle.
+                            // Note: we do not break here so an earlier element in the path could be considered.
+                        }
+                    }
                 } catch (err) {
                     console.warn('Could not get computed style for node:', el, err);
                 }
             }
         }
 
-        if (!zoomHandledInside) {
+        // For zoom gestures, forward when nothing inside handles zoom.
+        // For normal scroll, forward only on overscroll-at-boundary.
+        if ((isZoom && !zoomHandledInside) || (!isZoom && shouldForwardToParent)) {
             event.preventDefault();
             event.stopPropagation();
 
             if (penpalParent) {
-                penpalParent
-                    .onFrameWheel({
-                        deltaY: event.deltaY,
-                        clientX: event.clientX,
-                        clientY: event.clientY,
-                        ctrlKey: event.ctrlKey,
-                        metaKey: event.metaKey,
-                    })
-                    .catch(() => {
-                        // ignore
-                    });
+                const maybeCall = (penpalParent as any).onFrameWheel;
+                if (typeof maybeCall === 'function') {
+                    Promise.resolve(
+                        maybeCall({
+                            deltaY: event.deltaY,
+                            clientX: event.clientX,
+                            clientY: event.clientY,
+                            ctrlKey: event.ctrlKey,
+                            metaKey: event.metaKey,
+                            // If you extend the types, forward these too:
+                            // deltaX: event.deltaX,
+                            // deltaZ: event.deltaZ,
+                            // deltaMode: event.deltaMode,
+                            // shiftKey: event.shiftKey,
+                            // altKey: event.altKey,
+                        }),
+                    ).catch(() => {
+                        // ignore
+                    });
+                }
             }
         }
     }
 
     window.addEventListener('wheel', handleWheel, { passive: false, capture: true });
 }

This keeps default scrolling intact but forwards wheel to the canvas when an inner scroller hits its boundary.


47-59: Forward complete wheel signal (deltaX/deltaZ/deltaMode) for trackpad fidelity

If the parent API is extended, include deltaX/deltaZ/deltaMode and modifier keys to preserve gesture fidelity (especially on macOS trackpads).

-                    .onFrameWheel({
-                        deltaY: event.deltaY,
-                        clientX: event.clientX,
-                        clientY: event.clientY,
-                        ctrlKey: event.ctrlKey,
-                        metaKey: event.metaKey,
-                    })
+                    .onFrameWheel({
+                        deltaY: event.deltaY,
+                        deltaX: event.deltaX,
+                        deltaZ: event.deltaZ,
+                        deltaMode: event.deltaMode,
+                        clientX: event.clientX,
+                        clientY: event.clientY,
+                        ctrlKey: event.ctrlKey,
+                        metaKey: event.metaKey,
+                        shiftKey: event.shiftKey,
+                        altKey: event.altKey,
+                    })
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 00fba94 and 6bd055c.

📒 Files selected for processing (5)
  • apps/web/client/src/app/project/[id]/_components/canvas/frame/web-frame.tsx (2 hunks)
  • apps/web/client/src/app/project/[id]/_components/main.tsx (0 hunks)
  • apps/web/preload/script/api/events/index.ts (1 hunks)
  • apps/web/preload/script/api/events/wheel.ts (1 hunks)
  • packages/penpal/src/parent.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • apps/web/client/src/app/project/[id]/_components/main.tsx
🔇 Additional comments (2)
apps/web/preload/script/api/events/index.ts (1)

4-8: Wiring the wheel listener into the lifecycle: LGTM

Hooking listenForWheelZoom() after DOM/resize listeners is appropriate. No functional issues here.

apps/web/client/src/app/project/[id]/_components/canvas/frame/web-frame.tsx (1)

126-158: Follow-up: verify UX on pages with their own zoom handlers

Please test with apps that implement their own zoom (e.g., Figma-like editors) to ensure our heuristics avoid double-handling and the zoom center feels correct at different scales.

Suggested manual checks:

  • Different iframe zoom levels (0.5x, 1x, 2x) and canvas zoom levels.
  • Trackpad vs mouse wheel on macOS/Windows.
  • Pages using Canvas/SVG zoom to confirm we don’t forward when the page is handling it.
  • Verify the zoom focal point tracks the pointer accurately at non-1x scales.

Comment on lines +126 to +158
onFrameWheel: (data: {
deltaY: number;
clientX: number;
clientY: number;
ctrlKey: boolean;
metaKey: boolean;
}) => {
try {
const iframe = iframeRef.current;
if (!iframe) return;
const rect = iframe.getBoundingClientRect();
const parentClientX = rect.left + data.clientX;
const parentClientY = rect.top + data.clientY;

const container = document.getElementById(
EditorAttributes.CANVAS_CONTAINER_ID,
);
if (!container) return;

const wheelEvent = new WheelEvent('wheel', {
deltaY: data.deltaY,
clientX: parentClientX,
clientY: parentClientY,
ctrlKey: data.ctrlKey,
metaKey: data.metaKey,
bubbles: true,
cancelable: true,
});
container.dispatchEvent(wheelEvent);
} catch (error) {
console.error('Failed to forward wheel event to canvas', error);
}
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix coordinate mapping: account for iframe CSS transform scale (pointer mismatch bug)

clientX/clientY from inside the iframe are unscaled CSS pixels. Since the iframe is scaled via transform (setZoomLevel), you must multiply by the current scale. This likely explains the pointer misalignment called out in the preview.

Apply this diff:

-                                const parentClientX = rect.left + data.clientX;
-                                const parentClientY = rect.top + data.clientY;
+                                const scale = zoomLevel.current || 1;
+                                const parentClientX = rect.left + data.clientX * scale;
+                                const parentClientY = rect.top + data.clientY * scale;

Optional: if you extend the payload (per parent.ts suggestion), also forward deltaX/deltaZ/deltaMode and modifiers:

-                                const wheelEvent = new WheelEvent('wheel', {
+                                const wheelEvent = new WheelEvent('wheel', {
                                     deltaY: data.deltaY,
+                                    deltaX: data.deltaX ?? 0,
+                                    deltaZ: data.deltaZ ?? 0,
+                                    deltaMode: data.deltaMode ?? 0,
                                     clientX: parentClientX,
                                     clientY: parentClientY,
                                     ctrlKey: data.ctrlKey,
                                     metaKey: data.metaKey,
+                                    shiftKey: data.shiftKey ?? false,
+                                    altKey: data.altKey ?? false,
                                     bubbles: true,
                                     cancelable: true,
                                 });

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In apps/web/client/src/app/project/[id]/_components/canvas/frame/web-frame.tsx
around lines 126 to 158, the forwarded clientX/clientY are using unscaled iframe
coordinates so pointer events mismatch when the iframe is CSS-transformed
(scaled) by setZoomLevel; multiply the iframe-local clientX/clientY by the
current scale factor before adding rect.left/top (i.e. compute parentClientX =
rect.left + data.clientX * scale and parentClientY = rect.top + data.clientY *
scale), obtain the scale from the iframe’s computed transform or the component’s
zoom state, and include any additional wheel fields (deltaX, deltaZ, deltaMode)
and modifier keys when constructing the WheelEvent so the parent receives the
correct, fully-scaled and complete event payload.

return;
}

const path = event.composedPath() as HTMLElement[];
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Correct the composedPath typing

event.composedPath() returns EventTarget[], not HTMLElement[]. Casting to HTMLElement[] is unsafe. Use the correct type and keep the instanceof guard.

-        const path = event.composedPath() as HTMLElement[];
+        const path = event.composedPath();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const path = event.composedPath() as HTMLElement[];
const path = event.composedPath();
🤖 Prompt for AI Agents
In apps/web/preload/script/api/events/wheel.ts around line 9, the code casts
event.composedPath() to HTMLElement[] which is unsafe; change the type to
EventTarget[] (e.g. const path = event.composedPath() as EventTarget[]) and
retain the existing instanceof HTMLElement guards before accessing
HTMLElement-specific properties so you only treat elements as HTMLElements after
a runtime type check.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/web/client/src/app/project/[id]/_components/main.tsx (1)

68-68: Adding overflow-hidden to the root container is sensible; verify browser zoom behavior outside the canvas and consider a minimal guard

This helps prevent scroll bleed/scrollbars and keeps the app visually contained. One thing to double‑check after removing the global wheel handler: Ctrl/Cmd + wheel (or pinch on macOS trackpads that sets ctrlKey) anywhere outside the canvas/iframe will now zoom the browser by default. If that’s not desired, consider reintroducing a minimal guard that only prevents browser zoom outside the canvas while staying out of the new iframe → canvas forwarding path.

  • Please verify:
    • Ctrl/Cmd + wheel or pinch outside the canvas no longer causes unintended browser zoom (unless that is now intended).
    • The new iframe wheel forwarding works on macOS trackpad pinch and Windows/Linux Ctrl+wheel.
    • No regressions for apps that implement their own in-iframe zoom.
    • Overlays/popovers remain visible and aren’t clipped by overflow-hidden (should be fine if they’re portaled).

Optional local guard (keeps the previous “don’t browser-zoom outside the canvas” UX):

// Also add these imports at the top of the file:
import { useEffect } from 'react';
import { EditorAttributes } from '@onlook/constants';

// Add inside the Main component:
useEffect(() => {
  const onWheel = (e: WheelEvent) => {
    // Prevent browser zoom when zoom gesture happens outside the canvas
    if (e.ctrlKey || e.metaKey) {
      const canvasEl = document.getElementById(EditorAttributes.CANVAS_CONTAINER_ID);
      if (!canvasEl || !canvasEl.contains(e.target as Node)) {
        e.preventDefault();
        e.stopPropagation();
      }
    }
  };

  // Capture early and mark passive:false so preventDefault works
  window.addEventListener('wheel', onWheel, { capture: true, passive: false });
  return () => window.removeEventListener('wheel', onWheel, true);
}, []);

This keeps page zoom disabled across the app UI while letting the new cross-frame forwarding handle canvas zoom. If the intentional UX is to allow browser zoom outside the canvas, ignore this suggestion.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 6bd055c and 2132243.

📒 Files selected for processing (1)
  • apps/web/client/src/app/project/[id]/_components/main.tsx (2 hunks)
🔇 Additional comments (1)
apps/web/client/src/app/project/[id]/_components/main.tsx (1)

11-11: React import cleanup after dropping the global wheel listener — LGTM

Importing only useRef is correct now that the global wheel useEffect was removed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants