From fd4f5b3f59fa6fe9fe1de35fb7e9da241fabc94d Mon Sep 17 00:00:00 2001 From: liuhao1024 Date: Sun, 14 Jun 2026 08:56:25 +0800 Subject: [PATCH] fix(macos): defer isOverflowing mutation to break SwiftUI render loop (fixes #43480) (#92778) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(macos): defer isOverflowing mutation to break SwiftUI render loop measuredHeight() mutated model.isOverflowing synchronously during a SwiftUI view update cycle. The onChange(of: attributed) handler triggered updateWindowFrame → targetFrame → measuredHeight, which set isOverflowing, invalidating the view and re-triggering onChange — an infinite render loop causing 100% CPU pinwheel. Fix: defer the isOverflowing mutation via DispatchQueue.main.async with an equality guard to prevent redundant updates. The frame calculation itself remains synchronous so the window size is correct immediately. Fixes #43480 * fix(macos): preserve latest overflow measurement --------- Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com> --- .../OpenClaw/VoiceWakeOverlayController+Window.swift | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/macos/Sources/OpenClaw/VoiceWakeOverlayController+Window.swift b/apps/macos/Sources/OpenClaw/VoiceWakeOverlayController+Window.swift index 23133811e80e..39f667cc2741 100644 --- a/apps/macos/Sources/OpenClaw/VoiceWakeOverlayController+Window.swift +++ b/apps/macos/Sources/OpenClaw/VoiceWakeOverlayController+Window.swift @@ -92,7 +92,13 @@ extension VoiceWakeOverlayController { let contentHeight = ceil(used.height + (textInset.height * 2)) let total = contentHeight + self.verticalPadding * 2 - self.model.isOverflowing = total > self.maxHeight + // Defer the overflow state mutation to break the SwiftUI onChange → measuredHeight → + // isOverflowing → re-render → onChange synchronous render loop (fixes #43480). + let overflowing = total > self.maxHeight + DispatchQueue.main.async { [weak self] in + guard let self, self.model.isOverflowing != overflowing else { return } + self.model.isOverflowing = overflowing + } return max(self.minHeight, min(total, self.maxHeight)) }