From 409246f7adee3b42ca9ec2c1dbf5e4f6af2ad78c Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 28 Apr 2026 23:36:36 +0000 Subject: [PATCH] =?UTF-8?q?perf(layout):=20override=20explicitAlignment=20?= =?UTF-8?q?to=20stop=20O(n=C3=97depth)=20cascade=20in=20custom=20Layout=20?= =?UTF-8?q?wrappers=20(LUM-1167)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BottomAlignedMinHeightLayout and WidthCapLayout were introduced to replace _FlexFrameLayout, but neither overrode Layout.explicitAlignment. The default protocol extension merges all subviews' alignment guides — the exact same O(n×depth) recursive cascade that _FlexFrameLayout performs. When BottomAlignedMinHeightLayout wraps the entire LazyVStack scroll content, this cascade walks every visible cell on each layout pass, producing 2000ms+ main-thread hangs (Sentry MACOS-M9, MACOS-JG). Override both explicitAlignment(of: HorizontalAlignment) and explicitAlignment(of: VerticalAlignment) to return nil on both layouts. Returning nil means 'no explicit guide — use default positioning', which is correct because both layouts position children via placeSubviews, not alignment guides. Closes LUM-1167 Co-Authored-By: ashlee@vellum.ai --- .../BottomAlignedMinHeightLayout.swift | 22 +++++++++++++++++++ .../Modifiers/WidthCapLayout.swift | 19 ++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/clients/shared/DesignSystem/Modifiers/BottomAlignedMinHeightLayout.swift b/clients/shared/DesignSystem/Modifiers/BottomAlignedMinHeightLayout.swift index 6c7b594e32a..38dadc472d2 100644 --- a/clients/shared/DesignSystem/Modifiers/BottomAlignedMinHeightLayout.swift +++ b/clients/shared/DesignSystem/Modifiers/BottomAlignedMinHeightLayout.swift @@ -46,6 +46,28 @@ public struct BottomAlignedMinHeightLayout: Layout { proposal: ProposedViewSize(width: childSize.width, height: childSize.height) ) } + + // MARK: - Alignment (opt out of default cascade) + + /// Returns `nil` to opt out of the default guide-merging cascade. + /// + /// The default `Layout` protocol implementation iterates every subview + /// and recursively queries their alignment guides — O(n × depth). When + /// this layout wraps the entire LazyVStack scroll content, the cascade + /// walks every visible cell, producing multi-second hangs. + /// + /// Returning `nil` tells ancestors "no explicit guide value; use default + /// positioning", which is correct because this layout positions its + /// child via `placeSubviews`, not alignment guides. + /// + /// Reference: [Layout.explicitAlignment](https://developer.apple.com/documentation/swiftui/layout/explicitalignment(of:in:proposal:subviews:cache:)-8ofeu) + public func explicitAlignment(of guide: HorizontalAlignment, in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGFloat? { + nil + } + + public func explicitAlignment(of guide: VerticalAlignment, in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGFloat? { + nil + } } extension View { diff --git a/clients/shared/DesignSystem/Modifiers/WidthCapLayout.swift b/clients/shared/DesignSystem/Modifiers/WidthCapLayout.swift index 016cb1f3b14..8c90f64a37e 100644 --- a/clients/shared/DesignSystem/Modifiers/WidthCapLayout.swift +++ b/clients/shared/DesignSystem/Modifiers/WidthCapLayout.swift @@ -29,6 +29,25 @@ public struct WidthCapLayout: Layout { proposal: ProposedViewSize(width: bounds.width, height: bounds.height) ) } + + // MARK: - Alignment (opt out of default cascade) + + /// Returns `nil` to opt out of the default guide-merging cascade. + /// + /// The default `Layout` protocol implementation iterates every subview + /// and recursively queries their alignment guides — O(n × depth). + /// Returning `nil` tells ancestors "no explicit guide value; use default + /// positioning", which is correct because this layout positions its + /// child via `placeSubviews`, not alignment guides. + /// + /// Reference: [Layout.explicitAlignment](https://developer.apple.com/documentation/swiftui/layout/explicitalignment(of:in:proposal:subviews:cache:)-8ofeu) + public func explicitAlignment(of guide: HorizontalAlignment, in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGFloat? { + nil + } + + public func explicitAlignment(of guide: VerticalAlignment, in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGFloat? { + nil + } } extension View {