From 0083bab951efbf9e0a6bac5cdf6993eebb62cdea Mon Sep 17 00:00:00 2001 From: meh Date: Tue, 15 Sep 2026 02:23:09 +0700 Subject: [PATCH 1/2] fix(dom): update a hoisted paint child instead of appending a copy `place_hoisted_fixed` joins an ancestor stacking context that is already built, so an incremental resolve that re-flushes the same node reached it again and pushed a second entry for one child. Two entries paint the same box twice. The visible symptom was a pill menu whose shadow darkened every time the pointer crossed it, roughly 11.8k pixels per pass, with the DOM and the layout still flat because the duplication only ever existed in this paint list. Frames also slowed enough to miss timing budgets in nearby checks. `correct_hoisted_fixed_positions` already loops over every entry matching a node id rather than finding one, which is the same duplication seen from the other side. Find and replace the existing entry, append only when absent. --- packages/blitz-dom/src/layout/damage.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/blitz-dom/src/layout/damage.rs b/packages/blitz-dom/src/layout/damage.rs index fd90a1e2..5d1afb2a 100644 --- a/packages/blitz-dom/src/layout/damage.rs +++ b/packages/blitz-dom/src/layout/damage.rs @@ -1022,7 +1022,23 @@ impl BaseDocument { }; let mut hoisted = HoistedPaintChild::new(child_id, z_index, Position::Fixed); hoisted.position = position; - context.children.push(hoisted); + // Update in place, never append a second copy. + // + // This joins an ancestor context that is *already built*, so an + // incremental resolve that re-flushes this node reaches it again with + // the same child. Pushing unconditionally recorded the node twice, and + // two entries paint the same box twice: a hovered pill menu stacked its + // shadow, ~11.8k pixels darker each pass, with the DOM and layout still + // flat because the duplication is only in this paint list. + if let Some(existing) = context + .children + .iter_mut() + .find(|child| child.node_id == child_id) + { + *existing = hoisted; + } else { + context.children.push(hoisted); + } let mut context = self.nodes[host].stacking_context.take().unwrap(); context.sort(); context.compute_content_size(self); From 023cec542b89737b3f351f2716bf45f9b8e57ab4 Mon Sep 17 00:00:00 2001 From: meh Date: Tue, 15 Sep 2026 02:31:47 +0700 Subject: [PATCH 2/2] release: 0.4.10 Publishes the hoisted paint child fix. `blitz-net` carries its own version since 0.4.10 (see the cookie provider work), so the workspace bump does not collide with it. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index e78b47d0..4ad3944e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,7 +42,7 @@ exclude = ["sites", "packages/blitz-wasm/guest", ".ps-observability", ".chuzz", resolver = "2" [workspace.package] -version = "0.4.9" +version = "0.4.10" license = "MIT OR Apache-2.0" homepage = "https://github.com/pathscale/ps-blitz" repository = "https://github.com/pathscale/ps-blitz"