perf(editor): centralize shape culling display updates#7841
perf(editor): centralize shape culling display updates#7841steveruizok merged 5 commits intoperformance-stack-1from
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
5 Skipped Deployments
|
|
Hey! 👋 This PR contains changes to Here's an example: Once you add this section, the check will pass automatically! |
Instead of each Shape component having its own useQuickReactor that subscribes to getCulledShapes(), this creates a centralized ShapeCullingManager that: - Maintains a registry of shape container refs - Uses a single reactor in ShapesToDisplay to update visibility - Only updates containers whose culling state actually changed This reduces O(N) subscriptions to O(1), improving performance when many shapes are on the canvas. Closes #7831
293b799 to
34bbaa7
Compare
474e3d5 to
4f53d45
Compare
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| 🔵 In progress View logs |
branching-chat-template | 4f53d45 | Feb 04 2026, 06:34 PM |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| 🔵 In progress View logs |
agent-template | 4f53d45 | Feb 04 2026, 06:34 PM |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
agent-template | 4f53d45 | Feb 04 2026, 06:40 PM |
| hashed = hashString(string) | ||
| if (STRING_HASH_CACHE_SIZE === STRING_HASH_CACHE_MAX_SIZE) { | ||
| STRING_HASH_CACHE_SIZE = 0 | ||
| stringHashCache = {} |
There was a problem hiding this comment.
Unbounded string hash cache causes memory leak
High Severity
The stringHashCache is now unbounded, causing a memory leak. The original Immutable.js implementation limited the cache to 255 entries and cleared it when full, and only cached strings longer than 16 characters. This change removes both safeguards: all strings (regardless of length) are now cached, and the cache is never cleared. In a long-running editor session with many unique string keys, this will cause continuous memory growth.
Additional Locations (1)
Replace the imperative ShapeCullingManager with a React context-based provider and hook. Adds packages/editor/src/lib/hooks/useShapeCulling.tsx (ShapeCullingProvider + useShapeCulling) and removes the ShapeCullingManager class and its export. Update Shape to register/unregister via useShapeCulling, wrap rendered shapes in DefaultCanvas with ShapeCullingProvider, and have CullingController call the provider's updateCulling. Also remove the editor.shapeCulling property and related exports/initialization.
Replace getCurrentPageShapeIds() with getCurrentPageShapes() and iterate the shape objects directly. This avoids repeated editor.getShape() lookups, uses shape.id for set operations, and updates the fast-path visibility check to use allShapes.length. Minor performance and clarity improvements with no functional change.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.


Summary
ShapeCullingProviderReact context that maintains a registry of shape container refsuseQuickReactorsubscriptions with a single centralized reactor inShapesToDisplayCullingControlleruses the context to update visibility of all registered containersThis keeps DOM element tracking in React-land rather than on the Editor, maintaining separation between the editor and rendering layers.
Performance improvement
With N shapes on canvas, this changes:
EffectSchedulerinstances, each subscribing togetCulledShapes()and running when it changesThis reduces subscription overhead from O(N) to O(1).
Test plan
yarn devand open the examples appCloses #7831
Note
Medium Risk
Changes the shape rendering pipeline by moving culling visibility updates to a shared registry and single reactor, which could cause shapes to incorrectly hide/show if registration or updates misfire. Also adjusts store string hashing cache behavior, which may affect memory/perf characteristics under load.
Overview
Centralizes shape culling display toggling. Shapes now register their DOM containers via a new
ShapeCullingProvider/useShapeCullingcontext, andDefaultCanvasruns a singleCullingControllerreactor to applydisplay: none/blockupdates for all registered shapes (replacing per-shape culling reactors inShape.tsx).Reduces reactive churn in a few hot paths.
notVisibleShapesis rewritten to avoid unnecessarySetallocations when results don’t change,CanvasShapeIndicatorsreplaces generic deep equality with a purpose-built comparator for its computed render data, anduseActivePeerIds$switches to aSetequality comparator.Tweaks store hashing behavior.
ImmutableMapnow always usescachedHashStringfor strings and removes cache size/length limits, changing caching/memory tradeoffs for string hashing.Written by Cursor Bugbot for commit 1881c57. This will update automatically on new commits. Configure here.