From 5ccc13ad1eb2aabc2d01aa17b65a3b74847c2bfd Mon Sep 17 00:00:00 2001 From: ciregenz Date: Wed, 5 Aug 2026 00:38:10 -0700 Subject: [PATCH] [eric] canvas: memoize the wash blend + grid tile strings, card-drag frames stop rebuilding them --- .../pages/Dashboard/canvas/DashboardCanvas.tsx | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/frontend/src/app/pages/Dashboard/canvas/DashboardCanvas.tsx b/frontend/src/app/pages/Dashboard/canvas/DashboardCanvas.tsx index 3bf0c2a8..d3478f9a 100644 --- a/frontend/src/app/pages/Dashboard/canvas/DashboardCanvas.tsx +++ b/frontend/src/app/pages/Dashboard/canvas/DashboardCanvas.tsx @@ -167,9 +167,15 @@ const DashboardCanvas: React.FC = ({ const { accent, gradient } = useThemeAccent(); const { washOpacity, grain } = useThemeWash(); // A single picked color stores gradient=null, so fall back to the accent (mirrors BeatShell). - const washStops = effectiveWashStops(gradient, accent); + const washStops = React.useMemo(() => effectiveWashStops(gradient, accent), [gradient, accent]); const dotSize = Math.max(1, 1.5 * canvas.zoom); const dotSpacing = 24 * canvas.zoom; + // Memoized: this component re-renders every card-drag frame, and rebuilding these strings (SVG encode + hex blends) per frame is pure waste. + const washUnderlay = React.useMemo(() => washUnderlayColor(washStops, washOpacity, c.bg.page), [washStops, washOpacity, c.bg.page]); + const washUrl = React.useMemo(() => washOpaqueBackgroundUrl(washStops, washOpacity, c.bg.page), [washStops, washOpacity, c.bg.page]); + const gridTileUrl = React.useMemo(() => `url("data:image/svg+xml,${encodeURIComponent( + ``, + )}")`, [dotSpacing, dotSize, c.border.medium]); useWebviewSuspend(browserCards, canvas.panX, canvas.panY, canvas.zoom, canvas.viewportRef); @@ -374,7 +380,7 @@ const DashboardCanvas: React.FC = ({ inset: 0, overflow: 'hidden', // Last line of the never-white guarantee: if every background layer's raster is gone, the viewport itself still paints tint (solid colors are compositor quads, not evictable textures). - backgroundColor: washUnderlayColor(washStops, washOpacity, c.bg.page), + backgroundColor: washUnderlay, cursor: canvas.isPanning ? 'grabbing' : (canvas.spaceHeld || canvas.cmdHeld) @@ -391,8 +397,8 @@ const DashboardCanvas: React.FC = ({ position: 'absolute', inset: 0, pointerEvents: 'none', - backgroundColor: washUnderlayColor(washStops, washOpacity, c.bg.page), - backgroundImage: washOpaqueBackgroundUrl(washStops, washOpacity, c.bg.page), + backgroundColor: washUnderlay, + backgroundImage: washUrl, backgroundSize: '100% 100%', }} /> @@ -421,9 +427,7 @@ const DashboardCanvas: React.FC = ({ transform: `translate3d(${canvas.panX % dotSpacing}px, ${canvas.panY % dotSpacing}px, 0)`, // Arc fullscreen: the float sits on a clean themed ground, the dot texture is canvas-only. display: anyFullscreen ? 'none' : undefined, - backgroundImage: `url("data:image/svg+xml,${encodeURIComponent( - ``, - )}")`, + backgroundImage: gridTileUrl, backgroundSize: `${dotSpacing}px ${dotSpacing}px`, }} />